mod builder;
mod config;
mod proof;
use std::fmt;
use rand::distributions::{Distribution, Standard};
use serde::{Deserialize, Serialize};
use crate::{
connection::{ConnectionInfo, ServerCertCommitment, ServerEphemKey},
hash::{impl_domain_separator, Hash, HashAlgorithm, HashAlgorithmExt, TypedHash},
index::Index,
merkle::MerkleTree,
presentation::PresentationBuilder,
signing::{Signature, VerifyingKey},
transcript::{encoding::EncodingCommitment, hash::PlaintextHash},
CryptoProvider,
};
pub use builder::{AttestationBuilder, AttestationBuilderError};
pub use config::{AttestationConfig, AttestationConfigBuilder, AttestationConfigError};
pub use proof::{AttestationError, AttestationProof};
pub const VERSION: Version = Version(0);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Uid(pub [u8; 16]);
impl From<[u8; 16]> for Uid {
fn from(id: [u8; 16]) -> Self {
Self(id)
}
}
impl Distribution<Uid> for Standard {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Uid {
Uid(self.sample(rng))
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Version(u32);
impl_domain_separator!(Version);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Field<T> {
pub id: FieldId,
pub data: T,
}
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub struct FieldId(pub u32);
impl FieldId {
pub(crate) fn next<T>(&mut self, data: T) -> Field<T> {
let id = *self;
self.0 += 1;
Field { id, data }
}
}
impl fmt::Display for FieldId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u8)]
pub enum FieldKind {
ConnectionInfo = 0x01,
ServerEphemKey = 0x02,
ServerIdentityCommitment = 0x03,
EncodingCommitment = 0x04,
PlaintextHash = 0x05,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Header {
pub id: Uid,
pub version: Version,
pub root: TypedHash,
}
impl_domain_separator!(Header);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Body {
verifying_key: Field<VerifyingKey>,
connection_info: Field<ConnectionInfo>,
server_ephemeral_key: Field<ServerEphemKey>,
cert_commitment: Field<ServerCertCommitment>,
encoding_commitment: Option<Field<EncodingCommitment>>,
plaintext_hashes: Index<Field<PlaintextHash>>,
}
impl Body {
pub fn verifying_key(&self) -> &VerifyingKey {
&self.verifying_key.data
}
pub(crate) fn root(&self, hasher: &dyn HashAlgorithm) -> TypedHash {
let mut tree = MerkleTree::new(hasher.id());
let fields = self
.hash_fields(hasher)
.into_iter()
.map(|(_, hash)| hash)
.collect::<Vec<_>>();
tree.insert(hasher, fields);
tree.root()
}
pub(crate) fn hash_fields(&self, hasher: &dyn HashAlgorithm) -> Vec<(FieldId, Hash)> {
let Self {
verifying_key,
connection_info: conn_info,
server_ephemeral_key,
cert_commitment,
encoding_commitment,
plaintext_hashes,
} = self;
let mut fields: Vec<(FieldId, Hash)> = vec![
(verifying_key.id, hasher.hash_separated(&verifying_key.data)),
(conn_info.id, hasher.hash_separated(&conn_info.data)),
(
server_ephemeral_key.id,
hasher.hash_separated(&server_ephemeral_key.data),
),
(
cert_commitment.id,
hasher.hash_separated(&cert_commitment.data),
),
];
if let Some(encoding_commitment) = encoding_commitment {
fields.push((
encoding_commitment.id,
hasher.hash_separated(&encoding_commitment.data),
));
}
for field in plaintext_hashes.iter() {
fields.push((field.id, hasher.hash_separated(&field.data)));
}
fields.sort_by_key(|(id, _)| *id);
fields
}
pub(crate) fn connection_info(&self) -> &ConnectionInfo {
&self.connection_info.data
}
pub(crate) fn server_ephemeral_key(&self) -> &ServerEphemKey {
&self.server_ephemeral_key.data
}
pub(crate) fn cert_commitment(&self) -> &ServerCertCommitment {
&self.cert_commitment.data
}
pub(crate) fn encoding_commitment(&self) -> Option<&EncodingCommitment> {
self.encoding_commitment.as_ref().map(|field| &field.data)
}
pub(crate) fn plaintext_hashes(&self) -> &Index<Field<PlaintextHash>> {
&self.plaintext_hashes
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Attestation {
pub signature: Signature,
pub header: Header,
pub body: Body,
}
impl Attestation {
pub fn builder(config: &AttestationConfig) -> AttestationBuilder<'_> {
AttestationBuilder::new(config)
}
pub fn presentation_builder<'a>(
&'a self,
provider: &'a CryptoProvider,
) -> PresentationBuilder<'a> {
PresentationBuilder::new(provider, self)
}
}