1use std::collections::BTreeMap;
36
37use nostro2_traits::NostrKeypair;
38use nostro2_traits::hex::Hexable;
39use zeroize::Zeroize;
40
41use super::{Nip104Crypto, Nip104Error};
42
43type Result<T> = std::result::Result<T, Nip104Error>;
44
45pub const SENDER_KEY_MAX_SKIP: u32 = 10_000;
48
49pub const SENDER_KEY_MAX_STORED_SKIPPED_KEYS: usize = 2_000;
51
52const SENDER_KEY_KDF_SALT: &[u8] = b"ndr-sender-key-v1";
54
55#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct SenderKeyState {
64 pub key_id: u32,
66 chain_key: String,
68 iteration: u32,
70 skipped_message_keys: BTreeMap<u32, String>,
72}
73
74impl Drop for SenderKeyState {
76 fn drop(&mut self) {
77 self.chain_key.zeroize();
78 for key in self.skipped_message_keys.values_mut() {
79 key.zeroize();
80 }
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
86pub struct SenderKeyEncryptPlan {
87 pub next_state: SenderKeyState,
89 pub key_id: u32,
91 pub message_number: u32,
93 pub ciphertext: String,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
99pub struct SenderKeyDecryptPlan {
100 pub next_state: SenderKeyState,
102 pub plaintext: Vec<u8>,
104}
105
106impl SenderKeyState {
107 #[must_use]
110 pub fn new(key_id: u32, chain_key: &[u8; 32], iteration: u32) -> Self {
111 Self {
112 key_id,
113 chain_key: chain_key.to_hex(),
114 iteration,
115 skipped_message_keys: BTreeMap::new(),
116 }
117 }
118
119 #[must_use]
121 pub const fn key_id(&self) -> u32 {
122 self.key_id
123 }
124
125 #[must_use]
127 pub const fn iteration(&self) -> u32 {
128 self.iteration
129 }
130
131 #[must_use]
134 pub fn chain_key_hex(&self) -> String {
135 self.chain_key.clone()
136 }
137
138 #[must_use]
140 pub fn skipped_len(&self) -> usize {
141 self.skipped_message_keys.len()
142 }
143
144 #[must_use]
150 pub const fn skipped_keys(&self) -> &BTreeMap<u32, String> {
151 &self.skipped_message_keys
152 }
153
154 #[must_use]
160 pub const fn from_parts(
161 key_id: u32,
162 chain_key_hex: String,
163 iteration: u32,
164 skipped_message_keys: BTreeMap<u32, String>,
165 ) -> Self {
166 Self {
167 key_id,
168 chain_key: chain_key_hex,
169 iteration,
170 skipped_message_keys,
171 }
172 }
173
174 pub fn plan_encrypt<K: NostrKeypair>(&self, plaintext: &[u8]) -> Result<SenderKeyEncryptPlan> {
181 let mut next_state = self.clone();
182 let message_number = next_state.iteration;
183 let (next_chain_key, message_key) =
184 Self::derive_message_key::<K>(&K::decode_hex_32(&next_state.chain_key)?);
185 next_state.chain_key = next_chain_key.to_hex();
186 next_state.iteration = next_state
187 .iteration
188 .checked_add(1)
189 .ok_or(Nip104Error::SessionNotReady)?;
190
191 let ciphertext = K::encrypt_with_message_key(&message_key, plaintext)?;
192 Ok(SenderKeyEncryptPlan {
193 next_state,
194 key_id: self.key_id,
195 message_number,
196 ciphertext,
197 })
198 }
199
200 pub fn apply_encrypt(&mut self, plan: SenderKeyEncryptPlan) {
202 *self = plan.next_state;
203 }
204
205 pub fn encrypt<K: NostrKeypair>(&mut self, plaintext: &[u8]) -> Result<(u32, String)> {
211 let plan = self.plan_encrypt::<K>(plaintext)?;
212 let out = (plan.message_number, plan.ciphertext.clone());
213 self.apply_encrypt(plan);
214 Ok(out)
215 }
216
217 pub fn plan_decrypt<K: NostrKeypair>(
225 &self,
226 key_id: u32,
227 message_number: u32,
228 ciphertext_b64: &str,
229 ) -> Result<SenderKeyDecryptPlan> {
230 if key_id != self.key_id {
231 return Err(Nip104Error::InvalidHeader);
232 }
233 let mut next_state = self.clone();
234 let plaintext = next_state.decrypt_in_place::<K>(message_number, ciphertext_b64)?;
235 Ok(SenderKeyDecryptPlan {
236 next_state,
237 plaintext,
238 })
239 }
240
241 pub fn apply_decrypt(&mut self, plan: SenderKeyDecryptPlan) -> Vec<u8> {
243 *self = plan.next_state;
244 plan.plaintext
245 }
246
247 pub fn decrypt<K: NostrKeypair>(
253 &mut self,
254 message_number: u32,
255 ciphertext_b64: &str,
256 ) -> Result<Vec<u8>> {
257 let plan = self.plan_decrypt::<K>(self.key_id, message_number, ciphertext_b64)?;
258 Ok(self.apply_decrypt(plan))
259 }
260
261 fn decrypt_in_place<K: NostrKeypair>(
264 &mut self,
265 message_number: u32,
266 ciphertext_b64: &str,
267 ) -> Result<Vec<u8>> {
268 if message_number < self.iteration {
270 let key = self
271 .skipped_message_keys
272 .remove(&message_number)
273 .ok_or(Nip104Error::InvalidHeader)?;
274 return K::decrypt_with_message_key(&K::decode_hex_32(&key)?, ciphertext_b64);
275 }
276
277 let delta = message_number - self.iteration;
279 if delta > SENDER_KEY_MAX_SKIP {
280 return Err(Nip104Error::TooManySkippedMessages);
281 }
282
283 while self.iteration < message_number {
285 let (next_chain_key, message_key) =
286 Self::derive_message_key::<K>(&K::decode_hex_32(&self.chain_key)?);
287 self.chain_key = next_chain_key.to_hex();
288 self.skipped_message_keys
289 .insert(self.iteration, message_key.to_hex());
290 self.iteration = self
291 .iteration
292 .checked_add(1)
293 .ok_or(Nip104Error::SessionNotReady)?;
294 }
295
296 let (next_chain_key, message_key) =
298 Self::derive_message_key::<K>(&K::decode_hex_32(&self.chain_key)?);
299 self.chain_key = next_chain_key.to_hex();
300 self.iteration = self
301 .iteration
302 .checked_add(1)
303 .ok_or(Nip104Error::SessionNotReady)?;
304 Self::prune_skipped(&mut self.skipped_message_keys);
305
306 K::decrypt_with_message_key(&message_key, ciphertext_b64)
307 }
308
309 fn derive_message_key<K: NostrKeypair>(chain_key: &[u8; 32]) -> ([u8; 32], [u8; 32]) {
311 let outs = K::kdf(chain_key, SENDER_KEY_KDF_SALT, 2);
312 (outs[0], outs[1])
313 }
314
315 fn prune_skipped(map: &mut BTreeMap<u32, String>) {
317 while map.len() > SENDER_KEY_MAX_STORED_SKIPPED_KEYS {
318 let Some(first) = map.keys().next().copied() else {
319 break;
320 };
321 map.remove(&first);
322 }
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 type K = crate::tests::NipTester;
331
332 #[test]
333 fn roundtrip_single_message() {
334 let ck = [7_u8; 32];
335 let mut sender = SenderKeyState::new(1, &ck, 0);
336 let mut receiver = SenderKeyState::new(1, &ck, 0);
337
338 let (n, ct) = sender.encrypt::<K>(b"hello").unwrap();
339 assert_eq!(n, 0);
340 assert_eq!(receiver.decrypt::<K>(n, &ct).unwrap(), b"hello");
341 assert_eq!(sender.iteration(), receiver.iteration());
342 }
343
344 #[test]
345 fn decrypt_out_of_order() {
346 let ck = [9_u8; 32];
347 let mut sender = SenderKeyState::new(1, &ck, 0);
348 let mut receiver = SenderKeyState::new(1, &ck, 0);
349
350 let (n0, c0) = sender.encrypt::<K>(b"m0").unwrap();
351 let (n1, c1) = sender.encrypt::<K>(b"m1").unwrap();
352
353 assert_eq!(receiver.decrypt::<K>(n1, &c1).unwrap(), b"m1");
354 assert_eq!(receiver.decrypt::<K>(n0, &c0).unwrap(), b"m0");
355 }
356
357 #[test]
358 fn rejects_duplicate_message() {
359 let ck = [11_u8; 32];
360 let mut sender = SenderKeyState::new(1, &ck, 0);
361 let mut receiver = SenderKeyState::new(1, &ck, 0);
362 let (n, c) = sender.encrypt::<K>(b"once").unwrap();
363
364 assert_eq!(receiver.decrypt::<K>(n, &c).unwrap(), b"once");
365 assert!(receiver.decrypt::<K>(n, &c).is_err());
366 }
367
368 #[test]
369 fn wrong_key_id_does_not_mutate_receiver() {
370 let ck = [13_u8; 32];
371 let mut sender = SenderKeyState::new(1, &ck, 0);
372 let receiver = SenderKeyState::new(1, &ck, 0);
373 let (n, c) = sender.encrypt::<K>(b"x").unwrap();
374 let before = receiver.clone();
375
376 assert!(receiver.plan_decrypt::<K>(2, n, &c).is_err());
377 assert_eq!(receiver, before);
378 }
379
380 #[test]
381 fn plan_encrypt_is_pure_until_apply() {
382 let ck = [4_u8; 32];
383 let mut sender = SenderKeyState::new(7, &ck, 0);
384 let before = sender.clone();
385
386 let plan = sender.plan_encrypt::<K>(b"deferred").unwrap();
387 assert_eq!(sender, before);
388 assert_eq!(plan.key_id, 7);
389 assert_eq!(plan.message_number, 0);
390
391 sender.apply_encrypt(plan);
392 assert_eq!(sender.iteration(), 1);
393 assert_ne!(sender, before);
394 }
395
396 #[test]
397 fn skip_ahead_then_backfill() {
398 let ck = [19_u8; 32];
399 let mut sender = SenderKeyState::new(1, &ck, 0);
400 let mut receiver = SenderKeyState::new(1, &ck, 0);
401 let (n0, c0) = sender.encrypt::<K>(b"m0").unwrap();
402 let (n1, c1) = sender.encrypt::<K>(b"m1").unwrap();
403 let (n2, c2) = sender.encrypt::<K>(b"m2").unwrap();
404
405 assert_eq!(receiver.decrypt::<K>(n2, &c2).unwrap(), b"m2");
407 assert_eq!(receiver.skipped_len(), 2);
408 assert_eq!(receiver.decrypt::<K>(n0, &c0).unwrap(), b"m0");
410 assert_eq!(receiver.decrypt::<K>(n1, &c1).unwrap(), b"m1");
411 assert_eq!(receiver.skipped_len(), 0);
412 }
413
414 #[test]
415 fn rejects_too_many_skipped() {
416 let ck = [3_u8; 32];
417 let receiver = SenderKeyState::new(1, &ck, 0);
418 let err = receiver.plan_decrypt::<K>(1, SENDER_KEY_MAX_SKIP + 1, "AA");
419 assert!(matches!(err, Err(Nip104Error::TooManySkippedMessages)));
420 }
421
422 #[test]
428 #[allow(clippy::cast_possible_truncation)] fn skip_ahead_prunes_oldest_skipped_keys() {
430 const AHEAD: u32 = SENDER_KEY_MAX_STORED_SKIPPED_KEYS as u32 + 500;
432 let ck = [21_u8; 32];
433 let mut sender = SenderKeyState::new(1, &ck, 0);
434 let mut receiver = SenderKeyState::new(1, &ck, 0);
435 let mut first = None;
436 let mut recent = None;
437 for i in 0..=AHEAD {
438 let (n, c) = sender.encrypt::<K>(format!("m{i}").as_bytes()).unwrap();
439 if i == 0 {
440 first = Some((n, c.clone()));
441 }
442 if i == AHEAD - 1 {
443 recent = Some((n, c.clone()));
444 }
445 if i == AHEAD {
446 assert_eq!(
449 receiver.decrypt::<K>(n, &c).unwrap(),
450 format!("m{i}").as_bytes()
451 );
452 }
453 }
454 assert_eq!(
455 receiver.skipped_len(),
456 SENDER_KEY_MAX_STORED_SKIPPED_KEYS,
457 "stored skipped keys must be capped"
458 );
459
460 let (rn, rc) = recent.unwrap();
462 assert_eq!(
463 receiver.decrypt::<K>(rn, &rc).unwrap(),
464 format!("m{}", AHEAD - 1).as_bytes()
465 );
466
467 let (fn0, fc0) = first.unwrap();
469 assert!(matches!(
470 receiver.plan_decrypt::<K>(1, fn0, &fc0),
471 Err(Nip104Error::InvalidHeader)
472 ));
473 }
474
475 #[test]
478 fn skip_ceiling_is_inclusive() {
479 let ck = [23_u8; 32];
480 let receiver = SenderKeyState::new(1, &ck, 0);
485 let at_ceiling = receiver.plan_decrypt::<K>(1, SENDER_KEY_MAX_SKIP, "not-base64!!");
486 assert!(
487 !matches!(at_ceiling, Err(Nip104Error::TooManySkippedMessages)),
488 "delta == MAX_SKIP must pass the skip gate"
489 );
490 let over = receiver.plan_decrypt::<K>(1, SENDER_KEY_MAX_SKIP + 1, "not-base64!!");
491 assert!(matches!(over, Err(Nip104Error::TooManySkippedMessages)));
492 }
493
494 #[test]
498 fn iteration_overflow_on_encrypt_is_rejected() {
499 let ck = [25_u8; 32];
500 let sender = SenderKeyState::new(1, &ck, u32::MAX);
501 assert!(matches!(
502 sender.plan_encrypt::<K>(b"boom"),
503 Err(Nip104Error::SessionNotReady)
504 ));
505 }
506
507 #[test]
511 fn payload_size_bounds_match_nip44() {
512 let ck = [27_u8; 32];
513 let mut sender = SenderKeyState::new(1, &ck, 0);
514 let mut receiver = SenderKeyState::new(1, &ck, 0);
515
516 let (n0, c0) = sender.encrypt::<K>(b"x").unwrap();
518 assert_eq!(receiver.decrypt::<K>(n0, &c0).unwrap(), b"x");
519
520 let max = vec![0x5A_u8; 65_535];
522 let (n1, c1) = sender.encrypt::<K>(&max).unwrap();
523 assert_eq!(receiver.decrypt::<K>(n1, &c1).unwrap(), max);
524
525 assert!(sender.plan_encrypt::<K>(b"").is_err());
528 let over = vec![0_u8; 65_536];
529 assert!(sender.plan_encrypt::<K>(&over).is_err());
530 assert_eq!(sender.iteration(), 2);
532 }
533
534 #[test]
538 fn tampered_ciphertext_fails_without_advancing() {
539 use base64::engine::{Engine as _, general_purpose};
540 let ck = [29_u8; 32];
541 let mut sender = SenderKeyState::new(1, &ck, 0);
542 let receiver = SenderKeyState::new(1, &ck, 0);
543 let (n, c) = sender.encrypt::<K>(b"authentic").unwrap();
544
545 let mut raw = general_purpose::STANDARD.decode(&c).unwrap();
547 let mid = raw.len() / 2;
548 raw[mid] ^= 0xFF;
549 let tampered = general_purpose::STANDARD.encode(&raw);
550
551 let before = receiver.clone();
552 assert!(receiver.plan_decrypt::<K>(1, n, &tampered).is_err());
553 assert_eq!(receiver, before, "failed decrypt must not mutate state");
554
555 let mut receiver = receiver;
557 assert_eq!(receiver.decrypt::<K>(n, &c).unwrap(), b"authentic");
558 }
559
560 #[test]
563 fn sustained_in_order_volume() {
564 const N: u32 = 5_000;
565 let ck = [31_u8; 32];
566 let mut sender = SenderKeyState::new(1, &ck, 0);
567 let mut receiver = SenderKeyState::new(1, &ck, 0);
568 for i in 0..N {
569 let (n, c) = sender.encrypt::<K>(format!("msg-{i}").as_bytes()).unwrap();
570 assert_eq!(n, i);
571 assert_eq!(
572 receiver.decrypt::<K>(n, &c).unwrap(),
573 format!("msg-{i}").as_bytes()
574 );
575 }
576 assert_eq!(sender.iteration(), N);
577 assert_eq!(receiver.iteration(), N);
578 assert_eq!(receiver.skipped_len(), 0);
579 }
580}