pub mod fixed;
pub mod generic;
pub mod hash;
#[cfg(feature = "encryption")]
pub mod encrypted;
pub use fixed::Token;
pub use generic::GenericToken;
pub use hash::TokenHash;
#[cfg(feature = "encryption")]
pub use encrypted::EncryptedToken;
#[cfg(feature = "encryption")]
use crate::crypto::{CipherSuite, EncryptedSecret};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[cfg(feature = "encryption")]
#[error("Crypto error: {0}")]
Crypto(#[from] crate::crypto::Error),
#[error("Random error: {0}")]
Random(#[from] crate::random::Error),
#[error("Length mismatch: expected = {expected}; provided = {provided}")]
LengthMismatch {
expected: usize,
provided: usize,
},
#[error("Invalid base64: {0}")]
Base64(#[from] base64::DecodeError),
#[error("Hash algorithm is missing")]
HashAlgorithmMissing,
#[error("Hash algorithm is unknown or invalid: {0}")]
HashAlgorithmInvalid(u8),
}
pub type Result<T> = std::result::Result<T, Error>;
pub trait TokenMaterial: Sized {
#[must_use]
fn as_bytes(&self) -> &[u8];
fn from_vec(bytes: Vec<u8>) -> Result<Self>;
#[must_use]
fn to_default_hash(&self, pepper: &[u8]) -> TokenHash {
TokenHash::compute(self, pepper)
}
#[must_use]
fn to_blake3(&self, pepper: &[u8]) -> TokenHash {
TokenHash::compute_blake3_v1(self, pepper)
}
#[must_use]
fn to_base64(&self) -> String {
crate::encoding::base64::encode(self.as_bytes())
}
fn from_base64(data: &str) -> Result<Self> {
let decoded = crate::encoding::base64::decode(data)?;
Self::from_vec(decoded)
}
#[cfg(feature = "encryption")]
fn encrypt(&self, key: &[u8], algo: CipherSuite) -> Result<EncryptedToken<Self>> {
let enc = EncryptedSecret::encrypt(self.as_bytes(), key, algo)?;
Ok(EncryptedToken::from_secret(enc))
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Test(Vec<u8>);
impl TokenMaterial for Test {
fn as_bytes(&self) -> &[u8] {
&self.0
}
fn from_vec(bytes: Vec<u8>) -> Result<Self> {
Ok(Self(bytes))
}
}
#[test]
fn base64_roundtrip() {
let obj = Test(b"abcdef".into());
let s = obj.to_base64();
let deserialized = Test::from_base64(&s).unwrap();
assert_eq!(obj.0, deserialized.0);
assert_eq!(s, "YWJjZGVm");
}
#[test]
fn reject_base64_wrong_length() {
let obj = Test(b"abcdef".into());
let mut s = obj.to_base64();
s.pop();
assert!(Test::from_base64(&s).is_err());
}
}