Skip to content

Commit

Permalink
[#14] Use assembly in WebAuthnVerifier for Base64 encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
akshay-ap committed Mar 11, 2024
1 parent e907b6c commit eb111a4
Showing 1 changed file with 67 additions and 2 deletions.
69 changes: 67 additions & 2 deletions modules/4337/contracts/experimental/verifiers/WebAuthnVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
pragma solidity >=0.8.0;

import {P256Wrapper} from "./P256Wrapper.sol";
import {Base64Url} from "../../vendor/FCL/utils/Base64Url.sol";

/**
* @title WebAuthnConstants
Expand Down Expand Up @@ -90,6 +89,9 @@ interface IWebAuthnVerifier {
contract WebAuthnVerifier is IWebAuthnVerifier, P256Wrapper {
constructor(address verifier) P256Wrapper(verifier) {}

string internal constant ENCODING_TABLE =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";

/**
* @dev Generates a signing message based on the authenticator data, challenge, and client data fields.
* @param authenticatorData Authenticator data.
Expand All @@ -102,7 +104,70 @@ contract WebAuthnVerifier is IWebAuthnVerifier, P256Wrapper {
bytes32 challenge,
bytes calldata clientDataFields
) internal pure returns (bytes32 message) {
string memory encodedChallenge = Base64Url.encode(abi.encodePacked(challenge));

// Result is 44 bytes long because the encoded challenge is 32 bytes long
// 4*(32 + 2)/3 = 44 after rounding.
string memory result = new string(44);
string memory table = ENCODING_TABLE;

assembly {
let tablePtr := add(table, 1)
let resultPtr := add(result, 32)

for{ let dataPtr := challenge
let i := 0} lt(i, 32) { i := add(i, 3) }{
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)

}
}

string memory encodedChallenge = new string(44);

assembly {
let tablePtr := add(table, 1)
let resultPtr := add(encodedChallenge, 32)
let buffer
for{let i:= 0} lt(i, 10)
{
i := add(i, 1)
}{
let shift := sub(256, mul(add(i, 1), 24))
buffer := and(shr(shift, challenge), 0xFFFFFF)

mstore8(resultPtr, mload(add(tablePtr, and(shr(18, buffer), 0x3F))))
resultPtr := add(resultPtr, 1)

mstore8(resultPtr, mload(add(tablePtr, and(shr(12, buffer), 0x3F))))
resultPtr := add(resultPtr, 1)

mstore8(resultPtr, mload(add(tablePtr, and(shr(6, buffer), 0x3F))))
resultPtr := add(resultPtr, 1)

mstore8(resultPtr, mload(add(tablePtr, and(buffer, 0x3F))))
resultPtr := add(resultPtr, 1)
}

// As 32 bytes input is not divisible by 3, process last 2 bytes of challenge separately
buffer := shl(8, and(challenge, 0xFFFF))

mstore8(resultPtr, mload(add(tablePtr, and(shr(18, buffer), 0x3F))))
resultPtr := add(resultPtr, 1)

mstore8(resultPtr, mload(add(tablePtr, and(shr(12, buffer), 0x3F))))
resultPtr := add(resultPtr, 1)

mstore8(resultPtr, mload(add(tablePtr, and(shr(6, buffer), 0x3F))))
resultPtr := add(resultPtr, 1)

mstore8(resultPtr, mload(add(tablePtr, and(buffer, 0x3F))))
resultPtr := add(resultPtr, 1)

// Because the input is fixed 32 bytes long
resultPtr := sub(resultPtr, 1)
mstore(encodedChallenge, sub(resultPtr, add(encodedChallenge, 32)))
}

/* solhint-disable quotes */
bytes memory clientDataJson = abi.encodePacked(
'{"type":"webauthn.get","challenge":"',
Expand Down

0 comments on commit eb111a4

Please sign in to comment.