1use blake3::Hasher;
2use rand_core::OsRng;
3
4use ed25519_dalek::{SecretKey, SigningKey, VerifyingKey};
5use x25519_dalek::{PublicKey, StaticSecret};
6
7use postcard;
8use serde::{Deserialize, Serialize};
9
10use zeroize::{ZeroizeOnDrop, Zeroize};
11
12use crate::Fingerprint;
13
14mod ser;
15
16#[derive(Serialize, Deserialize, ZeroizeOnDrop, Clone)]
40pub struct Cert {
41 #[serde(with = "ser::fpr")]
42 fpr: Fingerprint,
43 uid: String,
44 #[serde(with = "ser::enc_pub")]
45 encryption_public: PublicKey,
46 #[zeroize(skip)]
47 #[serde(with = "ser::sign_pub")]
48 signing_public: VerifyingKey,
49 #[serde(with = "ser::enc_sec")]
50 encryption_secret: Option<StaticSecret>,
51 #[serde(with = "ser::sign_sec")]
52 signing_secret: Option<SecretKey>,
53}
54
55impl Cert {
56 fn derive_fpr(uid: &str, enc_pub: &PublicKey, sig_pub: &VerifyingKey) -> Fingerprint {
57 let timestamp = crate::timestamp_bytes();
58 let mut hasher = Hasher::new();
59 hasher.update(uid.as_bytes());
60 hasher.update(enc_pub.as_bytes());
61 hasher.update(sig_pub.as_bytes());
62 hasher.update(×tamp);
63 hasher.finalize()
64 }
65
66 pub fn new(uid: &str) -> Self {
68 let signing_pair = SigningKey::generate(&mut OsRng);
69 let signing_public = signing_pair.verifying_key();
70 let encryption_secret = StaticSecret::random_from_rng(&mut OsRng);
71 let encryption_public = PublicKey::from(&encryption_secret);
72 let fpr = Self::derive_fpr(uid, &encryption_public, &signing_public);
73 Self {
74 fpr,
75 uid: uid.to_string(),
76 encryption_public,
77 signing_public,
78 encryption_secret: Some(encryption_secret),
79 signing_secret: Some(signing_pair.to_bytes()),
80 }
81 }
82
83 pub fn fingerprint(&self) -> &Fingerprint {
85 &self.fpr
86 }
87
88 pub fn user_id(&self) -> &str {
90 &self.uid
91 }
92
93 pub fn encryption_public(&self) -> &PublicKey {
95 &self.encryption_public
96 }
97
98 pub fn signing_public(&self) -> &VerifyingKey {
100 &self.signing_public
101 }
102
103 pub fn encryption_secret(&self) -> Option<&StaticSecret> {
105 self.encryption_secret.as_ref()
106 }
107
108 pub fn signing_secret(&self) -> Option<&SecretKey> {
110 self.signing_secret.as_ref()
111 }
112
113 pub fn is_secret(&self) -> bool {
115 self.encryption_secret.is_some() && self.signing_secret.is_some()
116 }
117
118 pub fn strip_secrets(mut self) -> Self {
121 self.encryption_secret = None;
122 self.signing_secret = None;
123 self
124 }
125}
126
127impl PartialEq for Cert {
128 fn eq(&self, other: &Self) -> bool {
129 let mut lhs = postcard::to_stdvec(self).unwrap();
130 let mut rhs = postcard::to_stdvec(other).unwrap();
131 let cmp = lhs == rhs;
132 lhs.zeroize();
133 rhs.zeroize();
134 cmp
135 }
136}
137
138impl std::fmt::Debug for Cert {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 let status = if self.is_secret() { "secret" } else { "public" };
141 write!(f, "({}) {}: <{}>", status, self.fpr.to_hex(), &self.uid)
142 }
143}
144
145impl std::fmt::Display for Cert {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 write!(f, "{}: <{}>", self.fpr.to_hex(), &self.uid)
148 }
149}
150
151#[cfg(test)]
152mod tests;