chore: add core transcript unit tests (#649)

* Add transcript proof and lib tests.

* Init encoding tree test.

* Add encoding proof tests.

* Generalise fixture tests.

* Add seed arg to attestation fixture fn.

* Adjust cosmetics.

* Format comment.

---------

Co-authored-by: yuroitaki <>
This commit is contained in:
yuroitaki
2024-11-04 13:59:31 +08:00
committed by GitHub
parent faab999339
commit c10c9155a7
7 changed files with 727 additions and 187 deletions

View File

@@ -245,64 +245,18 @@ mod test {
use crate::{ use crate::{
connection::{HandshakeData, HandshakeDataV1_2}, connection::{HandshakeData, HandshakeDataV1_2},
fixtures::{encoder_seed, encoding_provider, ConnectionFixture}, fixtures::{
encoder_seed, encoding_provider, request_fixture, ConnectionFixture, RequestFixture,
},
hash::Blake3, hash::Blake3,
request::RequestConfig, transcript::Transcript,
transcript::{encoding::EncodingTree, Transcript, TranscriptCommitConfigBuilder},
}; };
use super::*; use super::*;
fn request_and_connection() -> (Request, ConnectionFixture) {
let provider = CryptoProvider::default();
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 = encoding_provider(GET_WITH_HEADER, OK_JSON);
// At the end of the TLS connection the Prover holds the:
let ConnectionFixture {
server_name,
server_cert_data,
..
} = ConnectionFixture::tlsnotary(transcript.length());
// 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();
let transcripts_commitment_config = transcript_commitment_builder.build().unwrap();
// Prover constructs encoding tree.
let encoding_tree = EncodingTree::new(
&Blake3::default(),
transcripts_commitment_config.iter_encoding(),
&encodings_provider,
&transcript.length(),
)
.unwrap();
let request_config = RequestConfig::default();
let mut request_builder = Request::builder(&request_config);
request_builder
.server_name(server_name.clone())
.server_cert_data(server_cert_data)
.transcript(transcript.clone())
.encoding_tree(encoding_tree);
let (request, _) = request_builder.build(&provider).unwrap();
(request, ConnectionFixture::tlsnotary(transcript.length()))
}
#[fixture] #[fixture]
#[once] #[once]
fn default_attestation_config() -> AttestationConfig { fn attestation_config() -> AttestationConfig {
AttestationConfig::builder() AttestationConfig::builder()
.supported_signature_algs([SignatureAlgId::SECP256K1]) .supported_signature_algs([SignatureAlgId::SECP256K1])
.build() .build()
@@ -319,7 +273,16 @@ mod test {
#[rstest] #[rstest]
fn test_attestation_builder_accept_unsupported_signer() { fn test_attestation_builder_accept_unsupported_signer() {
let (request, _) = request_and_connection(); 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(),
);
let attestation_config = AttestationConfig::builder() let attestation_config = AttestationConfig::builder()
.supported_signature_algs([SignatureAlgId::SECP256R1]) .supported_signature_algs([SignatureAlgId::SECP256R1])
.build() .build()
@@ -334,7 +297,15 @@ mod test {
#[rstest] #[rstest]
fn test_attestation_builder_accept_unsupported_hasher() { fn test_attestation_builder_accept_unsupported_hasher() {
let (request, _) = request_and_connection(); 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(),
);
let attestation_config = AttestationConfig::builder() let attestation_config = AttestationConfig::builder()
.supported_signature_algs([SignatureAlgId::SECP256K1]) .supported_signature_algs([SignatureAlgId::SECP256K1])
@@ -351,7 +322,15 @@ mod test {
#[rstest] #[rstest]
fn test_attestation_builder_accept_unsupported_encoding_commitment() { fn test_attestation_builder_accept_unsupported_encoding_commitment() {
let (request, _) = request_and_connection(); 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(),
);
let attestation_config = AttestationConfig::builder() let attestation_config = AttestationConfig::builder()
.supported_signature_algs([SignatureAlgId::SECP256K1]) .supported_signature_algs([SignatureAlgId::SECP256K1])
@@ -371,13 +350,19 @@ mod test {
} }
#[rstest] #[rstest]
fn test_attestation_builder_sign_missing_signer( fn test_attestation_builder_sign_missing_signer(attestation_config: &AttestationConfig) {
default_attestation_config: &AttestationConfig, let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
) { let connection = ConnectionFixture::tlsnotary(transcript.length());
let (request, _) = request_and_connection();
let attestation_builder = Attestation::builder(default_attestation_config) let RequestFixture { request, .. } = request_fixture(
.accept_request(request.clone()) transcript,
encoding_provider(GET_WITH_HEADER, OK_JSON),
connection,
Blake3::default(),
);
let attestation_builder = Attestation::builder(attestation_config)
.accept_request(request)
.unwrap(); .unwrap();
let mut provider = CryptoProvider::default(); let mut provider = CryptoProvider::default();
@@ -389,13 +374,21 @@ mod test {
#[rstest] #[rstest]
fn test_attestation_builder_sign_missing_encoding_seed( fn test_attestation_builder_sign_missing_encoding_seed(
default_attestation_config: &AttestationConfig, attestation_config: &AttestationConfig,
crypto_provider: &CryptoProvider, crypto_provider: &CryptoProvider,
) { ) {
let (request, connection) = request_and_connection(); let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
let connection = ConnectionFixture::tlsnotary(transcript.length());
let mut attestation_builder = Attestation::builder(default_attestation_config) let RequestFixture { request, .. } = request_fixture(
.accept_request(request.clone()) transcript,
encoding_provider(GET_WITH_HEADER, OK_JSON),
connection.clone(),
Blake3::default(),
);
let mut attestation_builder = Attestation::builder(attestation_config)
.accept_request(request)
.unwrap(); .unwrap();
let ConnectionFixture { let ConnectionFixture {
@@ -407,10 +400,10 @@ mod test {
let HandshakeData::V1_2(HandshakeDataV1_2 { let HandshakeData::V1_2(HandshakeDataV1_2 {
server_ephemeral_key, server_ephemeral_key,
.. ..
}) = server_cert_data.handshake.clone(); }) = server_cert_data.handshake;
attestation_builder attestation_builder
.connection_info(connection_info.clone()) .connection_info(connection_info)
.server_ephemeral_key(server_ephemeral_key); .server_ephemeral_key(server_ephemeral_key);
let err = attestation_builder.build(crypto_provider).err().unwrap(); let err = attestation_builder.build(crypto_provider).err().unwrap();
@@ -419,13 +412,21 @@ mod test {
#[rstest] #[rstest]
fn test_attestation_builder_sign_missing_server_ephemeral_key( fn test_attestation_builder_sign_missing_server_ephemeral_key(
default_attestation_config: &AttestationConfig, attestation_config: &AttestationConfig,
crypto_provider: &CryptoProvider, crypto_provider: &CryptoProvider,
) { ) {
let (request, connection) = request_and_connection(); let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
let connection = ConnectionFixture::tlsnotary(transcript.length());
let mut attestation_builder = Attestation::builder(default_attestation_config) let RequestFixture { request, .. } = request_fixture(
.accept_request(request.clone()) transcript,
encoding_provider(GET_WITH_HEADER, OK_JSON),
connection.clone(),
Blake3::default(),
);
let mut attestation_builder = Attestation::builder(attestation_config)
.accept_request(request)
.unwrap(); .unwrap();
let ConnectionFixture { let ConnectionFixture {
@@ -433,7 +434,7 @@ mod test {
} = connection; } = connection;
attestation_builder attestation_builder
.connection_info(connection_info.clone()) .connection_info(connection_info)
.encoding_seed(encoder_seed().to_vec()); .encoding_seed(encoder_seed().to_vec());
let err = attestation_builder.build(crypto_provider).err().unwrap(); let err = attestation_builder.build(crypto_provider).err().unwrap();
@@ -442,13 +443,21 @@ mod test {
#[rstest] #[rstest]
fn test_attestation_builder_sign_missing_connection_info( fn test_attestation_builder_sign_missing_connection_info(
default_attestation_config: &AttestationConfig, attestation_config: &AttestationConfig,
crypto_provider: &CryptoProvider, crypto_provider: &CryptoProvider,
) { ) {
let (request, connection) = request_and_connection(); let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
let connection = ConnectionFixture::tlsnotary(transcript.length());
let mut attestation_builder = Attestation::builder(default_attestation_config) let RequestFixture { request, .. } = request_fixture(
.accept_request(request.clone()) transcript,
encoding_provider(GET_WITH_HEADER, OK_JSON),
connection.clone(),
Blake3::default(),
);
let mut attestation_builder = Attestation::builder(attestation_config)
.accept_request(request)
.unwrap(); .unwrap();
let ConnectionFixture { let ConnectionFixture {
@@ -458,7 +467,7 @@ mod test {
let HandshakeData::V1_2(HandshakeDataV1_2 { let HandshakeData::V1_2(HandshakeDataV1_2 {
server_ephemeral_key, server_ephemeral_key,
.. ..
}) = server_cert_data.handshake.clone(); }) = server_cert_data.handshake;
attestation_builder attestation_builder
.server_ephemeral_key(server_ephemeral_key) .server_ephemeral_key(server_ephemeral_key)

View File

@@ -8,14 +8,23 @@ use hex::FromHex;
use p256::ecdsa::SigningKey; use p256::ecdsa::SigningKey;
use crate::{ use crate::{
attestation::{Attestation, AttestationConfig},
connection::{ connection::{
Certificate, ConnectionInfo, HandshakeData, HandshakeDataV1_2, KeyType, ServerCertData, Certificate, ConnectionInfo, HandshakeData, HandshakeDataV1_2, KeyType, ServerCertData,
ServerEphemKey, ServerName, ServerSignature, SignatureScheme, TlsVersion, TranscriptLength, ServerEphemKey, ServerName, ServerSignature, SignatureScheme, TlsVersion, TranscriptLength,
}, },
transcript::{encoding::EncodingProvider, Transcript}, hash::HashAlgorithm,
request::{Request, RequestConfig},
signing::SignatureAlgId,
transcript::{
encoding::{EncodingProvider, EncodingTree},
Transcript, TranscriptCommitConfigBuilder,
},
CryptoProvider,
}; };
/// A fixture containing various TLS connection data. /// A fixture containing various TLS connection data.
#[derive(Clone)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct ConnectionFixture { pub struct ConnectionFixture {
pub server_name: ServerName, pub server_name: ServerName,
@@ -134,3 +143,101 @@ pub fn encoder_seed() -> [u8; 32] {
pub fn notary_signing_key() -> SigningKey { pub fn notary_signing_key() -> SigningKey {
SigningKey::from_slice(&[1; 32]).unwrap() SigningKey::from_slice(&[1; 32]).unwrap()
} }
/// 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,
) -> RequestFixture {
let provider = CryptoProvider::default();
let (sent_len, recv_len) = transcript.len();
let ConnectionFixture {
server_name,
server_cert_data,
..
} = connection;
let mut transcript_commitment_builder = TranscriptCommitConfigBuilder::new(&transcript);
transcript_commitment_builder
.commit_sent(&(0..sent_len))
.unwrap()
.commit_recv(&(0..recv_len))
.unwrap();
let transcripts_commitment_config = transcript_commitment_builder.build().unwrap();
// Prover constructs encoding tree.
let encoding_tree = EncodingTree::new(
&encoding_hasher,
transcripts_commitment_config.iter_encoding(),
&encodings_provider,
&transcript.length(),
)
.unwrap();
let request_config = RequestConfig::default();
let mut request_builder = Request::builder(&request_config);
request_builder
.server_name(server_name)
.server_cert_data(server_cert_data)
.transcript(transcript)
.encoding_tree(encoding_tree.clone());
let (request, _) = request_builder.build(&provider).unwrap();
RequestFixture {
encoding_tree,
request,
}
}
/// Returns an attestation fixture for testing.
pub fn attestation_fixture(
request: Request,
connection: ConnectionFixture,
signature_alg: SignatureAlgId,
encoding_seed: Vec<u8>,
) -> Attestation {
let ConnectionFixture {
connection_info,
server_cert_data,
..
} = connection;
let HandshakeData::V1_2(HandshakeDataV1_2 {
server_ephemeral_key,
..
}) = server_cert_data.handshake;
let mut provider = CryptoProvider::default();
match signature_alg {
SignatureAlgId::SECP256K1 => provider.signer.set_secp256k1(&[42u8; 32]).unwrap(),
SignatureAlgId::SECP256R1 => provider.signer.set_secp256r1(&[42u8; 32]).unwrap(),
_ => unimplemented!(),
};
let attestation_config = AttestationConfig::builder()
.supported_signature_algs([signature_alg])
.build()
.unwrap();
let mut attestation_builder = Attestation::builder(&attestation_config)
.accept_request(request)
.unwrap();
attestation_builder
.connection_info(connection_info)
.server_ephemeral_key(server_ephemeral_key)
.encoding_seed(encoding_seed);
attestation_builder.build(&provider).unwrap()
}

View File

@@ -90,115 +90,62 @@ pub struct InconsistentAttestation(String);
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};
use super::*; use super::*;
use crate::{ use crate::{
attestation::{Attestation, AttestationConfig}, connection::{ServerCertOpening, TranscriptLength},
connection::{HandshakeData, HandshakeDataV1_2, ServerCertOpening, TranscriptLength}, fixtures::{
fixtures::{encoder_seed, encoding_provider, ConnectionFixture}, attestation_fixture, encoder_seed, encoding_provider, request_fixture,
ConnectionFixture, RequestFixture,
},
hash::{Blake3, Hash, HashAlgId}, hash::{Blake3, Hash, HashAlgId},
signing::SignatureAlgId, signing::SignatureAlgId,
transcript::{encoding::EncodingTree, Transcript, TranscriptCommitConfigBuilder}, transcript::Transcript,
CryptoProvider, CryptoProvider,
}; };
use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};
fn attestation(payload: (Request, ConnectionFixture)) -> Attestation {
let (request, connection) = payload;
let ConnectionFixture {
connection_info,
server_cert_data,
..
} = connection;
let HandshakeData::V1_2(HandshakeDataV1_2 {
server_ephemeral_key,
..
}) = server_cert_data.handshake.clone();
let mut provider = CryptoProvider::default();
provider.signer.set_secp256k1(&[42u8; 32]).unwrap();
let attestation_config = AttestationConfig::builder()
.supported_signature_algs([SignatureAlgId::SECP256K1])
.build()
.unwrap();
let mut attestation_builder = Attestation::builder(&attestation_config)
.accept_request(request.clone())
.unwrap();
attestation_builder
.connection_info(connection_info.clone())
.server_ephemeral_key(server_ephemeral_key)
.encoding_seed(encoder_seed().to_vec());
attestation_builder.build(&provider).unwrap()
}
fn request_and_connection() -> (Request, ConnectionFixture) {
let provider = CryptoProvider::default();
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 = encoding_provider(GET_WITH_HEADER, OK_JSON);
// At the end of the TLS connection the Prover holds the:
let ConnectionFixture {
server_name,
server_cert_data,
..
} = ConnectionFixture::tlsnotary(transcript.length());
// 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();
let transcripts_commitment_config = transcript_commitment_builder.build().unwrap();
// Prover constructs encoding tree.
let encoding_tree = EncodingTree::new(
&Blake3::default(),
transcripts_commitment_config.iter_encoding(),
&encodings_provider,
&transcript.length(),
)
.unwrap();
let request_config = RequestConfig::default();
let mut request_builder = Request::builder(&request_config);
request_builder
.server_name(server_name.clone())
.server_cert_data(server_cert_data)
.transcript(transcript.clone())
.encoding_tree(encoding_tree);
let (request, _) = request_builder.build(&provider).unwrap();
(request, ConnectionFixture::tlsnotary(transcript.length()))
}
#[test] #[test]
fn test_success() { fn test_success() {
let (request, connection) = request_and_connection(); let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
let connection = ConnectionFixture::tlsnotary(transcript.length());
let attestation = attestation((request.clone(), connection)); let RequestFixture { request, .. } = request_fixture(
transcript,
encoding_provider(GET_WITH_HEADER, OK_JSON),
connection.clone(),
Blake3::default(),
);
let attestation = attestation_fixture(
request.clone(),
connection,
SignatureAlgId::SECP256K1,
encoder_seed().to_vec(),
);
assert!(request.validate(&attestation).is_ok()) assert!(request.validate(&attestation).is_ok())
} }
#[test] #[test]
fn test_wrong_signature_alg() { fn test_wrong_signature_alg() {
let (mut request, connection) = request_and_connection(); let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
let connection = ConnectionFixture::tlsnotary(transcript.length());
let attestation = attestation((request.clone(), connection)); let RequestFixture { mut request, .. } = request_fixture(
transcript,
encoding_provider(GET_WITH_HEADER, OK_JSON),
connection.clone(),
Blake3::default(),
);
let attestation = attestation_fixture(
request.clone(),
connection,
SignatureAlgId::SECP256K1,
encoder_seed().to_vec(),
);
request.signature_alg = SignatureAlgId::SECP256R1; request.signature_alg = SignatureAlgId::SECP256R1;
@@ -208,9 +155,22 @@ mod test {
#[test] #[test]
fn test_wrong_hash_alg() { fn test_wrong_hash_alg() {
let (mut request, connection) = request_and_connection(); let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
let connection = ConnectionFixture::tlsnotary(transcript.length());
let attestation = attestation((request.clone(), connection)); let RequestFixture { mut request, .. } = request_fixture(
transcript,
encoding_provider(GET_WITH_HEADER, OK_JSON),
connection.clone(),
Blake3::default(),
);
let attestation = attestation_fixture(
request.clone(),
connection,
SignatureAlgId::SECP256K1,
encoder_seed().to_vec(),
);
request.hash_alg = HashAlgId::SHA256; request.hash_alg = HashAlgId::SHA256;
@@ -220,9 +180,22 @@ mod test {
#[test] #[test]
fn test_wrong_server_commitment() { fn test_wrong_server_commitment() {
let (mut request, connection) = request_and_connection(); let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
let connection = ConnectionFixture::tlsnotary(transcript.length());
let attestation = attestation((request.clone(), connection)); let RequestFixture { mut request, .. } = request_fixture(
transcript,
encoding_provider(GET_WITH_HEADER, OK_JSON),
connection.clone(),
Blake3::default(),
);
let attestation = attestation_fixture(
request.clone(),
connection,
SignatureAlgId::SECP256K1,
encoder_seed().to_vec(),
);
let ConnectionFixture { let ConnectionFixture {
server_cert_data, .. server_cert_data, ..
@@ -242,9 +215,22 @@ mod test {
#[test] #[test]
fn test_wrong_encoding_commitment_root() { fn test_wrong_encoding_commitment_root() {
let (mut request, connection) = request_and_connection(); let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
let connection = ConnectionFixture::tlsnotary(transcript.length());
let attestation = attestation((request.clone(), connection)); let RequestFixture { mut request, .. } = request_fixture(
transcript,
encoding_provider(GET_WITH_HEADER, OK_JSON),
connection.clone(),
Blake3::default(),
);
let attestation = attestation_fixture(
request.clone(),
connection,
SignatureAlgId::SECP256K1,
encoder_seed().to_vec(),
);
request.encoding_commitment_root = Some(TypedHash { request.encoding_commitment_root = Some(TypedHash {
alg: HashAlgId::BLAKE3, alg: HashAlgId::BLAKE3,

View File

@@ -611,7 +611,7 @@ mod tests {
} }
#[rstest] #[rstest]
fn test_get_subsequence(transcript: Transcript) { fn test_transcript_get_subsequence(transcript: Transcript) {
let subseq = transcript let subseq = transcript
.get(Direction::Received, &Idx(RangeSet::from([0..4, 7..10]))) .get(Direction::Received, &Idx(RangeSet::from([0..4, 7..10])))
.unwrap(); .unwrap();
@@ -631,4 +631,207 @@ mod tests {
let subseq = transcript.get(Direction::Sent, &Idx(RangeSet::from([0..4, 7..10, 11..13]))); let subseq = transcript.get(Direction::Sent, &Idx(RangeSet::from([0..4, 7..10, 11..13])));
assert_eq!(subseq, None); assert_eq!(subseq, None);
} }
#[rstest]
fn test_transcript_to_partial_success(transcript: Transcript) {
let partial = transcript.to_partial(Idx::new(0..2), Idx::new(3..7));
assert_eq!(partial.sent_unsafe(), [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(
partial.received_unsafe(),
[0, 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
);
}
#[rstest]
#[should_panic]
fn test_transcript_to_partial_failure(transcript: Transcript) {
let _ = transcript.to_partial(Idx::new(0..14), Idx::new(3..7));
}
#[rstest]
fn test_partial_transcript_contains(transcript: Transcript) {
let partial = transcript.to_partial(Idx::new(0..2), Idx::new(3..7));
assert!(partial.contains(Direction::Sent, &Idx::new([0..5, 7..10])));
assert!(!partial.contains(Direction::Received, &Idx::new([4..6, 7..13])))
}
#[rstest]
fn test_partial_transcript_unauthed(transcript: Transcript) {
let partial = transcript.to_partial(Idx::new(0..2), Idx::new(3..7));
assert_eq!(partial.sent_unauthed(), Idx::new(2..12));
assert_eq!(partial.received_unauthed(), Idx::new([0..3, 7..12]));
}
#[rstest]
fn test_partial_transcript_union_success(transcript: Transcript) {
// Non overlapping ranges.
let mut simple_partial = transcript.to_partial(Idx::new(0..2), Idx::new(3..7));
let other_simple_partial = transcript.to_partial(Idx::new(3..5), Idx::new(1..2));
simple_partial.union_transcript(&other_simple_partial);
assert_eq!(
simple_partial.sent_unsafe(),
[0, 1, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
simple_partial.received_unsafe(),
[0, 1, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
);
assert_eq!(simple_partial.sent_authed(), &Idx::new([0..2, 3..5]));
assert_eq!(simple_partial.received_authed(), &Idx::new([1..2, 3..7]));
// Overwrite with another partial transcript.
let another_simple_partial = transcript.to_partial(Idx::new(1..4), Idx::new(6..9));
simple_partial.union_transcript(&another_simple_partial);
assert_eq!(
simple_partial.sent_unsafe(),
[0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
simple_partial.received_unsafe(),
[0, 1, 0, 3, 4, 5, 6, 7, 8, 0, 0, 0]
);
assert_eq!(simple_partial.sent_authed(), &Idx::new(0..5));
assert_eq!(simple_partial.received_authed(), &Idx::new([1..2, 3..9]));
// Overlapping ranges.
let mut overlap_partial = transcript.to_partial(Idx::new(4..6), Idx::new(3..7));
let other_overlap_partial = transcript.to_partial(Idx::new(3..5), Idx::new(5..9));
overlap_partial.union_transcript(&other_overlap_partial);
assert_eq!(
overlap_partial.sent_unsafe(),
[0, 0, 0, 3, 4, 5, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
overlap_partial.received_unsafe(),
[0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 0]
);
assert_eq!(overlap_partial.sent_authed(), &Idx::new([3..5, 4..6]));
assert_eq!(overlap_partial.received_authed(), &Idx::new([3..7, 5..9]));
// Equal ranges.
let mut equal_partial = transcript.to_partial(Idx::new(4..6), Idx::new(3..7));
let other_equal_partial = transcript.to_partial(Idx::new(4..6), Idx::new(3..7));
equal_partial.union_transcript(&other_equal_partial);
assert_eq!(
equal_partial.sent_unsafe(),
[0, 0, 0, 0, 4, 5, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
equal_partial.received_unsafe(),
[0, 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
);
assert_eq!(equal_partial.sent_authed(), &Idx::new(4..6));
assert_eq!(equal_partial.received_authed(), &Idx::new(3..7));
// Subset ranges.
let mut subset_partial = transcript.to_partial(Idx::new(4..10), Idx::new(3..11));
let other_subset_partial = transcript.to_partial(Idx::new(6..9), Idx::new(5..6));
subset_partial.union_transcript(&other_subset_partial);
assert_eq!(
subset_partial.sent_unsafe(),
[0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 0, 0]
);
assert_eq!(
subset_partial.received_unsafe(),
[0, 0, 0, 3, 4, 5, 6, 7, 8, 9, 10, 0]
);
assert_eq!(subset_partial.sent_authed(), &Idx::new(4..10));
assert_eq!(subset_partial.received_authed(), &Idx::new(3..11));
}
#[rstest]
#[should_panic]
fn test_partial_transcript_union_failure(transcript: Transcript) {
let mut partial = transcript.to_partial(Idx::new(4..10), Idx::new(3..11));
let other_transcript = Transcript::new(
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
);
let other_partial = other_transcript.to_partial(Idx::new(6..9), Idx::new(5..6));
partial.union_transcript(&other_partial);
}
#[rstest]
fn test_partial_transcript_union_subseq_success(transcript: Transcript) {
let mut partial = transcript.to_partial(Idx::new(4..10), Idx::new(3..11));
let sent_seq = Subsequence::new(Idx::new([0..3, 5..7]), [0, 1, 2, 5, 6].into()).unwrap();
let recv_seq = Subsequence::new(Idx::new([0..4, 5..7]), [0, 1, 2, 3, 5, 6].into()).unwrap();
partial.union_subsequence(Direction::Sent, &sent_seq);
partial.union_subsequence(Direction::Received, &recv_seq);
assert_eq!(partial.sent_unsafe(), [0, 1, 2, 0, 4, 5, 6, 7, 8, 9, 0, 0]);
assert_eq!(
partial.received_unsafe(),
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0]
);
assert_eq!(partial.sent_authed(), &Idx::new([0..3, 4..10]));
assert_eq!(partial.received_authed(), &Idx::new(0..11));
// Overwrite with another subseq.
let other_sent_seq = Subsequence::new(Idx::new(0..3), [3, 2, 1].into()).unwrap();
partial.union_subsequence(Direction::Sent, &other_sent_seq);
assert_eq!(partial.sent_unsafe(), [3, 2, 1, 0, 4, 5, 6, 7, 8, 9, 0, 0]);
assert_eq!(partial.sent_authed(), &Idx::new([0..3, 4..10]));
}
#[rstest]
#[should_panic]
fn test_partial_transcript_union_subseq_failure(transcript: Transcript) {
let mut partial = transcript.to_partial(Idx::new(4..10), Idx::new(3..11));
let sent_seq = Subsequence::new(Idx::new([0..3, 13..15]), [0, 1, 2, 5, 6].into()).unwrap();
partial.union_subsequence(Direction::Sent, &sent_seq);
}
#[rstest]
fn test_partial_transcript_set_unauthed_range(transcript: Transcript) {
let mut partial = transcript.to_partial(Idx::new(4..10), Idx::new(3..7));
partial.set_unauthed_range(7, Direction::Sent, 2..5);
partial.set_unauthed_range(5, Direction::Sent, 0..2);
partial.set_unauthed_range(3, Direction::Received, 4..6);
partial.set_unauthed_range(1, Direction::Received, 3..7);
assert_eq!(partial.sent_unsafe(), [5, 5, 7, 7, 4, 5, 6, 7, 8, 9, 0, 0]);
assert_eq!(
partial.received_unsafe(),
[0, 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
);
}
#[rstest]
#[should_panic]
fn test_subsequence_new_invalid_len() {
let _ = Subsequence::new(Idx::new([0..3, 5..8]), [0, 1, 2, 5, 6].into()).unwrap();
}
#[rstest]
#[should_panic]
fn test_subsequence_copy_to_invalid_len() {
let seq = Subsequence::new(Idx::new([0..3, 5..7]), [0, 1, 2, 5, 6].into()).unwrap();
let mut data: [u8; 3] = [0, 1, 2];
seq.copy_to(&mut data);
}
} }

View File

@@ -182,3 +182,144 @@ impl From<MerkleError> for EncodingProofError {
Self::new(ErrorKind::Proof, error) Self::new(ErrorKind::Proof, error)
} }
} }
#[cfg(test)]
mod test {
use tlsn_data_fixtures::http::{request::POST_JSON, response::OK_JSON};
use crate::{
fixtures::{encoder_seed, encoding_provider},
hash::Blake3,
transcript::{encoding::EncodingTree, Idx, Transcript},
};
use super::*;
struct EncodingFixture {
transcript: Transcript,
proof: EncodingProof,
commitment: EncodingCommitment,
}
fn new_encoding_fixture(seed: Vec<u8>) -> EncodingFixture {
let transcript = Transcript::new(POST_JSON, OK_JSON);
let idx_0 = (Direction::Sent, Idx::new(0..POST_JSON.len()));
let idx_1 = (Direction::Received, Idx::new(0..OK_JSON.len()));
let provider = encoding_provider(transcript.sent(), transcript.received());
let transcript_length = TranscriptLength {
sent: transcript.sent().len() as u32,
received: transcript.received().len() as u32,
};
let tree = EncodingTree::new(
&Blake3::default(),
[&idx_0, &idx_1],
&provider,
&transcript_length,
)
.unwrap();
let proof = tree
.proof(&transcript, [&idx_0, &idx_1].into_iter())
.unwrap();
let commitment = EncodingCommitment {
root: tree.root(),
seed,
};
EncodingFixture {
transcript,
proof,
commitment,
}
}
#[test]
fn test_verify_encoding_proof_invalid_seed() {
let EncodingFixture {
transcript,
proof,
commitment,
} = new_encoding_fixture(encoder_seed().to_vec().split_off(1));
let err = proof
.verify_with_provider(
&CryptoProvider::default(),
&transcript.length(),
&commitment,
)
.unwrap_err();
assert!(matches!(err.kind, ErrorKind::Commitment));
}
#[test]
fn test_verify_encoding_proof_out_of_range() {
let EncodingFixture {
transcript,
proof,
commitment,
} = new_encoding_fixture(encoder_seed().to_vec());
let err = proof
.verify_with_provider(
&CryptoProvider::default(),
&TranscriptLength {
sent: (transcript.len_of_direction(Direction::Sent) - 1) as u32,
received: (transcript.len_of_direction(Direction::Received) - 2) as u32,
},
&commitment,
)
.unwrap_err();
assert!(matches!(err.kind, ErrorKind::Proof));
}
#[test]
fn test_verify_encoding_proof_tampered_encoding_seq() {
let EncodingFixture {
transcript,
mut proof,
commitment,
} = new_encoding_fixture(encoder_seed().to_vec());
let Opening { seq, .. } = proof.openings.values_mut().next().unwrap();
*seq = Subsequence::new(Idx::new([0..3, 13..15]), [0, 1, 2, 5, 6].into()).unwrap();
let err = proof
.verify_with_provider(
&CryptoProvider::default(),
&transcript.length(),
&commitment,
)
.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(encoder_seed().to_vec());
let Opening { blinder, .. } = proof.openings.values_mut().next().unwrap();
*blinder = rand::random();
let err = proof
.verify_with_provider(
&CryptoProvider::default(),
&transcript.length(),
&commitment,
)
.unwrap_err();
assert!(matches!(err.kind, ErrorKind::Proof));
}
}

View File

@@ -287,6 +287,22 @@ mod tests {
assert_eq!(partial_transcript.received_unsafe(), transcript.received()); assert_eq!(partial_transcript.received_unsafe(), transcript.received());
} }
#[test]
fn test_encoding_tree_proof_missing_leaf() {
let transcript = Transcript::new(POST_JSON, OK_JSON);
let idx_0 = (Direction::Sent, Idx::new(0..POST_JSON.len()));
let idx_1 = (Direction::Received, Idx::new(0..4));
let idx_2 = (Direction::Received, Idx::new(4..OK_JSON.len()));
let tree = new_tree(&transcript, [&idx_0, &idx_1].into_iter()).unwrap();
let result = tree
.proof(&transcript, [&idx_0, &idx_1, &idx_2].into_iter())
.unwrap_err();
assert!(matches!(result, EncodingTreeError::MissingLeaf { .. }));
}
#[test] #[test]
fn test_encoding_tree_out_of_bounds() { fn test_encoding_tree_out_of_bounds() {
let transcript = Transcript::new(POST_JSON, OK_JSON); let transcript = Transcript::new(POST_JSON, OK_JSON);
@@ -317,14 +333,5 @@ mod tests {
) )
.unwrap_err(); .unwrap_err();
assert!(matches!(result, EncodingTreeError::MissingEncoding { .. })); assert!(matches!(result, EncodingTreeError::MissingEncoding { .. }));
let result = EncodingTree::new(
&Blake3::default(),
[(Direction::Sent, Idx::new(0..8))].iter(),
&provider,
&transcript_length,
)
.unwrap_err();
assert!(matches!(result, EncodingTreeError::MissingEncoding { .. }));
} }
} }

View File

@@ -357,10 +357,21 @@ impl fmt::Display for TranscriptProofBuilderError {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};
use crate::{
fixtures::{
attestation_fixture, encoder_seed, encoding_provider, request_fixture,
ConnectionFixture, RequestFixture,
},
hash::Blake3,
signing::SignatureAlgId,
};
use super::*; use super::*;
#[test] #[test]
fn test_range_out_of_bounds() { fn test_reveal_range_out_of_bounds() {
let transcript = Transcript::new( 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],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
@@ -368,7 +379,83 @@ mod tests {
let index = Index::default(); let index = Index::default();
let mut builder = TranscriptProofBuilder::new(&transcript, None, &index); let mut builder = TranscriptProofBuilder::new(&transcript, None, &index);
assert!(builder.reveal(&(10..15), Direction::Sent).is_err()); let err = builder.reveal(&(10..15), Direction::Sent).err().unwrap();
assert!(builder.reveal(&(10..15), Direction::Received).is_err()); assert!(matches!(err.kind, BuilderErrorKind::Index));
let err = builder
.reveal(&(10..15), Direction::Received)
.err()
.unwrap();
assert!(matches!(err.kind, BuilderErrorKind::Index));
}
#[test]
fn test_reveal_missing_encoding_tree() {
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],
);
let index = Index::default();
let mut builder = TranscriptProofBuilder::new(&transcript, None, &index);
let err = builder.reveal_recv(&(9..11)).err().unwrap();
assert!(matches!(err.kind, BuilderErrorKind::MissingCommitment));
}
#[test]
fn test_reveal_missing_encoding_commitment_range() {
let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
let connection = ConnectionFixture::tlsnotary(transcript.length());
let RequestFixture { encoding_tree, .. } = request_fixture(
transcript.clone(),
encoding_provider(GET_WITH_HEADER, OK_JSON),
connection,
Blake3::default(),
);
let index = Index::default();
let mut builder = TranscriptProofBuilder::new(&transcript, Some(&encoding_tree), &index);
let err = builder.reveal_recv(&(0..11)).err().unwrap();
assert!(matches!(err.kind, BuilderErrorKind::MissingCommitment));
}
#[test]
fn test_verify_missing_encoding_commitment() {
let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
let connection = ConnectionFixture::tlsnotary(transcript.length());
let RequestFixture {
mut request,
encoding_tree,
} = request_fixture(
transcript.clone(),
encoding_provider(GET_WITH_HEADER, OK_JSON),
connection.clone(),
Blake3::default(),
);
let index = Index::default();
let mut builder = TranscriptProofBuilder::new(&transcript, Some(&encoding_tree), &index);
builder.reveal_recv(&(0..transcript.len().1)).unwrap();
let transcript_proof = builder.build().unwrap();
request.encoding_commitment_root = None;
let attestation = attestation_fixture(
request,
connection,
SignatureAlgId::SECP256K1,
encoder_seed().to_vec(),
);
let provider = CryptoProvider::default();
let err = transcript_proof
.verify_with_provider(&provider, &attestation.body)
.err()
.unwrap();
assert!(matches!(err.kind, ErrorKind::Encoding));
} }
} }