crypto_box/
secret_key.rs

1use crate::{PublicKey, KEY_SIZE};
2use core::{
3    array::TryFromSliceError,
4    fmt::{self, Debug},
5};
6use curve25519_dalek::{
7    scalar::{clamp_integer, Scalar},
8    MontgomeryPoint,
9};
10use subtle::ConstantTimeEq;
11use zeroize::Zeroize;
12
13#[cfg(feature = "rand_core")]
14use aead::rand_core::CryptoRngCore;
15
16#[cfg(feature = "seal")]
17use {
18    crate::{get_seal_nonce, SalsaBox},
19    aead::Aead,
20    alloc::vec::Vec,
21};
22
23#[cfg(feature = "serde")]
24use serdect::serde::{de, ser, Deserialize, Serialize};
25
26/// A `crypto_box` secret key.
27#[derive(Clone)]
28pub struct SecretKey {
29    pub(crate) bytes: [u8; KEY_SIZE],
30    pub(crate) scalar: Scalar,
31}
32
33impl SecretKey {
34    /// Initialize [`SecretKey`] from a byte array.
35    #[inline]
36    pub fn from_bytes(bytes: [u8; KEY_SIZE]) -> Self {
37        let scalar = Scalar::from_bytes_mod_order(clamp_integer(bytes));
38        Self { bytes, scalar }
39    }
40
41    /// Initialize [`SecretKey`] from a byte slice.
42    ///
43    /// Returns [`TryFromSliceError`] if the slice length is not exactly equal
44    /// to [`KEY_SIZE`].
45    pub fn from_slice(slice: &[u8]) -> Result<Self, TryFromSliceError> {
46        slice.try_into().map(Self::from_bytes)
47    }
48
49    /// Generate a random [`SecretKey`].
50    #[cfg(feature = "rand_core")]
51    pub fn generate(csprng: &mut impl CryptoRngCore) -> Self {
52        let mut bytes = [0u8; KEY_SIZE];
53        csprng.fill_bytes(&mut bytes);
54        bytes.into()
55    }
56
57    /// Get the [`PublicKey`] which corresponds to this [`SecretKey`]
58    pub fn public_key(&self) -> PublicKey {
59        PublicKey(MontgomeryPoint::mul_base(&self.scalar))
60    }
61
62    /// Serialize [`SecretKey`] to bytes.
63    ///
64    /// # ⚠️Warning
65    ///
66    /// The serialized bytes are secret key material. Please treat them with
67    /// the care they deserve!
68    ///
69    /// # `Scalar` conversion notes
70    ///
71    /// If you are using the `From<Scalar>` impl on [`SecretKey`] (as opposed
72    /// to using [`SecretKey::from_bytes`] or one of the other methods that
73    /// decodes a secret key from bytes), this method will return the same
74    /// value as `Scalar::to_bytes`, which may reflect "clamping" if it was
75    /// applied to the original `Scalar`.
76    ///
77    /// In such cases, it may be undesirable to call this method, since such a
78    /// value may not reflect the original scalar prior to clamping. We suggest
79    /// you don't call this method when using `From<Scalar>` unless you know
80    /// what you're doing.
81    ///
82    /// Calling [`SecretKey::to_scalar`] can be used to safely round-trip the
83    /// scalar value in such cases.
84    pub fn to_bytes(&self) -> [u8; KEY_SIZE] {
85        self.bytes
86    }
87
88    /// Obtain the inner [`Scalar`] value of this [`SecretKey`].
89    ///
90    /// # ⚠️Warning
91    ///
92    /// This value is key material. Please treat it with the care it deserves!
93    pub fn to_scalar(&self) -> Scalar {
94        self.scalar
95    }
96
97    /// Implementation of `crypto_box_seal_open` function from [libsodium "sealed boxes"].
98    ///
99    /// Sealed boxes are designed to anonymously send messages to a recipient given their public key.
100    ///
101    /// [libsodium "sealed boxes"]: https://doc.libsodium.org/public-key_cryptography/sealed_boxes
102    #[cfg(feature = "seal")]
103    pub fn unseal(&self, ciphertext: &[u8]) -> Result<Vec<u8>, aead::Error> {
104        if ciphertext.len() <= KEY_SIZE {
105            return Err(aead::Error);
106        }
107
108        let ephemeral_sk: [u8; KEY_SIZE] = ciphertext[..KEY_SIZE].try_into().unwrap();
109        let ephemeral_pk = ephemeral_sk.into();
110        let nonce = get_seal_nonce(&ephemeral_pk, &self.public_key());
111        let salsabox = SalsaBox::new(&ephemeral_pk, self);
112        salsabox.decrypt(&nonce, &ciphertext[KEY_SIZE..])
113    }
114}
115
116impl Debug for SecretKey {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        f.debug_struct("SecretKey").finish_non_exhaustive()
119    }
120}
121
122impl Drop for SecretKey {
123    fn drop(&mut self) {
124        self.scalar.zeroize();
125    }
126}
127
128impl Eq for SecretKey {}
129
130impl From<Scalar> for SecretKey {
131    fn from(scalar: Scalar) -> Self {
132        let bytes = scalar.to_bytes();
133        SecretKey { bytes, scalar }
134    }
135}
136
137impl From<[u8; KEY_SIZE]> for SecretKey {
138    fn from(bytes: [u8; KEY_SIZE]) -> SecretKey {
139        Self::from_bytes(bytes)
140    }
141}
142
143impl PartialEq for SecretKey {
144    fn eq(&self, other: &Self) -> bool {
145        self.scalar.ct_eq(&other.scalar).into()
146    }
147}
148
149impl TryFrom<&[u8]> for SecretKey {
150    type Error = TryFromSliceError;
151
152    fn try_from(slice: &[u8]) -> Result<Self, TryFromSliceError> {
153        Self::from_slice(slice)
154    }
155}
156
157#[cfg(feature = "serde")]
158impl Serialize for SecretKey {
159    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
160    where
161        S: ser::Serializer,
162    {
163        serdect::array::serialize_hex_upper_or_bin(&self.to_bytes(), serializer)
164    }
165}
166
167#[cfg(feature = "serde")]
168impl<'de> Deserialize<'de> for SecretKey {
169    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
170    where
171        D: de::Deserializer<'de>,
172    {
173        let mut bytes = [0u8; KEY_SIZE];
174        serdect::array::deserialize_hex_or_bin(&mut bytes, deserializer)?;
175        Ok(SecretKey::from(bytes))
176    }
177}