diff --git a/Cargo.lock b/Cargo.lock index 1663fedec..dcc9dbf3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7314,6 +7314,7 @@ dependencies = [ "p256", "rand 0.9.2", "rand06-compat", + "rangeset 0.4.0", "rstest", "serde", "thiserror 1.0.69", diff --git a/crates/attestation/Cargo.toml b/crates/attestation/Cargo.toml index 1f8afbc80..4a206ae71 100644 --- a/crates/attestation/Cargo.toml +++ b/crates/attestation/Cargo.toml @@ -27,6 +27,7 @@ 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 } rand06-compat = { workspace = true } +rangeset = { workspace = true } rstest = { workspace = true } tlsn-core = { workspace = true, features = ["fixtures"] } tlsn-data-fixtures = { workspace = true } diff --git a/crates/attestation/src/builder.rs b/crates/attestation/src/builder.rs index 731f79403..714553cd1 100644 --- a/crates/attestation/src/builder.rs +++ b/crates/attestation/src/builder.rs @@ -5,7 +5,7 @@ use rand::{Rng, rng}; use tlsn_core::{ connection::{ConnectionInfo, ServerEphemKey}, hash::HashAlgId, - transcript::{TranscriptCommitment, encoding::EncoderSecret}, + transcript::TranscriptCommitment, }; use crate::{ @@ -25,7 +25,6 @@ pub struct Sign { connection_info: Option, server_ephemeral_key: Option, cert_commitment: ServerCertCommitment, - encoder_secret: Option, extensions: Vec, transcript_commitments: Vec, } @@ -87,7 +86,6 @@ impl<'a> AttestationBuilder<'a, Accept> { connection_info: None, server_ephemeral_key: None, cert_commitment, - encoder_secret: None, transcript_commitments: Vec::new(), extensions, }, @@ -108,12 +106,6 @@ impl AttestationBuilder<'_, Sign> { self } - /// Sets the secret for encoding commitments. - pub fn encoder_secret(&mut self, secret: EncoderSecret) -> &mut Self { - self.state.encoder_secret = Some(secret); - self - } - /// Adds an extension to the attestation. pub fn extension(&mut self, extension: Extension) -> &mut Self { self.state.extensions.push(extension); @@ -137,7 +129,6 @@ impl AttestationBuilder<'_, Sign> { connection_info, server_ephemeral_key, cert_commitment, - encoder_secret, extensions, transcript_commitments, } = self.state; @@ -168,7 +159,6 @@ impl AttestationBuilder<'_, Sign> { AttestationBuilderError::new(ErrorKind::Field, "handshake data was not set") })?), cert_commitment: field_id.next(cert_commitment), - encoder_secret: encoder_secret.map(|secret| field_id.next(secret)), extensions: extensions .into_iter() .map(|extension| field_id.next(extension)) @@ -253,7 +243,7 @@ mod test { use rstest::{fixture, rstest}; use tlsn_core::{ connection::{CertBinding, CertBindingV1_2}, - fixtures::{ConnectionFixture, encoding_provider}, + fixtures::ConnectionFixture, hash::Blake3, transcript::Transcript, }; @@ -285,13 +275,8 @@ mod test { let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON); let connection = ConnectionFixture::tlsnotary(transcript.length()); - let RequestFixture { request, .. } = request_fixture( - transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), - connection, - Blake3::default(), - Vec::new(), - ); + let RequestFixture { request, .. } = + request_fixture(transcript, connection, Blake3::default(), Vec::new()); let attestation_config = AttestationConfig::builder() .supported_signature_algs([SignatureAlgId::SECP256R1]) @@ -310,13 +295,8 @@ mod test { let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON); let connection = ConnectionFixture::tlsnotary(transcript.length()); - let RequestFixture { request, .. } = request_fixture( - transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), - connection, - Blake3::default(), - Vec::new(), - ); + let RequestFixture { request, .. } = + request_fixture(transcript, connection, Blake3::default(), Vec::new()); let attestation_config = AttestationConfig::builder() .supported_signature_algs([SignatureAlgId::SECP256K1]) @@ -336,13 +316,8 @@ mod test { let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON); let connection = ConnectionFixture::tlsnotary(transcript.length()); - let RequestFixture { request, .. } = request_fixture( - transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), - connection, - Blake3::default(), - Vec::new(), - ); + let RequestFixture { request, .. } = + request_fixture(transcript, connection, Blake3::default(), Vec::new()); let attestation_builder = Attestation::builder(attestation_config) .accept_request(request) @@ -365,7 +340,6 @@ mod test { let RequestFixture { request, .. } = request_fixture( transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), connection.clone(), Blake3::default(), Vec::new(), @@ -395,7 +369,6 @@ mod test { let RequestFixture { request, .. } = request_fixture( transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), connection.clone(), Blake3::default(), Vec::new(), @@ -432,7 +405,6 @@ mod test { let RequestFixture { request, .. } = request_fixture( transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), connection.clone(), Blake3::default(), vec![Extension { @@ -461,7 +433,6 @@ mod test { let RequestFixture { request, .. } = request_fixture( transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), connection.clone(), Blake3::default(), vec![Extension { diff --git a/crates/attestation/src/fixtures.rs b/crates/attestation/src/fixtures.rs index 405a33057..a9b996367 100644 --- a/crates/attestation/src/fixtures.rs +++ b/crates/attestation/src/fixtures.rs @@ -3,10 +3,7 @@ use tlsn_core::{ connection::{CertBinding, CertBindingV1_2}, fixtures::ConnectionFixture, hash::HashAlgorithm, - transcript::{ - Transcript, TranscriptCommitConfigBuilder, TranscriptCommitment, - encoding::{EncodingProvider, EncodingTree}, - }, + transcript::{Transcript, TranscriptCommitConfigBuilder, TranscriptCommitment}, }; use crate::{ @@ -21,16 +18,14 @@ use crate::{ /// A Request fixture used for testing. #[allow(missing_docs)] pub struct RequestFixture { - pub encoding_tree: EncodingTree, pub request: Request, } /// Returns a request fixture for testing. pub fn request_fixture( transcript: Transcript, - encodings_provider: impl EncodingProvider, connection: ConnectionFixture, - encoding_hasher: impl HashAlgorithm, + _hasher: impl HashAlgorithm, extensions: Vec, ) -> RequestFixture { let provider = CryptoProvider::default(); @@ -50,16 +45,10 @@ pub fn request_fixture( .unwrap(); let transcripts_commitment_config = transcript_commitment_builder.build().unwrap(); - // Prover constructs encoding tree. - let encoding_tree = EncodingTree::new( - &encoding_hasher, - transcripts_commitment_config.iter_encoding(), - &encodings_provider, - ) - .unwrap(); - let mut builder = RequestConfig::builder(); + builder.transcript_commit(transcripts_commitment_config); + for extension in extensions { builder.extension(extension); } @@ -74,10 +63,7 @@ pub fn request_fixture( let (request, _) = request_builder.build(&provider).unwrap(); - RequestFixture { - encoding_tree, - request, - } + RequestFixture { request } } /// Returns an attestation fixture for testing. diff --git a/crates/attestation/src/lib.rs b/crates/attestation/src/lib.rs index 089c8d801..2b1d400e1 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -79,8 +79,6 @@ //! //! // Specify all the transcript commitments we want to make. //! builder -//! // Use BLAKE3 for encoding commitments. -//! .encoding_hash_alg(HashAlgId::BLAKE3) //! // Commit to all sent data. //! .commit_sent(&(0..sent_len))? //! // Commit to the first 10 bytes of sent data. @@ -129,7 +127,7 @@ //! //! ```no_run //! # use tlsn_attestation::{Attestation, CryptoProvider, Secrets, presentation::Presentation}; -//! # use tlsn_core::transcript::{TranscriptCommitmentKind, Direction}; +//! # use tlsn_core::transcript::Direction; //! # fn main() -> Result<(), Box> { //! # let attestation: Attestation = unimplemented!(); //! # let secrets: Secrets = unimplemented!(); @@ -140,8 +138,6 @@ //! let mut builder = secrets.transcript_proof_builder(); //! //! builder -//! // Use transcript encoding commitments. -//! .commitment_kinds(&[TranscriptCommitmentKind::Encoding]) //! // Disclose the first 10 bytes of the sent data. //! .reveal(&(0..10), Direction::Sent)? //! // Disclose all of the received data. @@ -219,7 +215,7 @@ use tlsn_core::{ connection::{ConnectionInfo, ServerEphemKey}, hash::{Hash, HashAlgorithm, TypedHash}, merkle::MerkleTree, - transcript::{TranscriptCommitment, encoding::EncoderSecret}, + transcript::TranscriptCommitment, }; use crate::{ @@ -301,8 +297,6 @@ pub enum FieldKind { ServerEphemKey = 0x02, /// Server identity commitment. ServerIdentityCommitment = 0x03, - /// Encoding commitment. - EncodingCommitment = 0x04, /// Plaintext hash commitment. PlaintextHash = 0x05, } @@ -327,7 +321,6 @@ pub struct Body { connection_info: Field, server_ephemeral_key: Field, cert_commitment: Field, - encoder_secret: Option>, extensions: Vec>, transcript_commitments: Vec>, } @@ -373,7 +366,6 @@ impl Body { connection_info: conn_info, server_ephemeral_key, cert_commitment, - encoder_secret, extensions, transcript_commitments, } = self; @@ -391,13 +383,6 @@ impl Body { ), ]; - if let Some(encoder_secret) = encoder_secret { - fields.push(( - encoder_secret.id, - hasher.hash_separated(&encoder_secret.data), - )); - } - for field in extensions.iter() { fields.push((field.id, hasher.hash_separated(&field.data))); } diff --git a/crates/attestation/src/presentation.rs b/crates/attestation/src/presentation.rs index efaa79c9e..754b043fd 100644 --- a/crates/attestation/src/presentation.rs +++ b/crates/attestation/src/presentation.rs @@ -91,11 +91,6 @@ impl Presentation { transcript.verify_with_provider( &provider.hash, &attestation.body.connection_info().transcript_length, - attestation - .body - .encoder_secret - .as_ref() - .map(|field| &field.data), attestation.body.transcript_commitments(), ) }) diff --git a/crates/attestation/src/request.rs b/crates/attestation/src/request.rs index 5d6bc2b71..603a96a11 100644 --- a/crates/attestation/src/request.rs +++ b/crates/attestation/src/request.rs @@ -145,7 +145,7 @@ impl std::fmt::Display for ErrorKind { mod test { use tlsn_core::{ connection::TranscriptLength, - fixtures::{ConnectionFixture, encoding_provider}, + fixtures::ConnectionFixture, hash::{Blake3, HashAlgId}, transcript::Transcript, }; @@ -166,7 +166,6 @@ mod test { let RequestFixture { request, .. } = request_fixture( transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), connection.clone(), Blake3::default(), Vec::new(), @@ -187,7 +186,6 @@ mod test { let RequestFixture { mut request, .. } = request_fixture( transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), connection.clone(), Blake3::default(), Vec::new(), @@ -211,7 +209,6 @@ mod test { let RequestFixture { mut request, .. } = request_fixture( transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), connection.clone(), Blake3::default(), Vec::new(), @@ -235,7 +232,6 @@ mod test { let RequestFixture { mut request, .. } = request_fixture( transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), connection.clone(), Blake3::default(), Vec::new(), @@ -267,7 +263,6 @@ mod test { let RequestFixture { request, .. } = request_fixture( transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), connection.clone(), Blake3::default(), Vec::new(), @@ -291,7 +286,6 @@ mod test { let RequestFixture { request, .. } = request_fixture( transcript, - encoding_provider(GET_WITH_HEADER, OK_JSON), connection.clone(), Blake3::default(), Vec::new(), diff --git a/crates/attestation/src/serialize.rs b/crates/attestation/src/serialize.rs index 285c3371d..08adf62bf 100644 --- a/crates/attestation/src/serialize.rs +++ b/crates/attestation/src/serialize.rs @@ -49,6 +49,4 @@ impl_domain_separator!(tlsn_core::connection::ConnectionInfo); impl_domain_separator!(tlsn_core::connection::CertBinding); impl_domain_separator!(tlsn_core::transcript::TranscriptCommitment); impl_domain_separator!(tlsn_core::transcript::TranscriptSecret); -impl_domain_separator!(tlsn_core::transcript::encoding::EncoderSecret); -impl_domain_separator!(tlsn_core::transcript::encoding::EncodingCommitment); impl_domain_separator!(tlsn_core::transcript::hash::PlaintextHash); diff --git a/crates/attestation/tests/api.rs b/crates/attestation/tests/api.rs index 64c94d87a..08eab4d47 100644 --- a/crates/attestation/tests/api.rs +++ b/crates/attestation/tests/api.rs @@ -1,3 +1,5 @@ +use rand::{Rng, SeedableRng, rngs::StdRng}; +use rangeset::set::RangeSet; use tlsn_attestation::{ Attestation, AttestationConfig, CryptoProvider, presentation::PresentationOutput, @@ -6,12 +8,11 @@ use tlsn_attestation::{ }; use tlsn_core::{ connection::{CertBinding, CertBindingV1_2}, - fixtures::{self, ConnectionFixture, encoder_secret}, - hash::Blake3, + fixtures::ConnectionFixture, + hash::{Blake3, Blinder, HashAlgId}, transcript::{ - Direction, Transcript, TranscriptCommitConfigBuilder, TranscriptCommitment, - TranscriptSecret, - encoding::{EncodingCommitment, EncodingTree}, + Direction, Transcript, TranscriptCommitment, TranscriptSecret, + hash::{PlaintextHash, PlaintextHashSecret, hash_plaintext}, }, }; use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON}; @@ -19,6 +20,7 @@ use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON}; /// Tests that the attestation protocol and verification work end-to-end #[test] fn test_api() { + let mut rng = StdRng::seed_from_u64(0); let mut provider = CryptoProvider::default(); // Configure signer for Notary @@ -26,8 +28,6 @@ fn test_api() { let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON); let (sent_len, recv_len) = transcript.len(); - // Plaintext encodings which the Prover obtained from GC evaluation - let encodings_provider = fixtures::encoding_provider(GET_WITH_HEADER, OK_JSON); // At the end of the TLS connection the Prover holds the: let ConnectionFixture { @@ -44,26 +44,38 @@ fn test_api() { unreachable!() }; - // Prover specifies the ranges it wants to commit to. - let mut transcript_commitment_builder = TranscriptCommitConfigBuilder::new(&transcript); - transcript_commitment_builder - .commit_sent(&(0..sent_len)) - .unwrap() - .commit_recv(&(0..recv_len)) - .unwrap(); + // Create hash commitments + let hasher = Blake3::default(); + let sent_blinder: Blinder = rng.random(); + let recv_blinder: Blinder = rng.random(); - let transcripts_commitment_config = transcript_commitment_builder.build().unwrap(); + let sent_idx = RangeSet::from(0..sent_len); + let recv_idx = RangeSet::from(0..recv_len); - // Prover constructs encoding tree. - let encoding_tree = EncodingTree::new( - &Blake3::default(), - transcripts_commitment_config.iter_encoding(), - &encodings_provider, - ) - .unwrap(); + let sent_hash_commitment = PlaintextHash { + direction: Direction::Sent, + idx: sent_idx.clone(), + hash: hash_plaintext(&hasher, transcript.sent(), &sent_blinder), + }; - let encoding_commitment = EncodingCommitment { - root: encoding_tree.root(), + let recv_hash_commitment = PlaintextHash { + direction: Direction::Received, + idx: recv_idx.clone(), + hash: hash_plaintext(&hasher, transcript.received(), &recv_blinder), + }; + + let sent_hash_secret = PlaintextHashSecret { + direction: Direction::Sent, + idx: sent_idx, + alg: HashAlgId::BLAKE3, + blinder: sent_blinder, + }; + + let recv_hash_secret = PlaintextHashSecret { + direction: Direction::Received, + idx: recv_idx, + alg: HashAlgId::BLAKE3, + blinder: recv_blinder, }; let request_config = RequestConfig::default(); @@ -74,8 +86,14 @@ fn test_api() { .handshake_data(server_cert_data) .transcript(transcript) .transcript_commitments( - vec![TranscriptSecret::Encoding(encoding_tree)], - vec![TranscriptCommitment::Encoding(encoding_commitment.clone())], + vec![ + TranscriptSecret::Hash(sent_hash_secret), + TranscriptSecret::Hash(recv_hash_secret), + ], + vec![ + TranscriptCommitment::Hash(sent_hash_commitment.clone()), + TranscriptCommitment::Hash(recv_hash_commitment.clone()), + ], ); let (request, secrets) = request_builder.build(&provider).unwrap(); @@ -95,8 +113,10 @@ fn test_api() { .connection_info(connection_info.clone()) // Server key Notary received during handshake .server_ephemeral_key(server_ephemeral_key) - .encoder_secret(encoder_secret()) - .transcript_commitments(vec![TranscriptCommitment::Encoding(encoding_commitment)]); + .transcript_commitments(vec![ + TranscriptCommitment::Hash(sent_hash_commitment), + TranscriptCommitment::Hash(recv_hash_commitment), + ]); let attestation = attestation_builder.build(&provider).unwrap(); diff --git a/crates/core/src/fixtures.rs b/crates/core/src/fixtures.rs index 6ee8caa0b..7b108f8d7 100644 --- a/crates/core/src/fixtures.rs +++ b/crates/core/src/fixtures.rs @@ -1,10 +1,7 @@ //! Fixtures for testing -mod provider; pub mod transcript; -pub use provider::FixtureEncodingProvider; - use hex::FromHex; use crate::{ @@ -13,10 +10,6 @@ use crate::{ ServerEphemKey, ServerName, ServerSignature, SignatureAlgorithm, TlsVersion, TranscriptLength, }, - transcript::{ - encoding::{EncoderSecret, EncodingProvider}, - Transcript, - }, webpki::CertificateDer, }; @@ -129,27 +122,3 @@ impl ConnectionFixture { server_ephemeral_key } } - -/// Returns an encoding provider fixture. -pub fn encoding_provider(tx: &[u8], rx: &[u8]) -> impl EncodingProvider { - let secret = encoder_secret(); - FixtureEncodingProvider::new(&secret, Transcript::new(tx, rx)) -} - -/// Seed fixture. -const SEED: [u8; 32] = [0; 32]; - -/// Delta fixture. -const DELTA: [u8; 16] = [1; 16]; - -/// Returns an encoder secret fixture. -pub fn encoder_secret() -> EncoderSecret { - EncoderSecret::new(SEED, DELTA) -} - -/// Returns a tampered encoder secret fixture. -pub fn encoder_secret_tampered_seed() -> EncoderSecret { - let mut seed = SEED; - seed[0] += 1; - EncoderSecret::new(seed, DELTA) -} diff --git a/crates/core/src/fixtures/provider.rs b/crates/core/src/fixtures/provider.rs deleted file mode 100644 index 2dfeb1932..000000000 --- a/crates/core/src/fixtures/provider.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::ops::Range; - -use crate::transcript::{ - encoding::{new_encoder, Encoder, EncoderSecret, EncodingProvider, EncodingProviderError}, - Direction, Transcript, -}; - -/// A encoding provider fixture. -pub struct FixtureEncodingProvider { - encoder: Box, - transcript: Transcript, -} - -impl FixtureEncodingProvider { - /// Creates a new encoding provider fixture. - pub(crate) fn new(secret: &EncoderSecret, transcript: Transcript) -> Self { - Self { - encoder: Box::new(new_encoder(secret)), - transcript, - } - } -} - -impl EncodingProvider for FixtureEncodingProvider { - fn provide_encoding( - &self, - direction: Direction, - range: Range, - dest: &mut Vec, - ) -> Result<(), EncodingProviderError> { - let transcript = match direction { - Direction::Sent => &self.transcript.sent(), - Direction::Received => &self.transcript.received(), - }; - - let data = transcript.get(range.clone()).ok_or(EncodingProviderError)?; - self.encoder.encode_data(direction, range, data, dest); - - Ok(()) - } -} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 3be20f3b6..f1b4ed578 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -19,9 +19,7 @@ use serde::{Deserialize, Serialize}; use crate::{ connection::ServerName, - transcript::{ - encoding::EncoderSecret, PartialTranscript, TranscriptCommitment, TranscriptSecret, - }, + transcript::{PartialTranscript, TranscriptCommitment, TranscriptSecret}, }; /// Prover output. @@ -42,8 +40,6 @@ pub struct VerifierOutput { pub server_name: Option, /// Transcript data. pub transcript: Option, - /// Encoding commitment secret. - pub encoder_secret: Option, /// Transcript commitments. pub transcript_commitments: Vec, } diff --git a/crates/core/src/merkle.rs b/crates/core/src/merkle.rs index e12ead0cd..e291fbbd5 100644 --- a/crates/core/src/merkle.rs +++ b/crates/core/src/merkle.rs @@ -63,11 +63,6 @@ impl MerkleProof { Ok(()) } - - /// Returns the leaf count of the Merkle tree associated with the proof. - pub(crate) fn leaf_count(&self) -> usize { - self.leaf_count - } } #[derive(Clone)] diff --git a/crates/core/src/transcript.rs b/crates/core/src/transcript.rs index c9c3bc358..5e2208372 100644 --- a/crates/core/src/transcript.rs +++ b/crates/core/src/transcript.rs @@ -19,7 +19,6 @@ //! withheld. mod commit; -pub mod encoding; pub mod hash; mod proof; mod tls; diff --git a/crates/core/src/transcript/commit.rs b/crates/core/src/transcript/commit.rs index da656ec30..239d35e76 100644 --- a/crates/core/src/transcript/commit.rs +++ b/crates/core/src/transcript/commit.rs @@ -8,27 +8,15 @@ use serde::{Deserialize, Serialize}; use crate::{ hash::HashAlgId, transcript::{ - encoding::{EncodingCommitment, EncodingTree}, hash::{PlaintextHash, PlaintextHashSecret}, Direction, RangeSet, Transcript, }, }; -/// The maximum allowed total bytelength of committed data for a single -/// commitment kind. Used to prevent DoS during verification. (May cause the -/// verifier to hash up to a max of 1GB * 128 = 128GB of data for certain kinds -/// of encoding commitments.) -/// -/// This value must not exceed bcs's MAX_SEQUENCE_LENGTH limit (which is (1 << -/// 31) - 1 by default) -pub(crate) const MAX_TOTAL_COMMITTED_DATA: usize = 1_000_000_000; - /// Kind of transcript commitment. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[non_exhaustive] pub enum TranscriptCommitmentKind { - /// A commitment to encodings of the transcript. - Encoding, /// A hash commitment to plaintext in the transcript. Hash { /// The hash algorithm used. @@ -39,7 +27,6 @@ pub enum TranscriptCommitmentKind { impl fmt::Display for TranscriptCommitmentKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Encoding => f.write_str("encoding"), Self::Hash { alg } => write!(f, "hash ({alg})"), } } @@ -49,8 +36,6 @@ impl fmt::Display for TranscriptCommitmentKind { #[derive(Debug, Clone, Serialize, Deserialize)] #[non_exhaustive] pub enum TranscriptCommitment { - /// Encoding commitment. - Encoding(EncodingCommitment), /// Plaintext hash commitment. Hash(PlaintextHash), } @@ -59,8 +44,6 @@ pub enum TranscriptCommitment { #[derive(Debug, Clone, Serialize, Deserialize)] #[non_exhaustive] pub enum TranscriptSecret { - /// Encoding tree. - Encoding(EncodingTree), /// Plaintext hash secret. Hash(PlaintextHashSecret), } @@ -68,9 +51,6 @@ pub enum TranscriptSecret { /// Configuration for transcript commitments. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TranscriptCommitConfig { - encoding_hash_alg: HashAlgId, - has_encoding: bool, - has_hash: bool, commits: Vec<((Direction, RangeSet), TranscriptCommitmentKind)>, } @@ -80,53 +60,23 @@ impl TranscriptCommitConfig { TranscriptCommitConfigBuilder::new(transcript) } - /// Returns the hash algorithm to use for encoding commitments. - pub fn encoding_hash_alg(&self) -> &HashAlgId { - &self.encoding_hash_alg - } - - /// Returns `true` if the configuration has any encoding commitments. - pub fn has_encoding(&self) -> bool { - self.has_encoding - } - /// Returns `true` if the configuration has any hash commitments. pub fn has_hash(&self) -> bool { - self.has_hash - } - - /// Returns an iterator over the encoding commitment indices. - pub fn iter_encoding(&self) -> impl Iterator)> { - self.commits.iter().filter_map(|(idx, kind)| match kind { - TranscriptCommitmentKind::Encoding => Some(idx), - _ => None, - }) + self.commits + .iter() + .any(|(_, kind)| matches!(kind, TranscriptCommitmentKind::Hash { .. })) } /// Returns an iterator over the hash commitment indices. pub fn iter_hash(&self) -> impl Iterator), &HashAlgId)> { self.commits.iter().filter_map(|(idx, kind)| match kind { TranscriptCommitmentKind::Hash { alg } => Some((idx, alg)), - _ => None, }) } /// Returns a request for the transcript commitments. pub fn to_request(&self) -> TranscriptCommitRequest { TranscriptCommitRequest { - encoding: self.has_encoding.then(|| { - let mut sent = RangeSet::default(); - let mut recv = RangeSet::default(); - - for (dir, idx) in self.iter_encoding() { - match dir { - Direction::Sent => sent.union_mut(idx), - Direction::Received => recv.union_mut(idx), - } - } - - (sent, recv) - }), hash: self .iter_hash() .map(|((dir, idx), alg)| (*dir, idx.clone(), *alg)) @@ -136,15 +86,9 @@ impl TranscriptCommitConfig { } /// A builder for [`TranscriptCommitConfig`]. -/// -/// The default hash algorithm is [`HashAlgId::BLAKE3`] and the default kind -/// is [`TranscriptCommitmentKind::Encoding`]. #[derive(Debug)] pub struct TranscriptCommitConfigBuilder<'a> { transcript: &'a Transcript, - encoding_hash_alg: HashAlgId, - has_encoding: bool, - has_hash: bool, default_kind: TranscriptCommitmentKind, commits: HashSet<((Direction, RangeSet), TranscriptCommitmentKind)>, } @@ -154,20 +98,13 @@ impl<'a> TranscriptCommitConfigBuilder<'a> { pub fn new(transcript: &'a Transcript) -> Self { Self { transcript, - encoding_hash_alg: HashAlgId::BLAKE3, - has_encoding: false, - has_hash: false, - default_kind: TranscriptCommitmentKind::Encoding, + default_kind: TranscriptCommitmentKind::Hash { + alg: HashAlgId::BLAKE3, + }, commits: HashSet::default(), } } - /// Sets the hash algorithm to use for encoding commitments. - pub fn encoding_hash_alg(&mut self, alg: HashAlgId) -> &mut Self { - self.encoding_hash_alg = alg; - self - } - /// Sets the default kind of commitment to use. pub fn default_kind(&mut self, default_kind: TranscriptCommitmentKind) -> &mut Self { self.default_kind = default_kind; @@ -201,11 +138,6 @@ impl<'a> TranscriptCommitConfigBuilder<'a> { )); } - match kind { - TranscriptCommitmentKind::Encoding => self.has_encoding = true, - TranscriptCommitmentKind::Hash { .. } => self.has_hash = true, - } - self.commits.insert(((direction, idx), kind)); Ok(self) @@ -252,9 +184,6 @@ impl<'a> TranscriptCommitConfigBuilder<'a> { /// Builds the configuration. pub fn build(self) -> Result { Ok(TranscriptCommitConfig { - encoding_hash_alg: self.encoding_hash_alg, - has_encoding: self.has_encoding, - has_hash: self.has_hash, commits: Vec::from_iter(self.commits), }) } @@ -301,16 +230,10 @@ impl fmt::Display for TranscriptCommitConfigBuilderError { /// Request to compute transcript commitments. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TranscriptCommitRequest { - encoding: Option<(RangeSet, RangeSet)>, hash: Vec<(Direction, RangeSet, HashAlgId)>, } impl TranscriptCommitRequest { - /// Returns `true` if an encoding commitment is requested. - pub fn has_encoding(&self) -> bool { - self.encoding.is_some() - } - /// Returns `true` if a hash commitment is requested. pub fn has_hash(&self) -> bool { !self.hash.is_empty() @@ -320,11 +243,6 @@ impl TranscriptCommitRequest { pub fn iter_hash(&self) -> impl Iterator, HashAlgId)> { self.hash.iter() } - - /// Returns the ranges of the encoding commitments. - pub fn encoding(&self) -> Option<&(RangeSet, RangeSet)> { - self.encoding.as_ref() - } } #[cfg(test)] diff --git a/crates/core/src/transcript/encoding.rs b/crates/core/src/transcript/encoding.rs deleted file mode 100644 index 985e738fd..000000000 --- a/crates/core/src/transcript/encoding.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! Transcript encoding commitments and proofs. - -mod encoder; -mod proof; -mod provider; -mod tree; - -pub use encoder::{new_encoder, Encoder, EncoderSecret}; -pub use proof::{EncodingProof, EncodingProofError}; -pub use provider::{EncodingProvider, EncodingProviderError}; -pub use tree::{EncodingTree, EncodingTreeError}; - -use serde::{Deserialize, Serialize}; - -use crate::hash::TypedHash; - -/// Transcript encoding commitment. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct EncodingCommitment { - /// Merkle root of the encoding commitments. - pub root: TypedHash, -} diff --git a/crates/core/src/transcript/encoding/encoder.rs b/crates/core/src/transcript/encoding/encoder.rs deleted file mode 100644 index 86841520c..000000000 --- a/crates/core/src/transcript/encoding/encoder.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::ops::Range; - -use crate::transcript::Direction; -use itybity::ToBits; -use rand::{RngCore, SeedableRng}; -use rand_chacha::ChaCha12Rng; -use serde::{Deserialize, Serialize}; - -/// The size of the encoding for 1 bit, in bytes. -const BIT_ENCODING_SIZE: usize = 16; -/// The size of the encoding for 1 byte, in bytes. -const BYTE_ENCODING_SIZE: usize = 128; - -/// Secret used by an encoder to generate encodings. -#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct EncoderSecret { - seed: [u8; 32], - delta: [u8; BIT_ENCODING_SIZE], -} - -opaque_debug::implement!(EncoderSecret); - -impl EncoderSecret { - /// Creates a new secret. - /// - /// # Arguments - /// - /// * `seed` - The seed for the PRG. - /// * `delta` - Delta for deriving the one-encodings. - pub fn new(seed: [u8; 32], delta: [u8; 16]) -> Self { - Self { seed, delta } - } - - /// Returns the seed. - pub fn seed(&self) -> &[u8; 32] { - &self.seed - } - - /// Returns the delta. - pub fn delta(&self) -> &[u8; 16] { - &self.delta - } -} - -/// Creates a new encoder. -pub fn new_encoder(secret: &EncoderSecret) -> impl Encoder { - ChaChaEncoder::new(secret) -} - -pub(crate) struct ChaChaEncoder { - seed: [u8; 32], - delta: [u8; 16], -} - -impl ChaChaEncoder { - pub(crate) fn new(secret: &EncoderSecret) -> Self { - let seed = *secret.seed(); - let delta = *secret.delta(); - - Self { seed, delta } - } - - pub(crate) fn new_prg(&self, stream_id: u64) -> ChaCha12Rng { - let mut prg = ChaCha12Rng::from_seed(self.seed); - prg.set_stream(stream_id); - prg.set_word_pos(0); - prg - } -} - -/// A transcript encoder. -/// -/// This is an internal implementation detail that should not be exposed to the -/// public API. -pub trait Encoder { - /// Writes the zero encoding for the given range of the transcript into the - /// destination buffer. - fn encode_range(&self, direction: Direction, range: Range, dest: &mut Vec); - - /// Writes the encoding for the given data into the destination buffer. - fn encode_data( - &self, - direction: Direction, - range: Range, - data: &[u8], - dest: &mut Vec, - ); -} - -impl Encoder for ChaChaEncoder { - fn encode_range(&self, direction: Direction, range: Range, dest: &mut Vec) { - // ChaCha encoder works with 32-bit words. Each encoded bit is 128 bits long. - const WORDS_PER_BYTE: u128 = 8 * 128 / 32; - - let stream_id: u64 = match direction { - Direction::Sent => 0, - Direction::Received => 1, - }; - - let mut prg = self.new_prg(stream_id); - let len = range.len() * BYTE_ENCODING_SIZE; - let pos = dest.len(); - - // Write 0s to the destination buffer. - dest.resize(pos + len, 0); - - // Fill the destination buffer with the PRG. - prg.set_word_pos(range.start as u128 * WORDS_PER_BYTE); - prg.fill_bytes(&mut dest[pos..pos + len]); - } - - fn encode_data( - &self, - direction: Direction, - range: Range, - data: &[u8], - dest: &mut Vec, - ) { - const ZERO: [u8; 16] = [0; BIT_ENCODING_SIZE]; - - let pos = dest.len(); - - // Write the zero encoding for the given range. - self.encode_range(direction, range, dest); - let dest = &mut dest[pos..]; - - for (pos, bit) in data.iter_lsb0().enumerate() { - // Add the delta to the encoding whenever the encoded bit is 1, - // otherwise add a zero. - let summand = if bit { &self.delta } else { &ZERO }; - dest[pos * BIT_ENCODING_SIZE..(pos + 1) * BIT_ENCODING_SIZE] - .iter_mut() - .zip(summand) - .for_each(|(a, b)| *a ^= *b); - } - } -} diff --git a/crates/core/src/transcript/encoding/proof.rs b/crates/core/src/transcript/encoding/proof.rs deleted file mode 100644 index 880398ac5..000000000 --- a/crates/core/src/transcript/encoding/proof.rs +++ /dev/null @@ -1,361 +0,0 @@ -use std::{collections::HashMap, fmt}; - -use rangeset::set::RangeSet; -use serde::{Deserialize, Serialize}; - -use crate::{ - hash::{Blinder, HashProvider, HashProviderError}, - merkle::{MerkleError, MerkleProof}, - transcript::{ - commit::MAX_TOTAL_COMMITTED_DATA, - encoding::{new_encoder, Encoder, EncoderSecret, EncodingCommitment}, - Direction, - }, -}; - -/// An opening of a leaf in the encoding tree. -#[derive(Clone, Serialize, Deserialize)] -pub(super) struct Opening { - pub(super) direction: Direction, - pub(super) idx: RangeSet, - pub(super) blinder: Blinder, -} - -opaque_debug::implement!(Opening); - -/// An encoding commitment proof. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(try_from = "validation::EncodingProofUnchecked")] -pub struct EncodingProof { - /// The proof of inclusion of the commitment(s) in the Merkle tree of - /// commitments. - pub(super) inclusion_proof: MerkleProof, - pub(super) openings: HashMap, -} - -impl EncodingProof { - /// Verifies the proof against the commitment. - /// - /// Returns the authenticated indices of the sent and received data, - /// respectively. - /// - /// # Arguments - /// - /// * `provider` - Hash provider. - /// * `commitment` - Encoding commitment to verify against. - /// * `sent` - Sent data to authenticate. - /// * `recv` - Received data to authenticate. - pub fn verify_with_provider( - &self, - provider: &HashProvider, - secret: &EncoderSecret, - commitment: &EncodingCommitment, - sent: &[u8], - recv: &[u8], - ) -> Result<(RangeSet, RangeSet), EncodingProofError> { - let hasher = provider.get(&commitment.root.alg)?; - - let encoder = new_encoder(secret); - let Self { - inclusion_proof, - openings, - } = self; - - let mut leaves = Vec::with_capacity(openings.len()); - let mut expected_leaf = Vec::default(); - let mut total_opened = 0u128; - let mut auth_sent = RangeSet::default(); - let mut auth_recv = RangeSet::default(); - for ( - id, - Opening { - direction, - idx, - blinder, - }, - ) in openings - { - // Make sure the amount of data being proved is bounded. - total_opened += idx.len() as u128; - if total_opened > MAX_TOTAL_COMMITTED_DATA as u128 { - return Err(EncodingProofError::new( - ErrorKind::Proof, - "exceeded maximum allowed data", - ))?; - } - - let (data, auth) = match direction { - Direction::Sent => (sent, &mut auth_sent), - Direction::Received => (recv, &mut auth_recv), - }; - - // Make sure the ranges are within the bounds of the transcript. - if idx.end().unwrap_or(0) > data.len() { - return Err(EncodingProofError::new( - ErrorKind::Proof, - format!( - "index out of bounds of the transcript ({}): {} > {}", - direction, - idx.end().unwrap_or(0), - data.len() - ), - )); - } - - expected_leaf.clear(); - for range in idx.iter() { - encoder.encode_data(*direction, range.clone(), &data[range], &mut expected_leaf); - } - expected_leaf.extend_from_slice(blinder.as_bytes()); - - // Compute the expected hash of the commitment to make sure it is - // present in the merkle tree. - leaves.push((*id, hasher.hash(&expected_leaf))); - - auth.union_mut(idx); - } - - // Verify that the expected hashes are present in the merkle tree. - // - // This proves the Prover committed to the purported data prior to the encoder - // seed being revealed. Ergo, if the encodings are authentic then the purported - // data is authentic. - inclusion_proof.verify(hasher, &commitment.root, leaves)?; - - Ok((auth_sent, auth_recv)) - } -} - -/// Error for [`EncodingProof`]. -#[derive(Debug, thiserror::Error)] -pub struct EncodingProofError { - kind: ErrorKind, - source: Option>, -} - -impl EncodingProofError { - fn new(kind: ErrorKind, source: E) -> Self - where - E: Into>, - { - Self { - kind, - source: Some(source.into()), - } - } -} - -#[derive(Debug)] -enum ErrorKind { - Provider, - Proof, -} - -impl fmt::Display for EncodingProofError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("encoding proof error: ")?; - - match self.kind { - ErrorKind::Provider => f.write_str("provider error")?, - ErrorKind::Proof => f.write_str("proof error")?, - } - - if let Some(source) = &self.source { - write!(f, " caused by: {source}")?; - } - - Ok(()) - } -} - -impl From for EncodingProofError { - fn from(error: HashProviderError) -> Self { - Self::new(ErrorKind::Provider, error) - } -} - -impl From for EncodingProofError { - fn from(error: MerkleError) -> Self { - Self::new(ErrorKind::Proof, error) - } -} - -/// Invalid encoding proof error. -#[derive(Debug, thiserror::Error)] -#[error("invalid encoding proof: {0}")] -pub struct InvalidEncodingProof(&'static str); - -mod validation { - use super::*; - - /// The maximum allowed height of the Merkle tree of encoding commitments. - /// - /// The statistical security parameter (SSP) of the encoding commitment - /// protocol is calculated as "the number of uniformly random bits in a - /// single bit's encoding minus `MAX_HEIGHT`". - /// - /// For example, a bit encoding used in garbled circuits typically has 127 - /// uniformly random bits, hence when using it in the encoding - /// commitment protocol, the SSP is 127 - 30 = 97 bits. - /// - /// Leaving this validation here as a fail-safe in case we ever start - /// using shorter encodings. - const MAX_HEIGHT: usize = 30; - - #[derive(Debug, Deserialize)] - pub(super) struct EncodingProofUnchecked { - inclusion_proof: MerkleProof, - openings: HashMap, - } - - impl TryFrom for EncodingProof { - type Error = InvalidEncodingProof; - - fn try_from(unchecked: EncodingProofUnchecked) -> Result { - if unchecked.inclusion_proof.leaf_count() > 1 << MAX_HEIGHT { - return Err(InvalidEncodingProof( - "the height of the tree exceeds the maximum allowed", - )); - } - - Ok(Self { - inclusion_proof: unchecked.inclusion_proof, - openings: unchecked.openings, - }) - } - } -} - -#[cfg(test)] -mod test { - use tlsn_data_fixtures::http::{request::POST_JSON, response::OK_JSON}; - - use crate::{ - fixtures::{encoder_secret, encoder_secret_tampered_seed, encoding_provider}, - hash::Blake3, - transcript::{encoding::EncodingTree, Transcript}, - }; - - use super::*; - - struct EncodingFixture { - transcript: Transcript, - proof: EncodingProof, - commitment: EncodingCommitment, - } - - fn new_encoding_fixture() -> EncodingFixture { - let transcript = Transcript::new(POST_JSON, OK_JSON); - - let idx_0 = (Direction::Sent, RangeSet::from(0..POST_JSON.len())); - let idx_1 = (Direction::Received, RangeSet::from(0..OK_JSON.len())); - - let provider = encoding_provider(transcript.sent(), transcript.received()); - let tree = EncodingTree::new(&Blake3::default(), [&idx_0, &idx_1], &provider).unwrap(); - - let proof = tree.proof([&idx_0, &idx_1].into_iter()).unwrap(); - - let commitment = EncodingCommitment { root: tree.root() }; - - EncodingFixture { - transcript, - proof, - commitment, - } - } - - #[test] - fn test_verify_encoding_proof_tampered_seed() { - let EncodingFixture { - transcript, - proof, - commitment, - } = new_encoding_fixture(); - - let err = proof - .verify_with_provider( - &HashProvider::default(), - &encoder_secret_tampered_seed(), - &commitment, - transcript.sent(), - transcript.received(), - ) - .unwrap_err(); - - assert!(matches!(err.kind, ErrorKind::Proof)); - } - - #[test] - fn test_verify_encoding_proof_out_of_range() { - let EncodingFixture { - transcript, - proof, - commitment, - } = new_encoding_fixture(); - - let sent = &transcript.sent()[transcript.sent().len() - 1..]; - let recv = &transcript.received()[transcript.received().len() - 2..]; - - let err = proof - .verify_with_provider( - &HashProvider::default(), - &encoder_secret(), - &commitment, - sent, - recv, - ) - .unwrap_err(); - - assert!(matches!(err.kind, ErrorKind::Proof)); - } - - #[test] - fn test_verify_encoding_proof_tampered_idx() { - let EncodingFixture { - transcript, - mut proof, - commitment, - } = new_encoding_fixture(); - - let Opening { idx, .. } = proof.openings.values_mut().next().unwrap(); - - *idx = RangeSet::from([0..3, 13..15]); - - let err = proof - .verify_with_provider( - &HashProvider::default(), - &encoder_secret(), - &commitment, - transcript.sent(), - transcript.received(), - ) - .unwrap_err(); - - assert!(matches!(err.kind, ErrorKind::Proof)); - } - - #[test] - fn test_verify_encoding_proof_tampered_encoding_blinder() { - let EncodingFixture { - transcript, - mut proof, - commitment, - } = new_encoding_fixture(); - - let Opening { blinder, .. } = proof.openings.values_mut().next().unwrap(); - - *blinder = rand::random(); - - let err = proof - .verify_with_provider( - &HashProvider::default(), - &encoder_secret(), - &commitment, - transcript.sent(), - transcript.received(), - ) - .unwrap_err(); - - assert!(matches!(err.kind, ErrorKind::Proof)); - } -} diff --git a/crates/core/src/transcript/encoding/provider.rs b/crates/core/src/transcript/encoding/provider.rs deleted file mode 100644 index c70e666a4..000000000 --- a/crates/core/src/transcript/encoding/provider.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::ops::Range; - -use crate::transcript::Direction; - -/// A provider of plaintext encodings. -pub trait EncodingProvider { - /// Writes the encoding of the given range into the destination buffer. - fn provide_encoding( - &self, - direction: Direction, - range: Range, - dest: &mut Vec, - ) -> Result<(), EncodingProviderError>; -} - -/// Error for [`EncodingProvider`]. -#[derive(Debug, thiserror::Error)] -#[error("failed to provide encoding")] -pub struct EncodingProviderError; diff --git a/crates/core/src/transcript/encoding/tree.rs b/crates/core/src/transcript/encoding/tree.rs deleted file mode 100644 index f3adb4802..000000000 --- a/crates/core/src/transcript/encoding/tree.rs +++ /dev/null @@ -1,327 +0,0 @@ -use std::collections::HashMap; - -use bimap::BiMap; -use rangeset::set::RangeSet; -use serde::{Deserialize, Serialize}; - -use crate::{ - hash::{Blinder, HashAlgId, HashAlgorithm, TypedHash}, - merkle::MerkleTree, - transcript::{ - encoding::{ - proof::{EncodingProof, Opening}, - EncodingProvider, - }, - Direction, - }, -}; - -/// Encoding tree builder error. -#[derive(Debug, thiserror::Error)] -pub enum EncodingTreeError { - /// Index is out of bounds of the transcript. - #[error("index is out of bounds of the transcript")] - OutOfBounds { - /// The index. - index: RangeSet, - /// The transcript length. - transcript_length: usize, - }, - /// Encoding provider is missing an encoding for an index. - #[error("encoding provider is missing an encoding for an index")] - MissingEncoding { - /// The index which is missing. - index: RangeSet, - }, - /// Index is missing from the tree. - #[error("index is missing from the tree")] - MissingLeaf { - /// The index which is missing. - index: RangeSet, - }, -} - -/// A merkle tree of transcript encodings. -#[derive(Clone, Serialize, Deserialize)] -pub struct EncodingTree { - /// Merkle tree of the commitments. - tree: MerkleTree, - /// Nonces used to blind the hashes. - blinders: Vec, - /// Mapping between the index of a leaf and the transcript index it - /// corresponds to. - idxs: BiMap)>, - /// Union of all transcript indices in the sent direction. - sent_idx: RangeSet, - /// Union of all transcript indices in the received direction. - received_idx: RangeSet, -} - -opaque_debug::implement!(EncodingTree); - -impl EncodingTree { - /// Creates a new encoding tree. - /// - /// # Arguments - /// - /// * `hasher` - The hash algorithm to use. - /// * `idxs` - The subsequence indices to commit to. - /// * `provider` - The encoding provider. - pub fn new<'idx>( - hasher: &dyn HashAlgorithm, - idxs: impl IntoIterator)>, - provider: &dyn EncodingProvider, - ) -> Result { - let mut this = Self { - tree: MerkleTree::new(hasher.id()), - blinders: Vec::new(), - idxs: BiMap::new(), - sent_idx: RangeSet::default(), - received_idx: RangeSet::default(), - }; - - let mut leaves = Vec::new(); - let mut encoding = Vec::new(); - for dir_idx in idxs { - let direction = dir_idx.0; - let idx = &dir_idx.1; - - // Ignore empty indices. - if idx.is_empty() { - continue; - } - - if this.idxs.contains_right(dir_idx) { - // The subsequence is already in the tree. - continue; - } - - let blinder: Blinder = rand::random(); - - encoding.clear(); - for range in idx.iter() { - provider - .provide_encoding(direction, range, &mut encoding) - .map_err(|_| EncodingTreeError::MissingEncoding { index: idx.clone() })?; - } - encoding.extend_from_slice(blinder.as_bytes()); - - let leaf = hasher.hash(&encoding); - - leaves.push(leaf); - this.blinders.push(blinder); - this.idxs.insert(this.idxs.len(), dir_idx.clone()); - match direction { - Direction::Sent => this.sent_idx.union_mut(idx), - Direction::Received => this.received_idx.union_mut(idx), - } - } - - this.tree.insert(hasher, leaves); - - Ok(this) - } - - /// Returns the root of the tree. - pub fn root(&self) -> TypedHash { - self.tree.root() - } - - /// Returns the hash algorithm of the tree. - pub fn algorithm(&self) -> HashAlgId { - self.tree.algorithm() - } - - /// Generates a proof for the given indices. - /// - /// # Arguments - /// - /// * `idxs` - The transcript indices to prove. - pub fn proof<'idx>( - &self, - idxs: impl Iterator)>, - ) -> Result { - let mut openings = HashMap::new(); - for dir_idx in idxs { - let direction = dir_idx.0; - let idx = &dir_idx.1; - - let leaf_idx = *self - .idxs - .get_by_right(dir_idx) - .ok_or_else(|| EncodingTreeError::MissingLeaf { index: idx.clone() })?; - let blinder = self.blinders[leaf_idx].clone(); - - openings.insert( - leaf_idx, - Opening { - direction, - idx: idx.clone(), - blinder, - }, - ); - } - - let mut indices = openings.keys().copied().collect::>(); - indices.sort(); - - Ok(EncodingProof { - inclusion_proof: self.tree.proof(&indices), - openings, - }) - } - - /// Returns whether the tree contains the given transcript index. - pub fn contains(&self, idx: &(Direction, RangeSet)) -> bool { - self.idxs.contains_right(idx) - } - - pub(crate) fn idx(&self, direction: Direction) -> &RangeSet { - match direction { - Direction::Sent => &self.sent_idx, - Direction::Received => &self.received_idx, - } - } - - /// Returns the committed transcript indices. - pub(crate) fn transcript_indices(&self) -> impl Iterator)> { - self.idxs.right_values() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - fixtures::{encoder_secret, encoding_provider}, - hash::{Blake3, HashProvider}, - transcript::{encoding::EncodingCommitment, Transcript}, - }; - use tlsn_data_fixtures::http::{request::POST_JSON, response::OK_JSON}; - - fn new_tree<'seq>( - transcript: &Transcript, - idxs: impl Iterator)>, - ) -> Result { - let provider = encoding_provider(transcript.sent(), transcript.received()); - - EncodingTree::new(&Blake3::default(), idxs, &provider) - } - - #[test] - fn test_encoding_tree() { - let transcript = Transcript::new(POST_JSON, OK_JSON); - - let idx_0 = (Direction::Sent, RangeSet::from(0..POST_JSON.len())); - let idx_1 = (Direction::Received, RangeSet::from(0..OK_JSON.len())); - - let tree = new_tree(&transcript, [&idx_0, &idx_1].into_iter()).unwrap(); - - assert!(tree.contains(&idx_0)); - assert!(tree.contains(&idx_1)); - - let proof = tree.proof([&idx_0, &idx_1].into_iter()).unwrap(); - - let commitment = EncodingCommitment { root: tree.root() }; - - let (auth_sent, auth_recv) = proof - .verify_with_provider( - &HashProvider::default(), - &encoder_secret(), - &commitment, - transcript.sent(), - transcript.received(), - ) - .unwrap(); - - assert_eq!(auth_sent, idx_0.1); - assert_eq!(auth_recv, idx_1.1); - } - - #[test] - fn test_encoding_tree_multiple_ranges() { - let transcript = Transcript::new(POST_JSON, OK_JSON); - - let idx_0 = (Direction::Sent, RangeSet::from(0..1)); - let idx_1 = (Direction::Sent, RangeSet::from(1..POST_JSON.len())); - let idx_2 = (Direction::Received, RangeSet::from(0..1)); - let idx_3 = (Direction::Received, RangeSet::from(1..OK_JSON.len())); - - let tree = new_tree(&transcript, [&idx_0, &idx_1, &idx_2, &idx_3].into_iter()).unwrap(); - - assert!(tree.contains(&idx_0)); - assert!(tree.contains(&idx_1)); - assert!(tree.contains(&idx_2)); - assert!(tree.contains(&idx_3)); - - let proof = tree - .proof([&idx_0, &idx_1, &idx_2, &idx_3].into_iter()) - .unwrap(); - - let commitment = EncodingCommitment { root: tree.root() }; - - let (auth_sent, auth_recv) = proof - .verify_with_provider( - &HashProvider::default(), - &encoder_secret(), - &commitment, - transcript.sent(), - transcript.received(), - ) - .unwrap(); - - let mut expected_auth_sent = RangeSet::default(); - expected_auth_sent.union_mut(&idx_0.1); - expected_auth_sent.union_mut(&idx_1.1); - - let mut expected_auth_recv = RangeSet::default(); - expected_auth_recv.union_mut(&idx_2.1); - expected_auth_recv.union_mut(&idx_3.1); - - assert_eq!(auth_sent, expected_auth_sent); - assert_eq!(auth_recv, expected_auth_recv); - } - - #[test] - fn test_encoding_tree_proof_missing_leaf() { - let transcript = Transcript::new(POST_JSON, OK_JSON); - - let idx_0 = (Direction::Sent, RangeSet::from(0..POST_JSON.len())); - let idx_1 = (Direction::Received, RangeSet::from(0..4)); - let idx_2 = (Direction::Received, RangeSet::from(4..OK_JSON.len())); - - let tree = new_tree(&transcript, [&idx_0, &idx_1].into_iter()).unwrap(); - - let result = tree - .proof([&idx_0, &idx_1, &idx_2].into_iter()) - .unwrap_err(); - assert!(matches!(result, EncodingTreeError::MissingLeaf { .. })); - } - - #[test] - fn test_encoding_tree_out_of_bounds() { - let transcript = Transcript::new(POST_JSON, OK_JSON); - - let idx_0 = (Direction::Sent, RangeSet::from(0..POST_JSON.len() + 1)); - let idx_1 = (Direction::Received, RangeSet::from(0..OK_JSON.len() + 1)); - - let result = new_tree(&transcript, [&idx_0].into_iter()).unwrap_err(); - assert!(matches!(result, EncodingTreeError::MissingEncoding { .. })); - - let result = new_tree(&transcript, [&idx_1].into_iter()).unwrap_err(); - assert!(matches!(result, EncodingTreeError::MissingEncoding { .. })); - } - - #[test] - fn test_encoding_tree_missing_encoding() { - let provider = encoding_provider(&[], &[]); - - let result = EncodingTree::new( - &Blake3::default(), - [(Direction::Sent, RangeSet::from(0..8))].iter(), - &provider, - ) - .unwrap_err(); - assert!(matches!(result, EncodingTreeError::MissingEncoding { .. })); - } -} diff --git a/crates/core/src/transcript/proof.rs b/crates/core/src/transcript/proof.rs index 6d169d0fe..227694de8 100644 --- a/crates/core/src/transcript/proof.rs +++ b/crates/core/src/transcript/proof.rs @@ -14,7 +14,6 @@ use crate::{ hash::{HashAlgId, HashProvider}, transcript::{ commit::{TranscriptCommitment, TranscriptCommitmentKind}, - encoding::{EncoderSecret, EncodingProof, EncodingProofError, EncodingTree}, hash::{hash_plaintext, PlaintextHash, PlaintextHashSecret}, Direction, PartialTranscript, RangeSet, Transcript, TranscriptSecret, }, @@ -32,14 +31,12 @@ const DEFAULT_COMMITMENT_KINDS: &[TranscriptCommitmentKind] = &[ TranscriptCommitmentKind::Hash { alg: HashAlgId::KECCAK256, }, - TranscriptCommitmentKind::Encoding, ]; /// Proof of the contents of a transcript. #[derive(Clone, Serialize, Deserialize)] pub struct TranscriptProof { transcript: PartialTranscript, - encoding_proof: Option, hash_secrets: Vec, } @@ -53,27 +50,18 @@ impl TranscriptProof { /// # Arguments /// /// * `provider` - The hash provider to use for verification. - /// * `attestation_body` - The attestation body to verify against. + /// * `length` - The transcript length. + /// * `commitments` - The commitments to verify against. pub fn verify_with_provider<'a>( self, provider: &HashProvider, length: &TranscriptLength, - encoder_secret: Option<&EncoderSecret>, commitments: impl IntoIterator, ) -> Result { - let mut encoding_commitment = None; let mut hash_commitments = HashSet::new(); // Index commitments. for commitment in commitments { match commitment { - TranscriptCommitment::Encoding(commitment) => { - if encoding_commitment.replace(commitment).is_some() { - return Err(TranscriptProofError::new( - ErrorKind::Encoding, - "multiple encoding commitments are present.", - )); - } - } TranscriptCommitment::Hash(plaintext_hash) => { hash_commitments.insert(plaintext_hash); } @@ -92,34 +80,6 @@ impl TranscriptProof { let mut total_auth_sent = RangeSet::default(); let mut total_auth_recv = RangeSet::default(); - // Verify encoding proof. - if let Some(proof) = self.encoding_proof { - let secret = encoder_secret.ok_or_else(|| { - TranscriptProofError::new( - ErrorKind::Encoding, - "contains an encoding proof but missing encoder secret", - ) - })?; - - let commitment = encoding_commitment.ok_or_else(|| { - TranscriptProofError::new( - ErrorKind::Encoding, - "contains an encoding proof but missing encoding commitment", - ) - })?; - - let (auth_sent, auth_recv) = proof.verify_with_provider( - provider, - secret, - commitment, - self.transcript.sent_unsafe(), - self.transcript.received_unsafe(), - )?; - - total_auth_sent.union_mut(&auth_sent); - total_auth_recv.union_mut(&auth_recv); - } - let mut buffer = Vec::new(); for PlaintextHashSecret { direction, @@ -203,7 +163,6 @@ impl TranscriptProofError { #[derive(Debug)] enum ErrorKind { - Encoding, Hash, Proof, } @@ -213,7 +172,6 @@ impl fmt::Display for TranscriptProofError { f.write_str("transcript proof error: ")?; match self.kind { - ErrorKind::Encoding => f.write_str("encoding error")?, ErrorKind::Hash => f.write_str("hash error")?, ErrorKind::Proof => f.write_str("proof error")?, } @@ -226,12 +184,6 @@ impl fmt::Display for TranscriptProofError { } } -impl From for TranscriptProofError { - fn from(e: EncodingProofError) -> Self { - TranscriptProofError::new(ErrorKind::Encoding, e) - } -} - /// Union of ranges to reveal. #[derive(Clone, Debug, PartialEq)] struct QueryIdx { @@ -276,7 +228,6 @@ pub struct TranscriptProofBuilder<'a> { /// Commitment kinds in order of preference for building transcript proofs. commitment_kinds: Vec, transcript: &'a Transcript, - encoding_tree: Option<&'a EncodingTree>, hash_secrets: Vec<&'a PlaintextHashSecret>, committed_sent: RangeSet, committed_recv: RangeSet, @@ -292,15 +243,9 @@ impl<'a> TranscriptProofBuilder<'a> { let mut committed_sent = RangeSet::default(); let mut committed_recv = RangeSet::default(); - let mut encoding_tree = None; let mut hash_secrets = Vec::new(); for secret in secrets { match secret { - TranscriptSecret::Encoding(tree) => { - committed_sent.union_mut(tree.idx(Direction::Sent)); - committed_recv.union_mut(tree.idx(Direction::Received)); - encoding_tree = Some(tree); - } TranscriptSecret::Hash(hash) => { match hash.direction { Direction::Sent => committed_sent.union_mut(&hash.idx), @@ -314,7 +259,6 @@ impl<'a> TranscriptProofBuilder<'a> { Self { commitment_kinds: DEFAULT_COMMITMENT_KINDS.to_vec(), transcript, - encoding_tree, hash_secrets, committed_sent, committed_recv, @@ -412,7 +356,6 @@ impl<'a> TranscriptProofBuilder<'a> { transcript: self .transcript .to_partial(self.query_idx.sent.clone(), self.query_idx.recv.clone()), - encoding_proof: None, hash_secrets: Vec::new(), }; let mut uncovered_query_idx = self.query_idx.clone(); @@ -424,46 +367,6 @@ impl<'a> TranscriptProofBuilder<'a> { // self.commitment_kinds. if let Some(kind) = commitment_kinds_iter.next() { match kind { - TranscriptCommitmentKind::Encoding => { - let Some(encoding_tree) = self.encoding_tree else { - // Proceeds to the next preferred commitment kind if encoding tree is - // not available. - continue; - }; - - let (sent_dir_idxs, sent_uncovered) = uncovered_query_idx.sent.cover_by( - encoding_tree - .transcript_indices() - .filter(|(dir, _)| *dir == Direction::Sent), - |(_, idx)| idx, - ); - // Uncovered ranges will be checked with ranges of the next - // preferred commitment kind. - uncovered_query_idx.sent = sent_uncovered; - - let (recv_dir_idxs, recv_uncovered) = uncovered_query_idx.recv.cover_by( - encoding_tree - .transcript_indices() - .filter(|(dir, _)| *dir == Direction::Received), - |(_, idx)| idx, - ); - uncovered_query_idx.recv = recv_uncovered; - - let dir_idxs = sent_dir_idxs - .into_iter() - .chain(recv_dir_idxs) - .collect::>(); - - // Skip proof generation if there are no committed ranges that can cover the - // query ranges. - if !dir_idxs.is_empty() { - transcript_proof.encoding_proof = Some( - encoding_tree - .proof(dir_idxs.into_iter()) - .expect("subsequences were checked to be in tree"), - ); - } - } TranscriptCommitmentKind::Hash { alg } => { let (sent_hashes, sent_uncovered) = uncovered_query_idx.sent.cover_by( self.hash_secrets.iter().filter(|hash| { @@ -590,46 +493,10 @@ mod tests { use rstest::rstest; use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON}; - use crate::{ - fixtures::{encoder_secret, encoding_provider}, - hash::{Blake3, Blinder, HashAlgId}, - transcript::TranscriptCommitConfigBuilder, - }; + use crate::hash::{Blinder, HashAlgId}; use super::*; - #[rstest] - fn test_verify_missing_encoding_commitment_root() { - let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON); - let idxs = vec![(Direction::Received, RangeSet::from(0..transcript.len().1))]; - let encoding_tree = EncodingTree::new( - &Blake3::default(), - &idxs, - &encoding_provider(transcript.sent(), transcript.received()), - ) - .unwrap(); - - let secrets = vec![TranscriptSecret::Encoding(encoding_tree)]; - let mut builder = TranscriptProofBuilder::new(&transcript, &secrets); - - builder.reveal_recv(&(0..transcript.len().1)).unwrap(); - - let transcript_proof = builder.build().unwrap(); - - let provider = HashProvider::default(); - let err = transcript_proof - .verify_with_provider( - &provider, - &transcript.length(), - Some(&encoder_secret()), - &[], - ) - .err() - .unwrap(); - - assert!(matches!(err.kind, ErrorKind::Encoding)); - } - #[rstest] fn test_reveal_range_out_of_bounds() { let transcript = Transcript::new( @@ -649,7 +516,7 @@ mod tests { } #[rstest] - fn test_reveal_missing_encoding_tree() { + fn test_reveal_missing_commitment() { let transcript = Transcript::new( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], @@ -698,7 +565,6 @@ mod tests { .verify_with_provider( &provider, &transcript.length(), - None, &[TranscriptCommitment::Hash(commitment)], ) .unwrap(); @@ -748,7 +614,6 @@ mod tests { .verify_with_provider( &provider, &transcript.length(), - None, &[TranscriptCommitment::Hash(commitment)], ) .unwrap_err(); @@ -764,24 +629,19 @@ mod tests { TranscriptCommitmentKind::Hash { alg: HashAlgId::SHA256, }, - TranscriptCommitmentKind::Encoding, TranscriptCommitmentKind::Hash { alg: HashAlgId::SHA256, }, TranscriptCommitmentKind::Hash { alg: HashAlgId::SHA256, }, - TranscriptCommitmentKind::Encoding, ]); assert_eq!( builder.commitment_kinds, - vec![ - TranscriptCommitmentKind::Hash { - alg: HashAlgId::SHA256 - }, - TranscriptCommitmentKind::Encoding - ] + vec![TranscriptCommitmentKind::Hash { + alg: HashAlgId::SHA256 + },] ); } @@ -791,7 +651,7 @@ mod tests { RangeSet::from([0..10, 12..30]), true, )] - #[case::reveal_all_rangesets_with_superset_ranges( + #[case::reveal_all_rangesets_with_single_superset_range( vec![RangeSet::from([0..1]), RangeSet::from([1..2, 8..9]), RangeSet::from([2..4, 6..8]), RangeSet::from([2..3, 6..7]), RangeSet::from([9..12])], RangeSet::from([0..4, 6..9]), true, @@ -822,29 +682,30 @@ mod tests { false, )] #[allow(clippy::single_range_in_vec_init)] - fn test_reveal_mutliple_rangesets_with_one_rangeset( + fn test_reveal_multiple_rangesets_with_one_rangeset( #[case] commit_recv_rangesets: Vec>, #[case] reveal_recv_rangeset: RangeSet, #[case] success: bool, ) { + use rand::{Rng, SeedableRng}; + + let mut rng = rand::rngs::StdRng::seed_from_u64(0); let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON); - // Encoding commitment kind - let mut transcript_commitment_builder = TranscriptCommitConfigBuilder::new(&transcript); + // Create hash commitments for each rangeset + let mut secrets = Vec::new(); for rangeset in commit_recv_rangesets.iter() { - transcript_commitment_builder.commit_recv(rangeset).unwrap(); + let blinder: crate::hash::Blinder = rng.random(); + + let secret = PlaintextHashSecret { + direction: Direction::Received, + idx: rangeset.clone(), + alg: HashAlgId::BLAKE3, + blinder, + }; + secrets.push(TranscriptSecret::Hash(secret)); } - let transcripts_commitment_config = transcript_commitment_builder.build().unwrap(); - - let encoding_tree = EncodingTree::new( - &Blake3::default(), - transcripts_commitment_config.iter_encoding(), - &encoding_provider(GET_WITH_HEADER, OK_JSON), - ) - .unwrap(); - - let secrets = vec![TranscriptSecret::Encoding(encoding_tree)]; let mut builder = TranscriptProofBuilder::new(&transcript, &secrets); if success { @@ -897,27 +758,34 @@ mod tests { #[case] uncovered_sent_rangeset: RangeSet, #[case] uncovered_recv_rangeset: RangeSet, ) { + use rand::{Rng, SeedableRng}; + + let mut rng = rand::rngs::StdRng::seed_from_u64(0); let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON); - // Encoding commitment kind - let mut transcript_commitment_builder = TranscriptCommitConfigBuilder::new(&transcript); + // Create hash commitments for each rangeset + let mut secrets = Vec::new(); for rangeset in commit_sent_rangesets.iter() { - transcript_commitment_builder.commit_sent(rangeset).unwrap(); + let blinder: crate::hash::Blinder = rng.random(); + let secret = PlaintextHashSecret { + direction: Direction::Sent, + idx: rangeset.clone(), + alg: HashAlgId::BLAKE3, + blinder, + }; + secrets.push(TranscriptSecret::Hash(secret)); } for rangeset in commit_recv_rangesets.iter() { - transcript_commitment_builder.commit_recv(rangeset).unwrap(); + let blinder: crate::hash::Blinder = rng.random(); + let secret = PlaintextHashSecret { + direction: Direction::Received, + idx: rangeset.clone(), + alg: HashAlgId::BLAKE3, + blinder, + }; + secrets.push(TranscriptSecret::Hash(secret)); } - let transcripts_commitment_config = transcript_commitment_builder.build().unwrap(); - - let encoding_tree = EncodingTree::new( - &Blake3::default(), - transcripts_commitment_config.iter_encoding(), - &encoding_provider(GET_WITH_HEADER, OK_JSON), - ) - .unwrap(); - - let secrets = vec![TranscriptSecret::Encoding(encoding_tree)]; let mut builder = TranscriptProofBuilder::new(&transcript, &secrets); builder.reveal_sent(&reveal_sent_rangeset).unwrap(); builder.reveal_recv(&reveal_recv_rangeset).unwrap(); diff --git a/crates/examples/attestation/prove.rs b/crates/examples/attestation/prove.rs index 7b9805f46..f6f198640 100644 --- a/crates/examples/attestation/prove.rs +++ b/crates/examples/attestation/prove.rs @@ -332,7 +332,6 @@ async fn notary( let ( VerifierOutput { transcript_commitments, - encoder_secret, .. }, verifier, @@ -393,10 +392,6 @@ async fn notary( .server_ephemeral_key(tls_transcript.server_ephemeral_key().clone()) .transcript_commitments(transcript_commitments); - if let Some(encoder_secret) = encoder_secret { - builder.encoder_secret(encoder_secret); - } - let attestation = builder.build(&provider)?; // Send attestation to prover. diff --git a/crates/tlsn/src/map.rs b/crates/tlsn/src/map.rs index 62b541783..057ecfade 100644 --- a/crates/tlsn/src/map.rs +++ b/crates/tlsn/src/map.rs @@ -21,20 +21,6 @@ impl RangeMap where T: Item, { - pub(crate) fn new(map: Vec<(usize, T)>) -> Self { - let mut pos = 0; - for (idx, item) in &map { - assert!( - *idx >= pos, - "items must be sorted by index and non-overlapping" - ); - - pos = *idx + item.length(); - } - - Self { map } - } - /// Returns `true` if the map is empty. pub(crate) fn is_empty(&self) -> bool { self.map.is_empty() @@ -47,11 +33,6 @@ where .map(|(idx, item)| *idx..*idx + item.length()) } - /// Returns the length of the map. - pub(crate) fn len(&self) -> usize { - self.map.iter().map(|(_, item)| item.length()).sum() - } - pub(crate) fn iter(&self) -> impl Iterator, &T)> { self.map .iter() diff --git a/crates/tlsn/src/mpz.rs b/crates/tlsn/src/mpz.rs index 62ef8d181..c3f9040ad 100644 --- a/crates/tlsn/src/mpz.rs +++ b/crates/tlsn/src/mpz.rs @@ -6,11 +6,6 @@ use mpz_core::Block; #[cfg(not(tlsn_insecure))] use mpz_garble::protocol::semihonest::{Evaluator, Garbler}; use mpz_garble_core::Delta; -use mpz_memory_core::{ - Vector, - binary::U8, - correlated::{Key, Mac}, -}; #[cfg(not(tlsn_insecure))] use mpz_ot::cot::{DerandCOTReceiver, DerandCOTSender}; use mpz_ot::{ @@ -24,8 +19,6 @@ use tlsn_core::config::tls_commit::mpc::{MpcTlsConfig, NetworkSetting}; use tlsn_deap::Deap; use tokio::sync::Mutex; -use crate::transcript_internal::commit::encoding::{KeyStore, MacStore}; - #[cfg(not(tlsn_insecure))] pub(crate) type ProverMpc = Garbler, Block>>>; @@ -193,41 +186,3 @@ pub(crate) fn translate_keys(keys: &mut SessionKeys, vm: &Deap .translate(keys.server_write_mac_key) .expect("VM memory should be consistent"); } - -impl KeyStore for Verifier { - fn delta(&self) -> &Delta { - self.delta() - } - - fn get_keys(&self, data: Vector) -> Option<&[Key]> { - self.get_keys(data).ok() - } -} - -impl MacStore for Prover { - fn get_macs(&self, data: Vector) -> Option<&[Mac]> { - self.get_macs(data).ok() - } -} - -#[cfg(tlsn_insecure)] -mod insecure { - use super::*; - use mpz_ideal_vm::IdealVm; - - impl KeyStore for IdealVm { - fn delta(&self) -> &Delta { - unimplemented!("encodings not supported in insecure mode") - } - - fn get_keys(&self, _data: Vector) -> Option<&[Key]> { - unimplemented!("encodings not supported in insecure mode") - } - } - - impl MacStore for IdealVm { - fn get_macs(&self, _data: Vector) -> Option<&[Mac]> { - unimplemented!("encodings not supported in insecure mode") - } - } -} diff --git a/crates/tlsn/src/prover/error.rs b/crates/tlsn/src/prover/error.rs index 46321a793..3616e6a95 100644 --- a/crates/tlsn/src/prover/error.rs +++ b/crates/tlsn/src/prover/error.rs @@ -2,8 +2,6 @@ use std::{error::Error, fmt}; use mpc_tls::MpcTlsError; -use crate::transcript_internal::commit::encoding::EncodingError; - /// Error for [`Prover`](crate::prover::Prover). #[derive(Debug, thiserror::Error)] pub struct ProverError { @@ -109,9 +107,3 @@ impl From for ProverError { Self::new(ErrorKind::Mpc, e) } } - -impl From for ProverError { - fn from(e: EncodingError) -> Self { - Self::new(ErrorKind::Commit, e) - } -} diff --git a/crates/tlsn/src/prover/prove.rs b/crates/tlsn/src/prover/prove.rs index 6850b5744..91efd02c2 100644 --- a/crates/tlsn/src/prover/prove.rs +++ b/crates/tlsn/src/prover/prove.rs @@ -13,17 +13,10 @@ use tlsn_core::{ use crate::{ prover::ProverError, - transcript_internal::{ - TranscriptRefs, - auth::prove_plaintext, - commit::{ - encoding::{self, MacStore}, - hash::prove_hash, - }, - }, + transcript_internal::{TranscriptRefs, auth::prove_plaintext, commit::hash::prove_hash}, }; -pub(crate) async fn prove + MacStore + Send + Sync>( +pub(crate) async fn prove + Send + Sync>( ctx: &mut Context, vm: &mut T, keys: &SessionKeys, @@ -45,13 +38,6 @@ pub(crate) async fn prove + MacStore + Send + Sync>( Direction::Sent => commit_sent.union_mut(idx), Direction::Received => commit_recv.union_mut(idx), }); - - commit_config - .iter_encoding() - .for_each(|(direction, idx)| match direction { - Direction::Sent => commit_sent.union_mut(idx), - Direction::Received => commit_recv.union_mut(idx), - }); } let transcript_refs = TranscriptRefs { @@ -102,45 +88,6 @@ pub(crate) async fn prove + MacStore + Send + Sync>( vm.execute_all(ctx).await.map_err(ProverError::zk)?; - if let Some(commit_config) = config.transcript_commit() - && commit_config.has_encoding() - { - let mut sent_ranges = RangeSet::default(); - let mut recv_ranges = RangeSet::default(); - for (dir, idx) in commit_config.iter_encoding() { - match dir { - Direction::Sent => sent_ranges.union_mut(idx), - Direction::Received => recv_ranges.union_mut(idx), - } - } - - let sent_map = transcript_refs - .sent - .index(&sent_ranges) - .expect("indices are valid"); - let recv_map = transcript_refs - .recv - .index(&recv_ranges) - .expect("indices are valid"); - - let (commitment, tree) = encoding::receive( - ctx, - vm, - *commit_config.encoding_hash_alg(), - &sent_map, - &recv_map, - commit_config.iter_encoding(), - ) - .await?; - - output - .transcript_commitments - .push(TranscriptCommitment::Encoding(commitment)); - output - .transcript_secrets - .push(TranscriptSecret::Encoding(tree)); - } - if let Some((hash_fut, hash_secrets)) = hash_commitments { let hash_commitments = hash_fut.try_recv().map_err(ProverError::commit)?; for (commitment, secret) in hash_commitments.into_iter().zip(hash_secrets) { diff --git a/crates/tlsn/src/transcript_internal/commit.rs b/crates/tlsn/src/transcript_internal/commit.rs index 45953e961..4a65690e4 100644 --- a/crates/tlsn/src/transcript_internal/commit.rs +++ b/crates/tlsn/src/transcript_internal/commit.rs @@ -1,4 +1,3 @@ //! Plaintext commitment and proof of encryption. -pub(crate) mod encoding; pub(crate) mod hash; diff --git a/crates/tlsn/src/transcript_internal/commit/encoding.rs b/crates/tlsn/src/transcript_internal/commit/encoding.rs deleted file mode 100644 index 39931921b..000000000 --- a/crates/tlsn/src/transcript_internal/commit/encoding.rs +++ /dev/null @@ -1,267 +0,0 @@ -//! Encoding commitment protocol. - -use std::ops::Range; - -use mpz_common::Context; -use mpz_memory_core::{ - Vector, - binary::U8, - correlated::{Delta, Key, Mac}, -}; -use rand::Rng; -use rangeset::set::RangeSet; -use serde::{Deserialize, Serialize}; -use serio::{SinkExt, stream::IoStreamExt}; -use tlsn_core::{ - hash::{Blake3, HashAlgId, HashAlgorithm, Keccak256, Sha256}, - transcript::{ - Direction, - encoding::{ - Encoder, EncoderSecret, EncodingCommitment, EncodingProvider, EncodingProviderError, - EncodingTree, EncodingTreeError, new_encoder, - }, - }, -}; - -use crate::{ - map::{Item, RangeMap}, - transcript_internal::ReferenceMap, -}; - -/// Bytes of encoding, per byte. -const ENCODING_SIZE: usize = 128; - -#[derive(Debug, Serialize, Deserialize)] -struct Encodings { - sent: Vec, - recv: Vec, -} - -/// Transfers encodings for the provided plaintext ranges. -pub(crate) async fn transfer( - ctx: &mut Context, - store: &K, - sent: &ReferenceMap, - recv: &ReferenceMap, -) -> Result<(EncoderSecret, EncodingCommitment), EncodingError> { - let secret = EncoderSecret::new(rand::rng().random(), store.delta().as_block().to_bytes()); - let encoder = new_encoder(&secret); - - // Collects the encodings for the provided plaintext ranges. - fn collect_encodings( - encoder: &impl Encoder, - store: &impl KeyStore, - direction: Direction, - map: &ReferenceMap, - ) -> Vec { - let mut encodings = Vec::with_capacity(map.len() * ENCODING_SIZE); - for (range, chunk) in map.iter() { - let start = encodings.len(); - encoder.encode_range(direction, range, &mut encodings); - let keys = store - .get_keys(*chunk) - .expect("keys are present for provided plaintext ranges"); - encodings[start..] - .iter_mut() - .zip(keys.iter().flat_map(|key| key.as_block().as_bytes())) - .for_each(|(encoding, key)| { - *encoding ^= *key; - }); - } - encodings - } - - let encodings = Encodings { - sent: collect_encodings(&encoder, store, Direction::Sent, sent), - recv: collect_encodings(&encoder, store, Direction::Received, recv), - }; - - let frame_limit = ctx - .io() - .limit() - .saturating_add(encodings.sent.len() + encodings.recv.len()); - ctx.io_mut().with_limit(frame_limit).send(encodings).await?; - - let root = ctx.io_mut().expect_next().await?; - - Ok((secret, EncodingCommitment { root })) -} - -/// Receives and commits to the encodings for the provided plaintext ranges. -pub(crate) async fn receive( - ctx: &mut Context, - store: &M, - hash_alg: HashAlgId, - sent: &ReferenceMap, - recv: &ReferenceMap, - idxs: impl IntoIterator)>, -) -> Result<(EncodingCommitment, EncodingTree), EncodingError> { - let hasher: &(dyn HashAlgorithm + Send + Sync) = match hash_alg { - HashAlgId::SHA256 => &Sha256::default(), - HashAlgId::KECCAK256 => &Keccak256::default(), - HashAlgId::BLAKE3 => &Blake3::default(), - alg => { - return Err(ErrorRepr::UnsupportedHashAlgorithm(alg).into()); - } - }; - - let (sent_len, recv_len) = (sent.len(), recv.len()); - let frame_limit = ctx - .io() - .limit() - .saturating_add(ENCODING_SIZE * (sent_len + recv_len)); - let encodings: Encodings = ctx.io_mut().with_limit(frame_limit).expect_next().await?; - - if encodings.sent.len() != sent_len * ENCODING_SIZE { - return Err(ErrorRepr::IncorrectMacCount { - direction: Direction::Sent, - expected: sent_len, - got: encodings.sent.len() / ENCODING_SIZE, - } - .into()); - } - - if encodings.recv.len() != recv_len * ENCODING_SIZE { - return Err(ErrorRepr::IncorrectMacCount { - direction: Direction::Received, - expected: recv_len, - got: encodings.recv.len() / ENCODING_SIZE, - } - .into()); - } - - // Collects a map of plaintext ranges to their encodings. - fn collect_map( - store: &impl MacStore, - mut encodings: Vec, - map: &ReferenceMap, - ) -> RangeMap { - let mut encoding_map = Vec::new(); - let mut pos = 0; - for (range, chunk) in map.iter() { - let macs = store - .get_macs(*chunk) - .expect("MACs are present for provided plaintext ranges"); - let encoding = &mut encodings[pos..pos + range.len() * ENCODING_SIZE]; - encoding - .iter_mut() - .zip(macs.iter().flat_map(|mac| mac.as_bytes())) - .for_each(|(encoding, mac)| { - *encoding ^= *mac; - }); - - encoding_map.push((range.start, EncodingSlice::from(&(*encoding)))); - pos += range.len() * ENCODING_SIZE; - } - RangeMap::new(encoding_map) - } - - let provider = Provider { - sent: collect_map(store, encodings.sent, sent), - recv: collect_map(store, encodings.recv, recv), - }; - - let tree = EncodingTree::new(hasher, idxs, &provider)?; - let root = tree.root(); - - ctx.io_mut().send(root.clone()).await?; - - let commitment = EncodingCommitment { root }; - - Ok((commitment, tree)) -} - -pub(crate) trait KeyStore { - fn delta(&self) -> Δ - - fn get_keys(&self, data: Vector) -> Option<&[Key]>; -} - -pub(crate) trait MacStore { - fn get_macs(&self, data: Vector) -> Option<&[Mac]>; -} - -#[derive(Debug)] -struct Provider { - sent: RangeMap, - recv: RangeMap, -} - -impl EncodingProvider for Provider { - fn provide_encoding( - &self, - direction: Direction, - range: Range, - dest: &mut Vec, - ) -> Result<(), EncodingProviderError> { - let encodings = match direction { - Direction::Sent => &self.sent, - Direction::Received => &self.recv, - }; - - let encoding = encodings.get(range).ok_or(EncodingProviderError)?; - - dest.extend_from_slice(encoding); - - Ok(()) - } -} - -#[derive(Debug)] -struct EncodingSlice(Vec); - -impl From<&[u8]> for EncodingSlice { - fn from(value: &[u8]) -> Self { - Self(value.to_vec()) - } -} - -impl Item for EncodingSlice { - type Slice<'a> - = &'a [u8] - where - Self: 'a; - - fn length(&self) -> usize { - self.0.len() / ENCODING_SIZE - } - - fn slice<'a>(&'a self, range: Range) -> Option> { - self.0 - .get(range.start * ENCODING_SIZE..range.end * ENCODING_SIZE) - } -} - -/// Encoding protocol error. -#[derive(Debug, thiserror::Error)] -#[error(transparent)] -pub struct EncodingError(#[from] ErrorRepr); - -#[derive(Debug, thiserror::Error)] -#[error("encoding protocol error: {0}")] -enum ErrorRepr { - #[error("I/O error: {0}")] - Io(std::io::Error), - #[error("incorrect MAC count for {direction}: expected {expected}, got {got}")] - IncorrectMacCount { - direction: Direction, - expected: usize, - got: usize, - }, - #[error("encoding tree error: {0}")] - EncodingTree(EncodingTreeError), - #[error("unsupported hash algorithm: {0}")] - UnsupportedHashAlgorithm(HashAlgId), -} - -impl From for EncodingError { - fn from(value: std::io::Error) -> Self { - Self(ErrorRepr::Io(value)) - } -} - -impl From for EncodingError { - fn from(value: EncodingTreeError) -> Self { - Self(ErrorRepr::EncodingTree(value)) - } -} diff --git a/crates/tlsn/src/verifier/error.rs b/crates/tlsn/src/verifier/error.rs index e58121178..c6423ac26 100644 --- a/crates/tlsn/src/verifier/error.rs +++ b/crates/tlsn/src/verifier/error.rs @@ -2,8 +2,6 @@ use std::{error::Error, fmt}; use mpc_tls::MpcTlsError; -use crate::transcript_internal::commit::encoding::EncodingError; - /// Error for [`Verifier`](crate::verifier::Verifier). #[derive(Debug, thiserror::Error)] pub struct VerifierError { @@ -57,7 +55,6 @@ enum ErrorKind { Config, Mpc, Zk, - Commit, Verify, } @@ -70,7 +67,6 @@ impl fmt::Display for VerifierError { ErrorKind::Config => f.write_str("config error")?, ErrorKind::Mpc => f.write_str("mpc error")?, ErrorKind::Zk => f.write_str("zk error")?, - ErrorKind::Commit => f.write_str("commit error")?, ErrorKind::Verify => f.write_str("verification error")?, } @@ -105,9 +101,3 @@ impl From for VerifierError { Self::new(ErrorKind::Mpc, e) } } - -impl From for VerifierError { - fn from(e: EncodingError) -> Self { - Self::new(ErrorKind::Commit, e) - } -} diff --git a/crates/tlsn/src/verifier/verify.rs b/crates/tlsn/src/verifier/verify.rs index 3f83ecba0..c50c6e931 100644 --- a/crates/tlsn/src/verifier/verify.rs +++ b/crates/tlsn/src/verifier/verify.rs @@ -14,19 +14,12 @@ use tlsn_core::{ }; use crate::{ - transcript_internal::{ - TranscriptRefs, - auth::verify_plaintext, - commit::{ - encoding::{self, KeyStore}, - hash::verify_hash, - }, - }, + transcript_internal::{TranscriptRefs, auth::verify_plaintext, commit::hash::verify_hash}, verifier::VerifierError, }; #[allow(clippy::too_many_arguments)] -pub(crate) async fn verify + KeyStore + Send + Sync>( +pub(crate) async fn verify + Send + Sync>( ctx: &mut Context, vm: &mut T, keys: &SessionKeys, @@ -94,11 +87,6 @@ pub(crate) async fn verify + KeyStore + Send + Sync>( Direction::Sent => commit_sent.union_mut(idx), Direction::Received => commit_recv.union_mut(idx), }); - - if let Some((sent, recv)) = commit_config.encoding() { - commit_sent.union_mut(sent); - commit_recv.union_mut(recv); - } } let (sent_refs, sent_proof) = verify_plaintext( @@ -151,24 +139,6 @@ pub(crate) async fn verify + KeyStore + Send + Sync>( sent_proof.verify().map_err(VerifierError::verify)?; recv_proof.verify().map_err(VerifierError::verify)?; - let mut encoder_secret = None; - if let Some(commit_config) = request.transcript_commit() - && let Some((sent, recv)) = commit_config.encoding() - { - let sent_map = transcript_refs - .sent - .index(sent) - .expect("ranges were authenticated"); - let recv_map = transcript_refs - .recv - .index(recv) - .expect("ranges were authenticated"); - - let (secret, commitment) = encoding::transfer(ctx, vm, &sent_map, &recv_map).await?; - encoder_secret = Some(secret); - transcript_commitments.push(TranscriptCommitment::Encoding(commitment)); - } - if let Some(hash_commitments) = hash_commitments { for commitment in hash_commitments.try_recv().map_err(VerifierError::verify)? { transcript_commitments.push(TranscriptCommitment::Hash(commitment)); @@ -178,7 +148,6 @@ pub(crate) async fn verify + KeyStore + Send + Sync>( Ok(VerifierOutput { server_name, transcript: request.reveal().is_some().then_some(transcript), - encoder_secret, transcript_commitments, }) } diff --git a/crates/tlsn/tests/test.rs b/crates/tlsn/tests/test.rs index 22ddbdde3..f87021ff9 100644 --- a/crates/tlsn/tests/test.rs +++ b/crates/tlsn/tests/test.rs @@ -1,5 +1,4 @@ use futures::{AsyncReadExt, AsyncWriteExt}; -use rangeset::set::RangeSet; use tlsn::{ config::{ prove::ProveConfig, @@ -9,12 +8,9 @@ use tlsn::{ verifier::VerifierConfig, }, connection::ServerName, - hash::{HashAlgId, HashProvider}, + hash::HashAlgId, prover::Prover, - transcript::{ - Direction, Transcript, TranscriptCommitConfig, TranscriptCommitment, - TranscriptCommitmentKind, TranscriptSecret, - }, + transcript::{Direction, Transcript, TranscriptCommitConfig, TranscriptCommitmentKind}, verifier::{Verifier, VerifierOutput}, webpki::{CertificateDer, RootCertStore}, }; @@ -42,7 +38,7 @@ async fn test() { let (socket_0, socket_1) = tokio::io::duplex(2 << 23); - let ((full_transcript, prover_output), verifier_output) = + let ((_full_transcript, _prover_output), verifier_output) = tokio::join!(prover(socket_0), verifier(socket_1)); let partial_transcript = verifier_output.transcript.unwrap(); @@ -58,50 +54,6 @@ async fn test() { partial_transcript.received_authed().iter().next().unwrap(), 0..10 ); - - let encoding_tree = prover_output - .transcript_secrets - .iter() - .find_map(|secret| { - if let TranscriptSecret::Encoding(tree) = secret { - Some(tree) - } else { - None - } - }) - .unwrap(); - - let encoding_commitment = prover_output - .transcript_commitments - .iter() - .find_map(|commitment| { - if let TranscriptCommitment::Encoding(commitment) = commitment { - Some(commitment) - } else { - None - } - }) - .unwrap(); - - let prove_sent = RangeSet::from(1..full_transcript.sent().len() - 1); - let prove_recv = RangeSet::from(1..full_transcript.received().len() - 1); - let idxs = [ - (Direction::Sent, prove_sent.clone()), - (Direction::Received, prove_recv.clone()), - ]; - let proof = encoding_tree.proof(idxs.iter()).unwrap(); - let (auth_sent, auth_recv) = proof - .verify_with_provider( - &HashProvider::default(), - &verifier_output.encoder_secret.unwrap(), - encoding_commitment, - full_transcript.sent(), - full_transcript.received(), - ) - .unwrap(); - - assert_eq!(auth_sent, prove_sent); - assert_eq!(auth_recv, prove_recv); } #[instrument(skip(verifier_socket))] @@ -163,25 +115,21 @@ async fn prover( let mut builder = TranscriptCommitConfig::builder(prover.transcript()); - for kind in [ - TranscriptCommitmentKind::Encoding, - TranscriptCommitmentKind::Hash { - alg: HashAlgId::SHA256, - }, - ] { - builder - .commit_with_kind(&(0..sent_tx_len), Direction::Sent, kind) - .unwrap(); - builder - .commit_with_kind(&(0..recv_tx_len), Direction::Received, kind) - .unwrap(); - builder - .commit_with_kind(&(1..sent_tx_len - 1), Direction::Sent, kind) - .unwrap(); - builder - .commit_with_kind(&(1..recv_tx_len - 1), Direction::Received, kind) - .unwrap(); - } + let kind = TranscriptCommitmentKind::Hash { + alg: HashAlgId::SHA256, + }; + builder + .commit_with_kind(&(0..sent_tx_len), Direction::Sent, kind) + .unwrap(); + builder + .commit_with_kind(&(0..recv_tx_len), Direction::Received, kind) + .unwrap(); + builder + .commit_with_kind(&(1..sent_tx_len - 1), Direction::Sent, kind) + .unwrap(); + builder + .commit_with_kind(&(1..recv_tx_len - 1), Direction::Received, kind) + .unwrap(); let transcript_commit = builder.build().unwrap();