Skip to main content

securedrop_protocol_minimal/keys/
fpf.rs

1use rand_core::{CryptoRng, RngCore};
2
3use crate::sign::{DomainTag, Signature, SigningKey, VerifyingKey};
4
5/// A key pair for FPF (Freedom of the Press Foundation).
6pub struct FPFKeyPair {
7    sk: SigningKey,
8}
9
10// hax struggles with the debug format function signature, but it is
11// debug only, so we can exclude it from extraction
12#[cfg_attr(hax, hax_lib::exclude)]
13impl core::fmt::Debug for FPFKeyPair {
14    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
15        f.debug_struct("FPFKeyPair")
16            .field("vk", &self.sk.vk)
17            .finish_non_exhaustive()
18    }
19}
20
21impl FPFKeyPair {
22    /// Generate a new FPF key pair.
23    ///
24    /// # Errors
25    ///
26    /// Returns an error if the key generation fails.
27    pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, anyhow::Error> {
28        let sk = SigningKey::new(rng)?;
29        Ok(Self { sk })
30    }
31
32    /// Returns the verification key.
33    pub fn verifying_key(&self) -> VerifyingKey {
34        self.sk.vk
35    }
36
37    /// Sign `msg` in domain `D` using the FPF signing key.
38    pub fn sign<D: DomainTag>(&self, msg: &[u8]) -> Signature<D> {
39        self.sk.sign(msg)
40    }
41
42    /// The FPF signing key used as a secret.
43    pub fn as_bytes(&self) -> [u8; SigningKey::SEED_LEN] {
44        self.sk.as_bytes()
45    }
46
47    /// Reconstruct an [`FPFKeyPair`] from its secret.
48    pub fn from_bytes(seed: [u8; SigningKey::SEED_LEN]) -> Self {
49        Self {
50            sk: SigningKey::from_seed(seed),
51        }
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use proptest::prelude::*;
59
60    proptest! {
61        #[test]
62        fn fpf_keypair_seed_roundtrip(seed: [u8; SigningKey::SEED_LEN]) {
63            let kp = FPFKeyPair::from_bytes(seed);
64            prop_assert_eq!(kp.as_bytes(), seed);
65            let kp2 = FPFKeyPair::from_bytes(kp.as_bytes());
66            prop_assert_eq!(
67                kp.verifying_key().into_bytes(),
68                kp2.verifying_key().into_bytes()
69            );
70        }
71    }
72}