tlsn_core/
lib.rs

1//! TLSNotary core library.
2//!
3//! # Introduction
4//!
5//! This library provides core functionality for the TLSNotary **attestation**
6//! protocol, including some more general types which are useful outside
7//! of attestations.
8//!
9//! Once the MPC-TLS protocol has been completed the Prover holds a collection
10//! of commitments pertaining to the TLS connection. Most importantly, the
11//! Prover is committed to the [`ServerName`](crate::connection::ServerName),
12//! and the [`Transcript`](crate::transcript::Transcript) of application data.
13//! Subsequently, the Prover can request an
14//! [`Attestation`](crate::attestation::Attestation) from the Notary who will
15//! include the commitments as well as any additional information which may be
16//! useful to an attestation Verifier.
17//!
18//! Holding an attestation, the Prover can construct a
19//! [`Presentation`](crate::presentation::Presentation) which facilitates
20//! selectively disclosing various aspects of the TLS connection to a Verifier.
21//! If the Verifier trusts the Notary, or more specifically the verifying key of
22//! the attestation, then the Verifier can trust the authenticity of the
23//! information disclosed in the presentation.
24//!
25//! **Be sure to check out the various submodules for more information.**
26//!
27//! # Committing to the transcript
28//!
29//! The MPC-TLS protocol produces commitments to the entire transcript of
30//! application data. However, we may want to disclose only a subset of the data
31//! in a presentation. Prior to attestation, the Prover has the opportunity to
32//! slice and dice the commitments into smaller sections which can be
33//! selectively disclosed. Additionally, the Prover may want to use different
34//! commitment schemes depending on the context they expect to disclose.
35//!
36//! The primary API for this process is the
37//! [`TranscriptCommitConfigBuilder`](crate::transcript::TranscriptCommitConfigBuilder)
38//! which is used to build up a configuration.
39//!
40//! Currently, only the
41//! [`Encoding`](crate::transcript::TranscriptCommitmentKind::Encoding)
42//! commitment kind is supported. In the future you will be able to acquire hash
43//! commitments directly to the transcript data.
44//!
45//! ```no_run
46//! # use tlsn_core::transcript::{TranscriptCommitConfigBuilder, Transcript, Direction};
47//! # use tlsn_core::hash::HashAlgId;
48//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
49//! # let transcript: Transcript = unimplemented!();
50//! let (sent_len, recv_len) = transcript.len();
51//!
52//! // Create a new configuration builder.
53//! let mut builder = TranscriptCommitConfigBuilder::new(&transcript);
54//!
55//! // Specify all the transcript commitments we want to make.
56//! builder
57//!     // Use BLAKE3 for encoding commitments.
58//!     .encoding_hash_alg(HashAlgId::BLAKE3)
59//!     // Commit to all sent data.
60//!     .commit_sent(&(0..sent_len))?
61//!     // Commit to the first 10 bytes of sent data.
62//!     .commit_sent(&(0..10))?
63//!     // Skip some bytes so it can be omitted in the presentation.
64//!     .commit_sent(&(20..sent_len))?
65//!     // Commit to all received data.
66//!     .commit_recv(&(0..recv_len))?;
67//!
68//! let config = builder.build()?;
69//! # Ok(())
70//! # }
71//! ```
72//!
73//! # Requesting an attestation
74//!
75//! The first step in the attestation protocol is for the Prover to make a
76//! [`Request`](crate::request::Request), which can be configured using the
77//! associated [builder](crate::request::RequestConfigBuilder). With it the
78//! Prover can configure some of the details of the attestation, such as which
79//! cryptographic algorithms are used (if the Notary supports them).
80//!
81//! The Prover may also request for extensions to be added to the attestation,
82//! see [here](crate::attestation#extensions) for more information.
83//!
84//! Upon being issued an attestation, the Prover will also hold a corresponding
85//! [`Secrets`] which contains all private information. This pair can be stored
86//! and used later to construct a
87//! [`Presentation`](crate::presentation::Presentation), [see
88//! below](#constructing-a-presentation).
89//!
90//! # Issuing an attestation
91//!
92//! Upon receiving a request, the Notary can issue an
93//! [`Attestation`](crate::attestation::Attestation) which can be configured
94//! using the associated
95//! [builder](crate::attestation::AttestationConfigBuilder).
96//!
97//! The Notary's [`CryptoProvider`] must be configured with an appropriate
98//! signing key for attestations. See
99//! [`SignerProvider`](crate::signing::SignerProvider) for more information.
100//!
101//! # Constructing a presentation
102//!
103//! A Prover can use an [`Attestation`](crate::attestation::Attestation) and the
104//! corresponding [`Secrets`] to construct a verifiable
105//! [`Presentation`](crate::presentation::Presentation).
106//!
107//! ```no_run
108//! # use tlsn_core::presentation::Presentation;
109//! # use tlsn_core::attestation::Attestation;
110//! # use tlsn_core::transcript::{TranscriptCommitmentKind, Direction};
111//! # use tlsn_core::{Secrets, CryptoProvider};
112//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
113//! # let attestation: Attestation = unimplemented!();
114//! # let secrets: Secrets = unimplemented!();
115//! # let crypto_provider: CryptoProvider = unimplemented!();
116//! let (_sent_len, recv_len) = secrets.transcript().len();
117//!
118//! // First, we decide which application data we would like to disclose.
119//! let mut builder = secrets.transcript_proof_builder();
120//!
121//! builder
122//!     // Use transcript encoding commitments.
123//!     .commitment_kinds(&[TranscriptCommitmentKind::Encoding])
124//!     // Disclose the first 10 bytes of the sent data.
125//!     .reveal(&(0..10), Direction::Sent)?
126//!     // Disclose all of the received data.
127//!     .reveal(&(0..recv_len), Direction::Received)?;
128//!
129//! let transcript_proof = builder.build()?;
130//!
131//! // Most cases we will also disclose the server identity.
132//! let identity_proof = secrets.identity_proof();
133//!
134//! // Now we can construct the presentation.
135//! let mut builder = attestation.presentation_builder(&crypto_provider);
136//!
137//! builder
138//!     .identity_proof(identity_proof)
139//!     .transcript_proof(transcript_proof);
140//!
141//! // Finally, we build the presentation. Send it to a verifier!
142//! let presentation: Presentation = builder.build()?;
143//! # Ok(())
144//! # }
145//! ```
146//!
147//! # Verifying a presentation
148//!
149//! Verifying a presentation is as simple as checking the verifier trusts the
150//! verifying key then calling
151//! [`Presentation::verify`](crate::presentation::Presentation::verify).
152//!
153//! ```no_run
154//! # use tlsn_core::presentation::{Presentation, PresentationOutput};
155//! # use tlsn_core::signing::VerifyingKey;
156//! # use tlsn_core::CryptoProvider;
157//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
158//! # let presentation: Presentation = unimplemented!();
159//! # let trusted_key: VerifyingKey = unimplemented!();
160//! # let crypto_provider: CryptoProvider = unimplemented!();
161//! // Assert that we trust the verifying key.
162//! assert_eq!(presentation.verifying_key(), &trusted_key);
163//!
164//! let PresentationOutput {
165//!     attestation,
166//!     server_name,
167//!     connection_info,
168//!     transcript,
169//!     ..
170//! } = presentation.verify(&crypto_provider)?;
171//! # Ok(())
172//! # }
173//! ```
174
175#![deny(missing_docs, unreachable_pub, unused_must_use)]
176#![deny(clippy::all)]
177#![forbid(unsafe_code)]
178
179pub mod attestation;
180pub mod connection;
181#[cfg(any(test, feature = "fixtures"))]
182pub mod fixtures;
183pub mod hash;
184pub(crate) mod merkle;
185pub mod presentation;
186mod provider;
187pub mod request;
188mod secrets;
189pub(crate) mod serialize;
190pub mod signing;
191pub mod transcript;
192
193pub use provider::CryptoProvider;
194pub use secrets::Secrets;
195
196use rangeset::ToRangeSet;
197use serde::{Deserialize, Serialize};
198
199use crate::{
200    connection::{ServerCertData, ServerName},
201    transcript::{
202        Direction, Idx, PartialTranscript, Transcript, TranscriptCommitConfig,
203        TranscriptCommitRequest, TranscriptCommitment, TranscriptSecret,
204    },
205};
206
207/// Configuration to prove information to the verifier.
208#[derive(Debug, Clone)]
209pub struct ProveConfig {
210    server_identity: bool,
211    transcript: Option<PartialTranscript>,
212    transcript_commit: Option<TranscriptCommitConfig>,
213}
214
215impl ProveConfig {
216    /// Creates a new builder.
217    pub fn builder(transcript: &Transcript) -> ProveConfigBuilder {
218        ProveConfigBuilder::new(transcript)
219    }
220
221    /// Returns `true` if the server identity is to be proven.
222    pub fn server_identity(&self) -> bool {
223        self.server_identity
224    }
225
226    /// Returns the transcript to be proven.
227    pub fn transcript(&self) -> Option<&PartialTranscript> {
228        self.transcript.as_ref()
229    }
230
231    /// Returns the transcript commitment configuration.
232    pub fn transcript_commit(&self) -> Option<&TranscriptCommitConfig> {
233        self.transcript_commit.as_ref()
234    }
235}
236
237/// Builder for [`ProveConfig`].
238#[derive(Debug)]
239pub struct ProveConfigBuilder<'a> {
240    transcript: &'a Transcript,
241    server_identity: bool,
242    reveal_sent: Idx,
243    reveal_recv: Idx,
244    transcript_commit: Option<TranscriptCommitConfig>,
245}
246
247impl<'a> ProveConfigBuilder<'a> {
248    /// Creates a new builder.
249    pub fn new(transcript: &'a Transcript) -> Self {
250        Self {
251            transcript,
252            server_identity: false,
253            reveal_sent: Idx::default(),
254            reveal_recv: Idx::default(),
255            transcript_commit: None,
256        }
257    }
258
259    /// Proves the server identity.
260    pub fn server_identity(&mut self) -> &mut Self {
261        self.server_identity = true;
262        self
263    }
264
265    /// Configures transcript commitments.
266    pub fn transcript_commit(&mut self, transcript_commit: TranscriptCommitConfig) -> &mut Self {
267        self.transcript_commit = Some(transcript_commit);
268        self
269    }
270
271    /// Reveals the given ranges of the transcript.
272    pub fn reveal(
273        &mut self,
274        direction: Direction,
275        ranges: &dyn ToRangeSet<usize>,
276    ) -> Result<&mut Self, ProveConfigBuilderError> {
277        let idx = Idx::new(ranges.to_range_set());
278
279        if idx.end() > self.transcript.len_of_direction(direction) {
280            return Err(ProveConfigBuilderError(
281                ProveConfigBuilderErrorRepr::IndexOutOfBounds {
282                    direction,
283                    actual: idx.end(),
284                    len: self.transcript.len_of_direction(direction),
285                },
286            ));
287        }
288
289        match direction {
290            Direction::Sent => self.reveal_sent.union_mut(&idx),
291            Direction::Received => self.reveal_recv.union_mut(&idx),
292        }
293        Ok(self)
294    }
295
296    /// Reveals the given ranges of the sent data transcript.
297    pub fn reveal_sent(
298        &mut self,
299        ranges: &dyn ToRangeSet<usize>,
300    ) -> Result<&mut Self, ProveConfigBuilderError> {
301        self.reveal(Direction::Sent, ranges)
302    }
303
304    /// Reveals the given ranges of the received data transcript.
305    pub fn reveal_recv(
306        &mut self,
307        ranges: &dyn ToRangeSet<usize>,
308    ) -> Result<&mut Self, ProveConfigBuilderError> {
309        self.reveal(Direction::Received, ranges)
310    }
311
312    /// Builds the configuration.
313    pub fn build(self) -> Result<ProveConfig, ProveConfigBuilderError> {
314        let transcript = if !self.reveal_sent.is_empty() || !self.reveal_recv.is_empty() {
315            Some(
316                self.transcript
317                    .to_partial(self.reveal_sent, self.reveal_recv),
318            )
319        } else {
320            None
321        };
322
323        Ok(ProveConfig {
324            server_identity: self.server_identity,
325            transcript,
326            transcript_commit: self.transcript_commit,
327        })
328    }
329}
330
331/// Error for [`ProveConfigBuilder`].
332#[derive(Debug, thiserror::Error)]
333#[error(transparent)]
334pub struct ProveConfigBuilderError(#[from] ProveConfigBuilderErrorRepr);
335
336#[derive(Debug, thiserror::Error)]
337enum ProveConfigBuilderErrorRepr {
338    #[error("range is out of bounds of the transcript ({direction}): {actual} > {len}")]
339    IndexOutOfBounds {
340        direction: Direction,
341        actual: usize,
342        len: usize,
343    },
344}
345
346/// Configuration to verify information from the prover.
347#[derive(Debug, Default, Clone)]
348pub struct VerifyConfig {}
349
350impl VerifyConfig {
351    /// Creates a new builder.
352    pub fn builder() -> VerifyConfigBuilder {
353        VerifyConfigBuilder::new()
354    }
355}
356
357/// Builder for [`VerifyConfig`].
358#[derive(Debug, Default)]
359pub struct VerifyConfigBuilder {}
360
361impl VerifyConfigBuilder {
362    /// Creates a new builder.
363    pub fn new() -> Self {
364        Self {}
365    }
366
367    /// Builds the configuration.
368    pub fn build(self) -> Result<VerifyConfig, VerifyConfigBuilderError> {
369        Ok(VerifyConfig {})
370    }
371}
372
373/// Error for [`VerifyConfigBuilder`].
374#[derive(Debug, thiserror::Error)]
375#[error(transparent)]
376pub struct VerifyConfigBuilderError(#[from] VerifyConfigBuilderErrorRepr);
377
378#[derive(Debug, thiserror::Error)]
379enum VerifyConfigBuilderErrorRepr {}
380
381/// Payload sent to the verifier.
382#[doc(hidden)]
383#[derive(Debug, Serialize, Deserialize)]
384pub struct ProvePayload {
385    /// Server identity data.
386    pub server_identity: Option<(ServerName, ServerCertData)>,
387    /// Transcript data.
388    pub transcript: Option<PartialTranscript>,
389    /// Transcript commitment configuration.
390    pub transcript_commit: Option<TranscriptCommitRequest>,
391}
392
393/// Prover output.
394pub struct ProverOutput {
395    /// Transcript commitments.
396    pub transcript_commitments: Vec<TranscriptCommitment>,
397    /// Transcript commitment secrets.
398    pub transcript_secrets: Vec<TranscriptSecret>,
399}
400
401opaque_debug::implement!(ProverOutput);
402
403/// Verifier output.
404pub struct VerifierOutput {
405    /// Server identity.
406    pub server_name: Option<ServerName>,
407    /// Transcript data.
408    pub transcript: Option<PartialTranscript>,
409    /// Transcript commitments.
410    pub transcript_commitments: Vec<TranscriptCommitment>,
411}
412
413opaque_debug::implement!(VerifierOutput);