1use std::fmt;
7
8use subtle::ConstantTimeEq;
9use zeroize::{Zeroize, ZeroizeOnDrop};
10
11use crate::constants::{
12 CRYPTO_BOX_BEFORENMBYTES, CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SECRETKEYBYTES,
13};
14use crate::error::Error;
15use crate::types::{ByteArray, Bytes, MutByteArray, MutBytes, StackByteArray};
16
17type InnerKey = StackByteArray<CRYPTO_BOX_BEFORENMBYTES>;
18
19#[derive(Zeroize, ZeroizeOnDrop, Clone)]
29pub struct PrecalcSecretKey<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize>(InnerKey);
30
31impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize> fmt::Debug
32 for PrecalcSecretKey<InnerKey>
33{
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 f.debug_tuple("PrecalcSecretKey")
36 .field(&"[REDACTED]")
37 .finish()
38 }
39}
40
41impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize> PartialEq
42 for PrecalcSecretKey<InnerKey>
43{
44 fn eq(&self, other: &Self) -> bool {
45 self.0.as_slice().ct_eq(other.0.as_slice()).into()
46 }
47}
48
49impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize> Eq for PrecalcSecretKey<InnerKey> {}
50
51impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Bytes + Zeroize> Bytes
52 for PrecalcSecretKey<InnerKey>
53{
54 #[inline]
55 fn as_slice(&self) -> &[u8] {
56 self.0.as_slice()
57 }
58
59 #[inline]
60 fn is_empty(&self) -> bool {
61 self.0.is_empty()
62 }
63
64 #[inline]
65 fn len(&self) -> usize {
66 self.0.len()
67 }
68}
69
70impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize> ByteArray<CRYPTO_BOX_BEFORENMBYTES>
71 for PrecalcSecretKey<InnerKey>
72{
73 #[inline]
74 fn as_array(&self) -> &[u8; CRYPTO_BOX_BEFORENMBYTES] {
75 self.0.as_array()
76 }
77}
78
79impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize + MutBytes> MutBytes
80 for PrecalcSecretKey<InnerKey>
81{
82 #[inline]
83 fn as_mut_slice(&mut self) -> &mut [u8] {
84 self.0.as_mut_slice()
85 }
86
87 #[inline]
88 fn copy_from_slice(&mut self, other: &[u8]) {
89 self.0.copy_from_slice(other);
90 }
91}
92
93impl<InnerKey: MutByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize>
94 MutByteArray<CRYPTO_BOX_BEFORENMBYTES> for PrecalcSecretKey<InnerKey>
95{
96 #[inline]
97 fn as_mut_array(&mut self) -> &mut [u8; CRYPTO_BOX_BEFORENMBYTES] {
98 self.0.as_mut_array()
99 }
100}
101
102impl PrecalcSecretKey<InnerKey> {
103 #[inline]
113 pub fn precalculate<
114 ThirdPartyPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
115 SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
116 >(
117 third_party_public_key: &ThirdPartyPublicKey,
118 secret_key: &SecretKey,
119 ) -> Result<Self, Error> {
120 use crate::classic::crypto_box::crypto_box_beforenm;
121
122 Ok(Self(
123 crypto_box_beforenm(third_party_public_key.as_array(), secret_key.as_array())?.into(),
124 ))
125 }
126}
127
128#[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
129#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
130pub mod protected {
131 use super::*;
133 pub use crate::protected::*;
134
135 type InnerKey = HeapByteArray<CRYPTO_BOX_PUBLICKEYBYTES>;
136
137 impl PrecalcSecretKey<Locked<InnerKey>> {
138 pub fn precalculate_locked<
153 ThirdPartyPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
154 SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
155 >(
156 third_party_public_key: &ThirdPartyPublicKey,
157 secret_key: &SecretKey,
158 ) -> Result<Self, Error> {
159 use crate::classic::crypto_box::crypto_box_beforenm;
160
161 let mut precalc = HeapByteArray::<CRYPTO_BOX_BEFORENMBYTES>::new_locked()?;
162 let mut key =
163 crypto_box_beforenm(third_party_public_key.as_array(), secret_key.as_array())?;
164
165 precalc.copy_from_slice(&key);
166 key.zeroize();
167
168 Ok(PrecalcSecretKey(precalc))
169 }
170 }
171
172 impl PrecalcSecretKey<LockedRO<InnerKey>> {
173 pub fn precalculate_readonly_locked<
190 ThirdPartyPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
191 SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
192 >(
193 third_party_public_key: &ThirdPartyPublicKey,
194 secret_key: &SecretKey,
195 ) -> Result<Self, Error> {
196 use crate::classic::crypto_box::crypto_box_beforenm;
197
198 let mut precalc = HeapByteArray::<CRYPTO_BOX_BEFORENMBYTES>::new_locked()?;
199 let mut key =
200 crypto_box_beforenm(third_party_public_key.as_array(), secret_key.as_array())?;
201
202 precalc.copy_from_slice(&key);
203 key.zeroize();
204
205 Ok(PrecalcSecretKey(precalc.mprotect_readonly()?))
206 }
207 }
208}
209
210impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize> std::ops::Deref
211 for PrecalcSecretKey<InnerKey>
212{
213 type Target = InnerKey;
214
215 fn deref(&self) -> &Self::Target {
216 &self.0
217 }
218}
219
220impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize> std::ops::DerefMut
221 for PrecalcSecretKey<InnerKey>
222{
223 fn deref_mut(&mut self) -> &mut Self::Target {
224 &mut self.0
225 }
226}
227#[cfg(test)]
228mod tests {
229 use super::*;
230
231 #[test]
232 fn precalculated_key_debug_redacts_contents_and_equality_is_value_based() {
233 let key = PrecalcSecretKey(StackByteArray::from([0xabu8; CRYPTO_BOX_BEFORENMBYTES]));
234 let same = key.clone();
235 let different = PrecalcSecretKey(StackByteArray::from([0xcdu8; CRYPTO_BOX_BEFORENMBYTES]));
236
237 assert_eq!(format!("{key:?}"), "PrecalcSecretKey(\"[REDACTED]\")");
238 assert_eq!(key, same);
239 assert_ne!(key, different);
240 }
241 use crate::constants::{CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SECRETKEYBYTES};
242
243 #[test]
244 fn test_precalculate() {
245 let mut public_key = StackByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::default();
246 public_key.as_mut_array()[0] = 9;
247 let secret_key = StackByteArray::<CRYPTO_BOX_SECRETKEYBYTES>::default();
248 let precalc_key = PrecalcSecretKey::precalculate(&public_key, &secret_key).unwrap();
249 assert!(!precalc_key.is_empty());
250 assert_eq!(precalc_key.len(), CRYPTO_BOX_BEFORENMBYTES);
251
252 let low_order_public_key = StackByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::default();
253 assert!(PrecalcSecretKey::precalculate(&low_order_public_key, &secret_key).is_err());
254 }
255
256 #[cfg(all(feature = "protected", any(unix, windows)))]
257 #[test]
258 fn test_precalculate_locked() {
259 let mut public_key = StackByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::default();
260 public_key.as_mut_array()[0] = 9;
261 let secret_key = StackByteArray::<CRYPTO_BOX_SECRETKEYBYTES>::default();
262 let mut precalc_key =
263 PrecalcSecretKey::precalculate_locked(&public_key, &secret_key).unwrap();
264 assert!(!precalc_key.is_empty());
265 assert_eq!(precalc_key.len(), CRYPTO_BOX_BEFORENMBYTES);
266
267 precalc_key.as_mut_slice()[0] = 0;
269 precalc_key.as_mut_array()[0] = 1;
270
271 let low_order_public_key = StackByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::default();
272 assert!(PrecalcSecretKey::precalculate_locked(&low_order_public_key, &secret_key).is_err());
273 }
274
275 #[cfg(all(feature = "protected", any(unix, windows)))]
276 #[test]
277 fn test_precalculate_readonly_locked() {
278 let mut public_key = StackByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::default();
279 public_key.as_mut_array()[0] = 9;
280 let secret_key = StackByteArray::<CRYPTO_BOX_SECRETKEYBYTES>::default();
281 let precalc_key =
282 PrecalcSecretKey::precalculate_readonly_locked(&public_key, &secret_key).unwrap();
283 assert!(!precalc_key.is_empty());
284 assert_eq!(precalc_key.len(), CRYPTO_BOX_BEFORENMBYTES);
285
286 let low_order_public_key = StackByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::default();
287 assert!(
288 PrecalcSecretKey::precalculate_readonly_locked(&low_order_public_key, &secret_key)
289 .is_err()
290 );
291 }
292}