use crate::card::{Card, Retries};
use crate::data::{
ApIdentification, CardVerifiableCertificate, Date, KeyId, MyNumber, RsaPublicKey, Sex,
TlvFields, check_offsets, malformed,
};
use crate::error::Result;
use crate::pin::Pin;
use crate::tlv::ber;
use crate::transport::Transmit;
pub const DF: [u8; 10] = [0xD3, 0x92, 0x10, 0x00, 0x31, 0x00, 0x01, 0x01, 0x04, 0x08];
pub mod ef {
pub const MY_NUMBER: u16 = 0x0001;
pub const ATTRIBUTES: u16 = 0x0002;
pub const INTEGRITY: u16 = 0x0003;
pub const CERTIFICATE: u16 = 0x0004;
pub const AP_BASIC_DATA: u16 = 0x0005;
pub const SESSION_KEY_PUBLIC_KEY: u16 = 0x0006;
pub const SIGNED_PUBLIC_KEY: u16 = 0x0007;
pub const UNKNOWN_0008: u16 = 0x0008;
pub const PIN: u16 = 0x0011;
pub const BLOCKED_0012: u16 = 0x0012;
pub const CODE_A: u16 = 0x0014;
pub const CODE_B: u16 = 0x0015;
}
#[derive(Debug)]
pub struct TextAp<'a, T> {
card: &'a mut Card<T>,
}
impl<'a, T: Transmit> TextAp<'a, T> {
pub fn select(card: &'a mut Card<T>) -> Result<Self> {
card.select_df(&DF)?;
Ok(TextAp { card })
}
pub fn card(&mut self) -> &mut Card<T> {
self.card
}
pub fn read_ef(&mut self, id: u16) -> Result<Vec<u8>> {
self.card.select_ef(id)?;
self.card.read_binary_all()
}
pub fn read_my_number(&mut self) -> Result<MyNumber> {
let raw = self.read_ef(ef::MY_NUMBER)?;
parse_my_number(&raw)
}
pub fn read_attributes(&mut self) -> Result<Attributes> {
let raw = self.read_ef(ef::ATTRIBUTES)?;
Attributes::parse(&raw)
}
pub fn read_certificate(&mut self) -> Result<CardVerifiableCertificate> {
let raw = self.read_ef(ef::CERTIFICATE)?;
CardVerifiableCertificate::parse(&raw)
}
pub fn read_ap_basic_data(&mut self) -> Result<ApBasicData> {
let raw = self.read_ef(ef::AP_BASIC_DATA)?;
ApBasicData::parse(&raw)
}
pub fn read_integrity_record(&mut self) -> Result<IntegrityRecord> {
let raw = self.read_ef(ef::INTEGRITY)?;
IntegrityRecord::parse(&raw)
}
pub fn read_signed_public_key(&mut self) -> Result<SignedPublicKey> {
let raw = self.read_ef(ef::SIGNED_PUBLIC_KEY)?;
SignedPublicKey::parse(&raw)
}
pub fn read_session_key_public_key(&mut self) -> Result<SessionKeyPublicKey> {
let raw = self.read_ef(ef::SESSION_KEY_PUBLIC_KEY)?;
SessionKeyPublicKey::parse(&raw)
}
pub fn read_my_number_file(&mut self) -> Result<Vec<u8>> {
self.card.select_ef(ef::MY_NUMBER)?;
self.card.read_binary_physical()
}
#[cfg(feature = "verify")]
pub fn sign(&mut self, data: &[u8]) -> Result<Vec<u8>> {
let digest_info = crate::data::sha256_digest_info(&crate::data::sha256(data));
self.card.call_ok(&crate::apdu::Command::with_data_le(
0x80,
crate::card::ins::COMPUTE_SIGNATURE,
0x00,
0x00,
digest_info,
256,
))
}
#[cfg(feature = "sm")]
pub fn open_secure_session(
&mut self,
seed: &[u8; crate::sm::SEED_LEN],
) -> Result<crate::sm::SecureSession<'_, T>> {
let key = self.read_session_key_public_key()?.public_key;
crate::sm::SecureSession::establish(self.card, &key, seed)
}
pub fn verify_pin(&mut self, pin: &Pin) -> Result<()> {
self.verify_key(ef::PIN, 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_reference_data(new_pin)
}
pub fn verify_code_a(&mut self, code: &Pin) -> Result<()> {
self.verify_key(ef::CODE_A, code)
}
pub fn verify_code_b(&mut self, code: &Pin) -> Result<()> {
self.verify_key(ef::CODE_B, code)
}
pub fn retries(&mut self, key: u16) -> Result<Retries> {
self.card.select_ef(key)?;
self.card.pin_retries()
}
fn verify_key(&mut self, key: u16, value: &Pin) -> Result<()> {
self.card.select_ef(key)?;
self.card.verify(value)
}
}
const TAG_MY_NUMBER: u32 = 0xFF10;
fn parse_my_number(raw: &[u8]) -> Result<MyNumber> {
let tlv = ber::parse(raw)?;
if tlv.tag != TAG_MY_NUMBER {
return Err(malformed(&format!(
"expected tag FF10, got {:04X}",
tlv.tag
)));
}
MyNumber::parse(tlv.value)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Attributes {
pub name: String,
pub address: String,
pub birth_date: Date,
pub sex: Sex,
}
impl Attributes {
pub const TAG: u32 = 0xFF20;
pub const TAG_OFFSETS: u32 = 0xDF21;
pub const TAG_NAME: u32 = 0xDF22;
pub const TAG_ADDRESS: u32 = 0xDF23;
pub const TAG_BIRTH_DATE: u32 = 0xDF24;
pub const TAG_SEX: u32 = 0xDF25;
pub fn parse(raw: &[u8]) -> Result<Self> {
let outer = ber::parse(raw)?;
if outer.tag != Self::TAG {
return Err(malformed(&format!(
"expected tag FF20, got {:04X}",
outer.tag
)));
}
let mut pos = raw.len() - outer.value.len();
let mut rest = outer.value;
let mut offsets = None;
let mut fields: Vec<(u32, &[u8], usize)> = Vec::new();
while let Some(&first) = rest.first() {
if first == 0x00 || first == 0xFF {
break; }
let header = ber::parse_header(rest)?;
let end = header.total_len();
let value = rest
.get(header.header_len..end)
.ok_or_else(|| malformed("a field runs past the end of the file"))?;
if header.tag == Self::TAG_OFFSETS {
offsets = Some(value);
} else {
fields.push((header.tag, value, pos));
}
pos += end;
rest = &rest[end..];
}
let find = |tag: u32| {
fields
.iter()
.find(|(t, _, _)| *t == tag)
.map(|(_, v, _)| *v)
.ok_or_else(|| malformed(&format!("no field with tag {tag:04X}")))
};
let name = find(Self::TAG_NAME)?;
let address = find(Self::TAG_ADDRESS)?;
let birth_date = find(Self::TAG_BIRTH_DATE)?;
let sex = find(Self::TAG_SEX)?;
if let Some(table) = offsets {
let starts: Vec<usize> = fields.iter().map(|(_, _, s)| *s).collect();
check_offsets(raw, table, &starts)?;
}
Ok(Attributes {
name: decode_text(name, "氏名")?,
address: decode_text(address, "住所")?,
birth_date: Date::parse(birth_date)?,
sex: Sex::from_byte(*sex.first().ok_or_else(|| malformed("性別 is empty"))?),
})
}
pub fn digest_source(raw: &[u8]) -> Result<&[u8]> {
let outer = ber::parse(raw)?;
if outer.tag != Self::TAG {
return Err(malformed(&format!(
"expected tag FF20, got {:04X}",
outer.tag
)));
}
let table = ber::parse_header(outer.value)?;
if table.tag != Self::TAG_OFFSETS {
return Err(malformed("the offset table is not the first object"));
}
outer
.value
.get(table.total_len()..)
.ok_or_else(|| malformed("nothing follows the offset table"))
}
pub fn split_name(&self) -> Option<(&str, &str)> {
self.name.split_once('\u{3000}')
}
}
fn decode_text(bytes: &[u8], what: &str) -> Result<String> {
String::from_utf8(bytes.to_vec()).map_err(|_| malformed(&format!("{what} is not valid UTF-8")))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::transport::mock::MockTransport;
const MY_NUMBER_FILE: &[u8] = &[
0xFF, 0x10, 0x0C, b'5', b'3', b'7', b'6', b'8', b'6', b'6', b'7', b'7', b'1', b'8', b'8',
];
fn attributes_file() -> Vec<u8> {
let hex = "ff2065df2108000e002000590064df22\
0fe9bb92e6a190e38080e5b9b9e4b99f\
df2336e69db1e4baace983bde6b885e7\
80ace5b882e8a6b3e5b883e5ad90e58d\
97efbc91efbc92efbc8defbc97efbc8d\
efbc92efbc90efbc92df240831393830\
30323137df250131";
(0..hex.len() / 2)
.map(|i| u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16).unwrap())
.collect()
}
#[test]
fn parses_the_my_number_file() {
let n = parse_my_number(MY_NUMBER_FILE).unwrap();
assert_eq!(n.as_str(), "537686677188");
}
#[test]
fn rejects_a_my_number_file_with_the_wrong_tag() {
let mut bad = MY_NUMBER_FILE.to_vec();
bad[1] = 0x11;
assert!(parse_my_number(&bad).is_err());
}
#[test]
fn parses_the_basic_four_attributes() {
let a = Attributes::parse(&attributes_file()).unwrap();
assert_eq!(a.name, "黒桐 幹也");
assert_eq!(a.address, "東京都清瀬市観布子南12-7-202");
assert_eq!(
a.birth_date,
Date {
year: 1980,
month: 2,
day: 17
}
);
assert_eq!(a.sex, Sex::Male);
assert_eq!(a.split_name(), Some(("黒桐", "幹也")));
}
#[test]
fn checks_the_offset_table_rather_than_trusting_it() {
let mut file = attributes_file();
assert_eq!(&file[6..8], &[0x00, 0x0E]);
file[7] = 0x0F;
let err = Attributes::parse(&file).unwrap_err();
assert!(format!("{err}").contains("offset 0"), "{err}");
}
#[test]
fn reads_and_parses_over_a_transport() {
let mut ok = attributes_file();
ok.extend_from_slice(&[0x90, 0x00]);
let mut card = Card::new(MockTransport::new([
vec![0x90, 0x00], vec![0x90, 0x00], ok, ]));
let mut text = TextAp::select(&mut card).unwrap();
assert_eq!(text.read_attributes().unwrap().sex, Sex::Male);
}
#[test]
fn changing_the_pin_selects_verifies_and_replaces() {
let mut card = Card::new(MockTransport::new([
vec![0x90, 0x00], vec![0x90, 0x00], vec![0x90, 0x00], vec![0x90, 0x00], ]));
let mut text = TextAp::select(&mut card).unwrap();
text.change_pin(
&Pin::numeric("1234").unwrap(),
&Pin::numeric("5678").unwrap(),
)
.unwrap();
assert_eq!(
card.transport().sent[1],
[0x00, 0xA4, 0x02, 0x0C, 0x02, 0x00, 0x11]
);
assert_eq!(
card.transport().sent[2],
[0x00, 0x20, 0x00, 0x80, 0x04, b'1', b'2', b'3', b'4']
);
assert_eq!(
card.transport().sent[3],
[0x00, 0x24, 0x01, 0x80, 0x04, b'5', b'6', b'7', b'8']
);
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ApBasicData {
pub identification: ApIdentification,
pub public_key_id: KeyId,
pub trailing: Vec<u8>,
}
impl ApBasicData {
pub const TAG: u32 = 0xFF40;
pub fn digest(&self) -> Option<&[u8]> {
let (head, filler) = self.trailing.split_at_checked(32)?;
filler.iter().all(|b| *b == 0xFF).then_some(head)
}
pub fn parse(raw: &[u8]) -> Result<Self> {
let f = TlvFields::parse(raw, Self::TAG, None)?;
Ok(ApBasicData {
identification: ApIdentification::parse(f.get(0xDF41)?)?,
public_key_id: KeyId::parse(f.get(0xDF42)?)?,
trailing: f.get(0xDF43)?.to_vec(),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IntegrityRecord {
pub my_number_digest: [u8; 32],
pub attributes_digest: [u8; 32],
pub signature: Vec<u8>,
pub signed_data: Vec<u8>,
}
impl IntegrityRecord {
pub const TAG: u32 = 0xFF30;
pub fn parse(raw: &[u8]) -> Result<Self> {
let f = TlvFields::parse(raw, Self::TAG, None)?;
let digest = |tag: u32| -> Result<[u8; 32]> {
<[u8; 32]>::try_from(f.get(tag)?)
.map_err(|_| malformed(&format!("{tag:04X} is not a 32 byte digest")))
};
Ok(IntegrityRecord {
my_number_digest: digest(0xDF31)?,
attributes_digest: digest(0xDF32)?,
signature: f.get(0xDF33)?.to_vec(),
signed_data: f.bytes_before(0xDF33)?.to_vec(),
})
}
#[cfg(feature = "verify")]
pub fn matches_attributes_file(&self, attributes: &[u8]) -> Result<bool> {
let source = Attributes::digest_source(attributes)?;
Ok(crate::data::sha256(source) == self.attributes_digest)
}
#[cfg(feature = "verify")]
pub fn matches_my_number_file(&self, physical: &[u8]) -> bool {
crate::data::sha256(physical) == self.my_number_digest
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SessionKeyPublicKey {
pub public_key: RsaPublicKey,
}
impl SessionKeyPublicKey {
pub const TAG: u32 = 0xA1;
pub fn parse(raw: &[u8]) -> Result<Self> {
let outer = ber::parse(raw)?;
if outer.tag != Self::TAG {
return Err(malformed(&format!(
"expected tag A1, got {:04X}",
outer.tag
)));
}
Ok(SessionKeyPublicKey {
public_key: RsaPublicKey::parse(outer.value)?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SignedPublicKey {
pub public_key: RsaPublicKey,
pub signature: Vec<u8>,
pub signed_data: Vec<u8>,
}
impl SignedPublicKey {
pub const TAG: u32 = 0xFF50;
pub fn parse(raw: &[u8]) -> Result<Self> {
let f = TlvFields::parse(raw, Self::TAG, None)?;
Ok(SignedPublicKey {
public_key: RsaPublicKey::parse(f.get(0xDF51)?)?,
signature: f.get(0xDF52)?.to_vec(),
signed_data: f.bytes_before(0xDF52)?.to_vec(),
})
}
}
#[cfg(feature = "verify")]
mod verify {
use super::{IntegrityRecord, SignedPublicKey};
use crate::data::RsaPublicKey;
use crate::error::Result;
impl IntegrityRecord {
pub fn verify(&self, issuer: &RsaPublicKey) -> Result<()> {
issuer.verify_pkcs1_sha256(&self.signed_data, &self.signature)
}
}
impl SignedPublicKey {
pub fn verify(&self, issuer: &RsaPublicKey) -> Result<()> {
issuer.verify_pkcs1_sha256(&self.signed_data, &self.signature)
}
}
}