use std::collections::hash_map::HashMap;
use crate::{
AtomClass,
AtomType,
CaseInsensitiveCiphertext,
code::{
Code,
Decode,
Encode,
},
CryptoError,
CryptographicAtom,
map,
};
use super::{
base64_decode,
base64_encode,
};
#[derive(Clone, Copy)]
pub struct Base64Context {
atom_class: AtomClass,
atom_type: AtomType,
}
impl Base64Context {
fn get_decode_alphabet() -> HashMap<u8, u32> {
map![
(0x41, 0), (0x42, 1), (0x43, 2), (0x44, 3),
(0x45, 4), (0x46, 5), (0x47, 6), (0x48, 7),
(0x49, 8), (0x4a, 9), (0x4b, 10), (0x4c, 11),
(0x4d, 12), (0x4e, 13), (0x4f, 14), (0x50, 15),
(0x51, 16), (0x52, 17), (0x53, 18), (0x54, 19),
(0x55, 20), (0x56, 21), (0x57, 22), (0x58, 23),
(0x59, 24), (0x5a, 25), (0x61, 26), (0x62, 27),
(0x63, 28), (0x64, 29), (0x65, 30), (0x66, 31),
(0x67, 32), (0x68, 33), (0x69, 34), (0x6a, 35),
(0x6b, 36), (0x6c, 37), (0x6d, 38), (0x6e, 39),
(0x6f, 40), (0x70, 41), (0x71, 42), (0x72, 43),
(0x73, 44), (0x74, 45), (0x75, 46), (0x76, 47),
(0x77, 48), (0x78, 49), (0x79, 50), (0x7a, 51),
(0x30, 52), (0x31, 53), (0x32, 54), (0x33, 55),
(0x34, 56), (0x35, 57), (0x36, 58), (0x37, 59),
(0x38, 60), (0x39, 61), (0x2b, 62), (0x2f, 63),
(0x3d, 64)
]
}
fn get_encode_alphabet() -> [u8; 65] {
[
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f,
0x3d
]
}
pub fn new() -> Base64Context {
Base64Context {
atom_class: AtomClass::Code,
atom_type: AtomType::Base64,
}
}
fn validate_ciphertext(
ciphertext: &Vec<u8>,
) -> Result<(), CryptoError> {
let length = ciphertext.len();
if length % 4 != 0 {
return Err(CryptoError::IllegalResidueClass(
"Failure during Base64 ciphertext validation!".to_string(),
(ciphertext.len() % 4) as u8, 4,
));
}
let alphabet = Base64Context::get_decode_alphabet();
let padding_byte = Base64Context::get_encode_alphabet()[64];
let mut second_last_byte_is_padding = false;
let mut i = 0;
for c in ciphertext {
if i < length - 2 && (!alphabet.contains_key(c) || *c == padding_byte) {
return Err(CryptoError::IllegalCharacter(
"Failure during Base64 ciphertext validation!".to_string(), i,
ciphertext.clone(),
));
} else if i == length - 2 {
if !alphabet.contains_key(c) {
return Err(CryptoError::IllegalCharacter(
"Failure during Base64 ciphertext validation!".to_string(), i,
ciphertext.clone(),
));
} else {
second_last_byte_is_padding = *ciphertext.get(i).unwrap() == padding_byte;
}
} else {
if !alphabet.contains_key(c) {
return Err(CryptoError::IllegalCharacter(
"Failure during Base64 ciphertext validation!".to_string(), i,
ciphertext.clone(),
));
} else if second_last_byte_is_padding
&& *ciphertext.get(i).unwrap() != padding_byte {
return Err(CryptoError::MalformedPadding(
"Failure during Base64 ciphertext validation!".to_string(),
ciphertext.clone(),
));
}
}
i += 1;
}
Ok(())
}
}
impl CaseInsensitiveCiphertext for Base64Context {
fn ciphertext_is_capitalized(&self) -> Option<bool> { None }
fn set_ciphertext_capitalization(&mut self, _capitalized: bool) {}
}
impl Code for Base64Context {}
impl CryptographicAtom for Base64Context {
fn get_atom_class(&self) -> AtomClass { self.atom_class }
fn get_atom_type(&self) -> AtomType { self.atom_type }
}
impl Decode for Base64Context {
fn decode(
&mut self,
ciphertext: &Vec<u8>,
) -> Result<Vec<u8>, CryptoError> {
if let Err(error) = Base64Context::validate_ciphertext(ciphertext) {
Err(error)
} else {
Ok(base64_decode(ciphertext, Base64Context::get_decode_alphabet()))
}
}
}
impl Encode for Base64Context {
fn encode(
&mut self,
plaintext: &Vec<u8>,
) -> Result<Vec<u8>, CryptoError> {
Ok(base64_encode(plaintext, Base64Context::get_encode_alphabet()))
}
}
#[cfg(any(test, feature = "doc_tests"))]
mod tests {
use super::*;
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_01() {
let ctx = Base64Context::new();
assert_eq![ctx.get_atom_class(), AtomClass::Code];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_02() {
let ctx = Base64Context::new();
assert_eq![ctx.get_atom_type(), AtomType::Base64];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_03() {
let ctx = Base64Context::new();
assert_eq![ctx.ciphertext_is_capitalized(), None];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_04() {
let mut ctx = Base64Context::new();
ctx.set_ciphertext_capitalization(true);
assert_eq![ctx.ciphertext_is_capitalized(), None];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_05() {
let mut ctx = Base64Context::new();
ctx.set_ciphertext_capitalization(false);
assert_eq![ctx.ciphertext_is_capitalized(), None];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_06() {
let ciphertext = vec![];
let expected_plaintext = vec![];
let mut ctx = Base64Context::new();
let plaintext = ctx.decode(&ciphertext);
assert_eq![plaintext, Ok(expected_plaintext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_07() {
let ciphertext = vec![0x5a, 0x67, 0x3d, 0x3d];
let expected_plaintext = vec![0x66];
let mut ctx = Base64Context::new();
let plaintext = ctx.decode(&ciphertext);
assert_eq![plaintext, Ok(expected_plaintext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_08() {
let ciphertext = vec![0x5a, 0x6d, 0x38, 0x3d];
let expected_plaintext = vec![0x66, 0x6f];
let mut ctx = Base64Context::new();
let plaintext = ctx.decode(&ciphertext);
assert_eq![plaintext, Ok(expected_plaintext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_09() {
let ciphertext = vec![0x5a, 0x6d, 0x39, 0x76];
let expected_plaintext = vec![0x66, 0x6f, 0x6f];
let mut ctx = Base64Context::new();
let plaintext = ctx.decode(&ciphertext);
assert_eq![plaintext, Ok(expected_plaintext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_10() {
let ciphertext = vec![0x5a, 0x6d, 0x39, 0x76, 0x59, 0x67, 0x3d, 0x3d];
let expected_plaintext = vec![0x66, 0x6f, 0x6f, 0x62];
let mut ctx = Base64Context::new();
let plaintext = ctx.decode(&ciphertext);
assert_eq![plaintext, Ok(expected_plaintext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_11() {
let ciphertext = vec![0x5a, 0x6d, 0x39, 0x76, 0x59, 0x6d, 0x45, 0x3d];
let expected_plaintext = vec![0x66, 0x6f, 0x6f, 0x62, 0x61];
let mut ctx = Base64Context::new();
let plaintext = ctx.decode(&ciphertext);
assert_eq![plaintext, Ok(expected_plaintext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_12() {
let ciphertext = vec![0x5a, 0x6d, 0x39, 0x76, 0x59, 0x6d, 0x46, 0x79];
let expected_plaintext = vec![0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72];
let mut ctx = Base64Context::new();
let plaintext = ctx.decode(&ciphertext);
assert_eq![plaintext, Ok(expected_plaintext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_13() {
let ciphertext = vec![0x61, 0x41, 0x3d, 0x3d];
let expected_plaintext = vec![0x68];
let mut ctx = Base64Context::new();
let plaintext = ctx.decode(&ciphertext);
assert_eq![plaintext, Ok(expected_plaintext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_14() {
let ciphertext = vec![0x34, 0x34, 0x47, 0x2f, 0x34, 0x34, 0x47, 0x2b];
let expected_plaintext = vec![0xe3, 0x81, 0xbf, 0xe3, 0x81, 0xbe];
let mut ctx = Base64Context::new();
let plaintext = ctx.decode(&ciphertext);
assert_eq![plaintext, Ok(expected_plaintext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_15() {
let plaintext = vec![];
let expected_ciphertext = vec![];
let mut ctx = Base64Context::new();
let ciphertext = ctx.encode(&plaintext);
assert_eq![ciphertext, Ok(expected_ciphertext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_16() {
let plaintext = vec![0x66];
let expected_ciphertext = vec![0x5a, 0x67, 0x3d, 0x3d];
let mut ctx = Base64Context::new();
let ciphertext = ctx.encode(&plaintext);
assert_eq![ciphertext, Ok(expected_ciphertext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_17() {
let plaintext = vec![0x66, 0x6f];
let expected_ciphertext = vec![0x5a, 0x6d, 0x38, 0x3d];
let mut ctx = Base64Context::new();
let ciphertext = ctx.encode(&plaintext);
assert_eq![ciphertext, Ok(expected_ciphertext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_18() {
let plaintext = vec![0x66, 0x6f, 0x6f];
let expected_ciphertext = vec![0x5a, 0x6d, 0x39, 0x76];
let mut ctx = Base64Context::new();
let ciphertext = ctx.encode(&plaintext);
assert_eq![ciphertext, Ok(expected_ciphertext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_19() {
let plaintext = vec![0x66, 0x6f, 0x6f, 0x62];
let expected_ciphertext = vec![0x5a, 0x6d, 0x39, 0x76, 0x59, 0x67, 0x3d, 0x3d];
let mut ctx = Base64Context::new();
let ciphertext = ctx.encode(&plaintext);
assert_eq![ciphertext, Ok(expected_ciphertext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_20() {
let plaintext = vec![0x66, 0x6f, 0x6f, 0x62, 0x61];
let expected_ciphertext = vec![0x5a, 0x6d, 0x39, 0x76, 0x59, 0x6d, 0x45, 0x3d];
let mut ctx = Base64Context::new();
let ciphertext = ctx.encode(&plaintext);
assert_eq![ciphertext, Ok(expected_ciphertext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_21() {
let plaintext = vec![0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72];
let expected_ciphertext = vec![0x5a, 0x6d, 0x39, 0x76, 0x59, 0x6d, 0x46, 0x79];
let mut ctx = Base64Context::new();
let ciphertext = ctx.encode(&plaintext);
assert_eq![ciphertext, Ok(expected_ciphertext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_22() {
let plaintext = vec![0x68];
let expected_ciphertext = vec![0x61, 0x41, 0x3d, 0x3d];
let mut ctx = Base64Context::new();
let ciphertext = ctx.encode(&plaintext);
assert_eq![ciphertext, Ok(expected_ciphertext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_23() {
let plaintext = vec![0xe3, 0x81, 0xbf, 0xe3, 0x81, 0xbe];
let expected_ciphertext = vec![0x34, 0x34, 0x47, 0x2f, 0x34, 0x34, 0x47, 0x2b];
let mut ctx = Base64Context::new();
let ciphertext = ctx.encode(&plaintext);
assert_eq![ciphertext, Ok(expected_ciphertext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_24() {
let mut decode_alphabet: HashMap<u8, u32> = HashMap::new();
let mut k = 0x40;
for v in 0..=25 {
k += 1;
decode_alphabet.insert(k, v);
}
k = 0x60;
for v in 26..=51 {
k += 1;
decode_alphabet.insert(k, v);
}
k = 0x2f;
for v in 52..=61 {
k += 1;
decode_alphabet.insert(k, v);
}
decode_alphabet.insert(0x2b, 62);
decode_alphabet.insert(0x2f, 63);
decode_alphabet.insert(0x3d, 64);
assert_eq![Base64Context::get_decode_alphabet().len(), decode_alphabet.len()];
assert_eq![Base64Context::get_decode_alphabet(), decode_alphabet];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_25() {
let mut encode_alphabet: [u8; 65] = [0; 65];
let mut k = 0;
for v in 0x41..=0x5a {
encode_alphabet[k] = v;
k += 1;
}
for v in 0x61..=0x7a {
encode_alphabet[k] = v;
k += 1;
}
for v in 0x30..=0x39 {
encode_alphabet[k] = v;
k += 1;
}
encode_alphabet[k] = 0x2b;
k += 1;
encode_alphabet[k] = 0x2f;
k += 1;
encode_alphabet[k] = 0x3d;
assert_eq![Base64Context::get_encode_alphabet().len(), encode_alphabet.len()];
assert_eq![Base64Context::get_encode_alphabet(), encode_alphabet];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_26() {
let ciphertext = vec![];
assert_eq![Base64Context::validate_ciphertext(&ciphertext), Ok(())];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_27() {
let ciphertext = vec![0x41, 0x42, 0x43, 0x44];
assert_eq![Base64Context::validate_ciphertext(&ciphertext), Ok(())];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_28() {
let ciphertext = vec![0x56, 0x57, 0x58, 0x3d];
assert_eq![Base64Context::validate_ciphertext(&ciphertext), Ok(())];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_29() {
let ciphertext = vec![0x59, 0x5a, 0x3d, 0x3d];
assert_eq![Base64Context::validate_ciphertext(&ciphertext), Ok(())];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_30() {
let ciphertext = vec![0x41, 0x42];
assert_eq![
Base64Context::validate_ciphertext(&ciphertext), Err(CryptoError::IllegalResidueClass(
"Failure during Base64 ciphertext validation!".to_string(), 2, 4,
))
];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_31() {
let ciphertext = vec![0x45, 0x40, 0x46, 0x47];
assert_eq![
Base64Context::validate_ciphertext(&ciphertext), Err(CryptoError::IllegalCharacter(
"Failure during Base64 ciphertext validation!".to_string(), 1,
ciphertext,
))
];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_32() {
let ciphertext = vec![0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x3a, 0x3d];
assert_eq![
Base64Context::validate_ciphertext(&ciphertext), Err(CryptoError::IllegalCharacter(
"Failure during Base64 ciphertext validation!".to_string(), 6,
ciphertext,
))
];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_33() {
let ciphertext = vec![0x48, 0x49, 0x4a, 0x4b, 0x3d, 0x4d, 0x4e, 0x4f];
assert_eq![
Base64Context::validate_ciphertext(&ciphertext), Err(CryptoError::IllegalCharacter(
"Failure during Base64 ciphertext validation!".to_string(), 4,
ciphertext,
))
];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_34() {
let ciphertext = vec![
0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71,
0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x3d, 0x39,
];
assert_eq![
Base64Context::validate_ciphertext(&ciphertext), Err(CryptoError::MalformedPadding(
"Failure during Base64 ciphertext validation!".to_string(),
ciphertext,
))
];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_35() {
let ciphertext = vec![
0x7a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x2b, 0x2f, 0x2f, 0x2b, 0x3d, 0xff,
];
assert_eq![Base64Context::validate_ciphertext(&ciphertext),
Err(CryptoError::IllegalCharacter(
"Failure during Base64 ciphertext validation!".to_string(), 15,
ciphertext,
))];
}
}