Skip to main content

securedrop_protocol_minimal/keys/
newsroom.rs

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