add example of presentation verification with no syscalls

This commit is contained in:
dan
2025-12-12 11:05:36 +02:00
parent 4fe5c1defd
commit 4d35f66f3e
5 changed files with 229 additions and 0 deletions

1
Cargo.lock generated
View File

@@ -7264,6 +7264,7 @@ dependencies = [
"alloy-signer",
"alloy-signer-local",
"bcs",
"bincode 1.3.3",
"blake3",
"k256",
"opaque-debug",

View File

@@ -26,6 +26,7 @@ tiny-keccak = { workspace = true, features = ["keccak"] }
alloy-primitives = { version = "1.3.1", default-features = false }
alloy-signer = { version = "1.0", default-features = false }
alloy-signer-local = { version = "1.0", default-features = false }
bincode = { workspace = true }
rand06-compat = { workspace = true }
rstest = { workspace = true }
tlsn-core = { workspace = true, features = ["fixtures"] }
@@ -37,3 +38,11 @@ workspace = true
[[test]]
name = "api"
required-features = ["fixtures"]
[[example]]
name = "prover_tcp"
required-features = ["fixtures"]
[[example]]
name = "verifier_tcp"
required-features = ["fixtures"]

View File

@@ -0,0 +1,132 @@
//! Prover that creates a presentation and sends it over TCP.
//! Compiled with normal getrandom.
use std::io::Write;
use std::net::TcpListener;
use tlsn_attestation::{
Attestation, AttestationConfig, CryptoProvider,
request::{Request, RequestConfig},
signing::SignatureAlgId,
};
use tlsn_core::{
connection::{CertBinding, CertBindingV1_2},
fixtures::{self, ConnectionFixture, encoder_secret},
hash::Blake3,
transcript::{
Direction, Transcript, TranscriptCommitConfigBuilder, TranscriptCommitment,
TranscriptSecret,
encoding::{EncodingCommitment, EncodingTree},
},
};
use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};
fn main() {
let mut provider = CryptoProvider::default();
provider.signer.set_secp256k1(&[42u8; 32]).unwrap();
let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
let (sent_len, recv_len) = transcript.len();
let encodings_provider = fixtures::encoding_provider(GET_WITH_HEADER, OK_JSON);
let ConnectionFixture {
server_name,
connection_info,
server_cert_data,
} = ConnectionFixture::tlsnotary(transcript.length());
let CertBinding::V1_2(CertBindingV1_2 {
server_ephemeral_key,
..
}) = server_cert_data.binding.clone()
else {
unreachable!()
};
let mut transcript_commitment_builder = TranscriptCommitConfigBuilder::new(&transcript);
transcript_commitment_builder
.commit_sent(&(0..sent_len))
.unwrap()
.commit_recv(&(0..recv_len))
.unwrap();
let transcripts_commitment_config = transcript_commitment_builder.build().unwrap();
let encoding_tree = EncodingTree::new(
&Blake3::default(),
transcripts_commitment_config.iter_encoding(),
&encodings_provider,
)
.unwrap();
let encoding_commitment = EncodingCommitment {
root: encoding_tree.root(),
};
let request_config = RequestConfig::default();
let mut request_builder = Request::builder(&request_config);
request_builder
.server_name(server_name.clone())
.handshake_data(server_cert_data)
.transcript(transcript)
.transcript_commitments(
vec![TranscriptSecret::Encoding(encoding_tree)],
vec![TranscriptCommitment::Encoding(encoding_commitment.clone())],
);
let (request, secrets) = request_builder.build(&provider).unwrap();
let attestation_config = AttestationConfig::builder()
.supported_signature_algs([SignatureAlgId::SECP256K1])
.build()
.unwrap();
let mut attestation_builder = Attestation::builder(&attestation_config)
.accept_request(request.clone())
.unwrap();
attestation_builder
.connection_info(connection_info.clone())
.server_ephemeral_key(server_ephemeral_key)
.encoder_secret(encoder_secret())
.transcript_commitments(vec![TranscriptCommitment::Encoding(encoding_commitment)]);
let attestation = attestation_builder.build(&provider).unwrap();
request.validate(&attestation, &provider).unwrap();
let mut transcript_proof_builder = secrets.transcript_proof_builder();
transcript_proof_builder
.reveal(&(0..sent_len), Direction::Sent)
.unwrap();
transcript_proof_builder
.reveal(&(0..recv_len), Direction::Received)
.unwrap();
let transcript_proof = transcript_proof_builder.build().unwrap();
let mut builder = attestation.presentation_builder(&provider);
builder.identity_proof(secrets.identity_proof());
builder.transcript_proof(transcript_proof);
let presentation = builder.build().unwrap();
// Serialize presentation
let presentation_bytes = bincode::serialize(&presentation).unwrap();
// Send over TCP
let listener = TcpListener::bind("127.0.0.1:19844").unwrap();
println!("Prover listening on 127.0.0.1:19844");
println!("Presentation size: {} bytes", presentation_bytes.len());
let (mut stream, _) = listener.accept().unwrap();
println!("Verifier connected, sending presentation...");
// Send length first, then data
let len = presentation_bytes.len() as u32;
stream.write_all(&len.to_be_bytes()).unwrap();
stream.write_all(&presentation_bytes).unwrap();
stream.flush().unwrap();
println!("Presentation sent successfully");
}

