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

Refactor Contract Layout #319

Merged
merged 1 commit into from
Mar 18, 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
11 changes: 6 additions & 5 deletions modules/passkey/contracts/4337/Safe256BitECSignerLaunchpad.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ import {IAccount} from "@account-abstraction/contracts/interfaces/IAccount.sol";
import {PackedUserOperation} from "@account-abstraction/contracts/interfaces/PackedUserOperation.sol";
import {_packValidationData} from "@account-abstraction/contracts/core/Helpers.sol";
import {SafeStorage} from "@safe-global/safe-contracts/contracts/libraries/SafeStorage.sol";
import {SignatureValidatorConstants} from "../SignatureValidatorConstants.sol";

import {ICustom256BitECSignerFactory} from "../interfaces/ICustomSignerFactory.sol";
import {ISafeSetup} from "../interfaces/ISafe.sol";
import {ISafe} from "../interfaces/ISafe.sol";
import {ERC1271} from "../libraries/ERC1271.sol";

/**
* @title SafeOpLaunchpad - A contract for Safe initialization with custom unique signers that would violate ERC-4337 factory rules.
* @dev The is intended to be set as a Safe proxy's implementation for ERC-4337 user operation that deploys the account.
*/
contract Safe256BitECSignerLaunchpad is IAccount, SafeStorage, SignatureValidatorConstants {
contract Safe256BitECSignerLaunchpad is IAccount, SafeStorage {
bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");

// keccak256("SafeSignerLaunchpad.initHash") - 1
Expand Down Expand Up @@ -197,7 +198,7 @@ contract Safe256BitECSignerLaunchpad is IAccount, SafeStorage, SignatureValidato
)
returns (bytes4 magicValue) {
// The timestamps are validated by the entry point, therefore we will not check them again
validationData = _packValidationData(magicValue != EIP1271_MAGIC_VALUE, validUntil, validAfter);
validationData = _packValidationData(magicValue != ERC1271.MAGIC_VALUE, validUntil, validAfter);
} catch {
validationData = _packValidationData(true, validUntil, validAfter);
}
Expand All @@ -219,7 +220,7 @@ contract Safe256BitECSignerLaunchpad is IAccount, SafeStorage, SignatureValidato
address[] memory owners = new address[](1);
owners[0] = ICustom256BitECSignerFactory(signerFactory).createSigner(signerX, signerY, signerVerifier);

ISafeSetup(address(this)).setup(owners, 1, setupTo, setupData, fallbackHandler, address(0), 0, payable(address(0)));
ISafe(address(this)).setup(owners, 1, setupTo, setupData, fallbackHandler, address(0), 0, payable(address(0)));
}

(bool success, bytes memory returnData) = address(this).delegatecall(callData);
Expand Down
15 changes: 0 additions & 15 deletions modules/passkey/contracts/SignatureValidatorConstants.sol

This file was deleted.

126 changes: 12 additions & 114 deletions modules/passkey/contracts/WebAuthnSigner.sol
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;
Expand All @@ -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
);
}
}
86 changes: 86 additions & 0 deletions modules/passkey/contracts/WebAuthnSignerFactory.sol
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;
}
}
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.
Expand All @@ -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;
}
}

Expand All @@ -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;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand Down Expand Up @@ -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 {
/**
Expand Down
34 changes: 34 additions & 0 deletions modules/passkey/contracts/interfaces/IP256Verifier.sol
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);
}
2 changes: 1 addition & 1 deletion modules/passkey/contracts/interfaces/ISafe.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.8.0 <0.9.0;

interface ISafeSetup {
interface ISafe {
function setup(
address[] calldata _owners,
uint256 _threshold,
Expand Down
Loading
Loading