paseto_v3/core/
pke.rs

1use alloc::boxed::Box;
2use alloc::vec::Vec;
3
4use cipher::StreamCipher;
5use generic_array::sequence::Split;
6use hmac::Mac;
7use paseto_core::PasetoError;
8use paseto_core::key::HasKey;
9use paseto_core::paserk::{PkeSealingVersion, PkeUnsealingVersion};
10use paseto_core::version::{PkePublic, PkeSecret, Public, Secret};
11use sha2::Digest;
12
13use super::{LocalKey, PublicKey, SecretKey, V3};
14
15impl HasKey<PkePublic> for V3 {
16    type Key = PublicKey;
17    fn decode(bytes: &[u8]) -> Result<PublicKey, PasetoError> {
18        <V3 as HasKey<Public>>::decode(bytes)
19    }
20    fn encode(key: &PublicKey) -> Box<[u8]> {
21        <V3 as HasKey<Public>>::encode(key)
22    }
23}
24
25impl HasKey<PkeSecret> for V3 {
26    type Key = SecretKey;
27    fn decode(bytes: &[u8]) -> Result<SecretKey, PasetoError> {
28        <V3 as HasKey<Secret>>::decode(bytes)
29    }
30    fn encode(key: &SecretKey) -> Box<[u8]> {
31        <V3 as HasKey<Secret>>::encode(key)
32    }
33}
34
35impl PkeSealingVersion for V3 {
36    fn seal_key(sealing_key: &PublicKey, key: LocalKey) -> Result<Box<[u8]>, PasetoError> {
37        use cipher::KeyIvInit;
38        use p384::EncodedPoint;
39        use p384::ecdh::diffie_hellman;
40        use p384::elliptic_curve::sec1::ToEncodedPoint;
41
42        let pk = sealing_key.0.to_encoded_point(true);
43
44        let esk = p384::SecretKey::from(SecretKey::random()?.0);
45        let epk: EncodedPoint = esk.public_key().to_encoded_point(true);
46
47        let xk = diffie_hellman(esk.to_nonzero_scalar(), sealing_key.0.as_affine());
48
49        let mut ek = sha2::Sha384::new();
50        ek.update(b"\x01k3.seal.");
51        ek.update(xk.raw_secret_bytes());
52        ek.update(epk);
53        ek.update(pk.as_bytes());
54        let (ek, n) = ek.finalize().split();
55
56        let mut ak = sha2::Sha384::new();
57        ak.update(b"\x02k3.seal.");
58        ak.update(xk.raw_secret_bytes());
59        ak.update(epk);
60        ak.update(pk.as_bytes());
61        let ak = ak.finalize();
62
63        let mut edk = key.0;
64        ctr::Ctr64BE::<aes::Aes256>::new(&ek, &n).apply_keystream(&mut edk);
65
66        let mut tag = hmac::Hmac::<sha2::Sha384>::new_from_slice(&ak).unwrap();
67        tag.update(b"k3.seal.");
68        tag.update(epk.as_bytes());
69        tag.update(&edk);
70        let tag = tag.finalize().into_bytes();
71
72        let mut output = Vec::with_capacity(48 + 49 + 32);
73        output.extend_from_slice(&tag);
74        output.extend_from_slice(epk.as_bytes());
75        output.extend_from_slice(&edk);
76
77        Ok(output.into_boxed_slice())
78    }
79}
80
81impl PkeUnsealingVersion for V3 {
82    fn unseal_key(
83        unsealing_key: &SecretKey,
84        mut key_data: Box<[u8]>,
85    ) -> Result<LocalKey, PasetoError> {
86        use cipher::KeyIvInit;
87        use p384::ecdh::diffie_hellman;
88        use p384::{AffinePoint, EncodedPoint};
89
90        let (tag, key_data) = key_data
91            .split_first_chunk_mut::<48>()
92            .ok_or(PasetoError::InvalidKey)?;
93        let (epk, edk) = key_data
94            .split_first_chunk_mut::<49>()
95            .ok_or(PasetoError::InvalidKey)?;
96
97        let epk: &[u8; 49] = &*epk;
98        let edk: &mut [u8; 32] = edk.try_into().map_err(|_| PasetoError::InvalidKey)?;
99
100        let sk = p384::SecretKey::from(&unsealing_key.0);
101
102        let pk: EncodedPoint = sk.public_key().into();
103        let pk = pk.compress();
104
105        let epk_point = EncodedPoint::from_bytes(epk).map_err(|_| PasetoError::CryptoError)?;
106        let epk_point = AffinePoint::try_from(&epk_point).map_err(|_| PasetoError::CryptoError)?;
107
108        let xk = diffie_hellman(sk.to_nonzero_scalar(), epk_point);
109
110        let mut ak = sha2::Sha384::new();
111        ak.update(b"\x02k3.seal.");
112        ak.update(xk.raw_secret_bytes());
113        ak.update(epk);
114        ak.update(pk.as_bytes());
115        let ak = ak.finalize();
116
117        let mut t2 = hmac::Hmac::<sha2::Sha384>::new_from_slice(&ak).unwrap();
118        t2.update(b"k3.seal.");
119        t2.update(epk);
120        t2.update(edk);
121
122        // step 6: Compare t2 with t, using a constant-time compare function. If it does not match, abort.
123        t2.verify((&*tag).into())
124            .map_err(|_| PasetoError::CryptoError)?;
125
126        let mut ek = sha2::Sha384::new();
127        ek.update(b"\x01k3.seal.");
128        ek.update(xk.raw_secret_bytes());
129        ek.update(epk);
130        ek.update(pk.as_bytes());
131        let (ek, n) = ek.finalize().split();
132
133        ctr::Ctr64BE::<aes::Aes256>::new(&ek, &n).apply_keystream(edk);
134
135        Ok(LocalKey(*edk))
136    }
137}