Skip to main content

dcrypt_api/
types.rs

1// File: crates/api/src/types.rs
2
3//! Core types with security guarantees for the dcrypt library
4//!
5//! This module provides fundamental type definitions that enforce
6//! compile-time and runtime guarantees for cryptographic operations.
7
8use crate::{
9    error::Error,
10    traits::serialize::{Serialize, SerializeSecret},
11    Result,
12};
13use core::fmt;
14use core::ops::{Deref, DerefMut};
15use dcrypt_internal::constant_time::ct_eq;
16use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
17
18#[cfg(all(not(feature = "std"), feature = "alloc"))]
19use alloc::{vec, vec::Vec};
20#[cfg(feature = "std")]
21use std::vec::Vec;
22
23/// A fixed-size array of bytes that is securely zeroed when dropped
24#[derive(Clone, Zeroize, ZeroizeOnDrop)]
25pub struct SecretBytes<const N: usize> {
26    data: [u8; N],
27}
28
29impl<const N: usize> SecretBytes<N> {
30    pub fn new(data: [u8; N]) -> Self {
31        Self { data }
32    }
33    pub fn from_slice(slice: &[u8]) -> Result<Self> {
34        if slice.len() != N {
35            return Err(Error::InvalidLength {
36                context: "SecretBytes::from_slice",
37                expected: N,
38                actual: slice.len(),
39            });
40        }
41        let mut data = [0u8; N];
42        data.copy_from_slice(slice);
43        Ok(Self { data })
44    }
45    pub fn zeroed() -> Self {
46        Self { data: [0u8; N] }
47    }
48    pub fn random<R: rand::RngCore + rand::CryptoRng>(rng: &mut R) -> Self {
49        let mut data = [0u8; N];
50        rng.fill_bytes(&mut data);
51        Self { data }
52    }
53    pub fn len(&self) -> usize {
54        N
55    }
56    pub fn is_empty(&self) -> bool {
57        N == 0
58    }
59}
60
61impl<const N: usize> AsRef<[u8]> for SecretBytes<N> {
62    fn as_ref(&self) -> &[u8] {
63        &self.data
64    }
65}
66
67impl<const N: usize> AsMut<[u8]> for SecretBytes<N> {
68    fn as_mut(&mut self) -> &mut [u8] {
69        &mut self.data
70    }
71}
72
73impl<const N: usize> Deref for SecretBytes<N> {
74    type Target = [u8; N];
75    fn deref(&self) -> &Self::Target {
76        &self.data
77    }
78}
79
80impl<const N: usize> DerefMut for SecretBytes<N> {
81    fn deref_mut(&mut self) -> &mut Self::Target {
82        &mut self.data
83    }
84}
85
86impl<const N: usize> PartialEq for SecretBytes<N> {
87    fn eq(&self, other: &Self) -> bool {
88        ct_eq(self.data, other.data)
89    }
90}
91
92impl<const N: usize> Eq for SecretBytes<N> {}
93
94impl<const N: usize> fmt::Debug for SecretBytes<N> {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        write!(f, "SecretBytes<{}>[REDACTED]", N)
97    }
98}
99
100impl<const N: usize> SerializeSecret for SecretBytes<N> {
101    fn from_bytes(bytes: &[u8]) -> Result<Self> {
102        Self::from_slice(bytes)
103    }
104    fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
105        Zeroizing::new(self.data.to_vec())
106    }
107}
108
109/// A variable-length vector of bytes that is securely zeroed when dropped
110#[derive(Zeroize, ZeroizeOnDrop)]
111pub struct SecretVec {
112    data: Vec<u8>,
113}
114
115impl SecretVec {
116    /// Create a new SecretVec.
117    ///
118    /// Accepts `Vec<u8>` (move) or `&[u8]` (copy).
119    /// Moving a `Vec<u8>` is preferred for security as it ensures the original
120    /// memory allocation is controlled and zeroized by SecretVec.
121    /// Allocations the caller freed before passing the current `Vec` cannot be
122    /// recovered or retroactively wiped.
123    pub fn new<T: Into<Vec<u8>>>(data: T) -> Self {
124        let mut data = data.into();
125        // A caller-owned Vec may previously have been truncated. Wipe its
126        // unused allocation before accepting responsibility for it.
127        Self::zeroize_spare_capacity(&mut data);
128        Self { data }
129    }
130
131    pub fn from_slice(slice: &[u8]) -> Self {
132        Self::new(slice.to_vec())
133    }
134    pub fn zeroed(len: usize) -> Self {
135        Self::new(vec![0u8; len])
136    }
137    pub fn random<R: rand::RngCore + rand::CryptoRng>(rng: &mut R, len: usize) -> Self {
138        let mut data = vec![0u8; len];
139        rng.fill_bytes(&mut data);
140        Self::new(data)
141    }
142    pub fn len(&self) -> usize {
143        self.data.len()
144    }
145    pub fn is_empty(&self) -> bool {
146        self.data.is_empty()
147    }
148
149    /// Return the secret bytes as a slice.
150    pub fn as_slice(&self) -> &[u8] {
151        &self.data
152    }
153
154    /// Return the secret bytes as a mutable slice.
155    ///
156    /// Slice access permits in-place byte changes but cannot resize or
157    /// reallocate the backing storage.
158    pub fn as_mut_slice(&mut self) -> &mut [u8] {
159        &mut self.data
160    }
161
162    /// Return the allocation capacity.
163    pub fn capacity(&self) -> usize {
164        self.data.capacity()
165    }
166
167    /// Extend the value, wiping the old allocation before freeing it if growth
168    /// requires a larger allocation.
169    pub fn extend_from_slice(&mut self, slice: &[u8]) {
170        self.ensure_additional_capacity(slice.len());
171        self.data.extend_from_slice(slice);
172    }
173
174    /// Resize the value. Bytes removed by shrinking are wiped first.
175    pub fn resize(&mut self, new_len: usize, value: u8) {
176        if new_len <= self.data.len() {
177            self.truncate(new_len);
178            return;
179        }
180
181        self.ensure_additional_capacity(new_len - self.data.len());
182        self.data.resize(new_len, value);
183    }
184
185    /// Shorten the value, wiping every removed byte before changing its length.
186    pub fn truncate(&mut self, len: usize) {
187        if len >= self.data.len() {
188            return;
189        }
190
191        self.data[len..].zeroize();
192        self.data.truncate(len);
193    }
194
195    /// Remove all bytes while retaining a fully zeroed allocation.
196    pub fn clear(&mut self) {
197        self.data.zeroize();
198    }
199
200    /// Append one byte, securely replacing the allocation when it is full.
201    pub fn push(&mut self, value: u8) {
202        self.ensure_additional_capacity(1);
203        self.data.push(value);
204    }
205
206    /// Remove and return the last byte, wiping its allocation slot first.
207    pub fn pop(&mut self) -> Option<u8> {
208        let value = self.data.last().copied()?;
209        let new_len = self.data.len() - 1;
210        self.data[new_len].zeroize();
211        self.data.truncate(new_len);
212        Some(value)
213    }
214
215    /// Reserve room for at least `additional` more bytes without resizing the
216    /// live secret allocation in place.
217    pub fn reserve(&mut self, additional: usize) {
218        self.ensure_additional_capacity(additional);
219    }
220
221    /// Reduce capacity to the current length while wiping the old allocation.
222    pub fn shrink_to_fit(&mut self) {
223        if self.data.capacity() > self.data.len() {
224            self.secure_reallocate(self.data.len());
225        }
226    }
227
228    fn ensure_additional_capacity(&mut self, additional: usize) {
229        let required = self
230            .data
231            .len()
232            .checked_add(additional)
233            .expect("SecretVec capacity overflow");
234
235        if required > self.data.capacity() {
236            self.secure_reallocate(required);
237        }
238    }
239
240    fn secure_reallocate(&mut self, capacity: usize) {
241        debug_assert!(capacity >= self.data.len());
242
243        let mut replacement = Vec::with_capacity(capacity);
244        replacement.extend_from_slice(&self.data);
245        Self::zeroize_spare_capacity(&mut replacement);
246
247        // Vec::zeroize wipes both its initialized bytes and its entire spare
248        // capacity. Free the allocation only after that wipe is complete.
249        self.data.zeroize();
250        #[cfg(test)]
251        assert!(Self::allocation_is_zeroed(&self.data));
252        self.data = replacement;
253    }
254
255    fn zeroize_spare_capacity(data: &mut Vec<u8>) {
256        data.spare_capacity_mut().zeroize();
257    }
258
259    #[cfg(test)]
260    fn allocation_is_zeroed(data: &Vec<u8>) -> bool {
261        // Callers use this only after Vec::zeroize(), which initializes the
262        // entire allocation with zero bytes and sets len to zero.
263        let allocation = unsafe { core::slice::from_raw_parts(data.as_ptr(), data.capacity()) };
264        allocation.iter().all(|byte| *byte == 0)
265    }
266}
267
268impl Clone for SecretVec {
269    fn clone(&self) -> Self {
270        Self::from_slice(&self.data)
271    }
272}
273
274impl From<Vec<u8>> for SecretVec {
275    fn from(data: Vec<u8>) -> Self {
276        Self::new(data)
277    }
278}
279
280impl AsRef<[u8]> for SecretVec {
281    fn as_ref(&self) -> &[u8] {
282        &self.data
283    }
284}
285
286impl AsMut<[u8]> for SecretVec {
287    fn as_mut(&mut self) -> &mut [u8] {
288        &mut self.data
289    }
290}
291
292impl Deref for SecretVec {
293    type Target = [u8];
294    fn deref(&self) -> &Self::Target {
295        &self.data
296    }
297}
298
299impl DerefMut for SecretVec {
300    fn deref_mut(&mut self) -> &mut Self::Target {
301        &mut self.data
302    }
303}
304
305impl PartialEq for SecretVec {
306    fn eq(&self, other: &Self) -> bool {
307        ct_eq(&self.data, &other.data)
308    }
309}
310
311impl Eq for SecretVec {}
312
313impl fmt::Debug for SecretVec {
314    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315        write!(f, "SecretVec({})[REDACTED]", self.data.len())
316    }
317}
318
319impl SerializeSecret for SecretVec {
320    fn from_bytes(bytes: &[u8]) -> Result<Self> {
321        Ok(Self::from_slice(bytes))
322    }
323    fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
324        Zeroizing::new(self.data.clone())
325    }
326}
327
328#[cfg(test)]
329mod secret_vec_tests {
330    use super::SecretVec;
331
332    #[cfg(all(not(feature = "std"), feature = "alloc"))]
333    use alloc::{vec, vec::Vec};
334
335    fn assert_spare_capacity_is_zero(secret: &SecretVec) {
336        // SecretVec initializes every byte of spare capacity. Reading this
337        // range is therefore valid even though Vec does not expose it as an
338        // initialized slice.
339        let spare = unsafe {
340            core::slice::from_raw_parts(
341                secret.data.as_ptr().add(secret.data.len()),
342                secret.data.capacity() - secret.data.len(),
343            )
344        };
345        assert!(spare.iter().all(|byte| *byte == 0));
346    }
347
348    #[test]
349    fn constructor_wipes_preexisting_unused_capacity() {
350        let mut raw = vec![0xA5; 64];
351        raw.truncate(4);
352        let secret = SecretVec::new(raw);
353
354        assert_eq!(secret.as_slice(), &[0xA5; 4]);
355        assert_spare_capacity_is_zero(&secret);
356    }
357
358    #[test]
359    fn shrinking_operations_wipe_removed_slots() {
360        let mut secret = SecretVec::from_slice(&[1, 2, 3, 4, 5, 6]);
361
362        secret.truncate(4);
363        assert_eq!(secret.as_slice(), &[1, 2, 3, 4]);
364        assert_spare_capacity_is_zero(&secret);
365
366        secret.resize(2, 0xFF);
367        assert_eq!(secret.as_slice(), &[1, 2]);
368        assert_spare_capacity_is_zero(&secret);
369
370        assert_eq!(secret.pop(), Some(2));
371        assert_eq!(secret.as_slice(), &[1]);
372        assert_spare_capacity_is_zero(&secret);
373
374        secret.clear();
375        assert!(secret.is_empty());
376        assert_spare_capacity_is_zero(&secret);
377    }
378
379    #[test]
380    fn growth_and_shrink_replace_allocations_securely() {
381        let mut raw = Vec::with_capacity(4);
382        raw.extend_from_slice(&[0x11; 4]);
383        let mut secret = SecretVec::new(raw);
384        let initial_capacity = secret.capacity();
385
386        // secure_reallocate contains a test assertion that observes the old
387        // allocation after zeroization and immediately before deallocation.
388        secret.extend_from_slice(&[0x22, 0x33]);
389        assert!(secret.capacity() > initial_capacity);
390        assert_eq!(secret.as_slice(), &[0x11, 0x11, 0x11, 0x11, 0x22, 0x33]);
391        assert_spare_capacity_is_zero(&secret);
392
393        secret.reserve(32);
394        assert!(secret.capacity() >= secret.len() + 32);
395        assert_spare_capacity_is_zero(&secret);
396
397        secret.shrink_to_fit();
398        assert_eq!(secret.capacity(), secret.len());
399        assert_eq!(secret.as_slice(), &[0x11, 0x11, 0x11, 0x11, 0x22, 0x33]);
400    }
401}
402
403/// Base key type that provides secure memory handling
404#[derive(Clone, Zeroize, ZeroizeOnDrop)]
405pub struct Key {
406    data: Vec<u8>,
407}
408
409impl Key {
410    /// Create a new Key.
411    ///
412    /// Accepts `Vec<u8>` (move) or `&[u8]` (copy).
413    /// Moving a `Vec<u8>` is preferred for security as it ensures the original
414    /// memory allocation is controlled and zeroized by Key.
415    pub fn new<T: Into<Vec<u8>>>(data: T) -> Self {
416        Self { data: data.into() }
417    }
418    pub fn new_zeros(len: usize) -> Self {
419        Self {
420            data: vec![0u8; len],
421        }
422    }
423    pub fn len(&self) -> usize {
424        self.data.len()
425    }
426    pub fn is_empty(&self) -> bool {
427        self.data.is_empty()
428    }
429}
430
431impl From<Vec<u8>> for Key {
432    fn from(data: Vec<u8>) -> Self {
433        Self::new(data)
434    }
435}
436
437impl AsRef<[u8]> for Key {
438    fn as_ref(&self) -> &[u8] {
439        &self.data
440    }
441}
442
443impl AsMut<[u8]> for Key {
444    fn as_mut(&mut self) -> &mut [u8] {
445        &mut self.data
446    }
447}
448
449impl SerializeSecret for Key {
450    fn from_bytes(bytes: &[u8]) -> Result<Self> {
451        Ok(Self::new(bytes))
452    }
453    fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
454        Zeroizing::new(self.data.clone())
455    }
456}
457
458/// Wrapper for public key data
459#[derive(Clone, Zeroize)]
460pub struct PublicKey {
461    data: Vec<u8>,
462}
463
464impl PublicKey {
465    /// Create a new PublicKey.
466    ///
467    /// Accepts `Vec<u8>` (move) or `&[u8]` (copy).
468    /// Moving a `Vec<u8>` avoids unnecessary allocation.
469    pub fn new<T: Into<Vec<u8>>>(data: T) -> Self {
470        Self { data: data.into() }
471    }
472    pub fn len(&self) -> usize {
473        self.data.len()
474    }
475    pub fn is_empty(&self) -> bool {
476        self.data.is_empty()
477    }
478}
479
480impl From<Vec<u8>> for PublicKey {
481    fn from(data: Vec<u8>) -> Self {
482        Self::new(data)
483    }
484}
485
486impl AsRef<[u8]> for PublicKey {
487    fn as_ref(&self) -> &[u8] {
488        &self.data
489    }
490}
491
492impl AsMut<[u8]> for PublicKey {
493    fn as_mut(&mut self) -> &mut [u8] {
494        &mut self.data
495    }
496}
497
498impl Serialize for PublicKey {
499    fn to_bytes(&self) -> Vec<u8> {
500        self.data.clone()
501    }
502    fn from_bytes(bytes: &[u8]) -> Result<Self> {
503        Ok(Self::new(bytes))
504    }
505}
506
507/// Wrapper for ciphertext data
508#[derive(Clone)]
509pub struct Ciphertext {
510    data: Vec<u8>,
511}
512
513impl Ciphertext {
514    /// Create a new Ciphertext.
515    ///
516    /// Accepts `Vec<u8>` (move) or `&[u8]` (copy).
517    /// Moving a `Vec<u8>` avoids unnecessary allocation, which is critical
518    /// for large ciphertexts.
519    pub fn new<T: Into<Vec<u8>>>(data: T) -> Self {
520        Self { data: data.into() }
521    }
522    pub fn len(&self) -> usize {
523        self.data.len()
524    }
525    pub fn is_empty(&self) -> bool {
526        self.data.is_empty()
527    }
528}
529
530impl From<Vec<u8>> for Ciphertext {
531    fn from(data: Vec<u8>) -> Self {
532        Self::new(data)
533    }
534}
535
536impl AsRef<[u8]> for Ciphertext {
537    fn as_ref(&self) -> &[u8] {
538        &self.data
539    }
540}
541
542impl AsMut<[u8]> for Ciphertext {
543    fn as_mut(&mut self) -> &mut [u8] {
544        &mut self.data
545    }
546}
547
548impl Serialize for Ciphertext {
549    fn to_bytes(&self) -> Vec<u8> {
550        self.data.clone()
551    }
552    fn from_bytes(bytes: &[u8]) -> Result<Self> {
553        Ok(Self::new(bytes))
554    }
555}