paseto_v1/core/
pw_wrap.rs

1use 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}