Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 fix challenge offset constants #31

Merged
merged 1 commit into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions src/WebAuthn256r1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@

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,

Check warning on line 7 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name UV_FLAG_MASK is not used

Check warning on line 7 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name UV_FLAG_MASK is not used
OFFSET_CLIENT_CHALLENGE_GET,

Check warning on line 8 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name OFFSET_CLIENT_CHALLENGE_GET is not used

Check warning on line 8 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name OFFSET_CLIENT_CHALLENGE_GET is not used
OFFSET_CLIENT_CHALLENGE_CREATE,

Check warning on line 9 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name OFFSET_CLIENT_CHALLENGE_CREATE is not used

Check warning on line 9 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name OFFSET_CLIENT_CHALLENGE_CREATE is not used
OFFSET_FLAG,

Check warning on line 10 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name OFFSET_FLAG is not used

Check warning on line 10 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name OFFSET_FLAG is not used
OFFSET_CLIENT_TYPE,

Check warning on line 11 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name OFFSET_CLIENT_TYPE is not used

Check warning on line 11 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name OFFSET_CLIENT_TYPE is not used
TYPE_GET_INDICATOR,

Check warning on line 12 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name TYPE_GET_INDICATOR is not used

Check warning on line 12 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name TYPE_GET_INDICATOR is not used
TYPE_CREATE_INDICATOR

Check warning on line 13 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name TYPE_CREATE_INDICATOR is not used

Check warning on line 13 in src/WebAuthn256r1.sol

View workflow job for this annotation

GitHub Actions / lint

imported name TYPE_CREATE_INDICATOR is not used
} from "src/utils.sol";

/// @title WebAuthn256r1
/// @notice A library to verify ECDSA signature though WebAuthn on the secp256r1 curve
Expand Down Expand Up @@ -76,12 +84,20 @@
// 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) {
Expand Down
24 changes: 21 additions & 3 deletions src/utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,32 @@ 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":<challenge>,"origin":"<origin>"}`
// Ex: `{"type":"webauthn.get","challenge":<challenge>,"origin":"<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;
// The offset where the credential ID value starts
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
Loading