diff --git a/modules/passkey/contracts/verifiers/WebAuthnVerifier.sol b/modules/passkey/contracts/verifiers/WebAuthnVerifier.sol index 1b3c7a51..e4b3c792 100644 --- a/modules/passkey/contracts/verifiers/WebAuthnVerifier.sol +++ b/modules/passkey/contracts/verifiers/WebAuthnVerifier.sol @@ -3,7 +3,6 @@ pragma solidity >=0.8.0; import {IP256Verifier, P256VerifierLib} from "./IP256Verifier.sol"; -import {Base64Url} from "../vendor/FCL/utils/Base64Url.sol"; /** * @title WebAuthnConstants @@ -89,6 +88,9 @@ interface IWebAuthnVerifier { */ contract WebAuthnVerifier is IWebAuthnVerifier { IP256Verifier internal immutable P256_VERIFIER; + + string internal constant ENCODING_TABLE = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; constructor(IP256Verifier verifier) { P256_VERIFIER = verifier; @@ -106,15 +108,43 @@ contract WebAuthnVerifier is IWebAuthnVerifier { bytes32 challenge, bytes calldata clientDataFields ) internal pure returns (bytes32 message) { - string memory encodedChallenge = Base64Url.encode(abi.encodePacked(challenge)); /* solhint-disable quotes */ + // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA is placeholder for the encoded challenge bytes memory clientDataJson = abi.encodePacked( '{"type":"webauthn.get","challenge":"', - encodedChallenge, + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", '",', clientDataFields, "}" ); + + string memory table = ENCODING_TABLE; + + // solhint-disable-next-line no-inline-assembly + assembly { + // Skip first 32 bytes of the table containing the length + let tablePtr := add(table, 1) + // Skip first 36 bytes of the clientDataJson containing '{"type":"webauthn.get","challenge":"' + let resultPtr := add(clientDataJson, 68) + + // Store group of 6 bits from the challenge to be encoded. Storing of 6 bits group can be removed but, kept here for readability. + let sixBitGroup + + // Iterate over challenge in group of 6 bits, for each 6 bits lookup the ENCODING_TABLE, transform it and store it in the result + for {let i := 0} lt(i, 252) + { + i := add(i, 6) + } { + sixBitGroup := and(shr(sub(250, i), challenge), 0x3F) + mstore8(resultPtr, mload(add(tablePtr, sixBitGroup))) + resultPtr := add(resultPtr, 1) + } + + // Load the remaining last 4 bits of challenge that are yet to be encoded and then shift left to add 2 bits at the end to make it a group of 6 bits. + sixBitGroup := shl(2, and(challenge, 0x0F)) + mstore8(resultPtr, mload(add(tablePtr, sixBitGroup))) + } + /* solhint-enable quotes */ message = sha256(abi.encodePacked(authenticatorData, sha256(clientDataJson))); }