encrypted_message/
strategy.rs1use std::fmt::Debug;
4
5use hmac::{Hmac, Mac};
6use sha2::Sha256;
7
8mod private {
9 pub trait Sealed {}
10
11 impl Sealed for super::Deterministic {}
12 impl Sealed for super::Randomized {}
13}
14
15pub trait Strategy: private::Sealed + Debug {
16 fn generate_nonce_for(payload: &[u8], key: &[u8; 32]) -> [u8; 12];
18}
19
20#[derive(Debug, PartialEq, Eq)]
26pub struct Deterministic;
27impl Strategy for Deterministic {
28 fn generate_nonce_for(payload: &[u8], key: &[u8; 32]) -> [u8; 12] {
30 let mut mac = Hmac::<Sha256>::new_from_slice(key).unwrap();
31 mac.update(payload);
32
33 mac.finalize().into_bytes()[0..12].try_into().unwrap()
34 }
35}
36
37#[derive(Debug, PartialEq, Eq)]
43pub struct Randomized;
44impl Strategy for Randomized {
45 fn generate_nonce_for(_payload: &[u8], _key: &[u8; 32]) -> [u8; 12] {
47 rand::random()
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 use secrecy::ExposeSecret as _;
56
57 use crate::{
58 config::Config as _,
59 testing::{TestConfigDeterministic, TestConfigRandomized},
60 utilities::base64,
61 };
62
63 mod deterministic {
64 use super::*;
65
66 #[test]
67 fn nonce_is_deterministic() {
68 let key = TestConfigDeterministic.primary_key();
69 let nonce = Deterministic::generate_nonce_for("rigo is cool".as_bytes(), key.expose_secret());
70
71 assert_eq!(nonce.len(), 12);
73
74 assert_eq!(nonce, *base64::decode("Ts2jGkMEW9NFsQZX").unwrap());
76 }
77 }
78
79 mod randomized {
80 use super::*;
81
82 #[test]
83 fn nonce_is_randomized() {
84 let payload = "much secret much secure".as_bytes();
85 let key = TestConfigRandomized.primary_key();
86 let first_nonce = Randomized::generate_nonce_for(payload, key.expose_secret());
87 let second_nonce = Randomized::generate_nonce_for(payload, key.expose_secret());
88
89 assert_eq!(first_nonce.len(), 12);
91 assert_eq!(second_nonce.len(), 12);
92
93 assert_ne!(first_nonce, second_nonce);
95 }
96 }
97}