Skip to main content

TEE Two-Factor Authentication

The Succinct Prover Network supports a two-factor authentication feature known as SP1-2FA that provides a second layer of verification for SP1 proofs through Trusted Execution Environments (TEEs). It is currently powered by the AWS Nitro enclave system.

How It Works

SP1-2FA leverages two independent verification paths to ensure maximal safety for users of SP1:

  1. Standard ZK Path: The SP1 RISC-V zkVM generates zero-knowledge proofs of the given bytecode and inputs.
  2. TEE Execution Path: The same program and inputs run in "execution mode" inside the SP1 RISC-V emulator within a secure hardware enclave, which produces a verifiable attestation over the outputs.

These two paths produce public outputs that are cross-referenced. The onchain verifier checks both the ZK proof and the TEE attestation, ensuring they match. This provides protection against potential vulnerabilities in SP1 itself.

Usage

Adding SP1-2FA powered by TEEs to your application requires minimal changes:

use sp1_sdk::{Prover, ProverClient, TEEProof};

// Initialize the prover using the network.
let prover = ProverClient::builder().network().build();

// Setup your program.
let (pk, vk) = prover.setup(ELF);

// Generate a proof with TEE attestation.
let proof = prover
.prove(&pk, &stdin)
.tee_2fa() // Enable SP1-2FA.
.plonk()
.await
.unwrap();

// Make sure the proof with the TEE signature + ZK proof verifies.
assert!(prover.verify(&proof, &vk).is_ok());

For a more comprehensive end-to-end example of generating and verifying TEE-protected proofs, see the Fibonacci example in the canonical SP1 TEE repository.

Onchain Verification

To verify SP1-2FA proofs onchain, you need to use the SP1TEEVerifier contract that verifies both the Groth16/PLONK proof and TEE signatures. The SP1TEEVerifier follows the ISP1Verifier interface, so existing users just need to point their verifier address to a new contract to switch to SP1-2FA.

This change enforces TEE 2FA on all proofs to your contract.

To simplify contract management, we've deployed new canonical verifier gateways for SP1-2FA for TEE + Groth16 and TEE + PLONK. Each canonical verifier gateway has different security properties, which is why they're maintained as separate addresses.

You can find the contract addresses and supported chains for the canonical SP1-2FA verifier gateways here.

Verifiying Attestations

SP1-2FA TEE's produce attestations committing to the signing key and the source code of the program used in the enclave. While ideally the attestations could be verified directly on-chain, the attestations are large and are prohibitively expensive to verify.

SP1-2FA relies on a trusted admin to verify attestations and subsequently whitelist the signing keys on-chain. Anyone may verify attestations after the fact to gain confidence that the keys are truly secure in the TEE, as attestations bind to the "program" being run inside of the TEE. Users can see that the signing key for the attestation is generated as a part of the program, and can't be extracted. You can find the verification script here.

To run the verify script, clone the sp1-tee repository and rebuild the enclave image locally to get the "program measurement", which is a commitment to the source code running inside the TEE.

Building the image requires you to install the nitro-cli. It is recommended to do this on an Amazon Linux 2023 EC2 instance to avoid having to install AWS dependencies manually.

cd sp1-tee
docker build -t sp1-tee .
nitro-cli build-enclave --docker-uri sp1-tee:latest --output-file sp1-tee.eif
nitro-cli describe-eif --eif-path sp1-tee.eif

The above will output the measurments in a JSON like object:

{
"Measurements": {
"HashAlgorithm": "Sha384 { ... }",
"PCR0": "...",
"PCR1": "...",
"PCR2": "..."
},
}

To validate all the signers on some given verifier contract, run this command:

$ cargo run --bin validate_signers --features production -- contract \
<contract_address> --pcr0 <...> --rpc-url <...> --version <...>

To verify an individual signer:

$ cargo --bin validate_signers --features production -- signer \
<address> --pcr0 <...> --version <...>

The version may be found in the sp1_sdk:

use sp1_sdk::network::tee::SP1_TEE_VERSION;

The validation code can be used as a library as well by importing the functionality from here.

Security Model

The TEE 2FA protocol relies on a trusted admin to honestly verify attestations and correctly whitelist signers.

When using the SP1 proving system alone, if someone had the ability to generate a malicious proof, then they could lie about any statement. Compared to the TEE 2FA model, they must also compromise the admin.

The assumption goes both ways, if only the admin were to be compromised, as long as you're using the SP1TeeVerifier they cannot verify a "false proof" without also compromising the SP1 proof system.

In future iterations, verifiying attestations on-chain can remove the trust assumptions from the admin.

Further Resources