canvas-website/contracts/WebCryptoProxy.sol

159 lines
6.1 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title WebCryptoProxy
* @notice Minimal proxy contract that verifies Web Crypto API (P-256) signatures
* and executes transactions. Designed to minimize gas costs and complexity.
* @dev Each user deploys their own proxy contract, which stores their Web Crypto public key
* and verifies P-256 signatures before executing transactions.
*/
contract WebCryptoProxy {
// Web Crypto P-256 public key (stored as bytes32 for gas efficiency)
// P-256 public keys are 65 bytes (0x04 || 32-byte X || 32-byte Y)
// We store the X coordinate (32 bytes) and Y coordinate (32 bytes) separately
bytes32 public publicKeyX;
bytes32 public publicKeyY;
// Replay protection: nonce tracking
mapping(uint256 => bool) public usedNonces;
// Events
event TransactionExecuted(
address indexed to,
uint256 value,
bytes data,
uint256 nonce
);
event PublicKeySet(bytes32 indexed publicKeyX, bytes32 indexed publicKeyY);
/**
* @notice Constructor sets the Web Crypto public key
* @param _publicKeyX X coordinate of P-256 public key (32 bytes)
* @param _publicKeyY Y coordinate of P-256 public key (32 bytes)
*/
constructor(bytes32 _publicKeyX, bytes32 _publicKeyY) {
publicKeyX = _publicKeyX;
publicKeyY = _publicKeyY;
emit PublicKeySet(_publicKeyX, _publicKeyY);
}
/**
* @notice Execute a transaction if signature is valid
* @param to Target address for the transaction
* @param value Amount of ETH to send (in wei)
* @param data Transaction data (contract call data)
* @param nonce Unique nonce for replay protection
* @param deadline Transaction expiration timestamp
* @param signature Web Crypto P-256 signature (r, s, v format)
* @dev Signature verification uses P-256 curve, not secp256k1
* This requires a custom verification library or precompile
*/
function execute(
address to,
uint256 value,
bytes calldata data,
uint256 nonce,
uint256 deadline,
bytes calldata signature
) external {
// Check deadline
require(block.timestamp <= deadline, "WebCryptoProxy: Transaction expired");
// Check nonce hasn't been used
require(!usedNonces[nonce], "WebCryptoProxy: Nonce already used");
// Mark nonce as used
usedNonces[nonce] = true;
// Verify signature
// Note: P-256 signature verification requires a library or precompile
// For now, this is a placeholder - you'll need to implement or import
// a P-256 verification function
bytes32 messageHash = keccak256(
abi.encodePacked(
"\x19\x01", // EIP-712 prefix
keccak256(abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes("WebCryptoProxy")),
keccak256(bytes("1")),
block.chainid,
address(this)
)),
keccak256(abi.encode(
keccak256("Transaction(address to,uint256 value,bytes data,uint256 nonce,uint256 deadline)"),
to,
value,
keccak256(data),
nonce,
deadline
))
)
);
// TODO: Verify P-256 signature
// This requires a P-256 signature verification library
// Options:
// 1. Use a precompile (if available on your chain)
// 2. Import a P-256 verification library
// 3. Use a verification contract
// For now, we'll use a simplified check
// In production, replace this with proper P-256 verification
require(verifyP256Signature(messageHash, signature), "WebCryptoProxy: Invalid signature");
// Execute the transaction
(bool success, ) = to.call{value: value}(data);
require(success, "WebCryptoProxy: Transaction failed");
emit TransactionExecuted(to, value, data, nonce);
}
/**
* @notice Verify P-256 signature
* @dev This is a placeholder - implement with proper P-256 verification
* You can use libraries like:
* - A precompile if your chain supports it
* - A P-256 verification library (e.g., from OpenZeppelin or custom)
* - An external verification contract
* @param messageHash The message hash to verify
* @param signature The signature (needs to be parsed for r, s, v)
* @return true if signature is valid
*/
function verifyP256Signature(
bytes32 messageHash,
bytes calldata signature
) internal view returns (bool) {
// TODO: Implement P-256 signature verification
// For now, this is a placeholder that always returns false for safety
// In production, you must implement proper P-256 verification
// Example structure (you'll need to adapt based on your verification method):
// 1. Parse signature to extract r, s, v (or r, s for P-256)
// 2. Recover public key from signature
// 3. Compare recovered public key with stored publicKeyX and publicKeyY
// 4. Return true if they match
// Placeholder: This will reject all signatures until properly implemented
// Remove this and implement actual verification
revert("WebCryptoProxy: P-256 verification not yet implemented");
// Uncomment and implement when you have P-256 verification:
// bytes32 r = ...;
// bytes32 s = ...;
// (bytes32 recoveredX, bytes32 recoveredY) = recoverP256PublicKey(messageHash, r, s);
// return (recoveredX == publicKeyX && recoveredY == publicKeyY);
}
/**
* @notice Receive ETH
*/
receive() external payable {}
/**
* @notice Fallback function
*/
fallback() external payable {}
}