> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sei.io/llms.txt
> Use this file to discover all available pages before exploring further.

# P256 Precompile

> Use the P256 precompile to verify secp256r1/P-256 signatures directly from EVM smart contracts, enabling efficient passkey-based authentication and signature verification.

**Address**: `0x0000000000000000000000000000000000001011`

This is the implementation of [RIP-7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md), which provides a precompiled contract for verifying signatures in the `secp256r1` or `P-256` elliptic curve.

## Overview

The P256 precompile enables efficient signature verification for the `secp256r1` curve, which is widely used in modern security systems including:

* Apple's Secure Enclave
* WebAuthn/FIDO2
* Android Keychain
* Various hardware security modules (HSMs)
* PassKeys

This precompile implementation is significantly more gas efficient (up to 60x) compared to Solidity-based implementations.

## Interface

```solidity theme={"dark"}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

address constant P256VERIFY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001011;

IP256VERIFY constant P256VERIFY_CONTRACT = IP256VERIFY(P256VERIFY_PRECOMPILE_ADDRESS);

interface IP256VERIFY {
    function verify(
        bytes memory signature
    ) external view returns (bytes memory response);
}
```

## Implementation Library

Here's a complete implementation of the P256 library that provides a convenient wrapper around the precompile:

```solidity theme={"dark"}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "./ECDSA.sol";
import "./P256Verify.sol";

/// @title P256
/// @author klkvr <https://github.com/klkvr>
/// @author jxom <https://github.com/jxom>
/// @notice Wrapper function to abstract low level details of call to the P256
///         signature verification precompile as defined in EIP-7212, see
///         <https://eips.ethereum.org/EIPS/eip-7212>.
library P256 {
    /// @notice P256VERIFY operation
    /// @param digest 32 bytes of the signed data hash
    /// @param signature Signature of the signer
    /// @param publicKey Public key of the signer
    /// @return success Represents if the operation was successful
    function verify(bytes32 digest, ECDSA.Signature memory signature, ECDSA.PublicKey memory publicKey)
        internal
        view
        returns (bool)
    {
        bytes memory input = abi.encode(digest, signature.r, signature.s, publicKey.x, publicKey.y);
        // On invalid signatures the precompile returns no data, which would
        // make a high-level interface call revert when decoding the `bytes`
        // return value. Use a low-level staticcall and treat empty return
        // data as "invalid signature" instead.
        (bool success, bytes memory output) = P256VERIFY_PRECOMPILE_ADDRESS.staticcall(
            abi.encodeWithSelector(IP256VERIFY.verify.selector, input)
        );

        return success && output.length > 0;
    }
}
```

## Basic Usage

### Using the P256 Library

```solidity theme={"dark"}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "./P256.sol";
import "./ECDSA.sol";

contract P256Example {
    using P256 for bytes32;

    function verifySignature(
        bytes32 messageHash,
        ECDSA.Signature memory signature,
        ECDSA.PublicKey memory publicKey
    ) public view returns (bool) {
        return P256.verify(messageHash, signature, publicKey);
    }
}
```

### Direct Precompile Usage

For more control, you can call the precompile directly:

```solidity theme={"dark"}
pragma solidity ^0.8.0;

interface IP256Verify {
    function verify(bytes calldata input) external view returns (bytes memory response);
}

contract P256Verifier {
    IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011);

    function verifySignature(
        bytes32 messageHash,
        bytes32 r,
        bytes32 s,
        bytes32 publicKeyX,
        bytes32 publicKeyY
    ) external view returns (bool) {
        bytes memory input = abi.encodePacked(
            messageHash,
            r,
            s,
            publicKeyX,
            publicKeyY
        );

        // Success returns a non-empty (ABI-encoded 32-byte) value; invalid
        // signatures return no data, which would revert a high-level call.
        (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall(
            abi.encodeWithSelector(IP256Verify.verify.selector, input)
        );
        return ok && output.length > 0;
    }
}
```

## Signature Format

The signature verification requires the following components:

* `digest`: 32 bytes of the signed data hash
* `signature`: Contains the r and s components of the signature
* `publicKey`: Contains the x and y coordinates of the public key

The precompile expects these components to be encoded in a specific format:

* First 32 bytes: message hash
* Next 32 bytes: `r` component of the signature
* Next 32 bytes: `s` component of the signature
* Next 32 bytes: `x` coordinate of the public key
* Next 32 bytes: `y` coordinate of the public key

Total length: 160 bytes

## Gas Costs

The precompile is highly gas efficient compared to Solidity implementations. The exact gas cost per byte of verified data is set to `GasCostPerByte = 300`, which gives us:

* **Total Cost**: 300 × 160 = **48,000 gas** per verification
* **Efficiency**: Up to 60x more efficient than pure Solidity implementations

## Real-World Use Cases

### WebAuthn/PassKeys Authentication

```solidity theme={"dark"}
pragma solidity ^0.8.0;

contract WebAuthnAuth {
    IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011);

    struct PublicKey {
        bytes32 x;
        bytes32 y;
    }

    mapping(address => PublicKey) public userKeys;

    function registerPasskey(
        bytes32 keyX,
        bytes32 keyY
    ) external {
        userKeys[msg.sender] = PublicKey(keyX, keyY);
    }

    function authenticateWithPasskey(
        bytes32 challengeHash,
        bytes32 r,
        bytes32 s
    ) external view returns (bool) {
        PublicKey memory userKey = userKeys[msg.sender];
        require(userKey.x != 0 && userKey.y != 0, "No passkey registered");

        bytes memory input = abi.encodePacked(
            challengeHash,
            r,
            s,
            userKey.x,
            userKey.y
        );

        (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall(
            abi.encodeWithSelector(IP256Verify.verify.selector, input)
        );
        return ok && output.length > 0;
    }
}
```

### Apple Secure Enclave Integration

```solidity theme={"dark"}
contract SecureEnclaveAuth {
    IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011);

    event SecureOperation(address indexed user, bytes32 indexed operationHash);

    function executeSecureOperation(
        bytes32 operationHash,
        bytes32 r,
        bytes32 s,
        bytes32 enclaveKeyX,
        bytes32 enclaveKeyY
    ) external {
        require(
            verifyEnclaveSignature(operationHash, r, s, enclaveKeyX, enclaveKeyY),
            "Invalid Secure Enclave signature"
        );

        emit SecureOperation(msg.sender, operationHash);
    }

    function verifyEnclaveSignature(
        bytes32 hash,
        bytes32 r,
        bytes32 s,
        bytes32 x,
        bytes32 y
    ) internal view returns (bool) {
        bytes memory input = abi.encodePacked(hash, r, s, x, y);
        (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall(
            abi.encodeWithSelector(IP256Verify.verify.selector, input)
        );
        return ok && output.length > 0;
    }
}
```

### Multi-Signature with Hardware Keys

```solidity theme={"dark"}
contract HardwareMultiSig {
    IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011);

    struct Signature {
        bytes32 r;
        bytes32 s;
    }

    struct PublicKey {
        bytes32 x;
        bytes32 y;
    }

    function verifyMultipleHardwareKeys(
        bytes32 messageHash,
        Signature[] calldata signatures,
        PublicKey[] calldata publicKeys,
        uint256 threshold
    ) external view returns (bool) {
        require(signatures.length == publicKeys.length, "Mismatched arrays");
        require(threshold <= signatures.length, "Invalid threshold");

        uint256 validSignatures = 0;

        for (uint i = 0; i < signatures.length; i++) {
            bytes memory input = abi.encodePacked(
                messageHash,
                signatures[i].r,
                signatures[i].s,
                publicKeys[i].x,
                publicKeys[i].y
            );

            (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall(
                abi.encodeWithSelector(IP256Verify.verify.selector, input)
            );
            if (ok && output.length > 0) {
                validSignatures++;
            }
        }

        return validSignatures >= threshold;
    }
}
```

## Security Considerations

<Warning>**Important**: Always validate public keys and signature components before verification to prevent invalid curve point attacks.</Warning>

### Public Key Validation

```solidity theme={"dark"}
function isValidPublicKey(bytes32 x, bytes32 y) internal pure returns (bool) {
    // Ensure point is not at infinity
    if (x == 0 && y == 0) return false;

    // Additional validation should be performed for production use
    // The precompile will reject invalid curve points
    return true;
}
```

### Signature Malleability

P256 signatures can be malleable. If your application requires unique signatures, implement additional checks:

```solidity theme={"dark"}
function isLowS(bytes32 s) internal pure returns (bool) {
    // Check if s is in the lower half of the curve order (n / 2, where n is
    // the P-256 group order). This prevents signature malleability.
    return uint256(s) <= 0x7FFFFFFF800000007FFFFFFFFFFFFFFFDE737D56D38BCF4279DCE5617E3192A8;
}
```

