Skip to main content

crypto_csprng/
types.rs

1// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use secrecy::{ExposeSecret, ExposeSecretMut, SecretBox};
6use subtle::ConstantTimeEq;
7use zeroize::{Zeroize, ZeroizeOnDrop};
8
9use crate::constants::{
10    AEAD_NONCE_12_LENGTH, AES_256_GCM_KEY_LENGTH, ARGON2_SALT_16_LENGTH, ARGON2_SALT_32_LENGTH,
11    ML_DSA_87_SEED_LENGTH, ML_KEM_1024_SEED_LENGTH,
12};
13
14/// A randomly generated 12-byte AEAD nonce.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct AeadNonce12([u8; AEAD_NONCE_12_LENGTH]);
17
18impl AeadNonce12 {
19    /// Returns a reference to the raw nonce bytes.
20    pub const fn as_bytes(&self) -> &[u8; AEAD_NONCE_12_LENGTH] {
21        &self.0
22    }
23
24    pub(crate) fn from_array(bytes: [u8; AEAD_NONCE_12_LENGTH]) -> Self {
25        Self(bytes)
26    }
27}
28
29/// A randomly generated 16-byte Argon2 salt.
30///
31/// Argon2 salts can become stable account-correlating identifiers. This owner
32/// therefore clears its bytes on drop and never exposes them through `Debug`.
33#[derive(Zeroize, ZeroizeOnDrop)]
34pub struct Argon2Salt16([u8; ARGON2_SALT_16_LENGTH]);
35
36impl PartialEq for Argon2Salt16 {
37    fn eq(&self, other: &Self) -> bool {
38        bool::from(self.0.ct_eq(&other.0))
39    }
40}
41
42impl Eq for Argon2Salt16 {}
43
44impl core::hash::Hash for Argon2Salt16 {
45    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
46        core::hash::Hash::hash(&self.0, state);
47    }
48}
49
50impl Argon2Salt16 {
51    /// Returns a reference to the raw salt bytes.
52    pub const fn as_bytes(&self) -> &[u8; ARGON2_SALT_16_LENGTH] {
53        &self.0
54    }
55
56    pub(crate) fn from_array(bytes: [u8; ARGON2_SALT_16_LENGTH]) -> Self {
57        Self(bytes)
58    }
59}
60
61impl core::fmt::Debug for Argon2Salt16 {
62    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
63        formatter.write_str("Argon2Salt16(<redacted>)")
64    }
65}
66
67/// A randomly generated 32-byte Argon2 salt.
68///
69/// Argon2 salts can become stable account-correlating identifiers. This owner
70/// therefore clears its bytes on drop and never exposes them through `Debug`.
71#[derive(Zeroize, ZeroizeOnDrop)]
72pub struct Argon2Salt32([u8; ARGON2_SALT_32_LENGTH]);
73
74impl PartialEq for Argon2Salt32 {
75    fn eq(&self, other: &Self) -> bool {
76        bool::from(self.0.ct_eq(&other.0))
77    }
78}
79
80impl Eq for Argon2Salt32 {}
81
82impl core::hash::Hash for Argon2Salt32 {
83    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
84        core::hash::Hash::hash(&self.0, state);
85    }
86}
87
88impl Argon2Salt32 {
89    /// Returns a reference to the raw salt bytes.
90    pub const fn as_bytes(&self) -> &[u8; ARGON2_SALT_32_LENGTH] {
91        &self.0
92    }
93
94    pub(crate) fn from_array(bytes: [u8; ARGON2_SALT_32_LENGTH]) -> Self {
95        Self(bytes)
96    }
97}
98
99impl core::fmt::Debug for Argon2Salt32 {
100    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
101        formatter.write_str("Argon2Salt32(<redacted>)")
102    }
103}
104
105/// Randomly generated AES-256-GCM key material.
106///
107/// This is a sensitive-security-parameter owner and zeroizes on drop.
108#[derive(Zeroize, ZeroizeOnDrop)]
109pub struct Aes256GcmKeyMaterial {
110    bytes: SecretBox<[u8; AES_256_GCM_KEY_LENGTH]>,
111}
112
113impl Aes256GcmKeyMaterial {
114    /// Returns a reference to the raw key bytes.
115    pub fn as_bytes(&self) -> &[u8; AES_256_GCM_KEY_LENGTH] {
116        self.bytes.expose_secret()
117    }
118
119    /// Consumes the owner and returns the raw key bytes.
120    ///
121    /// The returned array is now the caller's SSP owner and must be cleared by
122    /// that caller when no longer needed.
123    pub fn into_bytes(mut self) -> [u8; AES_256_GCM_KEY_LENGTH] {
124        let output = *self.bytes.expose_secret();
125        self.bytes.zeroize();
126        output
127    }
128
129    pub(crate) fn from_array(mut bytes: [u8; AES_256_GCM_KEY_LENGTH]) -> Self {
130        let mut secret = SecretBox::new(Box::new([0u8; AES_256_GCM_KEY_LENGTH]));
131        *secret.expose_secret_mut() = bytes;
132        bytes.zeroize();
133        Self { bytes: secret }
134    }
135}
136
137/// Randomly generated ML-KEM-1024 FIPS 203 seed material.
138///
139/// This is a sensitive-security-parameter owner and zeroizes on drop.
140#[derive(Zeroize, ZeroizeOnDrop)]
141pub struct MlKem1024Seed {
142    bytes: SecretBox<[u8; ML_KEM_1024_SEED_LENGTH]>,
143}
144
145impl MlKem1024Seed {
146    /// Returns a reference to the raw seed bytes.
147    pub fn as_bytes(&self) -> &[u8; ML_KEM_1024_SEED_LENGTH] {
148        self.bytes.expose_secret()
149    }
150
151    /// Consumes the owner and returns the raw seed bytes.
152    ///
153    /// The returned array is now the caller's SSP owner and must be cleared by
154    /// that caller when no longer needed.
155    pub fn into_bytes(mut self) -> [u8; ML_KEM_1024_SEED_LENGTH] {
156        let output = *self.bytes.expose_secret();
157        self.bytes.zeroize();
158        output
159    }
160
161    pub(crate) fn from_array(mut bytes: [u8; ML_KEM_1024_SEED_LENGTH]) -> Self {
162        let mut secret = SecretBox::new(Box::new([0u8; ML_KEM_1024_SEED_LENGTH]));
163        *secret.expose_secret_mut() = bytes;
164        bytes.zeroize();
165        Self { bytes: secret }
166    }
167}
168
169/// Randomly generated ML-DSA-87 FIPS 204 seed material.
170///
171/// This is a sensitive-security-parameter owner and zeroizes on drop.
172#[derive(Zeroize, ZeroizeOnDrop)]
173pub struct MlDsa87Seed {
174    bytes: SecretBox<[u8; ML_DSA_87_SEED_LENGTH]>,
175}
176
177impl MlDsa87Seed {
178    /// Returns a reference to the raw seed bytes.
179    pub fn as_bytes(&self) -> &[u8; ML_DSA_87_SEED_LENGTH] {
180        self.bytes.expose_secret()
181    }
182
183    /// Consumes the owner and returns the raw seed bytes.
184    ///
185    /// The returned array is now the caller's SSP owner and must be cleared by
186    /// that caller when no longer needed.
187    pub fn into_bytes(mut self) -> [u8; ML_DSA_87_SEED_LENGTH] {
188        let output = *self.bytes.expose_secret();
189        self.bytes.zeroize();
190        output
191    }
192
193    pub(crate) fn from_array(mut bytes: [u8; ML_DSA_87_SEED_LENGTH]) -> Self {
194        let mut secret = SecretBox::new(Box::new([0u8; ML_DSA_87_SEED_LENGTH]));
195        *secret.expose_secret_mut() = bytes;
196        bytes.zeroize();
197        Self { bytes: secret }
198    }
199}
200
201/// A buffer of `N` randomly generated bytes.
202///
203/// Zeroizes its bytes on drop.
204#[derive(Zeroize, ZeroizeOnDrop)]
205pub struct RandomBytes<const N: usize> {
206    bytes: [u8; N],
207}
208
209impl<const N: usize> RandomBytes<N> {
210    /// Returns a reference to the raw random bytes.
211    pub const fn as_bytes(&self) -> &[u8; N] {
212        &self.bytes
213    }
214
215    /// Consumes the buffer, returning the owned random bytes.
216    ///
217    /// The returned array is no longer zeroized on drop by this type.
218    pub fn into_bytes(self) -> [u8; N] {
219        self.bytes
220    }
221
222    pub(crate) fn from_array(bytes: [u8; N]) -> Self {
223        Self { bytes }
224    }
225}