diff --git a/src/utils/WebAuthn.sol b/src/utils/WebAuthn.sol index 3302bd98b..f14f260e3 100644 --- a/src/utils/WebAuthn.sol +++ b/src/utils/WebAuthn.sol @@ -21,16 +21,30 @@ library WebAuthn { // The WebAuthn client data JSON. // See: https://www.w3.org/TR/webauthn-2/#dom-authenticatorresponse-clientdatajson. string clientDataJSON; - // The index at which "challenge":"..." occurs in `clientDataJSON`. + // Start index of "challenge":"..." in `clientDataJSON`. uint256 challengeIndex; - // The index at which "type":"..." occurs in `clientDataJSON`. + // Start index of "type":"..." in `clientDataJSON`. uint256 typeIndex; - // The r value of secp256r1 signature + // The r value of secp256r1 signature. bytes32 r; - // The s value of secp256r1 signature + // The s value of secp256r1 signature. bytes32 s; } + /// @dev Alternative struct for verification (Ithaca style). + struct WebAuthnMetadata { + // The WebAuthn authenticator data. + bytes authenticatorData; + // The WebAuthn client data JSON. + string clientDataJSON; + // Start index of "challenge":"..." in `clientDataJSON`. + uint256 challengeIndex; + // Start index of "type":"..." in `clientDataJSON`. + uint256 typeIndex; + // Whether to check that the "User Verified" flag in `authenticatorData` is set. + bool requireUserVerification; + } + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ @@ -159,6 +173,31 @@ library WebAuthn { return result && P256.verifySignature(messageHash, auth.r, auth.s, x, y); } + /// @dev Alternative syntax for verification (Ithaca style). + function verify( + bytes memory challenge, + WebAuthnMetadata memory metadata, + bytes32 r, + bytes32 s, + bytes32 x, + bytes32 y + ) internal view returns (bool) { + return verify( + challenge, + metadata.requireUserVerification, + WebAuthnAuth( + metadata.authenticatorData, + metadata.clientDataJSON, + metadata.challengeIndex, + metadata.typeIndex, + r, + s + ), + x, + y + ); + } + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ENCODING / DECODING HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ diff --git a/test/WebAuthn.t.sol b/test/WebAuthn.t.sol index 3d428c9fb..9506da63d 100644 --- a/test/WebAuthn.t.sol +++ b/test/WebAuthn.t.sol @@ -51,6 +51,27 @@ contract WebAuthnTest is P256VerifierEtcher { assertTrue(WebAuthn.verify(t.challenge, false, auth, t.x, t.y)); } + function testSafariAltSyntax() public { + _etchRIPPrecompile(true); + _etchVerifier(true); + _TestTemps memory t = _testTemps(); + WebAuthn.WebAuthnMetadata memory metadata; + metadata.authenticatorData = + hex"49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000101"; + metadata.clientDataJSON = string( + abi.encodePacked( + '{"type":"webauthn.get","challenge":"', + Base64.encode(t.challenge, true, true), + '","origin":"http://localhost:3005"}' + ) + ); + metadata.challengeIndex = 23; + metadata.typeIndex = 1; + bytes32 r = 0x60946081650523acad13c8eff94996a409b1ed60e923c90f9e366aad619adffa; + bytes32 s = 0x3216a237b73765d01b839e0832d73474bc7e63f4c86ef05fbbbfbeb34b35602b; + assertTrue(WebAuthn.verify(t.challenge, metadata, r, s, t.x, t.y)); + } + function testChrome() public { _etchRIPPrecompile(true); _etchVerifier(true); @@ -72,6 +93,27 @@ contract WebAuthnTest is P256VerifierEtcher { assertTrue(WebAuthn.verify(t.challenge, false, auth, t.x, t.y)); } + function testChromeAltSyntax() public { + _etchRIPPrecompile(true); + _etchVerifier(true); + _TestTemps memory t = _testTemps(); + WebAuthn.WebAuthnMetadata memory metadata; + metadata.authenticatorData = + hex"49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763050000010a"; + metadata.clientDataJSON = string( + abi.encodePacked( + '{"type":"webauthn.get","challenge":"', + Base64.encode(t.challenge, true, true), + '","origin":"http://localhost:3005","crossOrigin":false}' + ) + ); + metadata.challengeIndex = 23; + metadata.typeIndex = 1; + bytes32 r = 0x41c01ca5ecdfeb23ef70d6cc216fd491ac3aa3d40c480751f3618a3a9ef67b41; + bytes32 s = 0x6595569abf76c2777e832a9252bae14efdb77febd0fa3b919aa16f6208469e86; + assertTrue(WebAuthn.verify(t.challenge, metadata, r, s, t.x, t.y)); + } + function testPassthroughDifferential(bytes32) public { _etchVerifierPassthrough(true); _etchRIPPrecompilePassthrough(true);