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    index::Index,
47    merkle::MerkleTree,
48    presentation::PresentationBuilder,
49    signing::{Signature, VerifyingKey},
50    transcript::{encoding::EncodingCommitment, hash::PlaintextHash},
51    CryptoProvider,
52};
53
54pub use builder::{AttestationBuilder, AttestationBuilderError};
55pub use config::{AttestationConfig, AttestationConfigBuilder, AttestationConfigError};
56pub use extension::{Extension, InvalidExtension};
57pub use proof::{AttestationError, AttestationProof};
58
59/// Current version of attestations.
60pub const VERSION: Version = Version(0);
61
62/// Unique identifier for an attestation.
63#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
64pub struct Uid(pub [u8; 16]);
65
66impl From<[u8; 16]> for Uid {
67    fn from(id: [u8; 16]) -> Self {
68        Self(id)
69    }
70}
71
72impl Distribution<Uid> for StandardUniform {
73    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Uid {
74        Uid(self.sample(rng))
75    }
76}
77
78/// Version of an attestation.
79#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
80pub struct Version(u32);
81
82impl_domain_separator!(Version);
83
84/// Public attestation field.
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct Field<T> {
87    /// Identifier of the field.
88    pub id: FieldId,
89    /// Field data.
90    pub data: T,
91}
92
93/// Identifier for a field.
94#[derive(
95    Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
96)]
97pub struct FieldId(pub u32);
98
99impl FieldId {
100    pub(crate) fn next<T>(&mut self, data: T) -> Field<T> {
101        let id = *self;
102        self.0 += 1;
103
104        Field { id, data }
105    }
106}
107
108impl fmt::Display for FieldId {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        write!(f, "{}", self.0)
111    }
112}
113
114/// Kind of an attestation field.
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
116#[repr(u8)]
117pub enum FieldKind {
118    /// Connection information.
119    ConnectionInfo = 0x01,
120    /// Server ephemeral key.
121    ServerEphemKey = 0x02,
122    /// Server identity commitment.
123    ServerIdentityCommitment = 0x03,
124    /// Encoding commitment.
125    EncodingCommitment = 0x04,
126    /// Plaintext hash commitment.
127    PlaintextHash = 0x05,
128}
129
130/// Attestation header.
131///
132/// See [module level documentation](crate::attestation) for more information.
133#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
134pub struct Header {
135    /// An identifier for the attestation.
136    pub id: Uid,
137    /// Version of the attestation.
138    pub version: Version,
139    /// Merkle root of the attestation fields.
140    pub root: TypedHash,
141}
142
143impl_domain_separator!(Header);
144
145/// Attestation body.
146///
147/// See [module level documentation](crate::attestation) for more information.
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct Body {
150    verifying_key: Field<VerifyingKey>,
151    connection_info: Field<ConnectionInfo>,
152    server_ephemeral_key: Field<ServerEphemKey>,
153    cert_commitment: Field<ServerCertCommitment>,
154    encoding_commitment: Option<Field<EncodingCommitment>>,
155    plaintext_hashes: Index<Field<PlaintextHash>>,
156    extensions: Vec<Field<Extension>>,
157}
158
159impl Body {
160    /// Returns an iterator over the extensions.
161    pub fn extensions(&self) -> impl Iterator<Item = &Extension> {
162        self.extensions.iter().map(|field| &field.data)
163    }
164
165    /// Returns the attestation verifying key.
166    pub fn verifying_key(&self) -> &VerifyingKey {
167        &self.verifying_key.data
168    }
169
170    /// Computes the Merkle root of the attestation fields.
171    ///
172    /// This is only used when building an attestation.
173    pub(crate) fn root(&self, hasher: &dyn HashAlgorithm) -> TypedHash {
174        let mut tree = MerkleTree::new(hasher.id());
175        let fields = self
176            .hash_fields(hasher)
177            .into_iter()
178            .map(|(_, hash)| hash)
179            .collect::<Vec<_>>();
180        tree.insert(hasher, fields);
181        tree.root()
182    }
183
184    /// Returns the fields of the body hashed and sorted by id.
185    ///
186    /// Each field is hashed with a domain separator to mitigate type confusion
187    /// attacks.
188    ///
189    /// # Note
190    ///
191    /// The order of fields is not stable across versions.
192    pub(crate) fn hash_fields(&self, hasher: &dyn HashAlgorithm) -> Vec<(FieldId, Hash)> {
193        // CRITICAL: ensure all fields are included! If a new field is added to the
194        // struct without including it here, it will not be included in the attestation.
195        let Self {
196            verifying_key,
197            connection_info: conn_info,
198            server_ephemeral_key,
199            cert_commitment,
200            encoding_commitment,
201            plaintext_hashes,
202            extensions,
203        } = self;
204
205        let mut fields: Vec<(FieldId, Hash)> = vec![
206            (verifying_key.id, hasher.hash_separated(&verifying_key.data)),
207            (conn_info.id, hasher.hash_separated(&conn_info.data)),
208            (
209                server_ephemeral_key.id,
210                hasher.hash_separated(&server_ephemeral_key.data),
211            ),
212            (
213                cert_commitment.id,
214                hasher.hash_separated(&cert_commitment.data),
215            ),
216        ];
217
218        if let Some(encoding_commitment) = encoding_commitment {
219            fields.push((
220                encoding_commitment.id,
221                hasher.hash_separated(&encoding_commitment.data),
222            ));
223        }
224
225        for field in plaintext_hashes.iter() {
226            fields.push((field.id, hasher.hash_separated(&field.data)));
227        }
228
229        for field in extensions.iter() {
230            fields.push((field.id, hasher.hash_separated(&field.data)));
231        }
232
233        fields.sort_by_key(|(id, _)| *id);
234        fields
235    }
236
237    /// Returns the connection information.
238    pub(crate) fn connection_info(&self) -> &ConnectionInfo {
239        &self.connection_info.data
240    }
241
242    /// Returns the server's ephemeral public key.
243    pub(crate) fn server_ephemeral_key(&self) -> &ServerEphemKey {
244        &self.server_ephemeral_key.data
245    }
246
247    /// Returns the commitment to a server certificate.
248    pub(crate) fn cert_commitment(&self) -> &ServerCertCommitment {
249        &self.cert_commitment.data
250    }
251
252    /// Returns the encoding commitment.
253    pub(crate) fn encoding_commitment(&self) -> Option<&EncodingCommitment> {
254        self.encoding_commitment.as_ref().map(|field| &field.data)
255    }
256
257    /// Returns the plaintext hash commitments.
258    pub(crate) fn plaintext_hashes(&self) -> &Index<Field<PlaintextHash>> {
259        &self.plaintext_hashes
260    }
261}
262
263/// An attestation document.
264///
265/// See [module level documentation](crate::attestation) for more information.
266#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct Attestation {
268    /// The signature of the attestation.
269    pub signature: Signature,
270    /// The attestation header.
271    pub header: Header,
272    /// The attestation body.
273    pub body: Body,
274}
275
276impl Attestation {
277    /// Returns an attestation builder.
278    pub fn builder(config: &AttestationConfig) -> AttestationBuilder<'_> {
279        AttestationBuilder::new(config)
280    }
281
282    /// Returns a presentation builder.
283    pub fn presentation_builder<'a>(
284        &'a self,
285        provider: &'a CryptoProvider,
286    ) -> PresentationBuilder<'a> {
287        PresentationBuilder::new(provider, self)
288    }
289}