use std::fmt;
use zeroize::{Zeroize, ZeroizeOnDrop};
mod error;
mod limits;
pub use error::{DeterministicCborError, DeterministicCborProfileError};
pub use limits::{
DETERMINISTIC_CBOR_NEGATIVE_MAX, DETERMINISTIC_CBOR_NEGATIVE_MIN,
MAX_DETERMINISTIC_CBOR_AGGREGATE_BYTE_STRING_BYTES,
MAX_DETERMINISTIC_CBOR_AGGREGATE_TEXT_BYTES, MAX_DETERMINISTIC_CBOR_CONTAINER_ENTRIES,
MAX_DETERMINISTIC_CBOR_INPUT_LEN, MAX_DETERMINISTIC_CBOR_NESTING_DEPTH,
MAX_DETERMINISTIC_CBOR_NODES, MAX_DETERMINISTIC_CBOR_OUTPUT_LEN,
};
#[non_exhaustive]
pub enum DeterministicCborInteger {
Unsigned(u64),
Negative(DeterministicCborNegativeInteger),
}
impl DeterministicCborInteger {
pub const fn unsigned(value: u64) -> Self {
Self::Unsigned(value)
}
pub const fn negative(value: i64) -> Result<Self, DeterministicCborProfileError> {
if value < 0 {
Ok(Self::Negative(DeterministicCborNegativeInteger { value }))
} else {
Err(DeterministicCborProfileError::NegativeIntegerMustBeNegative)
}
}
}
impl fmt::Debug for DeterministicCborInteger {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Unsigned(_) => formatter
.debug_tuple("DeterministicCborInteger::Unsigned")
.field(&Redacted)
.finish(),
Self::Negative(value) => formatter
.debug_tuple("DeterministicCborInteger::Negative")
.field(value)
.finish(),
}
}
}
impl DeterministicCborInteger {
fn zeroize_owned(&mut self) {
match self {
Self::Unsigned(value) => value.zeroize(),
Self::Negative(value) => value.zeroize_owned(),
}
}
}
impl Drop for DeterministicCborInteger {
fn drop(&mut self) {
self.zeroize_owned();
}
}
impl ZeroizeOnDrop for DeterministicCborInteger {}
pub struct DeterministicCborNegativeInteger {
value: i64,
}
impl DeterministicCborNegativeInteger {
pub const fn value(&self) -> i64 {
self.value
}
}
impl fmt::Debug for DeterministicCborNegativeInteger {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_tuple("DeterministicCborNegativeInteger")
.field(&Redacted)
.finish()
}
}
impl DeterministicCborNegativeInteger {
fn zeroize_owned(&mut self) {
self.value.zeroize();
}
}
impl Drop for DeterministicCborNegativeInteger {
fn drop(&mut self) {
self.zeroize_owned();
}
}
impl ZeroizeOnDrop for DeterministicCborNegativeInteger {}
#[non_exhaustive]
pub enum DeterministicCborMapKey {
Integer(DeterministicCborInteger),
Text(String),
}
impl DeterministicCborMapKey {
pub fn text(value: String) -> Self {
Self::Text(value)
}
}
impl fmt::Debug for DeterministicCborMapKey {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Integer(value) => formatter
.debug_tuple("DeterministicCborMapKey::Integer")
.field(value)
.finish(),
Self::Text(value) => formatter
.debug_struct("DeterministicCborMapKey::Text")
.field("byte_len", &value.len())
.field("value", &Redacted)
.finish(),
}
}
}
impl Drop for DeterministicCborMapKey {
fn drop(&mut self) {
self.zeroize_owned();
}
}
impl DeterministicCborMapKey {
fn zeroize_owned(&mut self) {
match self {
Self::Integer(value) => value.zeroize_owned(),
Self::Text(value) => value.zeroize(),
}
}
}
impl ZeroizeOnDrop for DeterministicCborMapKey {}
pub struct DeterministicCborMapEntry {
key: DeterministicCborMapKey,
value: DeterministicCborValue,
}
impl DeterministicCborMapEntry {
pub fn new(key: DeterministicCborMapKey, value: DeterministicCborValue) -> Self {
Self { key, value }
}
pub const fn key(&self) -> &DeterministicCborMapKey {
&self.key
}
pub const fn value(&self) -> &DeterministicCborValue {
&self.value
}
}
impl fmt::Debug for DeterministicCborMapEntry {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("DeterministicCborMapEntry")
.field("key", &self.key)
.field("value", &self.value)
.finish()
}
}
impl DeterministicCborMapEntry {
fn zeroize_owned(&mut self) {
self.key.zeroize_owned();
self.value.zeroize_owned();
}
}
impl Drop for DeterministicCborMapEntry {
fn drop(&mut self) {
self.zeroize_owned();
}
}
impl ZeroizeOnDrop for DeterministicCborMapEntry {}
#[non_exhaustive]
pub enum DeterministicCborValue {
Null,
Bool(bool),
Integer(DeterministicCborInteger),
Text(String),
Bytes(Vec<u8>),
Array(Vec<DeterministicCborValue>),
Map(Vec<DeterministicCborMapEntry>),
}
impl fmt::Debug for DeterministicCborValue {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Null => formatter.write_str("DeterministicCborValue::Null"),
Self::Bool(_) => formatter
.debug_tuple("DeterministicCborValue::Bool")
.field(&Redacted)
.finish(),
Self::Integer(value) => formatter
.debug_tuple("DeterministicCborValue::Integer")
.field(value)
.finish(),
Self::Text(value) => formatter
.debug_struct("DeterministicCborValue::Text")
.field("byte_len", &value.len())
.field("value", &Redacted)
.finish(),
Self::Bytes(value) => formatter
.debug_struct("DeterministicCborValue::Bytes")
.field("byte_len", &value.len())
.field("value", &Redacted)
.finish(),
Self::Array(values) => formatter
.debug_struct("DeterministicCborValue::Array")
.field("len", &values.len())
.finish(),
Self::Map(entries) => formatter
.debug_struct("DeterministicCborValue::Map")
.field("len", &entries.len())
.finish(),
}
}
}
impl Drop for DeterministicCborValue {
fn drop(&mut self) {
self.zeroize_owned();
}
}
impl DeterministicCborValue {
fn zeroize_owned(&mut self) {
match self {
Self::Bool(value) => value.zeroize(),
Self::Integer(value) => value.zeroize_owned(),
Self::Text(value) => value.zeroize(),
Self::Bytes(value) => value.zeroize(),
Self::Array(values) => zeroize_values(values),
Self::Map(entries) => zeroize_entries(entries),
Self::Null => {}
}
}
}
impl ZeroizeOnDrop for DeterministicCborValue {}
pub(crate) fn try_vec_with_capacity<T>(capacity: usize) -> Result<Vec<T>, DeterministicCborError> {
let mut values = Vec::new();
values
.try_reserve_exact(capacity)
.map_err(|_| DeterministicCborError::AllocationFailure)?;
Ok(values)
}
pub(crate) fn try_string_copy(value: &str) -> Result<String, DeterministicCborError> {
let mut copy = String::new();
copy.try_reserve_exact(value.len())
.map_err(|_| DeterministicCborError::AllocationFailure)?;
copy.push_str(value);
Ok(copy)
}
fn zeroize_values(values: &mut Vec<DeterministicCborValue>) {
for value in values.iter_mut() {
value.zeroize_owned();
}
values.clear();
values.spare_capacity_mut().zeroize();
}
fn zeroize_entries(entries: &mut Vec<DeterministicCborMapEntry>) {
for entry in entries.iter_mut() {
entry.zeroize_owned();
}
entries.clear();
entries.spare_capacity_mut().zeroize();
}
struct Redacted;
impl fmt::Debug for Redacted {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("<redacted>")
}
}
#[cfg(test)]
mod tests;