securedrop_protocol_minimal/server/
session.rs1use alloc::vec::Vec;
6use anyhow::Error;
7use rand_core::{CryptoRng, RngCore};
8use uuid::Uuid;
9
10use crate::Envelope;
11use crate::keys::NewsroomKeyPair;
12use crate::schemes::challenge::MAX_MESSAGES;
13use crate::server::challenge::compute_fetch_challenges;
14use crate::server::storage::ServerStorage;
15use crate::sign::{FpfOnNewsroom, NewsroomOnJournalist, Signature, VerifyingKey};
16use crate::wire::core::{
17 JournalistEphemeralKeys, JournalistLongTermView, MessageChallengeFetchRequest,
18 MessageChallengeFetchResponse, MessageFetchRequest, WelcomeBundle,
19};
20use crate::wire::setup::{
21 JournalistEphemeralKeyRequest, JournalistSetupRequest, JournalistSetupResponse,
22 NewsroomSetupRequest,
23};
24
25#[derive(Default)]
27pub struct Server {
28 storage: ServerStorage,
29 newsroom_keys: Option<NewsroomKeyPair>,
30 signature: Option<Signature<FpfOnNewsroom>>,
32}
33
34impl Server {
35 pub fn new() -> Self {
39 Self::default()
40 }
41
42 pub fn create_newsroom_setup_request<R: RngCore + CryptoRng>(
49 &mut self,
50 rng: &mut R,
51 ) -> Result<NewsroomSetupRequest, Error> {
52 let newsroom_keys = NewsroomKeyPair::new(rng)?;
53 let newsroom_vk = newsroom_keys.verifying_key();
54
55 self.newsroom_keys = Some(newsroom_keys);
57
58 Ok(NewsroomSetupRequest {
59 newsroom_verifying_key: newsroom_vk,
60 })
61 }
62
63 pub fn setup_journalist(
74 &mut self,
75 request: JournalistSetupRequest,
76 ) -> Result<JournalistSetupResponse, Error> {
77 let journalist_signing_key = request.enrollment.verification_key;
79
80 journalist_signing_key
82 .verify(
83 &request.enrollment.bundle.bundle_bytes(),
84 &request.enrollment.bundle.selfsig,
85 )
86 .map_err(|_| anyhow::anyhow!("Invalid signature on longterm keys"))?;
87
88 let verifying_key_bytes = request.enrollment.verification_key.into_bytes();
90 let newsroom_keys = self
91 .newsroom_keys
92 .as_ref()
93 .ok_or_else(|| anyhow::anyhow!("Newsroom keys not found in session"))?;
94 let newsroom_signature: Signature<NewsroomOnJournalist> =
95 newsroom_keys.sign(&verifying_key_bytes);
96
97 let _journalist_id = self
99 .storage
100 .add_journalist(request.enrollment, newsroom_signature.clone());
101
102 Ok(JournalistSetupResponse {
103 sig: newsroom_signature,
104 })
105 }
106
107 pub fn handle_ephemeral_key_request(
117 &mut self,
118 request: JournalistEphemeralKeyRequest,
119 ) -> Result<(), Error> {
120 let journalist_id = self
122 .storage
123 .find_journalist_by_verifying_key(&request.verifying_key)
124 .ok_or_else(|| anyhow::anyhow!("Journalist not found in storage"))?;
125
126 request
129 .bundles
130 .iter()
131 .try_for_each(|k| request.verifying_key.verify(&k.0.as_bytes(), &k.1))
132 .map_err(|_| anyhow::anyhow!("Invalid signature on ephemeral keys"))?;
133
134 self.storage
136 .add_ephemeral_keys(journalist_id, request.bundles);
137
138 Ok(())
139 }
140
141 pub fn newsroom_verifying_key(&self) -> Option<VerifyingKey> {
143 self.newsroom_keys.as_ref().map(|keys| keys.verifying_key())
144 }
145
146 pub fn set_fpf_signature(&mut self, signature: Signature<FpfOnNewsroom>) {
148 self.signature = Some(signature);
149 }
150
151 pub fn ephemeral_keys_count(&self, journalist_id: Uuid) -> usize {
153 self.storage.ephemeral_keys_count(journalist_id)
154 }
155
156 pub fn has_ephemeral_keys(&self, journalist_id: Uuid) -> bool {
158 self.storage.has_ephemeral_keys(journalist_id)
159 }
160
161 pub fn find_journalist_id(&self, verifying_key: &VerifyingKey) -> Option<Uuid> {
163 self.storage.find_journalist_by_verifying_key(verifying_key)
164 }
165
166 pub fn has_message(&self, message_id: &Uuid) -> bool {
168 self.storage.get_messages().contains_key(message_id)
169 }
170
171 pub fn handle_welcome(&self) -> WelcomeBundle {
172 let newsroom_verifying_key = self
173 .newsroom_keys
174 .as_ref()
175 .expect("Newsroom keys not found")
176 .verifying_key();
177 let fpf_sig = self
178 .signature
179 .as_ref()
180 .expect("FPF signature not found")
181 .clone();
182
183 let mut journalists = Vec::new();
184 for (_id, entry) in self.storage.get_journalists().iter() {
185 let (vk, signed_longterm_key_bundle, nr_signature) = entry.clone();
186 journalists.push(JournalistLongTermView {
187 vk,
188 signed_longterm_key_bundle,
189 nr_signature,
190 });
191 }
192
193 WelcomeBundle {
194 newsroom_verifying_key,
195 fpf_sig,
196 journalists,
197 }
198 }
199
200 pub fn handle_journalist_ephemeral_keys<R: RngCore + CryptoRng>(
201 &mut self,
202 rng: &mut R,
203 ) -> Vec<JournalistEphemeralKeys> {
204 let mut responses = Vec::new();
205
206 let journalist_ephemeral_keys = self.storage.get_all_ephemeral_keys(rng);
207
208 for (journalist_id, ephemeral_bundle) in journalist_ephemeral_keys.iter() {
209 let entry = self
211 .storage
212 .get_journalists()
213 .get(journalist_id)
214 .expect("Journalist should exist in storage")
215 .clone();
216 let vk = entry.0;
217
218 responses.push(JournalistEphemeralKeys {
219 vk,
220 ephemeral: ephemeral_bundle.clone(),
221 });
222 }
223
224 responses
225 }
226
227 pub fn handle_message_submit<R: RngCore + CryptoRng>(
229 &mut self,
230 message: Envelope,
231 rng: &mut R,
232 ) -> Result<Uuid, Error> {
233 let message_id = self.storage.deterministic_uuid(rng);
235
236 self.storage.add_message(message_id, message);
238
239 Ok(message_id)
240 }
241
242 pub fn handle_request_challenges<R: RngCore + CryptoRng>(
244 &self,
245 _request: MessageChallengeFetchRequest,
246 rng: &mut R,
247 ) -> Result<MessageChallengeFetchResponse, Error> {
248 let total_challenges: usize = MAX_MESSAGES;
249 let entries: Vec<_> = self
250 .storage
251 .get_messages()
252 .iter()
253 .take(total_challenges)
254 .map(|(uuid, envelope)| (*uuid.as_bytes(), envelope.clone()))
255 .collect();
256 let chall = compute_fetch_challenges(rng, &entries, total_challenges);
257
258 Ok(MessageChallengeFetchResponse {
259 count: total_challenges,
260 messages: chall,
261 })
262 }
263
264 pub fn handle_message_fetch(&self, request: MessageFetchRequest) -> Option<Envelope> {
266 self.storage
267 .get_messages()
268 .get(&request.message_id)
269 .cloned()
270 }
271}