Skip to content

Commit

Permalink
🐛 Switch between pre-compiled and on chain p256 verifier via a signat…
Browse files Browse the repository at this point in the history
…ure flag
  • Loading branch information
KONFeature committed Feb 14, 2024
1 parent 4afd3fe commit d8d6f59
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 47 deletions.
45 changes: 4 additions & 41 deletions src/validator/webauthn/WebAuthnFclValidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,18 @@ struct WebAuthnFclValidatorStorage {
/// @notice Using the awesome FreshCryptoLib: https://github.com/rdubois-crypto/FreshCryptoLib/
/// @notice Inspired by the cometh Gnosis Safe signer: https://github.com/cometh-game/p256-signer
contract WebAuthnFclValidator is IKernelValidator {
/// @dev Error throwned if someone is trying to update to the precompiled p256 verifier, but it's not yet enable on the current chain
error Rip7212NotavailableOnThisChain();
/// @dev Error throwned if the pre-compiled p256 verifier is already setup
error Rip7212AlreadyEnabled();

/// @dev Event emitted when the p256 verifier is changed
event P256VerifierChanged(address newP256Verifier);

/// @dev Event emitted when the public key signing the WebAuthN user operation is changed for a given `kernel`.
event WebAuthnPublicKeyChanged(address indexed kernel, uint256 x, uint256 y);

/// @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
/// @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 The address of the pre-compiled p256 verifier
address constant P256_VERIFIER_PRECOMPILED = address(0x100);

/// @dev The current p256 verifier
address private currentP256Verifier;

/// @dev Simple constructor, setting the P256 verifier address
constructor(address _p256Verifier) {
P256_VERIFIER = _p256Verifier;
currentP256Verifier = _p256Verifier;
}

/// @dev Disable this validator for a given `kernel` (msg.sender)
Expand Down Expand Up @@ -113,8 +97,9 @@ 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(
currentP256Verifier, _hash, _signature, _kernelValidatorStorage.x, _kernelValidatorStorage.y
P256_VERIFIER, _hash, _signature, _kernelValidatorStorage.x, _kernelValidatorStorage.y
);
}

Expand All @@ -123,23 +108,6 @@ contract WebAuthnFclValidator is IKernelValidator {
revert NotImplemented();
}

/// @dev Update the p256 verifier address if the pre-compiled version is available
function updateToPrecompiledP256() external {
// Early exit if already enabled
if (currentP256Verifier == P256_VERIFIER_PRECOMPILED) {
revert Rip7212AlreadyEnabled();
}

// Check if it's available, if not exit
if (!isPreCompiledP256Available()) {
revert Rip7212NotavailableOnThisChain();
}

// Update the current p256 verifier
emit P256VerifierChanged(P256_VERIFIER_PRECOMPILED);
currentP256Verifier = P256_VERIFIER_PRECOMPILED;
}

/* -------------------------------------------------------------------------- */
/* Public view methods */
/* -------------------------------------------------------------------------- */
Expand All @@ -161,7 +129,7 @@ contract WebAuthnFclValidator is IKernelValidator {
hex"4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e";

// Perform the static call
(bool success, bytes memory data) = P256_VERIFIER_PRECOMPILED.staticcall(testSignatureData);
(bool success, bytes memory data) = WebAuthnFclVerifier.PRECOMPILED_P256_VERIFIER.staticcall(testSignatureData);
if (!success || data.length == 0) {
return false;
}
Expand All @@ -172,9 +140,4 @@ contract WebAuthnFclValidator is IKernelValidator {
// Check it's 1 (valid signature)
return result == uint256(1);
}

/// @dev Get the current p256 verifier address
function getCurrentP256Verifier() public view returns (address) {
return currentP256Verifier;
}
}
11 changes: 10 additions & 1 deletion 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 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 d8d6f59

Please sign in to comment.