1mod 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
59pub const VERSION: Version = Version(0);
61
62#[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#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
80pub struct Version(u32);
81
82impl_domain_separator!(Version);
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct Field<T> {
87 pub id: FieldId,
89 pub data: T,
91}
92
93#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
116#[repr(u8)]
117pub enum FieldKind {
118 ConnectionInfo = 0x01,
120 ServerEphemKey = 0x02,
122 ServerIdentityCommitment = 0x03,
124 EncodingCommitment = 0x04,
126 PlaintextHash = 0x05,
128}
129
130#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
134pub struct Header {
135 pub id: Uid,
137 pub version: Version,
139 pub root: TypedHash,
141}
142
143impl_domain_separator!(Header);
144
145#[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 pub fn extensions(&self) -> impl Iterator<Item = &Extension> {
162 self.extensions.iter().map(|field| &field.data)
163 }
164
165 pub fn verifying_key(&self) -> &VerifyingKey {
167 &self.verifying_key.data
168 }
169
170 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 pub(crate) fn hash_fields(&self, hasher: &dyn HashAlgorithm) -> Vec<(FieldId, Hash)> {
193 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 pub(crate) fn connection_info(&self) -> &ConnectionInfo {
239 &self.connection_info.data
240 }
241
242 pub(crate) fn server_ephemeral_key(&self) -> &ServerEphemKey {
244 &self.server_ephemeral_key.data
245 }
246
247 pub(crate) fn cert_commitment(&self) -> &ServerCertCommitment {
249 &self.cert_commitment.data
250 }
251
252 pub(crate) fn encoding_commitment(&self) -> Option<&EncodingCommitment> {
254 self.encoding_commitment.as_ref().map(|field| &field.data)
255 }
256
257 pub(crate) fn plaintext_hashes(&self) -> &Index<Field<PlaintextHash>> {
259 &self.plaintext_hashes
260 }
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct Attestation {
268 pub signature: Signature,
270 pub header: Header,
272 pub body: Body,
274}
275
276impl Attestation {
277 pub fn builder(config: &AttestationConfig) -> AttestationBuilder<'_> {
279 AttestationBuilder::new(config)
280 }
281
282 pub fn presentation_builder<'a>(
284 &'a self,
285 provider: &'a CryptoProvider,
286 ) -> PresentationBuilder<'a> {
287 PresentationBuilder::new(provider, self)
288 }
289}