Skip to content

Commit

Permalink
♻️ Encode the challenge on-chain
Browse files Browse the repository at this point in the history
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 opinionated and assumes that it is used in the context of Account
Abstraction. In this context, valuable information required to proceed
with the transaction correctly will be stored in the challenge field
meaning we need the challenge in clear to use later in the flow. That's
why we decided to encode it here.
  • Loading branch information
qd-qd committed Aug 7, 2023
1 parent 2745500 commit 3d59ac7
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 12 deletions.
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

0 comments on commit 3d59ac7

Please sign in to comment.