1use alloc::boxed::Box;
2#[cfg(feature = "encrypting")]
3use alloc::vec::Vec;
4
5use cipher::StreamCipher;
6use generic_array::GenericArray;
7use generic_array::sequence::Split;
8use generic_array::typenum::{U16, U32};
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, V1};
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 V1 {
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 nonce: &GenericArray<u8, U32> = nonce.into();
47 let (n1, n2) = nonce.split();
48
49 let ek = kdf(&self.0, b"paseto-encryption-key", n1);
50 let ak = kdf(&self.0, b"paseto-auth-key-for-aead", n1);
51
52 let cipher = ctr::Ctr64BE::<aes::Aes256>::new(&ek, n2);
53 let mac = hmac::Hmac::new_from_slice(&ak).expect("key should be valid");
54 (cipher, mac)
55 }
56}
57
58#[cfg(feature = "encrypting")]
59impl paseto_core::version::SealingVersion<Local> for V1 {
60 fn unsealing_key(key: &LocalKey) -> LocalKey {
61 LocalKey(key.0)
62 }
63
64 fn random() -> Result<LocalKey, PasetoError> {
65 let mut bytes = [0; 32];
66 getrandom::fill(&mut bytes).map_err(|_| PasetoError::CryptoError)?;
67 Ok(LocalKey(bytes))
68 }
69
70 fn nonce() -> Result<Vec<u8>, PasetoError> {
71 let mut nonce = [0; 32];
72 getrandom::fill(&mut nonce).map_err(|_| PasetoError::CryptoError)?;
73
74 let mut payload = Vec::with_capacity(80);
75 payload.extend_from_slice(&nonce);
76 Ok(payload)
77 }
78
79 fn dangerous_seal_with_nonce(
80 key: &LocalKey,
81 encoding: &'static str,
82 mut payload: Vec<u8>,
83 footer: &[u8],
84 aad: &[u8],
85 ) -> Result<Vec<u8>, PasetoError> {
86 if !aad.is_empty() {
87 return Err(PasetoError::ClaimsError);
88 }
89
90 let (nonce, ciphertext) = payload
91 .split_first_chunk_mut::<32>()
92 .ok_or(PasetoError::InvalidToken)?;
93
94 let mut n: hmac::Hmac<sha2::Sha384> =
95 digest::Mac::new_from_slice(nonce).expect("all sized keys are valid with hmac");
96 n.update(ciphertext);
97 *nonce = n.finalize().into_bytes()[0..32]
98 .try_into()
99 .expect("nonce is 32 bytes");
100
101 let (mut cipher, mut mac) = key.keys(nonce);
102 cipher.apply_keystream(ciphertext);
103 preauth_local(&mut mac, encoding, nonce, ciphertext, footer);
104 payload.extend_from_slice(&mac.finalize().into_bytes());
105
106 Ok(payload)
107 }
108}
109
110#[cfg(feature = "decrypting")]
111impl paseto_core::version::UnsealingVersion<Local> for V1 {
112 fn unseal<'a>(
113 key: &LocalKey,
114 encoding: &'static str,
115 payload: &'a mut [u8],
116 footer: &[u8],
117 aad: &[u8],
118 ) -> Result<&'a [u8], PasetoError> {
119 if !aad.is_empty() {
120 return Err(PasetoError::ClaimsError);
121 }
122
123 let len = payload.len();
124 if len < 80 {
125 return Err(PasetoError::InvalidToken);
126 }
127
128 let (ciphertext, tag) = payload
129 .split_last_chunk_mut::<48>()
130 .ok_or(PasetoError::InvalidToken)?;
131 let (nonce, ciphertext) = ciphertext
132 .split_first_chunk_mut::<32>()
133 .ok_or(PasetoError::InvalidToken)?;
134
135 let (mut cipher, mut mac) = key.keys(nonce);
136 preauth_local(&mut mac, encoding, nonce, ciphertext, footer);
137 mac.verify_slice(tag)
138 .map_err(|_| PasetoError::CryptoError)?;
139 cipher.apply_keystream(ciphertext);
140
141 Ok(ciphertext)
142 }
143}
144
145fn kdf(key: &[u8], sep: &'static [u8], nonce: &GenericArray<u8, U16>) -> GenericArray<u8, U32> {
146 let mut output = GenericArray::<u8, U32>::default();
147 hkdf::Hkdf::<sha2::Sha384>::new(Some(nonce), key)
148 .expand(sep, &mut output)
149 .unwrap();
150 output
151}
152
153fn preauth_local(
154 mac: &mut hmac::Hmac<sha2::Sha384>,
155 encoding: &'static str,
156 nonce: &[u8],
157 ciphertext: &[u8],
158 footer: &[u8],
159) {
160 use paseto_core::key::KeyType;
161 struct Context<'a>(&'a mut hmac::Hmac<sha2::Sha384>);
162 impl WriteBytes for Context<'_> {
163 fn write(&mut self, slice: &[u8]) {
164 self.0.update(slice);
165 }
166 }
167
168 pre_auth_encode(
169 [
170 &[
171 "v1".as_bytes(),
172 encoding.as_bytes(),
173 Local::HEADER.as_bytes(),
174 ],
175 &[nonce],
176 &[ciphertext],
177 &[footer],
178 ],
179 Context(mac),
180 );
181}