1use std::collections::VecDeque;
9
10use openmls_traits::crypto::OpenMlsCrypto;
11
12use openmls_traits::types::Ciphersuite;
13
14use crate::ciphersuite::{AeadNonce, *};
15#[cfg(feature = "virtual-clients-draft")]
16use crate::tree::dual_use_ratchet::DualUseRatchet;
17use crate::tree::secret_tree::*;
18
19use super::*;
20
21pub(crate) type Generation = u32;
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
36pub struct SenderRatchetConfiguration {
37 out_of_order_tolerance: Generation,
38 maximum_forward_distance: Generation,
39}
40
41impl SenderRatchetConfiguration {
42 pub fn new(out_of_order_tolerance: Generation, maximum_forward_distance: Generation) -> Self {
44 Self {
45 out_of_order_tolerance,
46 maximum_forward_distance,
47 }
48 }
49 pub fn out_of_order_tolerance(&self) -> Generation {
51 self.out_of_order_tolerance
52 }
53
54 pub fn maximum_forward_distance(&self) -> Generation {
56 self.maximum_forward_distance
57 }
58}
59
60impl Default for SenderRatchetConfiguration {
61 fn default() -> Self {
62 Self::new(5, 1000)
63 }
64}
65
66pub(crate) type RatchetKeyMaterial = (AeadKey, AeadNonce);
69
70#[derive(Serialize, Deserialize)]
80#[serde(from = "SerdeSenderRatchet")]
81#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))]
82#[cfg_attr(any(feature = "crypto-debug", test), derive(Debug))]
83pub(crate) enum SenderRatchet {
84 #[cfg_attr(feature = "virtual-clients-draft", allow(unused))]
85 EncryptionRatchet(RatchetSecret),
86 DecryptionRatchet(DecryptionRatchet),
87 #[cfg(feature = "virtual-clients-draft")]
88 DualUse(DualUseRatchet),
89}
90
91#[derive(Deserialize)]
102enum SerdeSenderRatchet {
103 EncryptionRatchet(RatchetSecret),
104 DecryptionRatchet(DecryptionRatchet),
105 DualUse(SerdeDualUseRatchet),
106}
107
108#[cfg(feature = "virtual-clients-draft")]
109type SerdeDualUseRatchet = DualUseRatchet;
110
111#[cfg(not(feature = "virtual-clients-draft"))]
112#[derive(Deserialize)]
113struct SerdeDualUseRatchet {
114 ratchet_head: RatchetSecret,
115}
116
117impl From<SerdeSenderRatchet> for SenderRatchet {
118 fn from(repr: SerdeSenderRatchet) -> Self {
119 match repr {
120 SerdeSenderRatchet::EncryptionRatchet(ratchet_secret) => {
121 #[cfg(feature = "virtual-clients-draft")]
122 {
123 SenderRatchet::DualUse(DualUseRatchet::from(ratchet_secret))
124 }
125 #[cfg(not(feature = "virtual-clients-draft"))]
126 {
127 SenderRatchet::EncryptionRatchet(ratchet_secret)
128 }
129 }
130 SerdeSenderRatchet::DecryptionRatchet(dec_ratchet) => {
131 SenderRatchet::DecryptionRatchet(dec_ratchet)
132 }
133 SerdeSenderRatchet::DualUse(dual_ratchet) => {
134 #[cfg(feature = "virtual-clients-draft")]
135 {
136 SenderRatchet::DualUse(dual_ratchet)
137 }
138 #[cfg(not(feature = "virtual-clients-draft"))]
139 {
140 SenderRatchet::EncryptionRatchet(dual_ratchet.ratchet_head)
141 }
142 }
143 }
144 }
145}
146
147impl SenderRatchet {
148 #[cfg(test)]
149 pub(crate) fn generation(&self) -> Generation {
150 match self {
151 SenderRatchet::EncryptionRatchet(enc_ratchet) => enc_ratchet.generation(),
152 SenderRatchet::DecryptionRatchet(dec_ratchet) => dec_ratchet.generation(),
153 #[cfg(feature = "virtual-clients-draft")]
154 SenderRatchet::DualUse(dual_ratchet) => dual_ratchet.generation(),
155 }
156 }
157}
158
159#[derive(Debug, Serialize, Deserialize, Default)]
164#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))]
165pub(crate) struct RatchetSecret {
166 secret: Secret,
167 generation: Generation,
168}
169
170impl RatchetSecret {
171 pub(crate) fn initial_ratchet_secret(secret: Secret) -> Self {
174 Self {
175 secret,
176 generation: 0,
177 }
178 }
179
180 pub(crate) fn generation(&self) -> Generation {
182 self.generation
183 }
184
185 pub(crate) fn ratchet_forward(
188 &mut self,
189 crypto: &impl OpenMlsCrypto,
190 ciphersuite: Ciphersuite,
191 ) -> Result<(Generation, RatchetKeyMaterial), SecretTreeError> {
192 log::trace!("Ratcheting forward in generation {}.", self.generation);
193 log_crypto!(trace, " with secret {:x?}", self.secret);
194
195 if self.generation == u32::MAX {
197 return Err(SecretTreeError::RatchetTooLong);
198 }
199 let nonce = derive_tree_secret(
200 ciphersuite,
201 &self.secret,
202 "nonce",
203 self.generation,
204 ciphersuite.aead_nonce_length(),
205 crypto,
206 )?;
207 let key = derive_tree_secret(
208 ciphersuite,
209 &self.secret,
210 "key",
211 self.generation,
212 ciphersuite.aead_key_length(),
213 crypto,
214 )?;
215 self.secret = derive_tree_secret(
216 ciphersuite,
217 &self.secret,
218 "secret",
219 self.generation,
220 ciphersuite.hash_length(),
221 crypto,
222 )?;
223 let generation = self.generation;
224 self.generation += 1;
225 Ok((
226 generation,
227 (
228 AeadKey::from_secret(key, ciphersuite),
229 AeadNonce::from_secret(nonce),
230 ),
231 ))
232 }
233
234 #[cfg(test)]
235 pub(crate) fn set_generation(&mut self, generation: Generation) {
236 self.generation = generation
237 }
238}
239
240#[derive(Serialize, Deserialize)]
245#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))]
246#[cfg_attr(any(feature = "crypto-debug", test), derive(Debug))]
247pub struct DecryptionRatchet {
248 past_secrets: VecDeque<Option<RatchetKeyMaterial>>,
249 ratchet_head: RatchetSecret,
250}
251
252impl DecryptionRatchet {
253 pub(crate) fn new(secret: Secret) -> Self {
255 Self {
256 past_secrets: VecDeque::new(),
257 ratchet_head: RatchetSecret::initial_ratchet_secret(secret),
258 }
259 }
260
261 #[cfg(test)]
263 pub(crate) fn generation(&self) -> Generation {
264 self.ratchet_head.generation()
265 }
266
267 #[cfg(test)]
268 pub(crate) fn ratchet_secret_mut(&mut self) -> &mut RatchetSecret {
269 &mut self.ratchet_head
270 }
271
272 pub(crate) fn secret_for_decryption(
275 &mut self,
276 ciphersuite: Ciphersuite,
277 crypto: &impl OpenMlsCrypto,
278 generation: Generation,
279 configuration: &SenderRatchetConfiguration,
280 ) -> Result<RatchetKeyMaterial, SecretTreeError> {
281 log::debug!("secret_for_decryption");
282 let head_generation = self.ratchet_head.generation();
283 if head_generation < u32::MAX - configuration.maximum_forward_distance()
285 && generation > head_generation + configuration.maximum_forward_distance()
286 {
287 return Err(SecretTreeError::TooDistantInTheFuture);
288 }
289 if generation < head_generation
291 && (head_generation - generation) > configuration.out_of_order_tolerance()
292 {
293 log::error!(" Generation is too far in the past (broke out of order tolerance ({}) {generation} < {head_generation}).", configuration.out_of_order_tolerance());
294 return Err(SecretTreeError::TooDistantInThePast);
295 }
296 if generation >= head_generation {
298 for _ in 0..(generation - head_generation) {
300 let ratchet_secrets = self
302 .ratchet_head
303 .ratchet_forward(crypto, ciphersuite)
304 .map(|(_, key_material)| key_material)?;
305 self.past_secrets.push_front(Some(ratchet_secrets));
307 }
308 let ratchet_secrets = self
309 .ratchet_head
310 .ratchet_forward(crypto, ciphersuite)
311 .map(|(_, key_material)| key_material)?;
312 self.past_secrets.push_front(None);
314 self.past_secrets
315 .truncate(configuration.out_of_order_tolerance() as usize);
316 Ok(ratchet_secrets)
317 } else {
318 let window_index = ((head_generation - generation) as i32) - 1;
321 let index = if window_index >= 0 {
324 window_index as usize
325 } else {
326 log::error!(" Generation is too far in the past (not in the window).");
327 return Err(SecretTreeError::TooDistantInThePast);
328 };
329 self.past_secrets
331 .get_mut(index)
332 .ok_or(SecretTreeError::IndexOutOfBounds)?
333 .take()
337 .ok_or(SecretTreeError::SecretReuseError)
340 }
341 }
342}
343
344#[cfg(test)]
345mod persistence_tests {
346 use super::*;
347
348 #[cfg(feature = "virtual-clients-draft")]
350 #[test]
351 fn pre_feature_encryption_ratchet_is_promoted_to_dual_use() {
352 let ratchet_head = RatchetSecret::initial_ratchet_secret(Secret::from_slice(&[0; 32]));
353 let restored: SenderRatchet =
354 SerdeSenderRatchet::EncryptionRatchet(ratchet_head.clone()).into();
355 match restored {
356 SenderRatchet::DualUse(dual_use) => {
357 assert_eq!(dual_use.generation(), ratchet_head.generation())
358 }
359 _ => panic!("expected promotion to DualUse"),
360 }
361 }
362
363 #[cfg(not(feature = "virtual-clients-draft"))]
366 #[test]
367 fn persisted_dual_use_is_readable_without_the_feature() {
368 let ratchet_head = RatchetSecret::initial_ratchet_secret(Secret::from_slice(&[0; 32]));
369 let restored: SenderRatchet = SerdeSenderRatchet::DualUse(SerdeDualUseRatchet {
370 ratchet_head: ratchet_head.clone(),
371 })
372 .into();
373 match restored {
374 SenderRatchet::EncryptionRatchet(rs) => {
375 assert_eq!(rs.generation(), ratchet_head.generation())
376 }
377 _ => panic!("expected fallback to EncryptionRatchet"),
378 }
379 }
380}