use crate::errors::LedgerError;
const MAX_DATA_SIZE: usize = 255;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct APDUData(Vec<u8>);
impl APDUData {
pub fn new(buf: &[u8]) -> Self {
let length = std::cmp::min(buf.len(), MAX_DATA_SIZE);
APDUData(buf[..length].to_vec())
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn resize(&mut self, new_size: usize, fill_with: u8) {
self.0.resize(std::cmp::min(new_size, MAX_DATA_SIZE), fill_with)
}
pub fn data(self) -> Vec<u8> {
self.0
}
}
impl From<&[u8]> for APDUData {
fn from(buf: &[u8]) -> Self {
Self::new(buf)
}
}
impl From<Vec<u8>> for APDUData {
fn from(mut v: Vec<u8>) -> Self {
v.resize(std::cmp::min(v.len(), MAX_DATA_SIZE), 0);
Self(v)
}
}
impl AsRef<[u8]> for APDUData {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct APDUCommand {
pub ins: u8,
pub p1: u8,
pub p2: u8,
pub data: APDUData,
pub response_len: Option<u8>,
}
impl APDUCommand {
pub fn serialized_length(&self) -> usize {
let mut length = 4;
if !self.data.is_empty() {
length += 1;
length += self.data.len();
}
length += if self.response_len.is_some() { 1 } else { 0 };
length
}
pub fn write_to<W: std::io::Write>(&self, w: &mut W) -> Result<usize, std::io::Error> {
w.write_all(&[0xE0, self.ins, self.p1, self.p2])?;
if !self.data.is_empty() {
w.write_all(&[self.data.len() as u8])?;
w.write_all(&self.data.as_ref())?;
}
if let Some(response_len) = self.response_len {
w.write_all(&[response_len])?;
}
Ok(self.serialized_length())
}
pub fn serialize(&self) -> Vec<u8> {
let mut v = Vec::with_capacity(self.serialized_length());
self.write_to(&mut v).unwrap();
v
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct APDUAnswer {
response: Vec<u8>
}
impl std::fmt::Display for APDUAnswer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "APDUAnswer: {{\n\tResponse: {} \n\tData: {:?}\n}}", self.response_status(), self.data())
}
}
impl APDUAnswer {
pub fn from_answer(response: Vec<u8>) -> Result<APDUAnswer, LedgerError> {
if response.len() < 2 {
Err(LedgerError::ResponseTooShort(response.to_vec()))
} else {
Ok(Self{response})
}
}
pub fn len(&self) -> usize {
self.response.len()
}
pub fn is_empty(&self) -> bool {
self.response.is_empty()
}
pub fn is_success(&self) -> bool {
self.response_status().is_success()
}
pub fn retcode(&self) -> u16 {
let mut buf = [0u8; 2];
buf.copy_from_slice(&self.response[self.len() - 2..]);
u16::from_be_bytes(buf)
}
pub fn response_status(&self) -> APDUResponseCodes {
self.retcode().into()
}
pub fn data(&self) -> Option<&[u8]> {
if self.is_success() {
Some(&self.response[..self.len() - 2])
} else {
None
}
}
}
#[repr(u16)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum APDUResponseCodes {
NoError = 0x9000,
ExecutionError = 0x6400,
WrongLength = 0x6700,
EmptyBuffer = 0x6982,
OutputBufferTooSmall = 0x6983,
DataInvalid = 0x6984,
ConditionsNotSatisfied = 0x6985,
CommandNotAllowed = 0x6986,
BadKeyHandle = 0x6A80,
InvalidP1P2 = 0x6B00,
InsNotSupported = 0x6D00,
ClaNotSupported = 0x6E00,
Unknown = 0x6F00,
SignVerifyError = 0x6F01,
}
impl std::fmt::Display for APDUResponseCodes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Code {:x} ({})", *self as u16, self.description())
}
}
impl APDUResponseCodes {
pub fn is_success(self) -> bool {
self == APDUResponseCodes::NoError
}
pub fn description(self) -> &'static str {
match self {
APDUResponseCodes::NoError => "[APDU_CODE_NOERROR]",
APDUResponseCodes::ExecutionError => "[APDU_CODE_EXECUTION_ERROR] No information given (NV-Ram not changed)",
APDUResponseCodes::WrongLength => "[APDU_CODE_WRONG_LENGTH] Wrong length",
APDUResponseCodes::EmptyBuffer => "[APDU_CODE_EMPTY_BUFFER]",
APDUResponseCodes::OutputBufferTooSmall => "[APDU_CODE_OUTPUT_BUFFER_TOO_SMALL]",
APDUResponseCodes::DataInvalid => "[APDU_CODE_DATA_INVALID] data reversibly blocked (invalidated)",
APDUResponseCodes::ConditionsNotSatisfied => "[APDU_CODE_CONDITIONS_NOT_SATISFIED] Conditions of use not satisfied",
APDUResponseCodes::CommandNotAllowed => "[APDU_CODE_COMMAND_NOT_ALLOWED] Command not allowed (no current EF)",
APDUResponseCodes::BadKeyHandle => "[APDU_CODE_BAD_KEY_HANDLE] The parameters in the data field are incorrect",
APDUResponseCodes::InvalidP1P2 => "[APDU_CODE_INVALIDP1P2] Wrong parameter(s) P1-P2",
APDUResponseCodes::InsNotSupported => "[APDU_CODE_INS_NOT_SUPPORTED] Instruction code not supported or invalid",
APDUResponseCodes::ClaNotSupported => "[APDU_CODE_CLA_NOT_SUPPORTED] Class not supported",
APDUResponseCodes::Unknown => "[APDU_CODE_UNKNOWN]",
APDUResponseCodes::SignVerifyError => "[APDU_CODE_SIGN_VERIFY_ERROR]",
}
}
}
impl From<u16> for APDUResponseCodes {
fn from(code: u16) -> Self {
match code {
0x9000 => APDUResponseCodes::NoError,
0x6400 => APDUResponseCodes::ExecutionError,
0x6700 => APDUResponseCodes::WrongLength,
0x6982 => APDUResponseCodes::EmptyBuffer,
0x6983 => APDUResponseCodes::OutputBufferTooSmall,
0x6984 => APDUResponseCodes::DataInvalid,
0x6985 => APDUResponseCodes::ConditionsNotSatisfied,
0x6986 => APDUResponseCodes::CommandNotAllowed,
0x6A80 => APDUResponseCodes::BadKeyHandle,
0x6B00 => APDUResponseCodes::InvalidP1P2,
0x6D00 => APDUResponseCodes::InsNotSupported,
0x6E00 => APDUResponseCodes::ClaNotSupported,
0x6F00 => APDUResponseCodes::Unknown,
0x6F01 => APDUResponseCodes::SignVerifyError,
_ => { panic!("Unknown APDU response code {:x}", code) }
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn serialize() {
let data: &[u8] = &[0, 0, 0, 1, 0, 0, 0, 1];
let command = APDUCommand {
ins: 0x01,
p1: 0x00,
p2: 0x00,
data: data.into(),
response_len: None,
};
let serialized_command = command.serialize();
let expected = vec![224, 1, 0, 0, 8, 0, 0, 0, 1, 0, 0, 0, 1];
assert_eq!(serialized_command, expected);
let command = APDUCommand {
ins: 0x01,
p1: 0x00,
p2: 0x00,
data: data.into(),
response_len: Some(13),
};
let serialized_command = command.serialize();
let expected = vec![224, 1, 0, 0, 8, 0, 0, 0, 1, 0, 0, 0, 1, 13];
assert_eq!(serialized_command, expected)
}
}