tlsn_core/
attestation.rs

1//! Attestation types.
2//!
3//! An attestation is a cryptographically signed document issued by a Notary who
4//! witnessed a TLS connection. It contains various fields which can be used to
5//! verify statements about the connection and the associated application data.
6//!
7//! Attestations are comprised of two parts: a [`Header`] and a [`Body`].
8//!
9//! The header is the data structure which is signed by a Notary. It
10//! contains a unique identifier, the protocol version, and a Merkle root
11//! of the body fields.
12//!
13//! The body contains the fields of the attestation. These fields include data
14//! which can be used to verify aspects of a TLS connection, such as the
15//! server's identity, and facts about the transcript.
16//!
17//! # Extensions
18//!
19//! An attestation may be extended using [`Extension`] fields included in the
20//! body. Extensions (currently) have no canonical semantics, but may be used to
21//! implement application specific functionality.
22//!
23//! A Prover may [append
24//! extensions](crate::request::RequestConfigBuilder::extension)
25//! to their attestation request, provided that the Notary supports them
26//! (disallowed by default). A Notary may also be configured to
27//! [validate](crate::attestation::AttestationConfigBuilder::extension_validator)
28//! any extensions requested by a Prover using custom application logic.
29//! Additionally, a Notary may
30//! [include](crate::attestation::AttestationBuilder::extension)
31//! their own extensions.
32
33mod builder;
34mod config;
35mod extension;
36mod proof;
37
38use std::fmt;
39
40use rand::distr::{Distribution, StandardUniform};
41use serde::{Deserialize, Serialize};
42
43use crate::{
44    connection::{ConnectionInfo, ServerCertCommitment, ServerEphemKey},
45    hash::{impl_domain_separator, Hash, HashAlgorithm, HashAlgorithmExt, TypedHash},
46    merkle::MerkleTree,
47    presentation::PresentationBuilder,
48    signing::{Signature, VerifyingKey},
49    transcript::TranscriptCommitment,
50    CryptoProvider,
51};
52
53pub use builder::{AttestationBuilder, AttestationBuilderError};
54pub use config::{AttestationConfig, AttestationConfigBuilder, AttestationConfigError};
55pub use extension::{Extension, InvalidExtension};
56pub use proof::{AttestationError, AttestationProof};
57
58/// Current version of attestations.
59pub const VERSION: Version = Version(0);
60
61/// Unique identifier for an attestation.
62#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
63pub struct Uid(pub [u8; 16]);
64
65impl From<[u8; 16]> for Uid {
66    fn from(id: [u8; 16]) -> Self {
67        Self(id)
68    }
69}
70
71impl Distribution<Uid> for StandardUniform {
72    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Uid {
73        Uid(self.sample(rng))
74    }
75}
76
77/// Version of an attestation.
78#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
79pub struct Version(u32);
80
81impl_domain_separator!(Version);
82
83/// Public attestation field.
84#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct Field<T> {
86    /// Identifier of the field.
87    pub id: FieldId,
88    /// Field data.
89    pub data: T,
90}
91
92/// Identifier for a field.
93#[derive(
94    Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
95)]
96pub struct FieldId(pub u32);
97
98impl FieldId {
99    pub(crate) fn next<T>(&mut self, data: T) -> Field<T> {
100        let id = *self;
101        self.0 += 1;
102
103        Field { id, data }
104    }
105}
106
107impl fmt::Display for FieldId {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        write!(f, "{}", self.0)
110    }
111}
112
113/// Kind of an attestation field.
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
115#[repr(u8)]
116pub enum FieldKind {
117    /// Connection information.
118    ConnectionInfo = 0x01,
119    /// Server ephemeral key.
120    ServerEphemKey = 0x02,
121    /// Server identity commitment.
122    ServerIdentityCommitment = 0x03,
123    /// Encoding commitment.
124    EncodingCommitment = 0x04,
125    /// Plaintext hash commitment.
126    PlaintextHash = 0x05,
127}
128
129/// Attestation header.
130///
131/// See [module level documentation](crate::attestation) for more information.
132#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
133pub struct Header {
134    /// An identifier for the attestation.
135    pub id: Uid,
136    /// Version of the attestation.
137    pub version: Version,
138    /// Merkle root of the attestation fields.
139    pub root: TypedHash,
140}
141
142impl_domain_separator!(Header);
143
144/// Attestation body.
145///
146/// See [module level documentation](crate::attestation) for more information.
147#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct Body {
149    verifying_key: Field<VerifyingKey>,
150    connection_info: Field<ConnectionInfo>,
151    server_ephemeral_key: Field<ServerEphemKey>,
152    cert_commitment: Field<ServerCertCommitment>,
153    extensions: Vec<Field<Extension>>,
154    transcript_commitments: Vec<Field<TranscriptCommitment>>,
155}
156
157impl Body {
158    /// Returns an iterator over the extensions.
159    pub fn extensions(&self) -> impl Iterator<Item = &Extension> {
160        self.extensions.iter().map(|field| &field.data)
161    }
162
163    /// Returns the attestation verifying key.
164    pub fn verifying_key(&self) -> &VerifyingKey {
165        &self.verifying_key.data
166    }
167
168    /// Computes the Merkle root of the attestation fields.
169    ///
170    /// This is only used when building an attestation.
171    pub(crate) fn root(&self, hasher: &dyn HashAlgorithm) -> TypedHash {
172        let mut tree = MerkleTree::new(hasher.id());
173        let fields = self
174            .hash_fields(hasher)
175            .into_iter()
176            .map(|(_, hash)| hash)
177            .collect::<Vec<_>>();
178        tree.insert(hasher, fields);
179        tree.root()
180    }
181
182    /// Returns the fields of the body hashed and sorted by id.
183    ///
184    /// Each field is hashed with a domain separator to mitigate type confusion
185    /// attacks.
186    ///
187    /// # Note
188    ///
189    /// The order of fields is not stable across versions.
190    pub(crate) fn hash_fields(&self, hasher: &dyn HashAlgorithm) -> Vec<(FieldId, Hash)> {
191        // CRITICAL: ensure all fields are included! If a new field is added to the
192        // struct without including it here, it will not be included in the attestation.
193        let Self {
194            verifying_key,
195            connection_info: conn_info,
196            server_ephemeral_key,
197            cert_commitment,
198            extensions,
199            transcript_commitments,
200        } = self;
201
202        let mut fields: Vec<(FieldId, Hash)> = vec![
203            (verifying_key.id, hasher.hash_separated(&verifying_key.data)),
204            (conn_info.id, hasher.hash_separated(&conn_info.data)),
205            (
206                server_ephemeral_key.id,
207                hasher.hash_separated(&server_ephemeral_key.data),
208            ),
209            (
210                cert_commitment.id,
211                hasher.hash_separated(&cert_commitment.data),
212            ),
213        ];
214
215        for field in extensions.iter() {
216            fields.push((field.id, hasher.hash_separated(&field.data)));
217        }
218
219        for field in transcript_commitments.iter() {
220            fields.push((field.id, hasher.hash_separated(&field.data)));
221        }
222
223        fields.sort_by_key(|(id, _)| *id);
224        fields
225    }
226
227    /// Returns the connection information.
228    pub(crate) fn connection_info(&self) -> &ConnectionInfo {
229        &self.connection_info.data
230    }
231
232    /// Returns the server's ephemeral public key.
233    pub(crate) fn server_ephemeral_key(&self) -> &ServerEphemKey {
234        &self.server_ephemeral_key.data
235    }
236
237    /// Returns the commitment to a server certificate.
238    pub(crate) fn cert_commitment(&self) -> &ServerCertCommitment {
239        &self.cert_commitment.data
240    }
241
242    /// Returns the transcript commitments.
243    pub(crate) fn transcript_commitments(&self) -> impl Iterator<Item = &TranscriptCommitment> {
244        self.transcript_commitments.iter().map(|field| &field.data)
245    }
246}
247
248/// An attestation document.
249///
250/// See [module level documentation](crate::attestation) for more information.
251#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct Attestation {
253    /// The signature of the attestation.
254    pub signature: Signature,
255    /// The attestation header.
256    pub header: Header,
257    /// The attestation body.
258    pub body: Body,
259}
260
261impl Attestation {
262    /// Returns an attestation builder.
263    pub fn builder(config: &AttestationConfig) -> AttestationBuilder<'_> {
264        AttestationBuilder::new(config)
265    }
266
267    /// Returns a presentation builder.
268    pub fn presentation_builder<'a>(
269        &'a self,
270        provider: &'a CryptoProvider,
271    ) -> PresentationBuilder<'a> {
272        PresentationBuilder::new(provider, self)
273    }
274}