paseto_v4/core/
pw_wrap.rs1use alloc::vec::Vec;
2
3use blake2::Blake2bMac;
4use chacha20::XChaCha20;
5use cipher::StreamCipher;
6use digest::Mac;
7use generic_array::GenericArray;
8use generic_array::typenum::U32;
9use paseto_core::PasetoError;
10use paseto_core::paserk::PwWrapVersion;
11use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned, big_endian};
12
13use super::V4;
14
15fn wrap_keys(pass: &[u8], prefix: &Prefix) -> Result<(XChaCha20, Blake2bMac<U32>), PasetoError> {
16 use cipher::KeyIvInit;
17
18 let mut key = [0u8; 32];
19 prefix
20 .params
21 .pbkdf()?
22 .hash_password_into(pass, &prefix.salt, &mut key)
23 .map_err(|_| PasetoError::CryptoError)?;
24
25 let ek = kdf(&key, 0xFF);
26 let ak = kdf(&key, 0xFE);
27
28 let cipher = XChaCha20::new(&ek, (&prefix.nonce).into());
29 let mac = blake2::Blake2bMac::new_from_slice(&ak).expect("key should be valid");
30 Ok((cipher, mac))
31}
32
33#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
34#[repr(C)]
35struct Prefix {
36 salt: [u8; 16],
37 params: Params,
38 nonce: [u8; 24],
39}
40
41#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
42#[repr(C)]
43struct Suffix {
44 tag: [u8; 32],
45}
46
47#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Clone, Copy)]
48#[repr(C)]
49pub struct Params {
50 mem: big_endian::U64,
51 time: big_endian::U32,
52 para: big_endian::U32,
53}
54
55impl Default for Params {
56 fn default() -> Self {
57 const {
58 Self {
59 mem: big_endian::U64::new(argon2::Params::DEFAULT_M_COST as u64 * 1024),
60 time: big_endian::U32::new(argon2::Params::DEFAULT_T_COST),
61 para: big_endian::U32::new(argon2::Params::DEFAULT_P_COST),
62 }
63 }
64 }
65}
66
67impl Params {
68 fn pbkdf(&self) -> Result<argon2::Argon2<'static>, PasetoError> {
69 let mem = self.mem.get();
70 if !mem.is_multiple_of(1024) {
71 return Err(PasetoError::InvalidKey);
72 }
73 let mem = mem / 1024;
74 let mem = u32::try_from(mem).map_err(|_| PasetoError::InvalidKey)?;
75
76 let params = argon2::ParamsBuilder::new()
77 .m_cost(mem)
78 .p_cost(self.para.get())
79 .t_cost(self.time.get())
80 .build()
81 .map_err(|_| PasetoError::InvalidKey)?;
82
83 Ok(argon2::Argon2::new(
84 argon2::Algorithm::Argon2id,
85 argon2::Version::V0x13,
86 params,
87 ))
88 }
89}
90
91impl PwWrapVersion for V4 {
92 type Params = Params;
93
94 fn pw_wrap_key(
95 header: &'static str,
96 pass: &[u8],
97 params: &Params,
98 mut key_data: Vec<u8>,
99 ) -> Result<Vec<u8>, PasetoError> {
100 let mut out =
101 Vec::with_capacity(size_of::<Prefix>() + key_data.len() + size_of::<Suffix>());
102 out.extend_from_slice(&[0; size_of::<Prefix>()]);
103 let prefix = Prefix::mut_from_bytes(&mut out).expect("should be correct size");
104
105 prefix.params = *params;
106 getrandom::fill(&mut prefix.salt).map_err(|_| PasetoError::CryptoError)?;
107 getrandom::fill(&mut prefix.nonce).map_err(|_| PasetoError::CryptoError)?;
108
109 let (mut cipher, mut mac) = wrap_keys(pass, prefix)?;
110 cipher.apply_keystream(&mut key_data);
111 auth(&mut mac, header, prefix, &key_data);
112
113 out.extend_from_slice(&key_data);
114 out.extend_from_slice(&mac.finalize().into_bytes());
115 Ok(out)
116 }
117
118 fn get_params(key_data: &[u8]) -> Result<Self::Params, PasetoError> {
119 let (prefix, _) = Prefix::ref_from_prefix(key_data).map_err(|_| PasetoError::InvalidKey)?;
120 Ok(prefix.params)
121 }
122
123 fn pw_unwrap_key<'key>(
124 header: &'static str,
125 pass: &[u8],
126 key_data: &'key mut [u8],
127 ) -> Result<&'key [u8], PasetoError> {
128 let (prefix, ciphertext) =
129 Prefix::mut_from_prefix(key_data).map_err(|_| PasetoError::InvalidKey)?;
130 let (ciphertext, suffix) =
131 Suffix::mut_from_suffix(ciphertext).map_err(|_| PasetoError::InvalidKey)?;
132
133 let (mut cipher, mut mac) = wrap_keys(pass, prefix)?;
134 auth(&mut mac, header, prefix, ciphertext);
135 mac.verify((&suffix.tag).into())
136 .map_err(|_| PasetoError::CryptoError)?;
137
138 cipher.apply_keystream(ciphertext);
139
140 Ok(ciphertext)
141 }
142}
143
144fn kdf(key: &[u8], sep: u8) -> GenericArray<u8, U32> {
145 use digest::Digest;
146
147 let mut mac = blake2::Blake2b::<U32>::default();
148 mac.update([sep]);
149 mac.update(key);
150 mac.finalize()
151}
152
153fn auth(
154 mac: &mut blake2::Blake2bMac<U32>,
155 header: &'static str,
156 prefix: &Prefix,
157 ciphertext: &[u8],
158) {
159 mac.update(b"k4");
160 mac.update(header.as_bytes());
161 mac.update(prefix.as_bytes());
162 mac.update(ciphertext);
163}