use crate::{
error::Error,
response::{Code, Response, SubFunction},
utils, AdministrativeParameter, Configuration, ResponseData, Service,
SignatureEncryptionCalculation,
};
use std::{collections::HashSet, sync::LazyLock};
pub static SECURED_DATA_TRANS_NEGATIVES: LazyLock<HashSet<Code>> = LazyLock::new(|| {
HashSet::from([
Code::IncorrectMessageLengthOrInvalidFormat,
#[cfg(any(feature = "std2020"))]
Code::SecureDataVerificationFailed,
])
});
#[derive(Debug, Clone)]
pub struct SecuredDataTransPositive {
pub apar: AdministrativeParameter,
pub signature: SignatureEncryptionCalculation,
pub anti_replay_cnt: u16,
pub response: u8, pub response_params: Vec<u8>,
pub signature_data: Vec<u8>,
}
impl SecuredDataTransPositive {
pub fn new(
mut apar: AdministrativeParameter,
signature: SignatureEncryptionCalculation,
anti_replay_cnt: u16,
response: u8,
response_params: Vec<u8>,
signature_data: Vec<u8>,
) -> Result<Self, Error> {
if signature_data.len() > u16::MAX as usize {
return Err(Error::InvalidParam(
"length of `Signature/MAC Byte` is out of range".to_string(),
));
}
if apar.is_request() {
apar.request_set(false);
}
Ok(Self {
apar,
signature,
anti_replay_cnt,
response,
response_params,
signature_data,
})
}
}
#[derive(Debug, Clone)]
pub struct SecuredDataTransNegative {
pub apar: AdministrativeParameter,
pub signature: SignatureEncryptionCalculation,
pub anti_replay_cnt: u16,
pub service: u8, pub response: u8, pub signature_data: Vec<u8>,
}
impl SecuredDataTransNegative {
pub fn new(
mut apar: AdministrativeParameter,
signature: SignatureEncryptionCalculation,
anti_replay_cnt: u16,
service: u8,
response: u8,
signature_data: Vec<u8>,
) -> Result<Self, Error> {
if signature_data.len() > u16::MAX as usize {
return Err(Error::InvalidParam(
"length of `Signature/MAC Byte` is out of range".to_string(),
));
}
if apar.is_request() {
apar.request_set(false);
}
Ok(Self {
apar,
signature,
anti_replay_cnt,
service,
response,
signature_data,
})
}
}
#[derive(Debug, Clone)]
pub enum SecuredDataTrans {
Successful(SecuredDataTransPositive),
Unsuccessful(SecuredDataTransNegative),
}
impl From<SecuredDataTrans> for Vec<u8> {
fn from(v: SecuredDataTrans) -> Self {
let mut result = Vec::new();
match v {
SecuredDataTrans::Successful(mut v) => {
result.append(&mut v.apar.into());
result.push(v.signature.into());
let signature_len = v.signature_data.len() as u16;
result.extend(signature_len.to_be_bytes());
result.extend(v.anti_replay_cnt.to_be_bytes());
result.push(v.response);
result.append(&mut v.response_params);
result.append(&mut v.signature_data);
}
SecuredDataTrans::Unsuccessful(mut v) => {
result.append(&mut v.apar.into());
result.push(v.signature.into());
let signature_len = v.signature_data.len() as u16;
result.extend(signature_len.to_be_bytes());
result.extend(v.anti_replay_cnt.to_be_bytes());
result.push(Service::NRC.into());
result.push(v.service);
result.push(v.response);
result.append(&mut v.signature_data);
}
}
result
}
}
impl ResponseData for SecuredDataTrans {
fn new_response<T: AsRef<[u8]>>(
data: T,
sub_func: Option<u8>,
_: &Configuration,
) -> Result<Response, Error> {
let data = data.as_ref();
match sub_func {
Some(_) => Err(Error::SubFunctionError(Service::SecuredDataTrans)),
None => {
utils::data_length_check(data.len(), 8, false)?;
Ok(Response {
service: Service::SecuredDataTrans,
negative: false,
sub_func: None,
data: data.to_vec(),
})
}
}
}
}
impl TryFrom<(&Response, &Configuration)> for SecuredDataTrans {
type Error = Error;
fn try_from((resp, _): (&Response, &Configuration)) -> Result<Self, Self::Error> {
let service = resp.service;
if service != Service::SecuredDataTrans || resp.sub_func.is_some() {
return Err(Error::ServiceError(service));
}
let data = &resp.data;
let data_len = data.len();
let mut offset = 0;
let apar =
AdministrativeParameter::from(u16::from_be_bytes([data[offset], data[offset + 1]]));
offset += 2;
if apar.is_request() {
return Err(Error::InvalidData(hex::encode(data)));
}
let signature = SignatureEncryptionCalculation::try_from(data[offset])?;
offset += 1;
let signature_len = u16::from_be_bytes([data[offset], data[offset + 1]]);
offset += 2;
let anti_replay_cnt = u16::from_be_bytes([data[offset], data[offset + 1]]);
offset += 2;
let code = data[offset];
offset += 1;
if code == Service::NRC as u8 {
utils::data_length_check(data_len, offset + signature_len as usize + 2, false)?;
let service = data[offset];
offset += 1;
let response = data[offset];
offset += 1;
let signature_data = data[offset..].to_vec();
Ok(Self::Unsuccessful(SecuredDataTransNegative::new(
apar,
signature,
anti_replay_cnt,
service,
response,
signature_data,
)?))
} else {
utils::data_length_check(data_len, offset + signature_len as usize, false)?;
let curr_offset = data_len - offset - signature_len as usize;
let response_params = data[offset..offset + curr_offset].to_vec();
offset += curr_offset;
let signature_data = data[offset..].to_vec();
Ok(Self::Successful(SecuredDataTransPositive::new(
apar,
signature,
anti_replay_cnt,
code,
response_params,
signature_data,
)?))
}
}
}