use crate::card::{Card, Retries, ShortEfId};
use crate::data::{Date, malformed};
use crate::error::Result;
use crate::pin::Pin;
use crate::tlv::simple;
use crate::transport::Transmit;
pub const DF: [u8; 10] = [0xD3, 0x92, 0x10, 0x00, 0x31, 0x00, 0x01, 0x01, 0x01, 0x00];
pub mod ef {
pub const CARD_INFO: u16 = 0x0001;
pub const KEY_REFERENCE: u16 = 0x0002;
pub const INTERNAL_AUTHENTICATION_KEY: u16 = 0x0019;
pub const PIN: u16 = 0x001C;
}
#[derive(Debug)]
pub struct CommonAp<'a, T> {
card: &'a mut Card<T>,
}
impl<'a, T: Transmit> CommonAp<'a, T> {
pub fn select(card: &'a mut Card<T>) -> Result<Self> {
card.select_df(&DF)?;
Ok(CommonAp { card })
}
pub fn card(&mut self) -> &mut Card<T> {
self.card
}
pub fn read_record(&mut self, id: u16, record: u8) -> Result<Vec<u8>> {
self.card.select_ef(id)?;
self.card.read_record(record)
}
pub fn read_card_info(&mut self) -> Result<CardInfo> {
let raw = self.read_record(ef::CARD_INFO, 1)?;
CardInfo::parse(&raw)
}
pub fn internal_authenticate(&mut self, challenge: &[u8]) -> Result<Vec<u8>> {
let sfi = ShortEfId::from_ef_id(ef::INTERNAL_AUTHENTICATION_KEY)?;
self.card.internal_authenticate(sfi, challenge)
}
pub fn verify_pin(&mut self, pin: &Pin) -> Result<()> {
self.card.select_ef(ef::PIN)?;
self.card.verify(pin)
}
pub fn change_pin(&mut self, current_pin: &Pin, new_pin: &Pin) -> Result<()> {
self.card.select_ef(ef::PIN)?;
self.card.verify(current_pin)?;
self.card.change_key(new_pin)
}
pub fn pin_retries(&mut self) -> Result<Retries> {
self.card.select_ef(ef::PIN)?;
self.card.pin_retries()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CardInfo {
pub serial: String,
pub municipality_code: String,
pub expiry: Date,
}
impl CardInfo {
pub const TAG: u8 = 0x01;
pub const LEN: usize = 28;
pub fn parse(record: &[u8]) -> Result<Self> {
let tlv = simple::parse(record)?;
if tlv.tag != Self::TAG {
return Err(malformed(&format!("expected tag 01, got {:02X}", tlv.tag)));
}
if tlv.value.len() != Self::LEN {
return Err(malformed(&format!(
"card info must be {} digits, got {}",
Self::LEN,
tlv.value.len()
)));
}
if !tlv.value.iter().all(u8::is_ascii_digit) {
return Err(malformed("card info must be all digits"));
}
let text = std::str::from_utf8(tlv.value).expect("digits are ASCII");
Ok(CardInfo {
serial: text[..15].to_owned(),
municipality_code: text[15..20].to_owned(),
expiry: Date::parse(&tlv.value[20..])?,
})
}
pub fn prefecture_code(&self) -> &str {
&self.municipality_code[..2]
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::transport::mock::MockTransport;
const RECORD: &[u8] = b"\x01\x1c4000000127198431322120350217";
#[test]
fn parses_the_card_info_record() {
let info = CardInfo::parse(RECORD).unwrap();
assert_eq!(info.serial, "400000012719843");
assert_eq!(info.municipality_code, "13221");
assert_eq!(info.prefecture_code(), "13");
assert_eq!(
info.expiry,
Date {
year: 2035,
month: 2,
day: 17
}
);
}
#[test]
fn rejects_a_record_of_the_wrong_shape() {
assert!(CardInfo::parse(b"\x02\x1c4000000127198431322120350217").is_err());
assert!(CardInfo::parse(b"\x01\x1b400000012719843132212035021").is_err());
assert!(CardInfo::parse(b"\x01\x1c40000001271984313221203502XX").is_err());
}
#[test]
fn changing_the_pin_uses_jicsap_change_key() {
let mut card = Card::new(MockTransport::new([
vec![0x90, 0x00], vec![0x90, 0x00], vec![0x90, 0x00], vec![0x90, 0x00], ]));
let mut common = CommonAp::select(&mut card).unwrap();
common
.change_pin(
&Pin::numeric("1234").unwrap(),
&Pin::numeric("5678").unwrap(),
)
.unwrap();
assert_eq!(
card.transport().sent[1],
[0x00, 0xA4, 0x02, 0x0C, 0x02, 0x00, 0x1C]
);
assert_eq!(
card.transport().sent[3],
[0x80, 0x32, 0x00, 0x80, 0x04, b'5', b'6', b'7', b'8']
);
}
}