securedrop_protocol_minimal/
keys.rs1mod newsroom;
2
3use rand_core::{CryptoRng, RngCore};
4
5use crate::sign::{
6 DomainTag, FpfOnNewsroom, JournalistEphemeralKey, JournalistLongTermKey, Signature, SigningKey,
7 VerifyingKey,
8};
9
10use crate::message::{MessageKeyPair, MessagePublicKey};
11use crate::metadata::{MetadataKeyPair, MetadataPublicKey};
12use crate::primitives::ristretto255::{DHPrivateKey, DHPublicKey};
13use alloc::string::String;
14use alloc::vec::Vec;
15use serde::de::Error as _;
16use serde::{Deserialize, Serialize};
17
18pub struct KeyPair<SK, PK> {
20 pub(crate) sk: SK,
21 pub(crate) pk: PK,
22}
23
24pub type DhFetchKeyPair = KeyPair<DHPrivateKey, DHPublicKey>;
27
28pub type SignedKeyBundlePublic = (KeyBundlePublic, Signature<JournalistEphemeralKey>);
31
32#[derive(Debug, Clone)]
34#[cfg_attr(not(hax), derive(Serialize, Deserialize))]
35pub struct KeyBundlePublic {
36 pub apke_pk: MessagePublicKey,
38 pub metadata_pk: MetadataPublicKey,
40}
41
42impl KeyBundlePublic {
43 pub fn as_bytes(&self) -> Vec<u8> {
47 let mut out = Vec::new();
48 out.extend_from_slice(&self.apke_pk.as_bytes());
49 out.extend_from_slice(&self.metadata_pk.as_bytes());
50 out
51 }
52}
53
54pub(crate) struct MessageKeyBundle {
55 pub(crate) apke: MessageKeyPair,
56 pub(crate) metadata_kp: MetadataKeyPair,
57}
58
59impl MessageKeyBundle {
60 pub fn new(apke: MessageKeyPair, metadata_kp: MetadataKeyPair) -> Self {
61 Self { apke, metadata_kp }
62 }
63
64 pub(crate) fn public(&self) -> KeyBundlePublic {
65 KeyBundlePublic {
66 apke_pk: self.apke.public_key().clone(),
67 metadata_pk: self.metadata_kp.public_key().clone(),
68 }
69 }
70}
71
72pub(crate) struct SignedMessageKeyBundle {
73 pub(crate) bundle: MessageKeyBundle,
74 pub(crate) selfsig: Signature<JournalistEphemeralKey>,
75}
76
77#[derive(Debug, Clone)]
78pub(crate) struct LongtermKeyBundle {
79 pub(crate) apke: MessagePublicKey,
80 pub(crate) fetch_pk: DHPublicKey,
81}
82
83impl LongtermKeyBundle {
84 pub const LEN: usize = MessagePublicKey::LEN + DHPublicKey::LEN;
85
86 pub fn new(apke: MessagePublicKey, fetch_pk: DHPublicKey) -> Self {
87 Self { apke, fetch_pk }
88 }
89
90 pub fn as_bytes(&self) -> [u8; Self::LEN] {
95 let apke_bytes = self.apke.as_bytes();
96 let fetch_bytes = self.fetch_pk.into_bytes();
97
98 let mut pubkey_bytes = [0u8; Self::LEN];
99 pubkey_bytes[..apke_bytes.len()].copy_from_slice(&apke_bytes);
100 pubkey_bytes[apke_bytes.len()..].copy_from_slice(&fetch_bytes);
101
102 pubkey_bytes
103 }
104}
105
106#[derive(Debug, Clone)]
107pub struct SignedLongtermKeyBundle {
108 pub bundle: LongtermKeyBundle,
109 pub selfsig: Signature<JournalistLongTermKey>,
110}
111
112impl SignedLongtermKeyBundle {
113 pub fn new(bundle: LongtermKeyBundle, selfsig: Signature<JournalistLongTermKey>) -> Self {
114 Self { bundle, selfsig }
115 }
116
117 pub fn as_bytes(&self) -> Vec<u8> {
118 let bundle_bytes = self.bundle.as_bytes();
119 let sig_bytes = self.selfsig.as_bytes();
120
121 let mut pubkey_bytes = Vec::with_capacity(bundle_bytes.len() + sig_bytes.len());
122 pubkey_bytes.extend_from_slice(&bundle_bytes);
123 pubkey_bytes.extend_from_slice(&sig_bytes);
124
125 pubkey_bytes
126 }
127
128 pub fn bundle_bytes(&self) -> [u8; LongtermKeyBundle::LEN] {
129 self.bundle.as_bytes()
130 }
131
132 pub fn apke(&self) -> &MessagePublicKey {
133 &self.bundle.apke
134 }
135
136 pub fn fetch_pk(&self) -> &DHPublicKey {
137 &self.bundle.fetch_pk
138 }
139}
140
141#[cfg_attr(hax, hax_lib::exclude)]
142impl Serialize for SignedLongtermKeyBundle {
143 fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
144 ser.serialize_str(&hex::encode(self.as_bytes()))
145 }
146}
147
148#[cfg_attr(hax, hax_lib::exclude)]
149impl<'de> Deserialize<'de> for SignedLongtermKeyBundle {
150 fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
151 type Sig = Signature<JournalistLongTermKey>;
152 let s = String::deserialize(de)?;
153 let mut bytes = [0u8; LongtermKeyBundle::LEN + Sig::LEN];
154 hex::decode_to_slice(s.trim(), &mut bytes).map_err(D::Error::custom)?;
155
156 let offset = MessagePublicKey::LEN;
157 let apke = MessagePublicKey::from_bytes(&bytes[..offset]).map_err(D::Error::custom)?;
158 let mut fetch_key_bytes = [0u8; DHPublicKey::LEN];
159 fetch_key_bytes.copy_from_slice(&bytes[offset..offset + DHPublicKey::LEN]);
160 let fetch = DHPublicKey::decode(fetch_key_bytes).map_err(D::Error::custom)?;
161 let mut sig_bytes = [0u8; Sig::LEN];
162 sig_bytes.copy_from_slice(&bytes[offset + DHPublicKey::LEN..]);
163 Ok(Self {
164 bundle: LongtermKeyBundle::new(apke, fetch),
165 selfsig: Sig::from_bytes(sig_bytes),
166 })
167 }
168}
169
170#[derive(Clone, Debug)]
171#[cfg_attr(not(hax), derive(Serialize, Deserialize))]
172pub struct Enrollment {
173 pub bundle: SignedLongtermKeyBundle,
174 pub verification_key: VerifyingKey,
176}
177
178pub struct SessionStorage {
180 pub fpf_key: Option<VerifyingKey>,
181 pub nr_key: Option<VerifyingKey>,
182 pub fpf_signature: Option<Signature<FpfOnNewsroom>>,
183}
184
185pub struct FPFKeyPair {
187 sk: SigningKey,
188}
189
190#[cfg_attr(hax, hax_lib::exclude)]
193impl core::fmt::Debug for FPFKeyPair {
194 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
195 f.debug_struct("FPFKeyPair")
196 .field("vk", &self.sk.vk)
197 .finish_non_exhaustive()
198 }
199}
200
201impl FPFKeyPair {
202 pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, anyhow::Error> {
208 let sk = SigningKey::new(rng)?;
209 Ok(Self { sk })
210 }
211
212 pub fn verifying_key(&self) -> VerifyingKey {
214 self.sk.vk
215 }
216
217 pub fn sign<D: DomainTag>(&self, msg: &[u8]) -> Signature<D> {
219 self.sk.sign(msg)
220 }
221
222 pub fn as_bytes(&self) -> [u8; SigningKey::SEED_LEN] {
224 self.sk.as_bytes()
225 }
226
227 pub fn from_bytes(seed: [u8; SigningKey::SEED_LEN]) -> Self {
229 Self {
230 sk: SigningKey::from_seed(seed),
231 }
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238 use proptest::prelude::*;
239
240 proptest! {
241 #[test]
242 fn fpf_keypair_seed_roundtrip(seed: [u8; SigningKey::SEED_LEN]) {
243 let kp = FPFKeyPair::from_bytes(seed);
244 prop_assert_eq!(kp.as_bytes(), seed);
245 let kp2 = FPFKeyPair::from_bytes(kp.as_bytes());
246 prop_assert_eq!(
247 kp.verifying_key().into_bytes(),
248 kp2.verifying_key().into_bytes()
249 );
250 }
251 }
252}
253
254pub use newsroom::NewsroomKeyPair;