paseto_v1/core/
pw_wrap.rs1use alloc::vec::Vec;
2
3use cipher::StreamCipher;
4use digest::Mac;
5use generic_array::GenericArray;
6use generic_array::sequence::Split;
7use generic_array::typenum::U48;
8use paseto_core::PasetoError;
9use paseto_core::paserk::PwWrapVersion;
10use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned, big_endian};
11
12use super::V1;
13
14fn wrap_keys(
15 pass: &[u8],
16 prefix: &Prefix,
17) -> (ctr::Ctr64BE<aes::Aes256>, hmac::Hmac<sha2::Sha384>) {
18 use cipher::KeyIvInit;
19
20 let key = pbkdf2::pbkdf2_array::<hmac::Hmac<sha2::Sha384>, 32>(
21 pass,
22 &prefix.salt,
23 prefix.params.iterations.get(),
24 )
25 .expect("HMAC accepts all password length inputs");
26
27 let (ek, _) = kdf(&key, 0xFF).split();
28 let ak = kdf(&key, 0xFE);
29
30 let cipher = ctr::Ctr64BE::<aes::Aes256>::new(&ek, (&prefix.nonce).into());
31 let mac = hmac::Hmac::new_from_slice(&ak).expect("key should be valid");
32 (cipher, mac)
33}
34
35#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
36#[repr(C)]
37struct Prefix {
38 salt: [u8; 32],
39 params: Params,
40 nonce: [u8; 16],
41}
42
43#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
44#[repr(C)]
45struct Suffix {
46 tag: [u8; 48],
47}
48
49#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Clone, Copy)]
50#[repr(C)]
51pub struct Params {
52 iterations: big_endian::U32,
53}
54
55impl Default for Params {
56 fn default() -> Self {
57 const {
58 Self {
59 iterations: big_endian::U32::new(100_000),
60 }
61 }
62 }
63}
64
65impl PwWrapVersion for V1 {
66 type Params = Params;
67
68 fn pw_wrap_key(
69 header: &'static str,
70 pass: &[u8],
71 params: &Params,
72 mut key_data: Vec<u8>,
73 ) -> Result<Vec<u8>, PasetoError> {
74 let mut out =
75 Vec::with_capacity(size_of::<Prefix>() + key_data.len() + size_of::<Suffix>());
76 out.extend_from_slice(&[0; size_of::<Prefix>()]);
77 let prefix = Prefix::mut_from_bytes(&mut out).expect("should be correct size");
78
79 prefix.params = *params;
80 getrandom::fill(&mut prefix.salt).map_err(|_| PasetoError::CryptoError)?;
81 getrandom::fill(&mut prefix.nonce).map_err(|_| PasetoError::CryptoError)?;
82
83 let (mut cipher, mut mac) = wrap_keys(pass, prefix);
84 cipher.apply_keystream(&mut key_data);
85 auth(&mut mac, header, prefix, &key_data);
86
87 out.extend_from_slice(&key_data);
88 out.extend_from_slice(&mac.finalize().into_bytes());
89 Ok(out)
90 }
91
92 fn get_params(key_data: &[u8]) -> Result<Self::Params, PasetoError> {
93 let (prefix, _) = Prefix::ref_from_prefix(key_data).map_err(|_| PasetoError::InvalidKey)?;
94 Ok(prefix.params)
95 }
96
97 fn pw_unwrap_key<'key>(
98 header: &'static str,
99 pass: &[u8],
100 key_data: &'key mut [u8],
101 ) -> Result<&'key [u8], PasetoError> {
102 let (prefix, ciphertext) =
103 Prefix::mut_from_prefix(key_data).map_err(|_| PasetoError::InvalidKey)?;
104 let (ciphertext, suffix) =
105 Suffix::mut_from_suffix(ciphertext).map_err(|_| PasetoError::InvalidKey)?;
106
107 let (mut cipher, mut mac) = wrap_keys(pass, prefix);
108 auth(&mut mac, header, prefix, ciphertext);
109 mac.verify((&suffix.tag).into())
110 .map_err(|_| PasetoError::CryptoError)?;
111
112 cipher.apply_keystream(ciphertext);
113
114 Ok(ciphertext)
115 }
116}
117
118fn kdf(key: &[u8], sep: u8) -> GenericArray<u8, U48> {
119 use digest::Digest;
120
121 let mut mac = sha2::Sha384::default();
122 mac.update([sep]);
123 mac.update(key);
124 mac.finalize()
125}
126
127fn auth(
128 mac: &mut hmac::Hmac<sha2::Sha384>,
129 header: &'static str,
130 prefix: &Prefix,
131 ciphertext: &[u8],
132) {
133 mac.update(b"k1");
134 mac.update(header.as_bytes());
135 mac.update(prefix.as_bytes());
136 mac.update(ciphertext);
137}