Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add the options to upgrade to the RIP-7212 p256 verifier #82

Merged
merged 3 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading