Verifier
The Verifier is a specialised Internet Computer (IC) Smart Contract (Canister) designed to verify TLS session proofs and full TLS proofs. It provides both asynchronous and direct verification methods, with the latter including tECDSA signatures for cross-chain or off-chain verification.
Mainnet Canister ID: yf57k-fyaaa-aaaaj-azw2a-cai
Interface
The canister exposes the following methods as defined in the Candid interface:
Methods
ping
- Signature:
() -> (text) query
- Purpose: A simple query method that returns a greeting message. This is typically used for testing connectivity and basic functionality.
verify_proof_direct
- Signature:
(proof_requests: vec text, notary_pub_key: text) -> (DirectVerificationResult)
- Purpose: This method is used for direct verification of proof requests. It returns a detailed verification response, including a tECDSA signature over the Merkle Root, which is essential for verification in foreign chains or off-chain environments like zkVMs.
verify_proof_async
- Signature:
(proof_requests: vec text, notary_pub_key: text) -> (ProofVerificationResponse)
- Purpose: This method is used for asynchronous verification of proof requests. It is intended for verifications that exist within the confines of the Internet Computer (IC).
public_key
- Signature:
() -> (record { sec1_pk: text; etherum_pk: text })
- Purpose: Retrieves the public key of the Canister, which includes both the SEC1 and Ethereum public keys.
Async vs Direct Verification
Async Verification
Async verification is optimised for IC internal usage. It:
- Performs proof verification
- Returns results directly
- No additional signatures or Merkle trees
- Efficient for Canister-to-Canister communication
Direct Verification
Direct verification adds extra security layers for external verification:
- Performs proof verification
- Generates Merkle tree of results
- Signs the Merkle root using tECDSA
- Enables verification in foreign chains or zkVMs
- Provides cryptographic proof of verification that can be validated outside the IC
TLS Proof Verification Process
The Smart Contract (Canister) verifies TLS proofs either partially or fully, depending on the type of proof submitted. The verification process involves:
Session Proofs
Session proofs verify the TLS session establishment:
- Validates handshake parameters
- Verifies server identity
- Confirms session key establishment
- Validates the session proof against notary’s signature
- Returns a hash of the verified proof
Full Proofs
Full proofs verify complete TLS sessions:
- Validates the full TLS proof including:
- Session establishment
- HTTP request data
- HTTP response data
- Verifies data integrity
- Confirms server responses
- Enables verification of specific HTTP interactions
Merkle Tree Generation
For direct verification, results are organised in a Merkle tree:
- Each proof response is hashed using SHA-256
- Hashes become leaves in the Merkle tree
- Tree root is computed
- Root is signed using the canister’s tECDSA key
- Both root and signature are returned for external verification
Security Considerations
- Always validate the notary’s public key before verification
- For cross-chain verification, ensure the IC Verifier Canister’s public key is properly registered
- When using direct verification, validate both the Merkle proof via
verify-local
module, and the tECDSA signature - Ensure proof requests are properly formatted JSON strings
- Handle potential errors in proof verification gracefully
Technical Implementation Details
The verification process utilises several key components:
-
Proof Parsing:
- JSON proofs are validated for required fields
- Proofs are categorised as Session or Full proofs
- Invalid proofs are rejected early in the process
-
Cryptographic Operations:
- SHA-256 for hashing proof responses
- tECDSA for signing Merkle roots
- Merkle tree construction for batch verification
-
State Management:
- Canister maintains its configuration state
- ECDSA key management through IC system APIs
- Thread-local storage for configuration
Error Handling
The canister provides detailed error messages for common failure scenarios:
- Invalid proof format
- Invalid notary signature
- Failed TLS verification
- Invalid handshake parameters
- ECDSA signing failures
Performance Considerations
-
Batch Processing:
- Multiple proofs can be verified in a single call
- Merkle tree construction optimises for batch verification
-
Resource Usage:
- Proof verification is computationally intensive
- Consider batch sizes carefully
- Async verification has lower overhead
-
Network Interaction:
- Direct verification requires additional cycles for ECDSA operations
- Plan for slightly longer execution times with direct verification
Example verify_proof_async
Integration
The following integration is adopted from the IC-ADC repository. It demonstrates how to use the verify_proof_async
method for proof verification within the Internet Computer (IC) environment. The example includes defining the Candid interface, implementing the async verification call, and handling the verification response.
In your Rust code, you can use the ic_cdk::call
function to make an async call to the Verifier Canister. Here’s how you can do it:
In this snippet, the request_proof_verification
function makes an async call to the Verifier Canister using the verify_proof_async
method. It sends the proofs and the notary public key, and awaits the response.
The response is a vector of ProofResponse
objects, which contain the verified proofs.
The async verification process does not utilize a Merkle tree, as it is intended to operate independently within the IC environment.
Example verify_proof_direct
Integration
The following integration is adopted from the example zkTLS flow in the Verity DP repository. It demonstrates how the verify_proof_direct
interface is used under the hood by the verify-remote
module to precompute over public facets of the TLS proof before sending the remote proof and the private facets of the TLS proof to a zkVM for verification via the verify-local
. In this dynamic, the Verifier will respond with a tECDSA signed Merkle Root hash representing each of the verifications and precomputes performed so that the zkVM can verify that the TLS proofs were prepared in part by a set of honest actors facilitated by the Internet Computer.
In your Rust code, you can use the ic_cdk::call
function to make a direct call to the Verifier Canister. Here’s how you can do it: