1use crate::errors::*;
2use chacha20poly1305::{
3 aead::{Aead, KeyInit},
4 XChaCha20Poly1305, XNonce,
5};
6use hkdf::Hkdf;
7use rand::RngCore;
8use sha2::Sha256;
9
10pub struct KeySchedule {
11 key: chacha20poly1305::Key,
12}
13
14impl KeySchedule {
15 pub fn derive(ikm: &[u8], salt: &[u8]) -> Result<Self> {
16 let hk: Hkdf<Sha256> = Hkdf::new(Some(salt), ikm);
17 let mut okm = [0u8; 32];
18 hk.expand(&[], &mut okm).map_err(|_| S3pError::Kdf)?;
19 Ok(Self { key: okm.into() })
20 }
21
22 pub fn seal(&self, aad: &[u8], plaintext: &[u8]) -> Result<(Vec<u8>, [u8; 24])> {
24 let cipher = XChaCha20Poly1305::new(&self.key);
25 let mut nonce_bytes = [0u8; 24];
26 rand::thread_rng().fill_bytes(&mut nonce_bytes);
27 let nonce = XNonce::from_slice(&nonce_bytes);
28 let ct = cipher
29 .encrypt(
30 nonce,
31 chacha20poly1305::aead::Payload {
32 msg: plaintext,
33 aad,
34 },
35 )
36 .map_err(|e| S3pError::Aead(format!("{e}")))?;
37 Ok((ct, nonce_bytes))
38 }
39
40 pub fn seal_with_nonce(
42 &self,
43 aad: &[u8],
44 nonce: &[u8; 24],
45 plaintext: &[u8],
46 ) -> Result<Vec<u8>> {
47 let cipher = XChaCha20Poly1305::new(&self.key);
48 let n = XNonce::from_slice(nonce);
49 let ct = cipher
50 .encrypt(
51 n,
52 chacha20poly1305::aead::Payload {
53 msg: plaintext,
54 aad,
55 },
56 )
57 .map_err(|e| S3pError::Aead(format!("{e}")))?;
58 Ok(ct)
59 }
60
61 pub fn open(&self, aad: &[u8], nonce: &[u8; 24], ciphertext: &[u8]) -> Result<Vec<u8>> {
62 let cipher = XChaCha20Poly1305::new(&self.key);
63 let nonce = XNonce::from_slice(nonce);
64 let pt = cipher
65 .decrypt(
66 nonce,
67 chacha20poly1305::aead::Payload {
68 msg: ciphertext,
69 aad,
70 },
71 )
72 .map_err(|e| S3pError::Aead(format!("{e}")))?;
73 Ok(pt)
74 }
75}