mirror of
https://github.com/tlsnotary/tlsn.git
synced 2026-01-09 14:48:13 -05:00
chore: core-rewrite unit tests (#608)
* Add tests for signing, index. * Add error scenarios. * Add cert tests, modify previous tests. * Improve cert tests. * Add tests for request. * Fix clippy * Fix clippy. * Change requests test style. * Add attestation unit tests. * Formatting. * Clippy. * make data fixtures optional --------- Co-authored-by: yuroitaki <> Co-authored-by: sinu <65924192+sinui0@users.noreply.github.com>
This commit is contained in:
@@ -10,9 +10,10 @@ edition = "2021"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
fixtures = ["dep:hex"]
|
||||
fixtures = ["dep:hex", "dep:tlsn-data-fixtures"]
|
||||
|
||||
[dependencies]
|
||||
tlsn-data-fixtures = { workspace = true, optional = true }
|
||||
tlsn-tls-core = { workspace = true, features = ["serde"] }
|
||||
tlsn-utils = { workspace = true }
|
||||
|
||||
@@ -29,6 +30,7 @@ k256 = { workspace = true }
|
||||
opaque-debug = { workspace = true }
|
||||
p256 = { workspace = true, features = ["serde"] }
|
||||
rand = { workspace = true }
|
||||
rand_core = { workspace = true }
|
||||
rs_merkle = { workspace = true, features = ["serde"] }
|
||||
rstest = { workspace = true, optional = true }
|
||||
serde = { workspace = true }
|
||||
@@ -41,7 +43,6 @@ webpki-roots = { workspace = true }
|
||||
[dev-dependencies]
|
||||
rstest = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
rand_core = { workspace = true }
|
||||
rand_chacha = { workspace = true }
|
||||
bincode = { workspace = true }
|
||||
tlsn-data-fixtures = { workspace = true }
|
||||
|
||||
@@ -237,3 +237,234 @@ impl std::fmt::Display for AttestationBuilderError {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rstest::{fixture, rstest};
|
||||
use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};
|
||||
|
||||
use crate::{
|
||||
connection::{HandshakeData, HandshakeDataV1_2},
|
||||
fixtures::{encoder_seed, encoding_provider, ConnectionFixture},
|
||||
hash::Blake3,
|
||||
request::RequestConfig,
|
||||
transcript::{encoding::EncodingTree, Transcript, TranscriptCommitConfigBuilder},
|
||||
};
|
||||
|
||||
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]
|
||||
#[once]
|
||||
fn default_attestation_config() -> AttestationConfig {
|
||||
AttestationConfig::builder()
|
||||
.supported_signature_algs([SignatureAlgId::SECP256K1])
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[fixture]
|
||||
#[once]
|
||||
fn crypto_provider() -> CryptoProvider {
|
||||
let mut provider = CryptoProvider::default();
|
||||
provider.signer.set_secp256k1(&[42u8; 32]).unwrap();
|
||||
provider
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_attestation_builder_accept_unsupported_signer() {
|
||||
let (request, _) = request_and_connection();
|
||||
let attestation_config = AttestationConfig::builder()
|
||||
.supported_signature_algs([SignatureAlgId::SECP256R1])
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let err = Attestation::builder(&attestation_config)
|
||||
.accept_request(request)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert!(err.is_request());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_attestation_builder_accept_unsupported_hasher() {
|
||||
let (request, _) = request_and_connection();
|
||||
|
||||
let attestation_config = AttestationConfig::builder()
|
||||
.supported_signature_algs([SignatureAlgId::SECP256K1])
|
||||
.supported_hash_algs([HashAlgId::KECCAK256])
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let err = Attestation::builder(&attestation_config)
|
||||
.accept_request(request)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert!(err.is_request());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_attestation_builder_accept_unsupported_encoding_commitment() {
|
||||
let (request, _) = request_and_connection();
|
||||
|
||||
let attestation_config = AttestationConfig::builder()
|
||||
.supported_signature_algs([SignatureAlgId::SECP256K1])
|
||||
.supported_fields([
|
||||
FieldKind::ConnectionInfo,
|
||||
FieldKind::ServerEphemKey,
|
||||
FieldKind::ServerIdentityCommitment,
|
||||
])
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let err = Attestation::builder(&attestation_config)
|
||||
.accept_request(request)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert!(err.is_request());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_attestation_builder_sign_missing_signer(
|
||||
default_attestation_config: &AttestationConfig,
|
||||
) {
|
||||
let (request, _) = request_and_connection();
|
||||
|
||||
let attestation_builder = Attestation::builder(default_attestation_config)
|
||||
.accept_request(request.clone())
|
||||
.unwrap();
|
||||
|
||||
let mut provider = CryptoProvider::default();
|
||||
provider.signer.set_secp256r1(&[42u8; 32]).unwrap();
|
||||
|
||||
let err = attestation_builder.build(&provider).err().unwrap();
|
||||
assert!(matches!(err.kind, ErrorKind::Config));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_attestation_builder_sign_missing_encoding_seed(
|
||||
default_attestation_config: &AttestationConfig,
|
||||
crypto_provider: &CryptoProvider,
|
||||
) {
|
||||
let (request, connection) = request_and_connection();
|
||||
|
||||
let mut attestation_builder = Attestation::builder(default_attestation_config)
|
||||
.accept_request(request.clone())
|
||||
.unwrap();
|
||||
|
||||
let ConnectionFixture {
|
||||
connection_info,
|
||||
server_cert_data,
|
||||
..
|
||||
} = connection;
|
||||
|
||||
let HandshakeData::V1_2(HandshakeDataV1_2 {
|
||||
server_ephemeral_key,
|
||||
..
|
||||
}) = server_cert_data.handshake.clone();
|
||||
|
||||
attestation_builder
|
||||
.connection_info(connection_info.clone())
|
||||
.server_ephemeral_key(server_ephemeral_key);
|
||||
|
||||
let err = attestation_builder.build(crypto_provider).err().unwrap();
|
||||
assert!(matches!(err.kind, ErrorKind::Field));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_attestation_builder_sign_missing_server_ephemeral_key(
|
||||
default_attestation_config: &AttestationConfig,
|
||||
crypto_provider: &CryptoProvider,
|
||||
) {
|
||||
let (request, connection) = request_and_connection();
|
||||
|
||||
let mut attestation_builder = Attestation::builder(default_attestation_config)
|
||||
.accept_request(request.clone())
|
||||
.unwrap();
|
||||
|
||||
let ConnectionFixture {
|
||||
connection_info, ..
|
||||
} = connection;
|
||||
|
||||
attestation_builder
|
||||
.connection_info(connection_info.clone())
|
||||
.encoding_seed(encoder_seed().to_vec());
|
||||
|
||||
let err = attestation_builder.build(crypto_provider).err().unwrap();
|
||||
assert!(matches!(err.kind, ErrorKind::Field));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_attestation_builder_sign_missing_connection_info(
|
||||
default_attestation_config: &AttestationConfig,
|
||||
crypto_provider: &CryptoProvider,
|
||||
) {
|
||||
let (request, connection) = request_and_connection();
|
||||
|
||||
let mut attestation_builder = Attestation::builder(default_attestation_config)
|
||||
.accept_request(request.clone())
|
||||
.unwrap();
|
||||
|
||||
let ConnectionFixture {
|
||||
server_cert_data, ..
|
||||
} = connection;
|
||||
|
||||
let HandshakeData::V1_2(HandshakeDataV1_2 {
|
||||
server_ephemeral_key,
|
||||
..
|
||||
}) = server_cert_data.handshake.clone();
|
||||
|
||||
attestation_builder
|
||||
.server_ephemeral_key(server_ephemeral_key)
|
||||
.encoding_seed(encoder_seed().to_vec());
|
||||
|
||||
let err = attestation_builder.build(crypto_provider).err().unwrap();
|
||||
assert!(matches!(err.kind, ErrorKind::Field));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,3 +376,291 @@ pub enum CertificateVerificationError {
|
||||
#[error("invalid server ephemeral key")]
|
||||
InvalidServerEphemeralKey,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{fixtures::ConnectionFixture, transcript::Transcript};
|
||||
|
||||
use hex::FromHex;
|
||||
use rstest::*;
|
||||
use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};
|
||||
|
||||
#[fixture]
|
||||
#[once]
|
||||
fn crypto_provider() -> CryptoProvider {
|
||||
CryptoProvider::default()
|
||||
}
|
||||
|
||||
fn tlsnotary() -> ConnectionFixture {
|
||||
ConnectionFixture::tlsnotary(Transcript::new(GET_WITH_HEADER, OK_JSON).length())
|
||||
}
|
||||
|
||||
fn appliedzkp() -> ConnectionFixture {
|
||||
ConnectionFixture::appliedzkp(Transcript::new(GET_WITH_HEADER, OK_JSON).length())
|
||||
}
|
||||
|
||||
/// Expect chain verification to succeed.
|
||||
#[rstest]
|
||||
#[case::tlsnotary(tlsnotary())]
|
||||
#[case::appliedzkp(appliedzkp())]
|
||||
fn test_verify_cert_chain_sucess_ca_implicit(
|
||||
crypto_provider: &CryptoProvider,
|
||||
#[case] mut data: ConnectionFixture,
|
||||
) {
|
||||
// Remove the CA cert
|
||||
data.server_cert_data.certs.pop();
|
||||
|
||||
assert!(data
|
||||
.server_cert_data
|
||||
.verify_with_provider(
|
||||
crypto_provider,
|
||||
data.connection_info.time,
|
||||
data.server_ephemeral_key(),
|
||||
&ServerName::from(data.server_name.as_ref()),
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
/// Expect chain verification to succeed even when a trusted CA is provided
|
||||
/// among the intermediate certs. webpki handles such cases properly.
|
||||
#[rstest]
|
||||
#[case::tlsnotary(tlsnotary())]
|
||||
#[case::appliedzkp(appliedzkp())]
|
||||
fn test_verify_cert_chain_success_ca_explicit(
|
||||
crypto_provider: &CryptoProvider,
|
||||
#[case] data: ConnectionFixture,
|
||||
) {
|
||||
assert!(data
|
||||
.server_cert_data
|
||||
.verify_with_provider(
|
||||
crypto_provider,
|
||||
data.connection_info.time,
|
||||
data.server_ephemeral_key(),
|
||||
&ServerName::from(data.server_name.as_ref()),
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
/// Expect to fail since the end entity cert was not valid at the time.
|
||||
#[rstest]
|
||||
#[case::tlsnotary(tlsnotary())]
|
||||
#[case::appliedzkp(appliedzkp())]
|
||||
fn test_verify_cert_chain_fail_bad_time(
|
||||
crypto_provider: &CryptoProvider,
|
||||
#[case] data: ConnectionFixture,
|
||||
) {
|
||||
// unix time when the cert chain was NOT valid
|
||||
let bad_time: u64 = 1571465711;
|
||||
|
||||
let err = data.server_cert_data.verify_with_provider(
|
||||
crypto_provider,
|
||||
bad_time,
|
||||
data.server_ephemeral_key(),
|
||||
&ServerName::from(data.server_name.as_ref()),
|
||||
);
|
||||
|
||||
assert!(matches!(
|
||||
err.unwrap_err(),
|
||||
CertificateVerificationError::InvalidCert
|
||||
));
|
||||
}
|
||||
|
||||
/// Expect to fail when no intermediate cert provided.
|
||||
#[rstest]
|
||||
#[case::tlsnotary(tlsnotary())]
|
||||
#[case::appliedzkp(appliedzkp())]
|
||||
fn test_verify_cert_chain_fail_no_interm_cert(
|
||||
crypto_provider: &CryptoProvider,
|
||||
#[case] mut data: ConnectionFixture,
|
||||
) {
|
||||
// Remove the CA cert
|
||||
data.server_cert_data.certs.pop();
|
||||
// Remove the intermediate cert
|
||||
data.server_cert_data.certs.pop();
|
||||
|
||||
let err = data.server_cert_data.verify_with_provider(
|
||||
crypto_provider,
|
||||
data.connection_info.time,
|
||||
data.server_ephemeral_key(),
|
||||
&ServerName::from(data.server_name.as_ref()),
|
||||
);
|
||||
|
||||
assert!(matches!(
|
||||
err.unwrap_err(),
|
||||
CertificateVerificationError::InvalidCert
|
||||
));
|
||||
}
|
||||
|
||||
/// Expect to fail when no intermediate cert provided even if a trusted CA
|
||||
/// cert is provided.
|
||||
#[rstest]
|
||||
#[case::tlsnotary(tlsnotary())]
|
||||
#[case::appliedzkp(appliedzkp())]
|
||||
fn test_verify_cert_chain_fail_no_interm_cert_with_ca_cert(
|
||||
crypto_provider: &CryptoProvider,
|
||||
#[case] mut data: ConnectionFixture,
|
||||
) {
|
||||
// Remove the intermediate cert
|
||||
data.server_cert_data.certs.remove(1);
|
||||
|
||||
let err = data.server_cert_data.verify_with_provider(
|
||||
crypto_provider,
|
||||
data.connection_info.time,
|
||||
data.server_ephemeral_key(),
|
||||
&ServerName::from(data.server_name.as_ref()),
|
||||
);
|
||||
|
||||
assert!(matches!(
|
||||
err.unwrap_err(),
|
||||
CertificateVerificationError::InvalidCert
|
||||
));
|
||||
}
|
||||
|
||||
/// Expect to fail because end-entity cert is wrong.
|
||||
#[rstest]
|
||||
#[case::tlsnotary(tlsnotary())]
|
||||
#[case::appliedzkp(appliedzkp())]
|
||||
fn test_verify_cert_chain_fail_bad_ee_cert(
|
||||
crypto_provider: &CryptoProvider,
|
||||
#[case] mut data: ConnectionFixture,
|
||||
) {
|
||||
let ee: &[u8] = include_bytes!("./fixtures/data/unknown/ee.der");
|
||||
|
||||
// Change the end entity cert
|
||||
data.server_cert_data.certs[0] = Certificate(ee.to_vec());
|
||||
|
||||
let err = data.server_cert_data.verify_with_provider(
|
||||
crypto_provider,
|
||||
data.connection_info.time,
|
||||
data.server_ephemeral_key(),
|
||||
&ServerName::from(data.server_name.as_ref()),
|
||||
);
|
||||
|
||||
assert!(matches!(
|
||||
err.unwrap_err(),
|
||||
CertificateVerificationError::InvalidCert
|
||||
));
|
||||
}
|
||||
|
||||
/// Expect sig verification to fail because client_random is wrong.
|
||||
#[rstest]
|
||||
#[case::tlsnotary(tlsnotary())]
|
||||
#[case::appliedzkp(appliedzkp())]
|
||||
fn test_verify_sig_ke_params_fail_bad_client_random(
|
||||
crypto_provider: &CryptoProvider,
|
||||
#[case] mut data: ConnectionFixture,
|
||||
) {
|
||||
let HandshakeData::V1_2(HandshakeDataV1_2 { client_random, .. }) =
|
||||
&mut data.server_cert_data.handshake;
|
||||
client_random[31] = client_random[31].wrapping_add(1);
|
||||
|
||||
let err = data.server_cert_data.verify_with_provider(
|
||||
crypto_provider,
|
||||
data.connection_info.time,
|
||||
data.server_ephemeral_key(),
|
||||
&ServerName::from(data.server_name.as_ref()),
|
||||
);
|
||||
|
||||
assert!(matches!(
|
||||
err.unwrap_err(),
|
||||
CertificateVerificationError::InvalidServerSignature
|
||||
));
|
||||
}
|
||||
|
||||
/// Expect sig verification to fail because the sig is wrong.
|
||||
#[rstest]
|
||||
#[case::tlsnotary(tlsnotary())]
|
||||
#[case::appliedzkp(appliedzkp())]
|
||||
fn test_verify_sig_ke_params_fail_bad_sig(
|
||||
crypto_provider: &CryptoProvider,
|
||||
#[case] mut data: ConnectionFixture,
|
||||
) {
|
||||
data.server_cert_data.sig.sig[31] = data.server_cert_data.sig.sig[31].wrapping_add(1);
|
||||
|
||||
let err = data.server_cert_data.verify_with_provider(
|
||||
crypto_provider,
|
||||
data.connection_info.time,
|
||||
data.server_ephemeral_key(),
|
||||
&ServerName::from(data.server_name.as_ref()),
|
||||
);
|
||||
|
||||
assert!(matches!(
|
||||
err.unwrap_err(),
|
||||
CertificateVerificationError::InvalidServerSignature
|
||||
));
|
||||
}
|
||||
|
||||
/// Expect to fail because the dns name is not in the cert.
|
||||
#[rstest]
|
||||
#[case::tlsnotary(tlsnotary())]
|
||||
#[case::appliedzkp(appliedzkp())]
|
||||
fn test_check_dns_name_present_in_cert_fail_bad_host(
|
||||
crypto_provider: &CryptoProvider,
|
||||
#[case] data: ConnectionFixture,
|
||||
) {
|
||||
let bad_name = ServerName::from("badhost.com");
|
||||
|
||||
let err = data.server_cert_data.verify_with_provider(
|
||||
crypto_provider,
|
||||
data.connection_info.time,
|
||||
data.server_ephemeral_key(),
|
||||
&bad_name,
|
||||
);
|
||||
|
||||
assert!(matches!(
|
||||
err.unwrap_err(),
|
||||
CertificateVerificationError::InvalidCert
|
||||
));
|
||||
}
|
||||
|
||||
/// Expect to fail because the ephemeral key provided is wrong.
|
||||
#[rstest]
|
||||
#[case::tlsnotary(tlsnotary())]
|
||||
#[case::appliedzkp(appliedzkp())]
|
||||
fn test_invalid_ephemeral_key(
|
||||
crypto_provider: &CryptoProvider,
|
||||
#[case] data: ConnectionFixture,
|
||||
) {
|
||||
let wrong_ephemeral_key = ServerEphemKey {
|
||||
typ: KeyType::SECP256R1,
|
||||
key: Vec::<u8>::from_hex(include_bytes!("./fixtures/data/unknown/pubkey")).unwrap(),
|
||||
};
|
||||
|
||||
let err = data.server_cert_data.verify_with_provider(
|
||||
crypto_provider,
|
||||
data.connection_info.time,
|
||||
&wrong_ephemeral_key,
|
||||
&ServerName::from(data.server_name.as_ref()),
|
||||
);
|
||||
|
||||
assert!(matches!(
|
||||
err.unwrap_err(),
|
||||
CertificateVerificationError::InvalidServerEphemeralKey
|
||||
));
|
||||
}
|
||||
|
||||
/// Expect to fail when no cert provided.
|
||||
#[rstest]
|
||||
#[case::tlsnotary(tlsnotary())]
|
||||
#[case::appliedzkp(appliedzkp())]
|
||||
fn test_verify_cert_chain_fail_no_cert(
|
||||
crypto_provider: &CryptoProvider,
|
||||
#[case] mut data: ConnectionFixture,
|
||||
) {
|
||||
// Empty certs
|
||||
data.server_cert_data.certs = Vec::new();
|
||||
|
||||
let err = data.server_cert_data.verify_with_provider(
|
||||
crypto_provider,
|
||||
data.connection_info.time,
|
||||
data.server_ephemeral_key(),
|
||||
&ServerName::from(data.server_name.as_ref()),
|
||||
);
|
||||
|
||||
assert!(matches!(
|
||||
err.unwrap_err(),
|
||||
CertificateVerificationError::MissingCerts
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ impl ConnectionFixture {
|
||||
Certificate(include_bytes!("fixtures/data/appliedzkp.org/ca.der").to_vec()),
|
||||
],
|
||||
sig: ServerSignature {
|
||||
scheme: SignatureScheme::RSA_PKCS1_SHA256,
|
||||
scheme: SignatureScheme::ECDSA_NISTP256_SHA256,
|
||||
sig: Vec::<u8>::from_hex(include_bytes!(
|
||||
"fixtures/data/appliedzkp.org/signature"
|
||||
))
|
||||
@@ -109,6 +109,15 @@ impl ConnectionFixture {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the server_ephemeral_key fixture.
|
||||
pub fn server_ephemeral_key(&self) -> &ServerEphemKey {
|
||||
let HandshakeData::V1_2(HandshakeDataV1_2 {
|
||||
server_ephemeral_key,
|
||||
..
|
||||
}) = &self.server_cert_data.handshake;
|
||||
server_ephemeral_key
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an encoding provider fixture.
|
||||
|
||||
1
crates/core/src/fixtures/data/unknown/pubkey
Normal file
1
crates/core/src/fixtures/data/unknown/pubkey
Normal file
@@ -0,0 +1 @@
|
||||
14e1f634ecfee5bd4f987f8c571146cb2acb432e400b2fabcbd8ed77f6ef08bd5496cd51d449ce131efd74a24d07b01c38ec794d22d3d43b2b05b907e72797534e
|
||||
@@ -104,3 +104,74 @@ impl From<Vec<PlaintextHashSecret>> for Index<PlaintextHashSecret> {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use utils::range::RangeSet;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
struct Stub {
|
||||
field_index: FieldId,
|
||||
index: Idx,
|
||||
}
|
||||
|
||||
impl From<Vec<Stub>> for Index<Stub> {
|
||||
fn from(items: Vec<Stub>) -> Self {
|
||||
Self::new(items, |item: &Stub| (&item.field_index, &item.index))
|
||||
}
|
||||
}
|
||||
|
||||
fn stubs() -> Vec<Stub> {
|
||||
vec![
|
||||
Stub {
|
||||
field_index: FieldId(1),
|
||||
index: Idx::new(RangeSet::from([0..1, 18..21])),
|
||||
},
|
||||
Stub {
|
||||
field_index: FieldId(2),
|
||||
index: Idx::new(RangeSet::from([1..5, 8..11])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_successful_retrieval() {
|
||||
let stub_a_index = Idx::new(RangeSet::from([0..4, 7..10]));
|
||||
let stub_b_field_index = FieldId(8);
|
||||
|
||||
let stubs = vec![
|
||||
Stub {
|
||||
field_index: FieldId(1),
|
||||
index: stub_a_index.clone(),
|
||||
},
|
||||
Stub {
|
||||
field_index: stub_b_field_index,
|
||||
index: Idx::new(RangeSet::from([1..5, 8..11])),
|
||||
},
|
||||
];
|
||||
let stubs_index: Index<Stub> = stubs.clone().into();
|
||||
|
||||
assert_eq!(
|
||||
stubs_index.get_by_field_id(&stub_b_field_index),
|
||||
Some(&stubs[1])
|
||||
);
|
||||
assert_eq!(
|
||||
stubs_index.get_by_transcript_idx(&stub_a_index),
|
||||
Some(&stubs[0])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_failed_retrieval() {
|
||||
let stubs = stubs();
|
||||
let stubs_index: Index<Stub> = stubs.clone().into();
|
||||
|
||||
let wrong_index = Idx::new(RangeSet::from([0..3, 4..5]));
|
||||
let wrong_field_index = FieldId(200);
|
||||
|
||||
assert_eq!(stubs_index.get_by_field_id(&wrong_field_index), None);
|
||||
assert_eq!(stubs_index.get_by_transcript_idx(&wrong_index), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,3 +87,171 @@ impl Request {
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("inconsistent attestation: {0}")]
|
||||
pub struct InconsistentAttestation(String);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use crate::{
|
||||
attestation::{Attestation, AttestationConfig},
|
||||
connection::{HandshakeData, HandshakeDataV1_2, ServerCertOpening, TranscriptLength},
|
||||
fixtures::{encoder_seed, encoding_provider, ConnectionFixture},
|
||||
hash::{Blake3, Hash, HashAlgId},
|
||||
signing::SignatureAlgId,
|
||||
transcript::{encoding::EncodingTree, Transcript, TranscriptCommitConfigBuilder},
|
||||
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]
|
||||
fn test_success() {
|
||||
let (request, connection) = request_and_connection();
|
||||
|
||||
let attestation = attestation((request.clone(), connection));
|
||||
|
||||
assert!(request.validate(&attestation).is_ok())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrong_signature_alg() {
|
||||
let (mut request, connection) = request_and_connection();
|
||||
|
||||
let attestation = attestation((request.clone(), connection));
|
||||
|
||||
request.signature_alg = SignatureAlgId::SECP256R1;
|
||||
|
||||
let res = request.validate(&attestation);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrong_hash_alg() {
|
||||
let (mut request, connection) = request_and_connection();
|
||||
|
||||
let attestation = attestation((request.clone(), connection));
|
||||
|
||||
request.hash_alg = HashAlgId::SHA256;
|
||||
|
||||
let res = request.validate(&attestation);
|
||||
assert!(res.is_err())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrong_server_commitment() {
|
||||
let (mut request, connection) = request_and_connection();
|
||||
|
||||
let attestation = attestation((request.clone(), connection));
|
||||
|
||||
let ConnectionFixture {
|
||||
server_cert_data, ..
|
||||
} = ConnectionFixture::appliedzkp(TranscriptLength {
|
||||
sent: 100,
|
||||
received: 100,
|
||||
});
|
||||
let opening = ServerCertOpening::new(server_cert_data);
|
||||
|
||||
let crypto_provider = CryptoProvider::default();
|
||||
request.server_cert_commitment =
|
||||
opening.commit(crypto_provider.hash.get(&HashAlgId::BLAKE3).unwrap());
|
||||
|
||||
let res = request.validate(&attestation);
|
||||
assert!(res.is_err())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrong_encoding_commitment_root() {
|
||||
let (mut request, connection) = request_and_connection();
|
||||
|
||||
let attestation = attestation((request.clone(), connection));
|
||||
|
||||
request.encoding_commitment_root = Some(TypedHash {
|
||||
alg: HashAlgId::BLAKE3,
|
||||
value: Hash::default(),
|
||||
});
|
||||
|
||||
let res = request.validate(&attestation);
|
||||
assert!(res.is_err())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,3 +372,78 @@ mod secp256r1 {
|
||||
}
|
||||
|
||||
pub use secp256r1::{Secp256r1Signer, Secp256r1Verifier};
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rand_core::OsRng;
|
||||
use rstest::{fixture, rstest};
|
||||
|
||||
#[fixture]
|
||||
#[once]
|
||||
fn secp256k1_signer() -> Secp256k1Signer {
|
||||
let signing_key = k256::ecdsa::SigningKey::random(&mut OsRng);
|
||||
Secp256k1Signer::new(&signing_key.to_bytes()).unwrap()
|
||||
}
|
||||
|
||||
#[fixture]
|
||||
#[once]
|
||||
fn secp256r1_signer() -> Secp256r1Signer {
|
||||
let signing_key = p256::ecdsa::SigningKey::random(&mut OsRng);
|
||||
Secp256r1Signer::new(&signing_key.to_bytes()).unwrap()
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_secp256k1_success(secp256k1_signer: &Secp256k1Signer) {
|
||||
assert_eq!(secp256k1_signer.alg_id(), SignatureAlgId::SECP256K1);
|
||||
|
||||
let msg = "test payload";
|
||||
let signature = secp256k1_signer.sign(msg.as_bytes()).unwrap();
|
||||
let verifying_key = secp256k1_signer.verifying_key();
|
||||
|
||||
let verifier = Secp256k1Verifier {};
|
||||
assert_eq!(verifier.alg_id(), SignatureAlgId::SECP256K1);
|
||||
let result = verifier.verify(&verifying_key, msg.as_bytes(), &signature.data);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_secp256r1_success(secp256r1_signer: &Secp256r1Signer) {
|
||||
assert_eq!(secp256r1_signer.alg_id(), SignatureAlgId::SECP256R1);
|
||||
|
||||
let msg = "test payload";
|
||||
let signature = secp256r1_signer.sign(msg.as_bytes()).unwrap();
|
||||
let verifying_key = secp256r1_signer.verifying_key();
|
||||
|
||||
let verifier = Secp256r1Verifier {};
|
||||
assert_eq!(verifier.alg_id(), SignatureAlgId::SECP256R1);
|
||||
let result = verifier.verify(&verifying_key, msg.as_bytes(), &signature.data);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::wrong_signer(&secp256r1_signer(), false, false)]
|
||||
#[case::corrupted_signature(&secp256k1_signer(), true, false)]
|
||||
#[case::wrong_signature(&secp256k1_signer(), false, true)]
|
||||
fn test_failure(
|
||||
#[case] signer: &dyn Signer,
|
||||
#[case] corrupted_signature: bool,
|
||||
#[case] wrong_signature: bool,
|
||||
) {
|
||||
let msg = "test payload";
|
||||
let mut signature = signer.sign(msg.as_bytes()).unwrap();
|
||||
let verifying_key = signer.verifying_key();
|
||||
|
||||
if corrupted_signature {
|
||||
signature.data.push(0);
|
||||
}
|
||||
|
||||
if wrong_signature {
|
||||
signature = signer.sign("different payload".as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
let verifier = Secp256k1Verifier {};
|
||||
let result = verifier.verify(&verifying_key, msg.as_bytes(), &signature.data);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user