Skip to content

Commit

Permalink
✨ Add the options to upgrade to the RIP-7212 p256 verifier (#82)
Browse files Browse the repository at this point in the history
* ✨ Add the options to upgrade to the RIP-7212 p256 verifier

* 🐛 Handle empty response from the pre compiled p256 verifier

* 🐛 Switch between pre-compiled and on chain p256 verifier via a signature flag
  • Loading branch information
KONFeature authored Feb 16, 2024
1 parent 78b2a0e commit 10cd5f0
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 10 deletions.
25 changes: 22 additions & 3 deletions src/validator/webauthn/WebAuthnFclValidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ contract WebAuthnFclValidator is IKernelValidator {
/// @dev Mapping of kernel address to each webAuthn specific storage
mapping(address kernel => WebAuthnFclValidatorStorage webAuthnStorage) private webAuthnValidatorStorage;

/// @dev The address of the p256 verifier contract (should be 0x100 on the RIP-7212 compliant chains)
/// @dev To follow up for the deployment: https://forum.polygon.technology/t/pip-27-precompiled-for-secp256r1-curve-support/13049
address public immutable P256_VERIFIER;
/// @dev The address of the on-chain p256 verifier contract (will be used if the user want that instead of the pre-compiled one, that way this validator can work on every chain out of the box while rip7212 is slowly being implemented everywhere)
address private immutable P256_VERIFIER;

/// @dev Simple constructor, setting the P256 verifier address
constructor(address _p256Verifier) {
Expand Down Expand Up @@ -98,6 +97,7 @@ contract WebAuthnFclValidator is IKernelValidator {
bytes32 _hash,
bytes calldata _signature
) private view returns (bool isValid) {
// Extract the first byte of the signature to check
return WebAuthnFclVerifier._verifyWebAuthNSignature(
P256_VERIFIER, _hash, _signature, _kernelValidatorStorage.x, _kernelValidatorStorage.y
);
Expand All @@ -121,4 +121,23 @@ contract WebAuthnFclValidator is IKernelValidator {
x = kernelValidatorStorage.x;
y = kernelValidatorStorage.y;
}

/// @dev Check if the pre-compiled p256 verifier is available on this chain
function isPreCompiledP256Available() public view returns (bool) {
// Test signature data, from https://gist.github.com/ulerdogan/8f1714895e23a54147fc529ea30517eb
bytes memory testSignatureData =
hex"4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e";

// Perform the static call
(bool success, bytes memory data) = WebAuthnFclVerifier.PRECOMPILED_P256_VERIFIER.staticcall(testSignatureData);
if (!success || data.length == 0) {
return false;
}

// Decode the result
uint256 result = abi.decode(data, (uint256));

// Check it's 1 (valid signature)
return result == uint256(1);
}
}
16 changes: 14 additions & 2 deletions src/validator/webauthn/WebAuthnFclVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ library WebAuthnFclVerifier {
/// @dev Always 0x01 for user presence flag -> https://www.w3.org/TR/webauthn-2/#concept-user-present
bytes1 private constant AUTHENTICATOR_DATA_FLAG_MASK = 0x01;

/// @dev The address of the pre-compiled p256 verifier contract (following RIP-7212)
address internal constant PRECOMPILED_P256_VERIFIER = address(0x100);

/// @dev layout of a signature (used to extract the reauired payload from the initial calldata)
struct FclSignatureLayout {
bool useOnChainP256Verifier;
bytes authenticatorData;
bytes clientData;
uint256 challengeOffset;
Expand Down Expand Up @@ -103,7 +107,7 @@ library WebAuthnFclVerifier {
}

/// @dev Proceed to the full webauth verification
/// @param _p256Verifier The p256 verifier contract
/// @param _p256Verifier The p256 verifier contract on-chain (if user want to use this instead of the precompiled one)
/// @param _hash The hash that has been signed via WebAuthN
/// @param _signature The signature that has been provided with the userOp
/// @param _x The X point of the public key
Expand All @@ -124,6 +128,11 @@ library WebAuthnFclVerifier {
signature := _signature.offset
}

// If the signature is using the on-chain p256 verifier, we will use it
if (!signature.useOnChainP256Verifier) {
_p256Verifier = PRECOMPILED_P256_VERIFIER;
}

// Format the webauthn challenge into a p256 message
bytes32 challenge = _formatWebAuthNChallenge(_hash, signature);

Expand All @@ -132,7 +141,10 @@ library WebAuthnFclVerifier {

// Send the call the the p256 verifier
(bool success, bytes memory ret) = _p256Verifier.staticcall(args);
assert(success); // never reverts, always returns 0 or 1
// If empty ret, return false
if (success == false || ret.length == 0) {
return false;
}

// Ensure that it has returned 1
return abi.decode(ret, (uint256)) == 1;
Expand Down
11 changes: 6 additions & 5 deletions test/foundry/validator/WebAuthnFclValidator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ contract WebAuthnFclValidatorTest is KernelTestBase {
uint256[2] memory rs = [type(uint256).max, type(uint256).max];

// Encode all of that into a signature
bytes memory signature = abi.encode(authenticatorData, clientData, clientChallengeDataOffset, rs);
bytes memory signature = abi.encode(true, authenticatorData, clientData, clientChallengeDataOffset, rs);

// Check the sig (and ensure we didn't revert here)
bool isValid = webAuthNTester.verifySignature(address(p256VerifierWrapper), bytes32(0), signature, x, y);
Expand All @@ -234,7 +234,7 @@ contract WebAuthnFclValidatorTest is KernelTestBase {
uint256[2] memory rs = [r, s];

// Encode all of that into a signature
bytes memory signature = abi.encode(authenticatorData, clientData, clientChallengeDataOffset, rs);
bytes memory signature = abi.encode(true, authenticatorData, clientData, clientChallengeDataOffset, rs);

// Ensure the signature is valid
bool isValid = webAuthNTester.verifySignature(address(p256VerifierWrapper), _hash, signature, pubX, pubY);
Expand All @@ -256,7 +256,7 @@ contract WebAuthnFclValidatorTest is KernelTestBase {
uint256[2] memory rs = [r, s];

// Encode all of that into a signature
bytes memory signature = abi.encode(authenticatorData, clientData, clientChallengeDataOffset, rs);
bytes memory signature = abi.encode(true, authenticatorData, clientData, clientChallengeDataOffset, rs);

// Ensure the signature is valid
bool isValid = webAuthNTester.verifySignature(address(p256VerifierWrapper), _hash, signature, pubX, pubY);
Expand All @@ -281,7 +281,7 @@ contract WebAuthnFclValidatorTest is KernelTestBase {
uint256[2] memory rs = [r, s];

// Return the signature
return abi.encode(authenticatorData, clientData, clientChallengeDataOffset, rs);
return abi.encode(true, authenticatorData, clientData, clientChallengeDataOffset, rs);
}

/// @dev Prepare all the base data needed to perform a webauthn signature o n the given `_hash`
Expand Down Expand Up @@ -310,6 +310,7 @@ contract WebAuthnFclValidatorTest is KernelTestBase {

// Build the signature layout
WebAuthnFclVerifier.FclSignatureLayout memory sigLayout = WebAuthnFclVerifier.FclSignatureLayout({
useOnChainP256Verifier: true,
authenticatorData: authenticatorData,
clientData: clientData,
challengeOffset: clientChallengeDataOffset,
Expand All @@ -330,7 +331,7 @@ contract WebAuthnFclValidatorTest is KernelTestBase {
uint256 constant P256_N_DIV_2 = 57896044605178124381348723474703786764998477612067880171211129530534256022184;

/// @dev Generate a p256 signature, from the given `_privateKey` on the given `_hash`
function _getP256Signature(uint256 _privateKey, bytes32 _hash) internal view returns (uint256, uint256) {
function _getP256Signature(uint256 _privateKey, bytes32 _hash) internal pure returns (uint256, uint256) {
// Generate the signature using the k value and the private key
(bytes32 r, bytes32 s) = vm.signP256(_privateKey, _hash);
return (uint256(r), uint256(s));
Expand Down

0 comments on commit 10cd5f0

Please sign in to comment.