1use crate::crypto::{AeadPipeline, KeyDerivation};
4use crate::seed::SeedRng;
5
6pub struct Session {
8 pub id: u64,
10 pub rng: SeedRng,
12 pub data_key: [u8; 32],
14 pub packet_counter: u64,
16 pub active: bool,
18 pub key_index: u64,
20}
21
22impl Session {
23 pub fn from_master_secret(
25 id: u64,
26 master_key: &[u8; 32],
27 timestamp: u64,
28 session_nonce: &[u8],
29 ) -> crate::error::Result<Self> {
30 let seed =
31 KeyDerivation::derive_initial_seed(master_key.as_slice(), timestamp, session_nonce)?;
32 let data_key = KeyDerivation::derive_data_key(&seed, 0)?;
33 Ok(Self::new(id, seed, data_key))
34 }
35
36 pub fn new(id: u64, seed: [u8; 32], data_key: [u8; 32]) -> Self {
38 Self {
39 id,
40 rng: SeedRng::new(seed),
41 data_key,
42 packet_counter: 0,
43 active: true,
44 key_index: 0,
45 }
46 }
47
48 pub fn next_packet_counter(&mut self) -> u64 {
50 self.packet_counter += 1;
51 self.packet_counter
52 }
53
54 pub fn rekey(&mut self) -> crate::error::Result<[u8; 32]> {
59 self.key_index += 1;
60 self.data_key = KeyDerivation::derive_data_key(&self.rng.seed_bytes(), self.key_index)?;
61 Ok(self.data_key)
62 }
63
64 pub fn close(&mut self) {
66 self.active = false;
67 }
68
69 pub fn encrypt_with_pipeline(
73 &mut self,
74 pipeline: &AeadPipeline,
75 plaintext: &[u8],
76 ) -> crate::error::Result<Vec<u8>> {
77 let counter = self.next_packet_counter();
78 let nonce = KeyDerivation::derive_nonce(&self.rng.seed_bytes(), counter)?;
79 pipeline.encrypt(nonce, plaintext.to_vec())
80 }
81
82 pub fn decrypt_with_pipeline(
84 &self,
85 pipeline: &AeadPipeline,
86 nonce: [u8; 12],
87 ciphertext: Vec<u8>,
88 ) -> crate::error::Result<Vec<u8>> {
89 pipeline.decrypt(nonce, ciphertext)
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use crate::config::AeadCipher as Variant;
97 use crate::crypto::KeyDerivation;
98
99 #[test]
100 fn from_master_secret_derivation() {
101 let master = [0x77u8; 32];
102 let s = Session::from_master_secret(1, &master, 1_700_000_000, b"nonce").unwrap();
103 assert_eq!(s.id, 1);
104 assert!(s.active);
105 }
106
107 #[test]
108 fn encrypt_decrypt_via_pipeline_matches_kdf_nonce() {
109 let seed = [0xABu8; 32];
110 let key = [0xCDu8; 32];
111 let pipe = AeadPipeline::new(Variant::ChaCha20Poly1305, &key, 3).unwrap();
112 let mut session = Session::new(9, seed, key);
113
114 let ct = session.encrypt_with_pipeline(&pipe, b"payload").unwrap();
115 let nonce =
116 KeyDerivation::derive_nonce(&session.rng.seed_bytes(), session.packet_counter).unwrap();
117 let pt = session.decrypt_with_pipeline(&pipe, nonce, ct).unwrap();
118 assert_eq!(pt.as_slice(), b"payload");
119 }
120}