## JavaScript Integration

### Preparing Input Data

```javascript theme={"dark"}
// Example: Preparing P256 verification input (ethers v6)
function prepareP256Input(messageHash, signature, publicKey) {
  // Ensure all components are 32 bytes
  const hash = ethers.zeroPadValue(messageHash, 32);
  const r = ethers.zeroPadValue(signature.r, 32);
  const s = ethers.zeroPadValue(signature.s, 32);
  const x = ethers.zeroPadValue(publicKey.x, 32);
  const y = ethers.zeroPadValue(publicKey.y, 32);

  // Concatenate all components
  return ethers.concat([hash, r, s, x, y]);
}

// Usage with ethers.js
const P256_VERIFY_ADDRESS = '0x0000000000000000000000000000000000001011';
const P256_VERIFY_ABI = ['function verify(bytes input) view returns (bytes response)'];

async function verifyP256Signature(provider, messageHash, signature, publicKey) {
  const precompile = new ethers.Contract(P256_VERIFY_ADDRESS, P256_VERIFY_ABI, provider);
  const input = prepareP256Input(messageHash, signature, publicKey);
  try {
    const result = await precompile.verify(input);
    // Success returns 32 bytes ending in 0x01
    return result === '0x0000000000000000000000000000000000000000000000000000000000000001';
  } catch (err) {
    // Invalid signatures return no data, which ethers cannot decode as `bytes`
    // (BAD_DATA) — treat that as a failed verification.
    return false;
  }
}
```

## Error Handling

The P256 precompile returns no data on failure. Because `verify` is declared as returning `bytes`, a high-level Solidity interface call reverts when it tries to decode that empty return data — always use a low-level `staticcall` (as in the examples above) so invalid signatures resolve to `false` instead of reverting. Common failure cases include:

1. **Invalid Input Length**: Input must be exactly 160 bytes
2. **Invalid Public Key**: Point not on the P256 curve
3. **Invalid Signature**: r or s values out of valid range
4. **Verification Failure**: Signature doesn't match message and public key

```solidity theme={"dark"}
function safeVerifyP256(
    bytes32 messageHash,
    bytes32 r,
    bytes32 s,
    bytes32 publicKeyX,
    bytes32 publicKeyY
) internal view returns (bool success, string memory error) {
    // Validate inputs
    if (!isValidPublicKey(publicKeyX, publicKeyY)) {
        return (false, "Invalid public key");
    }

    if (r == 0 || s == 0) {
        return (false, "Invalid signature components");
    }

    // Prepare input and verify
    bytes memory input = abi.encodePacked(
        messageHash, r, s, publicKeyX, publicKeyY
    );

    (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall(
        abi.encodeWithSelector(IP256Verify.verify.selector, input)
    );
    bool verified = ok && output.length > 0;
    return (verified, verified ? "" : "Verification failed");
}
```

## Testing

### Unit Tests

```solidity theme={"dark"}
contract P256Test {
    IP256Verify constant P256_PRECOMPILE = IP256Verify(0x0000000000000000000000000000000000001011);

    function testValidSignature() public {
        // Test with known valid P256 signature
        bytes32 messageHash = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef;
        // Use actual test vectors for production tests
        bytes32 r = 0x...; // Valid r component
        bytes32 s = 0x...; // Valid s component
        bytes32 x = 0x...; // Valid public key x
        bytes32 y = 0x...; // Valid public key y

        bytes memory input = abi.encodePacked(messageHash, r, s, x, y);
        (bool ok, bytes memory output) = address(P256_PRECOMPILE).staticcall(
            abi.encodeWithSelector(IP256Verify.verify.selector, input)
        );

        assert(ok && output.length > 0);
    }
}
```

## Performance Considerations

* **Gas Efficiency**: 48,000 gas per verification vs 2M+ gas for Solidity implementations
* **Batch Operations**: Consider batching multiple verifications in a single transaction
* **Caching**: Cache public keys on-chain to reduce calldata for repeated verifications
* **Hardware Integration**: Particularly efficient for applications using hardware-backed keys

<Info>For more information about the P256 precompile implementation, visit the [Sei Chain repository](https://github.com/sei-protocol/sei-chain/tree/main/precompiles/p256).</Info>
