1use alloc::boxed::Box;
2use alloc::vec::Vec;
3
4use cipher::StreamCipher;
5use curve25519_dalek::EdwardsPoint;
6use digest::Mac;
7use generic_array::typenum::U32;
8use paseto_core::PasetoError;
9use paseto_core::key::HasKey;
10use paseto_core::paserk::{PkeSealingVersion, PkeUnsealingVersion};
11use paseto_core::version::{PkePublic, PkeSecret, Public, Secret};
12
13use super::{LocalKey, PublicKey, SecretKey, V4};
14
15impl HasKey<PkePublic> for V4 {
16 type Key = PublicKey;
17 fn decode(bytes: &[u8]) -> Result<PublicKey, PasetoError> {
18 <V4 as HasKey<Public>>::decode(bytes)
19 }
20 fn encode(key: &PublicKey) -> Box<[u8]> {
21 <V4 as HasKey<Public>>::encode(key)
22 }
23}
24
25impl HasKey<PkeSecret> for V4 {
26 type Key = SecretKey;
27 fn decode(bytes: &[u8]) -> Result<SecretKey, PasetoError> {
28 <V4 as HasKey<Secret>>::decode(bytes)
29 }
30 fn encode(key: &SecretKey) -> Box<[u8]> {
31 <V4 as HasKey<Secret>>::encode(key)
32 }
33}
34
35impl PkeSealingVersion for V4 {
36 fn seal_key(sealing_key: &PublicKey, key: LocalKey) -> Result<Box<[u8]>, PasetoError> {
37 use cipher::KeyIvInit;
38 use curve25519_dalek::edwards::CompressedEdwardsY;
39 use curve25519_dalek::scalar::{Scalar, clamp_integer};
40 use digest::Digest;
41
42 let pk = CompressedEdwardsY(*sealing_key.0.as_bytes());
44
45 let xpk = pk.decompress().unwrap().to_montgomery();
47
48 let esk = Scalar::from_bytes_mod_order(clamp_integer({
49 let mut esk = [0; 32];
50 getrandom::fill(&mut esk).map_err(|_| PasetoError::CryptoError)?;
51 esk
52 }));
53 let epk = EdwardsPoint::mul_base(&esk).to_montgomery();
54
55 let xk = esk * xpk;
57
58 let mut ek = blake2::Blake2b::new();
59 ek.update(b"\x01k4.seal.");
60 ek.update(xk.as_bytes());
61 ek.update(epk.as_bytes());
62 ek.update(xpk.as_bytes());
63 let ek = ek.finalize();
64
65 let mut n = blake2::Blake2b::new();
66 n.update(epk.as_bytes());
67 n.update(xpk.as_bytes());
68 let n = n.finalize();
69
70 let mut edk = key.0;
71 chacha20::XChaCha20::new(&ek, &n).apply_keystream(&mut edk);
72
73 let mut ak = blake2::Blake2b::<U32>::new();
74 ak.update(b"\x02k4.seal.");
75 ak.update(xk.as_bytes());
76 ak.update(epk.as_bytes());
77 ak.update(xpk.as_bytes());
78 let ak = ak.finalize();
79
80 let mut tag = blake2::Blake2bMac::<U32>::new_from_slice(&ak).unwrap();
81 tag.update(b"k4.seal.");
82 tag.update(epk.as_bytes());
83 tag.update(&edk);
84 let tag = tag.finalize().into_bytes();
85
86 let mut output = Vec::with_capacity(96);
87 output.extend_from_slice(&tag);
88 output.extend_from_slice(epk.as_bytes());
89 output.extend_from_slice(&edk);
90
91 Ok(output.into_boxed_slice())
92 }
93}
94
95impl PkeUnsealingVersion for V4 {
96 fn unseal_key(
97 unsealing_key: &SecretKey,
98 mut key_data: Box<[u8]>,
99 ) -> Result<LocalKey, PasetoError> {
100 use cipher::KeyIvInit;
101 use digest::Digest;
102
103 let (tag, key_data) = key_data
104 .split_first_chunk_mut::<32>()
105 .ok_or(PasetoError::InvalidKey)?;
106 let (epk, edk) = key_data
107 .split_first_chunk_mut::<32>()
108 .ok_or(PasetoError::InvalidKey)?;
109 let edk: &mut [u8; 32] = edk.try_into().map_err(|_| PasetoError::InvalidKey)?;
110
111 let epk = curve25519_dalek::MontgomeryPoint(*epk);
112
113 let xpk = EdwardsPoint::mul_base(&unsealing_key.1.scalar).to_montgomery();
115
116 let xk = unsealing_key.1.scalar * epk;
118
119 let mut ak = blake2::Blake2b::<U32>::new();
120 ak.update(b"\x02k4.seal.");
121 ak.update(xk.as_bytes());
122 ak.update(epk.as_bytes());
123 ak.update(xpk.as_bytes());
124 let ak = ak.finalize();
125
126 let mut t2 = blake2::Blake2bMac::<U32>::new_from_slice(&ak).unwrap();
127 t2.update(b"k4.seal.");
128 t2.update(epk.as_bytes());
129 t2.update(edk);
130
131 t2.verify((&*tag).into())
133 .map_err(|_| PasetoError::CryptoError)?;
134
135 let mut ek = blake2::Blake2b::new();
136 ek.update(b"\x01k4.seal.");
137 ek.update(xk.as_bytes());
138 ek.update(epk.as_bytes());
139 ek.update(xpk.as_bytes());
140 let ek = ek.finalize();
141
142 let mut n = blake2::Blake2b::new();
143 n.update(epk.as_bytes());
144 n.update(xpk.as_bytes());
145 let n = n.finalize();
146
147 chacha20::XChaCha20::new(&ek, &n).apply_keystream(edk);
148
149 Ok(LocalKey(*edk))
150 }
151}