use super::header_constants::{
ALLOWED_ALGORITHMS, ALLOWED_EXPORTABILITIES, ALLOWED_KEY_USAGES, ALLOWED_MODES_OF_USE,
ALLOWED_VERSION_IDS,
};
use super::opt_block::OptBlock;
use std::error::Error;
#[derive(Debug, PartialEq)]
pub struct KeyBlockHeader {
version_id: String,
kb_length: u16,
key_usage: String,
algorithm: String,
mode_of_use: String,
key_version_number: String,
exportability: String,
num_opt_blocks: u8,
reserved_field: String,
opt_blocks: Option<Box<OptBlock>>,
}
impl KeyBlockHeader {
pub fn new_empty() -> Self {
Self {
version_id: String::new(),
kb_length: 0,
key_usage: String::new(),
algorithm: String::new(),
mode_of_use: String::new(),
key_version_number: String::new(),
exportability: String::new(),
num_opt_blocks: 0,
reserved_field: "00".to_string(),
opt_blocks: None,
}
}
pub fn new_with_values(
version_id: &str,
key_usage: &str,
algorithm: &str,
mode_of_use: &str,
key_version_number: &str,
exportability: &str,
) -> Result<Self, Box<dyn Error>> {
let mut header = KeyBlockHeader::new_empty();
header.set_version_id(version_id)?;
header.set_key_usage(key_usage)?;
header.set_algorithm(algorithm)?;
header.set_mode_of_use(mode_of_use)?;
header.set_key_version_number(key_version_number)?;
header.set_exportability(exportability)?;
Ok(header)
}
pub fn new_from_str(header_str: &str) -> Result<Self, Box<dyn Error>> {
if header_str.len() < 16 {
return Err(Box::<dyn Error>::from(
"ERROR TR-31 HEADER: Invalid data length",
));
}
let version_id = header_str[0..1].to_string();
let kb_length = header_str[1..5]
.parse::<u16>()
.map_err(|_| Box::<dyn Error>::from("ERROR TR-31 HEADER: Invalid key block length"))?;
let key_usage = header_str[5..7].to_string();
let algorithm = header_str[7..8].to_string();
let mode_of_use = header_str[8..9].to_string();
let key_version_number = header_str[9..11].to_string();
let exportability = header_str[11..12].to_string();
let num_optional_blocks = header_str[12..14].parse::<u8>().map_err(|_| {
Box::<dyn Error>::from("ERROR TR-31 HEADER: Invalid number of optional blocks")
})?;
let reserved_field = header_str[14..16].to_string();
let mut header = Self::new_empty();
header.set_version_id(&version_id)?;
header.set_kb_length(kb_length)?;
header.set_key_usage(&key_usage)?;
header.set_algorithm(&algorithm)?;
header.set_mode_of_use(&mode_of_use)?;
header.set_key_version_number(&key_version_number)?;
header.set_exportability(&exportability)?;
header.set_num_optional_blocks(num_optional_blocks)?;
header.set_reserved_field(&reserved_field)?;
if num_optional_blocks > 0 && header_str.len() < 20 {
return Err(
"ERROR TR-31 HEADER: Invalid header length containing optional blocks".into(),
);
}
if num_optional_blocks > 0 {
let opt_block_str = &header_str[16..];
let opt_block_res = OptBlock::new_from_str(opt_block_str, num_optional_blocks as usize);
if let Err(e) = opt_block_res {
return Err(
format!("ERROR TR-31 HEADER: Failed to parse optional blocks: {}", e).into(),
);
}
header.opt_blocks = Some(Box::new(opt_block_res.unwrap()));
}
Ok(header)
}
pub fn export_str(&self) -> Result<String, Box<dyn Error>> {
if self.version_id.is_empty()
|| self.key_usage.is_empty()
|| self.algorithm.is_empty()
|| self.mode_of_use.is_empty()
|| self.key_version_number.is_empty()
|| self.exportability.is_empty()
|| self.reserved_field.is_empty()
{
return Err(
"ERROR TR-31 HEADER: Export failed due to empty field(s) or zero length".into(),
);
}
let mut header_str = String::new();
header_str.push_str(&self.version_id());
header_str.push_str(&format!("{:04}", self.kb_length()));
header_str.push_str(&self.key_usage());
header_str.push_str(&self.algorithm());
header_str.push_str(&self.mode_of_use());
header_str.push_str(&self.key_version_number());
header_str.push_str(&self.exportability());
header_str.push_str(&format!("{:02}", self.num_opt_blocks));
header_str.push_str(&self.reserved_field());
if let Some(ref opt_blocks) = self.opt_blocks {
header_str.push_str(&opt_blocks.export_str()?);
}
Ok(header_str)
}
pub fn set_version_id(&mut self, value: &str) -> Result<(), Box<dyn Error>> {
if ALLOWED_VERSION_IDS.contains(&value) {
self.version_id = value.to_string();
Ok(())
} else {
Err(Box::<dyn Error>::from(format!(
"ERROR TR-31 HEADER: Invalid version ID: {}",
value
)))
}
}
pub fn version_id(&self) -> &str {
&self.version_id
}
pub fn set_kb_length(&mut self, value: u16) -> Result<(), Box<dyn Error>> {
if value > 9999 {
Err(Box::<dyn Error>::from(
"ERROR TR-31 HEADER: Invalid key block length",
))
} else {
self.kb_length = value;
Ok(())
}
}
pub fn kb_length(&self) -> u16 {
self.kb_length
}
pub fn set_key_usage(&mut self, value: &str) -> Result<(), Box<dyn Error>> {
if ALLOWED_KEY_USAGES.contains(&value) {
self.key_usage = value.to_string();
Ok(())
} else {
Err(Box::<dyn Error>::from(format!(
"ERROR TR-31 HEADER: Invalid key usage: {}",
value
)))
}
}
pub fn key_usage(&self) -> &str {
&self.key_usage
}
pub fn set_algorithm(&mut self, value: &str) -> Result<(), Box<dyn Error>> {
if ALLOWED_ALGORITHMS.contains(&value) {
self.algorithm = value.to_string();
Ok(())
} else {
Err(Box::<dyn Error>::from(format!(
"ERROR TR-31 HEADER: Invalid algorithm: {}",
value
)))
}
}
pub fn algorithm(&self) -> &str {
&self.algorithm
}
pub fn set_mode_of_use(&mut self, value: &str) -> Result<(), Box<dyn Error>> {
if ALLOWED_MODES_OF_USE.contains(&value) {
self.mode_of_use = value.to_string();
Ok(())
} else {
Err(Box::<dyn Error>::from(format!(
"ERROR TR-31 HEADER: Invalid mode of use: {}",
value
)))
}
}
pub fn mode_of_use(&self) -> &str {
&self.mode_of_use
}
pub fn set_key_version_number(&mut self, value: &str) -> Result<(), Box<dyn Error>> {
if value.len() != 2 {
return Err(Box::<dyn Error>::from(format!(
"ERROR TR-31 HEADER: Key version number must consist of 2 ASCII characters: {}",
value
)));
}
if !value.chars().all(|c| c.is_ascii()) {
return Err(Box::<dyn Error>::from(format!(
"ERROR TR-31 HEADER: Key version number must consist of ASCII characters: {}",
value
)));
}
self.key_version_number = value.to_string();
Ok(())
}
pub fn key_version_number(&self) -> &str {
&self.key_version_number
}
pub fn set_exportability(&mut self, value: &str) -> Result<(), Box<dyn Error>> {
if ALLOWED_EXPORTABILITIES.contains(&value) {
self.exportability = value.to_string();
Ok(())
} else {
Err(Box::<dyn Error>::from(format!(
"ERROR TR-31 HEADER: Invalid exportability: {}",
value
)))
}
}
pub fn exportability(&self) -> &str {
&self.exportability
}
pub fn set_num_optional_blocks(&mut self, value: u8) -> Result<(), Box<dyn Error>> {
if value > 99 {
return Err(Box::<dyn Error>::from(
"ERROR TR-31 HEADER: Number of opt blocks value is too large",
));
}
self.num_opt_blocks = value;
Ok(())
}
pub fn num_optional_blocks(&self) -> u8 {
self.num_opt_blocks
}
pub fn set_reserved_field(&mut self, value: &str) -> Result<(), Box<dyn Error>> {
if value == "00" {
self.reserved_field = value.to_string();
Ok(())
} else {
return Err(Box::<dyn Error>::from(format!(
"ERROR TR-31 HEADER: Invalid value for reserved field: {}",
value.to_string()
)));
}
}
pub fn reserved_field(&self) -> &str {
&self.reserved_field
}
pub fn set_opt_blocks(&mut self, opt_blocks: Option<Box<OptBlock>>) {
self.opt_blocks = opt_blocks;
self.num_opt_blocks = 0;
if let Some(ref opt_block) = self.opt_blocks {
let mut current_block: &OptBlock = opt_block.as_ref();
self.num_opt_blocks = 1;
while let Some(next_block) = current_block.next() {
self.num_opt_blocks += 1;
current_block = next_block;
}
}
}
pub fn append_opt_blocks(&mut self, opt_block_to_append: OptBlock) {
let mut additional_blocks_count = 1;
let mut current_block = &opt_block_to_append;
while let Some(next_block) = current_block.next() {
additional_blocks_count += 1;
current_block = next_block;
}
match &mut self.opt_blocks {
Some(existing_opt_block) => {
existing_opt_block.append(opt_block_to_append);
}
None => {
self.opt_blocks = Some(Box::new(opt_block_to_append));
}
}
self.num_opt_blocks += additional_blocks_count;
}
pub fn opt_blocks(&self) -> &Option<Box<OptBlock>> {
&self.opt_blocks
}
pub fn len(&self) -> usize {
let mut header_length = 16;
if let Some(ref opt_blocks) = self.opt_blocks {
header_length += opt_blocks.total_length();
}
header_length
}
pub fn finalize(&mut self) -> Result<(), Box<dyn Error>> {
let block_size = if self.version_id == "D" { 16 } else { 8 };
let header_length = self.len();
if let Some(ref mut opt_blocks) = self.opt_blocks {
if header_length % block_size != 0 {
let mut padding_needed = block_size - (header_length % block_size);
if padding_needed < 6 {
padding_needed += block_size;
}
let padding_data_length = padding_needed - 4;
let padding_data = "0".repeat(padding_data_length);
let padding_block = OptBlock::new("PB", &padding_data, None)?;
opt_blocks.append(padding_block);
self.num_opt_blocks += 1;
}
}
Ok(())
}
}