#![cfg(feature = "ztier")]
use super::zsecret::ZSecret;
use crate::{constants::SCHEME_MARKER_SIZE, error::Error, Encoding};
#[cfg(feature = "zmock")]
use super::zmock1::decrypt_zmock1;
#[cfg(feature = "zmock")]
use crate::constants::ZMOCK1_MARKER;
#[cfg(feature = "zrbcx")]
use crate::{constants::ZRBCX_MARKER, decrypt_zrbcx};
#[cfg(feature = "legacy")]
use super::legacy::decrypt_legacy;
pub(crate) fn dec_any_scheme_ztier(
zsecret: &ZSecret,
encoding: Encoding,
obtext: &str,
) -> Result<String, Error> {
let mut buffer = match crate::dec::decode_obtext_to_payload(obtext, encoding) {
Ok(ct) => ct,
Err(decode_err) => {
#[cfg(feature = "legacy")]
{
return dec_legacy_fallback(zsecret, obtext).or(Err(decode_err));
}
#[cfg(not(feature = "legacy"))]
return Err(decode_err);
}
};
if buffer.len() < SCHEME_MARKER_SIZE {
#[cfg(feature = "legacy")]
{
return dec_legacy_fallback(zsecret, obtext).or(Err(Error::PayloadTooShort));
}
#[cfg(not(feature = "legacy"))]
return Err(Error::PayloadTooShort);
}
let len = buffer.len();
buffer[len - 1] ^= buffer[0];
buffer[len - 2] ^= buffer[0];
let scheme_marker = [buffer[len - 2], buffer[len - 1]];
buffer.truncate(len - SCHEME_MARKER_SIZE);
#[cfg(feature = "zrbcx")]
if scheme_marker == ZRBCX_MARKER {
let plaintext_bytes = decrypt_zrbcx(zsecret.zrbcx(), &buffer)?;
return bytes_to_string(plaintext_bytes);
}
#[cfg(feature = "zmock")]
if scheme_marker == ZMOCK1_MARKER {
let plaintext_bytes = decrypt_zmock1(zsecret.zmock1(), &buffer)?;
return bytes_to_string(plaintext_bytes);
}
#[cfg(feature = "legacy")]
{
let legacy_result = dec_legacy_fallback(zsecret, obtext)?;
validate_legacy_output(&legacy_result)?;
return Ok(legacy_result);
}
#[cfg(not(feature = "legacy"))]
Err(Error::UnknownScheme)
}
#[inline]
fn bytes_to_string(plaintext_bytes: Vec<u8>) -> Result<String, Error> {
#[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)
}
}
#[cfg(feature = "legacy")]
fn dec_legacy_fallback(zsecret: &ZSecret, obtext: &str) -> Result<String, Error> {
let reversed: Vec<u8> = obtext.bytes().rev().collect();
let ciphertext = crate::base32::BASE32_RFC_LOWER
.decode(&reversed)
.map_err(|_| Error::InvalidB32)?;
let plaintext_bytes = decrypt_legacy(zsecret.legacy(), &ciphertext)?;
bytes_to_string(plaintext_bytes)
}
#[cfg(feature = "legacy")]
fn validate_legacy_output(output: &str) -> Result<(), Error> {
let printable_count = output
.chars()
.filter(|c| c.is_ascii_graphic() || c.is_whitespace())
.count();
let total_count = output.chars().count();
if total_count > 0 && (printable_count as f32 / total_count as f32) < 0.5 {
return Err(Error::InvalidLegacyOutput);
}
Ok(())
}
#[allow(dead_code)] pub(crate) fn dec_any_scheme_c32_ztier(zsecret: &ZSecret, obtext: &str) -> Result<String, Error> {
dec_any_scheme_ztier(zsecret, Encoding::C32, obtext)
}
#[allow(dead_code)] pub(crate) fn dec_any_scheme_b32_ztier(zsecret: &ZSecret, obtext: &str) -> Result<String, Error> {
dec_any_scheme_ztier(zsecret, Encoding::B32, obtext)
}
#[allow(dead_code)] pub(crate) fn dec_any_scheme_b64_ztier(zsecret: &ZSecret, obtext: &str) -> Result<String, Error> {
dec_any_scheme_ztier(zsecret, Encoding::B64, obtext)
}
#[allow(dead_code)] pub(crate) fn dec_any_scheme_hex_ztier(zsecret: &ZSecret, obtext: &str) -> Result<String, Error> {
dec_any_scheme_ztier(zsecret, Encoding::Hex, obtext)
}
pub(crate) fn dec_any_format_ztier(zsecret: &ZSecret, obtext: &str) -> Result<String, Error> {
if obtext.contains('-')
|| obtext.contains('_')
|| (obtext.chars().any(|c| c.is_ascii_lowercase())
&& obtext.chars().any(|c| c.is_ascii_uppercase()))
{
if let Ok(result) = dec_any_scheme_b64_ztier(zsecret, obtext) {
return Ok(result);
}
}
if obtext.chars().any(|c| c.is_ascii_uppercase()) {
if let Ok(result) = dec_any_scheme_b32_ztier(zsecret, obtext) {
return Ok(result);
}
if let Ok(result) = dec_any_scheme_b64_ztier(zsecret, obtext) {
return Ok(result);
}
}
if obtext.chars().any(|c| c.is_ascii_lowercase() && c > 'f') {
if let Ok(result) = dec_any_scheme_c32_ztier(zsecret, obtext) {
return Ok(result);
}
if let Ok(result) = dec_any_scheme_b64_ztier(zsecret, obtext) {
return Ok(result);
}
}
if let Ok(result) = dec_any_scheme_hex_ztier(zsecret, obtext) {
return Ok(result);
}
if let Ok(result) = dec_any_scheme_c32_ztier(zsecret, obtext) {
return Ok(result);
}
dec_any_scheme_b64_ztier(zsecret, obtext)
}