p256
Overview
The P256 precompile provides native support for verifying secp256r1 (P-256) elliptic curve signatures, implementing EIP-7212. This enables smart contracts to verify signatures from WebAuthn authenticators, secure hardware modules, and other systems using the P-256 curve.
Address: 0x0000000000000000000000000000000000000100
Related Standards: EIP-7212
Gas Costs
Fixed cost: 3,450 gas
Method
Signature Verification
The precompile exposes a single unnamed function that verifies P-256 signatures.
Input Format (160 bytes):
- Bytes 0-31:
message_hash
(32 bytes) - The hash of the message - Bytes 32-63:
r
(32 bytes) - The r component of the signature - Bytes 64-95:
s
(32 bytes) - The s component of the signature - Bytes 96-127:
x
(32 bytes) - The x coordinate of the public key - Bytes 128-159:
y
(32 bytes) - The y coordinate of the public key
Output Format (32 bytes):
- Returns
0x0000...0001
(1) if signature is valid - Returns
0x0000...0000
(0) if signature is invalid
Example Usage
// P256 signature verification
address constant P256_PRECOMPILE = 0x0000000000000000000000000000000000000100;
function verifyP256Signature(
bytes32 messageHash,
bytes32 r,
bytes32 s,
bytes32 x,
bytes32 y
) external view returns (bool) {
bytes memory input = abi.encodePacked(messageHash, r, s, x, y);
(bool success, bytes memory result) = P256_PRECOMPILE.staticcall(input);
if (!success || result.length != 32) {
return false;
}
return uint256(bytes32(result)) == 1;
}
const ethers = require('ethers');
// P256 precompile address
const P256_ADDRESS = '0x0000000000000000000000000000000000000100';
async function verifyP256Signature(provider, messageHash, r, s, x, y) {
// Encode the input data
const input = ethers.utils.concat([
messageHash,
r,
s,
x,
y
]);
// Call the precompile
const result = await provider.call({
to: P256_ADDRESS,
data: input
});
// Check if signature is valid (result should be 0x00...01)
return result === '0x' + '00'.repeat(31) + '01';
}
Implementation Details
Curve Parameters
The precompile uses the secp256r1 (NIST P-256) elliptic curve with the following parameters:
- Field prime:
p = 2^256 - 2^224 + 2^192 + 2^96 - 1
- Curve equation:
y² = x³ + ax + b
where:a = -3
b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b
Input Validation
The precompile performs the following validations:
- Input must be exactly 160 bytes
- Public key coordinates (x, y) must be valid points on the curve
- Signature components (r, s) must be within the valid range [1, n-1] where n is the curve order
Security Considerations
- The precompile only verifies that a signature is mathematically valid for the given public key
- Applications must implement additional checks such as:
- Public key authentication (e.g., WebAuthn credential verification)
- Message format validation
- Replay attack prevention
Use Cases
WebAuthn Integration
The P256 precompile enables smart contracts to verify WebAuthn assertions, allowing for:
- Passwordless authentication
- Hardware security key support
- Biometric authentication via compatible devices
Secure Hardware Modules
Many secure elements and hardware security modules use P-256 for signing operations:
- Apple Secure Enclave
- Android Keystore (when configured for P-256)
- TPM 2.0 modules
- Smart cards
Example: WebAuthn Verification
contract WebAuthnWallet {
using bytes for bytes;
struct Credential {
bytes32 credentialId;
uint256 publicKeyX;
uint256 publicKeyY;
}
mapping(address => Credential) public credentials;
function verify(
bytes calldata authenticatorData,
bytes calldata clientDataJSON,
bytes32 r,
bytes32 s
) external view returns (bool) {
Credential memory cred = credentials[msg.sender];
// Compute challenge hash according to WebAuthn spec
bytes32 clientDataHash = sha256(clientDataJSON);
bytes32 messageHash = sha256(abi.encodePacked(authenticatorData, clientDataHash));
// Verify P-256 signature
bytes memory input = abi.encodePacked(
messageHash,
r,
s,
bytes32(cred.publicKeyX),
bytes32(cred.publicKeyY)
);
(bool success, bytes memory result) = address(0x100).staticcall(input);
return success && result.length == 32 && uint256(bytes32(result)) == 1;
}
}
Gas Optimization
Since the gas cost is fixed at 3,450, optimizations should focus on:
- Minimizing the number of signature verifications
- Batch processing where possible
- Caching verification results when appropriate