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;
9pub mod state;
10
11use std::sync::Arc;
12
13pub use config::{VerifierConfig, VerifierConfigBuilder, VerifierConfigBuilderError};
14pub use error::VerifierError;
15pub use tlsn_core::{VerifierOutput, VerifyConfig, VerifyConfigBuilder, VerifyConfigBuilderError};
16
17use futures::{AsyncRead, AsyncWrite, TryFutureExt};
18use mpc_tls::{FollowerData, MpcTlsFollower, SessionKeys};
19use mpz_common::Context;
20use mpz_core::Block;
21use mpz_garble_core::Delta;
22use mpz_vm_core::prelude::*;
23use serio::{stream::IoStreamExt, SinkExt};
24use tls_core::msgs::enums::ContentType;
25use tlsn_common::{
26    commit::{commit_records, hash::verify_hash},
27    config::ProtocolConfig,
28    context::build_mt_context,
29    encoding,
30    mux::attach_mux,
31    tag::verify_tags,
32    transcript::{decode_transcript, verify_transcript, Record, TlsTranscript},
33    zk_aes_ctr::ZkAesCtr,
34    Role,
35};
36use tlsn_core::{
37    attestation::{Attestation, AttestationConfig},
38    connection::{ConnectionInfo, ServerName, TlsVersion, TranscriptLength},
39    request::Request,
40    transcript::TranscriptCommitment,
41    ProvePayload,
42};
43use tlsn_deap::Deap;
44use tokio::sync::Mutex;
45use web_time::{SystemTime, UNIX_EPOCH};
46
47use tracing::{debug, info, info_span, instrument, Span};
48
49pub(crate) type RCOTSender = mpz_ot::rcot::shared::SharedRCOTSender<
50    mpz_ot::ferret::Sender<mpz_ot::kos::Sender<mpz_ot::chou_orlandi::Receiver>>,
51    mpz_core::Block,
52>;
53pub(crate) type RCOTReceiver = mpz_ot::rcot::shared::SharedRCOTReceiver<
54    mpz_ot::kos::Receiver<mpz_ot::chou_orlandi::Sender>,
55    bool,
56    mpz_core::Block,
57>;
58pub(crate) type Mpc =
59    mpz_garble::protocol::semihonest::Evaluator<mpz_ot::cot::DerandCOTReceiver<RCOTReceiver>>;
60pub(crate) type Zk = mpz_zk::Verifier<RCOTSender>;
61
62/// Information about the TLS session.
63#[derive(Debug)]
64pub struct SessionInfo {
65    /// Server's name.
66    pub server_name: ServerName,
67    /// Connection information.
68    pub connection_info: ConnectionInfo,
69}
70
71/// A Verifier instance.
72pub struct Verifier<T: state::VerifierState = state::Initialized> {
73    config: VerifierConfig,
74    span: Span,
75    state: T,
76}
77
78impl Verifier<state::Initialized> {
79    /// Creates a new verifier.
80    pub fn new(config: VerifierConfig) -> Self {
81        let span = info_span!("verifier");
82        Self {
83            config,
84            span,
85            state: state::Initialized,
86        }
87    }
88
89    /// Sets up the verifier.
90    ///
91    /// This performs all MPC setup.
92    ///
93    /// # Arguments
94    ///
95    /// * `socket` - The socket to the prover.
96    #[instrument(parent = &self.span, level = "info", skip_all, err)]
97    pub async fn setup<S: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
98        self,
99        socket: S,
100    ) -> Result<Verifier<state::Setup>, VerifierError> {
101        let (mut mux_fut, mux_ctrl) = attach_mux(socket, Role::Verifier);
102        let mut mt = build_mt_context(mux_ctrl.clone());
103        let mut ctx = mux_fut.poll_with(mt.new_context()).await?;
104
105        // Receives protocol configuration from prover to perform compatibility check.
106        let protocol_config = mux_fut
107            .poll_with(async {
108                let peer_configuration: ProtocolConfig = ctx.io_mut().expect_next().await?;
109                self.config
110                    .protocol_config_validator()
111                    .validate(&peer_configuration)?;
112
113                Ok::<_, VerifierError>(peer_configuration)
114            })
115            .await?;
116
117        let delta = Delta::random(&mut rand::rng());
118        let (vm, mut mpc_tls) = build_mpc_tls(&self.config, &protocol_config, delta, ctx);
119
120        // Allocate resources for MPC-TLS in VM.
121        let mut keys = mpc_tls.alloc()?;
122        translate_keys(&mut keys, &vm.try_lock().expect("VM is not locked"))?;
123
124        // Allocate for committing to plaintext.
125        let mut zk_aes_ctr = ZkAesCtr::new(Role::Verifier);
126        zk_aes_ctr.set_key(keys.server_write_key, keys.server_write_iv);
127        zk_aes_ctr.alloc(
128            &mut (*vm.try_lock().expect("VM is not locked").zk()),
129            protocol_config.max_recv_data(),
130        )?;
131
132        debug!("setting up mpc-tls");
133
134        mux_fut.poll_with(mpc_tls.preprocess()).await?;
135
136        debug!("mpc-tls setup complete");
137
138        Ok(Verifier {
139            config: self.config,
140            span: self.span,
141            state: state::Setup {
142                mux_ctrl,
143                mux_fut,
144                delta,
145                mpc_tls,
146                zk_aes_ctr,
147                _keys: keys,
148                vm,
149            },
150        })
151    }
152
153    /// Runs the verifier to completion and attests to the TLS session.
154    ///
155    /// This is a convenience method which runs all the steps needed for
156    /// notarization.
157    ///
158    /// # Arguments
159    ///
160    /// * `socket` - The socket to the prover.
161    /// * `config` - The attestation configuration.
162    #[instrument(parent = &self.span, level = "info", skip_all, err)]
163    #[deprecated(
164        note = "attestation functionality will be removed from this API in future releases."
165    )]
166    pub async fn notarize<S: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
167        self,
168        socket: S,
169        config: &AttestationConfig,
170    ) -> Result<Attestation, VerifierError> {
171        let mut verifier = self.setup(socket).await?.run().await?;
172
173        #[allow(deprecated)]
174        let attestation = verifier.notarize(config).await?;
175
176        verifier.close().await?;
177
178        Ok(attestation)
179    }
180
181    /// Runs the TLS verifier to completion, verifying the TLS session.
182    ///
183    /// This is a convenience method which runs all the steps needed for
184    /// verification.
185    ///
186    /// # Arguments
187    ///
188    /// * `socket` - The socket to the prover.
189    #[instrument(parent = &self.span, level = "info", skip_all, err)]
190    pub async fn verify<S: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
191        self,
192        socket: S,
193        config: &VerifyConfig,
194    ) -> Result<VerifierOutput, VerifierError> {
195        let mut verifier = self.setup(socket).await?.run().await?;
196
197        let output = verifier.verify(config).await?;
198
199        verifier.close().await?;
200
201        Ok(output)
202    }
203}
204
205impl Verifier<state::Setup> {
206    /// Runs the verifier until the TLS connection is closed.
207    #[instrument(parent = &self.span, level = "info", skip_all, err)]
208    pub async fn run(self) -> Result<Verifier<state::Committed>, VerifierError> {
209        let state::Setup {
210            mux_ctrl,
211            mut mux_fut,
212            delta,
213            mpc_tls,
214            mut zk_aes_ctr,
215            vm,
216            ..
217        } = self.state;
218
219        let start_time = SystemTime::now()
220            .duration_since(UNIX_EPOCH)
221            .expect("system time should be available")
222            .as_secs();
223
224        info!("starting MPC-TLS");
225
226        let (
227            mut ctx,
228            FollowerData {
229                server_key,
230                mut transcript,
231                keys,
232                ..
233            },
234        ) = mux_fut.poll_with(mpc_tls.run()).await?;
235
236        info!("finished MPC-TLS");
237
238        {
239            let mut vm = vm.try_lock().expect("VM should not be locked");
240
241            translate_transcript(&mut transcript, &vm)?;
242
243            debug!("finalizing mpc");
244
245            mux_fut
246                .poll_with(vm.finalize(&mut ctx))
247                .await
248                .map_err(VerifierError::mpc)?;
249
250            debug!("mpc finalized");
251        }
252
253        // Pull out ZK VM.
254        let (_, mut vm) = Arc::into_inner(vm)
255            .expect("vm should have only 1 reference")
256            .into_inner()
257            .into_inner();
258
259        // Prepare for the prover to prove tag verification of the received
260        // records.
261        let tag_proof = verify_tags(
262            &mut vm,
263            (keys.server_write_key, keys.server_write_iv),
264            keys.server_write_mac_key,
265            transcript.recv.clone(),
266        )
267        .map_err(VerifierError::zk)?;
268
269        // Prepare for the prover to prove received plaintext.
270        let proof = commit_records(
271            &mut vm,
272            &mut zk_aes_ctr,
273            transcript
274                .recv
275                .iter_mut()
276                .filter(|record| record.typ == ContentType::ApplicationData),
277        )
278        .map_err(VerifierError::zk)?;
279
280        mux_fut
281            .poll_with(vm.execute_all(&mut ctx).map_err(VerifierError::zk))
282            .await?;
283
284        // Verify the tags.
285        // After the verification, the entire TLS trancript becomes
286        // authenticated from the verifier's perspective.
287        tag_proof.verify().map_err(VerifierError::zk)?;
288
289        // Verify the plaintext proofs.
290        proof.verify().map_err(VerifierError::zk)?;
291
292        let sent = transcript
293            .sent
294            .iter()
295            .filter(|record| record.typ == ContentType::ApplicationData)
296            .map(|record| record.ciphertext.len())
297            .sum::<usize>() as u32;
298        let received = transcript
299            .recv
300            .iter()
301            .filter(|record| record.typ == ContentType::ApplicationData)
302            .map(|record| record.ciphertext.len())
303            .sum::<usize>() as u32;
304
305        let transcript_refs = transcript
306            .to_transcript_refs()
307            .expect("transcript should be complete");
308
309        let connection_info = ConnectionInfo {
310            time: start_time,
311            version: TlsVersion::V1_2,
312            transcript_length: TranscriptLength { sent, received },
313        };
314
315        Ok(Verifier {
316            config: self.config,
317            span: self.span,
318            state: state::Committed {
319                mux_ctrl,
320                mux_fut,
321                delta,
322                ctx,
323                vm,
324                server_ephemeral_key: server_key
325                    .try_into()
326                    .expect("only supported key type should have been accepted"),
327                connection_info,
328                transcript_refs,
329            },
330        })
331    }
332}
333
334impl Verifier<state::Committed> {
335    /// Returns the connection information.
336    pub fn connection_info(&self) -> &ConnectionInfo {
337        &self.state.connection_info
338    }
339
340    /// Verifies information from the prover.
341    ///
342    /// # Arguments
343    ///
344    /// * `config` - Verification configuration.
345    #[instrument(parent = &self.span, level = "info", skip_all, err)]
346    pub async fn verify(
347        &mut self,
348        #[allow(unused_variables)] config: &VerifyConfig,
349    ) -> Result<VerifierOutput, VerifierError> {
350        let state::Committed {
351            mux_fut,
352            ctx,
353            delta,
354            vm,
355            connection_info,
356            server_ephemeral_key,
357            transcript_refs,
358            ..
359        } = &mut self.state;
360
361        let ProvePayload {
362            server_identity,
363            transcript,
364            transcript_commit,
365        } = mux_fut
366            .poll_with(ctx.io_mut().expect_next().map_err(VerifierError::from))
367            .await?;
368
369        let server_name = if let Some((name, cert_data)) = server_identity {
370            cert_data
371                .verify_with_provider(
372                    self.config.crypto_provider(),
373                    connection_info.time,
374                    server_ephemeral_key,
375                    &name,
376                )
377                .map_err(VerifierError::verify)?;
378
379            Some(name)
380        } else {
381            None
382        };
383
384        if let Some(partial_transcript) = &transcript {
385            // Check ranges.
386            if partial_transcript.len_sent() != connection_info.transcript_length.sent as usize
387                || partial_transcript.len_received()
388                    != connection_info.transcript_length.received as usize
389            {
390                return Err(VerifierError::verify(
391                    "prover sent transcript with incorrect length",
392                ));
393            }
394
395            decode_transcript(
396                vm,
397                partial_transcript.sent_authed(),
398                partial_transcript.received_authed(),
399                transcript_refs,
400            )
401            .map_err(VerifierError::zk)?;
402        }
403
404        let mut transcript_commitments = Vec::new();
405        let mut hash_commitments = None;
406        if let Some(commit_config) = transcript_commit {
407            if commit_config.encoding() {
408                let commitment = mux_fut
409                    .poll_with(encoding::transfer(
410                        ctx,
411                        transcript_refs,
412                        delta,
413                        |plaintext| vm.get_keys(plaintext).expect("reference is valid"),
414                    ))
415                    .await?;
416
417                transcript_commitments.push(TranscriptCommitment::Encoding(commitment));
418            }
419
420            if commit_config.has_hash() {
421                hash_commitments = Some(
422                    verify_hash(vm, transcript_refs, commit_config.iter_hash().cloned())
423                        .map_err(VerifierError::verify)?,
424                );
425            }
426        }
427
428        mux_fut
429            .poll_with(vm.execute_all(ctx).map_err(VerifierError::zk))
430            .await?;
431
432        // Verify revealed data.
433        if let Some(partial_transcript) = &transcript {
434            verify_transcript(vm, partial_transcript, transcript_refs)
435                .map_err(VerifierError::verify)?;
436        }
437
438        if let Some(hash_commitments) = hash_commitments {
439            for commitment in hash_commitments.try_recv().map_err(VerifierError::verify)? {
440                transcript_commitments.push(TranscriptCommitment::Hash(commitment));
441            }
442        }
443
444        Ok(VerifierOutput {
445            server_name,
446            transcript,
447            transcript_commitments,
448        })
449    }
450
451    /// Attests to the TLS session.
452    ///
453    /// # Arguments
454    ///
455    /// * `config` - Attestation configuration.
456    #[instrument(parent = &self.span, level = "info", skip_all, err)]
457    #[deprecated(
458        note = "attestation functionality will be removed from this API in future releases."
459    )]
460    pub async fn notarize(
461        &mut self,
462        config: &AttestationConfig,
463    ) -> Result<Attestation, VerifierError> {
464        let VerifierOutput {
465            server_name,
466            transcript,
467            transcript_commitments,
468        } = self.verify(&VerifyConfig::default()).await?;
469
470        if server_name.is_some() {
471            return Err(VerifierError::attestation(
472                "server name can not be revealed to a notary",
473            ));
474        } else if transcript.is_some() {
475            return Err(VerifierError::attestation(
476                "transcript data can not be revealed to a notary",
477            ));
478        }
479
480        let state::Committed {
481            mux_fut,
482            ctx,
483            server_ephemeral_key,
484            connection_info,
485            ..
486        } = &mut self.state;
487
488        let request: Request = mux_fut
489            .poll_with(ctx.io_mut().expect_next().map_err(VerifierError::from))
490            .await?;
491
492        let mut builder = Attestation::builder(config)
493            .accept_request(request)
494            .map_err(VerifierError::attestation)?;
495
496        builder
497            .connection_info(connection_info.clone())
498            .server_ephemeral_key(server_ephemeral_key.clone())
499            .transcript_commitments(transcript_commitments);
500
501        let attestation = builder
502            .build(self.config.crypto_provider())
503            .map_err(VerifierError::attestation)?;
504
505        mux_fut
506            .poll_with(
507                ctx.io_mut()
508                    .send(attestation.clone())
509                    .map_err(VerifierError::from),
510            )
511            .await?;
512
513        info!("Sent attestation");
514
515        Ok(attestation)
516    }
517
518    /// Closes the connection with the prover.
519    #[instrument(parent = &self.span, level = "info", skip_all, err)]
520    pub async fn close(self) -> Result<(), VerifierError> {
521        let state::Committed {
522            mux_ctrl, mux_fut, ..
523        } = self.state;
524
525        // Wait for the prover to correctly close the connection.
526        if !mux_fut.is_complete() {
527            mux_ctrl.close();
528            mux_fut.await?;
529        }
530
531        Ok(())
532    }
533}
534
535fn build_mpc_tls(
536    config: &VerifierConfig,
537    protocol_config: &ProtocolConfig,
538    delta: Delta,
539    ctx: Context,
540) -> (Arc<Mutex<Deap<Mpc, Zk>>>, MpcTlsFollower) {
541    let mut rng = rand::rng();
542
543    let base_ot_send = mpz_ot::chou_orlandi::Sender::default();
544    let base_ot_recv = mpz_ot::chou_orlandi::Receiver::default();
545    let rcot_send = mpz_ot::kos::Sender::new(
546        mpz_ot::kos::SenderConfig::default(),
547        delta.into_inner(),
548        base_ot_recv,
549    );
550    let rcot_send = mpz_ot::ferret::Sender::new(
551        mpz_ot::ferret::FerretConfig::builder()
552            .lpn_type(mpz_ot::ferret::LpnType::Regular)
553            .build()
554            .expect("ferret config is valid"),
555        Block::random(&mut rng),
556        rcot_send,
557    );
558    let rcot_recv =
559        mpz_ot::kos::Receiver::new(mpz_ot::kos::ReceiverConfig::default(), base_ot_send);
560
561    let rcot_send = mpz_ot::rcot::shared::SharedRCOTSender::new(rcot_send);
562    let rcot_recv = mpz_ot::rcot::shared::SharedRCOTReceiver::new(rcot_recv);
563
564    let mpc = Mpc::new(mpz_ot::cot::DerandCOTReceiver::new(rcot_recv.clone()));
565
566    let zk = Zk::new(delta, rcot_send.clone());
567
568    let vm = Arc::new(Mutex::new(Deap::new(tlsn_deap::Role::Follower, mpc, zk)));
569
570    (
571        vm.clone(),
572        MpcTlsFollower::new(
573            config.build_mpc_tls_config(protocol_config),
574            ctx,
575            vm,
576            rcot_send,
577            (rcot_recv.clone(), rcot_recv.clone(), rcot_recv),
578        ),
579    )
580}
581
582/// Translates VM references to the ZK address space.
583fn translate_keys<Mpc, Zk>(
584    keys: &mut SessionKeys,
585    vm: &Deap<Mpc, Zk>,
586) -> Result<(), VerifierError> {
587    keys.client_write_key = vm
588        .translate(keys.client_write_key)
589        .map_err(VerifierError::mpc)?;
590    keys.client_write_iv = vm
591        .translate(keys.client_write_iv)
592        .map_err(VerifierError::mpc)?;
593    keys.server_write_key = vm
594        .translate(keys.server_write_key)
595        .map_err(VerifierError::mpc)?;
596    keys.server_write_iv = vm
597        .translate(keys.server_write_iv)
598        .map_err(VerifierError::mpc)?;
599    keys.server_write_mac_key = vm
600        .translate(keys.server_write_mac_key)
601        .map_err(VerifierError::mpc)?;
602
603    Ok(())
604}
605
606/// Translates VM references to the ZK address space.
607fn translate_transcript<Mpc, Zk>(
608    transcript: &mut TlsTranscript,
609    vm: &Deap<Mpc, Zk>,
610) -> Result<(), VerifierError> {
611    for Record { plaintext_ref, .. } in transcript.sent.iter_mut().chain(transcript.recv.iter_mut())
612    {
613        if let Some(plaintext_ref) = plaintext_ref.as_mut() {
614            *plaintext_ref = vm.translate(*plaintext_ref).map_err(VerifierError::mpc)?;
615        }
616    }
617
618    Ok(())
619}