# verify.rs - source
Source of the Rust file `crates/tlsn/src/verifier/verify.rs`.

Skip to main content

#
tlsn/verifier/
verify.rs

    1use mpc_tls::SessionKeys;
    2use mpz_common::Context;
    3use mpz_memory_core::binary::Binary;
    4use mpz_vm_core::Vm;
    5use rangeset::set::RangeSet;
    6use tlsn_core::{
    7    VerifierOutput,
    8    config::prove::ProveRequest,
    9    connection::{HandshakeData, ServerName},
    10    transcript::{
    11        ContentType, Direction, PartialTranscript, Record, TlsTranscript, TranscriptCommitment,
    12    },
    13    webpki::ServerCertVerifier,
    14};
    15
    16use crate::{
    17    Error, Result,
    18    transcript_internal::{TranscriptRefs, auth::verify_plaintext, commit::hash::verify_hash},
    19};
    20
    21#[allow(clippy::too_many_arguments)]
    22pub(crate) async fn verify<T: Vm<Binary> + Send + Sync>(
    23    ctx: &mut Context,
    24    vm: &mut T,
    25    keys: &SessionKeys,
    26    cert_verifier: &ServerCertVerifier,
    27    tls_transcript: &TlsTranscript,
    28    request: ProveRequest,
    29    handshake: Option<(ServerName, HandshakeData)>,
    30    transcript: Option<PartialTranscript>,
    31) -> Result<VerifierOutput> {
    32    let ciphertext_sent = collect_ciphertext(tls_transcript.sent());
    33    let ciphertext_recv = collect_ciphertext(tls_transcript.recv());
    34
    35    let transcript = if let Some((auth_sent, auth_recv)) = request.reveal() {
    36        let Some(transcript) = transcript else {
    37            return Err(Error::internal().with_msg(
    38                "verification failed: prover requested to reveal data but did not send transcript",
    39            ));
    40        };
    41
    42        if transcript.len_sent() != ciphertext_sent.len()
    43            || transcript.len_received() != ciphertext_recv.len()
    44        {
    45            return Err(
    46                Error::internal().with_msg("verification failed: transcript length mismatch")
    47            );
    48        }
    49
    50        if transcript.sent_authed() != auth_sent {
    51            return Err(Error::internal().with_msg("verification failed: sent auth data mismatch"));
    52        }
    53
    54        if transcript.received_authed() != auth_recv {
    55            return Err(
    56                Error::internal().with_msg("verification failed: received auth data mismatch")
    57            );
    58        }
    59
    60        transcript
    61    } else {
    62        PartialTranscript::new(ciphertext_sent.len(), ciphertext_recv.len())
    63    };
    64
    65    let server_name = if let Some((name, cert_data)) = handshake {
    66        cert_data
    67            .verify(
    68                cert_verifier,
    69                tls_transcript.time(),
    70                tls_transcript.server_ephemeral_key(),
    71                &name,
    72            )
    73            .map_err(|e| {
    74                Error::internal()
    75                    .with_msg("verification failed: certificate verification failed")
    76                    .with_source(e)
    77            })?;
    78
    79        Some(name)
    80    } else {
    81        None
    82    };
    83
    84    let (mut commit_sent, mut commit_recv) = (RangeSet::default(), RangeSet::default());
    85    if let Some(commit_config) = request.transcript_commit() {
    86        commit_config
    87            .iter_hash()
    88            .for_each(|(direction, idx, _)| match direction {
    89                Direction::Sent => commit_sent.union_mut(idx),
    90                Direction::Received => commit_recv.union_mut(idx),
    91            });
    92    }
    93
    94    let (sent_refs, sent_proof) = verify_plaintext(
    95        vm,
    96        keys.client_write_key,
    97        keys.client_write_iv,
    98        transcript.sent_unsafe(),
    99        &ciphertext_sent,
    100        tls_transcript
    101            .sent()
    102            .iter()
    103            .filter(|record| record.typ == ContentType::ApplicationData),
    104        transcript.sent_authed(),
    105        &commit_sent,
    106    )
    107    .map_err(|e| {
    108        Error::internal()
    109            .with_msg("verification failed during sent plaintext verification")
    110            .with_source(e)
    111    })?;
    112    let (recv_refs, recv_proof) = verify_plaintext(
    113        vm,
    114        keys.server_write_key,
    115        keys.server_write_iv,
    116        transcript.received_unsafe(),
    117        &ciphertext_recv,
    118        tls_transcript
    119            .recv()
    120            .iter()
    121            .filter(|record| record.typ == ContentType::ApplicationData),
    122        transcript.received_authed(),
    123        &commit_recv,
    124    )
    125    .map_err(|e| {
    126        Error::internal()
    127            .with_msg("verification failed during received plaintext verification")
    128            .with_source(e)
    129    })?;
    130
    131    let transcript_refs = TranscriptRefs {
    132        sent: sent_refs,
    133        recv: recv_refs,
    134    };
    135
    136    let mut transcript_commitments = Vec::new();
    137    let mut hash_commitments = None;
    138    if let Some(commit_config) = request.transcript_commit()
    139        && commit_config.has_hash()
    140    {
    141        hash_commitments = Some(
    142            verify_hash(vm, &transcript_refs, commit_config.iter_hash().cloned()).map_err(|e| {
    143                Error::internal()
    144                    .with_msg("verification failed during hash commitment setup")
    145                    .with_source(e)
    146            })?,
    147        );
    148    }
    149
    150    vm.execute_all(ctx).await.map_err(|e| {
    151        Error::internal()
    152            .with_msg("verification failed during zk execution")
    153            .with_source(e)
    154    })?;
    155
    156    sent_proof.verify().map_err(|e| {
    157        Error::internal()
    158            .with_msg("verification failed: sent plaintext proof invalid")
    159            .with_source(e)
    160    })?;
    161    recv_proof.verify().map_err(|e| {
    162        Error::internal()
    163            .with_msg("verification failed: received plaintext proof invalid")
    164            .with_source(e)
    165    })?;
    166
    167    if let Some(hash_commitments) = hash_commitments {
    168        for commitment in hash_commitments.try_recv().map_err(|e| {
    169            Error::internal()
    170                .with_msg("verification failed during hash commitment finalization")
    171                .with_source(e)
    172        })? {
    173            transcript_commitments.push(TranscriptCommitment::Hash(commitment));
    174        }
    175    }
    176
    177    Ok(VerifierOutput {
    178        server_name,
    179        transcript: request.reveal().is_some().then_some(transcript),
    180        transcript_commitments,
    181    })
    182}
    183
    184fn collect_ciphertext<'a>(records: impl IntoIterator<Item = &'a Record>) -> Vec<u8> {
    185    let mut ciphertext = Vec::new();
    186    records
    187        .into_iter()
    188        .filter(|record| record.typ == ContentType::ApplicationData)
    189        .for_each(|record| {
    190            ciphertext.extend_from_slice(&record.ciphertext);
    191        });
    192    ciphertext
    193}