paseto_v4/core/
local.rs

1use alloc::boxed::Box;
2#[cfg(feature = "encrypting")]
3use alloc::vec::Vec;
4
5use blake2::Blake2bMac;
6use chacha20::XChaCha20;
7use cipher::StreamCipher;
8use digest::Mac;
9use generic_array::GenericArray;
10use generic_array::sequence::Split;
11use generic_array::typenum::{U32, U56};
12use paseto_core::PasetoError;
13use paseto_core::key::HasKey;
14use paseto_core::pae::pre_auth_encode;
15use paseto_core::version::Local;
16
17use super::{LocalKey, PreAuthEncodeDigest, V4, kdf};
18
19impl LocalKey {
20    pub fn as_raw_bytes(&self) -> &[u8; 32] {
21        &self.0
22    }
23
24    pub fn from_raw_bytes(b: [u8; 32]) -> Self {
25        Self(b)
26    }
27}
28
29impl HasKey<Local> for V4 {
30    type Key = LocalKey;
31
32    fn decode(bytes: &[u8]) -> Result<LocalKey, PasetoError> {
33        bytes
34            .try_into()
35            .map(LocalKey)
36            .map_err(|_| PasetoError::InvalidKey)
37    }
38    fn encode(key: &LocalKey) -> Box<[u8]> {
39        key.0.to_vec().into_boxed_slice()
40    }
41}
42
43impl LocalKey {
44    fn keys(&self, nonce: &GenericArray<u8, U32>) -> (XChaCha20, Blake2bMac<U32>) {
45        use cipher::KeyIvInit;
46        use digest::Mac;
47
48        let (ek, n2) = kdf::<U56>(&self.0, b"paseto-encryption-key", nonce).split();
49        let ak: GenericArray<u8, U32> = kdf(&self.0, b"paseto-auth-key-for-aead", nonce);
50
51        let cipher = XChaCha20::new(&ek, &n2);
52        let mac = blake2::Blake2bMac::new_from_slice(&ak).expect("key should be valid");
53        (cipher, mac)
54    }
55}
56
57#[cfg(feature = "encrypting")]
58impl paseto_core::version::SealingVersion<Local> for V4 {
59    fn unsealing_key(key: &LocalKey) -> LocalKey {
60        LocalKey(key.0)
61    }
62
63    fn random() -> Result<LocalKey, PasetoError> {
64        let mut bytes = [0; 32];
65        getrandom::fill(&mut bytes).map_err(|_| PasetoError::CryptoError)?;
66        Ok(LocalKey(bytes))
67    }
68
69    fn nonce() -> Result<Vec<u8>, PasetoError> {
70        let mut nonce = [0; 32];
71        getrandom::fill(&mut nonce).map_err(|_| PasetoError::CryptoError)?;
72
73        let mut payload = Vec::with_capacity(64);
74        payload.extend_from_slice(&nonce);
75        Ok(payload)
76    }
77
78    fn dangerous_seal_with_nonce(
79        key: &LocalKey,
80        encoding: &'static str,
81        mut payload: Vec<u8>,
82        footer: &[u8],
83        aad: &[u8],
84    ) -> Result<Vec<u8>, PasetoError> {
85        let (nonce, ciphertext) = payload.split_at_mut(32);
86        let nonce: &[u8] = nonce;
87
88        let (mut cipher, mut mac) = key.keys(nonce.into());
89        cipher.apply_keystream(ciphertext);
90        preauth_local(&mut mac, encoding, nonce, ciphertext, footer, aad);
91        payload.extend_from_slice(&mac.finalize().into_bytes());
92
93        Ok(payload)
94    }
95}
96
97#[cfg(feature = "decrypting")]
98impl paseto_core::version::UnsealingVersion<Local> for V4 {
99    fn unseal<'a>(
100        key: &LocalKey,
101        encoding: &'static str,
102        payload: &'a mut [u8],
103        footer: &[u8],
104        aad: &[u8],
105    ) -> Result<&'a [u8], PasetoError> {
106        let (ciphertext, tag) = payload
107            .split_last_chunk_mut::<32>()
108            .ok_or(PasetoError::InvalidToken)?;
109        let (nonce, ciphertext) = ciphertext
110            .split_first_chunk_mut::<32>()
111            .ok_or(PasetoError::InvalidToken)?;
112        let nonce: &[u8; 32] = nonce;
113        let tag: &[u8; 32] = tag;
114
115        let (mut cipher, mut mac) = key.keys(nonce.into());
116        preauth_local(&mut mac, encoding, nonce, ciphertext, footer, aad);
117        mac.verify(tag.into())
118            .map_err(|_| PasetoError::CryptoError)?;
119        cipher.apply_keystream(ciphertext);
120
121        Ok(ciphertext)
122    }
123}
124
125fn preauth_local(
126    mac: &mut blake2::Blake2bMac<U32>,
127    encoding: &'static str,
128    nonce: &[u8],
129    ciphertext: &[u8],
130    footer: &[u8],
131    aad: &[u8],
132) {
133    use paseto_core::key::KeyType;
134    pre_auth_encode(
135        [
136            &[
137                "v4".as_bytes(),
138                encoding.as_bytes(),
139                Local::HEADER.as_bytes(),
140            ],
141            &[nonce],
142            &[ciphertext],
143            &[footer],
144            &[aad],
145        ],
146        PreAuthEncodeDigest(mac),
147    );
148}