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}