use super::{ExposedSecret, ExposedSecretMut};
use crate::v2::bounded::BufferLengthError;
pub struct SecretArray<const CAP: usize> {
bytes: [u8; CAP],
len: usize,
}
impl<const CAP: usize> SecretArray<CAP> {
pub(super) fn from_frame(bytes: [u8; CAP], len: usize) -> Result<Self, BufferLengthError> {
Self::from_array(bytes, len)
}
pub fn from_array(mut bytes: [u8; CAP], len: usize) -> Result<Self, BufferLengthError> {
if len > CAP {
crate::wipe_bytes(&mut bytes);
return Err(BufferLengthError::new(len, CAP));
}
crate::wipe_tail(&mut bytes, len);
Ok(Self { bytes, len })
}
#[must_use]
pub fn expose_secret(&self) -> ExposedSecret<'_> {
ExposedSecret::new(&self.bytes[..self.len])
}
#[must_use]
pub fn expose_secret_mut(&mut self) -> ExposedSecretMut<'_> {
ExposedSecretMut {
bytes: &mut self.bytes[..self.len],
}
}
#[must_use = "declassification transfers cleanup responsibility to the caller"]
pub fn declassify(mut self) -> DeclassifiedArray<CAP> {
let bytes = core::mem::replace(&mut self.bytes, [0u8; CAP]);
let len = self.len;
self.len = 0;
DeclassifiedArray { bytes, len }
}
#[must_use]
pub const fn len(&self) -> usize {
self.len
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[must_use]
pub const fn capacity(&self) -> usize {
CAP
}
pub fn clear(&mut self) {
crate::wipe_bytes(&mut self.bytes);
self.len = 0;
}
#[cfg(test)]
pub(crate) const fn backing_for_test(&self) -> &[u8; CAP] {
&self.bytes
}
#[cfg(kani)]
pub(crate) const fn backing_for_proof(&self) -> &[u8; CAP] {
&self.bytes
}
}
impl<const CAP: usize> Drop for SecretArray<CAP> {
fn drop(&mut self) {
self.clear();
}
}
impl<const CAP: usize> core::fmt::Debug for SecretArray<CAP> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_struct("SecretArray")
.field("bytes", &"<redacted>")
.field("len", &self.len)
.field("capacity", &CAP)
.finish()
}
}
impl<const CAP: usize> core::fmt::Display for SecretArray<CAP> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("<redacted secret array>")
}
}
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct DeclassifiedArray<const CAP: usize> {
bytes: [u8; CAP],
len: usize,
}
impl<const CAP: usize> DeclassifiedArray<CAP> {
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.len]
}
#[must_use]
pub const fn len(&self) -> usize {
self.len
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[must_use]
pub const fn capacity(&self) -> usize {
CAP
}
#[must_use]
pub const fn into_parts(self) -> ([u8; CAP], usize) {
(self.bytes, self.len)
}
}
impl<const CAP: usize> AsRef<[u8]> for DeclassifiedArray<CAP> {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl<const CAP: usize> core::fmt::Debug for DeclassifiedArray<CAP> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter
.debug_tuple("DeclassifiedArray")
.field(&self.as_bytes())
.finish()
}
}
#[cfg(feature = "alloc")]
pub struct SecretVec {
bytes: alloc::vec::Vec<u8>,
}
#[cfg(feature = "alloc")]
impl SecretVec {
pub(super) fn from_frame(mut bytes: alloc::vec::Vec<u8>, len: usize) -> Self {
debug_assert!(len <= bytes.len());
bytes.truncate(len);
crate::wipe_vec_spare_capacity(&mut bytes);
Self { bytes }
}
#[must_use]
pub fn from_vec(mut bytes: alloc::vec::Vec<u8>) -> Self {
crate::wipe_vec_spare_capacity(&mut bytes);
Self { bytes }
}
#[must_use]
pub fn from_slice(bytes: &[u8]) -> Self {
Self::from_vec(bytes.to_vec())
}
pub fn replace_from_vec(&mut self, replacement: alloc::vec::Vec<u8>) {
drop(self.replace_and_wipe_displaced(replacement));
}
fn replace_and_wipe_displaced(
&mut self,
mut replacement: alloc::vec::Vec<u8>,
) -> alloc::vec::Vec<u8> {
crate::wipe_vec_spare_capacity(&mut replacement);
let mut displaced = core::mem::replace(&mut self.bytes, replacement);
crate::wipe_vec_all(&mut displaced);
displaced
}
#[cfg(test)]
pub(crate) fn replace_for_test(
&mut self,
replacement: alloc::vec::Vec<u8>,
) -> alloc::vec::Vec<u8> {
self.replace_and_wipe_displaced(replacement)
}
#[must_use]
pub fn expose_secret(&self) -> ExposedSecret<'_> {
ExposedSecret::new(&self.bytes)
}
#[must_use]
pub fn expose_secret_mut(&mut self) -> ExposedSecretMut<'_> {
ExposedSecretMut {
bytes: &mut self.bytes,
}
}
#[must_use = "caller must apply its approved cleanup policy to the returned Vec"]
pub fn declassify_into_unprotected_vec(mut self) -> alloc::vec::Vec<u8> {
core::mem::take(&mut self.bytes)
}
#[must_use]
pub fn len(&self) -> usize {
self.bytes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
#[must_use]
pub fn capacity(&self) -> usize {
self.bytes.capacity()
}
pub fn clear(&mut self) {
crate::wipe_vec_all(&mut self.bytes);
self.bytes.clear();
}
}
#[cfg(feature = "alloc")]
impl Drop for SecretVec {
fn drop(&mut self) {
self.clear();
}
}
#[cfg(feature = "alloc")]
redacted_formatting!(SecretVec, "SecretVec");