View File

@@ -0,0 +1,48 @@
#!/bin/bash
# Test that presentation verification works without getrandom syscalls.
#
# This script:
# 1. Builds prover with normal getrandom (needs RNG for creating attestation)
# 2. Builds verifier with getrandom_backend="unsupported" (no syscalls)
# 3. Runs both and verifies they communicate successfully
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CRATE_DIR="$(dirname "$SCRIPT_DIR")"
cd "$CRATE_DIR"
echo "=== Building prover (normal getrandom) ==="
cargo build --example prover_tcp --features fixtures
echo ""
echo "=== Building verifier (getrandom_backend=unsupported) ==="
RUSTFLAGS='--cfg getrandom_backend="unsupported"' cargo build --example verifier_tcp --features fixtures
echo ""
echo "=== Running test ==="
# Start prover in background
../../target/debug/examples/prover_tcp &
PROVER_PID=$!
# Give prover time to start listening
sleep 1
# Run verifier
../../target/debug/examples/verifier_tcp
VERIFIER_EXIT=$?
# Clean up
kill $PROVER_PID 2>/dev/null || true
if [ $VERIFIER_EXIT -eq 0 ]; then
echo ""
echo "=== TEST PASSED ==="
echo "Presentation verification works without getrandom syscalls!"
exit 0
else
echo ""
echo "=== TEST FAILED ==="
exit 1
fi

View File

@@ -0,0 +1,39 @@
//! Verifier that receives a presentation over TCP and verifies it.
//! Compiled with getrandom_backend="unsupported" to prove verification
//! doesn't need syscalls.
use std::io::Read;
use std::net::TcpStream;
use tlsn_attestation::{CryptoProvider, presentation::Presentation};
fn main() {
println!("Verifier connecting to 127.0.0.1:19844...");
let mut stream = TcpStream::connect("127.0.0.1:19844").unwrap();
println!("Connected to prover");
// Read length first
let mut len_bytes = [0u8; 4];
stream.read_exact(&mut len_bytes).unwrap();
let len = u32::from_be_bytes(len_bytes) as usize;
println!("Expecting {} bytes", len);
// Read presentation data
let mut presentation_bytes = vec![0u8; len];
stream.read_exact(&mut presentation_bytes).unwrap();
println!("Received presentation");
// Deserialize
let presentation: Presentation = bincode::deserialize(&presentation_bytes).unwrap();
println!("Deserialized presentation");
// Verify - this should work without getrandom!
let provider = CryptoProvider::default();
let output = presentation.verify(&provider).unwrap();
println!("Verification SUCCESS!");
println!("Server name: {:?}", output.server_name);
println!("Transcript sent length: {}", output.transcript.as_ref().map(|t| t.sent_unsafe().len()).unwrap_or(0));
println!("Transcript recv length: {}", output.transcript.as_ref().map(|t| t.received_unsafe().len()).unwrap_or(0));
}