s3p_core/
aead.rs

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    /// Случайный nonce (24B) + шифрование всего буфера.
23    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    /// Шифрование с переданным nonce (для стриминга/счётчика).
41    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}