use crate::{
error::Error,
traits::serialize::{Serialize, SerializeSecret},
Result,
};
use core::fmt;
use core::ops::{Deref, DerefMut};
use dcrypt_internal::constant_time::ct_eq;
pub use dcrypt_internal::zeroing::ZeroizingBytes;
use dcrypt_internal::{
random::try_fill_bytes_zeroing_on_error,
zeroing::{
boxed_bytes_from_slice, boxed_bytes_zeroed, zeroizing_bytes_from_slice, Zeroize,
ZeroizeOnDrop, Zeroizing,
},
};
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec::Vec};
#[cfg(feature = "std")]
use std::{boxed::Box, vec::Vec};
#[derive(Clone)]
pub struct SecretBytes<const N: usize> {
data: [u8; N],
}
impl<const N: usize> Zeroize for SecretBytes<N> {
fn zeroize(&mut self) {
self.data.zeroize();
}
}
impl<const N: usize> ZeroizeOnDrop for SecretBytes<N> {}
impl<const N: usize> Drop for SecretBytes<N> {
fn drop(&mut self) {
self.zeroize();
}
}
impl<const N: usize> SecretBytes<N> {
pub fn new(data: [u8; N]) -> Self {
Self { data }
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() != N {
return Err(Error::InvalidLength {
context: "SecretBytes::from_slice",
expected: N,
actual: slice.len(),
});
}
let mut data = [0u8; N];
data.copy_from_slice(slice);
Ok(Self { data })
}
pub fn zeroed() -> Self {
Self { data: [0u8; N] }
}
pub fn random<R: dcrypt_internal::random::CryptoRng + ?Sized>(rng: &mut R) -> Result<Self> {
let mut data = [0u8; N];
try_fill_bytes_zeroing_on_error(rng, &mut data).map_err(|_| {
Error::RandomGenerationError {
context: "SecretBytes::random",
#[cfg(feature = "std")]
message: "caller-provided randomness source failed".into(),
}
})?;
Ok(Self { data })
}
pub fn len(&self) -> usize {
N
}
pub fn is_empty(&self) -> bool {
N == 0
}
pub fn to_bytes_zeroizing_boxed(&self) -> ZeroizingBytes {
zeroizing_bytes_from_slice(&self.data)
}
}
impl<const N: usize> AsRef<[u8]> for SecretBytes<N> {
fn as_ref(&self) -> &[u8] {
&self.data
}
}
impl<const N: usize> AsMut<[u8]> for SecretBytes<N> {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.data
}
}
impl<const N: usize> Deref for SecretBytes<N> {
type Target = [u8; N];
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<const N: usize> DerefMut for SecretBytes<N> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl<const N: usize> PartialEq for SecretBytes<N> {
fn eq(&self, other: &Self) -> bool {
ct_eq(self.data.as_slice(), other.data.as_slice())
}
}
impl<const N: usize> Eq for SecretBytes<N> {}
impl<const N: usize> fmt::Debug for SecretBytes<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SecretBytes<{}>[REDACTED]", N)
}
}
impl<const N: usize> SerializeSecret for SecretBytes<N> {
fn from_bytes(bytes: &[u8]) -> Result<Self> {
Self::from_slice(bytes)
}
fn to_bytes_zeroizing(&self) -> ZeroizingBytes {
self.to_bytes_zeroizing_boxed()
}
}
pub struct SecretVec {
data: Box<[u8]>,
}
impl Zeroize for SecretVec {
fn zeroize(&mut self) {
self.data.zeroize();
}
}
impl ZeroizeOnDrop for SecretVec {}
impl Drop for SecretVec {
fn drop(&mut self) {
self.zeroize();
}
}
impl SecretVec {
pub fn new(data: Box<[u8]>) -> Self {
Self { data }
}
pub fn from_slice(slice: &[u8]) -> Self {
Self::new(boxed_bytes_from_slice(slice))
}
pub fn empty() -> Self {
Self::new(boxed_bytes_zeroed(0))
}
pub fn zeroed(len: usize) -> Self {
Self::new(boxed_bytes_zeroed(len))
}
pub fn random<R: dcrypt_internal::random::CryptoRng + ?Sized>(
rng: &mut R,
len: usize,
) -> Result<Self> {
let mut data = boxed_bytes_zeroed(len);
try_fill_bytes_zeroing_on_error(rng, &mut data).map_err(|_| {
Error::RandomGenerationError {
context: "SecretVec::random",
#[cfg(feature = "std")]
message: "caller-provided randomness source failed".into(),
}
})?;
Ok(Self::new(data))
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn as_slice(&self) -> &[u8] {
&self.data
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.data
}
pub fn to_bytes_zeroizing_boxed(&self) -> ZeroizingBytes {
zeroizing_bytes_from_slice(&self.data)
}
pub fn into_bytes_zeroizing_boxed(mut self) -> ZeroizingBytes {
let data = core::mem::replace(&mut self.data, boxed_bytes_zeroed(0));
Zeroizing::new(data)
}
pub fn capacity(&self) -> usize {
self.data.len()
}
pub fn extend_from_slice(&mut self, slice: &[u8]) {
let new_len = self
.data
.len()
.checked_add(slice.len())
.expect("SecretVec length overflow");
let mut replacement = boxed_bytes_zeroed(new_len);
replacement[..self.data.len()].copy_from_slice(&self.data);
replacement[self.data.len()..].copy_from_slice(slice);
self.replace_and_zeroize(replacement);
}
pub fn resize(&mut self, new_len: usize, value: u8) {
if new_len <= self.data.len() {
self.truncate(new_len);
return;
}
let mut replacement = boxed_bytes_zeroed(new_len);
replacement[..self.data.len()].copy_from_slice(&self.data);
replacement[self.data.len()..].fill(value);
self.replace_and_zeroize(replacement);
}
pub fn truncate(&mut self, len: usize) {
if len >= self.data.len() {
return;
}
let replacement = boxed_bytes_from_slice(&self.data[..len]);
self.replace_and_zeroize(replacement);
}
pub fn clear(&mut self) {
self.replace_and_zeroize(boxed_bytes_zeroed(0));
}
pub fn push(&mut self, value: u8) {
self.extend_from_slice(&[value]);
}
pub fn pop(&mut self) -> Option<u8> {
let value = self.data.last().copied()?;
self.truncate(self.data.len() - 1);
Some(value)
}
fn replace_and_zeroize(&mut self, replacement: Box<[u8]>) {
self.data.zeroize();
self.data = replacement;
}
}
impl Clone for SecretVec {
fn clone(&self) -> Self {
Self::from_slice(&self.data)
}
}
impl From<Box<[u8]>> for SecretVec {
fn from(data: Box<[u8]>) -> Self {
Self::new(data)
}
}
impl AsRef<[u8]> for SecretVec {
fn as_ref(&self) -> &[u8] {
self.data.as_ref()
}
}
impl AsMut<[u8]> for SecretVec {
fn as_mut(&mut self) -> &mut [u8] {
self.data.as_mut()
}
}
impl Deref for SecretVec {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.data.as_ref()
}
}
impl DerefMut for SecretVec {
fn deref_mut(&mut self) -> &mut Self::Target {
self.data.as_mut()
}
}
impl PartialEq for SecretVec {
fn eq(&self, other: &Self) -> bool {
ct_eq(self.data.as_ref(), other.data.as_ref())
}
}
impl Eq for SecretVec {}
impl fmt::Debug for SecretVec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SecretVec({})[REDACTED]", self.data.len())
}
}
impl SerializeSecret for SecretVec {
fn from_bytes(bytes: &[u8]) -> Result<Self> {
Ok(Self::from_slice(bytes))
}
fn to_bytes_zeroizing(&self) -> ZeroizingBytes {
self.to_bytes_zeroizing_boxed()
}
}
#[cfg(test)]
mod secret_vec_tests {
use super::{Key, SecretBytes, SecretVec};
use crate::traits::SerializeSecret;
use dcrypt_internal::random::{CryptoRng, Error as RandomError, RngCore};
#[test]
fn constructor_uses_exact_size_storage() {
let secret = SecretVec::new([0xA5; 4].into());
assert_eq!(secret.as_slice(), &[0xA5; 4]);
assert_eq!(secret.capacity(), secret.len());
}
#[test]
fn shrinking_operations_wipe_removed_slots() {
let mut secret = SecretVec::from_slice(&[1, 2, 3, 4, 5, 6]);
secret.truncate(4);
assert_eq!(secret.as_slice(), &[1, 2, 3, 4]);
assert_eq!(secret.capacity(), secret.len());
secret.resize(2, 0xFF);
assert_eq!(secret.as_slice(), &[1, 2]);
assert_eq!(secret.capacity(), secret.len());
assert_eq!(secret.pop(), Some(2));
assert_eq!(secret.as_slice(), &[1]);
assert_eq!(secret.capacity(), secret.len());
secret.clear();
assert!(secret.is_empty());
assert_eq!(secret.capacity(), secret.len());
}
#[test]
fn growth_and_shrink_replace_allocations_securely() {
let mut secret = SecretVec::from_slice(&[0x11; 4]);
secret.extend_from_slice(&[0x22, 0x33]);
assert_eq!(secret.capacity(), 6);
assert_eq!(secret.as_slice(), &[0x11, 0x11, 0x11, 0x11, 0x22, 0x33]);
}
#[test]
fn exact_size_serialization_supports_copy_and_ownership_transfer() {
let fixed = SecretBytes::<4>::new([1, 2, 3, 4]);
let fixed_bytes = fixed.to_bytes_zeroizing_boxed();
assert_eq!(&**fixed_bytes, &[1, 2, 3, 4]);
let trait_fixed_bytes = fixed.to_bytes_zeroizing();
assert_eq!(&**trait_fixed_bytes, &[1, 2, 3, 4]);
let secret = SecretVec::from_slice(&[5, 6, 7]);
let copied = secret.to_bytes_zeroizing_boxed();
assert_eq!(&**copied, &[5, 6, 7]);
let trait_copied = secret.to_bytes_zeroizing();
assert_eq!(&**trait_copied, &[5, 6, 7]);
let transferred = secret.into_bytes_zeroizing_boxed();
assert_eq!(&**transferred, &[5, 6, 7]);
let key = Key::from_slice(&[8, 9]);
let key_bytes = key.to_bytes_zeroizing_boxed();
assert_eq!(&**key_bytes, &[8, 9]);
let trait_key_bytes = key.to_bytes_zeroizing();
assert_eq!(&**trait_key_bytes, &[8, 9]);
}
struct PartiallyFailingRng;
impl RngCore for PartiallyFailingRng {
fn try_fill_bytes(&mut self, destination: &mut [u8]) -> Result<(), RandomError> {
let written = core::cmp::min(destination.len(), 5);
destination[..written].fill(0xA5);
Err(RandomError)
}
}
impl CryptoRng for PartiallyFailingRng {}
#[test]
fn random_secret_constructors_propagate_caller_rng_failure() {
let mut rng = PartiallyFailingRng;
assert!(SecretBytes::<32>::random(&mut rng).is_err());
assert!(SecretVec::random(&mut rng, 32).is_err());
}
}
#[derive(Clone)]
pub struct Key {
data: Box<[u8]>,
}
impl Zeroize for Key {
fn zeroize(&mut self) {
self.data.zeroize();
}
}
impl ZeroizeOnDrop for Key {}
impl Drop for Key {
fn drop(&mut self) {
self.zeroize();
}
}
impl Key {
pub fn new(data: &[u8]) -> Self {
Self::from_boxed_slice(boxed_bytes_from_slice(data))
}
pub fn from_boxed_slice(data: Box<[u8]>) -> Self {
Self { data }
}
pub fn from_slice(data: &[u8]) -> Self {
Self::new(data)
}
pub fn new_zeros(len: usize) -> Self {
Self::from_boxed_slice(boxed_bytes_zeroed(len))
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn to_bytes_zeroizing_boxed(&self) -> ZeroizingBytes {
zeroizing_bytes_from_slice(&self.data)
}
}
impl From<Box<[u8]>> for Key {
fn from(data: Box<[u8]>) -> Self {
Self::from_boxed_slice(data)
}
}
impl AsRef<[u8]> for Key {
fn as_ref(&self) -> &[u8] {
self.data.as_ref()
}
}
impl AsMut<[u8]> for Key {
fn as_mut(&mut self) -> &mut [u8] {
self.data.as_mut()
}
}
impl SerializeSecret for Key {
fn from_bytes(bytes: &[u8]) -> Result<Self> {
Ok(Self::from_slice(bytes))
}
fn to_bytes_zeroizing(&self) -> ZeroizingBytes {
self.to_bytes_zeroizing_boxed()
}
}
#[derive(Clone)]
pub struct PublicKey {
data: Vec<u8>,
}
impl PublicKey {
pub fn new<T: Into<Vec<u8>>>(data: T) -> Self {
Self { data: data.into() }
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
impl From<Vec<u8>> for PublicKey {
fn from(data: Vec<u8>) -> Self {
Self::new(data)
}
}
impl AsRef<[u8]> for PublicKey {
fn as_ref(&self) -> &[u8] {
&self.data
}
}
impl AsMut<[u8]> for PublicKey {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.data
}
}
impl Serialize for PublicKey {
fn to_bytes(&self) -> Vec<u8> {
self.data.clone()
}
fn from_bytes(bytes: &[u8]) -> Result<Self> {
Ok(Self::new(bytes))
}
}
#[derive(Clone)]
pub struct Ciphertext {
data: Vec<u8>,
}
impl Ciphertext {
pub fn new<T: Into<Vec<u8>>>(data: T) -> Self {
Self { data: data.into() }
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
impl From<Vec<u8>> for Ciphertext {
fn from(data: Vec<u8>) -> Self {
Self::new(data)
}
}
impl AsRef<[u8]> for Ciphertext {
fn as_ref(&self) -> &[u8] {
&self.data
}
}
impl AsMut<[u8]> for Ciphertext {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.data
}
}
impl Serialize for Ciphertext {
fn to_bytes(&self) -> Vec<u8> {
self.data.clone()
}
fn from_bytes(bytes: &[u8]) -> Result<Self> {
Ok(Self::new(bytes))
}
}