paseto_v4/core/
pie_wrap.rs

1use alloc::vec::Vec;
2
3use blake2::Blake2bMac;
4use chacha20::XChaCha20;
5use cipher::StreamCipher;
6use digest::Mac;
7use generic_array::GenericArray;
8use generic_array::sequence::Split;
9use generic_array::typenum::{U32, U56};
10use paseto_core::PasetoError;
11use paseto_core::paserk::PieWrapVersion;
12
13use super::{LocalKey, V4, kdf};
14
15impl LocalKey {
16    fn wrap_keys(&self, nonce: &[u8; 32]) -> (XChaCha20, Blake2bMac<U32>) {
17        use cipher::KeyIvInit;
18        use digest::Mac;
19
20        let (ek, n2) = kdf::<U56>(&self.0, &[0x80], nonce).split();
21        let ak: GenericArray<u8, U32> = kdf(&self.0, &[0x81], nonce);
22
23        let cipher = XChaCha20::new(&ek, &n2);
24        let mac = blake2::Blake2bMac::new_from_slice(&ak).expect("key should be valid");
25        (cipher, mac)
26    }
27}
28
29impl PieWrapVersion for V4 {
30    fn pie_wrap_key(
31        header: &'static str,
32        wrapping_key: &super::LocalKey,
33        mut key_data: Vec<u8>,
34    ) -> Result<Vec<u8>, PasetoError> {
35        let mut nonce = [0u8; 32];
36        getrandom::fill(&mut nonce).map_err(|_| PasetoError::CryptoError)?;
37        let (mut cipher, mut mac) = wrapping_key.wrap_keys(&nonce);
38        cipher.apply_keystream(&mut key_data);
39        auth(&mut mac, header, &nonce, &key_data);
40        let mut out = Vec::with_capacity(64 + key_data.len());
41        out.extend_from_slice(&mac.finalize().into_bytes());
42        out.extend_from_slice(&nonce);
43        out.extend_from_slice(&key_data);
44        Ok(out)
45    }
46
47    fn pie_unwrap_key<'key>(
48        header: &'static str,
49        wrapping_key: &super::LocalKey,
50        key_data: &'key mut [u8],
51    ) -> Result<&'key [u8], PasetoError> {
52        let (tag, ciphertext) = key_data
53            .split_first_chunk_mut()
54            .ok_or(PasetoError::InvalidKey)?;
55        let (nonce, ciphertext) = ciphertext
56            .split_first_chunk_mut()
57            .ok_or(PasetoError::InvalidKey)?;
58        let tag: &[u8; 32] = tag;
59
60        let (mut cipher, mut mac) = wrapping_key.wrap_keys(nonce);
61        auth(&mut mac, header, nonce, ciphertext);
62        mac.verify(tag.into())
63            .map_err(|_| PasetoError::CryptoError)?;
64
65        cipher.apply_keystream(ciphertext);
66
67        Ok(ciphertext)
68    }
69}
70
71fn auth(
72    mac: &mut blake2::Blake2bMac<U32>,
73    encoding: &'static str,
74    nonce: &[u8],
75    ciphertext: &[u8],
76) {
77    mac.update(b"k4");
78    mac.update(encoding.as_bytes());
79    mac.update(nonce);
80    mac.update(ciphertext);
81}