use crate::crypto::{AeadPipeline, KeyDerivation};
use crate::seed::SeedRng;
pub struct Session {
pub id: u64,
pub rng: SeedRng,
pub data_key: [u8; 32],
pub packet_counter: u64,
pub active: bool,
pub key_index: u64,
}
impl Session {
pub fn from_master_secret(
id: u64,
master_key: &[u8; 32],
timestamp: u64,
session_nonce: &[u8],
) -> crate::error::Result<Self> {
let seed =
KeyDerivation::derive_initial_seed(master_key.as_slice(), timestamp, session_nonce)?;
let data_key = KeyDerivation::derive_data_key(&seed, 0)?;
Ok(Self::new(id, seed, data_key))
}
pub fn new(id: u64, seed: [u8; 32], data_key: [u8; 32]) -> Self {
Self {
id,
rng: SeedRng::new(seed),
data_key,
packet_counter: 0,
active: true,
key_index: 0,
}
}
pub fn next_packet_counter(&mut self) -> u64 {
self.packet_counter += 1;
self.packet_counter
}
pub fn rekey(&mut self) -> crate::error::Result<[u8; 32]> {
self.key_index += 1;
self.data_key = KeyDerivation::derive_data_key(&self.rng.seed_bytes(), self.key_index)?;
Ok(self.data_key)
}
pub fn close(&mut self) {
self.active = false;
}
pub fn encrypt_with_pipeline(
&mut self,
pipeline: &AeadPipeline,
plaintext: &[u8],
) -> crate::error::Result<Vec<u8>> {
let counter = self.next_packet_counter();
let nonce = KeyDerivation::derive_nonce(&self.rng.seed_bytes(), counter)?;
pipeline.encrypt(nonce, plaintext.to_vec())
}
pub fn decrypt_with_pipeline(
&self,
pipeline: &AeadPipeline,
nonce: [u8; 12],
ciphertext: Vec<u8>,
) -> crate::error::Result<Vec<u8>> {
pipeline.decrypt(nonce, ciphertext)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::AeadCipher as Variant;
use crate::crypto::KeyDerivation;
#[test]
fn from_master_secret_derivation() {
let master = [0x77u8; 32];
let s = Session::from_master_secret(1, &master, 1_700_000_000, b"nonce").unwrap();
assert_eq!(s.id, 1);
assert!(s.active);
}
#[test]
fn encrypt_decrypt_via_pipeline_matches_kdf_nonce() {
let seed = [0xABu8; 32];
let key = [0xCDu8; 32];
let pipe = AeadPipeline::new(Variant::ChaCha20Poly1305, &key, 3).unwrap();
let mut session = Session::new(9, seed, key);
let ct = session.encrypt_with_pipeline(&pipe, b"payload").unwrap();
let nonce =
KeyDerivation::derive_nonce(&session.rng.seed_bytes(), session.packet_counter).unwrap();
let pt = session.decrypt_with_pipeline(&pipe, nonce, ct).unwrap();
assert_eq!(pt.as_slice(), b"payload");
}
}