use crate::{
base32::{BASE32_CROCKFORD, BASE32_RFC},
constants::SCHEME_MARKER_SIZE,
error::Error,
Encoding, Format, Scheme,
};
use data_encoding::{BASE64URL_NOPAD, HEXLOWER};
#[cfg(feature = "aags")]
use crate::decrypt_aags;
#[cfg(feature = "aasv")]
use crate::decrypt_aasv;
#[cfg(feature = "apgs")]
use crate::decrypt_apgs;
#[cfg(feature = "apsv")]
use crate::decrypt_apsv;
#[cfg(feature = "mock")]
use crate::decrypt_mock1;
#[cfg(feature = "mock")]
use crate::decrypt_mock2;
#[cfg(feature = "upbc")]
use crate::decrypt_upbc;
#[inline(always)]
pub(crate) fn dec_from_format(
obtext: &str,
format: Format,
master_key: &[u8; 64],
) -> Result<String, Error> {
let mut buffer = decode_obtext_to_payload(obtext, format.encoding())?;
if buffer.len() < SCHEME_MARKER_SIZE {
return Err(Error::PayloadTooShort);
}
let len = buffer.len();
let first_byte = buffer[0];
let scheme_marker = [buffer[len - 2] ^ first_byte, buffer[len - 1] ^ first_byte];
if scheme_marker != format.scheme().marker() {
return Err(Error::SchemeMarkerMismatch);
}
buffer.truncate(len - SCHEME_MARKER_SIZE);
let plaintext_bytes = match format.scheme() {
#[cfg(feature = "aags")]
Scheme::Aags => decrypt_aags(master_key, &buffer)?,
#[cfg(feature = "apgs")]
Scheme::Apgs => decrypt_apgs(master_key, &buffer)?,
#[cfg(feature = "aasv")]
Scheme::Aasv => decrypt_aasv(master_key, &buffer)?,
#[cfg(feature = "apsv")]
Scheme::Apsv => decrypt_apsv(master_key, &buffer)?,
#[cfg(feature = "upbc")]
Scheme::Upbc => decrypt_upbc(master_key, &mut buffer)?,
#[cfg(feature = "mock")]
Scheme::Mock1 => decrypt_mock1(master_key, &buffer)?,
#[cfg(feature = "mock")]
Scheme::Mock2 => decrypt_mock2(master_key, &buffer)?,
#[cfg(feature = "zrbcx")]
Scheme::Zrbcx => unreachable!("ztier uses separate path"),
#[cfg(feature = "zmock")]
Scheme::Zmock1 => unreachable!("ztier uses separate path"),
#[cfg(feature = "legacy")]
Scheme::Legacy => unreachable!("legacy uses separate path"),
};
#[cfg(feature = "unchecked-utf8")]
{
Ok(unsafe { String::from_utf8_unchecked(plaintext_bytes) })
}
#[cfg(not(feature = "unchecked-utf8"))]
{
String::from_utf8(plaintext_bytes).map_err(|_| Error::InvalidUtf8)
}
}
#[inline]
pub(crate) fn decode_obtext_to_payload(obtext: &str, encoding: Encoding) -> Result<Vec<u8>, Error> {
match encoding {
Encoding::B32 => BASE32_RFC
.decode(obtext.as_bytes())
.map_err(|_| Error::InvalidB32),
Encoding::C32 => BASE32_CROCKFORD
.decode(obtext.as_bytes())
.map_err(|_| Error::InvalidC32),
Encoding::B64 => BASE64URL_NOPAD
.decode(obtext.as_bytes())
.map_err(|_| Error::InvalidB64),
Encoding::Hex => HEXLOWER
.decode(obtext.as_bytes())
.map_err(|_| Error::InvalidHex),
}
}