diff --git a/notary-server/src/service.rs b/notary-server/src/service.rs index fa50e92e44..9d3248b6e0 100644 --- a/notary-server/src/service.rs +++ b/notary-server/src/service.rs @@ -162,10 +162,10 @@ pub async fn notary_service( let mut config_builder = VerifierConfig::builder(); - config_builder.id(session_id); + config_builder = config_builder.id(session_id); if let Some(max_transcript_size) = max_transcript_size { - config_builder.max_transcript_size(max_transcript_size); + config_builder = config_builder.max_transcript_size(max_transcript_size); } let config = config_builder.build()?; diff --git a/tlsn/Cargo.toml b/tlsn/Cargo.toml index ac2303cc59..89061a93d6 100644 --- a/tlsn/Cargo.toml +++ b/tlsn/Cargo.toml @@ -3,7 +3,7 @@ members = [ "tlsn-core", "tlsn-verifier", "tlsn-prover", - "tlsn-formats", +# "tlsn-formats", "tlsn-server-fixture", "tests-integration", "examples", @@ -16,7 +16,7 @@ tlsn-prover = { path = "tlsn-prover" } tlsn-verifier = { path = "tlsn-verifier" } tlsn-server-fixture = { path = "tlsn-server-fixture" } -tlsn-formats = { path = "tlsn-formats" } +#tlsn-formats = { path = "tlsn-formats" } tlsn-tls-core = { path = "../components/tls/tls-core" } tlsn-tls-mpc = { path = "../components/tls/tls-mpc" } diff --git a/tlsn/examples/discord/discord_dm_verifier.rs b/tlsn/examples/discord/discord_dm_verifier.rs index c66dc089f7..92dfb355b0 100644 --- a/tlsn/examples/discord/discord_dm_verifier.rs +++ b/tlsn/examples/discord/discord_dm_verifier.rs @@ -31,8 +31,9 @@ fn main() { let SessionProof { // The session header that was signed by the Notary is a succinct commitment to the TLS transcript. header, - // This is the server name, checked against the certificate chain shared in the TLS handshake. - server_name, + // This is the session_info, which contains the server_name, that is checked against the + // certificate chain shared in the TLS handshake. + session_info, .. } = session; @@ -51,7 +52,7 @@ fn main() { println!("-------------------------------------------------------------------"); println!( "Successfully verified that the bytes below came from a session with {:?} at {}.", - server_name, time + session_info.server_name, time ); println!("Note that the bytes which the Prover chose not to disclose are shown as X."); println!(); diff --git a/tlsn/examples/simple/simple_verifier.rs b/tlsn/examples/simple/simple_verifier.rs index 3b7fe80649..1baeaf5f52 100644 --- a/tlsn/examples/simple/simple_verifier.rs +++ b/tlsn/examples/simple/simple_verifier.rs @@ -31,8 +31,9 @@ fn main() { let SessionProof { // The session header that was signed by the Notary is a succinct commitment to the TLS transcript. header, - // This is the server name, checked against the certificate chain shared in the TLS handshake. - server_name, + // This is the session_info, which contains the server_name, that is checked against the + // certificate chain shared in the TLS handshake. + session_info, .. } = session; @@ -51,7 +52,7 @@ fn main() { println!("-------------------------------------------------------------------"); println!( "Successfully verified that the bytes below came from a session with {:?} at {}.", - server_name, time + session_info.server_name, time ); println!("Note that the bytes which the Prover chose not to disclose are shown as X."); println!(); diff --git a/tlsn/tests-integration/Cargo.toml b/tlsn/tests-integration/Cargo.toml index 380a629119..708e1f6170 100644 --- a/tlsn/tests-integration/Cargo.toml +++ b/tlsn/tests-integration/Cargo.toml @@ -7,9 +7,10 @@ publish = false [dev-dependencies] tlsn-core.workspace = true tlsn-tls-core.workspace = true -tlsn-prover = { workspace = true, features = ["tracing", "formats"] } +tlsn-prover = { workspace = true, features = ["tracing"] } tlsn-verifier = { workspace = true, features = ["tracing"] } tlsn-server-fixture.workspace = true +tlsn-utils.workspace = true p256 = { workspace = true, features = ["ecdsa"] } hyper = { workspace = true, features = ["client", "http1"] } diff --git a/tlsn/tests-integration/tests/test.rs b/tlsn/tests-integration/tests/notarize.rs similarity index 85% rename from tlsn/tests-integration/tests/test.rs rename to tlsn/tests-integration/tests/notarize.rs index f53fb9a303..0462d8ee23 100644 --- a/tlsn/tests-integration/tests/test.rs +++ b/tlsn/tests-integration/tests/notarize.rs @@ -9,7 +9,7 @@ use tracing::instrument; #[tokio::test] #[ignore] -async fn test() { +async fn notarize() { tracing_subscriber::fmt::init(); let (socket_0, socket_1) = tokio::io::duplex(2 << 23); @@ -73,19 +73,17 @@ async fn prover(notary_socke client_socket.close().await.unwrap(); - let mut prover = prover_task - .await - .unwrap() - .unwrap() - .to_http() - .unwrap() - .start_notarize(); + let mut prover = prover_task.await.unwrap().unwrap().start_notarize(); + let sent_tx_len = prover.sent_transcript().data().len(); + let recv_tx_len = prover.recv_transcript().data().len(); - prover.commit().unwrap(); + let builder = prover.commitment_builder(); - let notarized_session = prover.finalize().await.unwrap(); + // Commit to everything + builder.commit_sent(0..sent_tx_len).unwrap(); + builder.commit_recv(0..recv_tx_len).unwrap(); - _ = notarized_session.proof_builder().build().unwrap(); + let _notarized_session = prover.finalize().await.unwrap(); } #[instrument(skip(socket))] diff --git a/tlsn/tests-integration/tests/verify.rs b/tlsn/tests-integration/tests/verify.rs new file mode 100644 index 0000000000..15b84163bf --- /dev/null +++ b/tlsn/tests-integration/tests/verify.rs @@ -0,0 +1,119 @@ +use futures::AsyncWriteExt; +use hyper::{body::to_bytes, Body, Request, StatusCode}; +use tls_core::{anchors::RootCertStore, verify::WebPkiVerifier}; +use tlsn_core::{proof::SessionInfo, Direction, RedactedTranscript}; +use tlsn_prover::tls::{Prover, ProverConfig}; +use tlsn_server_fixture::{CA_CERT_DER, SERVER_DOMAIN}; +use tlsn_verifier::tls::{Verifier, VerifierConfig}; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; +use tracing::instrument; +use utils::range::RangeSet; + +#[tokio::test] +#[ignore] +async fn verify() { + tracing_subscriber::fmt::init(); + + let (socket_0, socket_1) = tokio::io::duplex(2 << 23); + + let (_, (sent, received, _session_info)) = tokio::join!(prover(socket_0), verifier(socket_1)); + + assert_eq!(sent.authed(), &RangeSet::from(0..sent.data().len() - 1)); + assert_eq!( + sent.redacted(), + &RangeSet::from(sent.data().len() - 1..sent.data().len()) + ); + + assert_eq!(received.authed(), &RangeSet::from(2..received.data().len())); + assert_eq!(received.redacted(), &RangeSet::from(0..2)); +} + +#[instrument(skip(notary_socket))] +async fn prover(notary_socket: T) { + let (client_socket, server_socket) = tokio::io::duplex(2 << 16); + + let server_task = tokio::spawn(tlsn_server_fixture::bind(server_socket.compat())); + + let mut root_store = RootCertStore::empty(); + root_store + .add(&tls_core::key::Certificate(CA_CERT_DER.to_vec())) + .unwrap(); + + let prover = Prover::new( + ProverConfig::builder() + .id("test") + .server_dns(SERVER_DOMAIN) + .root_cert_store(root_store) + .build() + .unwrap(), + ) + .setup(notary_socket.compat()) + .await + .unwrap(); + + let (tls_connection, prover_fut) = prover.connect(client_socket.compat()).await.unwrap(); + + let prover_task = tokio::spawn(prover_fut); + + let (mut request_sender, connection) = hyper::client::conn::handshake(tls_connection.compat()) + .await + .unwrap(); + + let connection_task = tokio::spawn(connection.without_shutdown()); + + let request = Request::builder() + .uri(format!("https://{}", SERVER_DOMAIN)) + .header("Host", SERVER_DOMAIN) + .header("Connection", "close") + .method("GET") + .body(Body::empty()) + .unwrap(); + + let response = request_sender.send_request(request).await.unwrap(); + + assert!(response.status() == StatusCode::OK); + + println!( + "{:?}", + String::from_utf8_lossy(&to_bytes(response.into_body()).await.unwrap()) + ); + + server_task.await.unwrap(); + + let mut client_socket = connection_task.await.unwrap().unwrap().io.into_inner(); + + client_socket.close().await.unwrap(); + + let mut prover = prover_task.await.unwrap().unwrap().start_prove(); + + let sent_transcript_len = prover.sent_transcript().data().len(); + let recv_transcript_len = prover.recv_transcript().data().len(); + + // Reveal parts of the transcript + _ = prover.reveal(0..sent_transcript_len - 1, Direction::Sent); + _ = prover.reveal(2..recv_transcript_len, Direction::Received); + prover.prove().await.unwrap(); + + prover.finalize().await.unwrap() +} + +#[instrument(skip(socket))] +async fn verifier( + socket: T, +) -> (RedactedTranscript, RedactedTranscript, SessionInfo) { + let mut root_store = RootCertStore::empty(); + root_store + .add(&tls_core::key::Certificate(CA_CERT_DER.to_vec())) + .unwrap(); + + let verifier_config = VerifierConfig::builder() + .id("test") + .cert_verifier(WebPkiVerifier::new(root_store, None)) + .build() + .unwrap(); + let verifier = Verifier::new(verifier_config); + + let (sent, received, session_info) = verifier.verify(socket.compat()).await.unwrap(); + (sent, received, session_info) +} diff --git a/tlsn/tlsn-core/src/msg.rs b/tlsn/tlsn-core/src/msg.rs index 1a5a7eca52..458447aa41 100644 --- a/tlsn/tlsn-core/src/msg.rs +++ b/tlsn/tlsn-core/src/msg.rs @@ -1,8 +1,9 @@ //! Protocol message types. use serde::{Deserialize, Serialize}; +use utils::range::RangeSet; -use crate::{merkle::MerkleRoot, signature::Signature, SessionHeader}; +use crate::{merkle::MerkleRoot, proof::SessionInfo, signature::Signature, SessionHeader}; /// Top-level enum for all messages #[derive(Debug, Serialize, Deserialize)] @@ -13,6 +14,10 @@ pub enum TlsnMessage { SignedSessionHeader(SignedSessionHeader), /// A session header. SessionHeader(SessionHeader), + /// Information about the TLS session + SessionInfo(SessionInfo), + /// Information about the values the prover wants to prove + ProvingInfo(ProvingInfo), } /// A signed session header. @@ -23,3 +28,14 @@ pub struct SignedSessionHeader { /// The notary's signature pub signature: Signature, } + +/// Information about the values the prover wants to prove +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct ProvingInfo { + /// The ids for the sent transcript + pub sent_ids: RangeSet, + /// The ids for the received transcript + pub recv_ids: RangeSet, + /// Purported cleartext values + pub cleartext: Vec, +} diff --git a/tlsn/tlsn-core/src/proof/mod.rs b/tlsn/tlsn-core/src/proof/mod.rs index d1c0a40dbb..e9f4ec9daa 100644 --- a/tlsn/tlsn-core/src/proof/mod.rs +++ b/tlsn/tlsn-core/src/proof/mod.rs @@ -1,9 +1,21 @@ //! Different types of proofs used in the TLSNotary protocol. +mod session; mod substrings; -mod tls; +pub use session::{default_cert_verifier, SessionInfo, SessionProof, SessionProofError}; pub use substrings::{ SubstringsProof, SubstringsProofBuilder, SubstringsProofBuilderError, SubstringsProofError, }; -pub use tls::{SessionProof, TlsProof}; + +use serde::{Deserialize, Serialize}; +use std::fmt::Debug; + +/// Proof that a transcript of communications took place between a Prover and Server. +#[derive(Debug, Serialize, Deserialize)] +pub struct TlsProof { + /// Proof of the TLS handshake, server identity, and commitments to the transcript. + pub session: SessionProof, + /// Proof regarding the contents of the transcript. + pub substrings: SubstringsProof, +} diff --git a/tlsn/tlsn-core/src/proof/tls.rs b/tlsn/tlsn-core/src/proof/session.rs similarity index 85% rename from tlsn/tlsn-core/src/proof/tls.rs rename to tlsn/tlsn-core/src/proof/session.rs index 91f2291351..8a81ed324b 100644 --- a/tlsn/tlsn-core/src/proof/tls.rs +++ b/tlsn/tlsn-core/src/proof/session.rs @@ -1,4 +1,4 @@ -use web_time::{Duration, SystemTime}; +use web_time::{Duration, UNIX_EPOCH}; use serde::{Deserialize, Serialize}; @@ -11,21 +11,11 @@ use tls_core::{ }; use crate::{ - proof::SubstringsProof, session::SessionHeader, signature::{Signature, SignatureVerifyError}, - NotaryPublicKey, ServerName, + HandshakeSummary, NotaryPublicKey, ServerName, }; -/// Proof that a transcript of communications took place between a Prover and Server. -#[derive(Debug, Serialize, Deserialize)] -pub struct TlsProof { - /// Proof of the TLS handshake, server identity, and commitments to the transcript. - pub session: SessionProof, - /// Proof regarding the contents of the transcript. - pub substrings: SubstringsProof, -} - /// An error that can occur while verifying a [`SessionProof`]. #[derive(Debug, thiserror::Error)] #[non_exhaustive] @@ -47,21 +37,21 @@ pub enum SessionProofError { InvalidServerCertificate(String), } -/// Proof of the TLS handshake, server identity, and commitments to the the transcript. +/// A session proof which is created from a [crate::session::NotarizedSession] +/// +/// Proof of the TLS handshake, server identity, and commitments to the transcript. #[derive(Debug, Serialize, Deserialize)] pub struct SessionProof { /// The session header pub header: SessionHeader, - /// The server name. - pub server_name: ServerName, /// Signature for the session header, if the notary signed it pub signature: Option, - /// Decommitment to the TLS handshake and server identity. - pub handshake_data_decommitment: Decommitment, + /// Information about the server + pub session_info: SessionInfo, } impl SessionProof { - /// Verify the session proof, returning the server's name. + /// Verify the session proof. /// /// # Arguments /// @@ -79,23 +69,58 @@ impl SessionProof { .ok_or(SessionProofError::MissingNotarySignature)?; signature.verify(&self.header.to_bytes(), notary_public_key)?; + self.session_info + .verify(self.header.handshake_summary(), cert_verifier)?; + + Ok(()) + } + + /// Verify the session proof using trust anchors from the `webpki-roots` crate. + /// + /// # Arguments + /// + /// * `notary_public_key` - The public key of the notary. + pub fn verify_with_default_cert_verifier( + &self, + notary_public_key: impl Into, + ) -> Result<(), SessionProofError> { + self.verify(notary_public_key, &default_cert_verifier()) + } +} +/// Contains information about the session +/// +/// Includes the [ServerName] and the decommitment to the [HandshakeData]. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SessionInfo { + /// The server name. + pub server_name: ServerName, + /// Decommitment to the TLS handshake and server identity. + pub handshake_decommitment: Decommitment, +} + +impl SessionInfo { + /// Verify the session info. + pub fn verify( + &self, + handshake_summary: &HandshakeSummary, + cert_verifier: &impl ServerCertVerifier, + ) -> Result<(), SessionProofError> { // Verify server name let server_name = TlsServerName::try_from(self.server_name.as_ref()) .map_err(|e| SessionProofError::InvalidServerName(e.to_string()))?; // Verify handshake - self.handshake_data_decommitment - .verify(self.header.handshake_summary().handshake_commitment()) + self.handshake_decommitment + .verify(handshake_summary.handshake_commitment()) .map_err(|e| SessionProofError::InvalidHandshake(e.to_string()))?; // Verify server certificate - self.handshake_data_decommitment + self.handshake_decommitment .data() .verify( cert_verifier, - SystemTime::UNIX_EPOCH - + Duration::from_secs(self.header.handshake_summary().time()), + UNIX_EPOCH + Duration::from_secs(handshake_summary.time()), &server_name, ) .map_err(|e| SessionProofError::InvalidServerCertificate(e.to_string()))?; @@ -103,20 +128,21 @@ impl SessionProof { Ok(()) } - /// Verify the session proof using trust anchors from the `webpki-roots` crate. + /// Verify the session info using trust anchors from the `webpki-roots` crate. /// /// # Arguments /// /// * `notary_public_key` - The public key of the notary. pub fn verify_with_default_cert_verifier( &self, - notary_public_key: impl Into, + handshake_summary: &HandshakeSummary, ) -> Result<(), SessionProofError> { - self.verify(notary_public_key, &default_cert_verifier()) + self.verify(handshake_summary, &default_cert_verifier()) } } -fn default_cert_verifier() -> WebPkiVerifier { +/// Create a new [`WebPkiVerifier`] with the default trust anchors from the `webpki-roots` crate. +pub fn default_cert_verifier() -> WebPkiVerifier { let mut root_store = RootCertStore::empty(); root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { OwnedTrustAnchor::from_subject_spki_name_constraints( diff --git a/tlsn/tlsn-core/src/proof/substrings.rs b/tlsn/tlsn-core/src/proof/substrings.rs index ec76515e66..7d35e8d9de 100644 --- a/tlsn/tlsn-core/src/proof/substrings.rs +++ b/tlsn/tlsn-core/src/proof/substrings.rs @@ -1,10 +1,4 @@ -//! Substrings proofs. - -use std::collections::HashMap; - -use mpz_circuits::types::ValueType; -use serde::{Deserialize, Serialize}; -use utils::range::{RangeDisjoint, RangeSet, RangeUnion}; +//! Substrings proofs based on commitments. use crate::{ commitment::{ @@ -15,8 +9,11 @@ use crate::{ Direction, EncodingId, RedactedTranscript, SessionHeader, Transcript, TranscriptSlice, MAX_TOTAL_COMMITTED_DATA, }; - +use mpz_circuits::types::ValueType; use mpz_garble_core::Encoder; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use utils::range::{RangeDisjoint, RangeSet, RangeUnion}; /// An error for [`SubstringsProofBuilder`] #[derive(Debug, thiserror::Error)] @@ -58,15 +55,20 @@ impl<'a> SubstringsProofBuilder<'a> { } } + /// Returns a reference to the commitments. + pub fn commitments(&self) -> &TranscriptCommitments { + self.commitments + } + /// Reveals data corresponding to the provided commitment id pub fn reveal(&mut self, id: CommitmentId) -> Result<&mut Self, SubstringsProofBuilderError> { let commitment = self - .commitments + .commitments() .get(&id) .ok_or(SubstringsProofBuilderError::InvalidCommitmentId(id))?; let info = self - .commitments + .commitments() .get_info(&id) .expect("info exists if commitment exists"); @@ -142,7 +144,9 @@ pub enum SubstringsProofError { InvalidInclusionProof(String), } -/// A substring proof containing the commitment openings and a proof +/// A substring proof using commitments +/// +/// This substring proof contains the commitment openings and a proof /// that the corresponding commitments are present in the merkle tree. #[derive(Serialize, Deserialize)] pub struct SubstringsProof { diff --git a/tlsn/tlsn-core/src/session/data.rs b/tlsn/tlsn-core/src/session/data.rs index 90be98794f..5bb148e802 100644 --- a/tlsn/tlsn-core/src/session/data.rs +++ b/tlsn/tlsn-core/src/session/data.rs @@ -1,13 +1,16 @@ use crate::{ - commitment::TranscriptCommitments, proof::SubstringsProofBuilder, ServerName, Transcript, + commitment::TranscriptCommitments, + proof::{SessionInfo, SubstringsProofBuilder}, + ServerName, Transcript, }; use mpz_core::commit::Decommitment; use serde::{Deserialize, Serialize}; use tls_core::handshake::HandshakeData; -/// Notarized session data. +/// Session data used for notarization. /// -/// This contains all the private data held by the `Prover` after notarization. +/// This contains all the private data held by the `Prover` after notarization including +/// commitments to the parts of the transcript. /// /// # Selective disclosure /// @@ -17,15 +20,12 @@ use tls_core::handshake::HandshakeData; /// See [`build_substrings_proof`](SessionData::build_substrings_proof). #[derive(Serialize, Deserialize)] pub struct SessionData { - server_name: ServerName, - handshake_data_decommitment: Decommitment, + session_info: SessionInfo, transcript_tx: Transcript, transcript_rx: Transcript, commitments: TranscriptCommitments, } -opaque_debug::implement!(SessionData); - impl SessionData { /// Creates new session data. pub fn new( @@ -35,23 +35,22 @@ impl SessionData { transcript_rx: Transcript, commitments: TranscriptCommitments, ) -> Self { - Self { + let session_info = SessionInfo { server_name, - handshake_data_decommitment, + handshake_decommitment: handshake_data_decommitment, + }; + + Self { + session_info, transcript_tx, transcript_rx, commitments, } } - /// Returns the server name. - pub fn server_name(&self) -> &ServerName { - &self.server_name - } - - /// Returns the decommitment to handshake data - pub fn handshake_data_decommitment(&self) -> &Decommitment { - &self.handshake_data_decommitment + /// Returns the session info + pub fn session_info(&self) -> &SessionInfo { + &self.session_info } /// Returns the transcript for data sent to the server @@ -74,3 +73,5 @@ impl SessionData { SubstringsProofBuilder::new(&self.commitments, &self.transcript_tx, &self.transcript_rx) } } + +opaque_debug::implement!(SessionData); diff --git a/tlsn/tlsn-core/src/session/mod.rs b/tlsn/tlsn-core/src/session/mod.rs index 3b64e9a5a1..a9762f1e2f 100644 --- a/tlsn/tlsn-core/src/session/mod.rs +++ b/tlsn/tlsn-core/src/session/mod.rs @@ -10,7 +10,10 @@ pub use data::SessionData; pub use handshake::{HandshakeSummary, HandshakeVerifyError}; pub use header::{SessionHeader, SessionHeaderVerifyError}; -use crate::{proof::SessionProof, signature::Signature}; +use crate::{ + proof::{SessionInfo, SessionProof}, + signature::Signature, +}; /// A validated notarized session stored by the Prover #[derive(Serialize, Deserialize)] @@ -34,11 +37,15 @@ impl NotarizedSession { /// Returns a proof of the TLS session pub fn session_proof(&self) -> SessionProof { + let session_info = SessionInfo { + server_name: self.data.session_info().server_name.clone(), + handshake_decommitment: self.data.session_info().handshake_decommitment.clone(), + }; + SessionProof { header: self.header.clone(), - server_name: self.data.server_name().clone(), signature: self.signature.clone(), - handshake_data_decommitment: self.data.handshake_data_decommitment().clone(), + session_info, } } diff --git a/tlsn/tlsn-core/src/transcript.rs b/tlsn/tlsn-core/src/transcript.rs index 64fd6630c4..13751767f8 100644 --- a/tlsn/tlsn-core/src/transcript.rs +++ b/tlsn/tlsn-core/src/transcript.rs @@ -136,7 +136,8 @@ pub struct TranscriptSlice { } impl TranscriptSlice { - pub(crate) fn new(range: Range, data: Vec) -> Self { + /// Creates a new transcript slice. + pub fn new(range: Range, data: Vec) -> Self { Self { range, data } } diff --git a/tlsn/tlsn-core/tests/api.rs b/tlsn/tlsn-core/tests/api.rs index cc3b32b6c6..d219664f4e 100644 --- a/tlsn/tlsn-core/tests/api.rs +++ b/tlsn/tlsn-core/tests/api.rs @@ -24,8 +24,8 @@ use tlsn_core::{ fixtures, msg::SignedSessionHeader, proof::{SessionProof, SubstringsProof}, - session::SessionData, - HandshakeSummary, NotarizedSession, ServerName, SessionHeader, Signature, Transcript, + HandshakeSummary, NotarizedSession, ServerName, SessionData, SessionHeader, Signature, + Transcript, }; #[test] @@ -83,7 +83,7 @@ fn test_api() { let commitments = commitment_builder.build().unwrap(); - let session_data = SessionData::new( + let notarized_session_data = SessionData::new( ServerName::Dns(testdata.dns_name.clone()), hs_decommitment.clone(), transcript_tx, @@ -106,7 +106,7 @@ fn test_api() { let header = SessionHeader::new( fixtures::encoder_seed(), - session_data.commitments().merkle_root(), + notarized_session_data.commitments().merkle_root(), data_sent.len(), data_recv.len(), // the session's end time and TLS handshake start time may be a few mins apart @@ -140,13 +140,13 @@ fn test_api() { .verify( time, &ephem_key, - &session_data.commitments().merkle_root(), + ¬arized_session_data.commitments().merkle_root(), header.encoder_seed(), - session_data.handshake_data_decommitment(), + ¬arized_session_data.session_info().handshake_decommitment, ) .unwrap(); - let session = NotarizedSession::new(header, Some(signature), session_data); + let session = NotarizedSession::new(header, Some(signature), notarized_session_data); // Prover converts NotarizedSession into SessionProof and SubstringsProof and sends them to the Verifier let session_proof = session.session_proof(); @@ -175,12 +175,15 @@ fn test_api() { let SessionProof { header, - server_name, + session_info, .. } = session_proof; // assert dns name is expected - assert_eq!(server_name.as_ref(), testdata.dns_name.as_str()); + assert_eq!( + session_info.server_name.as_ref(), + testdata.dns_name.as_str() + ); let (sent, recv) = substrings_proof.verify(&header).unwrap(); diff --git a/tlsn/tlsn-prover/Cargo.toml b/tlsn/tlsn-prover/Cargo.toml index 18e821be64..017dbac823 100644 --- a/tlsn/tlsn-prover/Cargo.toml +++ b/tlsn/tlsn-prover/Cargo.toml @@ -9,8 +9,8 @@ version = "0.1.0-alpha.3" edition = "2021" [features] -default = ["formats"] -formats = ["dep:tlsn-formats"] +default = [] +#formats = ["dep:tlsn-formats"] tracing = [ "dep:tracing", "tlsn-tls-client-async/tracing", @@ -23,7 +23,7 @@ tlsn-tls-core.workspace = true tlsn-tls-client.workspace = true tlsn-tls-client-async.workspace = true tlsn-core.workspace = true -tlsn-formats = { workspace = true, optional = true } +#tlsn-formats = { workspace = true, optional = true } tlsn-tls-mpc.workspace = true uid-mux.workspace = true diff --git a/tlsn/tlsn-prover/src/tls/error.rs b/tlsn/tlsn-prover/src/tls/error.rs index eda855b2eb..5ecc8751c6 100644 --- a/tlsn/tlsn-prover/src/tls/error.rs +++ b/tlsn/tlsn-prover/src/tls/error.rs @@ -1,5 +1,4 @@ use std::error::Error; - use tls_mpc::MpcTlsError; use tlsn_core::commitment::TranscriptCommitmentBuilderError; @@ -27,6 +26,8 @@ pub enum ProverError { ServerNoCloseNotify, #[error(transparent)] CommitmentError(#[from] CommitmentError), + #[error("Range exceeds transcript length")] + InvalidRange, } impl From for ProverError { @@ -41,6 +42,24 @@ impl From for ProverError { } } +impl From for ProverError { + fn from(e: mpz_garble::VmError) -> Self { + Self::MpcError(Box::new(e)) + } +} + +impl From for ProverError { + fn from(e: mpz_garble::MemoryError) -> Self { + Self::MpcError(Box::new(e)) + } +} + +impl From for ProverError { + fn from(e: mpz_garble::ProveError) -> Self { + Self::MpcError(Box::new(e)) + } +} + impl From for ProverError { fn from(value: mpz_ot::actor::kos::SenderActorError) -> Self { Self::MpcError(Box::new(value)) diff --git a/tlsn/tlsn-prover/src/tls/future.rs b/tlsn/tlsn-prover/src/tls/future.rs new file mode 100644 index 0000000000..53682a7c8c --- /dev/null +++ b/tlsn/tlsn-prover/src/tls/future.rs @@ -0,0 +1,67 @@ +//! This module collects futures which are used by the [Prover]. + +use super::{state, Prover, ProverError}; +use futures::{future::FusedFuture, Future}; +use std::pin::Pin; + +/// Prover future which must be polled for the TLS connection to make progress. +pub struct ProverFuture { + #[allow(clippy::type_complexity)] + pub(crate) fut: + Pin, ProverError>> + Send + 'static>>, +} + +impl Future for ProverFuture { + type Output = Result, ProverError>; + + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + self.fut.as_mut().poll(cx) + } +} + +/// A future which must be polled for the muxer to make progress. +pub(crate) struct MuxFuture { + pub(crate) fut: Pin> + Send + 'static>>, +} + +impl Future for MuxFuture { + type Output = Result<(), ProverError>; + + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + self.fut.as_mut().poll(cx) + } +} + +impl FusedFuture for MuxFuture { + fn is_terminated(&self) -> bool { + self.fut.is_terminated() + } +} + +/// A future which must be polled for the Oblivious Transfer protocol to make progress. +pub(crate) struct OTFuture { + pub(crate) fut: Pin> + Send + 'static>>, +} + +impl Future for OTFuture { + type Output = Result<(), ProverError>; + + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + self.fut.as_mut().poll(cx) + } +} + +impl FusedFuture for OTFuture { + fn is_terminated(&self) -> bool { + self.fut.is_terminated() + } +} diff --git a/tlsn/tlsn-prover/src/tls/mod.rs b/tlsn/tlsn-prover/src/tls/mod.rs index 8a12488c49..a5c6971d0f 100644 --- a/tlsn/tlsn-prover/src/tls/mod.rs +++ b/tlsn/tlsn-prover/src/tls/mod.rs @@ -7,61 +7,40 @@ mod config; mod error; +mod future; +mod notarize; +mod prove; pub mod state; pub use config::{ProverConfig, ProverConfigBuilder, ProverConfigBuilderError}; pub use error::ProverError; +pub use future::ProverFuture; -use ff::ShareConversionReveal; -use futures::{ - future::FusedFuture, AsyncRead, AsyncWrite, Future, FutureExt, SinkExt, StreamExt, TryFutureExt, -}; -use rand::Rng; -use std::{pin::Pin, sync::Arc}; -use tls_client_async::{bind_client, ClosedConnection, TlsConnection}; -use tls_mpc::{setup_components, MpcTlsLeader, TlsRole}; - +use crate::Mux; +use error::OTShutdownError; +use future::{MuxFuture, OTFuture}; +use futures::{AsyncRead, AsyncWrite, FutureExt, StreamExt, TryFutureExt}; use mpz_garble::{config::Role as DEAPRole, protocol::deap::DEAPVm}; use mpz_ot::{ actor::kos::{ReceiverActor, SenderActor, SharedReceiver, SharedSender}, chou_orlandi, kos, }; use mpz_share_conversion as ff; +use rand::Rng; +use state::{Notarize, Prove}; +use std::sync::Arc; use tls_client::{ClientConnection, ServerName as TlsServerName}; -use tlsn_core::{ - commitment::TranscriptCommitmentBuilder, - msg::{SignedSessionHeader, TlsnMessage}, - transcript::Transcript, - NotarizedSession, ServerName, SessionData, -}; -#[cfg(feature = "tracing")] -use tracing::{debug, debug_span, instrument, Instrument}; +use tls_client_async::{bind_client, ClosedConnection, TlsConnection}; +use tls_mpc::{setup_components, MpcTlsLeader, TlsRole}; +use tlsn_core::transcript::Transcript; use uid_mux::{yamux, UidYamux}; -use utils_aio::{codec::BincodeMux, expect_msg_or_err, mux::MuxChannel}; +use utils_aio::{codec::BincodeMux, mux::MuxChannel}; -use error::OTShutdownError; - -use crate::{ - http::{state as http_state, HttpProver, HttpProverError}, - Mux, -}; +#[cfg(feature = "formats")] +use http::{state as http_state, HttpProver, HttpProverError}; -/// Prover future which must be polled for the TLS connection to make progress. -pub struct ProverFuture { - #[allow(clippy::type_complexity)] - fut: Pin, ProverError>> + Send + 'static>>, -} - -impl Future for ProverFuture { - type Output = Result, ProverError>; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - self.fut.as_mut().poll(cx) - } -} +#[cfg(feature = "tracing")] +use tracing::{debug, debug_span, instrument, Instrument}; /// A prover instance. #[derive(Debug)] @@ -233,107 +212,22 @@ impl Prover { /// /// If the verifier is a Notary, this function will transition the prover to the next state /// where it can generate commitments to the transcript prior to finalization. - pub fn start_notarize(self) -> Prover { + pub fn start_notarize(self) -> Prover { Prover { config: self.config, state: self.state.into(), } } -} - -impl Prover { - /// Returns the transcript of the sent requests - pub fn sent_transcript(&self) -> &Transcript { - &self.state.transcript_tx - } - - /// Returns the transcript of the received responses - pub fn recv_transcript(&self) -> &Transcript { - &self.state.transcript_rx - } - - /// Returns the transcript commitment builder - pub fn commitment_builder(&mut self) -> &mut TranscriptCommitmentBuilder { - &mut self.state.builder - } - - /// Finalize the notarization returning a [`NotarizedSession`] - #[cfg_attr(feature = "tracing", instrument(level = "info", skip(self), err))] - pub async fn finalize(self) -> Result { - let state::Notarize { - mut notary_mux, - mut mux_fut, - mut vm, - mut ot_fut, - mut gf2, - start_time, - handshake_decommitment, - server_public_key, - transcript_tx, - transcript_rx, - builder, - } = self.state; - - let commitments = builder.build()?; - - let session_data = SessionData::new( - ServerName::Dns(self.config.server_dns().to_string()), - handshake_decommitment, - transcript_tx, - transcript_rx, - commitments, - ); - - let merkle_root = session_data.commitments().merkle_root(); - - let mut notarize_fut = Box::pin(async move { - let mut channel = notary_mux.get_channel("notarize").await?; - - channel - .send(TlsnMessage::TranscriptCommitmentRoot(merkle_root)) - .await?; - - let notary_encoder_seed = vm - .finalize() - .await - .map_err(|e| ProverError::MpcError(Box::new(e)))? - .expect("encoder seed returned"); - - // This is a temporary approach until a maliciously secure share conversion protocol is implemented. - // The prover is essentially revealing the TLS MAC key. In some exotic scenarios this allows a malicious - // TLS verifier to modify the prover's request. - gf2.reveal() - .await - .map_err(|e| ProverError::MpcError(Box::new(e)))?; - - let signed_header = expect_msg_or_err!(channel, TlsnMessage::SignedSessionHeader)?; - Ok::<_, ProverError>((notary_encoder_seed, signed_header)) - }) - .fuse(); - - let (notary_encoder_seed, SignedSessionHeader { header, signature }) = futures::select_biased! { - res = notarize_fut => res?, - _ = ot_fut => return Err(OTShutdownError)?, - _ = mux_fut => return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?, - }; - - // Check the header is consistent with the Prover's view - header - .verify( - start_time, - &server_public_key, - &session_data.commitments().merkle_root(), - ¬ary_encoder_seed, - session_data.handshake_data_decommitment(), - ) - .map_err(|_| { - ProverError::NotarizationError( - "notary signed an inconsistent session header".to_string(), - ) - })?; - - Ok(NotarizedSession::new(header, Some(signature), session_data)) + /// Starts proving the TLS session. + /// + /// This function transitions the prover into a state where it can prove content of the + /// transcript. + pub fn start_prove(self) -> Prover { + Prover { + config: self.config, + state: self.state.into(), + } } } @@ -455,47 +349,3 @@ async fn setup_mpc_backend( Ok((mpc_tls, vm, ot_recv, gf2, ot_fut)) } - -/// A future which must be polled for the muxer to make progress. -pub(crate) struct MuxFuture { - fut: Pin> + Send + 'static>>, -} - -impl Future for MuxFuture { - type Output = Result<(), ProverError>; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - self.fut.as_mut().poll(cx) - } -} - -impl FusedFuture for MuxFuture { - fn is_terminated(&self) -> bool { - self.fut.is_terminated() - } -} - -/// A future which must be polled for the Oblivious Transfer protocol to make progress. -pub(crate) struct OTFuture { - fut: Pin> + Send + 'static>>, -} - -impl Future for OTFuture { - type Output = Result<(), ProverError>; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - self.fut.as_mut().poll(cx) - } -} - -impl FusedFuture for OTFuture { - fn is_terminated(&self) -> bool { - self.fut.is_terminated() - } -} diff --git a/tlsn/tlsn-prover/src/tls/notarize.rs b/tlsn/tlsn-prover/src/tls/notarize.rs new file mode 100644 index 0000000000..f0caed6b97 --- /dev/null +++ b/tlsn/tlsn-prover/src/tls/notarize.rs @@ -0,0 +1,115 @@ +//! This module handles the notarization phase of the prover. +//! +//! The prover deals with a TLS verifier that is only a notary. + +use crate::tls::error::OTShutdownError; + +use super::{ff::ShareConversionReveal, state::Notarize, Prover, ProverError}; +use futures::{FutureExt, SinkExt, StreamExt}; +use tlsn_core::{ + commitment::TranscriptCommitmentBuilder, + msg::{SignedSessionHeader, TlsnMessage}, + transcript::Transcript, + NotarizedSession, ServerName, SessionData, +}; +#[cfg(feature = "tracing")] +use tracing::instrument; +use utils_aio::{expect_msg_or_err, mux::MuxChannel}; + +impl Prover { + /// Returns the transcript of the sent requests + pub fn sent_transcript(&self) -> &Transcript { + &self.state.transcript_tx + } + + /// Returns the transcript of the received responses + pub fn recv_transcript(&self) -> &Transcript { + &self.state.transcript_rx + } + + /// Returns the transcript commitment builder + pub fn commitment_builder(&mut self) -> &mut TranscriptCommitmentBuilder { + &mut self.state.builder + } + + /// Finalize the notarization returning a [`NotarizedSession`] + #[cfg_attr(feature = "tracing", instrument(level = "info", skip(self), err))] + pub async fn finalize(self) -> Result { + let Notarize { + mut notary_mux, + mut mux_fut, + mut vm, + mut ot_fut, + mut gf2, + start_time, + handshake_decommitment, + server_public_key, + transcript_tx, + transcript_rx, + builder, + } = self.state; + + let commitments = builder.build()?; + + let session_data = SessionData::new( + ServerName::Dns(self.config.server_dns().to_string()), + handshake_decommitment, + transcript_tx, + transcript_rx, + commitments, + ); + + let merkle_root = session_data.commitments().merkle_root(); + + let mut notarize_fut = Box::pin(async move { + let mut channel = notary_mux.get_channel("notarize").await?; + + channel + .send(TlsnMessage::TranscriptCommitmentRoot(merkle_root)) + .await?; + + let notary_encoder_seed = vm + .finalize() + .await + .map_err(|e| ProverError::MpcError(Box::new(e)))? + .expect("encoder seed returned"); + + // This is a temporary approach until a maliciously secure share conversion protocol is implemented. + // The prover is essentially revealing the TLS MAC key. In some exotic scenarios this allows a malicious + // TLS verifier to modify the prover's request. + gf2.reveal() + .await + .map_err(|e| ProverError::MpcError(Box::new(e)))?; + + let signed_header = expect_msg_or_err!(channel, TlsnMessage::SignedSessionHeader)?; + + Ok::<_, ProverError>((notary_encoder_seed, signed_header)) + }) + .fuse(); + + let (notary_encoder_seed, SignedSessionHeader { header, signature }) = futures::select_biased! { + res = notarize_fut => res?, + _ = ot_fut => return Err(OTShutdownError)?, + _ = &mut mux_fut => return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?, + }; + // Wait for the notary to correctly close the connection + mux_fut.await?; + + // Check the header is consistent with the Prover's view + header + .verify( + start_time, + &server_public_key, + &session_data.commitments().merkle_root(), + ¬ary_encoder_seed, + &session_data.session_info().handshake_decommitment, + ) + .map_err(|_| { + ProverError::NotarizationError( + "notary signed an inconsistent session header".to_string(), + ) + })?; + + Ok(NotarizedSession::new(header, Some(signature), session_data)) + } +} diff --git a/tlsn/tlsn-prover/src/tls/prove.rs b/tlsn/tlsn-prover/src/tls/prove.rs new file mode 100644 index 0000000000..63248a3544 --- /dev/null +++ b/tlsn/tlsn-prover/src/tls/prove.rs @@ -0,0 +1,206 @@ +//! This module handles the proving phase of the prover. +//! +//! Here the prover deals with a verifier directly, so there is no notary involved. Instead +//! the verifier directly verifies parts of the transcript. + +use super::{state::Prove as ProveState, Prover, ProverError}; +use crate::tls::error::OTShutdownError; +use futures::{FutureExt, SinkExt}; +use mpz_garble::{Memory, Prove, Vm}; +use mpz_share_conversion::ShareConversionReveal; +use tlsn_core::{ + msg::TlsnMessage, proof::SessionInfo, transcript::get_value_ids, Direction, ServerName, + Transcript, +}; +use utils::range::{RangeSet, RangeUnion}; +use utils_aio::mux::MuxChannel; + +#[cfg(feature = "tracing")] +use tracing::info; + +impl Prover { + /// Returns the transcript of the sent requests + pub fn sent_transcript(&self) -> &Transcript { + &self.state.transcript_tx + } + + /// Returns the transcript of the received responses + pub fn recv_transcript(&self) -> &Transcript { + &self.state.transcript_rx + } + + /// Reveal certain parts of the transcripts to the verifier + /// + /// This function allows to collect certain transcript ranges. When [Prover::prove] is called, these + /// ranges will be opened to the verifier. + /// + /// # Arguments + /// * `ranges` - The ranges of the transcript to reveal + /// * `direction` - The direction of the transcript to reveal + pub fn reveal( + &mut self, + ranges: impl Into>, + direction: Direction, + ) -> Result<(), ProverError> { + let sent_ids = &mut self.state.proving_info.sent_ids; + let recv_ids = &mut self.state.proving_info.recv_ids; + + let range_set = ranges.into(); + + // Check ranges + let transcript = match direction { + Direction::Sent => &self.state.transcript_tx, + Direction::Received => &self.state.transcript_rx, + }; + + if range_set.max().unwrap_or_default() > transcript.data().len() { + return Err(ProverError::InvalidRange); + } + + match direction { + Direction::Sent => *sent_ids = sent_ids.union(&range_set), + Direction::Received => *recv_ids = recv_ids.union(&range_set), + } + + Ok(()) + } + + /// Prove transcript values + pub async fn prove(&mut self) -> Result<(), ProverError> { + let mut proving_info = std::mem::take(&mut self.state.proving_info); + + let mut prove_fut = Box::pin(async { + // Create a new channel and vm thread if not already present + let channel = if let Some(ref mut channel) = self.state.channel { + channel + } else { + self.state.channel = Some(self.state.verify_mux.get_channel("prove-verify").await?); + self.state.channel.as_mut().unwrap() + }; + + let prove_thread = if let Some(ref mut prove_thread) = self.state.prove_thread { + prove_thread + } else { + self.state.prove_thread = Some(self.state.vm.new_thread("prove-verify").await?); + self.state.prove_thread.as_mut().unwrap() + }; + + // Now prove the transcript parts which have been marked for reveal + let sent_value_ids = proving_info + .sent_ids + .iter_ranges() + .map(|r| get_value_ids(&r.into(), Direction::Sent).collect::>()); + let recv_value_ids = proving_info + .recv_ids + .iter_ranges() + .map(|r| get_value_ids(&r.into(), Direction::Received).collect::>()); + + let value_refs = sent_value_ids + .chain(recv_value_ids) + .map(|ids| { + let inner_refs = ids + .iter() + .map(|id| { + prove_thread + .get_value(id.as_str()) + .expect("Byte should be in VM memory") + }) + .collect::>(); + + prove_thread + .array_from_values(inner_refs.as_slice()) + .expect("Byte should be in VM Memory") + }) + .collect::>(); + + // Extract cleartext we want to reveal from transcripts + let mut cleartext = + Vec::with_capacity(proving_info.sent_ids.len() + proving_info.recv_ids.len()); + proving_info + .sent_ids + .iter_ranges() + .for_each(|r| cleartext.extend_from_slice(&self.state.transcript_tx.data()[r])); + proving_info + .recv_ids + .iter_ranges() + .for_each(|r| cleartext.extend_from_slice(&self.state.transcript_rx.data()[r])); + proving_info.cleartext = cleartext; + + // Send the proving info to the verifier + channel.send(TlsnMessage::ProvingInfo(proving_info)).await?; + + #[cfg(feature = "tracing")] + info!("Sent proving info to verifier"); + + // Prove the revealed transcript parts + prove_thread.prove(value_refs.as_slice()).await?; + + #[cfg(feature = "tracing")] + info!("Successfully proved cleartext"); + + Ok::<_, ProverError>(()) + }) + .fuse(); + + futures::select_biased! { + res = prove_fut => res?, + _ = &mut self.state.ot_fut => return Err(OTShutdownError)?, + _ = &mut self.state.mux_fut => return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?, + }; + + Ok(()) + } + + /// Finalize the proving + pub async fn finalize(self) -> Result<(), ProverError> { + let ProveState { + mut verify_mux, + mut mux_fut, + mut vm, + mut ot_fut, + mut gf2, + handshake_decommitment, + .. + } = self.state; + + // Create session data and session_info + let session_info = SessionInfo { + server_name: ServerName::Dns(self.config.server_dns().to_string()), + handshake_decommitment, + }; + + let mut finalize_fut = Box::pin(async move { + let mut channel = verify_mux.get_channel("finalize").await?; + + _ = vm + .finalize() + .await + .map_err(|e| ProverError::MpcError(Box::new(e)))? + .expect("encoder seed returned"); + + // This is a temporary approach until a maliciously secure share conversion protocol is implemented. + // The prover is essentially revealing the TLS MAC key. In some exotic scenarios this allows a malicious + // TLS verifier to modify the prover's request. + gf2.reveal() + .await + .map_err(|e| ProverError::MpcError(Box::new(e)))?; + + // Send session_info to the verifier + channel.send(TlsnMessage::SessionInfo(session_info)).await?; + + Ok::<_, ProverError>(()) + }) + .fuse(); + + futures::select_biased! { + res = finalize_fut => res?, + _ = ot_fut => return Err(OTShutdownError)?, + _ = &mut mux_fut => return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?, + }; + + // We need to wait for the verifier to correctly close the connection. Otherwise the prover + // would rush ahead and close the connection before the verifier has finished. + mux_fut.await?; + Ok(()) + } +} diff --git a/tlsn/tlsn-prover/src/tls/state.rs b/tlsn/tlsn-prover/src/tls/state.rs index 733e73db69..d26d3285d4 100644 --- a/tlsn/tlsn-prover/src/tls/state.rs +++ b/tlsn/tlsn-prover/src/tls/state.rs @@ -1,21 +1,23 @@ //! TLS prover states. -use std::collections::HashMap; - +use crate::{ + tls::{MuxFuture, OTFuture}, + Mux, +}; +use mpz_core::commit::Decommitment; +use mpz_garble::protocol::deap::{DEAPThread, DEAPVm, PeerEncodings}; use mpz_garble_core::{encoding_state, EncodedValue}; use mpz_ot::actor::kos::{SharedReceiver, SharedSender}; - -use mpz_core::commit::Decommitment; -use mpz_garble::protocol::deap::{DEAPVm, PeerEncodings}; use mpz_share_conversion::{ConverterSender, Gf2_128}; +use std::collections::HashMap; use tls_core::{handshake::HandshakeData, key::PublicKey}; use tls_mpc::MpcTlsLeader; -use tlsn_core::{commitment::TranscriptCommitmentBuilder, Transcript}; - -use crate::{ - tls::{MuxFuture, OTFuture}, - Mux, +use tlsn_core::{ + commitment::TranscriptCommitmentBuilder, + msg::{ProvingInfo, TlsnMessage}, + Transcript, }; +use utils_aio::duplex::Duplex; /// Entry state pub struct Initialized; @@ -107,6 +109,43 @@ impl From for Notarize { } } +/// Proving state. +pub struct Prove { + pub(crate) verify_mux: Mux, + pub(crate) mux_fut: MuxFuture, + + pub(crate) vm: DEAPVm, + pub(crate) ot_fut: OTFuture, + pub(crate) gf2: ConverterSender, + + pub(crate) handshake_decommitment: Decommitment, + + pub(crate) transcript_tx: Transcript, + pub(crate) transcript_rx: Transcript, + + pub(crate) proving_info: ProvingInfo, + pub(crate) channel: Option>>, + pub(crate) prove_thread: Option>, +} + +impl From for Prove { + fn from(state: Closed) -> Self { + Self { + verify_mux: state.notary_mux, + mux_fut: state.mux_fut, + vm: state.vm, + ot_fut: state.ot_fut, + gf2: state.gf2, + handshake_decommitment: state.handshake_decommitment, + transcript_tx: state.transcript_tx, + transcript_rx: state.transcript_rx, + proving_info: ProvingInfo::default(), + channel: None, + prove_thread: None, + } + } +} + #[allow(missing_docs)] pub trait ProverState: sealed::Sealed {} @@ -114,6 +153,7 @@ impl ProverState for Initialized {} impl ProverState for Setup {} impl ProverState for Closed {} impl ProverState for Notarize {} +impl ProverState for Prove {} mod sealed { pub trait Sealed {} @@ -121,6 +161,7 @@ mod sealed { impl Sealed for super::Setup {} impl Sealed for super::Closed {} impl Sealed for super::Notarize {} + impl Sealed for super::Prove {} } fn collect_encodings( diff --git a/tlsn/tlsn-verifier/Cargo.toml b/tlsn/tlsn-verifier/Cargo.toml index 4eec6db6d1..5690d48b24 100644 --- a/tlsn/tlsn-verifier/Cargo.toml +++ b/tlsn/tlsn-verifier/Cargo.toml @@ -23,6 +23,7 @@ mpz-core.workspace = true mpz-garble.workspace = true mpz-ot.workspace = true mpz-share-conversion.workspace = true +mpz-circuits.workspace = true futures.workspace = true thiserror.workspace = true diff --git a/tlsn/tlsn-verifier/src/tls/config.rs b/tlsn/tlsn-verifier/src/tls/config.rs index 89f1e6e2ed..55122a23a0 100644 --- a/tlsn/tlsn-verifier/src/tls/config.rs +++ b/tlsn/tlsn-verifier/src/tls/config.rs @@ -1,12 +1,16 @@ use mpz_ot::{chou_orlandi, kos}; use mpz_share_conversion::{ReceiverConfig, SenderConfig}; +use std::fmt::{Debug, Formatter, Result}; +use tls_core::verify::{ServerCertVerifier, WebPkiVerifier}; use tls_mpc::{MpcTlsCommonConfig, MpcTlsFollowerConfig}; +use tlsn_core::proof::default_cert_verifier; const DEFAULT_MAX_TRANSCRIPT_SIZE: usize = 1 << 14; // 16Kb -/// Configuration for the [`Verifier`](crate::Verifier) +/// Configuration for the [`Verifier`](crate::tls::Verifier) #[allow(missing_docs)] -#[derive(Debug, Clone, derive_builder::Builder)] +#[derive(derive_builder::Builder)] +#[builder(pattern = "owned")] pub struct VerifierConfig { #[builder(setter(into))] id: String, @@ -16,6 +20,22 @@ pub struct VerifierConfig { /// This includes the number of bytes sent and received to the server. #[builder(default = "DEFAULT_MAX_TRANSCRIPT_SIZE")] max_transcript_size: usize, + #[builder( + pattern = "owned", + setter(strip_option), + default = "Some(default_cert_verifier())" + )] + cert_verifier: Option, +} + +impl Debug for VerifierConfig { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.debug_struct("VerifierConfig") + .field("id", &self.id) + .field("max_transcript_size", &self.max_transcript_size) + .field("cert_verifier", &"_") + .finish() + } } impl VerifierConfig { @@ -34,6 +54,13 @@ impl VerifierConfig { self.max_transcript_size } + /// Get the certificate verifier. + pub fn cert_verifier(&self) -> &impl ServerCertVerifier { + self.cert_verifier + .as_ref() + .expect("Certificate verifier should be set") + } + pub(crate) fn build_base_ot_sender_config(&self) -> chou_orlandi::SenderConfig { chou_orlandi::SenderConfig::default() } diff --git a/tlsn/tlsn-verifier/src/tls/error.rs b/tlsn/tlsn-verifier/src/tls/error.rs index 7c5c4f7d23..9791c3ebeb 100644 --- a/tlsn/tlsn-verifier/src/tls/error.rs +++ b/tlsn/tlsn-verifier/src/tls/error.rs @@ -1,5 +1,4 @@ use std::error::Error; - use tls_mpc::MpcTlsError; /// An error that can occur during TLS verification. @@ -12,6 +11,8 @@ pub enum VerifierError { MuxerError(#[from] utils_aio::mux::MuxerError), #[error("error occurred in MPC protocol: {0}")] MpcError(Box), + #[error("Range exceeds transcript length")] + InvalidRange, } impl From for VerifierError { @@ -37,3 +38,27 @@ impl From for VerifierError { Self::MpcError(Box::new(e)) } } + +impl From for VerifierError { + fn from(e: mpz_garble::VerifyError) -> Self { + Self::MpcError(Box::new(e)) + } +} + +impl From for VerifierError { + fn from(e: mpz_garble::MemoryError) -> Self { + Self::MpcError(Box::new(e)) + } +} + +impl From for VerifierError { + fn from(e: tlsn_core::proof::SessionProofError) -> Self { + Self::MpcError(Box::new(e)) + } +} + +impl From for VerifierError { + fn from(e: mpz_garble::VmError) -> Self { + Self::MpcError(Box::new(e)) + } +} diff --git a/tlsn/tlsn-verifier/src/tls/future.rs b/tlsn/tlsn-verifier/src/tls/future.rs new file mode 100644 index 0000000000..886fb4128e --- /dev/null +++ b/tlsn/tlsn-verifier/src/tls/future.rs @@ -0,0 +1,50 @@ +//! This module collects futures which are used by the [Verifier](crate::tls::Verifier). + +use super::{OTSenderActor, VerifierError}; +use futures::{future::FusedFuture, Future}; +use std::pin::Pin; + +/// A future which must be polled for the muxer to make progress. +pub(crate) struct MuxFuture { + pub(crate) fut: Pin> + Send + 'static>>, +} + +impl Future for MuxFuture { + type Output = Result<(), VerifierError>; + + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + self.fut.as_mut().poll(cx) + } +} + +impl FusedFuture for MuxFuture { + fn is_terminated(&self) -> bool { + self.fut.is_terminated() + } +} + +/// A future which must be polled for the Oblivious Transfer protocol to make progress. +pub(crate) struct OTFuture { + pub(crate) fut: + Pin> + Send + 'static>>, +} + +impl Future for OTFuture { + type Output = Result; + + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + self.fut.as_mut().poll(cx) + } +} + +impl FusedFuture for OTFuture { + fn is_terminated(&self) -> bool { + self.fut.is_terminated() + } +} diff --git a/tlsn/tlsn-verifier/src/tls/mod.rs b/tlsn/tlsn-verifier/src/tls/mod.rs index d38fb5b409..e02db4763b 100644 --- a/tlsn/tlsn-verifier/src/tls/mod.rs +++ b/tlsn/tlsn-verifier/src/tls/mod.rs @@ -2,23 +2,20 @@ pub(crate) mod config; mod error; +mod future; +mod notarize; pub mod state; +mod verify; pub use config::{VerifierConfig, VerifierConfigBuilder, VerifierConfigBuilderError}; pub use error::VerifierError; -use std::{ - pin::Pin, - time::{SystemTime, UNIX_EPOCH}, -}; - +use crate::{tls::future::OTFuture, Mux}; +use future::MuxFuture; use futures::{ - future::FusedFuture, stream::{SplitSink, SplitStream}, - AsyncRead, AsyncWrite, Future, FutureExt, SinkExt, StreamExt, TryFutureExt, + AsyncRead, AsyncWrite, FutureExt, StreamExt, TryFutureExt, }; - -use mpz_core::serialize::CanonicalSerialize; use mpz_garble::{config::Role as GarbleRole, protocol::deap::DEAPVm}; use mpz_ot::{ actor::kos::{ @@ -27,18 +24,14 @@ use mpz_ot::{ chou_orlandi, kos, }; use mpz_share_conversion as ff; -use mpz_share_conversion::ShareConversionVerify; use rand::Rng; use signature::Signer; +use state::{Notarize, Verify}; +use std::time::{SystemTime, UNIX_EPOCH}; use tls_mpc::{setup_components, MpcTlsFollower, TlsRole}; -use tlsn_core::{ - msg::{SignedSessionHeader, TlsnMessage}, - HandshakeSummary, SessionHeader, Signature, -}; +use tlsn_core::{proof::SessionInfo, RedactedTranscript, SessionHeader, Signature}; use uid_mux::{yamux, UidYamux}; -use utils_aio::{codec::BincodeMux, duplex::Duplex, expect_msg_or_err, mux::MuxChannel}; - -use crate::Mux; +use utils_aio::{codec::BincodeMux, duplex::Duplex, mux::MuxChannel}; #[cfg(feature = "tracing")] use tracing::{debug, info, instrument}; @@ -123,9 +116,24 @@ impl Verifier { .await? .run() .await? - .notarize(signer) + .start_notarize() + .finalize(signer) .await } + + /// Runs the TLS verifier to completion, verifying the TLS session. + /// + /// This is a convenience method which runs all the steps needed for verification. + pub async fn verify( + self, + socket: S, + ) -> Result<(RedactedTranscript, RedactedTranscript, SessionInfo), VerifierError> { + let mut verifier = self.setup(socket).await?.run().await?.start_verify(); + let (redacted_sent, redacted_received) = verifier.receive().await?; + + let session_info = verifier.finalize().await?; + Ok((redacted_sent, redacted_received, session_info)) + } } impl Verifier { @@ -186,92 +194,26 @@ impl Verifier { } impl Verifier { - /// Notarizes the TLS session. - pub async fn notarize(self, signer: &impl Signer) -> Result - where - T: Into, - { - let state::Closed { - mut mux, - mut mux_fut, - mut vm, - ot_send, - ot_recv, - ot_fut, - mut gf2, - encoder_seed, - start_time, - server_ephemeral_key, - handshake_commitment, - sent_len, - recv_len, - } = self.state; - - let notarize_fut = async { - let mut notarize_channel = mux.get_channel("notarize").await?; - - let merkle_root = - expect_msg_or_err!(notarize_channel, TlsnMessage::TranscriptCommitmentRoot)?; - - // Finalize all MPC before signing the session header - let (mut ot_sender_actor, _, _) = futures::try_join!( - ot_fut, - ot_send.shutdown().map_err(VerifierError::from), - ot_recv.shutdown().map_err(VerifierError::from) - )?; - - ot_sender_actor.reveal().await?; - - vm.finalize() - .await - .map_err(|e| VerifierError::MpcError(Box::new(e)))?; - - gf2.verify() - .await - .map_err(|e| VerifierError::MpcError(Box::new(e)))?; - - #[cfg(feature = "tracing")] - info!("Finalized all MPC"); - - let handshake_summary = - HandshakeSummary::new(start_time, server_ephemeral_key, handshake_commitment); - - let session_header = SessionHeader::new( - encoder_seed, - merkle_root, - sent_len, - recv_len, - handshake_summary, - ); - - let signature = signer.sign(&session_header.to_bytes()); - - #[cfg(feature = "tracing")] - info!("Signed session header"); - - notarize_channel - .send(TlsnMessage::SignedSessionHeader(SignedSessionHeader { - header: session_header.clone(), - signature: signature.into(), - })) - .await?; - - #[cfg(feature = "tracing")] - info!("Sent session header"); - - Ok::<_, VerifierError>(session_header) - }; - - let session_header = futures::select! { - res = notarize_fut.fuse() => res?, - _ = &mut mux_fut => Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?, - }; - - let mut mux = mux.into_inner(); - - futures::try_join!(mux.close().map_err(VerifierError::from), mux_fut)?; + /// Starts notarization of the TLS session. + /// + /// If the verifier is a Notary, this function will transition the verifier to the next state + /// where it can sign the prover's commitments to the transcript. + pub fn start_notarize(self) -> Verifier { + Verifier { + config: self.config, + state: self.state.into(), + } + } - Ok(session_header) + /// Starts verification of the TLS session. + /// + /// This function transitions the verifier into a state where it can verify content of the + /// transcript. + pub fn start_verify(self) -> Verifier { + Verifier { + config: self.config, + state: self.state.into(), + } } } @@ -395,47 +337,3 @@ async fn setup_mpc_backend( Ok((mpc_tls, vm, ot_send, ot_recv, gf2, ot_fut)) } - -/// A future which must be polled for the muxer to make progress. -pub(crate) struct MuxFuture { - fut: Pin> + Send + 'static>>, -} - -impl Future for MuxFuture { - type Output = Result<(), VerifierError>; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - self.fut.as_mut().poll(cx) - } -} - -impl FusedFuture for MuxFuture { - fn is_terminated(&self) -> bool { - self.fut.is_terminated() - } -} - -/// A future which must be polled for the Oblivious Transfer protocol to make progress. -pub(crate) struct OTFuture { - fut: Pin> + Send + 'static>>, -} - -impl Future for OTFuture { - type Output = Result; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - self.fut.as_mut().poll(cx) - } -} - -impl FusedFuture for OTFuture { - fn is_terminated(&self) -> bool { - self.fut.is_terminated() - } -} diff --git a/tlsn/tlsn-verifier/src/tls/notarize.rs b/tlsn/tlsn-verifier/src/tls/notarize.rs new file mode 100644 index 0000000000..9b07b19547 --- /dev/null +++ b/tlsn/tlsn-verifier/src/tls/notarize.rs @@ -0,0 +1,107 @@ +//! This module handles the notarization phase of the verifier. +//! +//! The TLS verifier is only a notary. + +use super::{state::Notarize, Verifier, VerifierError}; +use futures::{FutureExt, SinkExt, StreamExt, TryFutureExt}; +use mpz_core::serialize::CanonicalSerialize; +use mpz_share_conversion::ShareConversionVerify; +use signature::Signer; +use tlsn_core::{ + msg::{SignedSessionHeader, TlsnMessage}, + HandshakeSummary, SessionHeader, Signature, +}; +use utils_aio::{expect_msg_or_err, mux::MuxChannel}; + +#[cfg(feature = "tracing")] +use tracing::info; + +impl Verifier { + /// Notarizes the TLS session. + pub async fn finalize(self, signer: &impl Signer) -> Result + where + T: Into, + { + let Notarize { + mut mux, + mut mux_fut, + mut vm, + ot_send, + ot_recv, + ot_fut, + mut gf2, + encoder_seed, + start_time, + server_ephemeral_key, + handshake_commitment, + sent_len, + recv_len, + } = self.state; + + let notarize_fut = async { + let mut notarize_channel = mux.get_channel("notarize").await?; + + let merkle_root = + expect_msg_or_err!(notarize_channel, TlsnMessage::TranscriptCommitmentRoot)?; + + // Finalize all MPC before signing the session header + let (mut ot_sender_actor, _, _) = futures::try_join!( + ot_fut, + ot_send.shutdown().map_err(VerifierError::from), + ot_recv.shutdown().map_err(VerifierError::from) + )?; + + ot_sender_actor.reveal().await?; + + vm.finalize() + .await + .map_err(|e| VerifierError::MpcError(Box::new(e)))?; + + gf2.verify() + .await + .map_err(|e| VerifierError::MpcError(Box::new(e)))?; + + #[cfg(feature = "tracing")] + info!("Finalized all MPC"); + + let handshake_summary = + HandshakeSummary::new(start_time, server_ephemeral_key, handshake_commitment); + + let session_header = SessionHeader::new( + encoder_seed, + merkle_root, + sent_len, + recv_len, + handshake_summary, + ); + + let signature = signer.sign(&session_header.to_bytes()); + + #[cfg(feature = "tracing")] + info!("Signed session header"); + + notarize_channel + .send(TlsnMessage::SignedSessionHeader(SignedSessionHeader { + header: session_header.clone(), + signature: signature.into(), + })) + .await?; + + #[cfg(feature = "tracing")] + info!("Sent session header"); + + Ok::<_, VerifierError>(session_header) + }; + + let session_header = futures::select! { + res = notarize_fut.fuse() => res?, + _ = &mut mux_fut => Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?, + }; + + let mut mux = mux.into_inner(); + + futures::try_join!(mux.close().map_err(VerifierError::from), mux_fut)?; + + Ok(session_header) + } +} diff --git a/tlsn/tlsn-verifier/src/tls/state.rs b/tlsn/tlsn-verifier/src/tls/state.rs index aa65036b43..c03c6b636c 100644 --- a/tlsn/tlsn-verifier/src/tls/state.rs +++ b/tlsn/tlsn-verifier/src/tls/state.rs @@ -1,14 +1,16 @@ //! TLS Verifier state. use mpz_core::hash::Hash; -use mpz_garble::protocol::deap::DEAPVm; +use mpz_garble::protocol::deap::{DEAPThread, DEAPVm}; use mpz_ot::actor::kos::{SharedReceiver, SharedSender}; use mpz_share_conversion::{ConverterReceiver, Gf2_128}; use tls_core::key::PublicKey; use tls_mpc::MpcTlsFollower; +use tlsn_core::msg::TlsnMessage; +use utils_aio::duplex::Duplex; use crate::{ - tls::{MuxFuture, OTFuture}, + tls::future::{MuxFuture, OTFuture}, Mux, }; @@ -56,13 +58,102 @@ pub struct Closed { opaque_debug::implement!(Closed); +/// Notarizing state. +pub struct Notarize { + pub(crate) mux: Mux, + pub(crate) mux_fut: MuxFuture, + + pub(crate) vm: DEAPVm, + pub(crate) ot_send: SharedSender, + pub(crate) ot_recv: SharedReceiver, + pub(crate) ot_fut: OTFuture, + pub(crate) gf2: ConverterReceiver, + + pub(crate) encoder_seed: [u8; 32], + pub(crate) start_time: u64, + pub(crate) server_ephemeral_key: PublicKey, + pub(crate) handshake_commitment: Hash, + pub(crate) sent_len: usize, + pub(crate) recv_len: usize, +} + +opaque_debug::implement!(Notarize); + +impl From for Notarize { + fn from(value: Closed) -> Self { + Self { + mux: value.mux, + mux_fut: value.mux_fut, + vm: value.vm, + ot_send: value.ot_send, + ot_recv: value.ot_recv, + ot_fut: value.ot_fut, + gf2: value.gf2, + encoder_seed: value.encoder_seed, + start_time: value.start_time, + server_ephemeral_key: value.server_ephemeral_key, + handshake_commitment: value.handshake_commitment, + sent_len: value.sent_len, + recv_len: value.recv_len, + } + } +} + +/// Verifying state. +pub struct Verify { + pub(crate) mux: Mux, + pub(crate) mux_fut: MuxFuture, + + pub(crate) vm: DEAPVm, + pub(crate) ot_send: SharedSender, + pub(crate) ot_recv: SharedReceiver, + pub(crate) ot_fut: OTFuture, + pub(crate) gf2: ConverterReceiver, + + pub(crate) start_time: u64, + pub(crate) server_ephemeral_key: PublicKey, + pub(crate) handshake_commitment: Hash, + pub(crate) sent_len: usize, + pub(crate) recv_len: usize, + + pub(crate) channel: Option>>, + pub(crate) verify_thread: Option>, +} + +opaque_debug::implement!(Verify); + +impl From for Verify { + fn from(value: Closed) -> Self { + Self { + mux: value.mux, + mux_fut: value.mux_fut, + vm: value.vm, + ot_send: value.ot_send, + ot_recv: value.ot_recv, + ot_fut: value.ot_fut, + gf2: value.gf2, + start_time: value.start_time, + server_ephemeral_key: value.server_ephemeral_key, + handshake_commitment: value.handshake_commitment, + sent_len: value.sent_len, + recv_len: value.recv_len, + channel: None, + verify_thread: None, + } + } +} + impl VerifierState for Initialized {} impl VerifierState for Setup {} impl VerifierState for Closed {} +impl VerifierState for Notarize {} +impl VerifierState for Verify {} mod sealed { pub trait Sealed {} impl Sealed for super::Initialized {} impl Sealed for super::Setup {} impl Sealed for super::Closed {} + impl Sealed for super::Notarize {} + impl Sealed for super::Verify {} } diff --git a/tlsn/tlsn-verifier/src/tls/verify.rs b/tlsn/tlsn-verifier/src/tls/verify.rs new file mode 100644 index 0000000000..0c3cd447a3 --- /dev/null +++ b/tlsn/tlsn-verifier/src/tls/verify.rs @@ -0,0 +1,197 @@ +//! This module handles the verification phase of the verifier. +//! +//! The TLS verifier is an application-specific verifier. + +use super::{state::Verify as VerifyState, Verifier, VerifierError}; +use futures::{FutureExt, StreamExt, TryFutureExt}; +use mpz_circuits::types::Value; +use mpz_garble::{Memory, Verify, Vm}; +use mpz_share_conversion::ShareConversionVerify; +use tlsn_core::{ + msg::TlsnMessage, proof::SessionInfo, transcript::get_value_ids, Direction, HandshakeSummary, + RedactedTranscript, TranscriptSlice, +}; +use utils_aio::{expect_msg_or_err, mux::MuxChannel}; + +#[cfg(feature = "tracing")] +use tracing::info; + +impl Verifier { + /// Receives the **purported** transcript from the Prover. + /// + /// # Warning + /// + /// The content of the received transcripts can not be considered authentic until after finalization. + pub async fn receive( + &mut self, + ) -> Result<(RedactedTranscript, RedactedTranscript), VerifierError> { + let verify_fut = async { + // Create a new channel and vm thread if not already present + let channel = if let Some(ref mut channel) = self.state.channel { + channel + } else { + self.state.channel = Some(self.state.mux.get_channel("prove-verify").await?); + self.state.channel.as_mut().unwrap() + }; + + let verify_thread = if let Some(ref mut verify_thread) = self.state.verify_thread { + verify_thread + } else { + self.state.verify_thread = Some(self.state.vm.new_thread("prove-verify").await?); + self.state.verify_thread.as_mut().unwrap() + }; + + // Receive the proving info from the prover + let mut proving_info = expect_msg_or_err!(channel, TlsnMessage::ProvingInfo)?; + let mut cleartext = proving_info.cleartext.clone(); + + #[cfg(feature = "tracing")] + info!("Received proving info from prover"); + + // Check ranges + if proving_info.sent_ids.max().unwrap_or_default() > self.state.sent_len + || proving_info.recv_ids.max().unwrap_or_default() > self.state.recv_len + { + return Err(VerifierError::InvalidRange); + } + + // Now verify the transcript parts which the prover wants to reveal + let sent_value_ids = proving_info + .sent_ids + .iter_ranges() + .map(|r| get_value_ids(&r.into(), Direction::Sent).collect::>()); + let recv_value_ids = proving_info + .recv_ids + .iter_ranges() + .map(|r| get_value_ids(&r.into(), Direction::Received).collect::>()); + + let value_refs = sent_value_ids + .chain(recv_value_ids) + .map(|ids| { + let inner_refs = ids + .iter() + .map(|id| { + verify_thread + .get_value(id.as_str()) + .expect("Byte should be in VM memory") + }) + .collect::>(); + + verify_thread + .array_from_values(inner_refs.as_slice()) + .expect("Byte should be in VM Memory") + }) + .collect::>(); + + let values = proving_info + .sent_ids + .iter_ranges() + .chain(proving_info.recv_ids.iter_ranges()) + .map(|range| { + Value::Array(cleartext.drain(..range.len()).map(|b| (b).into()).collect()) + }) + .collect::>(); + + // Check that purported values are correct + verify_thread.verify(&value_refs, &values).await?; + + #[cfg(feature = "tracing")] + info!("Successfully verified purported cleartext"); + + // Create redacted transcripts + let mut transcripts = proving_info + .sent_ids + .iter_ranges() + .chain(proving_info.recv_ids.iter_ranges()) + .map(|range| { + TranscriptSlice::new( + range.clone(), + proving_info.cleartext.drain(..range.len()).collect(), + ) + }) + .collect::>(); + + let recv_transcripts = + transcripts.split_off(proving_info.sent_ids.iter_ranges().count()); + let (sent_redacted, recv_redacted) = ( + RedactedTranscript::new(self.state.sent_len, transcripts), + RedactedTranscript::new(self.state.recv_len, recv_transcripts), + ); + + #[cfg(feature = "tracing")] + info!("Successfully created redacted transcripts"); + + Ok::<_, VerifierError>((sent_redacted, recv_redacted)) + }; + + futures::select! { + res = verify_fut.fuse() => res, + _ = &mut self.state.mux_fut => Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?, + } + } + + /// Verify the TLS session. + pub async fn finalize(self) -> Result { + let VerifyState { + mut mux, + mut mux_fut, + mut vm, + ot_send, + ot_recv, + ot_fut, + mut gf2, + start_time, + server_ephemeral_key, + handshake_commitment, + .. + } = self.state; + + let finalize_fut = async { + let mut channel = mux.get_channel("finalize").await?; + + // Finalize all MPC + let (mut ot_sender_actor, _, _) = futures::try_join!( + ot_fut, + ot_send.shutdown().map_err(VerifierError::from), + ot_recv.shutdown().map_err(VerifierError::from) + )?; + + ot_sender_actor.reveal().await?; + + vm.finalize() + .await + .map_err(|e| VerifierError::MpcError(Box::new(e)))?; + + gf2.verify() + .await + .map_err(|e| VerifierError::MpcError(Box::new(e)))?; + + let session_info = expect_msg_or_err!(channel, TlsnMessage::SessionInfo)?; + + #[cfg(feature = "tracing")] + info!("Finalized all MPC"); + + Ok::<_, VerifierError>(session_info) + }; + + let session_info = futures::select! { + res = finalize_fut.fuse() => res?, + _ = &mut mux_fut => Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?, + }; + + let handshake_summary = + HandshakeSummary::new(start_time, server_ephemeral_key, handshake_commitment); + + // Verify the TLS session + session_info.verify(&handshake_summary, self.config.cert_verifier())?; + + #[cfg(feature = "tracing")] + info!("Successfully verified session"); + + let mut mux = mux.into_inner(); + + futures::try_join!(mux.close().map_err(VerifierError::from), mux_fut)?; + + Ok(session_info) + } +}