diff --git a/src/WebAuthn256r1.sol b/src/WebAuthn256r1.sol index 4f8f5b7..44475a2 100644 --- a/src/WebAuthn256r1.sol +++ b/src/WebAuthn256r1.sol @@ -3,7 +3,15 @@ pragma solidity >=0.8.19 <0.9.0; import { ECDSA256r1 } from "../lib/secp256r1-verify/src/ECDSA256r1.sol"; import { Base64 } from "../lib/solady/src/utils/Base64.sol"; -import { UV_FLAG_MASK, OFFSET_CLIENT_CHALLENGE, OFFSET_FLAG } from "src/utils.sol"; +import { + UV_FLAG_MASK, + OFFSET_CLIENT_CHALLENGE_GET, + OFFSET_CLIENT_CHALLENGE_CREATE, + OFFSET_FLAG, + OFFSET_CLIENT_TYPE, + TYPE_GET_INDICATOR, + TYPE_CREATE_INDICATOR +} from "src/utils.sol"; /// @title WebAuthn256r1 /// @notice A library to verify ECDSA signature though WebAuthn on the secp256r1 curve @@ -76,12 +84,20 @@ library WebAuthn256r1 { // Encode the client challenge in base64 and explicitly convert it to bytes bytes memory challengeEncoded = bytes(Base64.encode(clientChallenge, true, true)); + // Extract the client challenge offset based on the client type + // By checking the indicator we can determine if we need to use the offset for the get or create flow + // @dev: we don't need to check the overflow here as the EVM will automatically revert if + // `OFFSET_CLIENT_TYPE` is out of bound. + uint256 clientChallengeOffset = clientData[OFFSET_CLIENT_TYPE] == TYPE_CREATE_INDICATOR + ? OFFSET_CLIENT_CHALLENGE_CREATE + : OFFSET_CLIENT_CHALLENGE_GET; + // Extract the challenge from the client data and hash it // @dev: we don't need to check the overflow here as the EVM will automatically revert if - // `OFFSET_CLIENT_CHALLENGE + challengeEncoded.length` overflow. This is because we will + // `clientChallengeOffset + challengeEncoded.length` overflow. This is because we will // try to access a chunk of memory by passing an end index lower than the start index bytes32 challengeHashed = - keccak256(clientData[OFFSET_CLIENT_CHALLENGE:(OFFSET_CLIENT_CHALLENGE + challengeEncoded.length)]); + keccak256(clientData[clientChallengeOffset:(clientChallengeOffset + challengeEncoded.length)]); // Hash the encoded challenge and check both challenges are equal if (keccak256(challengeEncoded) != challengeHashed) { diff --git a/src/utils.sol b/src/utils.sol index 4ed4d9f..748a293 100644 --- a/src/utils.sol +++ b/src/utils.sol @@ -18,8 +18,26 @@ bytes1 constant UP_FLAG_MASK = 0x01; bytes1 constant UV_FLAG_MASK = 0x04; bytes1 constant BOTH_FLAG_MASK = 0x05; -// The offset of the client challenge in the client data -uint256 constant OFFSET_CLIENT_CHALLENGE = 0x24; +// The challenge is stored in the client data field of the WebAuthn response. +// The client data is a JSON object that contains a type, the challenge and the origin. +// For passkeys, the type is always "webauthn.get" or "webauthn.create". +// Ex: `{"type":"webauthn.create","challenge":,"origin":""}` +// Ex: `{"type":"webauthn.get","challenge":,"origin":""}` +// +// The client data always starts with a constant value which is the same for both +// the get and create flows. This constant value correspond to the beginning of +// the JSON object which is: `{"type":"webauthn.`. The next byte allow to distinguish +// between the get and create flows. It is either `g` (0x67) for get or `c` (0x63) for create. +// The three constants located below can be used to know if a WebAuthn response is a get or create flow. +uint256 constant OFFSET_CLIENT_TYPE = 0x12; +bytes1 constant TYPE_GET_INDICATOR = 0x67; +bytes1 constant TYPE_CREATE_INDICATOR = 0x63; +// The offset of the client challenge for the get flow +// Correspond to the constant value `{"type":"webauthn.get","challenge":` +uint256 constant OFFSET_CLIENT_CHALLENGE_GET = 0x24; +// The offset of the client challenge for the create flow +// Correspond to the constant value `{"type":"webauthn.create","challenge":` +uint256 constant OFFSET_CLIENT_CHALLENGE_CREATE = 0x27; // The offset where the credential ID length starts and its length uint256 constant OFFSET_CREDID_LENGTH = 0x35; // 53 uint256 constant CREDID_LENGTH_LENGTH = 0x02; @@ -27,5 +45,5 @@ uint256 constant CREDID_LENGTH_LENGTH = 0x02; uint256 constant OFFSET_CREDID = 0x37; // 55 // The offset of the flag mask uint256 constant OFFSET_FLAG = 0x20; // 32 - +// The length of the public key coordinates uint256 constant P256R1_PUBKEY_COORD_LENGTH = 0x20; // 32