#![cfg(feature = "ztier")]
#[cfg(feature = "keyless")]
use crate::constants::HARDCODED_SECRET_BYTES;
use crate::{format::IntoFormat, Encoding, Error, Format, ObtextCodec, Scheme};
use super::zdec_auto;
use super::zsecret::ZSecret;
pub struct Obz {
zsecret: ZSecret,
format: Format,
}
impl Obz {
pub fn new(format: impl IntoFormat, secret: &str) -> Result<Self, Error> {
let format = format.into_format()?;
validate_ztier_scheme(format.scheme())?;
Ok(Self {
zsecret: ZSecret::from_base64(secret)?,
format,
})
}
pub fn format(&self) -> Format {
self.format
}
pub fn set_format(&mut self, format: impl IntoFormat) -> Result<(), Error> {
let format = format.into_format()?;
validate_ztier_scheme(format.scheme())?;
self.format = format;
Ok(())
}
pub fn set_scheme(&mut self, scheme: Scheme) -> Result<(), Error> {
validate_ztier_scheme(scheme)?;
self.format = Format::new(scheme, self.format.encoding());
Ok(())
}
pub fn set_encoding(&mut self, encoding: Encoding) -> Result<(), Error> {
self.format = Format::new(self.format.scheme(), encoding);
Ok(())
}
#[inline]
pub fn autodec(&self, obtext: &str) -> Result<String, Error> {
if let Ok(result) =
zdec_auto::dec_any_scheme_ztier(&self.zsecret, self.format.encoding(), obtext)
{
return Ok(result);
}
zdec_auto::dec_any_format_ztier(&self.zsecret, obtext)
}
#[cfg(feature = "keyless")]
pub fn new_keyless(format: impl IntoFormat) -> Result<Self, Error> {
let format = format.into_format()?;
validate_ztier_scheme(format.scheme())?;
Ok(Self {
zsecret: ZSecret::from_bytes(&HARDCODED_SECRET_BYTES)?,
format,
})
}
#[cfg(feature = "hex-keys")]
pub fn from_hex_key(format: impl IntoFormat, secret_hex: &str) -> Result<Self, Error> {
let format = format.into_format()?;
validate_ztier_scheme(format.scheme())?;
Ok(Self {
zsecret: ZSecret::from_hex(secret_hex)?,
format,
})
}
#[cfg(feature = "bytes-keys")]
pub fn from_bytes(format: impl IntoFormat, secret: &[u8; 32]) -> Result<Self, Error> {
let format = format.into_format()?;
validate_ztier_scheme(format.scheme())?;
Ok(Self {
zsecret: ZSecret::from_bytes(secret)?,
format,
})
}
#[inline]
pub fn secret(&self) -> String {
self.zsecret.secret_base64()
}
#[inline]
#[cfg(feature = "hex-keys")]
pub fn secret_hex(&self) -> String {
self.zsecret.secret_hex()
}
#[inline]
#[cfg(feature = "bytes-keys")]
pub fn secret_bytes(&self) -> &[u8; 32] {
self.zsecret.secret_bytes()
}
}
impl ObtextCodec for Obz {
fn enc(&self, plaintext: &str) -> Result<String, Error> {
#[cfg(feature = "legacy")]
if self.format.scheme() == Scheme::Legacy {
let legacy = super::legacy::Legacy::from_master_secret(self.zsecret.master_secret())?;
return <super::legacy::Legacy as ObtextCodec>::enc(&legacy, plaintext);
}
crate::ztier::enc_to_format_ztier(plaintext, self.format, self.zsecret.master_secret())
}
fn dec(&self, obtext: &str) -> Result<String, Error> {
#[cfg(feature = "legacy")]
if self.format.scheme() == Scheme::Legacy {
let legacy = super::legacy::Legacy::from_master_secret(self.zsecret.master_secret())?;
return <super::legacy::Legacy as ObtextCodec>::dec(&legacy, obtext);
}
crate::ztier::dec_from_format_ztier(obtext, self.format, self.zsecret.master_secret())
}
fn format(&self) -> Format {
self.format
}
fn scheme(&self) -> Scheme {
self.format.scheme()
}
fn encoding(&self) -> Encoding {
self.format.encoding()
}
}
impl Obz {
#[inline]
pub fn enc(&self, plaintext: &str) -> Result<String, Error> {
<Self as ObtextCodec>::enc(self, plaintext)
}
#[inline]
pub fn dec(&self, obtext: &str) -> Result<String, Error> {
<Self as ObtextCodec>::dec(self, obtext)
}
#[inline]
pub fn scheme(&self) -> Scheme {
<Self as ObtextCodec>::scheme(self)
}
#[inline]
pub fn encoding(&self) -> Encoding {
<Self as ObtextCodec>::encoding(self)
}
}
fn validate_ztier_scheme(scheme: Scheme) -> Result<(), Error> {
match scheme {
#[cfg(feature = "zrbcx")]
Scheme::Zrbcx => Ok(()),
#[cfg(feature = "zmock")]
Scheme::Zmock1 => Ok(()),
#[cfg(feature = "legacy")]
Scheme::Legacy => Ok(()),
_ => Err(Error::InvalidScheme),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "zrbcx")]
fn test_obz_basic() {
let secret = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; let obz = Obz::new("zrbcx.b64", secret).unwrap();
let plaintext = "hello world";
let ot = obz.enc(plaintext).unwrap();
let pt2 = obz.dec(&ot).unwrap();
assert_eq!(pt2, plaintext);
}
#[test]
#[cfg(feature = "zrbcx")]
fn test_obz_format_switching() {
let secret = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
let mut obz = Obz::new("zrbcx.c32", secret).unwrap();
assert_eq!(obz.encoding(), Encoding::C32);
obz.set_encoding(Encoding::B64).unwrap();
assert_eq!(obz.encoding(), Encoding::B64);
}
#[test]
#[cfg(all(feature = "zrbcx", feature = "legacy"))]
fn test_obz_scheme_switching() {
let secret = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
let mut obz = Obz::new("zrbcx.b64", secret).unwrap();
assert_eq!(obz.scheme(), Scheme::Zrbcx);
obz.set_scheme(Scheme::Legacy).unwrap();
assert_eq!(obz.scheme(), Scheme::Legacy);
}
#[test]
#[cfg(feature = "zrbcx")]
fn test_obz_rejects_non_ztier_scheme() {
let secret = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
#[cfg(feature = "aasv")]
{
let result = Obz::new("aasv.b64", secret);
assert!(result.is_err());
}
}
}