Skip to main content

oxicrypto_core/
secret.rs

1use alloc::vec::Vec;
2use zeroize::{Zeroize, ZeroizeOnDrop};
3
4use crate::CryptoError;
5
6// ---------------------------------------------------------------------------
7// SecretKey<N> -- fixed-size secret with zeroize-on-drop
8// ---------------------------------------------------------------------------
9
10/// A fixed-size secret key that is automatically zeroed when dropped.
11///
12/// `SecretKey<N>` wraps a `[u8; N]` and implements [`Zeroize`] +
13/// [`ZeroizeOnDrop`], ensuring that key material does not linger in memory
14/// after the value goes out of scope.
15///
16/// # Examples
17///
18/// ```
19/// use oxicrypto_core::SecretKey;
20///
21/// let key = SecretKey::<32>::from_slice(&[0xAA; 32]).expect("wrong length");
22/// assert_eq!(key.as_bytes().len(), 32);
23/// // key is zeroed automatically when dropped
24/// ```
25#[derive(Zeroize, ZeroizeOnDrop)]
26pub struct SecretKey<const N: usize> {
27    bytes: [u8; N],
28}
29
30impl<const N: usize> SecretKey<N> {
31    /// Create a `SecretKey` from a raw byte array.
32    #[must_use]
33    pub fn new(bytes: [u8; N]) -> Self {
34        Self { bytes }
35    }
36
37    /// Create a `SecretKey` from a byte slice.
38    ///
39    /// Returns [`CryptoError::InvalidKey`] if `slice.len() != N`.
40    #[must_use = "result must be checked"]
41    pub fn from_slice(slice: &[u8]) -> Result<Self, CryptoError> {
42        let bytes: [u8; N] = slice.try_into().map_err(|_| CryptoError::InvalidKey)?;
43        Ok(Self { bytes })
44    }
45
46    /// Borrow the secret bytes.
47    ///
48    /// Callers should take care not to log or persist this value.
49    #[must_use]
50    pub fn as_bytes(&self) -> &[u8; N] {
51        &self.bytes
52    }
53}
54
55impl<const N: usize> core::fmt::Debug for SecretKey<N> {
56    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
57        write!(f, "SecretKey<{N}>(***)")
58    }
59}
60
61impl<const N: usize> Clone for SecretKey<N> {
62    fn clone(&self) -> Self {
63        Self { bytes: self.bytes }
64    }
65}
66
67// ---------------------------------------------------------------------------
68// SecretVec -- heap-allocated variable-length secret with zeroize-on-drop
69// ---------------------------------------------------------------------------
70
71/// A heap-allocated, variable-length secret that is automatically zeroed
72/// when dropped.
73///
74/// Use `SecretVec` when the key length is not known at compile time (e.g.
75/// RSA private keys, derived key material of arbitrary length).
76#[derive(Zeroize, ZeroizeOnDrop)]
77pub struct SecretVec {
78    bytes: Vec<u8>,
79}
80
81impl SecretVec {
82    /// Create a `SecretVec` from a `Vec<u8>`.
83    #[must_use]
84    pub fn new(bytes: Vec<u8>) -> Self {
85        Self { bytes }
86    }
87
88    /// Create a `SecretVec` by copying from a slice.
89    #[must_use]
90    pub fn from_slice(slice: &[u8]) -> Self {
91        Self {
92            bytes: slice.to_vec(),
93        }
94    }
95
96    /// Borrow the secret bytes.
97    #[must_use]
98    pub fn as_bytes(&self) -> &[u8] {
99        &self.bytes
100    }
101
102    /// Return the length in bytes.
103    #[must_use]
104    pub fn len(&self) -> usize {
105        self.bytes.len()
106    }
107
108    /// Return `true` if the secret is empty.
109    #[must_use]
110    pub fn is_empty(&self) -> bool {
111        self.bytes.is_empty()
112    }
113}
114
115impl core::fmt::Debug for SecretVec {
116    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
117        write!(f, "SecretVec(len={}, ***)", self.bytes.len())
118    }
119}
120
121impl Clone for SecretVec {
122    fn clone(&self) -> Self {
123        Self {
124            bytes: self.bytes.clone(),
125        }
126    }
127}
128
129// ---------------------------------------------------------------------------
130// KeyPair<SK, PK>
131// ---------------------------------------------------------------------------
132
133/// A generic key pair bundling a secret key and its corresponding public key.
134///
135/// The secret half is zeroized when the pair is dropped (via the [`Zeroize`]
136/// bound and explicit `Drop` implementation).
137pub struct KeyPair<SK: Zeroize, PK> {
138    secret: SK,
139    public: PK,
140}
141
142impl<SK: Zeroize, PK> KeyPair<SK, PK> {
143    /// Construct a new key pair.
144    #[must_use]
145    pub fn new(secret: SK, public: PK) -> Self {
146        Self { secret, public }
147    }
148
149    /// Borrow the secret key.
150    #[must_use]
151    pub fn secret(&self) -> &SK {
152        &self.secret
153    }
154
155    /// Borrow the public key.
156    #[must_use]
157    pub fn public(&self) -> &PK {
158        &self.public
159    }
160}
161
162impl<SK: Zeroize, PK> Drop for KeyPair<SK, PK> {
163    fn drop(&mut self) {
164        self.secret.zeroize();
165    }
166}
167
168impl<SK: Zeroize + core::fmt::Debug, PK: core::fmt::Debug> core::fmt::Debug for KeyPair<SK, PK> {
169    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
170        f.debug_struct("KeyPair")
171            .field("secret", &"***")
172            .field("public", &self.public)
173            .finish()
174    }
175}