Addressing some of the requested changes
This commit is contained in:
@@ -3,6 +3,8 @@ use core::convert::TryFrom;
|
|||||||
|
|
||||||
type ByteArray = &'static [u8];
|
type ByteArray = &'static [u8];
|
||||||
|
|
||||||
|
const APDU_HEADER_LEN: usize = 4;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Clone, Debug))]
|
#[cfg_attr(test, derive(Clone, Debug))]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
@@ -39,11 +41,11 @@ impl From<ApduStatusCode> for ByteArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum ApduIns {
|
pub enum ApduInstructions {
|
||||||
SELECT = 0xA4,
|
Select = 0xA4,
|
||||||
READ_BINARY = 0xB0,
|
ReadBinary = 0xB0,
|
||||||
GET_RESPONSE = 0xC0,
|
GetResponse = 0xC0,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Clone, Debug))]
|
#[cfg_attr(test, derive(Clone, Debug))]
|
||||||
@@ -67,6 +69,33 @@ impl From<&[u8]> for ApduHeader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(Clone, Debug))]
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
/// The APDU cases
|
||||||
|
pub enum Case {
|
||||||
|
Le,
|
||||||
|
LcData,
|
||||||
|
LcDataLe,
|
||||||
|
// TODO: More cases to add as extended length APDUs
|
||||||
|
// Le could be 2 or 3 Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(Clone, Debug))]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum ApduType {
|
||||||
|
Instruction,
|
||||||
|
Short(Case),
|
||||||
|
Extended(Case),
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ApduType {
|
||||||
|
fn default() -> ApduType {
|
||||||
|
ApduType::Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Clone, Debug))]
|
#[cfg_attr(test, derive(Clone, Debug))]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Default, PartialEq)]
|
#[derive(Default, PartialEq)]
|
||||||
@@ -75,11 +104,9 @@ pub struct APDU {
|
|||||||
lc: u16,
|
lc: u16,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
le: u32,
|
le: u32,
|
||||||
case_type: u8,
|
case_type: ApduType,
|
||||||
}
|
}
|
||||||
|
|
||||||
const APDU_HEADER_LEN: u8 = 4;
|
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for APDU {
|
impl TryFrom<&[u8]> for APDU {
|
||||||
type Error = ApduStatusCode;
|
type Error = ApduStatusCode;
|
||||||
|
|
||||||
@@ -90,24 +117,23 @@ impl TryFrom<&[u8]> for APDU {
|
|||||||
// +-----+-----+----+----+
|
// +-----+-----+----+----+
|
||||||
// header | CLA | INS | P1 | P2 |
|
// header | CLA | INS | P1 | P2 |
|
||||||
// +-----+-----+----+----+
|
// +-----+-----+----+----+
|
||||||
let (header, payload) = frame.split_at(APDU_HEADER_LEN as usize);
|
let (header, payload) = frame.split_at(APDU_HEADER_LEN);
|
||||||
|
|
||||||
let mut apdu = APDU {
|
let mut apdu = APDU {
|
||||||
header: header.into(),
|
header: header.into(),
|
||||||
lc: 0x00,
|
lc: 0x00,
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
le: 0x00,
|
le: 0x00,
|
||||||
case_type: 0x00,
|
case_type: ApduType::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// case 1
|
// case 1
|
||||||
if payload.is_empty() {
|
if payload.is_empty() {
|
||||||
apdu.case_type = 0x01;
|
apdu.case_type = ApduType::Instruction;
|
||||||
} else {
|
} else {
|
||||||
let byte_0 = payload[0];
|
let byte_0 = payload[0];
|
||||||
// case 2S (Le)
|
|
||||||
if payload.len() == 1 {
|
if payload.len() == 1 {
|
||||||
apdu.case_type = 0x02;
|
apdu.case_type = ApduType::Short(Case::Le);
|
||||||
apdu.le = if byte_0 == 0x00 {
|
apdu.le = if byte_0 == 0x00 {
|
||||||
// Ne = 256
|
// Ne = 256
|
||||||
0x100
|
0x100
|
||||||
@@ -115,15 +141,13 @@ impl TryFrom<&[u8]> for APDU {
|
|||||||
byte_0.into()
|
byte_0.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// case 3S (Lc + data)
|
|
||||||
if payload.len() == (1 + byte_0) as usize && byte_0 != 0 {
|
if payload.len() == (1 + byte_0) as usize && byte_0 != 0 {
|
||||||
apdu.case_type = 0x03;
|
apdu.case_type = ApduType::Short(Case::LcData);
|
||||||
apdu.lc = byte_0.into();
|
apdu.lc = byte_0.into();
|
||||||
apdu.data = payload[1..].to_vec();
|
apdu.data = payload[1..].to_vec();
|
||||||
}
|
}
|
||||||
// case 4S (Lc + data + Le)
|
|
||||||
if payload.len() == (1 + byte_0 + 1) as usize && byte_0 != 0 {
|
if payload.len() == (1 + byte_0 + 1) as usize && byte_0 != 0 {
|
||||||
apdu.case_type = 0x04;
|
apdu.case_type = ApduType::Short(Case::LcDataLe);
|
||||||
apdu.lc = byte_0.into();
|
apdu.lc = byte_0.into();
|
||||||
apdu.data = payload[1..(payload.len() - 1)].to_vec();
|
apdu.data = payload[1..(payload.len() - 1)].to_vec();
|
||||||
apdu.le = (*payload.last().unwrap()).into();
|
apdu.le = (*payload.last().unwrap()).into();
|
||||||
@@ -133,7 +157,7 @@ impl TryFrom<&[u8]> for APDU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Add extended length cases
|
// TODO: Add extended length cases
|
||||||
if apdu.case_type == 0x00 {
|
if apdu.case_type == ApduType::default() {
|
||||||
return Err(ApduStatusCode::SW_COND_USE_NOT_SATISFIED);
|
return Err(ApduStatusCode::SW_COND_USE_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
Ok(apdu)
|
Ok(apdu)
|
||||||
@@ -163,16 +187,15 @@ mod test {
|
|||||||
lc: 0x00,
|
lc: 0x00,
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
le: 0x00,
|
le: 0x00,
|
||||||
case_type: 0x01,
|
case_type: ApduType::Instruction,
|
||||||
};
|
};
|
||||||
assert_eq!(expected, response.unwrap());
|
assert_eq!(Ok(expected), response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_case_type_2_short() {
|
fn test_case_type_2_short() {
|
||||||
let frame: [u8; 5] = [0x00, 0xb0, 0x00, 0x00, 0x0f];
|
let frame: [u8; 5] = [0x00, 0xb0, 0x00, 0x00, 0x0f];
|
||||||
let response = pass_frame(&frame);
|
let response = pass_frame(&frame);
|
||||||
assert!(response.is_ok());
|
|
||||||
let expected = APDU {
|
let expected = APDU {
|
||||||
header: ApduHeader {
|
header: ApduHeader {
|
||||||
cla: 0x00,
|
cla: 0x00,
|
||||||
@@ -183,16 +206,15 @@ mod test {
|
|||||||
lc: 0x00,
|
lc: 0x00,
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
le: 0x0f,
|
le: 0x0f,
|
||||||
case_type: 0x02,
|
case_type: ApduType::Short(Case::Le),
|
||||||
};
|
};
|
||||||
assert_eq!(expected, response.unwrap());
|
assert_eq!(Ok(expected), response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_case_type_2_short_le() {
|
fn test_case_type_2_short_le() {
|
||||||
let frame: [u8; 5] = [0x00, 0xb0, 0x00, 0x00, 0x00];
|
let frame: [u8; 5] = [0x00, 0xb0, 0x00, 0x00, 0x00];
|
||||||
let response = pass_frame(&frame);
|
let response = pass_frame(&frame);
|
||||||
assert!(response.is_ok());
|
|
||||||
let expected = APDU {
|
let expected = APDU {
|
||||||
header: ApduHeader {
|
header: ApduHeader {
|
||||||
cla: 0x00,
|
cla: 0x00,
|
||||||
@@ -203,9 +225,9 @@ mod test {
|
|||||||
lc: 0x00,
|
lc: 0x00,
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
le: 0x100,
|
le: 0x100,
|
||||||
case_type: 0x02,
|
case_type: ApduType::Short(Case::Le),
|
||||||
};
|
};
|
||||||
assert_eq!(expected, response.unwrap());
|
assert_eq!(Ok(expected), response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -213,7 +235,6 @@ mod test {
|
|||||||
let frame: [u8; 7] = [0x00, 0xa4, 0x00, 0x0c, 0x02, 0xe1, 0x04];
|
let frame: [u8; 7] = [0x00, 0xa4, 0x00, 0x0c, 0x02, 0xe1, 0x04];
|
||||||
let payload = [0xe1, 0x04];
|
let payload = [0xe1, 0x04];
|
||||||
let response = pass_frame(&frame);
|
let response = pass_frame(&frame);
|
||||||
assert!(response.is_ok());
|
|
||||||
let expected = APDU {
|
let expected = APDU {
|
||||||
header: ApduHeader {
|
header: ApduHeader {
|
||||||
cla: 0x00,
|
cla: 0x00,
|
||||||
@@ -224,9 +245,9 @@ mod test {
|
|||||||
lc: 0x02,
|
lc: 0x02,
|
||||||
data: payload.to_vec(),
|
data: payload.to_vec(),
|
||||||
le: 0x00,
|
le: 0x00,
|
||||||
case_type: 0x03,
|
case_type: ApduType::Short(Case::LcData),
|
||||||
};
|
};
|
||||||
assert_eq!(expected, response.unwrap());
|
assert_eq!(Ok(expected), response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -236,7 +257,6 @@ mod test {
|
|||||||
];
|
];
|
||||||
let payload = [0xd2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01];
|
let payload = [0xd2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01];
|
||||||
let response = pass_frame(&frame);
|
let response = pass_frame(&frame);
|
||||||
assert!(response.is_ok());
|
|
||||||
let expected = APDU {
|
let expected = APDU {
|
||||||
header: ApduHeader {
|
header: ApduHeader {
|
||||||
cla: 0x00,
|
cla: 0x00,
|
||||||
@@ -247,9 +267,9 @@ mod test {
|
|||||||
lc: 0x07,
|
lc: 0x07,
|
||||||
data: payload.to_vec(),
|
data: payload.to_vec(),
|
||||||
le: 0xff,
|
le: 0xff,
|
||||||
case_type: 0x04,
|
case_type: ApduType::Short(Case::LcDataLe),
|
||||||
};
|
};
|
||||||
assert_eq!(expected, response.unwrap());
|
assert_eq!(Ok(expected), response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -259,7 +279,6 @@ mod test {
|
|||||||
];
|
];
|
||||||
let payload = [0xd2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01];
|
let payload = [0xd2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01];
|
||||||
let response = pass_frame(&frame);
|
let response = pass_frame(&frame);
|
||||||
assert!(response.is_ok());
|
|
||||||
let expected = APDU {
|
let expected = APDU {
|
||||||
header: ApduHeader {
|
header: ApduHeader {
|
||||||
cla: 0x00,
|
cla: 0x00,
|
||||||
@@ -270,17 +289,16 @@ mod test {
|
|||||||
lc: 0x07,
|
lc: 0x07,
|
||||||
data: payload.to_vec(),
|
data: payload.to_vec(),
|
||||||
le: 0x100,
|
le: 0x100,
|
||||||
case_type: 0x04,
|
case_type: ApduType::Short(Case::LcDataLe),
|
||||||
};
|
};
|
||||||
assert_eq!(expected, response.unwrap());
|
assert_eq!(Ok(expected), response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_apdu_header_length() {
|
fn test_invalid_apdu_header_length() {
|
||||||
let frame: [u8; 3] = [0x00, 0x12, 0x00];
|
let frame: [u8; 3] = [0x00, 0x12, 0x00];
|
||||||
let response = pass_frame(&frame);
|
let response = pass_frame(&frame);
|
||||||
assert!(response.is_err());
|
assert_eq!(Err(ApduStatusCode::SW_WRONG_DATA), response);
|
||||||
assert_eq!(Some(ApduStatusCode::SW_WRONG_DATA), response.err());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -294,10 +312,6 @@ mod test {
|
|||||||
0xcb, 0x00, 0x00,
|
0xcb, 0x00, 0x00,
|
||||||
];
|
];
|
||||||
let response = pass_frame(&frame);
|
let response = pass_frame(&frame);
|
||||||
assert!(response.is_err());
|
assert_eq!(Err(ApduStatusCode::SW_COND_USE_NOT_SATISFIED), response);
|
||||||
assert_eq!(
|
|
||||||
Some(ApduStatusCode::SW_COND_USE_NOT_SATISFIED),
|
|
||||||
response.err()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user