Files
tlsn/crates/examples/interactive/interactive.rs
th4s 9bbb2fb66c feat: make defer-decryption default
* feat: add `defer` option to inner configs

- `TranscriptConfig`
- `MpcTlsLeaderConfig`
- `MpcTlsFollowerConfig`

* adapt common `ProtocolConfig` to `deferred` feature

* Adapt prover and verifier configs.

* Adapt benches.

* Adapt examples.

* Adapt `crates/tests-integration`.

* Adapt notary integration test and wasm crates.

* Fix test.

* add clarifying comments

* Add feedback.

* Improve default handling for `max_deferred_size`.

* Use default handling instead of validation.

* Add feedback.

* fix: bugfix for `notarize.rs`

* Remove defaults for `ProtocolConfigValidator`

* Set `ProtocolConfigValidator` where needed.

* Only preprocess online part for transcript.

---------

Co-authored-by: Ubuntu <ubuntu@ip-10-35-1-161.eu-central-1.compute.internal>
2024-09-11 21:49:58 +02:00

202 lines
6.8 KiB
Rust

use http_body_util::Empty;
use hyper::{body::Bytes, Request, StatusCode, Uri};
use hyper_util::rt::TokioIo;
use tlsn_common::config::{ProtocolConfig, ProtocolConfigValidator};
use tlsn_core::{proof::SessionInfo, Direction, RedactedTranscript};
use tlsn_prover::tls::{state::Prove, Prover, ProverConfig};
use tlsn_verifier::tls::{Verifier, VerifierConfig};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt};
use tracing::instrument;
const SECRET: &str = "TLSNotary's private key 🤡";
const SERVER_DOMAIN: &str = "example.com";
// Maximum number of bytes that can be sent from prover to server
const MAX_SENT_DATA: usize = 1 << 12;
// Maximum number of bytes that can be received by prover from server
const MAX_RECV_DATA: usize = 1 << 14;
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
let uri = "https://example.com";
let id = "interactive verifier demo";
// Connect prover and verifier.
let (prover_socket, verifier_socket) = tokio::io::duplex(1 << 23);
let prover = prover(prover_socket, uri, id);
let verifier = verifier(verifier_socket, id);
let (_, (sent, received, _session_info)) = tokio::join!(prover, verifier);
println!("Successfully verified {}", &uri);
println!(
"Verified sent data:\n{}",
bytes_to_redacted_string(sent.data())
);
println!(
"Verified received data:\n{}",
bytes_to_redacted_string(received.data())
);
}
#[instrument(skip(verifier_socket))]
async fn prover<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
verifier_socket: T,
uri: &str,
id: &str,
) {
let uri = uri.parse::<Uri>().unwrap();
assert_eq!(uri.scheme().unwrap().as_str(), "https");
let server_domain = uri.authority().unwrap().host();
let server_port = uri.port_u16().unwrap_or(443);
// Create prover and connect to verifier.
//
// Perform the setup phase with the verifier.
let prover = Prover::new(
ProverConfig::builder()
.id(id)
.server_dns(server_domain)
.protocol_config(
ProtocolConfig::builder()
.max_sent_data(MAX_SENT_DATA)
.max_recv_data(MAX_RECV_DATA)
.build()
.unwrap(),
)
.build()
.unwrap(),
)
.setup(verifier_socket.compat())
.await
.unwrap();
// Connect to TLS Server.
let tls_client_socket = tokio::net::TcpStream::connect((server_domain, server_port))
.await
.unwrap();
// Pass server connection into the prover.
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());
// Spawn the Prover to run in the background.
let prover_task = tokio::spawn(prover_fut);
// MPC-TLS Handshake.
let (mut request_sender, connection) =
hyper::client::conn::http1::handshake(mpc_tls_connection)
.await
.unwrap();
// Spawn the connection to run in the background.
tokio::spawn(connection);
// MPC-TLS: Send Request and wait for Response.
let request = Request::builder()
.uri(uri.clone())
.header("Host", server_domain)
.header("Connection", "close")
.header("Secret", SECRET)
.method("GET")
.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.unwrap().unwrap().start_prove();
redact_and_reveal_received_data(&mut prover);
redact_and_reveal_sent_data(&mut prover);
prover.prove().await.unwrap();
// Finalize.
prover.finalize().await.unwrap()
}
#[instrument(skip(socket))]
async fn verifier<T: AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static>(
socket: T,
id: &str,
) -> (RedactedTranscript, RedactedTranscript, SessionInfo) {
// Setup Verifier.
let config_validator = ProtocolConfigValidator::builder()
.max_sent_data(MAX_SENT_DATA)
.max_recv_data(MAX_RECV_DATA)
.build()
.unwrap();
let verifier_config = VerifierConfig::builder()
.id(id)
.protocol_config_validator(config_validator)
.build()
.unwrap();
let verifier = Verifier::new(verifier_config);
// Verify MPC-TLS and wait for (redacted) data.
let (sent, received, session_info) = verifier.verify(socket.compat()).await.unwrap();
// Check sent data: check host.
let sent_data = String::from_utf8(sent.data().to_vec()).expect("Verifier expected sent data");
sent_data
.find(SERVER_DOMAIN)
.unwrap_or_else(|| panic!("Verification failed: Expected host {}", SERVER_DOMAIN));
// Check received data: check json and version number.
let response =
String::from_utf8(received.data().to_vec()).expect("Verifier expected received data");
response
.find("Example Domain")
.expect("Expected valid data from example.com");
// Check Session info: server name.
assert_eq!(session_info.server_name.as_str(), SERVER_DOMAIN);
(sent, received, session_info)
}
/// Redacts and reveals received data to the verifier.
fn redact_and_reveal_received_data(prover: &mut Prover<Prove>) {
let recv_transcript_len = prover.recv_transcript().data().len();
// Get the received data as a string.
let received_string = String::from_utf8(prover.recv_transcript().data().to_vec()).unwrap();
// Find the substring "illustrative".
let start = received_string
.find("illustrative")
.expect("Error: The substring 'illustrative' was not found in the received data.");
let end = start + "illustrative".len();
// Reveal everything except for the substring "illustrative".
_ = prover.reveal(0..start, Direction::Received);
_ = prover.reveal(end..recv_transcript_len, Direction::Received);
}
/// Redacts and reveals sent data to the verifier.
fn redact_and_reveal_sent_data(prover: &mut Prover<Prove>) {
let sent_transcript_len = prover.sent_transcript().data().len();
let sent_string = String::from_utf8(prover.sent_transcript().data().to_vec()).unwrap();
let secret_start = sent_string.find(SECRET).unwrap();
// Reveal everything except for the SECRET.
_ = prover.reveal(0..secret_start, Direction::Sent);
_ = prover.reveal(
secret_start + SECRET.len()..sent_transcript_len,
Direction::Sent,
);
}
/// Render redacted bytes as `🙈`.
fn bytes_to_redacted_string(bytes: &[u8]) -> String {
String::from_utf8(bytes.to_vec())
.unwrap()
.replace('\0', "🙈")
}