Skip to main content

oxicrypto_core/
secret.rs

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