tlsn_verifier/
lib.rs

1//! TLSNotary verifier library.
2
3#![deny(missing_docs, unreachable_pub, unused_must_use)]
4#![deny(clippy::all)]
5#![forbid(unsafe_code)]
6
7pub(crate) mod config;
8mod error;
9mod notarize;
10pub mod state;
11mod verify;
12
13use std::sync::Arc;
14
15pub use config::{VerifierConfig, VerifierConfigBuilder, VerifierConfigBuilderError};
16pub use error::VerifierError;
17
18use futures::{AsyncRead, AsyncWrite};
19use mpc_tls::{FollowerData, MpcTlsFollower, SessionKeys};
20use mpz_common::Context;
21use mpz_core::Block;
22use mpz_garble_core::Delta;
23use serio::stream::IoStreamExt;
24use state::{Notarize, Verify};
25use tls_core::msgs::enums::ContentType;
26use tlsn_common::{
27    commit::commit_records,
28    config::ProtocolConfig,
29    context::build_mt_context,
30    mux::attach_mux,
31    transcript::{Record, TlsTranscript},
32    zk_aes::ZkAesCtr,
33    Role,
34};
35use tlsn_core::{
36    attestation::{Attestation, AttestationConfig},
37    connection::{ConnectionInfo, ServerName, TlsVersion, TranscriptLength},
38    transcript::PartialTranscript,
39};
40use tlsn_deap::Deap;
41use tokio::sync::Mutex;
42use web_time::{SystemTime, UNIX_EPOCH};
43
44use tracing::{debug, info, info_span, instrument, Span};
45
46pub(crate) type RCOTSender = mpz_ot::rcot::shared::SharedRCOTSender<
47    mpz_ot::ferret::Sender<mpz_ot::kos::Sender<mpz_ot::chou_orlandi::Receiver>>,
48    mpz_core::Block,
49>;
50pub(crate) type RCOTReceiver = mpz_ot::rcot::shared::SharedRCOTReceiver<
51    mpz_ot::kos::Receiver<mpz_ot::chou_orlandi::Sender>,
52    bool,
53    mpz_core::Block,
54>;
55pub(crate) type Mpc =
56    mpz_garble::protocol::semihonest::Evaluator<mpz_ot::cot::DerandCOTReceiver<RCOTReceiver>>;
57pub(crate) type Zk = mpz_zk::Verifier<RCOTSender>;
58
59/// Information about the TLS session.
60#[derive(Debug)]
61pub struct SessionInfo {
62    /// Server's name.
63    pub server_name: ServerName,
64    /// Connection information.
65    pub connection_info: ConnectionInfo,
66}
67
68/// A Verifier instance.
69pub struct Verifier<T: state::VerifierState> {
70    config: VerifierConfig,
71    span: Span,
72    state: T,
73}
74
75impl Verifier<state::Initialized> {
76    /// Creates a new verifier.
77    pub fn new(config: VerifierConfig) -> Self {
78        let span = info_span!("verifier");
79        Self {
80            config,
81            span,
82            state: state::Initialized,
83        }
84    }
85
86    /// Sets up the verifier.
87    ///
88    /// This performs all MPC setup.
89    ///
90    /// # Arguments
91    ///
92    /// * `socket` - The socket to the prover.
93    #[instrument(parent = &self.span, level = "info", skip_all, err)]
94    pub async fn setup<S: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
95        self,
96        socket: S,
97    ) -> Result<Verifier<state::Setup>, VerifierError> {
98        let (mut mux_fut, mux_ctrl) = attach_mux(socket, Role::Verifier);
99        let mut mt = build_mt_context(mux_ctrl.clone());
100        let mut ctx = mux_fut.poll_with(mt.new_context()).await?;
101
102        // Receives protocol configuration from prover to perform compatibility check.
103        let protocol_config = mux_fut
104            .poll_with(async {
105                let peer_configuration: ProtocolConfig = ctx.io_mut().expect_next().await?;
106                self.config
107                    .protocol_config_validator()
108                    .validate(&peer_configuration)?;
109
110                Ok::<_, VerifierError>(peer_configuration)
111            })
112            .await?;
113
114        let delta = Delta::random(&mut rand::rng());
115        let (vm, mut mpc_tls) = build_mpc_tls(&self.config, &protocol_config, delta, ctx);
116
117        // Allocate resources for MPC-TLS in VM.
118        let mut keys = mpc_tls.alloc()?;
119        translate_keys(&mut keys, &vm.try_lock().expect("VM is not locked"))?;
120
121        // Allocate for committing to plaintext.
122        let mut zk_aes = ZkAesCtr::new(Role::Verifier);
123        zk_aes.set_key(keys.server_write_key, keys.server_write_iv);
124        zk_aes.alloc(
125            &mut (*vm.try_lock().expect("VM is not locked").zk()),
126            protocol_config.max_recv_data(),
127        )?;
128
129        debug!("setting up mpc-tls");
130
131        mux_fut.poll_with(mpc_tls.preprocess()).await?;
132
133        debug!("mpc-tls setup complete");
134
135        Ok(Verifier {
136            config: self.config,
137            span: self.span,
138            state: state::Setup {
139                mux_ctrl,
140                mux_fut,
141                mt,
142                delta,
143                mpc_tls,
144                zk_aes,
145                _keys: keys,
146                vm,
147            },
148        })
149    }
150
151    /// Runs the TLS verifier to completion, notarizing the TLS session.
152    ///
153    /// This is a convenience method which runs all the steps needed for
154    /// notarization.
155    ///
156    /// # Arguments
157    ///
158    /// * `socket` - The socket to the prover.
159    /// * `config` - The attestation configuration.
160    #[instrument(parent = &self.span, level = "info", skip_all, err)]
161    pub async fn notarize<S: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
162        self,
163        socket: S,
164        config: &AttestationConfig,
165    ) -> Result<Attestation, VerifierError> {
166        self.setup(socket)
167            .await?
168            .run()
169            .await?
170            .start_notarize()
171            .finalize(config)
172            .await
173    }
174
175    /// Runs the TLS verifier to completion, verifying the TLS session.
176    ///
177    /// This is a convenience method which runs all the steps needed for
178    /// verification.
179    ///
180    /// # Arguments
181    ///
182    /// * `socket` - The socket to the prover.
183    #[instrument(parent = &self.span, level = "info", skip_all, err)]
184    pub async fn verify<S: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
185        self,
186        socket: S,
187    ) -> Result<(PartialTranscript, SessionInfo), VerifierError> {
188        let mut verifier = self.setup(socket).await?.run().await?.start_verify();
189        let transcript = verifier.receive().await?;
190
191        let session_info = verifier.finalize().await?;
192        Ok((transcript, session_info))
193    }
194}
195
196impl Verifier<state::Setup> {
197    /// Runs the verifier until the TLS connection is closed.
198    #[instrument(parent = &self.span, level = "info", skip_all, err)]
199    pub async fn run(self) -> Result<Verifier<state::Closed>, VerifierError> {
200        let state::Setup {
201            mux_ctrl,
202            mut mux_fut,
203            mt,
204            delta,
205            mpc_tls,
206            mut zk_aes,
207            vm,
208            ..
209        } = self.state;
210
211        let start_time = SystemTime::now()
212            .duration_since(UNIX_EPOCH)
213            .expect("system time should be available")
214            .as_secs();
215
216        info!("starting MPC-TLS");
217
218        let (
219            mut ctx,
220            FollowerData {
221                server_key,
222                mut transcript,
223                keys,
224            },
225        ) = mux_fut.poll_with(mpc_tls.run()).await?;
226
227        info!("finished MPC-TLS");
228
229        {
230            let mut vm = vm.try_lock().expect("VM should not be locked");
231
232            translate_transcript(&mut transcript, &vm)?;
233
234            // Prepare for the prover to prove received plaintext.
235            let proof = commit_records(
236                &mut (*vm.zk()),
237                &mut zk_aes,
238                transcript
239                    .recv
240                    .iter_mut()
241                    .filter(|record| record.typ == ContentType::ApplicationData),
242            )
243            .map_err(VerifierError::zk)?;
244
245            debug!("finalizing mpc");
246
247            // Finalize DEAP and execute the plaintext proofs.
248            mux_fut
249                .poll_with(vm.finalize(&mut ctx))
250                .await
251                .map_err(VerifierError::mpc)?;
252
253            debug!("mpc finalized");
254
255            // Verify the plaintext proofs.
256            proof.verify().map_err(VerifierError::zk)?;
257        }
258
259        let sent = transcript
260            .sent
261            .iter()
262            .filter(|record| record.typ == ContentType::ApplicationData)
263            .map(|record| record.ciphertext.len())
264            .sum::<usize>() as u32;
265        let received = transcript
266            .recv
267            .iter()
268            .filter(|record| record.typ == ContentType::ApplicationData)
269            .map(|record| record.ciphertext.len())
270            .sum::<usize>() as u32;
271
272        let transcript_refs = transcript
273            .to_transcript_refs()
274            .expect("transcript should be complete");
275
276        let connection_info = ConnectionInfo {
277            time: start_time,
278            version: TlsVersion::V1_2,
279            transcript_length: TranscriptLength { sent, received },
280        };
281
282        // Pull out ZK VM.
283        let (_, vm) = Arc::into_inner(vm)
284            .expect("vm should have only 1 reference")
285            .into_inner()
286            .into_inner();
287
288        Ok(Verifier {
289            config: self.config,
290            span: self.span,
291            state: state::Closed {
292                mux_ctrl,
293                mux_fut,
294                mt,
295                delta,
296                ctx,
297                keys,
298                vm,
299                server_ephemeral_key: server_key
300                    .try_into()
301                    .expect("only supported key type should have been accepted"),
302                connection_info,
303                transcript_refs,
304            },
305        })
306    }
307}
308
309impl Verifier<state::Closed> {
310    /// Starts notarization of the TLS session.
311    ///
312    /// If the verifier is a Notary, this function will transition the verifier
313    /// to the next state where it can sign the prover's commitments to the
314    /// transcript.
315    pub fn start_notarize(self) -> Verifier<Notarize> {
316        Verifier {
317            config: self.config,
318            span: self.span,
319            state: self.state.into(),
320        }
321    }
322
323    /// Starts verification of the TLS session.
324    ///
325    /// This function transitions the verifier into a state where it can verify
326    /// the contents of the transcript.
327    pub fn start_verify(self) -> Verifier<Verify> {
328        Verifier {
329            config: self.config,
330            span: self.span,
331            state: self.state.into(),
332        }
333    }
334}
335
336fn build_mpc_tls(
337    config: &VerifierConfig,
338    protocol_config: &ProtocolConfig,
339    delta: Delta,
340    ctx: Context,
341) -> (Arc<Mutex<Deap<Mpc, Zk>>>, MpcTlsFollower) {
342    let mut rng = rand::rng();
343
344    let base_ot_send = mpz_ot::chou_orlandi::Sender::default();
345    let base_ot_recv = mpz_ot::chou_orlandi::Receiver::default();
346    let rcot_send = mpz_ot::kos::Sender::new(
347        mpz_ot::kos::SenderConfig::default(),
348        delta.into_inner(),
349        base_ot_recv,
350    );
351    let rcot_send = mpz_ot::ferret::Sender::new(
352        mpz_ot::ferret::FerretConfig::builder()
353            .lpn_type(mpz_ot::ferret::LpnType::Regular)
354            .build()
355            .expect("ferret config is valid"),
356        Block::random(&mut rng),
357        rcot_send,
358    );
359    let rcot_recv =
360        mpz_ot::kos::Receiver::new(mpz_ot::kos::ReceiverConfig::default(), base_ot_send);
361
362    let rcot_send = mpz_ot::rcot::shared::SharedRCOTSender::new(rcot_send);
363    let rcot_recv = mpz_ot::rcot::shared::SharedRCOTReceiver::new(rcot_recv);
364
365    let mpc = Mpc::new(mpz_ot::cot::DerandCOTReceiver::new(rcot_recv.clone()));
366
367    let zk = Zk::new(delta, rcot_send.clone());
368
369    let vm = Arc::new(Mutex::new(Deap::new(tlsn_deap::Role::Follower, mpc, zk)));
370
371    (
372        vm.clone(),
373        MpcTlsFollower::new(
374            config.build_mpc_tls_config(protocol_config),
375            ctx,
376            vm,
377            rcot_send,
378            (rcot_recv.clone(), rcot_recv.clone(), rcot_recv),
379        ),
380    )
381}
382
383/// Translates VM references to the ZK address space.
384fn translate_keys<Mpc, Zk>(
385    keys: &mut SessionKeys,
386    vm: &Deap<Mpc, Zk>,
387) -> Result<(), VerifierError> {
388    keys.client_write_key = vm
389        .translate(keys.client_write_key)
390        .map_err(VerifierError::mpc)?;
391    keys.client_write_iv = vm
392        .translate(keys.client_write_iv)
393        .map_err(VerifierError::mpc)?;
394    keys.server_write_key = vm
395        .translate(keys.server_write_key)
396        .map_err(VerifierError::mpc)?;
397    keys.server_write_iv = vm
398        .translate(keys.server_write_iv)
399        .map_err(VerifierError::mpc)?;
400
401    Ok(())
402}
403
404/// Translates VM references to the ZK address space.
405fn translate_transcript<Mpc, Zk>(
406    transcript: &mut TlsTranscript,
407    vm: &Deap<Mpc, Zk>,
408) -> Result<(), VerifierError> {
409    for Record { plaintext_ref, .. } in transcript.sent.iter_mut().chain(transcript.recv.iter_mut())
410    {
411        if let Some(plaintext_ref) = plaintext_ref.as_mut() {
412            *plaintext_ref = vm.translate(*plaintext_ref).map_err(VerifierError::mpc)?;
413        }
414    }
415
416    Ok(())
417}