use std::collections::hash_map::HashMap;
use crate::{
AtomClass,
AtomType,
CaseInsensitiveCiphertext,
code::{
Code,
Decode,
Encode,
},
CryptoError,
CryptographicAtom,
map,
util::text_to_mono_case,
};
#[derive(Clone, Copy)]
pub struct Base16Context {
atom_class: AtomClass,
atom_type: AtomType,
is_capitalized: bool,
}
impl Base16Context {
fn get_lowercase_decode_alphabet() -> HashMap<u8, u8> {
map![
(0x30, 0), (0x31, 1), (0x32, 2), (0x33, 3),
(0x34, 4), (0x35, 5), (0x36, 6), (0x37, 7),
(0x38, 8), (0x39, 9), (0x61, 10), (0x62, 11),
(0x63, 12), (0x64, 13), (0x65, 14), (0x66, 15)
]
}
fn get_uppercase_decode_alphabet() -> HashMap<u8, u8> {
map![
(0x30, 0), (0x31, 1), (0x32, 2), (0x33, 3),
(0x34, 4), (0x35, 5), (0x36, 6), (0x37, 7),
(0x38, 8), (0x39, 9), (0x41, 10), (0x42, 11),
(0x43, 12), (0x44, 13), (0x45, 14), (0x46, 15)
]
}
fn get_lowercase_encode_alphabet() -> [u8; 16] {
[
0x30, 0x31, 0x32, 0x33,
0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x61, 0x62,
0x63, 0x64, 0x65, 0x66,
]
}
fn get_uppercase_encode_alphabet() -> [u8; 16] {
[
0x30, 0x31, 0x32, 0x33,
0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x41, 0x42,
0x43, 0x44, 0x45, 0x46,
]
}
pub fn new() -> Base16Context {
Base16Context {
atom_class: AtomClass::Code,
atom_type: AtomType::Base16,
is_capitalized: false,
}
}
fn validate_ciphertext(
&self,
ciphertext: &Vec<u8>,
) -> Result<(), CryptoError> {
if ciphertext.len() % 2 != 0 {
return Err(CryptoError::IllegalResidueClass(
"Failure during Base16 ciphertext validation!".to_string(), 1, 2,
));
}
let alphabet = if self.is_capitalized {
Base16Context::get_uppercase_decode_alphabet()
} else {
Base16Context::get_lowercase_decode_alphabet()
};
let mut i = 0;
for c in ciphertext {
if !alphabet.contains_key(c) {
return Err(CryptoError::IllegalCharacter(
"Failure during Base16 ciphertext validation!".to_string(), i,
ciphertext.clone(),
));
}
i += 1;
}
Ok(())
}
}
impl CaseInsensitiveCiphertext for Base16Context {
fn ciphertext_is_capitalized(&self) -> Option<bool> { Some(self.is_capitalized) }
fn set_ciphertext_capitalization(
&mut self,
capitalized: bool,
) {
self.is_capitalized = capitalized;
}
}
impl Code for Base16Context {}
impl CryptographicAtom for Base16Context {
fn get_atom_class(&self) -> AtomClass { self.atom_class }
fn get_atom_type(&self) -> AtomType { self.atom_type }
}
impl Decode for Base16Context {
fn decode(
&mut self,
ciphertext: &Vec<u8>,
) -> Result<Vec<u8>, CryptoError> {
let mono_case_ciphertext = text_to_mono_case(
ciphertext, self.is_capitalized,
);
if let Err(error) = self.validate_ciphertext(&mono_case_ciphertext) {
Err(error)
} else {
let mut plaintext = Vec::new();
let mut iter = mono_case_ciphertext.iter();
let mut maybe_next = iter.next();
let alphabet = if self.is_capitalized {
Base16Context::get_uppercase_decode_alphabet()
} else {
Base16Context::get_lowercase_decode_alphabet()
};
while maybe_next != None {
let mut block: u8 = 0;
let mut byte = maybe_next.unwrap();
let mut character = *alphabet.get(byte).unwrap();
block |= character << 4;
byte = iter.next().unwrap();
character = *alphabet.get(byte).unwrap();
block |= character;
plaintext.push(block);
maybe_next = iter.next();
}
Ok(plaintext)
}
}
}
impl Encode for Base16Context {
fn encode(
&mut self,
plaintext: &Vec<u8>,
) -> Result<Vec<u8>, CryptoError> {
let mut ciphertext = Vec::new();
let mut iter = plaintext.iter();
let mut byte = iter.next();
let alphabet = if self.is_capitalized {
Base16Context::get_uppercase_encode_alphabet()
} else {
Base16Context::get_lowercase_encode_alphabet()
};
while byte != None {
let value = byte.unwrap();
ciphertext.push(alphabet[(value >> 4) as usize]);
ciphertext.push(alphabet[(value & 0xf) as usize]);
byte = iter.next();
}
Ok(ciphertext)
}
}
#[cfg(any(feature = "doc_tests", test))]
mod tests {
use super::*;
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_01() {
let ctx = Base16Context::new();
assert_eq![ctx.get_atom_class(), AtomClass::Code];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_02() {
let ctx = Base16Context::new();
assert_eq![ctx.get_atom_type(), AtomType::Base16];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_03() {
let ctx = Base16Context::new();
assert_eq![ctx.ciphertext_is_capitalized(), Some(false)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_04() {
let mut ctx = Base16Context::new();
ctx.set_ciphertext_capitalization(true);
assert_eq![ctx.ciphertext_is_capitalized(), Some(true)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_05() {
let mut ctx = Base16Context::new();
ctx.set_ciphertext_capitalization(true);
ctx.set_ciphertext_capitalization(false);
assert_eq![ctx.ciphertext_is_capitalized(), Some(false)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_06() {
let mut ctx = Base16Context::new();
ctx.set_ciphertext_capitalization(false);
assert_eq![ctx.ciphertext_is_capitalized(), Some(false)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_07() {
let ciphertext = vec![];
let expected_plaintext = vec![];
let mut ctx = Base16Context::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![
0x36, 0x36,
];
let expected_plaintext = vec![0x66];
let mut ctx = Base16Context::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![
0x36, 0x36, 0x36, 0x66,
];
let expected_plaintext = vec![0x66, 0x6f];
let mut ctx = Base16Context::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![
0x36, 0x36, 0x36, 0x46,
0x36, 0x46,
];
let expected_plaintext = vec![0x66, 0x6f, 0x6f];
let mut ctx = Base16Context::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![
0x36, 0x36, 0x36, 0x66,
0x36, 0x46, 0x36, 0x32,
];
let expected_plaintext = vec![0x66, 0x6f, 0x6f, 0x62];
let mut ctx = Base16Context::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![
0x36, 0x36, 0x36, 0x66,
0x36, 0x66, 0x36, 0x32,
0x36, 0x31,
];
let expected_plaintext = vec![0x66, 0x6f, 0x6f, 0x62, 0x61];
let mut ctx = Base16Context::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![
0x36, 0x36, 0x36, 0x46,
0x36, 0x46, 0x36, 0x32,
0x36, 0x31, 0x37, 0x32,
];
let expected_plaintext = vec![0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72];
let mut ctx = Base16Context::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![
0x45, 0x33, 0x38, 0x31,
0x42, 0x66, 0x65, 0x33,
0x38, 0x31, 0x62, 0x65,
];
let expected_plaintext = vec![0xe3, 0x81, 0xbf, 0xe3, 0x81, 0xbe];
let mut ctx = Base16Context::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 = Base16Context::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![
0x36, 0x36,
];
let mut ctx = Base16Context::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![
0x36, 0x36, 0x36, 0x46,
];
let mut ctx = Base16Context::new();
ctx.set_ciphertext_capitalization(true);
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![
0x36, 0x36, 0x36, 0x66,
0x36, 0x66,
];
let mut ctx = Base16Context::new();
ctx.is_capitalized = false;
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![
0x36, 0x36, 0x36, 0x46,
0x36, 0x46, 0x36, 0x32,
];
let mut ctx = Base16Context::new();
ctx.set_ciphertext_capitalization(true);
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![
0x36, 0x36, 0x36, 0x66,
0x36, 0x66, 0x36, 0x32,
0x36, 0x31,
];
let mut ctx = Base16Context::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![
0x36, 0x36, 0x36, 0x46,
0x36, 0x46, 0x36, 0x32,
0x36, 0x31, 0x37, 0x32,
];
let mut ctx = Base16Context::new();
ctx.set_ciphertext_capitalization(true);
let ciphertext = ctx.encode(&plaintext);
assert_eq![ciphertext, Ok(expected_ciphertext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_22() {
let plaintext = vec![0xe3, 0x81, 0xbf, 0xe3, 0x81, 0xbe];
let expected_ciphertext = vec![
0x65, 0x33, 0x38, 0x31,
0x62, 0x66, 0x65, 0x33,
0x38, 0x31, 0x62, 0x65,
];
let mut ctx = Base16Context::new();
let ciphertext = ctx.encode(&plaintext);
assert_eq![ciphertext, Ok(expected_ciphertext)];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_23() {
let mut decode_alphabet: HashMap<u8, u8> = HashMap::new();
for v in 0..=9 { decode_alphabet.insert(v + 0x30, v); }
for v in 10..=15 { decode_alphabet.insert(v + 0x37, v); }
assert_eq![Base16Context::get_uppercase_decode_alphabet().len(), decode_alphabet.len()];
assert_eq![Base16Context::get_uppercase_decode_alphabet(), decode_alphabet];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_24() {
let mut decode_alphabet: HashMap<u8, u8> = HashMap::new();
for v in 0..=9 { decode_alphabet.insert(v + 0x30, v); }
for v in 10..=15 { decode_alphabet.insert(v + 0x57, v); }
assert_eq![Base16Context::get_lowercase_decode_alphabet().len(), decode_alphabet.len()];
assert_eq![Base16Context::get_lowercase_decode_alphabet(), decode_alphabet];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_25() {
let mut encode_alphabet: [u8; 16] = [0; 16];
for v in 0x30..=0x39 { encode_alphabet[v - 0x30] = v as u8; }
for v in 0x41..=0x46 { encode_alphabet[v - 0x37] = v as u8; }
assert_eq![Base16Context::get_uppercase_encode_alphabet().len(), encode_alphabet.len()];
assert_eq![Base16Context::get_uppercase_encode_alphabet(), encode_alphabet];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_26() {
let mut encode_alphabet: [u8; 16] = [0; 16];
for v in 0x30..=0x39 { encode_alphabet[v - 0x30] = v as u8; }
for v in 0x61..=0x66 { encode_alphabet[v - 0x57] = v as u8; }
assert_eq![Base16Context::get_lowercase_encode_alphabet().len(), encode_alphabet.len()];
assert_eq![Base16Context::get_lowercase_encode_alphabet(), encode_alphabet];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_27() {
let ctx = Base16Context::new();
let ciphertext = vec![];
assert_eq![ctx.validate_ciphertext(&ciphertext), Ok(())];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_28() {
let mut ctx = Base16Context::new();
ctx.is_capitalized = true;
let ciphertext = vec![
0x30, 0x31, 0x41, 0x42,
0x43, 0x39, 0x38, 0x37,
0x36, 0x46, 0x45, 0x44,
0x32, 0x33, 0x34, 0x35,
];
assert_eq![ctx.validate_ciphertext(&ciphertext), Ok(())];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_29() {
let mut ctx = Base16Context::new();
ctx.is_capitalized = false;
let ciphertext = vec![
0x30, 0x31, 0x61, 0x62,
0x63, 0x39, 0x38, 0x37,
0x36, 0x66, 0x65, 0x64,
0x32, 0x33, 0x34, 0x35,
];
assert_eq![ctx.validate_ciphertext(&ciphertext), Ok(())];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_30() {
let ctx = Base16Context::new();
let ciphertext = vec![
0x32, 0x33, 0x34, 0x35,
0x36, 0x36, 0x37,
];
assert_eq![ctx.validate_ciphertext(&ciphertext), Err(CryptoError::IllegalResidueClass(
"Failure during Base16 ciphertext validation!".to_string(),
1, 2,
))];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_31() {
let mut ctx = Base16Context::new();
ctx.is_capitalized = true;
let ciphertext = vec![
0x41, 0x42, 0x43, 0x44,
0xff, 0x46, 0x30, 0x31,
0x32, 0x33, 0x34, 0x35,
0x36, 0x37, 0x38, 0x39,
];
assert_eq![ctx.validate_ciphertext(&ciphertext), Err(CryptoError::IllegalCharacter(
"Failure during Base16 ciphertext validation!".to_string(),
4, ciphertext,
))];
}
#[cfg_attr(not(feature = "doc_tests"), test)]
fn test_32() {
let mut ctx = Base16Context::new();
ctx.is_capitalized = true;
let ciphertext = vec![
0x41, 0x42, 0x43, 0x44,
0x45, 0x66, 0x30, 0x31,
0x32, 0x33, 0x34, 0x35,
0x36, 0x37, 0x38, 0x39,
];
assert_eq![ctx.validate_ciphertext(&ciphertext), Err(CryptoError::IllegalCharacter(
"Failure during Base16 ciphertext validation!".to_string(),
5, ciphertext,
))];
}
}