dryoc/
precalc.rs

1//! Precalculated secret key for use with `precalc_*` functions in
2//! [`crate::dryocbox::DryocBox`]
3//!
4//! You may want to use `precalc_*` functions if you need to
5//! encrypt/decrypt multiple messages between the same sender and receiver.
6use zeroize::{Zeroize, ZeroizeOnDrop};
7
8use crate::constants::{
9    CRYPTO_BOX_BEFORENMBYTES, CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SECRETKEYBYTES,
10};
11use crate::types::{ByteArray, Bytes, MutByteArray, MutBytes, StackByteArray};
12
13type InnerKey = StackByteArray<CRYPTO_BOX_BEFORENMBYTES>;
14
15/// Precalculated secret key for use with `precalc_*` functions in
16/// [`crate::dryocbox::DryocBox`].
17///
18/// You probably want to use `precalc_*` functions if you need to
19/// encrypt/decrypt multiple messages between the same sender and receiver.
20/// These functions save computation time by using [`PrecalcSecretKey`]
21/// instead of computing the shared secret every time.
22///
23/// Using precalculated secret keys is compatible with libsodium's
24/// `crypto_box_beforenm`.
25#[derive(Zeroize, ZeroizeOnDrop, Debug, PartialEq, Eq, Clone)]
26pub struct PrecalcSecretKey<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize>(InnerKey);
27
28impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Bytes + Zeroize> Bytes
29    for PrecalcSecretKey<InnerKey>
30{
31    #[inline]
32    fn as_slice(&self) -> &[u8] {
33        self.0.as_slice()
34    }
35
36    #[inline]
37    fn is_empty(&self) -> bool {
38        self.0.is_empty()
39    }
40
41    #[inline]
42    fn len(&self) -> usize {
43        self.0.len()
44    }
45}
46
47impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize> ByteArray<CRYPTO_BOX_BEFORENMBYTES>
48    for PrecalcSecretKey<InnerKey>
49{
50    #[inline]
51    fn as_array(&self) -> &[u8; CRYPTO_BOX_BEFORENMBYTES] {
52        self.0.as_array()
53    }
54}
55
56impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize + MutBytes> MutBytes
57    for PrecalcSecretKey<InnerKey>
58{
59    #[inline]
60    fn as_mut_slice(&mut self) -> &mut [u8] {
61        self.0.as_mut_slice()
62    }
63
64    #[inline]
65    fn copy_from_slice(&mut self, other: &[u8]) {
66        self.0.copy_from_slice(other);
67    }
68}
69
70impl<InnerKey: MutByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize>
71    MutByteArray<CRYPTO_BOX_BEFORENMBYTES> for PrecalcSecretKey<InnerKey>
72{
73    #[inline]
74    fn as_mut_array(&mut self) -> &mut [u8; CRYPTO_BOX_BEFORENMBYTES] {
75        self.0.as_mut_array()
76    }
77}
78
79impl PrecalcSecretKey<InnerKey> {
80    /// Computes a stack-allocated shared secret key for the given
81    /// `third_party_public_key` and `secret_key`.
82    ///
83    /// Compatible with libsodium's `crypto_box_beforenm`.
84    #[inline]
85    pub fn precalculate<
86        ThirdPartyPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
87        SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
88    >(
89        third_party_public_key: &ThirdPartyPublicKey,
90        secret_key: &SecretKey,
91    ) -> Self {
92        use crate::classic::crypto_box::crypto_box_beforenm;
93
94        Self(crypto_box_beforenm(third_party_public_key.as_array(), secret_key.as_array()).into())
95    }
96}
97
98#[cfg(any(feature = "nightly", all(doc, not(doctest))))]
99#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))]
100pub mod protected {
101    //! #  Protected memory for [`PrecalcSecretKey`]
102    use super::*;
103    pub use crate::protected::*;
104
105    type InnerKey = HeapByteArray<CRYPTO_BOX_PUBLICKEYBYTES>;
106
107    impl PrecalcSecretKey<Locked<InnerKey>> {
108        /// Computes a heap-allocated, page-aligned, locked shared secret key
109        /// for the given `third_party_public_key` and `secret_key`.
110        ///
111        /// Compatible with libsodium's `crypto_box_beforenm`.
112        pub fn precalculate_locked<
113            ThirdPartyPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
114            SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
115        >(
116            third_party_public_key: &ThirdPartyPublicKey,
117            secret_key: &SecretKey,
118        ) -> Result<Self, std::io::Error> {
119            use crate::classic::crypto_box::crypto_box_beforenm;
120
121            let mut precalc = HeapByteArray::<CRYPTO_BOX_BEFORENMBYTES>::new_locked()?;
122            let mut key =
123                crypto_box_beforenm(third_party_public_key.as_array(), secret_key.as_array());
124
125            precalc.copy_from_slice(&key);
126            key.zeroize();
127
128            Ok(PrecalcSecretKey(precalc))
129        }
130    }
131
132    impl PrecalcSecretKey<LockedRO<InnerKey>> {
133        /// Computes a heap-allocated, page-aligned, locked, read-only shared
134        /// secret key for the given `third_party_public_key` and
135        /// `secret_key`.
136        ///
137        /// Compatible with libsodium's `crypto_box_beforenm`.
138        pub fn precalculate_readonly_locked<
139            ThirdPartyPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
140            SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
141        >(
142            third_party_public_key: &ThirdPartyPublicKey,
143            secret_key: &SecretKey,
144        ) -> Result<Self, std::io::Error> {
145            use crate::classic::crypto_box::crypto_box_beforenm;
146
147            let mut precalc = HeapByteArray::<CRYPTO_BOX_BEFORENMBYTES>::new_locked()?;
148            let mut key =
149                crypto_box_beforenm(third_party_public_key.as_array(), secret_key.as_array());
150
151            precalc.copy_from_slice(&key);
152            key.zeroize();
153
154            Ok(PrecalcSecretKey(precalc.mprotect_readonly()?))
155        }
156    }
157}
158
159impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize> std::ops::Deref
160    for PrecalcSecretKey<InnerKey>
161{
162    type Target = InnerKey;
163
164    fn deref(&self) -> &Self::Target {
165        &self.0
166    }
167}
168
169impl<InnerKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize> std::ops::DerefMut
170    for PrecalcSecretKey<InnerKey>
171{
172    fn deref_mut(&mut self) -> &mut Self::Target {
173        &mut self.0
174    }
175}
176#[cfg(test)]
177mod tests {
178    use super::*;
179    use crate::constants::{CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SECRETKEYBYTES};
180
181    #[test]
182    fn test_precalculate() {
183        let public_key = StackByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::default();
184        let secret_key = StackByteArray::<CRYPTO_BOX_SECRETKEYBYTES>::default();
185        let precalc_key = PrecalcSecretKey::precalculate(&public_key, &secret_key);
186        assert!(!precalc_key.is_empty());
187        assert_eq!(precalc_key.len(), CRYPTO_BOX_BEFORENMBYTES);
188    }
189
190    #[cfg(feature = "nightly")]
191    #[test]
192    fn test_precalculate_locked() {
193        let public_key = StackByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::default();
194        let secret_key = StackByteArray::<CRYPTO_BOX_SECRETKEYBYTES>::default();
195        let mut precalc_key =
196            PrecalcSecretKey::precalculate_locked(&public_key, &secret_key).unwrap();
197        assert!(!precalc_key.is_empty());
198        assert_eq!(precalc_key.len(), CRYPTO_BOX_BEFORENMBYTES);
199
200        // should be able to write now without blowing up
201        precalc_key.as_mut_slice()[0] = 0;
202        precalc_key.as_mut_array()[0] = 1;
203        precalc_key.copy_from_slice(&precalc_key.as_slice().to_owned());
204    }
205
206    #[cfg(feature = "nightly")]
207    #[test]
208    fn test_precalculate_readonly_locked() {
209        let public_key = StackByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::default();
210        let secret_key = StackByteArray::<CRYPTO_BOX_SECRETKEYBYTES>::default();
211        let precalc_key =
212            PrecalcSecretKey::precalculate_readonly_locked(&public_key, &secret_key).unwrap();
213        assert!(!precalc_key.is_empty());
214        assert_eq!(precalc_key.len(), CRYPTO_BOX_BEFORENMBYTES);
215    }
216}