use crate::ValidationError;
use subtle::{Choice, ConstantTimeEq};
use zeroize::Zeroize;
#[derive(Eq)]
struct IdentifierBytes<const MIN: usize, const MAX: usize> {
bytes: [u8; MAX],
len: u8,
}
impl<const MIN: usize, const MAX: usize> IdentifierBytes<MIN, MAX> {
fn new(bytes: &[u8]) -> Result<Self, ValidationError> {
if bytes.is_empty() {
return Err(ValidationError::Empty);
}
if bytes.len() < MIN {
return Err(ValidationError::Malformed);
}
if bytes.len() > MAX {
return Err(ValidationError::TooLarge);
}
if bytes.iter().all(|byte| *byte == 0) {
return Err(ValidationError::ZeroValue);
}
let len = u8::try_from(bytes.len()).map_err(|_| ValidationError::TooLarge)?;
let mut stored = [0; MAX];
stored[..bytes.len()].copy_from_slice(bytes);
Ok(Self { bytes: stored, len })
}
fn as_bytes(&self) -> &[u8] {
self.bytes.split_at(self.len as usize).0
}
const fn len(&self) -> usize {
self.len as usize
}
fn ct_eq(&self, other: &Self) -> bool {
let len_eq: Choice = self.len.ct_eq(&other.len);
let bytes_eq: Choice = self.bytes.ct_eq(&other.bytes);
bool::from(len_eq & bytes_eq)
}
}
impl<const MIN: usize, const MAX: usize> PartialEq for IdentifierBytes<MIN, MAX> {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other)
}
}
impl<const MIN: usize, const MAX: usize> Drop for IdentifierBytes<MIN, MAX> {
fn drop(&mut self) {
self.bytes.zeroize();
self.len.zeroize();
}
}
macro_rules! define_identifier {
($(#[$meta:meta])* $name:ident, $min:expr, $max:expr) => {
$(#[$meta])*
#[derive(Eq, PartialEq)]
pub struct $name(IdentifierBytes<{ $min }, { $max }>);
impl $name {
pub const MIN_LEN: usize = $min;
pub const MAX_LEN: usize = $max;
pub fn new(bytes: &[u8]) -> Result<Self, ValidationError> {
IdentifierBytes::new(bytes).map(Self)
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
#[must_use]
pub const fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
false
}
}
impl core::fmt::Debug for $name {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str(concat!(stringify!($name), "(..)"))
}
}
};
}
#[derive(Clone, Copy, Eq)]
pub struct Digest([u8; Self::LEN]);
impl Digest {
pub const LEN: usize = 32;
#[must_use]
pub const fn new(bytes: [u8; Self::LEN]) -> Self {
Self(bytes)
}
#[must_use]
pub const fn as_bytes(&self) -> &[u8; Self::LEN] {
&self.0
}
#[must_use]
pub fn is_zero(&self) -> bool {
bool::from(self.0.ct_eq(&[0u8; Self::LEN]))
}
#[must_use]
pub fn ct_eq(&self, other: &Self) -> bool {
bool::from(self.0.ct_eq(&other.0))
}
}
impl PartialEq for Digest {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other)
}
}
impl core::hash::Hash for Digest {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
core::hash::Hash::hash(&self.0, state);
}
}
impl core::fmt::Debug for Digest {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("Digest(..)")
}
}
define_identifier!(
StatementId,
Digest::LEN,
Digest::LEN
);
define_identifier!(
SubjectId,
1,
64
);
define_identifier!(
RealmId,
1,
64
);
define_identifier!(
ProfileId,
1,
32
);
define_identifier!(
ProofSuiteId,
1,
32
);
define_identifier!(
PolicyId,
1,
32
);
define_identifier!(
CheckpointId,
Digest::LEN,
Digest::LEN
);
define_identifier!(
NativeBindingId,
1,
64
);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct EventId(Digest);
impl EventId {
pub fn new(digest: Digest) -> Result<Self, ValidationError> {
if digest.is_zero() {
Err(ValidationError::ZeroValue)
} else {
Ok(Self(digest))
}
}
#[must_use]
pub const fn digest(&self) -> Digest {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct CapabilityRef(Digest);
impl CapabilityRef {
pub fn new(digest: Digest) -> Result<Self, ValidationError> {
if digest.is_zero() {
Err(ValidationError::ZeroValue)
} else {
Ok(Self(digest))
}
}
#[must_use]
pub const fn digest(&self) -> Digest {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct PolicyEpoch(Digest);
impl PolicyEpoch {
pub fn new(digest: Digest) -> Result<Self, ValidationError> {
if digest.is_zero() {
Err(ValidationError::ZeroValue)
} else {
Ok(Self(digest))
}
}
#[must_use]
pub const fn digest(&self) -> Digest {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct OperationSequence(u64);
impl OperationSequence {
pub const fn new(value: u64) -> Result<Self, ValidationError> {
if value == 0 {
Err(ValidationError::ZeroValue)
} else {
Ok(Self(value))
}
}
#[must_use]
pub const fn get(self) -> u64 {
self.0
}
pub const fn next(self) -> Result<Self, ValidationError> {
match self.0.checked_add(1) {
Some(value) => Ok(Self(value)),
None => Err(ValidationError::TooLarge),
}
}
#[must_use]
pub const fn immediately_follows(self, previous: Self) -> bool {
match previous.0.checked_add(1) {
Some(expected) => self.0 == expected,
None => false,
}
}
}
#[derive(Eq)]
pub struct Nonce([u8; Self::LEN]);
impl Nonce {
pub const LEN: usize = 16;
pub fn new(bytes: [u8; Self::LEN]) -> Result<Self, ValidationError> {
if bool::from(bytes.ct_eq(&[0u8; Self::LEN])) {
Err(ValidationError::ZeroValue)
} else {
Ok(Self(bytes))
}
}
#[must_use]
pub const fn as_bytes(&self) -> &[u8; Self::LEN] {
&self.0
}
#[must_use]
pub fn ct_eq(&self, other: &Self) -> bool {
bool::from(self.0.ct_eq(&other.0))
}
}
impl PartialEq for Nonce {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other)
}
}
impl Drop for Nonce {
fn drop(&mut self) {
self.0.zeroize();
}
}
impl core::fmt::Debug for Nonce {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("Nonce(..)")
}
}