#![cfg_attr(not(feature = "std"), no_std)]
#![forbid(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
#[cfg(feature = "alloc")]
extern crate alloc;
pub mod wrappers {
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use core::ops::Deref;
#[cfg(feature = "alloc")]
use zeroize::{Zeroize, ZeroizeOnDrop};
#[cfg(feature = "alloc")]
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct SecretBytes(Vec<u8>);
#[cfg(feature = "alloc")]
impl SecretBytes {
pub fn new(bytes: Vec<u8>) -> Self {
Self(bytes)
}
pub fn expose(&self) -> &[u8] {
&self.0
}
pub fn into_inner(mut self) -> Vec<u8> {
core::mem::take(&mut self.0)
}
}
#[cfg(feature = "alloc")]
impl AsRef<[u8]> for SecretBytes {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[cfg(feature = "alloc")]
impl Deref for SecretBytes {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(feature = "alloc")]
impl From<Vec<u8>> for SecretBytes {
fn from(v: Vec<u8>) -> Self {
Self(v)
}
}
#[cfg(feature = "alloc")]
impl core::fmt::Debug for SecretBytes {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "SecretBytes([redacted], len={})", self.0.len())
}
}
#[cfg(feature = "alloc")]
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct SecretKey(Vec<u8>);
#[cfg(feature = "alloc")]
impl core::fmt::Debug for SecretKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "SecretKey([redacted], len={})", self.0.len())
}
}
#[cfg(feature = "alloc")]
impl SecretKey {
pub fn new(bytes: Vec<u8>) -> Self {
Self(bytes)
}
pub fn expose(&self) -> &[u8] {
&self.0
}
pub fn ct_eq(&self, other: &Self) -> bool {
let a = &self.0;
let b = &other.0;
let max_len = if a.len() > b.len() { a.len() } else { b.len() };
let mut acc: u8 = (a.len() ^ b.len()) as u8;
let mut i = 0;
while i < max_len {
let av = a.get(i).copied().unwrap_or(0);
let bv = b.get(i).copied().unwrap_or(0);
acc |= av ^ bv;
i += 1;
}
acc == 0
}
pub fn into_inner(mut self) -> Vec<u8> {
core::mem::take(&mut self.0)
}
}
#[cfg(feature = "alloc")]
impl AsRef<[u8]> for SecretKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[cfg(feature = "alloc")]
impl Deref for SecretKey {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(feature = "alloc")]
impl From<Vec<u8>> for SecretKey {
fn from(v: Vec<u8>) -> Self {
Self(v)
}
}
}
pub mod traits {
}
#[cfg(any(test, doc))]
pub mod test_utils {
}
#[cfg(test)]
mod tests {
use super::wrappers::{SecretBytes, SecretKey};
use alloc::format;
use alloc::vec;
use zeroize::Zeroize;
#[test]
fn secret_key_zeroize_sets_to_zero() {
let mut key = SecretKey::new(vec![1u8, 2, 3, 4, 5]);
assert!(key.expose().iter().any(|&b| b != 0));
key.zeroize();
assert!(key.expose().iter().all(|&b| b == 0));
}
#[test]
fn secret_bytes_zeroize_sets_to_zero() {
let mut bytes = SecretBytes::new(vec![10u8, 11, 12, 13]);
assert!(bytes.expose().iter().any(|&b| b != 0));
bytes.zeroize();
assert!(bytes.expose().iter().all(|&b| b == 0));
}
#[test]
fn secret_key_debug_is_redacted() {
let key = SecretKey::new(vec![9u8, 8, 7]);
let s = format!("{:?}", key);
assert!(s.contains("SecretKey([redacted]"));
assert!(s.contains("len=3"));
assert!(!s.contains("9, 8, 7"));
}
#[test]
fn secret_bytes_debug_is_redacted() {
let bytes = SecretBytes::new(vec![1u8, 2, 3, 4]);
let s = format!("{:?}", bytes);
assert!(s.contains("SecretBytes([redacted]"));
assert!(s.contains("len=4"));
assert!(!s.contains("1, 2, 3, 4"));
}
#[test]
fn secret_key_into_inner_round_trip() {
let original = vec![1u8, 2, 3, 4, 5];
let key = SecretKey::new(original.clone());
let out = key.into_inner();
assert_eq!(out, vec![1u8, 2, 3, 4, 5]);
}
#[test]
fn secret_bytes_into_inner_round_trip() {
let original = vec![10u8, 11, 12, 13];
let bytes = SecretBytes::new(original.clone());
let out = bytes.into_inner();
assert_eq!(out, vec![10u8, 11, 12, 13]);
}
#[test]
fn secret_key_as_ref_and_deref() {
let key = SecretKey::new(vec![42u8, 43, 44]);
assert_eq!(key.as_ref(), &[42u8, 43, 44]);
assert_eq!(&*key, &[42u8, 43, 44]);
}
#[test]
fn secret_bytes_as_ref_and_deref() {
let bytes = SecretBytes::new(vec![7u8, 8, 9]);
assert_eq!(bytes.as_ref(), &[7u8, 8, 9]);
assert_eq!(&*bytes, &[7u8, 8, 9]);
}
#[test]
fn secret_key_ct_eq_true_and_false() {
let a1 = SecretKey::new(vec![1u8, 2, 3, 4]);
let a2 = SecretKey::new(vec![1u8, 2, 3, 4]);
let b = SecretKey::new(vec![1u8, 2, 3, 5]);
assert!(a1.ct_eq(&a2));
assert!(!a1.ct_eq(&b));
}
}