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

♻️ Encode the challenge in base64 on-chain #6

Merged
merged 1 commit into from
Aug 7, 2023
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
24 changes: 16 additions & 8 deletions src/WebAuthn.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19 <0.9.0;

// import { Base64 } from "../lib/solady/src/utils/Base64.sol";
import { Base64 } from "../lib/solady/src/utils/Base64.sol";
import { ECDSA256r1 } from "../lib/secp256r1-verify/src/ECDSA256r1.sol";

error InvalidAuthenticatorData();
error InvalidClientData();

/// dev: this implementation assumes the caller check if User Presence (0x01) or User Verification (0x04) are set
/// @title A library to verify ECDSA signature though WebAuthn on the secp256r1 curve
/// @dev This implementation assumes the caller check if User Presence (0x01) or User Verification (0x04) are set
library WebAuthn {
/// @notice Validate the webauthn data and generate the signature message needed to recover
/// @dev You may probably ask why we encode the challenge in base64 on-chain instead of
/// of sending it already encoded to save some gas. This library is opiniated and
/// it assumes that it is used in the context of Account Abstraction. In this context,
/// valuable informations required to proceed the transaction will be stored in the challenge
/// meaning we need the challenge in clear to use it later in the flow. That's why we decided to
/// encode it here.
function generateMessage(
bytes1 authenticatorDataFlagMask,
bytes calldata authenticatorData,
Expand All @@ -26,15 +34,15 @@ library WebAuthn {
revert InvalidAuthenticatorData();
}

// TODO: Pass non-encode challenge? Convert the challenge to `bytes memory` and encode it to Base64
// bytes memory challengeEncoded = bytes(Base64.encode(abi.encodePacked(clientChallenge), true, true));
// Encode the client challenge in base64 and explicitly convert it to bytes
bytes memory challengeEncoded = bytes(Base64.encode(clientChallenge, true, true));

// Extract the challenge from the client data and hash it
bytes32 challengeHashed =
keccak256(clientData[clientChallengeOffset:(clientChallengeOffset + clientChallenge.length)]);
keccak256(clientData[clientChallengeOffset:(clientChallengeOffset + challengeEncoded.length)]);

// hash the encoded challenge and check both challenges are equal
if (keccak256(clientChallenge) != challengeHashed) {
// Hash the encoded challenge and check both challenges are equal
if (keccak256(challengeEncoded) != challengeHashed) {
revert InvalidClientData();
}

Expand All @@ -43,6 +51,7 @@ library WebAuthn {
}
}

/// @notice Verify ECDSA signature though WebAuthn on the secp256r1 curve
function verify(
bytes1 authenticatorDataFlagMask,
bytes calldata authenticatorData,
Expand All @@ -58,7 +67,6 @@ library WebAuthn {
returns (bool)
{
unchecked {
// Verify the signature over sha256(authenticatorData || sha256(clientData))
bytes32 message = generateMessage(
authenticatorDataFlagMask, authenticatorData, clientData, clientChallenge, clientChallengeOffset
);
Expand Down
8 changes: 4 additions & 4 deletions test/WebAuthn.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ contract ContractTest is Test {
hex"65223a224e546f2d3161424547526e78786a6d6b61544865687972444e583369"
hex"7a6c7169316f776d4f643955474a30222c226f726967696e223a226874747073"
hex"3a2f2f66726573682e6c65646765722e636f6d222c2263726f73734f726967696e223a66616c73657d",
// clientChallengeBase64
hex"4e546f2d3161424547526e78786a6d6b61544865687972444e5833697a6c7169316f776d4f643955474a30",
// clientChallenge
hex"353a3ed5a0441919f1c639a46931de872ac3357de2ce5aa2d68c2639df54189d",
// clientChallengeOffset
0x24,
// r
Expand All @@ -95,8 +95,8 @@ contract ContractTest is Test {
hex"65223a224e546f2d3161424547526e78786a6d6b61544865687972444e583369"
hex"7a6c7169316f776d4f643955474a30222c226f726967696e223a226874747073"
hex"3a2f2f66726573682e6c65646765722e636f6d222c2263726f73734f726967696e223a66616c73657d",
// clientChallengeBase64
hex"4e546f2d3161424547526e78786a6d6b61544865687972444e5833697a6c7169316f776d4f643955474a30",
// clientChallenge
hex"353a3ed5a0441919f1c639a46931de872ac3357de2ce5aa2d68c2639df54189d",
// clientChallengeOffset
0x24
);
Expand Down