mirror of
https://github.com/tlsnotary/tlsn.git
synced 2026-01-12 08:08:29 -05:00
Compare commits
1 Commits
feat/plugi
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1801c30599 |
2
.github/workflows/releng.yml
vendored
2
.github/workflows/releng.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
tag:
|
||||
description: 'Tag to publish to NPM'
|
||||
required: true
|
||||
default: 'v0.1.0-alpha.14-pre'
|
||||
default: 'v0.1.0-alpha.13'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
|
||||
1
.github/workflows/updatemain.yml
vendored
1
.github/workflows/updatemain.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: Fast-forward main branch to published release tag
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
|
||||
404
Cargo.lock
generated
404
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,6 @@ members = [
|
||||
"crates/data-fixtures",
|
||||
"crates/examples",
|
||||
"crates/formats",
|
||||
"crates/plugin-core",
|
||||
"crates/server-fixture/certs",
|
||||
"crates/server-fixture/server",
|
||||
"crates/tls/backend",
|
||||
@@ -54,7 +53,6 @@ tlsn-formats = { path = "crates/formats" }
|
||||
tlsn-hmac-sha256 = { path = "crates/components/hmac-sha256" }
|
||||
tlsn-key-exchange = { path = "crates/components/key-exchange" }
|
||||
tlsn-mpc-tls = { path = "crates/mpc-tls" }
|
||||
tlsn-plugin-core = { path = "crates/plugin-core" }
|
||||
tlsn-server-fixture = { path = "crates/server-fixture/server" }
|
||||
tlsn-server-fixture-certs = { path = "crates/server-fixture/certs" }
|
||||
tlsn-tls-backend = { path = "crates/tls/backend" }
|
||||
@@ -85,10 +83,9 @@ mpz-ideal-vm = { git = "https://github.com/privacy-ethereum/mpz", tag = "v0.1.0-
|
||||
|
||||
rangeset = { version = "0.2" }
|
||||
serio = { version = "0.2" }
|
||||
spansy = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "304b910" }
|
||||
spansy = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "6168663" }
|
||||
uid-mux = { version = "0.2" }
|
||||
websocket-relay = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "304b910" }
|
||||
futures-plex = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "304b910" }
|
||||
websocket-relay = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "6168663" }
|
||||
|
||||
aead = { version = "0.4" }
|
||||
aes = { version = "0.8" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tlsn-attestation"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "This crate provides implementations of ciphers for two parties"
|
||||
keywords = ["tls", "mpc", "2pc", "aes"]
|
||||
categories = ["cryptography"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tlsn-deap"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "A 2PC implementation of TLS HMAC-SHA256 PRF"
|
||||
keywords = ["tls", "mpc", "2pc", "hmac", "sha256"]
|
||||
categories = ["cryptography"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "Implementation of the 3-party key-exchange protocol"
|
||||
keywords = ["tls", "mpc", "2pc", "pms", "key-exchange"]
|
||||
categories = ["cryptography"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "Core types for TLSNotary"
|
||||
keywords = ["tls", "mpc", "2pc", "types"]
|
||||
categories = ["cryptography"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -171,7 +171,43 @@ enum ProveConfigBuilderErrorRepr {
|
||||
},
|
||||
}
|
||||
|
||||
/// Configuration to verify information from the prover.
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct VerifyConfig {}
|
||||
|
||||
impl VerifyConfig {
|
||||
/// Creates a new builder.
|
||||
pub fn builder() -> VerifyConfigBuilder {
|
||||
VerifyConfigBuilder::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for [`VerifyConfig`].
|
||||
#[derive(Debug, Default)]
|
||||
pub struct VerifyConfigBuilder {}
|
||||
|
||||
impl VerifyConfigBuilder {
|
||||
/// Creates a new builder.
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
/// Builds the configuration.
|
||||
pub fn build(self) -> Result<VerifyConfig, VerifyConfigBuilderError> {
|
||||
Ok(VerifyConfig {})
|
||||
}
|
||||
}
|
||||
|
||||
/// Error for [`VerifyConfigBuilder`].
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error(transparent)]
|
||||
pub struct VerifyConfigBuilderError(#[from] VerifyConfigBuilderErrorRepr);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum VerifyConfigBuilderErrorRepr {}
|
||||
|
||||
/// Request to prove statements about the connection.
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ProveRequest {
|
||||
/// Handshake data.
|
||||
|
||||
@@ -7,7 +7,6 @@ use crate::{
|
||||
transcript::{Direction, Transcript},
|
||||
webpki::CertificateDer,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tls_core::msgs::{
|
||||
alert::AlertMessagePayload,
|
||||
codec::{Codec, Reader},
|
||||
@@ -16,7 +15,7 @@ use tls_core::msgs::{
|
||||
};
|
||||
|
||||
/// A transcript of TLS records sent and received by the prover.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TlsTranscript {
|
||||
time: u64,
|
||||
version: TlsVersion,
|
||||
@@ -292,7 +291,7 @@ impl TlsTranscript {
|
||||
}
|
||||
|
||||
/// A TLS record.
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone)]
|
||||
pub struct Record {
|
||||
/// Sequence number.
|
||||
pub seq: u64,
|
||||
|
||||
@@ -3,4 +3,4 @@ Cookie: very-secret-cookie
|
||||
Content-Length: 44
|
||||
Content-Type: application/json
|
||||
|
||||
{"foo": "bar", "bazz": 123, "buzz": [1,"5"]}
|
||||
{"foo": "bar", "bazz": 123, "buzz": [1,"5"]}
|
||||
|
||||
@@ -15,7 +15,6 @@ tlsn-server-fixture = { workspace = true }
|
||||
tlsn-server-fixture-certs = { workspace = true }
|
||||
spansy = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
bincode = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
@@ -38,9 +37,7 @@ tokio = { workspace = true, features = [
|
||||
tokio-util = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
noir = { git = "https://github.com/zkmopro/noir-rs", tag = "v1.0.0-beta.8", features = [
|
||||
"barretenberg",
|
||||
] }
|
||||
noir = { git = "https://github.com/zkmopro/noir-rs", tag = "v1.0.0-beta.8", features = ["barretenberg"] }
|
||||
|
||||
[[example]]
|
||||
name = "interactive"
|
||||
|
||||
@@ -22,11 +22,13 @@ use tlsn::{
|
||||
signing::Secp256k1Signer,
|
||||
Attestation, AttestationConfig, CryptoProvider, Secrets,
|
||||
},
|
||||
config::{CertificateDer, PrivateKeyDer, ProtocolConfig, RootCertStore},
|
||||
config::{
|
||||
CertificateDer, PrivateKeyDer, ProtocolConfig, ProtocolConfigValidator, RootCertStore,
|
||||
},
|
||||
connection::{ConnectionInfo, HandshakeData, ServerName, TranscriptLength},
|
||||
prover::{state::Committed, ProveConfig, Prover, ProverConfig, ProverOutput, TlsConfig},
|
||||
transcript::{ContentType, TranscriptCommitConfig},
|
||||
verifier::{Verifier, VerifierConfig, VerifierOutput},
|
||||
verifier::{Verifier, VerifierConfig, VerifierOutput, VerifyConfig},
|
||||
};
|
||||
use tlsn_examples::ExampleType;
|
||||
use tlsn_formats::http::{DefaultHttpCommitter, HttpCommit, HttpTranscript};
|
||||
@@ -301,6 +303,13 @@ async fn notary<S: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(
|
||||
request_rx: Receiver<AttestationRequest>,
|
||||
attestation_tx: Sender<Attestation>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Set up Verifier.
|
||||
let config_validator = ProtocolConfigValidator::builder()
|
||||
.max_sent_data(tlsn_examples::MAX_SENT_DATA)
|
||||
.max_recv_data(tlsn_examples::MAX_RECV_DATA)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Create a root certificate store with the server-fixture's self-signed
|
||||
// certificate. This is only required for offline testing with the
|
||||
// server-fixture.
|
||||
@@ -308,24 +317,21 @@ async fn notary<S: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(
|
||||
.root_store(RootCertStore {
|
||||
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||
})
|
||||
.protocol_config_validator(config_validator)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let verifier = Verifier::new(verifier_config)
|
||||
let mut verifier = Verifier::new(verifier_config)
|
||||
.setup(socket.compat())
|
||||
.await?
|
||||
.accept()
|
||||
.await?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
let (
|
||||
VerifierOutput {
|
||||
transcript_commitments,
|
||||
..
|
||||
},
|
||||
verifier,
|
||||
) = verifier.verify().await?.accept().await?;
|
||||
let VerifierOutput {
|
||||
transcript_commitments,
|
||||
encoder_secret,
|
||||
..
|
||||
} = verifier.verify(&VerifyConfig::default()).await?;
|
||||
|
||||
let tls_transcript = verifier.tls_transcript().clone();
|
||||
|
||||
@@ -382,6 +388,10 @@ async fn notary<S: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(
|
||||
.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.
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::{
|
||||
net::{IpAddr, SocketAddr},
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use http_body_util::Empty;
|
||||
use hyper::{body::Bytes, Request, StatusCode, Uri};
|
||||
use hyper_util::rt::TokioIo;
|
||||
@@ -12,11 +11,11 @@ use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt};
|
||||
use tracing::instrument;
|
||||
|
||||
use tlsn::{
|
||||
config::{CertificateDer, ProtocolConfig, RootCertStore},
|
||||
config::{CertificateDer, ProtocolConfig, ProtocolConfigValidator, RootCertStore},
|
||||
connection::ServerName,
|
||||
prover::{ProveConfig, Prover, ProverConfig, TlsConfig},
|
||||
transcript::PartialTranscript,
|
||||
verifier::{Verifier, VerifierConfig, VerifierOutput},
|
||||
verifier::{Verifier, VerifierConfig, VerifierOutput, VerifyConfig},
|
||||
};
|
||||
use tlsn_server_fixture::DEFAULT_FIXTURE_PORT;
|
||||
use tlsn_server_fixture_certs::{CA_CERT_DER, SERVER_DOMAIN};
|
||||
@@ -47,7 +46,7 @@ async fn main() {
|
||||
let (prover_socket, verifier_socket) = tokio::io::duplex(1 << 23);
|
||||
let prover = prover(prover_socket, &server_addr, &uri);
|
||||
let verifier = verifier(verifier_socket);
|
||||
let (_, transcript) = tokio::try_join!(prover, verifier).unwrap();
|
||||
let (_, transcript) = tokio::join!(prover, verifier);
|
||||
|
||||
println!("Successfully verified {}", &uri);
|
||||
println!(
|
||||
@@ -65,7 +64,7 @@ async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
verifier_socket: T,
|
||||
server_addr: &SocketAddr,
|
||||
uri: &str,
|
||||
) -> Result<()> {
|
||||
) {
|
||||
let uri = uri.parse::<Uri>().unwrap();
|
||||
assert_eq!(uri.scheme().unwrap().as_str(), "https");
|
||||
let server_domain = uri.authority().unwrap().host();
|
||||
@@ -99,13 +98,15 @@ async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
// Perform the setup phase with the verifier.
|
||||
let prover = Prover::new(prover_config)
|
||||
.setup(verifier_socket.compat())
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Connect to TLS Server.
|
||||
let tls_client_socket = tokio::net::TcpStream::connect(server_addr).await?;
|
||||
let tls_client_socket = tokio::net::TcpStream::connect(server_addr).await.unwrap();
|
||||
|
||||
// Pass server connection into the prover.
|
||||
let (mpc_tls_connection, prover_fut) = prover.connect(tls_client_socket.compat()).await?;
|
||||
let (mpc_tls_connection, prover_fut) =
|
||||
prover.connect(tls_client_socket.compat()).await.unwrap();
|
||||
|
||||
// Wrap the connection in a TokioIo compatibility layer to use it with hyper.
|
||||
let mpc_tls_connection = TokioIo::new(mpc_tls_connection.compat());
|
||||
@@ -115,7 +116,9 @@ async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
|
||||
// MPC-TLS Handshake.
|
||||
let (mut request_sender, connection) =
|
||||
hyper::client::conn::http1::handshake(mpc_tls_connection).await?;
|
||||
hyper::client::conn::http1::handshake(mpc_tls_connection)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Spawn the connection to run in the background.
|
||||
tokio::spawn(connection);
|
||||
@@ -127,13 +130,14 @@ async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
.header("Connection", "close")
|
||||
.header("Secret", SECRET)
|
||||
.method("GET")
|
||||
.body(Empty::<Bytes>::new())?;
|
||||
let response = request_sender.send_request(request).await?;
|
||||
.body(Empty::<Bytes>::new())
|
||||
.unwrap();
|
||||
let response = request_sender.send_request(request).await.unwrap();
|
||||
|
||||
assert!(response.status() == StatusCode::OK);
|
||||
|
||||
// Create proof for the Verifier.
|
||||
let mut prover = prover_task.await??;
|
||||
let mut prover = prover_task.await.unwrap().unwrap();
|
||||
|
||||
let mut builder = ProveConfig::builder(prover.transcript());
|
||||
|
||||
@@ -149,8 +153,10 @@ async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
.expect("the secret should be in the sent data");
|
||||
|
||||
// Reveal everything except for the secret.
|
||||
builder.reveal_sent(&(0..pos))?;
|
||||
builder.reveal_sent(&(pos + SECRET.len()..prover.transcript().sent().len()))?;
|
||||
builder.reveal_sent(&(0..pos)).unwrap();
|
||||
builder
|
||||
.reveal_sent(&(pos + SECRET.len()..prover.transcript().sent().len()))
|
||||
.unwrap();
|
||||
|
||||
// Find the substring "Dick".
|
||||
let pos = prover
|
||||
@@ -161,21 +167,28 @@ async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
.expect("the substring 'Dick' should be in the received data");
|
||||
|
||||
// Reveal everything except for the substring.
|
||||
builder.reveal_recv(&(0..pos))?;
|
||||
builder.reveal_recv(&(pos + 4..prover.transcript().received().len()))?;
|
||||
builder.reveal_recv(&(0..pos)).unwrap();
|
||||
builder
|
||||
.reveal_recv(&(pos + 4..prover.transcript().received().len()))
|
||||
.unwrap();
|
||||
|
||||
let config = builder.build()?;
|
||||
let config = builder.build().unwrap();
|
||||
|
||||
prover.prove(&config).await?;
|
||||
prover.close().await?;
|
||||
|
||||
Ok(())
|
||||
prover.prove(&config).await.unwrap();
|
||||
prover.close().await.unwrap();
|
||||
}
|
||||
|
||||
#[instrument(skip(socket))]
|
||||
async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(
|
||||
socket: T,
|
||||
) -> Result<PartialTranscript> {
|
||||
) -> PartialTranscript {
|
||||
// Set up Verifier.
|
||||
let config_validator = ProtocolConfigValidator::builder()
|
||||
.max_sent_data(MAX_SENT_DATA)
|
||||
.max_recv_data(MAX_RECV_DATA)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Create a root certificate store with the server-fixture's self-signed
|
||||
// certificate. This is only required for offline testing with the
|
||||
// server-fixture.
|
||||
@@ -183,51 +196,20 @@ async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(
|
||||
.root_store(RootCertStore {
|
||||
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||
})
|
||||
.build()?;
|
||||
.protocol_config_validator(config_validator)
|
||||
.build()
|
||||
.unwrap();
|
||||
let verifier = Verifier::new(verifier_config);
|
||||
|
||||
// Validate the proposed configuration and then run the TLS commitment protocol.
|
||||
let verifier = verifier.setup(socket.compat()).await?;
|
||||
|
||||
// This is the opportunity to ensure the prover does not attempt to overload the
|
||||
// verifier.
|
||||
let reject = if verifier.config().max_sent_data() > MAX_SENT_DATA {
|
||||
Some("max_sent_data is too large")
|
||||
} else if verifier.config().max_recv_data() > MAX_RECV_DATA {
|
||||
Some("max_recv_data is too large")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if reject.is_some() {
|
||||
verifier.reject(reject).await?;
|
||||
return Err(anyhow::anyhow!("protocol configuration rejected"));
|
||||
}
|
||||
|
||||
// Runs the TLS commitment protocol to completion.
|
||||
let verifier = verifier.accept().await?.run().await?;
|
||||
|
||||
// Validate the proving request and then verify.
|
||||
let verifier = verifier.verify().await?;
|
||||
|
||||
if verifier.request().handshake.is_none() {
|
||||
let verifier = verifier
|
||||
.reject(Some("expecting to verify the server name"))
|
||||
.await?;
|
||||
verifier.close().await?;
|
||||
return Err(anyhow::anyhow!("prover did not reveal the server name"));
|
||||
}
|
||||
|
||||
let (
|
||||
VerifierOutput {
|
||||
server_name,
|
||||
transcript,
|
||||
..
|
||||
},
|
||||
verifier,
|
||||
) = verifier.accept().await?;
|
||||
|
||||
verifier.close().await?;
|
||||
// Receive authenticated data.
|
||||
let VerifierOutput {
|
||||
server_name,
|
||||
transcript,
|
||||
..
|
||||
} = verifier
|
||||
.verify(socket.compat(), &VerifyConfig::default())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let server_name = server_name.expect("prover should have revealed server name");
|
||||
let transcript = transcript.expect("prover should have revealed transcript data");
|
||||
@@ -250,7 +232,7 @@ async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(
|
||||
let ServerName::Dns(server_name) = server_name;
|
||||
assert_eq!(server_name.as_str(), SERVER_DOMAIN);
|
||||
|
||||
Ok(transcript)
|
||||
transcript
|
||||
}
|
||||
|
||||
/// Render redacted bytes as `🙈`.
|
||||
|
||||
@@ -2,7 +2,6 @@ mod prover;
|
||||
mod types;
|
||||
mod verifier;
|
||||
|
||||
use anyhow::Result;
|
||||
use prover::prover;
|
||||
use std::{
|
||||
env,
|
||||
@@ -13,7 +12,7 @@ use tlsn_server_fixture_certs::SERVER_DOMAIN;
|
||||
use verifier::verifier;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let server_host: String = env::var("SERVER_HOST").unwrap_or("127.0.0.1".into());
|
||||
@@ -26,7 +25,7 @@ async fn main() -> Result<()> {
|
||||
let uri = format!("https://{SERVER_DOMAIN}:{server_port}/elster");
|
||||
let server_ip: IpAddr = server_host
|
||||
.parse()
|
||||
.map_err(|e| anyhow::anyhow!("Invalid IP address '{server_host}': {e}"))?;
|
||||
.map_err(|e| format!("Invalid IP address '{}': {}", server_host, e))?;
|
||||
let server_addr = SocketAddr::from((server_ip, server_port));
|
||||
|
||||
// Connect prover and verifier.
|
||||
|
||||
@@ -4,7 +4,6 @@ use crate::types::received_commitments;
|
||||
|
||||
use super::types::ZKProofBundle;
|
||||
|
||||
use anyhow::Result;
|
||||
use chrono::{Datelike, Local, NaiveDate};
|
||||
use http_body_util::Empty;
|
||||
use hyper::{body::Bytes, header, Request, StatusCode, Uri};
|
||||
@@ -49,17 +48,14 @@ pub async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
mut verifier_extra_socket: T,
|
||||
server_addr: &SocketAddr,
|
||||
uri: &str,
|
||||
) -> Result<()> {
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let uri = uri.parse::<Uri>()?;
|
||||
|
||||
if uri.scheme().map(|s| s.as_str()) != Some("https") {
|
||||
return Err(anyhow::anyhow!("URI must use HTTPS scheme"));
|
||||
return Err("URI must use HTTPS scheme".into());
|
||||
}
|
||||
|
||||
let server_domain = uri
|
||||
.authority()
|
||||
.ok_or_else(|| anyhow::anyhow!("URI must have authority"))?
|
||||
.host();
|
||||
let server_domain = uri.authority().ok_or("URI must have authority")?.host();
|
||||
|
||||
// Create a root certificate store with the server-fixture's self-signed
|
||||
// certificate. This is only required for offline testing with the
|
||||
@@ -122,10 +118,7 @@ pub async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
let response = request_sender.send_request(request).await?;
|
||||
|
||||
if response.status() != StatusCode::OK {
|
||||
return Err(anyhow::anyhow!(
|
||||
"MPC-TLS request failed with status {}",
|
||||
response.status()
|
||||
));
|
||||
return Err(format!("MPC-TLS request failed with status {}", response.status()).into());
|
||||
}
|
||||
|
||||
// Create proof for the Verifier.
|
||||
@@ -170,11 +163,11 @@ pub async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
let received_commitments = received_commitments(&prover_output.transcript_commitments);
|
||||
let received_commitment = received_commitments
|
||||
.first()
|
||||
.ok_or_else(|| anyhow::anyhow!("No received commitments found"))?; // committed hash (of date of birth string)
|
||||
.ok_or("No received commitments found")?; // committed hash (of date of birth string)
|
||||
let received_secrets = received_secrets(&prover_output.transcript_secrets);
|
||||
let received_secret = received_secrets
|
||||
.first()
|
||||
.ok_or_else(|| anyhow::anyhow!("No received secrets found"))?; // hash blinder
|
||||
.ok_or("No received secrets found")?; // hash blinder
|
||||
let proof_input = prepare_zk_proof_input(received, received_commitment, received_secret)?;
|
||||
let proof_bundle = generate_zk_proof(&proof_input)?;
|
||||
|
||||
@@ -187,30 +180,28 @@ pub async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
}
|
||||
|
||||
// Reveal everything from the request, except for the authorization token.
|
||||
fn reveal_request(request: &[u8], builder: &mut ProveConfigBuilder<'_>) -> Result<()> {
|
||||
fn reveal_request(
|
||||
request: &[u8],
|
||||
builder: &mut ProveConfigBuilder<'_>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let reqs = Requests::new_from_slice(request).collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let req = reqs
|
||||
.first()
|
||||
.ok_or_else(|| anyhow::anyhow!("No requests found"))?;
|
||||
let req = reqs.first().ok_or("No requests found")?;
|
||||
|
||||
if req.request.method.as_str() != "GET" {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Expected GET method, found {}",
|
||||
req.request.method.as_str()
|
||||
));
|
||||
return Err(format!("Expected GET method, found {}", req.request.method.as_str()).into());
|
||||
}
|
||||
|
||||
let authorization_header = req
|
||||
.headers_with_name(header::AUTHORIZATION.as_str())
|
||||
.next()
|
||||
.ok_or_else(|| anyhow::anyhow!("Authorization header not found"))?;
|
||||
.ok_or("Authorization header not found")?;
|
||||
|
||||
let start_pos = authorization_header
|
||||
.span()
|
||||
.indices()
|
||||
.min()
|
||||
.ok_or_else(|| anyhow::anyhow!("Could not find authorization header start position"))?
|
||||
.ok_or("Could not find authorization header start position")?
|
||||
+ header::AUTHORIZATION.as_str().len()
|
||||
+ 2;
|
||||
let end_pos =
|
||||
@@ -226,43 +217,38 @@ fn reveal_received(
|
||||
received: &[u8],
|
||||
builder: &mut ProveConfigBuilder<'_>,
|
||||
transcript_commitment_builder: &mut TranscriptCommitConfigBuilder,
|
||||
) -> Result<()> {
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let resp = Responses::new_from_slice(received).collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let response = resp
|
||||
.first()
|
||||
.ok_or_else(|| anyhow::anyhow!("No responses found"))?;
|
||||
let body = response
|
||||
.body
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("Response body not found"))?;
|
||||
let response = resp.first().ok_or("No responses found")?;
|
||||
let body = response.body.as_ref().ok_or("Response body not found")?;
|
||||
|
||||
let BodyContent::Json(json) = &body.content else {
|
||||
return Err(anyhow::anyhow!("Expected JSON body content"));
|
||||
return Err("Expected JSON body content".into());
|
||||
};
|
||||
|
||||
// reveal tax year
|
||||
let tax_year = json
|
||||
.get("tax_year")
|
||||
.ok_or_else(|| anyhow::anyhow!("tax_year field not found in JSON"))?;
|
||||
.ok_or("tax_year field not found in JSON")?;
|
||||
let start_pos = tax_year
|
||||
.span()
|
||||
.indices()
|
||||
.min()
|
||||
.ok_or_else(|| anyhow::anyhow!("Could not find tax_year start position"))?
|
||||
.ok_or("Could not find tax_year start position")?
|
||||
- 11;
|
||||
let end_pos = tax_year
|
||||
.span()
|
||||
.indices()
|
||||
.max()
|
||||
.ok_or_else(|| anyhow::anyhow!("Could not find tax_year end position"))?
|
||||
.ok_or("Could not find tax_year end position")?
|
||||
+ 1;
|
||||
builder.reveal_recv(&(start_pos..end_pos))?;
|
||||
|
||||
// commit to hash of date of birth
|
||||
let dob = json
|
||||
.get("taxpayer.date_of_birth")
|
||||
.ok_or_else(|| anyhow::anyhow!("taxpayer.date_of_birth field not found in JSON"))?;
|
||||
.ok_or("taxpayer.date_of_birth field not found in JSON")?;
|
||||
|
||||
transcript_commitment_builder.commit_recv(dob.span())?;
|
||||
|
||||
@@ -293,7 +279,7 @@ fn prepare_zk_proof_input(
|
||||
received: &[u8],
|
||||
received_commitment: &PlaintextHash,
|
||||
received_secret: &PlaintextHashSecret,
|
||||
) -> Result<ZKProofInput> {
|
||||
) -> Result<ZKProofInput, Box<dyn std::error::Error>> {
|
||||
assert_eq!(received_commitment.direction, Direction::Received);
|
||||
assert_eq!(received_commitment.hash.alg, HashAlgId::SHA256);
|
||||
|
||||
@@ -302,11 +288,11 @@ fn prepare_zk_proof_input(
|
||||
let dob_start = received_commitment
|
||||
.idx
|
||||
.min()
|
||||
.ok_or_else(|| anyhow::anyhow!("No start index for DOB"))?;
|
||||
.ok_or("No start index for DOB")?;
|
||||
let dob_end = received_commitment
|
||||
.idx
|
||||
.end()
|
||||
.ok_or_else(|| anyhow::anyhow!("No end index for DOB"))?;
|
||||
.ok_or("No end index for DOB")?;
|
||||
let dob = received[dob_start..dob_end].to_vec();
|
||||
let blinder = received_secret.blinder.as_bytes().to_vec();
|
||||
let committed_hash = hash.value.as_bytes().to_vec();
|
||||
@@ -321,9 +307,7 @@ fn prepare_zk_proof_input(
|
||||
let computed_hash = hasher.finalize();
|
||||
|
||||
if committed_hash != computed_hash.as_slice() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Computed hash does not match committed hash"
|
||||
));
|
||||
return Err("Computed hash does not match committed hash".into());
|
||||
}
|
||||
|
||||
Ok(ZKProofInput {
|
||||
@@ -334,7 +318,9 @@ fn prepare_zk_proof_input(
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_zk_proof(proof_input: &ZKProofInput) -> Result<ZKProofBundle> {
|
||||
fn generate_zk_proof(
|
||||
proof_input: &ZKProofInput,
|
||||
) -> Result<ZKProofBundle, Box<dyn std::error::Error>> {
|
||||
tracing::info!("🔒 Generating ZK proof with Noir...");
|
||||
|
||||
const PROGRAM_JSON: &str = include_str!("./noir/target/noir.json");
|
||||
@@ -343,7 +329,7 @@ fn generate_zk_proof(proof_input: &ZKProofInput) -> Result<ZKProofBundle> {
|
||||
let json: Value = serde_json::from_str(PROGRAM_JSON)?;
|
||||
let bytecode = json["bytecode"]
|
||||
.as_str()
|
||||
.ok_or_else(|| anyhow::anyhow!("bytecode field not found in program.json"))?;
|
||||
.ok_or("bytecode field not found in program.json")?;
|
||||
|
||||
let mut inputs: Vec<String> = vec![];
|
||||
inputs.push(proof_input.proof_date.day().to_string());
|
||||
@@ -368,17 +354,16 @@ fn generate_zk_proof(proof_input: &ZKProofInput) -> Result<ZKProofBundle> {
|
||||
tracing::debug!("Witness inputs {:?}", inputs);
|
||||
|
||||
let input_refs: Vec<&str> = inputs.iter().map(String::as_str).collect();
|
||||
let witness = from_vec_str_to_witness_map(input_refs).map_err(|e| anyhow::anyhow!(e))?;
|
||||
let witness = from_vec_str_to_witness_map(input_refs)?;
|
||||
|
||||
// Setup SRS
|
||||
setup_srs_from_bytecode(bytecode, None, false).map_err(|e| anyhow::anyhow!(e))?;
|
||||
setup_srs_from_bytecode(bytecode, None, false)?;
|
||||
|
||||
// Verification key
|
||||
let vk = get_ultra_honk_verification_key(bytecode, false).map_err(|e| anyhow::anyhow!(e))?;
|
||||
let vk = get_ultra_honk_verification_key(bytecode, false)?;
|
||||
|
||||
// Generate proof
|
||||
let proof = prove_ultra_honk(bytecode, witness.clone(), vk.clone(), false)
|
||||
.map_err(|e| anyhow::anyhow!(e))?;
|
||||
let proof = prove_ultra_honk(bytecode, witness.clone(), vk.clone(), false)?;
|
||||
tracing::info!("✅ Proof generated ({} bytes)", proof.len());
|
||||
|
||||
let proof_bundle = ZKProofBundle { vk, proof };
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
use crate::types::received_commitments;
|
||||
|
||||
use super::types::ZKProofBundle;
|
||||
use anyhow::Result;
|
||||
use chrono::{Local, NaiveDate};
|
||||
use noir::barretenberg::verify::{get_ultra_honk_verification_key, verify_ultra_honk};
|
||||
use serde_json::Value;
|
||||
use tls_server_fixture::CA_CERT_DER;
|
||||
use tlsn::{
|
||||
config::{CertificateDer, RootCertStore},
|
||||
config::{CertificateDer, ProtocolConfigValidator, RootCertStore},
|
||||
connection::ServerName,
|
||||
hash::HashAlgId,
|
||||
transcript::{Direction, PartialTranscript},
|
||||
verifier::{Verifier, VerifierConfig, VerifierOutput},
|
||||
verifier::{Verifier, VerifierConfig, VerifierOutput, VerifyConfig},
|
||||
};
|
||||
use tlsn_examples::{MAX_RECV_DATA, MAX_SENT_DATA};
|
||||
use tlsn_server_fixture_certs::SERVER_DOMAIN;
|
||||
@@ -23,7 +22,13 @@ use tracing::instrument;
|
||||
pub async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(
|
||||
socket: T,
|
||||
mut extra_socket: T,
|
||||
) -> Result<PartialTranscript> {
|
||||
) -> Result<PartialTranscript, Box<dyn std::error::Error>> {
|
||||
// Set up Verifier.
|
||||
let config_validator = ProtocolConfigValidator::builder()
|
||||
.max_sent_data(MAX_SENT_DATA)
|
||||
.max_recv_data(MAX_RECV_DATA)
|
||||
.build()?;
|
||||
|
||||
// Create a root certificate store with the server-fixture's self-signed
|
||||
// certificate. This is only required for offline testing with the
|
||||
// server-fixture.
|
||||
@@ -31,78 +36,42 @@ pub async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>
|
||||
.root_store(RootCertStore {
|
||||
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||
})
|
||||
.protocol_config_validator(config_validator)
|
||||
.build()?;
|
||||
|
||||
let verifier = Verifier::new(verifier_config);
|
||||
|
||||
// Validate the proposed configuration and then run the TLS commitment protocol.
|
||||
let verifier = verifier.setup(socket.compat()).await?;
|
||||
// Receive authenticated data.
|
||||
let VerifierOutput {
|
||||
server_name,
|
||||
transcript,
|
||||
transcript_commitments,
|
||||
..
|
||||
} = verifier
|
||||
.verify(socket.compat(), &VerifyConfig::default())
|
||||
.await?;
|
||||
|
||||
// This is the opportunity to ensure the prover does not attempt to overload the
|
||||
// verifier.
|
||||
let reject = if verifier.config().max_sent_data() > MAX_SENT_DATA {
|
||||
Some("max_sent_data is too large")
|
||||
} else if verifier.config().max_recv_data() > MAX_RECV_DATA {
|
||||
Some("max_recv_data is too large")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if reject.is_some() {
|
||||
verifier.reject(reject).await?;
|
||||
return Err(anyhow::anyhow!("protocol configuration rejected"));
|
||||
}
|
||||
|
||||
// Runs the TLS commitment protocol to completion.
|
||||
let verifier = verifier.accept().await?.run().await?;
|
||||
|
||||
// Validate the proving request and then verify.
|
||||
let verifier = verifier.verify().await?;
|
||||
let request = verifier.request();
|
||||
|
||||
if request.handshake.is_none() || request.transcript.is_none() {
|
||||
let verifier = verifier
|
||||
.reject(Some(
|
||||
"expecting to verify the server name and transcript data",
|
||||
))
|
||||
.await?;
|
||||
verifier.close().await?;
|
||||
return Err(anyhow::anyhow!(
|
||||
"prover did not reveal the server name and transcript data"
|
||||
));
|
||||
}
|
||||
|
||||
let (
|
||||
VerifierOutput {
|
||||
server_name,
|
||||
transcript,
|
||||
transcript_commitments,
|
||||
..
|
||||
},
|
||||
verifier,
|
||||
) = verifier.accept().await?;
|
||||
|
||||
verifier.close().await?;
|
||||
|
||||
let server_name = server_name.expect("server name should be present");
|
||||
let transcript = transcript.expect("transcript should be present");
|
||||
let server_name = server_name.ok_or("Prover should have revealed server name")?;
|
||||
let transcript = transcript.ok_or("Prover should have revealed transcript data")?;
|
||||
|
||||
// Create hash commitment for the date of birth field from the response
|
||||
let sent = transcript.sent_unsafe().to_vec();
|
||||
let sent_data = String::from_utf8(sent.clone())
|
||||
.map_err(|e| anyhow::anyhow!("Verifier expected valid UTF-8 sent data: {e}"))?;
|
||||
.map_err(|e| format!("Verifier expected valid UTF-8 sent data: {}", e))?;
|
||||
|
||||
if !sent_data.contains(SERVER_DOMAIN) {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Verification failed: Expected host {SERVER_DOMAIN} not found in sent data"
|
||||
));
|
||||
return Err(format!(
|
||||
"Verification failed: Expected host {} not found in sent data",
|
||||
SERVER_DOMAIN
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
// Check received data.
|
||||
let received_commitments = received_commitments(&transcript_commitments);
|
||||
let received_commitment = received_commitments
|
||||
.first()
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing hash commitment"))?;
|
||||
.ok_or("Missing received hash commitment")?;
|
||||
|
||||
assert!(received_commitment.direction == Direction::Received);
|
||||
assert!(received_commitment.hash.alg == HashAlgId::SHA256);
|
||||
@@ -112,10 +81,12 @@ pub async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>
|
||||
// Check Session info: server name.
|
||||
let ServerName::Dns(server_name) = server_name;
|
||||
if server_name.as_str() != SERVER_DOMAIN {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Server name mismatch: expected {SERVER_DOMAIN}, got {}",
|
||||
return Err(format!(
|
||||
"Server name mismatch: expected {}, got {}",
|
||||
SERVER_DOMAIN,
|
||||
server_name.as_str()
|
||||
));
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
// Receive ZKProof information from prover
|
||||
@@ -123,28 +94,26 @@ pub async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>
|
||||
extra_socket.read_to_end(&mut buf).await?;
|
||||
|
||||
if buf.is_empty() {
|
||||
return Err(anyhow::anyhow!("No ZK proof data received from prover"));
|
||||
return Err("No ZK proof data received from prover".into());
|
||||
}
|
||||
|
||||
let msg: ZKProofBundle = bincode::deserialize(&buf)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to deserialize ZK proof bundle: {e}"))?;
|
||||
.map_err(|e| format!("Failed to deserialize ZK proof bundle: {}", e))?;
|
||||
|
||||
// Verify zk proof
|
||||
const PROGRAM_JSON: &str = include_str!("./noir/target/noir.json");
|
||||
let json: Value = serde_json::from_str(PROGRAM_JSON)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to parse Noir circuit: {e}"))?;
|
||||
.map_err(|e| format!("Failed to parse Noir circuit: {}", e))?;
|
||||
|
||||
let bytecode = json["bytecode"]
|
||||
.as_str()
|
||||
.ok_or_else(|| anyhow::anyhow!("Bytecode field missing in noir.json"))?;
|
||||
.ok_or("Bytecode field missing in noir.json")?;
|
||||
|
||||
let vk = get_ultra_honk_verification_key(bytecode, false)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to get verification key: {e}"))?;
|
||||
.map_err(|e| format!("Failed to get verification key: {}", e))?;
|
||||
|
||||
if vk != msg.vk {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Verification key mismatch between computed and provided by prover"
|
||||
));
|
||||
return Err("Verification key mismatch between computed and provided by prover".into());
|
||||
}
|
||||
|
||||
let proof = msg.proof.clone();
|
||||
@@ -156,10 +125,12 @@ pub async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>
|
||||
// * and 32*32 bytes for the hash
|
||||
let min_bytes = (32 + 3) * 32;
|
||||
if proof.len() < min_bytes {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Proof too short: expected at least {min_bytes} bytes, got {}",
|
||||
return Err(format!(
|
||||
"Proof too short: expected at least {} bytes, got {}",
|
||||
min_bytes,
|
||||
proof.len()
|
||||
));
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
// Check that the proof date is correctly included in the proof
|
||||
@@ -168,12 +139,14 @@ pub async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>
|
||||
let proof_date_year: i32 = i32::from_be_bytes(proof[92..96].try_into()?);
|
||||
let proof_date_from_proof =
|
||||
NaiveDate::from_ymd_opt(proof_date_year, proof_date_month, proof_date_day)
|
||||
.ok_or_else(|| anyhow::anyhow!("Invalid proof date in proof"))?;
|
||||
.ok_or("Invalid proof date in proof")?;
|
||||
let today = Local::now().date_naive();
|
||||
if (today - proof_date_from_proof).num_days() < 0 {
|
||||
return Err(anyhow::anyhow!(
|
||||
"The proof date can only be today or in the past: provided {proof_date_from_proof}, today {today}"
|
||||
));
|
||||
return Err(format!(
|
||||
"The proof date can only be today or in the past: provided {}, today {}",
|
||||
proof_date_from_proof, today
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
// Check that the committed hash in the proof matches the hash from the
|
||||
@@ -191,9 +164,7 @@ pub async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>
|
||||
hex::encode(&committed_hash_in_proof),
|
||||
hex::encode(&expected_hash)
|
||||
);
|
||||
return Err(anyhow::anyhow!(
|
||||
"Hash in proof does not match committed hash in MPC-TLS"
|
||||
));
|
||||
return Err("Hash in proof does not match committed hash in MPC-TLS".into());
|
||||
}
|
||||
tracing::info!(
|
||||
"✅ The hash in the proof matches the committed hash in MPC-TLS ({})",
|
||||
@@ -202,10 +173,10 @@ pub async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>
|
||||
|
||||
// Finally verify the proof
|
||||
let is_valid = verify_ultra_honk(msg.proof, msg.vk)
|
||||
.map_err(|e| anyhow::anyhow!("ZKProof Verification failed: {e}"))?;
|
||||
.map_err(|e| format!("ZKProof Verification failed: {}", e))?;
|
||||
if !is_valid {
|
||||
tracing::error!("❌ Age verification ZKProof failed to verify");
|
||||
return Err(anyhow::anyhow!("Age verification ZKProof failed to verify"));
|
||||
return Err("Age verification ZKProof failed to verify".into());
|
||||
}
|
||||
tracing::info!("✅ Age verification ZKProof successfully verified");
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tlsn-formats"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -2,30 +2,33 @@ use anyhow::Result;
|
||||
|
||||
use harness_core::bench::Bench;
|
||||
use tlsn::{
|
||||
config::{CertificateDer, RootCertStore},
|
||||
verifier::{Verifier, VerifierConfig},
|
||||
config::{CertificateDer, ProtocolConfigValidator, RootCertStore},
|
||||
verifier::{Verifier, VerifierConfig, VerifyConfig},
|
||||
};
|
||||
use tlsn_server_fixture_certs::CA_CERT_DER;
|
||||
|
||||
use crate::IoProvider;
|
||||
use crate::{IoProvider, bench::RECV_PADDING};
|
||||
|
||||
pub async fn bench_verifier(provider: &IoProvider, config: &Bench) -> Result<()> {
|
||||
let mut builder = ProtocolConfigValidator::builder();
|
||||
builder
|
||||
.max_sent_data(config.upload_size)
|
||||
.max_recv_data(config.download_size + RECV_PADDING);
|
||||
|
||||
let protocol_config = builder.build()?;
|
||||
|
||||
pub async fn bench_verifier(provider: &IoProvider, _config: &Bench) -> Result<()> {
|
||||
let verifier = Verifier::new(
|
||||
VerifierConfig::builder()
|
||||
.root_store(RootCertStore {
|
||||
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||
})
|
||||
.protocol_config_validator(protocol_config)
|
||||
.build()?,
|
||||
);
|
||||
|
||||
let verifier = verifier
|
||||
.setup(provider.provide_proto_io().await?)
|
||||
.await?
|
||||
.accept()
|
||||
.await?
|
||||
.run()
|
||||
.await?;
|
||||
let (_, verifier) = verifier.verify().await?.accept().await?;
|
||||
let verifier = verifier.setup(provider.provide_proto_io().await?).await?;
|
||||
let mut verifier = verifier.run().await?;
|
||||
verifier.verify(&VerifyConfig::default()).await?;
|
||||
verifier.close().await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use tlsn::{
|
||||
config::{CertificateDer, ProtocolConfig, RootCertStore},
|
||||
config::{CertificateDer, ProtocolConfig, ProtocolConfigValidator, RootCertStore},
|
||||
connection::ServerName,
|
||||
hash::HashAlgId,
|
||||
prover::{ProveConfig, Prover, ProverConfig, TlsConfig},
|
||||
transcript::{TranscriptCommitConfig, TranscriptCommitment, TranscriptCommitmentKind},
|
||||
verifier::{Verifier, VerifierConfig, VerifierOutput},
|
||||
verifier::{Verifier, VerifierConfig, VerifierOutput, VerifyConfig},
|
||||
};
|
||||
use tlsn_server_fixture_certs::{CA_CERT_DER, SERVER_DOMAIN};
|
||||
|
||||
@@ -113,34 +113,33 @@ async fn prover(provider: &IoProvider) {
|
||||
|
||||
async fn verifier(provider: &IoProvider) {
|
||||
let config = VerifierConfig::builder()
|
||||
.protocol_config_validator(
|
||||
ProtocolConfigValidator::builder()
|
||||
.max_sent_data(MAX_SENT_DATA)
|
||||
.max_recv_data(MAX_RECV_DATA)
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
.root_store(RootCertStore {
|
||||
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||
})
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let verifier = Verifier::new(config)
|
||||
.setup(provider.provide_proto_io().await.unwrap())
|
||||
.await
|
||||
.unwrap()
|
||||
.accept()
|
||||
.await
|
||||
.unwrap()
|
||||
.run()
|
||||
let verifier = Verifier::new(config);
|
||||
|
||||
let VerifierOutput {
|
||||
server_name,
|
||||
transcript_commitments,
|
||||
..
|
||||
} = verifier
|
||||
.verify(
|
||||
provider.provide_proto_io().await.unwrap(),
|
||||
&VerifyConfig::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let (
|
||||
VerifierOutput {
|
||||
server_name,
|
||||
transcript_commitments,
|
||||
..
|
||||
},
|
||||
verifier,
|
||||
) = verifier.verify().await.unwrap().accept().await.unwrap();
|
||||
|
||||
verifier.close().await.unwrap();
|
||||
|
||||
let ServerName::Dns(server_name) = server_name.unwrap();
|
||||
|
||||
assert_eq!(server_name.as_str(), SERVER_DOMAIN);
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "TLSNotary MPC-TLS protocol"
|
||||
keywords = ["tls", "mpc", "2pc"]
|
||||
categories = ["cryptography"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
[package]
|
||||
name = "tlsn-plugin-core"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
tlsn = { workspace = true }
|
||||
tlsn-core = { workspace = true }
|
||||
tlsn-formats = { workspace = true }
|
||||
|
||||
http-body-util = { workspace = true }
|
||||
hyper = { workspace = true, features = ["client", "http1"] }
|
||||
rangeset = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
spansy = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tlsn-data-fixtures = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1,105 +0,0 @@
|
||||
//! Core types of the prover and verifier plugin.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tlsn_core::{
|
||||
hash::HashAlgId,
|
||||
transcript::{Direction, TranscriptCommitmentKind},
|
||||
};
|
||||
|
||||
mod prover;
|
||||
mod verifier;
|
||||
|
||||
pub use prover::{
|
||||
Config as ProverPluginConfig, ConfigError as ProverPLuginConfigError,
|
||||
Output as ProverPluginOutput,
|
||||
};
|
||||
pub use verifier::{
|
||||
Config as VerifierPluginConfig, ConfigError as VerifierPluginConfigError,
|
||||
Output as VerifierPluginOutput,
|
||||
};
|
||||
|
||||
/// A rule for disclosing HTTP data.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DisclosureRule {
|
||||
http: HttpHandle,
|
||||
policy: DisclosurePolicy,
|
||||
}
|
||||
|
||||
/// Handle for a part of an HTTP message.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HttpHandle {
|
||||
typ: MessageType,
|
||||
part: MessagePart,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum MessageType {
|
||||
Request,
|
||||
Response,
|
||||
}
|
||||
|
||||
impl From<&MessageType> for Direction {
|
||||
fn from(mt: &MessageType) -> Self {
|
||||
match mt {
|
||||
MessageType::Request => Direction::Sent,
|
||||
MessageType::Response => Direction::Received,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Disclosure policy.
|
||||
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum DisclosurePolicy {
|
||||
/// Reveals data.
|
||||
Reveal,
|
||||
/// Creates a hiding commitment.
|
||||
Commit(Alg),
|
||||
}
|
||||
|
||||
/// Commitment algorithm.
|
||||
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum Alg {
|
||||
EncodingSha256,
|
||||
EncodingBlake3,
|
||||
EncodingKeccak256,
|
||||
Sha256,
|
||||
Blake3,
|
||||
}
|
||||
|
||||
impl From<&Alg> for TranscriptCommitmentKind {
|
||||
fn from(alg: &Alg) -> Self {
|
||||
match alg {
|
||||
Alg::EncodingSha256 | Alg::EncodingBlake3 | Alg::EncodingKeccak256 => {
|
||||
TranscriptCommitmentKind::Encoding
|
||||
}
|
||||
Alg::Sha256 => TranscriptCommitmentKind::Hash {
|
||||
alg: HashAlgId::SHA256,
|
||||
},
|
||||
Alg::Blake3 => TranscriptCommitmentKind::Hash {
|
||||
alg: HashAlgId::BLAKE3,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The part of an HTTP message.
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum MessagePart {
|
||||
All,
|
||||
StartLine,
|
||||
Header(HeaderParams),
|
||||
Body(BodyParams),
|
||||
}
|
||||
|
||||
/// Parameters for an HTTP header.
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct HeaderParams {
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
/// Parameters for a part of an HTTP body.
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum BodyParams {
|
||||
JsonPath(String),
|
||||
XPath(String),
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
//! Core types of the prover plugin.
|
||||
|
||||
use crate::HttpHandle;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tlsn_core::ProverOutput;
|
||||
|
||||
mod config;
|
||||
|
||||
pub use config::{Config, ConfigError};
|
||||
|
||||
/// Output of the prover plugin.
|
||||
#[allow(dead_code)]
|
||||
pub struct Output {
|
||||
output: ProverOutput,
|
||||
/// Plaintext exposed to the host.
|
||||
plaintext: Vec<(HttpHandle, Vec<u8>)>,
|
||||
}
|
||||
|
||||
/// Params for protocol prover.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ProverParams {
|
||||
max_recv_data: usize,
|
||||
max_sent_data: usize,
|
||||
prove_server_identity: bool,
|
||||
pub server_dns: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct HttpRequest {
|
||||
url: String,
|
||||
method: String,
|
||||
body: Option<Vec<u8>>,
|
||||
pub headers: Vec<(String, String)>,
|
||||
}
|
||||
@@ -1,463 +0,0 @@
|
||||
use crate::{
|
||||
BodyParams, DisclosurePolicy, DisclosureRule, HttpHandle, MessagePart, MessageType,
|
||||
prover::{HttpRequest, ProverParams},
|
||||
};
|
||||
|
||||
use crate::prover::Output;
|
||||
use http_body_util::Full;
|
||||
use hyper::{Request as HyperRequest, body::Bytes};
|
||||
use rangeset::RangeSet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tlsn::{
|
||||
config::ProtocolConfig,
|
||||
prover::{ProverConfig, TlsConfig},
|
||||
};
|
||||
use tlsn_core::{
|
||||
ProveConfig, ProveConfigBuilder, ProverOutput,
|
||||
connection::{DnsName, ServerName},
|
||||
transcript::{Transcript, TranscriptCommitConfig, TranscriptCommitConfigBuilder},
|
||||
webpki::RootCertStore,
|
||||
};
|
||||
use tlsn_formats::{
|
||||
http::{Body, Request, Requests, Response, Responses},
|
||||
json::JsonValue,
|
||||
spansy,
|
||||
spansy::Spanned,
|
||||
};
|
||||
|
||||
/// Prover plugin config.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
pub prover_params: ProverParams,
|
||||
pub request: HttpRequest,
|
||||
/// Data which will be disclosed to the verifier.
|
||||
pub disclose: Vec<DisclosureRule>,
|
||||
/// Data which will be exposed in the plugin output.
|
||||
pub expose: Vec<HttpHandle>,
|
||||
pub root_store: RootCertStore,
|
||||
pub verifier_endpoint: String,
|
||||
/// Proxy endpoint for connecting to the server.
|
||||
pub proxy_endpoint: Option<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Returns the verifier endpoint.
|
||||
pub fn prover_endpoint(&self) -> &String {
|
||||
&self.verifier_endpoint
|
||||
}
|
||||
|
||||
/// Builds and returns [ProverConfig].
|
||||
pub fn prover_config(&self) -> Result<ProverConfig, ConfigError> {
|
||||
let dns_name: DnsName = self
|
||||
.prover_params
|
||||
.server_dns
|
||||
.clone()
|
||||
.try_into()
|
||||
.map_err(|_| ConfigError("prover_config error".to_string()))?;
|
||||
|
||||
let mut builder = TlsConfig::builder();
|
||||
builder.root_store(self.root_store.clone());
|
||||
let tls_config = builder.build().unwrap();
|
||||
|
||||
let config = ProverConfig::builder()
|
||||
.server_name(ServerName::Dns(dns_name))
|
||||
.tls_config(tls_config)
|
||||
.protocol_config(
|
||||
ProtocolConfig::builder()
|
||||
.max_sent_data(self.prover_params.max_sent_data)
|
||||
.max_recv_data(self.prover_params.max_recv_data)
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Returns the HTTP request.
|
||||
pub fn http_request(&self) -> Result<HyperRequest<Full<Bytes>>, ConfigError> {
|
||||
let mut request = HyperRequest::builder()
|
||||
.uri(self.request.url.clone())
|
||||
.header("Host", self.prover_params.server_dns.clone());
|
||||
|
||||
for (k, v) in &self.request.headers {
|
||||
request = request.header(k, v);
|
||||
}
|
||||
|
||||
request = request.method(self.request.method.as_str());
|
||||
let body = match &self.request.body {
|
||||
Some(data) => Full::<Bytes>::from(data.clone()),
|
||||
None => Full::<Bytes>::from(vec![]),
|
||||
};
|
||||
|
||||
request
|
||||
.body(body)
|
||||
.map_err(|_| ConfigError("http_request error".to_string()))
|
||||
}
|
||||
|
||||
/// Creates a [ProveConfig] for the given `transcript`.
|
||||
pub fn prove_config(&self, transcript: &Transcript) -> Result<ProveConfig, ConfigError> {
|
||||
let mut prove_cfg = ProveConfig::builder(transcript);
|
||||
let mut commit_cfg = TranscriptCommitConfig::builder(transcript);
|
||||
|
||||
if self.prover_params.prove_server_identity {
|
||||
prove_cfg.server_identity();
|
||||
}
|
||||
|
||||
let reqs = Requests::new_from_slice(transcript.sent())
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| ConfigError("prove_config error".to_string()))?;
|
||||
let resps = Responses::new_from_slice(transcript.received())
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| ConfigError("prove_config error".to_string()))?;
|
||||
|
||||
let req = reqs.first().expect("at least one request");
|
||||
let resp = resps.first().expect("at least one response");
|
||||
|
||||
let req_rules = self
|
||||
.disclose
|
||||
.iter()
|
||||
.filter(|h| h.http.typ == MessageType::Request);
|
||||
let resp_rules = self
|
||||
.disclose
|
||||
.iter()
|
||||
.filter(|h| h.http.typ == MessageType::Response);
|
||||
|
||||
disclose_req(req, req_rules, &mut commit_cfg, &mut prove_cfg);
|
||||
disclose_resp(resp, resp_rules, &mut commit_cfg, &mut prove_cfg);
|
||||
|
||||
prove_cfg.transcript_commit(commit_cfg.build().unwrap());
|
||||
Ok(prove_cfg.build().unwrap())
|
||||
}
|
||||
|
||||
/// Returns the output of the plugin.
|
||||
pub fn output(
|
||||
&self,
|
||||
transcript: Transcript,
|
||||
prover_output: ProverOutput,
|
||||
) -> Result<Output, ConfigError> {
|
||||
let reqs = Requests::new_from_slice(transcript.sent())
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| ConfigError("output error".to_string()))?;
|
||||
let resps = Responses::new_from_slice(transcript.received())
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| ConfigError("output error".to_string()))?;
|
||||
|
||||
let req = reqs.first().expect("at least one request");
|
||||
let resp = resps.first().expect("at least one response");
|
||||
|
||||
let mut exposed = Vec::new();
|
||||
|
||||
// Extract the to-be-exposed data from the transcript.
|
||||
for h in self.expose.iter() {
|
||||
let range = if h.typ == MessageType::Request {
|
||||
req_part_range(req, h)
|
||||
} else {
|
||||
resp_part_range(resp, h)
|
||||
};
|
||||
|
||||
let seq = transcript
|
||||
.get((&h.typ).into(), &range)
|
||||
.ok_or(ConfigError("range not found in transcript".to_string()))?;
|
||||
|
||||
exposed.push((h.clone(), seq.data().to_vec()));
|
||||
}
|
||||
|
||||
Ok(Output {
|
||||
output: prover_output,
|
||||
plaintext: exposed,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("config error: {0}")]
|
||||
pub struct ConfigError(String);
|
||||
|
||||
/// Processes disclosure rules for the request.
|
||||
fn disclose_req<'a, I>(
|
||||
req: &Request,
|
||||
rules: I,
|
||||
commit_cfg: &mut TranscriptCommitConfigBuilder<'_>,
|
||||
prove_cfg: &mut ProveConfigBuilder<'_>,
|
||||
) where
|
||||
I: Iterator<Item = &'a DisclosureRule>,
|
||||
{
|
||||
for r in rules {
|
||||
let range = req_part_range(req, &r.http);
|
||||
|
||||
if range.is_empty() {
|
||||
// TODO: maybe return an error here when the part was not found.
|
||||
return;
|
||||
}
|
||||
|
||||
match &r.policy {
|
||||
DisclosurePolicy::Commit(alg) => {
|
||||
commit_cfg
|
||||
.commit_with_kind(&range, (&r.http.typ).into(), alg.into())
|
||||
.expect("range is in the transcript");
|
||||
}
|
||||
DisclosurePolicy::Reveal => {
|
||||
prove_cfg
|
||||
.reveal_sent(&range)
|
||||
.expect("range is in the transcript");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes disclosure rules for the response.
|
||||
fn disclose_resp<'a, I>(
|
||||
resp: &Response,
|
||||
rules: I,
|
||||
commit_cfg: &mut TranscriptCommitConfigBuilder<'_>,
|
||||
prove_cfg: &mut ProveConfigBuilder<'_>,
|
||||
) where
|
||||
I: Iterator<Item = &'a DisclosureRule>,
|
||||
{
|
||||
for r in rules {
|
||||
let range = resp_part_range(resp, &r.http);
|
||||
|
||||
if range.is_empty() {
|
||||
// TODO: maybe return an error here when the part was not found.
|
||||
return;
|
||||
}
|
||||
|
||||
match &r.policy {
|
||||
DisclosurePolicy::Commit(alg) => {
|
||||
commit_cfg
|
||||
.commit_with_kind(&range, (&r.http.typ).into(), alg.into())
|
||||
.expect("range is in the transcript");
|
||||
}
|
||||
DisclosurePolicy::Reveal => {
|
||||
prove_cfg
|
||||
.reveal_recv(&range)
|
||||
.expect("range is in the transcript");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the range for the given `part` of the HTTP request,
|
||||
fn req_part_range(req: &Request, part: &HttpHandle) -> RangeSet<usize> {
|
||||
match &part.part {
|
||||
MessagePart::All => {
|
||||
(req.span().indices().min().unwrap()..req.span().indices().end().unwrap()).into()
|
||||
}
|
||||
|
||||
MessagePart::StartLine => req.request.span().indices().clone(),
|
||||
|
||||
MessagePart::Header(params) => req
|
||||
.headers_with_name(params.key.as_str())
|
||||
.map(|h| h.span().indices())
|
||||
.fold(RangeSet::default(), |acc, r| acc | r),
|
||||
|
||||
MessagePart::Body(params) => match &req.body {
|
||||
Some(body) => {
|
||||
// Body offset from the start of an HTTP message.
|
||||
let body_offset = body
|
||||
.span()
|
||||
.indices()
|
||||
.min()
|
||||
.expect("body span cannot be empty");
|
||||
let mut range = body_params_range(body, params);
|
||||
range.shift_right(&body_offset);
|
||||
range
|
||||
}
|
||||
None => RangeSet::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the range for the given `part` of the HTTP response,
|
||||
fn resp_part_range(resp: &Response, part: &HttpHandle) -> RangeSet<usize> {
|
||||
match &part.part {
|
||||
MessagePart::All => {
|
||||
(resp.span().indices().min().unwrap()..resp.span().indices().end().unwrap()).into()
|
||||
}
|
||||
MessagePart::StartLine => resp.status.span().indices().clone(),
|
||||
MessagePart::Header(params) => resp
|
||||
.headers_with_name(params.key.as_str())
|
||||
.map(|h| h.span().indices())
|
||||
.fold(RangeSet::default(), |acc, r| acc | r),
|
||||
MessagePart::Body(params) => match &resp.body {
|
||||
Some(body) => {
|
||||
// Body offset from the start of an HTTP message.
|
||||
let body_offset = body.span().indices().min().expect("body cannot be empty");
|
||||
let mut range = body_params_range(body, params);
|
||||
range.shift_right(&body_offset);
|
||||
range
|
||||
}
|
||||
None => RangeSet::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the byte range of the `params` in the given `body`.
|
||||
fn body_params_range(body: &Body, params: &BodyParams) -> RangeSet<usize> {
|
||||
match params {
|
||||
BodyParams::JsonPath(path) => {
|
||||
// TODO: use a better approach than re-parsing the entire
|
||||
// json for each path.
|
||||
match spansy::json::parse(body.as_bytes().to_vec().into()) {
|
||||
Ok(json) => json_path_range(&json, path),
|
||||
Err(_) => RangeSet::default(),
|
||||
}
|
||||
}
|
||||
_ => unimplemented!("only json parsing is currently supported"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the byte range of the key–value pair corresponding to the given
|
||||
/// `path` in a JSON value `source`.
|
||||
///
|
||||
/// If the path points to an array element, only the range of the **value**
|
||||
/// of the element is returned.
|
||||
fn json_path_range(source: &JsonValue, path: &String) -> RangeSet<usize> {
|
||||
let val = match source.get(path) {
|
||||
Some(val) => val,
|
||||
None => return RangeSet::default(),
|
||||
};
|
||||
|
||||
let dot = ".";
|
||||
let last = path.split(dot).last().unwrap();
|
||||
// Whether `path` is a top-level key.
|
||||
let is_top_level = last == path;
|
||||
|
||||
if last.parse::<usize>().is_ok() {
|
||||
// The path points to an array element, so we only need the range of
|
||||
// the **value**.
|
||||
val.span().indices().clone()
|
||||
} else {
|
||||
let parent_val = if is_top_level {
|
||||
source
|
||||
} else {
|
||||
source
|
||||
.get(&path[..path.len() - last.len() - dot.len()])
|
||||
.expect("path is valid")
|
||||
};
|
||||
let JsonValue::Object(parent_obj) = parent_val else {
|
||||
unreachable!("parent value is always an object");
|
||||
};
|
||||
|
||||
// We need the range of the **key-value** pair.
|
||||
let kv = parent_obj
|
||||
.elems
|
||||
.iter()
|
||||
.find(|kv| kv.value == *val)
|
||||
.expect("element exists");
|
||||
|
||||
kv.without_separator()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::HeaderParams;
|
||||
|
||||
use super::*;
|
||||
use spansy::http::parse_response;
|
||||
use tlsn_data_fixtures::http::{request, response};
|
||||
use tlsn_formats::spansy::http::parse_request;
|
||||
|
||||
#[test]
|
||||
fn test_req_part_range() {
|
||||
let data = request::POST_JSON;
|
||||
let req = parse_request(data).unwrap();
|
||||
let s = std::str::from_utf8(data).unwrap();
|
||||
|
||||
//===============All
|
||||
let part = HttpHandle {
|
||||
part: MessagePart::All,
|
||||
typ: MessageType::Request,
|
||||
};
|
||||
let range = req_part_range(&req, &part);
|
||||
assert_eq!(range, 0..data.len());
|
||||
|
||||
//===============StartLine
|
||||
let part = HttpHandle {
|
||||
part: MessagePart::StartLine,
|
||||
typ: MessageType::Request,
|
||||
};
|
||||
let range = req_part_range(&req, &part);
|
||||
let end = s.find("\r\n").unwrap() + 2;
|
||||
assert_eq!(range, 0..end);
|
||||
|
||||
//===============Header
|
||||
let part = HttpHandle {
|
||||
part: MessagePart::Header(HeaderParams {
|
||||
key: "Content-Length".to_string(),
|
||||
}),
|
||||
typ: MessageType::Request,
|
||||
};
|
||||
let range = req_part_range(&req, &part);
|
||||
|
||||
let target: &'static str = "Content-Length: 44";
|
||||
let start = s.find(target).unwrap();
|
||||
let end = start + target.len() + 2;
|
||||
assert_eq!(range, start..end);
|
||||
|
||||
//===============Body
|
||||
let part = HttpHandle {
|
||||
part: MessagePart::Body(BodyParams::JsonPath("bazz".to_string())),
|
||||
typ: MessageType::Request,
|
||||
};
|
||||
let range = req_part_range(&req, &part);
|
||||
|
||||
let target: &'static str = "\"bazz\": 123";
|
||||
let start = s.find(target).unwrap();
|
||||
let end = start + target.len();
|
||||
assert_eq!(range, start..end);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resp_part_range() {
|
||||
let data = response::OK_JSON;
|
||||
let resp = parse_response(data).unwrap();
|
||||
let s = std::str::from_utf8(data).unwrap();
|
||||
|
||||
//===============All
|
||||
let part = HttpHandle {
|
||||
part: MessagePart::All,
|
||||
typ: MessageType::Response,
|
||||
};
|
||||
let range = resp_part_range(&resp, &part);
|
||||
assert_eq!(range, 0..data.len());
|
||||
|
||||
//===============StartLine
|
||||
let part = HttpHandle {
|
||||
part: MessagePart::StartLine,
|
||||
typ: MessageType::Response,
|
||||
};
|
||||
let range = resp_part_range(&resp, &part);
|
||||
let end = s.find("\r\n").unwrap() + 2;
|
||||
assert_eq!(range, 0..end);
|
||||
|
||||
//===============Header
|
||||
let part = HttpHandle {
|
||||
part: MessagePart::Header(HeaderParams {
|
||||
key: "Content-Length".to_string(),
|
||||
}),
|
||||
typ: MessageType::Response,
|
||||
};
|
||||
let range = resp_part_range(&resp, &part);
|
||||
|
||||
let target: &'static str = "Content-Length: 44";
|
||||
let start = s.find(target).unwrap();
|
||||
let end = start + target.len() + 2;
|
||||
assert_eq!(range, start..end);
|
||||
|
||||
//===============Body
|
||||
let part = HttpHandle {
|
||||
part: MessagePart::Body(BodyParams::JsonPath("bazz".to_string())),
|
||||
typ: MessageType::Request,
|
||||
};
|
||||
let range = resp_part_range(&resp, &part);
|
||||
|
||||
let target: &'static str = "\"bazz\": 123";
|
||||
let start = s.find(target).unwrap();
|
||||
let end = start + target.len();
|
||||
assert_eq!(range, start..end);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
//! Core types of the verifier plugin.
|
||||
|
||||
use tlsn_core::VerifierOutput;
|
||||
|
||||
mod config;
|
||||
|
||||
pub use config::{Config, ConfigError};
|
||||
|
||||
/// Output of the verifier plugin.
|
||||
#[allow(dead_code)]
|
||||
pub struct Output {
|
||||
output: VerifierOutput,
|
||||
}
|
||||
|
||||
/// Params for protocol verifier.
|
||||
pub struct VerifierParams {
|
||||
pub max_sent_data: usize,
|
||||
pub max_recv_data: usize,
|
||||
pub prover_endpoint: String,
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
use crate::{
|
||||
DisclosureRule,
|
||||
verifier::{Output, VerifierParams},
|
||||
};
|
||||
use tlsn::{
|
||||
config::{ProtocolConfig, RootCertStore},
|
||||
verifier::VerifierConfig,
|
||||
};
|
||||
use tlsn_core::VerifierOutput;
|
||||
|
||||
/// Verifier plugin config.
|
||||
#[allow(dead_code)]
|
||||
pub struct Config {
|
||||
pub verifier_params: VerifierParams,
|
||||
/// Data which the prover is expected to disclose.
|
||||
pub disclose: Vec<DisclosureRule>,
|
||||
pub root_store: RootCertStore,
|
||||
pub prover_endpoint: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Returns the prover endpoint.
|
||||
pub fn prover_endpoint(&self) -> &String {
|
||||
&self.verifier_params.prover_endpoint
|
||||
}
|
||||
|
||||
/// Builds and returns [VerifierConfig].
|
||||
pub fn verifier_config(&self) -> VerifierConfig {
|
||||
VerifierConfig::builder()
|
||||
.root_store(self.root_store.clone())
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Validates the given protocol `config`.
|
||||
pub fn validate_protocol_config(&self, config: &ProtocolConfig) -> Result<(), ConfigError> {
|
||||
if config.max_recv_data() > self.verifier_params.max_recv_data
|
||||
|| config.max_sent_data() > self.verifier_params.max_sent_data
|
||||
{
|
||||
Err(ConfigError(
|
||||
"failed to validate protocol config".to_string(),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns verifier plugin output.
|
||||
pub fn output(&self, output: VerifierOutput) -> Output {
|
||||
Output { output }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("config error: {0}")]
|
||||
pub struct ConfigError(String);
|
||||
@@ -5,7 +5,7 @@ description = "A TLS backend trait for TLSNotary"
|
||||
keywords = ["tls", "mpc", "2pc"]
|
||||
categories = ["cryptography"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "An async TLS client for TLSNotary"
|
||||
keywords = ["tls", "mpc", "2pc", "client", "async"]
|
||||
categories = ["cryptography"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "A TLS client for TLSNotary"
|
||||
keywords = ["tls", "mpc", "2pc", "client", "sync"]
|
||||
categories = ["cryptography"]
|
||||
license = "Apache-2.0 OR ISC OR MIT"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2021"
|
||||
autobenches = false
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ description = "Cryptographic operations for the TLSNotary TLS client"
|
||||
keywords = ["tls", "mpc", "2pc"]
|
||||
categories = ["cryptography"]
|
||||
license = "Apache-2.0 OR ISC OR MIT"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -4,7 +4,7 @@ authors = ["TLSNotary Team"]
|
||||
keywords = ["tls", "mpc", "2pc", "prover"]
|
||||
categories = ["cryptography"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2024"
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
//! TLSNotary protocol config and config utilities.
|
||||
|
||||
use core::fmt;
|
||||
use once_cell::sync::Lazy;
|
||||
use semver::Version;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::error::Error;
|
||||
|
||||
pub use tlsn_core::webpki::{CertificateDer, PrivateKeyDer, RootCertStore};
|
||||
|
||||
// Default is 32 bytes to decrypt the TLS protocol messages.
|
||||
const DEFAULT_MAX_RECV_ONLINE: usize = 32;
|
||||
// Default maximum number of TLS records to allow.
|
||||
//
|
||||
// This would allow for up to 50Mb upload from prover to verifier.
|
||||
const DEFAULT_RECORDS_LIMIT: usize = 256;
|
||||
|
||||
// Current version that is running.
|
||||
pub(crate) static VERSION: Lazy<Version> = Lazy::new(|| {
|
||||
Version::parse(env!("CARGO_PKG_VERSION")).expect("cargo pkg version should be a valid semver")
|
||||
static VERSION: Lazy<Version> = Lazy::new(|| {
|
||||
Version::parse(env!("CARGO_PKG_VERSION"))
|
||||
.map_err(|err| ProtocolConfigError::new(ErrorKind::Version, err))
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
/// Protocol configuration to be set up initially by prover and verifier.
|
||||
@@ -40,6 +47,9 @@ pub struct ProtocolConfig {
|
||||
/// Network settings.
|
||||
#[builder(default)]
|
||||
network: NetworkSetting,
|
||||
/// Version that is being run by prover/verifier.
|
||||
#[builder(setter(skip), default = "VERSION.clone()")]
|
||||
version: Version,
|
||||
}
|
||||
|
||||
impl ProtocolConfigBuilder {
|
||||
@@ -98,6 +108,125 @@ impl ProtocolConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Protocol configuration validator used by checker (i.e. verifier) to perform
|
||||
/// compatibility check with the peer's (i.e. the prover's) configuration.
|
||||
#[derive(derive_builder::Builder, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct ProtocolConfigValidator {
|
||||
/// Maximum number of bytes that can be sent.
|
||||
max_sent_data: usize,
|
||||
/// Maximum number of application data records that can be sent.
|
||||
#[builder(default = "DEFAULT_RECORDS_LIMIT")]
|
||||
max_sent_records: usize,
|
||||
/// Maximum number of bytes that can be received.
|
||||
max_recv_data: usize,
|
||||
/// Maximum number of application data records that can be received online.
|
||||
#[builder(default = "DEFAULT_RECORDS_LIMIT")]
|
||||
max_recv_records_online: usize,
|
||||
/// Version that is being run by checker.
|
||||
#[builder(setter(skip), default = "VERSION.clone()")]
|
||||
version: Version,
|
||||
}
|
||||
|
||||
impl ProtocolConfigValidator {
|
||||
/// Creates a new builder for `ProtocolConfigValidator`.
|
||||
pub fn builder() -> ProtocolConfigValidatorBuilder {
|
||||
ProtocolConfigValidatorBuilder::default()
|
||||
}
|
||||
|
||||
/// Returns the maximum number of bytes that can be sent.
|
||||
pub fn max_sent_data(&self) -> usize {
|
||||
self.max_sent_data
|
||||
}
|
||||
|
||||
/// Returns the maximum number of application data records that can
|
||||
/// be sent.
|
||||
pub fn max_sent_records(&self) -> usize {
|
||||
self.max_sent_records
|
||||
}
|
||||
|
||||
/// Returns the maximum number of bytes that can be received.
|
||||
pub fn max_recv_data(&self) -> usize {
|
||||
self.max_recv_data
|
||||
}
|
||||
|
||||
/// Returns the maximum number of application data records that can
|
||||
/// be received online.
|
||||
pub fn max_recv_records_online(&self) -> usize {
|
||||
self.max_recv_records_online
|
||||
}
|
||||
|
||||
/// Performs compatibility check of the protocol configuration between
|
||||
/// prover and verifier.
|
||||
pub fn validate(&self, config: &ProtocolConfig) -> Result<(), ProtocolConfigError> {
|
||||
self.check_max_transcript_size(config.max_sent_data, config.max_recv_data)?;
|
||||
self.check_max_records(config.max_sent_records, config.max_recv_records_online)?;
|
||||
self.check_version(&config.version)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Checks if both the sent and recv data are within limits.
|
||||
fn check_max_transcript_size(
|
||||
&self,
|
||||
max_sent_data: usize,
|
||||
max_recv_data: usize,
|
||||
) -> Result<(), ProtocolConfigError> {
|
||||
if max_sent_data > self.max_sent_data {
|
||||
return Err(ProtocolConfigError::max_transcript_size(format!(
|
||||
"max_sent_data {:?} is greater than the configured limit {:?}",
|
||||
max_sent_data, self.max_sent_data,
|
||||
)));
|
||||
}
|
||||
|
||||
if max_recv_data > self.max_recv_data {
|
||||
return Err(ProtocolConfigError::max_transcript_size(format!(
|
||||
"max_recv_data {:?} is greater than the configured limit {:?}",
|
||||
max_recv_data, self.max_recv_data,
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_max_records(
|
||||
&self,
|
||||
max_sent_records: Option<usize>,
|
||||
max_recv_records_online: Option<usize>,
|
||||
) -> Result<(), ProtocolConfigError> {
|
||||
if let Some(max_sent_records) = max_sent_records
|
||||
&& max_sent_records > self.max_sent_records
|
||||
{
|
||||
return Err(ProtocolConfigError::max_record_count(format!(
|
||||
"max_sent_records {} is greater than the configured limit {}",
|
||||
max_sent_records, self.max_sent_records,
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(max_recv_records_online) = max_recv_records_online
|
||||
&& max_recv_records_online > self.max_recv_records_online
|
||||
{
|
||||
return Err(ProtocolConfigError::max_record_count(format!(
|
||||
"max_recv_records_online {} is greater than the configured limit {}",
|
||||
max_recv_records_online, self.max_recv_records_online,
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Checks if both versions are the same (might support check for different but
|
||||
// compatible versions in the future).
|
||||
fn check_version(&self, peer_version: &Version) -> Result<(), ProtocolConfigError> {
|
||||
if *peer_version != self.version {
|
||||
return Err(ProtocolConfigError::version(format!(
|
||||
"prover's version {:?} is different from verifier's version {:?}",
|
||||
peer_version, self.version
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings for the network environment.
|
||||
///
|
||||
/// Provides optimization options to adapt the protocol to different network
|
||||
@@ -117,3 +246,123 @@ impl Default for NetworkSetting {
|
||||
Self::Latency
|
||||
}
|
||||
}
|
||||
|
||||
/// A ProtocolConfig error.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub struct ProtocolConfigError {
|
||||
kind: ErrorKind,
|
||||
#[source]
|
||||
source: Option<Box<dyn Error + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl ProtocolConfigError {
|
||||
fn new<E>(kind: ErrorKind, source: E) -> Self
|
||||
where
|
||||
E: Into<Box<dyn Error + Send + Sync>>,
|
||||
{
|
||||
Self {
|
||||
kind,
|
||||
source: Some(source.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn max_transcript_size(msg: impl Into<String>) -> Self {
|
||||
Self {
|
||||
kind: ErrorKind::MaxTranscriptSize,
|
||||
source: Some(msg.into().into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn max_record_count(msg: impl Into<String>) -> Self {
|
||||
Self {
|
||||
kind: ErrorKind::MaxRecordCount,
|
||||
source: Some(msg.into().into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn version(msg: impl Into<String>) -> Self {
|
||||
Self {
|
||||
kind: ErrorKind::Version,
|
||||
source: Some(msg.into().into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ProtocolConfigError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.kind {
|
||||
ErrorKind::MaxTranscriptSize => write!(f, "max transcript size exceeded")?,
|
||||
ErrorKind::MaxRecordCount => write!(f, "max record count exceeded")?,
|
||||
ErrorKind::Version => write!(f, "version error")?,
|
||||
}
|
||||
|
||||
if let Some(ref source) = self.source {
|
||||
write!(f, " caused by: {source}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ErrorKind {
|
||||
MaxTranscriptSize,
|
||||
MaxRecordCount,
|
||||
Version,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rstest::{fixture, rstest};
|
||||
|
||||
const TEST_MAX_SENT_LIMIT: usize = 1 << 12;
|
||||
const TEST_MAX_RECV_LIMIT: usize = 1 << 14;
|
||||
|
||||
#[fixture]
|
||||
#[once]
|
||||
fn config_validator() -> ProtocolConfigValidator {
|
||||
ProtocolConfigValidator::builder()
|
||||
.max_sent_data(TEST_MAX_SENT_LIMIT)
|
||||
.max_recv_data(TEST_MAX_RECV_LIMIT)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::same_max_sent_recv_data(TEST_MAX_SENT_LIMIT, TEST_MAX_RECV_LIMIT)]
|
||||
#[case::smaller_max_sent_data(1 << 11, TEST_MAX_RECV_LIMIT)]
|
||||
#[case::smaller_max_recv_data(TEST_MAX_SENT_LIMIT, 1 << 13)]
|
||||
#[case::smaller_max_sent_recv_data(1 << 7, 1 << 9)]
|
||||
fn test_check_success(
|
||||
config_validator: &ProtocolConfigValidator,
|
||||
#[case] max_sent_data: usize,
|
||||
#[case] max_recv_data: usize,
|
||||
) {
|
||||
let peer_config = ProtocolConfig::builder()
|
||||
.max_sent_data(max_sent_data)
|
||||
.max_recv_data(max_recv_data)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert!(config_validator.validate(&peer_config).is_ok())
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::bigger_max_sent_data(1 << 13, TEST_MAX_RECV_LIMIT)]
|
||||
#[case::bigger_max_recv_data(1 << 10, 1 << 16)]
|
||||
#[case::bigger_max_sent_recv_data(1 << 14, 1 << 21)]
|
||||
fn test_check_fail(
|
||||
config_validator: &ProtocolConfigValidator,
|
||||
#[case] max_sent_data: usize,
|
||||
#[case] max_recv_data: usize,
|
||||
) {
|
||||
let peer_config = ProtocolConfig::builder()
|
||||
.max_sent_data(max_sent_data)
|
||||
.max_recv_data(max_recv_data)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert!(config_validator.validate(&peer_config).is_err())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ pub mod config;
|
||||
pub(crate) mod context;
|
||||
pub(crate) mod ghash;
|
||||
pub(crate) mod map;
|
||||
pub(crate) mod msg;
|
||||
pub(crate) mod mux;
|
||||
pub mod prover;
|
||||
pub(crate) mod tag;
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
use semver::Version;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::config::ProtocolConfig;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub(crate) struct SetupRequest {
|
||||
pub(crate) config: ProtocolConfig,
|
||||
pub(crate) version: Version,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub(crate) struct Response {
|
||||
pub(crate) result: Result<(), RejectionReason>,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
pub(crate) fn ok() -> Self {
|
||||
Self { result: Ok(()) }
|
||||
}
|
||||
|
||||
pub(crate) fn err(msg: Option<impl Into<String>>) -> Self {
|
||||
Self {
|
||||
result: Err(RejectionReason(msg.map(Into::into))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub(crate) struct RejectionReason(Option<String>);
|
||||
|
||||
impl From<RejectionReason> for crate::prover::ProverError {
|
||||
fn from(value: RejectionReason) -> Self {
|
||||
if let Some(msg) = value.0 {
|
||||
crate::prover::ProverError::config(format!("verifier rejected with reason: {msg}"))
|
||||
} else {
|
||||
crate::prover::ProverError::config("verifier rejected without providing a reason")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,7 @@ pub use config::{ProverConfig, ProverConfigBuilder, TlsConfig, TlsConfigBuilder}
|
||||
pub use error::ProverError;
|
||||
pub use future::ProverFuture;
|
||||
use rustls_pki_types::CertificateDer;
|
||||
pub use tlsn_core::{
|
||||
ProveConfig, ProveConfigBuilder, ProveConfigBuilderError, ProveRequest, ProverOutput,
|
||||
};
|
||||
pub use tlsn_core::{ProveConfig, ProveConfigBuilder, ProveConfigBuilderError, ProverOutput};
|
||||
|
||||
use mpz_common::Context;
|
||||
use mpz_core::Block;
|
||||
@@ -21,23 +19,17 @@ use mpz_vm_core::prelude::*;
|
||||
use mpz_zk::ProverConfig as ZkProverConfig;
|
||||
use webpki::anchor_from_trusted_cert;
|
||||
|
||||
use crate::{
|
||||
Role,
|
||||
context::build_mt_context,
|
||||
msg::{Response, SetupRequest},
|
||||
mux::attach_mux,
|
||||
tag::verify_tags,
|
||||
};
|
||||
use crate::{Role, context::build_mt_context, mux::attach_mux, tag::verify_tags};
|
||||
|
||||
use futures::{AsyncRead, AsyncWrite, TryFutureExt};
|
||||
use mpc_tls::{LeaderCtrl, MpcTlsLeader, SessionKeys};
|
||||
use rand::Rng;
|
||||
use serio::{SinkExt, stream::IoStreamExt};
|
||||
use serio::SinkExt;
|
||||
use std::sync::Arc;
|
||||
use tls_client::{ClientConnection, ServerName as TlsServerName};
|
||||
use tls_client_async::{TlsConnection, bind_client};
|
||||
use tlsn_core::{
|
||||
connection::{HandshakeData, ServerName},
|
||||
connection::ServerName,
|
||||
transcript::{TlsTranscript, Transcript},
|
||||
};
|
||||
use tlsn_deap::Deap;
|
||||
@@ -100,20 +92,7 @@ impl Prover<state::Initialized> {
|
||||
|
||||
// Sends protocol configuration to verifier for compatibility check.
|
||||
mux_fut
|
||||
.poll_with(async {
|
||||
ctx.io_mut()
|
||||
.send(SetupRequest {
|
||||
config: self.config.protocol_config().clone(),
|
||||
version: crate::config::VERSION.clone(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
ctx.io_mut()
|
||||
.expect_next::<Response>()
|
||||
.await?
|
||||
.result
|
||||
.map_err(ProverError::from)
|
||||
})
|
||||
.poll_with(ctx.io_mut().send(self.config.protocol_config().clone()))
|
||||
.await?;
|
||||
|
||||
let (vm, mut mpc_tls) = build_mpc_tls(&self.config, ctx);
|
||||
@@ -331,40 +310,16 @@ impl Prover<state::Committed> {
|
||||
..
|
||||
} = &mut self.state;
|
||||
|
||||
let request = ProveRequest {
|
||||
handshake: config.server_identity().then(|| {
|
||||
(
|
||||
self.config.server_name().clone(),
|
||||
HandshakeData {
|
||||
certs: tls_transcript
|
||||
.server_cert_chain()
|
||||
.expect("server cert chain is present")
|
||||
.to_vec(),
|
||||
sig: tls_transcript
|
||||
.server_signature()
|
||||
.expect("server signature is present")
|
||||
.clone(),
|
||||
binding: tls_transcript.certificate_binding().clone(),
|
||||
},
|
||||
)
|
||||
}),
|
||||
transcript: config
|
||||
.reveal()
|
||||
.map(|(sent, recv)| transcript.to_partial(sent.clone(), recv.clone())),
|
||||
transcript_commit: config.transcript_commit().map(|config| config.to_request()),
|
||||
};
|
||||
|
||||
let output = mux_fut
|
||||
.poll_with(async {
|
||||
ctx.io_mut()
|
||||
.send(request)
|
||||
.await
|
||||
.map_err(ProverError::from)?;
|
||||
|
||||
ctx.io_mut().expect_next::<Response>().await?.result?;
|
||||
|
||||
prove::prove(ctx, vm, keys, transcript, tls_transcript, config).await
|
||||
})
|
||||
.poll_with(prove::prove(
|
||||
ctx,
|
||||
vm,
|
||||
keys,
|
||||
self.config.server_name(),
|
||||
transcript,
|
||||
tls_transcript,
|
||||
config,
|
||||
))
|
||||
.await?;
|
||||
|
||||
Ok(output)
|
||||
|
||||
@@ -3,8 +3,10 @@ use mpz_common::Context;
|
||||
use mpz_memory_core::binary::Binary;
|
||||
use mpz_vm_core::Vm;
|
||||
use rangeset::{RangeSet, UnionMut};
|
||||
use serio::SinkExt;
|
||||
use tlsn_core::{
|
||||
ProveConfig, ProverOutput,
|
||||
ProveConfig, ProveRequest, ProverOutput,
|
||||
connection::{HandshakeData, ServerName},
|
||||
transcript::{
|
||||
ContentType, Direction, TlsTranscript, Transcript, TranscriptCommitment, TranscriptSecret,
|
||||
},
|
||||
@@ -26,6 +28,7 @@ pub(crate) async fn prove<T: Vm<Binary> + MacStore + Send + Sync>(
|
||||
ctx: &mut Context,
|
||||
vm: &mut T,
|
||||
keys: &SessionKeys,
|
||||
server_name: &ServerName,
|
||||
transcript: &Transcript,
|
||||
tls_transcript: &TlsTranscript,
|
||||
config: &ProveConfig,
|
||||
@@ -35,6 +38,34 @@ pub(crate) async fn prove<T: Vm<Binary> + MacStore + Send + Sync>(
|
||||
transcript_secrets: Vec::default(),
|
||||
};
|
||||
|
||||
let request = ProveRequest {
|
||||
handshake: config.server_identity().then(|| {
|
||||
(
|
||||
server_name.clone(),
|
||||
HandshakeData {
|
||||
certs: tls_transcript
|
||||
.server_cert_chain()
|
||||
.expect("server cert chain is present")
|
||||
.to_vec(),
|
||||
sig: tls_transcript
|
||||
.server_signature()
|
||||
.expect("server signature is present")
|
||||
.clone(),
|
||||
binding: tls_transcript.certificate_binding().clone(),
|
||||
},
|
||||
)
|
||||
}),
|
||||
transcript: config
|
||||
.reveal()
|
||||
.map(|(sent, recv)| transcript.to_partial(sent.clone(), recv.clone())),
|
||||
transcript_commit: config.transcript_commit().map(|config| config.to_request()),
|
||||
};
|
||||
|
||||
ctx.io_mut()
|
||||
.send(request)
|
||||
.await
|
||||
.map_err(ProverError::from)?;
|
||||
|
||||
let (reveal_sent, reveal_recv) = config.reveal().cloned().unwrap_or_default();
|
||||
let (mut commit_sent, mut commit_recv) = (RangeSet::default(), RangeSet::default());
|
||||
if let Some(commit_config) = config.transcript_commit() {
|
||||
|
||||
@@ -9,15 +9,13 @@ use std::sync::Arc;
|
||||
|
||||
pub use config::{VerifierConfig, VerifierConfigBuilder, VerifierConfigBuilderError};
|
||||
pub use error::VerifierError;
|
||||
pub use tlsn_core::{VerifierOutput, webpki::ServerCertVerifier};
|
||||
pub use tlsn_core::{
|
||||
VerifierOutput, VerifyConfig, VerifyConfigBuilder, VerifyConfigBuilderError,
|
||||
webpki::ServerCertVerifier,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Role,
|
||||
config::ProtocolConfig,
|
||||
context::build_mt_context,
|
||||
msg::{Response, SetupRequest},
|
||||
mux::attach_mux,
|
||||
tag::verify_tags,
|
||||
Role, config::ProtocolConfig, context::build_mt_context, mux::attach_mux, tag::verify_tags,
|
||||
};
|
||||
use futures::{AsyncRead, AsyncWrite, TryFutureExt};
|
||||
use mpc_tls::{MpcTlsFollower, SessionKeys};
|
||||
@@ -26,9 +24,8 @@ use mpz_core::Block;
|
||||
use mpz_garble_core::Delta;
|
||||
use mpz_vm_core::prelude::*;
|
||||
use mpz_zk::VerifierConfig as ZkVerifierConfig;
|
||||
use serio::{SinkExt, stream::IoStreamExt};
|
||||
use serio::stream::IoStreamExt;
|
||||
use tlsn_core::{
|
||||
ProveRequest,
|
||||
connection::{ConnectionInfo, ServerName},
|
||||
transcript::TlsTranscript,
|
||||
};
|
||||
@@ -88,65 +85,24 @@ impl Verifier<state::Initialized> {
|
||||
pub async fn setup<S: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
self,
|
||||
socket: S,
|
||||
) -> Result<Verifier<state::Config>, VerifierError> {
|
||||
) -> Result<Verifier<state::Setup>, VerifierError> {
|
||||
let (mut mux_fut, mux_ctrl) = attach_mux(socket, Role::Verifier);
|
||||
let mut mt = build_mt_context(mux_ctrl.clone());
|
||||
let mut ctx = mux_fut.poll_with(mt.new_context()).await?;
|
||||
|
||||
// Receives protocol configuration from prover to perform compatibility check.
|
||||
let SetupRequest { config, version } =
|
||||
mux_fut.poll_with(ctx.io_mut().expect_next()).await?;
|
||||
let protocol_config = mux_fut
|
||||
.poll_with(async {
|
||||
let peer_configuration: ProtocolConfig = ctx.io_mut().expect_next().await?;
|
||||
self.config
|
||||
.protocol_config_validator()
|
||||
.validate(&peer_configuration)?;
|
||||
|
||||
if version != *crate::config::VERSION {
|
||||
let msg = format!(
|
||||
"prover version does not match with verifier: {version} != {}",
|
||||
*crate::config::VERSION
|
||||
);
|
||||
mux_fut
|
||||
.poll_with(ctx.io_mut().send(Response::err(Some(msg.clone()))))
|
||||
.await?;
|
||||
Ok::<_, VerifierError>(peer_configuration)
|
||||
})
|
||||
.await?;
|
||||
|
||||
// Wait for the prover to correctly close the connection.
|
||||
if !mux_fut.is_complete() {
|
||||
mux_ctrl.close();
|
||||
mux_fut.await?;
|
||||
}
|
||||
|
||||
return Err(VerifierError::config(msg));
|
||||
}
|
||||
|
||||
Ok(Verifier {
|
||||
config: self.config,
|
||||
span: self.span,
|
||||
state: state::Config {
|
||||
mux_ctrl,
|
||||
mux_fut,
|
||||
ctx,
|
||||
config,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Verifier<state::Config> {
|
||||
/// Returns the proposed protocol configuration.
|
||||
pub fn config(&self) -> &ProtocolConfig {
|
||||
&self.state.config
|
||||
}
|
||||
|
||||
/// Accepts the proposed protocol configuration.
|
||||
#[instrument(parent = &self.span, level = "info", skip_all, err)]
|
||||
pub async fn accept(self) -> Result<Verifier<state::Setup>, VerifierError> {
|
||||
let state::Config {
|
||||
mux_ctrl,
|
||||
mut mux_fut,
|
||||
mut ctx,
|
||||
config,
|
||||
} = self.state;
|
||||
|
||||
mux_fut.poll_with(ctx.io_mut().send(Response::ok())).await?;
|
||||
|
||||
let (vm, mut mpc_tls) = build_mpc_tls(&self.config, &config, ctx);
|
||||
let (vm, mut mpc_tls) = build_mpc_tls(&self.config, &protocol_config, ctx);
|
||||
|
||||
// Allocate resources for MPC-TLS in the VM.
|
||||
let mut keys = mpc_tls.alloc()?;
|
||||
@@ -173,27 +129,27 @@ impl Verifier<state::Config> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Rejects the proposed protocol configuration.
|
||||
/// Runs the TLS verifier to completion, verifying the TLS session.
|
||||
///
|
||||
/// This is a convenience method which runs all the steps needed for
|
||||
/// verification.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `socket` - The socket to the prover.
|
||||
#[instrument(parent = &self.span, level = "info", skip_all, err)]
|
||||
pub async fn reject(self, msg: Option<&str>) -> Result<(), VerifierError> {
|
||||
let state::Config {
|
||||
mux_ctrl,
|
||||
mut mux_fut,
|
||||
mut ctx,
|
||||
..
|
||||
} = self.state;
|
||||
pub async fn verify<S: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
self,
|
||||
socket: S,
|
||||
config: &VerifyConfig,
|
||||
) -> Result<VerifierOutput, VerifierError> {
|
||||
let mut verifier = self.setup(socket).await?.run().await?;
|
||||
|
||||
mux_fut
|
||||
.poll_with(ctx.io_mut().send(Response::err(msg)))
|
||||
.await?;
|
||||
let output = verifier.verify(config).await?;
|
||||
|
||||
// Wait for the prover to correctly close the connection.
|
||||
if !mux_fut.is_complete() {
|
||||
mux_ctrl.close();
|
||||
mux_fut.await?;
|
||||
}
|
||||
verifier.close().await?;
|
||||
|
||||
Ok(())
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,35 +231,47 @@ impl Verifier<state::Committed> {
|
||||
&self.state.tls_transcript
|
||||
}
|
||||
|
||||
/// Begins verification of statements from the prover.
|
||||
/// Verifies information from the prover.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `config` - Verification configuration.
|
||||
#[instrument(parent = &self.span, level = "info", skip_all, err)]
|
||||
pub async fn verify(self) -> Result<Verifier<state::Verify>, VerifierError> {
|
||||
pub async fn verify(
|
||||
&mut self,
|
||||
#[allow(unused_variables)] config: &VerifyConfig,
|
||||
) -> Result<VerifierOutput, VerifierError> {
|
||||
let state::Committed {
|
||||
mux_ctrl,
|
||||
mut mux_fut,
|
||||
mut ctx,
|
||||
mux_fut,
|
||||
ctx,
|
||||
vm,
|
||||
keys,
|
||||
tls_transcript,
|
||||
} = self.state;
|
||||
..
|
||||
} = &mut self.state;
|
||||
|
||||
let cert_verifier = if let Some(root_store) = self.config.root_store() {
|
||||
ServerCertVerifier::new(root_store).map_err(VerifierError::config)?
|
||||
} else {
|
||||
ServerCertVerifier::mozilla()
|
||||
};
|
||||
|
||||
let request = mux_fut
|
||||
.poll_with(ctx.io_mut().expect_next().map_err(VerifierError::from))
|
||||
.await?;
|
||||
|
||||
Ok(Verifier {
|
||||
config: self.config,
|
||||
span: self.span,
|
||||
state: state::Verify {
|
||||
mux_ctrl,
|
||||
mux_fut,
|
||||
let output = mux_fut
|
||||
.poll_with(verify::verify(
|
||||
ctx,
|
||||
vm,
|
||||
keys,
|
||||
&cert_verifier,
|
||||
tls_transcript,
|
||||
request,
|
||||
},
|
||||
})
|
||||
))
|
||||
.await?;
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Closes the connection with the prover.
|
||||
@@ -323,96 +291,6 @@ impl Verifier<state::Committed> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Verifier<state::Verify> {
|
||||
/// Returns the proving request.
|
||||
pub fn request(&self) -> &ProveRequest {
|
||||
&self.state.request
|
||||
}
|
||||
|
||||
/// Accepts the proving request.
|
||||
pub async fn accept(
|
||||
self,
|
||||
) -> Result<(VerifierOutput, Verifier<state::Committed>), VerifierError> {
|
||||
let state::Verify {
|
||||
mux_ctrl,
|
||||
mut mux_fut,
|
||||
mut ctx,
|
||||
mut vm,
|
||||
keys,
|
||||
tls_transcript,
|
||||
request,
|
||||
} = self.state;
|
||||
|
||||
mux_fut.poll_with(ctx.io_mut().send(Response::ok())).await?;
|
||||
|
||||
let cert_verifier = if let Some(root_store) = self.config.root_store() {
|
||||
ServerCertVerifier::new(root_store).map_err(VerifierError::config)?
|
||||
} else {
|
||||
ServerCertVerifier::mozilla()
|
||||
};
|
||||
|
||||
let output = mux_fut
|
||||
.poll_with(verify::verify(
|
||||
&mut ctx,
|
||||
&mut vm,
|
||||
&keys,
|
||||
&cert_verifier,
|
||||
&tls_transcript,
|
||||
request,
|
||||
))
|
||||
.await?;
|
||||
|
||||
Ok((
|
||||
output,
|
||||
Verifier {
|
||||
config: self.config,
|
||||
span: self.span,
|
||||
state: state::Committed {
|
||||
mux_ctrl,
|
||||
mux_fut,
|
||||
ctx,
|
||||
vm,
|
||||
keys,
|
||||
tls_transcript,
|
||||
},
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Rejects the proving request.
|
||||
pub async fn reject(
|
||||
self,
|
||||
msg: Option<&str>,
|
||||
) -> Result<Verifier<state::Committed>, VerifierError> {
|
||||
let state::Verify {
|
||||
mux_ctrl,
|
||||
mut mux_fut,
|
||||
mut ctx,
|
||||
vm,
|
||||
keys,
|
||||
tls_transcript,
|
||||
..
|
||||
} = self.state;
|
||||
|
||||
mux_fut
|
||||
.poll_with(ctx.io_mut().send(Response::err(msg)))
|
||||
.await?;
|
||||
|
||||
Ok(Verifier {
|
||||
config: self.config,
|
||||
span: self.span,
|
||||
state: state::Committed {
|
||||
mux_ctrl,
|
||||
mux_fut,
|
||||
ctx,
|
||||
vm,
|
||||
keys,
|
||||
tls_transcript,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn build_mpc_tls(
|
||||
config: &VerifierConfig,
|
||||
protocol_config: &ProtocolConfig,
|
||||
|
||||
@@ -4,20 +4,23 @@ use mpc_tls::Config;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tlsn_core::webpki::RootCertStore;
|
||||
|
||||
use crate::config::{NetworkSetting, ProtocolConfig};
|
||||
use crate::config::{NetworkSetting, ProtocolConfig, ProtocolConfigValidator};
|
||||
|
||||
/// Configuration for the [`Verifier`](crate::tls::Verifier).
|
||||
#[allow(missing_docs)]
|
||||
#[derive(derive_builder::Builder, Serialize, Deserialize)]
|
||||
#[builder(pattern = "owned")]
|
||||
pub struct VerifierConfig {
|
||||
protocol_config_validator: ProtocolConfigValidator,
|
||||
#[builder(default, setter(strip_option))]
|
||||
root_store: Option<RootCertStore>,
|
||||
}
|
||||
|
||||
impl Debug for VerifierConfig {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
f.debug_struct("VerifierConfig").finish_non_exhaustive()
|
||||
f.debug_struct("VerifierConfig")
|
||||
.field("protocol_config_validator", &self.protocol_config_validator)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +30,11 @@ impl VerifierConfig {
|
||||
VerifierConfigBuilder::default()
|
||||
}
|
||||
|
||||
/// Returns the protocol configuration validator.
|
||||
pub fn protocol_config_validator(&self) -> &ProtocolConfigValidator {
|
||||
&self.protocol_config_validator
|
||||
}
|
||||
|
||||
/// Returns the root certificate store.
|
||||
pub fn root_store(&self) -> Option<&RootCertStore> {
|
||||
self.root_store.as_ref()
|
||||
|
||||
@@ -88,6 +88,12 @@ impl From<std::io::Error> for VerifierError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::config::ProtocolConfigError> for VerifierError {
|
||||
fn from(e: crate::config::ProtocolConfigError) -> Self {
|
||||
Self::new(ErrorKind::Config, e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<uid_mux::yamux::ConnectionError> for VerifierError {
|
||||
fn from(e: uid_mux::yamux::ConnectionError) -> Self {
|
||||
Self::new(ErrorKind::Io, e)
|
||||
|
||||
@@ -2,13 +2,10 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
config::ProtocolConfig,
|
||||
mux::{MuxControl, MuxFuture},
|
||||
};
|
||||
use crate::mux::{MuxControl, MuxFuture};
|
||||
use mpc_tls::{MpcTlsFollower, SessionKeys};
|
||||
use mpz_common::Context;
|
||||
use tlsn_core::{ProveRequest, transcript::TlsTranscript};
|
||||
use tlsn_core::transcript::TlsTranscript;
|
||||
use tlsn_deap::Deap;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
@@ -22,16 +19,6 @@ pub struct Initialized;
|
||||
|
||||
opaque_debug::implement!(Initialized);
|
||||
|
||||
/// State after receiving protocol configuration from the prover.
|
||||
pub struct Config {
|
||||
pub(crate) mux_ctrl: MuxControl,
|
||||
pub(crate) mux_fut: MuxFuture,
|
||||
pub(crate) ctx: Context,
|
||||
pub(crate) config: ProtocolConfig,
|
||||
}
|
||||
|
||||
opaque_debug::implement!(Config);
|
||||
|
||||
/// State after setup has completed.
|
||||
pub struct Setup {
|
||||
pub(crate) mux_ctrl: MuxControl,
|
||||
@@ -41,8 +28,6 @@ pub struct Setup {
|
||||
pub(crate) vm: Arc<Mutex<Deap<Mpc, Zk>>>,
|
||||
}
|
||||
|
||||
opaque_debug::implement!(Setup);
|
||||
|
||||
/// State after the TLS connection has been closed.
|
||||
pub struct Committed {
|
||||
pub(crate) mux_ctrl: MuxControl,
|
||||
@@ -55,30 +40,13 @@ pub struct Committed {
|
||||
|
||||
opaque_debug::implement!(Committed);
|
||||
|
||||
/// State after receiving a proving request.
|
||||
pub struct Verify {
|
||||
pub(crate) mux_ctrl: MuxControl,
|
||||
pub(crate) mux_fut: MuxFuture,
|
||||
pub(crate) ctx: Context,
|
||||
pub(crate) vm: Zk,
|
||||
pub(crate) keys: SessionKeys,
|
||||
pub(crate) tls_transcript: TlsTranscript,
|
||||
pub(crate) request: ProveRequest,
|
||||
}
|
||||
|
||||
opaque_debug::implement!(Verify);
|
||||
|
||||
impl VerifierState for Initialized {}
|
||||
impl VerifierState for Config {}
|
||||
impl VerifierState for Setup {}
|
||||
impl VerifierState for Committed {}
|
||||
impl VerifierState for Verify {}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
impl Sealed for super::Initialized {}
|
||||
impl Sealed for super::Config {}
|
||||
impl Sealed for super::Setup {}
|
||||
impl Sealed for super::Committed {}
|
||||
impl Sealed for super::Verify {}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use futures::{AsyncReadExt, AsyncWriteExt};
|
||||
use rangeset::RangeSet;
|
||||
use tlsn::{
|
||||
config::{CertificateDer, ProtocolConfig, RootCertStore},
|
||||
config::{CertificateDer, ProtocolConfig, ProtocolConfigValidator, RootCertStore},
|
||||
connection::ServerName,
|
||||
hash::{HashAlgId, HashProvider},
|
||||
prover::{ProveConfig, Prover, ProverConfig, TlsConfig},
|
||||
@@ -9,7 +9,7 @@ use tlsn::{
|
||||
Direction, Transcript, TranscriptCommitConfig, TranscriptCommitment,
|
||||
TranscriptCommitmentKind, TranscriptSecret,
|
||||
},
|
||||
verifier::{Verifier, VerifierConfig, VerifierOutput},
|
||||
verifier::{Verifier, VerifierConfig, VerifierOutput, VerifyConfig},
|
||||
};
|
||||
use tlsn_core::ProverOutput;
|
||||
use tlsn_server_fixture::bind;
|
||||
@@ -204,27 +204,31 @@ async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
|
||||
async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(
|
||||
socket: T,
|
||||
) -> VerifierOutput {
|
||||
let config_validator = ProtocolConfigValidator::builder()
|
||||
.max_sent_data(MAX_SENT_DATA)
|
||||
.max_recv_data(MAX_RECV_DATA)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let verifier = Verifier::new(
|
||||
VerifierConfig::builder()
|
||||
.root_store(RootCertStore {
|
||||
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
|
||||
})
|
||||
.protocol_config_validator(config_validator)
|
||||
.build()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let verifier = verifier
|
||||
let mut verifier = verifier
|
||||
.setup(socket.compat())
|
||||
.await
|
||||
.unwrap()
|
||||
.accept()
|
||||
.await
|
||||
.unwrap()
|
||||
.run()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let (output, verifier) = verifier.verify().await.unwrap().accept().await.unwrap();
|
||||
let output = verifier.verify(&VerifyConfig::default()).await.unwrap();
|
||||
verifier.close().await.unwrap();
|
||||
|
||||
output
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tlsn-wasm"
|
||||
version = "0.1.0-alpha.14-pre"
|
||||
version = "0.1.0-alpha.13"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/tlsnotary/tlsn.git"
|
||||
description = "A core WebAssembly package for TLSNotary."
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use tlsn::config::ProtocolConfigValidator;
|
||||
use tsify_next::Tsify;
|
||||
|
||||
#[derive(Debug, Tsify, Deserialize)]
|
||||
@@ -9,3 +10,27 @@ pub struct VerifierConfig {
|
||||
pub max_sent_records: Option<usize>,
|
||||
pub max_recv_records_online: Option<usize>,
|
||||
}
|
||||
|
||||
impl From<VerifierConfig> for tlsn::verifier::VerifierConfig {
|
||||
fn from(value: VerifierConfig) -> Self {
|
||||
let mut builder = ProtocolConfigValidator::builder();
|
||||
|
||||
builder.max_sent_data(value.max_sent_data);
|
||||
builder.max_recv_data(value.max_recv_data);
|
||||
|
||||
if let Some(value) = value.max_sent_records {
|
||||
builder.max_sent_records(value);
|
||||
}
|
||||
|
||||
if let Some(value) = value.max_recv_records_online {
|
||||
builder.max_recv_records_online(value);
|
||||
}
|
||||
|
||||
let validator = builder.build().unwrap();
|
||||
|
||||
tlsn::verifier::VerifierConfig::builder()
|
||||
.protocol_config_validator(validator)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,10 @@ use enum_try_as_inner::EnumTryAsInner;
|
||||
use tls_core::msgs::enums::ContentType;
|
||||
use tlsn::{
|
||||
connection::{ConnectionInfo, ServerName, TranscriptLength},
|
||||
verifier::{state, Verifier},
|
||||
verifier::{
|
||||
state::{self, Initialized},
|
||||
Verifier, VerifyConfig,
|
||||
},
|
||||
};
|
||||
use tracing::info;
|
||||
use wasm_bindgen::prelude::*;
|
||||
@@ -18,7 +21,6 @@ type Result<T> = std::result::Result<T, JsError>;
|
||||
|
||||
#[wasm_bindgen(js_name = Verifier)]
|
||||
pub struct JsVerifier {
|
||||
config: VerifierConfig,
|
||||
state: State,
|
||||
}
|
||||
|
||||
@@ -47,10 +49,8 @@ impl State {
|
||||
impl JsVerifier {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(config: VerifierConfig) -> JsVerifier {
|
||||
let tlsn_config = tlsn::verifier::VerifierConfig::builder().build().unwrap();
|
||||
JsVerifier {
|
||||
state: State::Initialized(Verifier::new(tlsn_config)),
|
||||
config,
|
||||
state: State::Initialized(Verifier::new(config.into())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,27 +73,7 @@ impl JsVerifier {
|
||||
pub async fn verify(&mut self) -> Result<VerifierOutput> {
|
||||
let (verifier, prover_conn) = self.state.take().try_into_connected()?;
|
||||
|
||||
let verifier = verifier.setup(prover_conn.into_io()).await?;
|
||||
let config = verifier.config();
|
||||
|
||||
let reject = if config.max_sent_data() > self.config.max_sent_data {
|
||||
Some("max_sent_data is too large")
|
||||
} else if config.max_recv_data() > self.config.max_recv_data {
|
||||
Some("max_recv_data is too large")
|
||||
} else if config.max_sent_records() > self.config.max_sent_records {
|
||||
Some("max_sent_records is too large")
|
||||
} else if config.max_recv_records_online() > self.config.max_recv_records_online {
|
||||
Some("max_recv_records_online is too large")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if reject.is_some() {
|
||||
verifier.reject(reject).await?;
|
||||
return Err(JsError::new("protocol configuration rejected"));
|
||||
}
|
||||
|
||||
let verifier = verifier.accept().await?.run().await?;
|
||||
let mut verifier = verifier.setup(prover_conn.into_io()).await?.run().await?;
|
||||
|
||||
let sent = verifier
|
||||
.tls_transcript()
|
||||
@@ -120,7 +100,7 @@ impl JsVerifier {
|
||||
},
|
||||
};
|
||||
|
||||
let (output, verifier) = verifier.verify().await?.accept().await?;
|
||||
let output = verifier.verify(&VerifyConfig::default()).await?;
|
||||
verifier.close().await?;
|
||||
|
||||
self.state = State::Complete;
|
||||
@@ -135,3 +115,11 @@ impl JsVerifier {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<tlsn::verifier::Verifier<Initialized>> for JsVerifier {
|
||||
fn from(value: tlsn::verifier::Verifier<Initialized>) -> Self {
|
||||
Self {
|
||||
state: State::Initialized(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user