paseto_v3/core/
local.rs

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