-
Notifications
You must be signed in to change notification settings - Fork 86
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
361 additions
and
292 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,15 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
/* solhint-disable one-contract-per-file */ | ||
pragma solidity >=0.8.0; | ||
|
||
import {SignatureValidatorConstants} from "./SignatureValidatorConstants.sol"; | ||
import {ICustom256BitECSignerFactory} from "./interfaces/ICustomSignerFactory.sol"; | ||
import {SignatureValidator} from "./SignatureValidator.sol"; | ||
import {IWebAuthnVerifier, WebAuthnConstants} from "./verifiers/WebAuthnVerifier.sol"; | ||
|
||
struct SignatureData { | ||
bytes authenticatorData; | ||
bytes clientDataFields; | ||
uint256[2] rs; | ||
} | ||
import {SignatureValidator} from "./base/SignatureValidator.sol"; | ||
import {IWebAuthnVerifier} from "./interfaces/IWebAuthnVerifier.sol"; | ||
import {WebAuthnFlags} from "./libraries/WebAuthnFlags.sol"; | ||
import {WebAuthnSignature} from "./libraries/WebAuthnSignature.sol"; | ||
|
||
/** | ||
* @title WebAuthnSigner | ||
* @title WebAuthn Safe Signature Validator | ||
* @dev A contract that represents a WebAuthn signer. | ||
* @custom:security-contact [email protected] | ||
*/ | ||
contract WebAuthnSigner is SignatureValidator { | ||
uint256 public immutable X; | ||
|
@@ -38,114 +32,18 @@ contract WebAuthnSigner is SignatureValidator { | |
* @inheritdoc SignatureValidator | ||
*/ | ||
function _verifySignature(bytes32 message, bytes calldata signature) internal view virtual override returns (bool isValid) { | ||
SignatureData calldata signaturePointer; | ||
// solhint-disable-next-line no-inline-assembly | ||
assembly ("memory-safe") { | ||
signaturePointer := signature.offset | ||
} | ||
WebAuthnSignature.Data calldata data = WebAuthnSignature.cast(signature); | ||
|
||
return | ||
WEBAUTHN_SIG_VERIFIER.verifyWebAuthnSignatureAllowMalleability( | ||
signaturePointer.authenticatorData, | ||
WebAuthnConstants.AUTH_DATA_FLAGS_UV, | ||
data.authenticatorData, | ||
WebAuthnFlags.USER_VERIFICATION, | ||
message, | ||
signaturePointer.clientDataFields, | ||
signaturePointer.rs, | ||
data.clientDataFields, | ||
data.r, | ||
data.s, | ||
X, | ||
Y | ||
); | ||
} | ||
} | ||
|
||
/** | ||
* @title WebAuthnSignerFactory | ||
* @dev A factory contract for creating and managing WebAuthn signers. | ||
*/ | ||
contract WebAuthnSignerFactory is ICustom256BitECSignerFactory, SignatureValidatorConstants { | ||
// @inheritdoc ICustom256BitECSignerFactory | ||
function getSigner(uint256 qx, uint256 qy, address verifier) public view override returns (address signer) { | ||
bytes32 codeHash = keccak256(abi.encodePacked(type(WebAuthnSigner).creationCode, qx, qy, uint256(uint160(verifier)))); | ||
signer = address(uint160(uint256(keccak256(abi.encodePacked(hex"ff", address(this), bytes32(0), codeHash))))); | ||
} | ||
|
||
// @inheritdoc ICustom256BitECSignerFactory | ||
function createSigner(uint256 qx, uint256 qy, address verifier) external returns (address signer) { | ||
signer = getSigner(qx, qy, verifier); | ||
|
||
if (_hasNoCode(signer) && _validVerifier(verifier)) { | ||
WebAuthnSigner created = new WebAuthnSigner{salt: bytes32(0)}(qx, qy, verifier); | ||
require(address(created) == signer); | ||
} | ||
} | ||
|
||
/** | ||
* @dev Checks if the given verifier address contains code. | ||
* @param verifier The address of the verifier to check. | ||
* @return A boolean indicating whether the verifier contains code or not. | ||
*/ | ||
function _validVerifier(address verifier) internal view returns (bool) { | ||
// The verifier should contain code (The only way to implement a webauthn verifier is with a smart contract) | ||
return !_hasNoCode(verifier); | ||
} | ||
|
||
// @inheritdoc ICustom256BitECSignerFactory | ||
function isValidSignatureForSigner( | ||
uint256 qx, | ||
uint256 qy, | ||
address verifier, | ||
bytes32 message, | ||
bytes calldata signature | ||
) external view override returns (bytes4 magicValue) { | ||
if (checkSignature(verifier, message, signature, qx, qy)) { | ||
magicValue = EIP1271_MAGIC_VALUE; | ||
} | ||
} | ||
|
||
/** | ||
* @dev Checks if the provided account has no code. | ||
* @param account The address of the account to check. | ||
* @return True if the account has no code, false otherwise. | ||
*/ | ||
function _hasNoCode(address account) internal view returns (bool) { | ||
uint256 size; | ||
// solhint-disable-next-line no-inline-assembly | ||
assembly ("memory-safe") { | ||
size := extcodesize(account) | ||
} | ||
return size == 0; | ||
} | ||
|
||
/** | ||
* @dev Checks the validity of a signature using WebAuthnVerifier. | ||
* @param verifier The address of the WebAuthnVerifier contract. | ||
* @param dataHash The hash of the data being signed. | ||
* @param signature The signature to be verified. | ||
* @param qx The x-coordinate of the public key. | ||
* @param qy The y-coordinate of the public key. | ||
* @return A boolean indicating whether the signature is valid or not. | ||
*/ | ||
function checkSignature( | ||
address verifier, | ||
bytes32 dataHash, | ||
bytes calldata signature, | ||
uint256 qx, | ||
uint256 qy | ||
) internal view returns (bool) { | ||
SignatureData calldata signaturePointer; | ||
// solhint-disable-next-line no-inline-assembly | ||
assembly ("memory-safe") { | ||
signaturePointer := signature.offset | ||
} | ||
|
||
return | ||
IWebAuthnVerifier(verifier).verifyWebAuthnSignatureAllowMalleability( | ||
signaturePointer.authenticatorData, | ||
WebAuthnConstants.AUTH_DATA_FLAGS_UV, | ||
dataHash, | ||
signaturePointer.clientDataFields, | ||
signaturePointer.rs, | ||
qx, | ||
qy | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
pragma solidity >=0.8.0; | ||
|
||
import {ICustom256BitECSignerFactory} from "./interfaces/ICustomSignerFactory.sol"; | ||
import {IWebAuthnVerifier} from "./interfaces/IWebAuthnVerifier.sol"; | ||
import {ERC1271} from "./libraries/ERC1271.sol"; | ||
import {WebAuthnFlags} from "./libraries/WebAuthnFlags.sol"; | ||
import {WebAuthnSignature} from "./libraries/WebAuthnSignature.sol"; | ||
import {WebAuthnSigner} from "./WebAuthnSigner.sol"; | ||
|
||
/** | ||
* @title WebAuthnSignerFactory | ||
* @dev A factory contract for creating and managing WebAuthn signers. | ||
*/ | ||
contract WebAuthnSignerFactory is ICustom256BitECSignerFactory { | ||
// @inheritdoc ICustom256BitECSignerFactory | ||
function getSigner(uint256 qx, uint256 qy, address verifier) public view override returns (address signer) { | ||
bytes32 codeHash = keccak256(abi.encodePacked(type(WebAuthnSigner).creationCode, qx, qy, uint256(uint160(verifier)))); | ||
signer = address(uint160(uint256(keccak256(abi.encodePacked(hex"ff", address(this), bytes32(0), codeHash))))); | ||
} | ||
|
||
// @inheritdoc ICustom256BitECSignerFactory | ||
function createSigner(uint256 qx, uint256 qy, address verifier) external returns (address signer) { | ||
signer = getSigner(qx, qy, verifier); | ||
|
||
if (_hasNoCode(signer) && _validVerifier(verifier)) { | ||
WebAuthnSigner created = new WebAuthnSigner{salt: bytes32(0)}(qx, qy, verifier); | ||
require(address(created) == signer); | ||
} | ||
} | ||
|
||
// @inheritdoc ICustom256BitECSignerFactory | ||
function isValidSignatureForSigner( | ||
uint256 qx, | ||
uint256 qy, | ||
address verifier, | ||
bytes32 message, | ||
bytes calldata signature | ||
) external view override returns (bytes4 magicValue) { | ||
WebAuthnSignature.Data calldata data = WebAuthnSignature.cast(signature); | ||
|
||
// Work around stack-too-deep issues by helping out the compiler figure out how to re-order | ||
// the stack. | ||
uint256 x = qx; | ||
uint256 y = qy; | ||
|
||
if ( | ||
IWebAuthnVerifier(verifier).verifyWebAuthnSignatureAllowMalleability( | ||
data.authenticatorData, | ||
WebAuthnFlags.USER_VERIFICATION, | ||
message, | ||
data.clientDataFields, | ||
data.r, | ||
data.s, | ||
x, | ||
y | ||
) | ||
) { | ||
magicValue = ERC1271.MAGIC_VALUE; | ||
} | ||
} | ||
|
||
/** | ||
* @dev Checks if the given verifier address contains code. | ||
* @param verifier The address of the verifier to check. | ||
* @return A boolean indicating whether the verifier contains code or not. | ||
*/ | ||
function _validVerifier(address verifier) internal view returns (bool) { | ||
// The verifier should contain code (The only way to implement a webauthn verifier is with a smart contract) | ||
return !_hasNoCode(verifier); | ||
} | ||
|
||
/** | ||
* @dev Checks if the provided account has no code. | ||
* @param account The address of the account to check. | ||
* @return True if the account has no code, false otherwise. | ||
*/ | ||
function _hasNoCode(address account) internal view returns (bool) { | ||
uint256 size; | ||
// solhint-disable-next-line no-inline-assembly | ||
assembly ("memory-safe") { | ||
size := extcodesize(account) | ||
} | ||
return size == 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
pragma solidity >=0.8.0; | ||
|
||
import {SignatureValidatorConstants} from "./SignatureValidatorConstants.sol"; | ||
import {ERC1271} from "../libraries/ERC1271.sol"; | ||
|
||
/** | ||
* @title ISafeSigner | ||
* @dev A interface for smart contract Safe owners that supports multiple `isValidSignature` versions. | ||
* @title Signature Validator Base Contract | ||
* @dev A interface for smart contract Safe owners that supports multiple ERC-1271 `isValidSignature` versions. | ||
* @custom:security-contact [email protected] | ||
*/ | ||
abstract contract SignatureValidator is SignatureValidatorConstants { | ||
abstract contract SignatureValidator { | ||
/** | ||
* @dev Validates the signature for the given data. | ||
* @param data The signed data bytes. | ||
|
@@ -16,7 +17,7 @@ abstract contract SignatureValidator is SignatureValidatorConstants { | |
*/ | ||
function isValidSignature(bytes memory data, bytes calldata signature) external view returns (bytes4 magicValue) { | ||
if (_verifySignature(keccak256(data), signature)) { | ||
magicValue = LEGACY_EIP1271_MAGIC_VALUE; | ||
magicValue = ERC1271.LEGACY_MAGIC_VALUE; | ||
} | ||
} | ||
|
||
|
@@ -28,7 +29,7 @@ abstract contract SignatureValidator is SignatureValidatorConstants { | |
*/ | ||
function isValidSignature(bytes32 message, bytes calldata signature) external view returns (bytes4 magicValue) { | ||
if (_verifySignature(message, signature)) { | ||
magicValue = EIP1271_MAGIC_VALUE; | ||
magicValue = ERC1271.MAGIC_VALUE; | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,8 @@ pragma solidity >=0.8.0 <0.9.0; | |
/** | ||
* @title ICustomECSignerFactory | ||
* @dev Interface for creating and verifying ECDSA signers. This is a generalized interface that should be | ||
* compatible with curves of any order size. Currently not used in the project and exists here for reference. | ||
* compatible with curves of any order size. Currently not used in the project and exists here for reference. | ||
* @custom:security-contact [email protected] | ||
*/ | ||
interface ICustomECSignerFactory { | ||
/** | ||
|
@@ -42,6 +43,7 @@ interface ICustomECSignerFactory { | |
/** | ||
* @title ICustom256BitECSignerFactory | ||
* @dev Interface for creating and verifying ECDSA signers using 256-bit elliptic curves. | ||
* @custom:security-contact [email protected] | ||
*/ | ||
interface ICustom256BitECSignerFactory { | ||
/** | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
/* solhint-disable payable-fallback */ | ||
pragma solidity ^0.8.0; | ||
|
||
/** | ||
* @title P-256 Elliptic Curve Verifier. | ||
* @dev P-256 verifier contract that follows the EIP-7212 EC verify precompile interface. For more | ||
* details, refer to the EIP-7212 specification: <https://eips.ethereum.org/EIPS/eip-7212> | ||
* @custom:security-contact [email protected] | ||
*/ | ||
interface IP256Verifier { | ||
/** | ||
* @notice A fallback function that takes the following input format and returns a result | ||
* indicating whether the signature is valid or not: | ||
* - `input[ 0: 32]`: message | ||
* - `input[ 32: 64]`: signature r | ||
* - `input[ 64: 96]`: signature s | ||
* - `input[ 96:128]`: public key x | ||
* - `input[128:160]`: public key y | ||
* | ||
* The output is a Solidity ABI encoded boolean value indicating whether or not the signature is | ||
* valid. Specifically, it returns 32 bytes with a value of `0x00..00` or `0x00..01` for an | ||
* invalid or valid signature respectively. | ||
* | ||
* Note that this function does not follow the Solidity ABI format (in particular, it does not | ||
* have a 4-byte selector), which is why it requires a fallback function and not regular | ||
* Solidity function. Additionally, it has `view` function semantics, and is expected to be | ||
* called with `STATICCALL` opcode. | ||
* | ||
* @param input The encoded input parameters. | ||
* @return output The encoded signature verification result. | ||
*/ | ||
fallback(bytes calldata input) external returns (bytes memory output); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.