pyrus_crypto/cert/
mod.rs

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/// The certificate struct. It is either public or secret.
17///
18/// Public certificate means that it does not contain secret keys. Whereas 
19/// a secret certificate is fully featured - it contains secret keys and 
20/// public keys.
21///
22/// Each certificate has a user id ([`Cert::user_id`]) and a 
23/// (hopefully) unique fingerprint ([`Cert::fingerprint`]).
24///
25/// # Serialization
26///
27/// Certificates implement [`serde::Serialize`] and [`serde::Deserialize`] 
28/// traits, which allow for serialization and deserialization using any 
29/// serde format. Additionally for human readable formats certificates 
30/// serialize bytes as hex encoded strings to please the users' eyes.
31///
32/// # A note on equality
33///
34/// Two certificates are equal if they have the same secrecy and serialized 
35/// public forms are equal. Usually comparing fingerprints should be enough, 
36/// but the blake3 hashing algorithm used for fingerprints may not be fully 
37/// collision proof. Thus adding the public keys into the mix ensures 
38/// equality.
39#[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(&timestamp);
63        hasher.finalize()
64    }
65
66    /// Generates a new certificate with the given `uid`.
67    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    /// Returns the certificate's fingerprint.
84    pub fn fingerprint(&self) -> &Fingerprint {
85        &self.fpr
86    }
87
88    /// Returns the user id of the certificate.
89    pub fn user_id(&self) -> &str {
90        &self.uid
91    }
92
93    /// Returns the encryption public key of this certificate.
94    pub fn encryption_public(&self) -> &PublicKey {
95        &self.encryption_public
96    }
97
98    /// Returns the signing public key of this certificate.
99    pub fn signing_public(&self) -> &VerifyingKey {
100        &self.signing_public
101    }
102
103    /// Returns the encryption secret key of this certificate if `Some`.
104    pub fn encryption_secret(&self) -> Option<&StaticSecret> {
105        self.encryption_secret.as_ref()
106    }
107
108    /// Returns the signing secret key of this certificate if `Some`.
109    pub fn signing_secret(&self) -> Option<&SecretKey> {
110        self.signing_secret.as_ref()
111    }
112
113    /// Checks whether the certificate is a secret certificate.
114    pub fn is_secret(&self) -> bool {
115        self.encryption_secret.is_some() && self.signing_secret.is_some()
116    }
117
118    /// Consumes `self` and returns a public version of the certificate.
119    /// When used on a public certificate, does nothing.
120    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;