myna_card/mf.rs
1//! Files under the master file that JICSAP itself specifies.
2//!
3//! Unlike the application files, whose layouts had to be reverse engineered, these three are
4//! fully described by the specification, so they can be parsed rather than handed back raw:
5//!
6//! | EF | Contents | Reference |
7//! |---|---|---|
8//! | `001E` | card identifier | Annex B |
9//! | `2F10` | application folder list | Annex D |
10//! | `2F11` | IC manufacturer ID | Annex F |
11//!
12//! All three are record structured, so their records are simple encoded TLV
13//! ([`crate::tlv::simple`]), not BER.
14//!
15//! # None of this works on the Individual Number Card
16//!
17//! Annex D and Annex F say these files *should* exist, not that they must, and a sweep of a real
18//! card found that none of them do:
19//!
20//! - `2F10` and `2F11` answer 6A82, "no file to be accessed".
21//! - `001E` can be selected, but both READ BINARY and READ RECORD answer 6981, "command
22//! conflicting the file structure" — it is an internal EF, not the card identifier.
23//! - Sweeping every identifier `0001`-`001E` immediately after a cold reset finds nothing at all,
24//! so the MF holds no elementary file with a short identifier.
25//!
26//! The ISO MF cannot be re-selected: `00 A4 00 00` answers 6A86 after a reset and 9000 — *without
27//! changing the current DF* — once an application is selected, and 3F00 answers 6A82. The same
28//! observable power-on state is nevertheless reachable through GlobalPlatform: selecting the
29//! Issuer Security Domain at its default AID, `A0000001510000`, restores the MF-level GET DATA
30//! objects. [`MasterFile::select`] performs that selection; [`MasterFile::new`] remains available
31//! when the card is already freshly reset.
32//!
33//! The module is kept because it is what JICSAP specifies, and other cards built to the same
34//! specification do carry these files.
35//!
36//! # What is there instead
37//!
38//! The MF level is not empty — it is just not reachable through files. GET DATA answers there,
39//! and only there, for a set of objects that includes the card's contact-interface ATR, its
40//! identification number, the issuing municipality and expiry date, and a chain of
41//! card-verifiable certificates. See [`tag`] and [`MasterFile::data_object`].
42
43use crate::card::Card;
44use crate::data::CardVerifiableCertificate;
45use crate::error::{Error, Result};
46use crate::tlv::simple;
47use crate::transport::Transmit;
48
49/// Identifiers of the EFs under the master file.
50pub mod ef {
51 /// Card identifier (JICSAP 4.2 (2) reserves this identifier for it; see Annex B).
52 pub const CARD_IDENTIFIER: u16 = 0x001E;
53 /// Application folder list file (Annex D). Also present under each DF.
54 pub const APPLICATION_FOLDER_LIST: u16 = 0x2F10;
55 /// IC manufacturer ID file (Annex F).
56 pub const IC_MANUFACTURER_ID: u16 = 0x2F11;
57}
58
59/// Tags of the data objects GET DATA answers for with the master file current.
60///
61/// Every one of these was found by sweeping P1-P2; the card publishes no index. Which are present
62/// varies between cards, so treat a 6A88 as an ordinary answer rather than a fault.
63pub mod tag {
64 /// Issuer identification number, a 16 byte key reference.
65 pub const ISSUER_IDENTIFICATION: u16 = 0x0042;
66 /// Card identification number, ASCII.
67 pub const CARD_IDENTIFICATION: u16 = 0x0045;
68 /// Card recognition data: GlobalPlatform's, under the arc 1.2.840.114283.
69 pub const CARD_RECOGNITION: u16 = 0x0066;
70 /// 全国地方公共団体コード of the issuing municipality, five ASCII digits.
71 pub const MUNICIPALITY_CODE: u16 = 0x00F0;
72 /// Expiry date, eight ASCII digits.
73 pub const EXPIRY: u16 = 0x00F2;
74 /// 証明者鍵ID of the intermediate that signs [`CHAIN_LOWER`].
75 pub const INTERMEDIATE_KEY_ID: u16 = 0x00F7;
76 /// A card-verifiable certificate: the root certifying the intermediate.
77 pub const CHAIN_UPPER: u16 = 0x00F8;
78 /// A card-verifiable certificate: the intermediate certifying the key below it. Absent on
79 /// some cards.
80 pub const CHAIN_LOWER: u16 = 0x7F21;
81 /// The contact interface ATR, without its initial `TS`. Answers with an application current
82 /// too, and on some cards *only* then, so it lives on [`Card::contact_atr`](crate::card::Card::contact_atr).
83 pub const CONTACT_ATR: u16 = 0x5F51;
84}
85
86/// The master file, selected on a card.
87#[derive(Debug)]
88pub struct MasterFile<'a, T> {
89 card: &'a mut Card<T>,
90}
91
92impl<'a, T: Transmit> MasterFile<'a, T> {
93 /// Select the GlobalPlatform Issuer Security Domain and work with the power-on card-manager
94 /// state.
95 ///
96 /// The Individual Number Card does not implement a trustworthy ISO SELECT MF command, but its
97 /// default Issuer Security Domain AID is selectable. On the surveyed card this restores the
98 /// same GET DATA objects as a cold reset, even after an application DF was current.
99 pub fn select(card: &'a mut Card<T>) -> Result<Self> {
100 card.select_df(&crate::ap::DEFAULT_DF)?;
101 Ok(MasterFile { card })
102 }
103
104 /// Work with the master file as the current DF.
105 ///
106 /// This issues no SELECT. A card reset makes the card-manager state current on every logical
107 /// channel (JICSAP 4.5); [`MasterFile::select`] can restore it later through the
108 /// GlobalPlatform Issuer Security Domain AID.
109 ///
110 /// The caller is responsible for ensuring that state: reset the card, select the Issuer
111 /// Security Domain, or use this before selecting any application. If an application DF is
112 /// current instead, every read here silently comes from that application.
113 pub fn new(card: &'a mut Card<T>) -> Self {
114 MasterFile { card }
115 }
116
117 /// Borrow the underlying card, for operations this wrapper does not cover.
118 pub fn card(&mut self) -> &mut Card<T> {
119 self.card
120 }
121
122 /// Retrieve one of the MF level data objects; see [`tag`].
123 ///
124 /// This is GET DATA, not a file read, so it works even though the MF holds no readable EF.
125 pub fn data_object(&mut self, tag: u16) -> Result<Vec<u8>> {
126 self.card.get_data(tag)
127 }
128
129 /// The card-verifiable certificates at the MF level, root first.
130 ///
131 /// One or two, depending on the card: [`tag::CHAIN_UPPER`] is always there, and
132 /// [`tag::CHAIN_LOWER`] is missing on older cards. Consecutive entries chain — the second is
133 /// signed by the key the first certifies — and only the first needs a CA key from
134 /// [`crate::ca`], which is what makes the pair self-contained.
135 pub fn certificate_chain(&mut self) -> Result<Vec<CardVerifiableCertificate>> {
136 let mut chain = Vec::new();
137 for tag in [tag::CHAIN_UPPER, tag::CHAIN_LOWER] {
138 match self.data_object(tag) {
139 Ok(raw) => chain.push(CardVerifiableCertificate::parse(&raw)?),
140 // The card says it has no such object; that is an absence, not a failure.
141 Err(Error::Status(sw)) if matches!(sw.value(), 0x6A88 | 0x6A82) => break,
142 Err(err) => return Err(err),
143 }
144 }
145 Ok(chain)
146 }
147
148 /// Read the card identifier (Annex B).
149 pub fn card_identifier(&mut self) -> Result<CardIdentifier> {
150 let raw = self.read_all_records(ef::CARD_IDENTIFIER)?;
151 CardIdentifier::parse(&raw)
152 }
153
154 /// Read the application folder list of the master file (Annex D).
155 pub fn application_folders(&mut self) -> Result<ApplicationFolders> {
156 let raw = self.read_all_records(ef::APPLICATION_FOLDER_LIST)?;
157 ApplicationFolders::parse(&raw)
158 }
159
160 /// Read the IC manufacturer ID file (Annex F).
161 pub fn ic_manufacturer_id(&mut self) -> Result<IcManufacturerId> {
162 let raw = self.read_all_records(ef::IC_MANUFACTURER_ID)?;
163 IcManufacturerId::parse(&raw)
164 }
165
166 /// Read every record of a record structured EF, concatenated.
167 ///
168 /// Tries the multi-record form of READ RECORD(S) first and falls back to reading one record
169 /// at a time, since JICSAP 6.4.4 lets a card answer 6A81 to the multi-record form.
170 pub fn read_all_records(&mut self, id: u16) -> Result<Vec<u8>> {
171 self.card.select_ef(id)?;
172 match self.card.read_records_from(1) {
173 Ok(data) => Ok(data),
174 Err(Error::Status(sw)) if sw.value() == 0x6A81 => self.read_records_one_by_one(),
175 Err(err) => Err(err),
176 }
177 }
178
179 fn read_records_one_by_one(&mut self) -> Result<Vec<u8>> {
180 let mut out = Vec::new();
181 for record in 1..=u8::MAX {
182 match self.card.read_record(record) {
183 Ok(data) if data.is_empty() => break,
184 Ok(data) => out.extend_from_slice(&data),
185 // 6A83: no such record — we have reached the end of the file.
186 Err(Error::Status(sw)) if sw.value() == 0x6A83 => break,
187 Err(err) => return Err(err),
188 }
189 }
190 Ok(out)
191 }
192}
193
194/// The card identifier of JICSAP Annex B.
195///
196/// Set by the card manufacturer before the card reaches the issuer, and not rewritable.
197#[derive(Debug, Clone, PartialEq, Eq)]
198pub struct CardIdentifier {
199 /// Which manufacturer's issuance library the card expects. Administered by JICSAP.
200 pub manufacturer: u8,
201 /// Which encryption algorithms the card implements.
202 pub algorithms: Algorithms,
203 /// Which JICSAP specification version the card implements.
204 pub version: SpecVersion,
205 /// Which optional functions the card implements. Absent if the record is missing, though
206 /// Annex B calls it mandatory.
207 pub optional_functions: Option<OptionalFunctions>,
208 /// Manufacturer's proprietary information, 1 to 5 bytes. Optional.
209 pub proprietary: Option<Vec<u8>>,
210}
211
212impl CardIdentifier {
213 /// Tag of the manufacturer specific information record.
214 pub const TAG_MANUFACTURER: u8 = 0x00;
215 /// Tag of the optional function information record.
216 pub const TAG_OPTIONAL_FUNCTIONS: u8 = 0x01;
217 /// Tag of the manufacturer's proprietary information record.
218 pub const TAG_PROPRIETARY: u8 = 0x02;
219
220 /// Parse the concatenated records of EF `001E`.
221 pub fn parse(records: &[u8]) -> Result<Self> {
222 let manufacturer_record = simple::find(records, Self::TAG_MANUFACTURER)?
223 .ok_or_else(|| malformed("card identifier has no manufacturer record (tag 00)"))?;
224 let [manufacturer, algorithms, version] = <[u8; 3]>::try_from(manufacturer_record)
225 .map_err(|_| {
226 malformed(&format!(
227 "manufacturer record must be 3 bytes, got {}",
228 manufacturer_record.len()
229 ))
230 })?;
231
232 let optional_functions = simple::find(records, Self::TAG_OPTIONAL_FUNCTIONS)?
233 .and_then(|v| v.first().copied())
234 .map(OptionalFunctions);
235
236 Ok(CardIdentifier {
237 manufacturer,
238 algorithms: Algorithms(algorithms),
239 version: SpecVersion(version),
240 optional_functions,
241 proprietary: simple::find(records, Self::TAG_PROPRIETARY)?.map(<[u8]>::to_vec),
242 })
243 }
244}
245
246/// The encryption algorithm identifier of JICSAP Table B-1.
247#[derive(Debug, Clone, Copy, PartialEq, Eq)]
248pub struct Algorithms(pub u8);
249
250impl Algorithms {
251 /// b1 — DES.
252 pub const fn des(self) -> bool {
253 self.0 & 0x01 != 0
254 }
255 /// b2 — RSA.
256 pub const fn rsa(self) -> bool {
257 self.0 & 0x02 != 0
258 }
259 /// b3 — FEAL.
260 pub const fn feal(self) -> bool {
261 self.0 & 0x04 != 0
262 }
263 /// b4 — Triple DES.
264 pub const fn triple_des(self) -> bool {
265 self.0 & 0x08 != 0
266 }
267}
268
269/// The optional function information of JICSAP Table B-3.
270#[derive(Debug, Clone, Copy, PartialEq, Eq)]
271pub struct OptionalFunctions(pub u8);
272
273impl OptionalFunctions {
274 /// b1 — DF deletion.
275 pub const fn delete_df(self) -> bool {
276 self.0 & 0x01 != 0
277 }
278 /// b2 — IEF creation checking.
279 pub const fn check_ief_creation(self) -> bool {
280 self.0 & 0x02 != 0
281 }
282 /// b3 — unused DF memory size check.
283 pub const fn unused_df_memory_size_check(self) -> bool {
284 self.0 & 0x04 != 0
285 }
286 /// b4 — secure messaging, confidentiality.
287 pub const fn secure_messaging_confidentiality(self) -> bool {
288 self.0 & 0x08 != 0
289 }
290 /// b5 — secure messaging, integrity.
291 pub const fn secure_messaging_integrity(self) -> bool {
292 self.0 & 0x10 != 0
293 }
294 /// b6 — secure messaging, confidentiality and integrity together.
295 pub const fn secure_messaging_both(self) -> bool {
296 self.0 & 0x20 != 0
297 }
298 /// b7 — ECB mode for secure messaging confidentiality.
299 pub const fn ecb_mode(self) -> bool {
300 self.0 & 0x40 != 0
301 }
302 /// b8 — CBC mode for secure messaging confidentiality.
303 pub const fn cbc_mode(self) -> bool {
304 self.0 & 0x80 != 0
305 }
306}
307
308/// The specification version identifier of JICSAP Table B-2.
309#[derive(Debug, Clone, Copy, PartialEq, Eq)]
310pub struct SpecVersion(pub u8);
311
312impl SpecVersion {
313 /// The version as a string, for the two values Table B-2 assigns.
314 pub const fn name(self) -> Option<&'static str> {
315 match self.0 {
316 0x01 => Some("1.0"),
317 0x02 => Some("1.1"),
318 _ => None,
319 }
320 }
321}
322
323/// The application folder list file of JICSAP Annex D.
324#[derive(Debug, Clone, PartialEq, Eq, Default)]
325pub struct ApplicationFolders {
326 /// The name of the DF this file lives in. Empty under the MF, which has no DF name.
327 pub own_name: Vec<u8>,
328 /// The names of the DFs directly below it.
329 pub children: Vec<Vec<u8>>,
330}
331
332impl ApplicationFolders {
333 /// Tag of the record naming the DF this file lives in.
334 pub const TAG_SELF: u8 = 0x01;
335 /// Tag of a record naming a subordinate DF.
336 pub const TAG_CHILD: u8 = 0x02;
337 /// Tag marking a record as invalidated, reusable when a DF is issued later.
338 pub const TAG_INVALID: u8 = 0xFE;
339
340 /// Parse the concatenated records of an EF `2F10`.
341 ///
342 /// Records tagged `FE` are skipped: Annex D uses that tag for a slot that is reserved but
343 /// carries no DF, and 4.2.3 of the issuance library specification has the issuer write one
344 /// into every free record.
345 pub fn parse(records: &[u8]) -> Result<Self> {
346 let mut folders = ApplicationFolders::default();
347 for tlv in simple::iter(records) {
348 let tlv = tlv?;
349 match tlv.tag {
350 Self::TAG_SELF => folders.own_name = tlv.value.to_vec(),
351 Self::TAG_CHILD => folders.children.push(tlv.value.to_vec()),
352 Self::TAG_INVALID => {}
353 // 4.4.1 (1): a record whose tag is '00' has no tag, so it holds nothing.
354 simple::TAG_UNUSED => {}
355 other => {
356 return Err(malformed(&format!(
357 "unexpected tag {other:02X} in an application folder list"
358 )));
359 }
360 }
361 }
362 Ok(folders)
363 }
364}
365
366/// The IC manufacturer ID file of JICSAP Annex F.
367#[derive(Debug, Clone, PartialEq, Eq)]
368pub struct IcManufacturerId {
369 /// Embedder / IC assembler identifier, five alphanumeric bytes in the form `CCEEA`: an
370 /// ISO 3166 country code, the manufacturer identifier as two ASCII hex digits, and a field
371 /// that is a space when unused.
372 pub embedder: [u8; 5],
373 /// IC manufacturer identifier.
374 pub ic_manufacturer: u8,
375 /// Manufacturer's IC type identifier.
376 pub ic_type: u16,
377}
378
379impl IcManufacturerId {
380 /// Tag of the embedder / IC assembler identifier record.
381 pub const TAG_EMBEDDER: u8 = 0x45;
382 /// Tag of the IC manufacturer and IC type record.
383 pub const TAG_MANUFACTURER: u8 = 0x46;
384
385 /// Parse the concatenated records of EF `2F11`.
386 pub fn parse(records: &[u8]) -> Result<Self> {
387 let embedder = simple::find(records, Self::TAG_EMBEDDER)?
388 .ok_or_else(|| malformed("no embedder record (tag 45)"))?;
389 let embedder = <[u8; 5]>::try_from(embedder).map_err(|_| {
390 malformed(&format!(
391 "embedder record must be 5 bytes, got {}",
392 embedder.len()
393 ))
394 })?;
395
396 let manufacturer = simple::find(records, Self::TAG_MANUFACTURER)?
397 .ok_or_else(|| malformed("no IC manufacturer record (tag 46)"))?;
398 let [ic_manufacturer, type_hi, type_lo] =
399 <[u8; 3]>::try_from(manufacturer).map_err(|_| {
400 malformed(&format!(
401 "IC manufacturer record must be 3 bytes, got {}",
402 manufacturer.len()
403 ))
404 })?;
405
406 Ok(IcManufacturerId {
407 embedder,
408 ic_manufacturer,
409 ic_type: u16::from_be_bytes([type_hi, type_lo]),
410 })
411 }
412
413 /// The country code from the first two bytes of the embedder identifier.
414 pub fn country(&self) -> Option<&str> {
415 std::str::from_utf8(&self.embedder[..2]).ok()
416 }
417}
418
419fn malformed(what: &str) -> Error {
420 Error::Malformed(what.to_owned())
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426 use crate::transport::mock::MockTransport;
427
428 #[test]
429 fn parses_a_card_identifier() {
430 // Annex B: manufacturer record, optional function record, proprietary record.
431 let records = [
432 0x00, 0x03, 0x07, 0x0A, 0x02, // manufacturer 07, RSA + Triple DES, version 1.1
433 0x01, 0x01, 0x05, // delete DF + unused DF memory size check
434 0x02, 0x02, 0xDE, 0xAD,
435 ];
436 let id = CardIdentifier::parse(&records).unwrap();
437 assert_eq!(id.manufacturer, 0x07);
438 assert!(id.algorithms.rsa() && id.algorithms.triple_des());
439 assert!(!id.algorithms.des() && !id.algorithms.feal());
440 assert_eq!(id.version.name(), Some("1.1"));
441
442 let options = id.optional_functions.unwrap();
443 assert!(options.delete_df() && options.unused_df_memory_size_check());
444 assert!(!options.check_ief_creation() && !options.secure_messaging_confidentiality());
445 assert_eq!(id.proprietary.as_deref(), Some(&[0xDE, 0xAD][..]));
446 }
447
448 #[test]
449 fn card_identifier_needs_the_mandatory_record() {
450 assert!(CardIdentifier::parse(&[0x01, 0x01, 0x00]).is_err());
451 assert!(CardIdentifier::parse(&[0x00, 0x02, 0x07, 0x0A]).is_err());
452 }
453
454 #[test]
455 fn parses_an_application_folder_list() {
456 // The MF's list: no name of its own, two child DFs, then invalidated free records.
457 let records = [
458 0x01, 0x00, //
459 0x02, 0x02, 0x11, 0x22, //
460 0x02, 0x03, 0x33, 0x44, 0x55, //
461 0xFE, 0x02, 0x00, 0x00,
462 ];
463 let folders = ApplicationFolders::parse(&records).unwrap();
464 assert!(folders.own_name.is_empty());
465 assert_eq!(folders.children, [vec![0x11, 0x22], vec![0x33, 0x44, 0x55]]);
466 }
467
468 #[test]
469 fn parses_an_ic_manufacturer_id() {
470 // 'JP' + manufacturer "07" + unused field, then manufacturer 07 and IC type 1234.
471 let records = [
472 0x45, 0x05, b'J', b'P', b'0', b'7', b' ', //
473 0x46, 0x03, 0x07, 0x12, 0x34,
474 ];
475 let id = IcManufacturerId::parse(&records).unwrap();
476 assert_eq!(id.country(), Some("JP"));
477 assert_eq!(id.ic_manufacturer, 0x07);
478 assert_eq!(id.ic_type, 0x1234);
479 }
480
481 #[test]
482 fn get_data_falls_back_to_an_extended_le() {
483 // The card refuses a short Le for the large objects rather than reporting the length, so
484 // the first attempt is spent finding that out.
485 let big = vec![0xAA; 300];
486 let mut card = Card::new(MockTransport::new([
487 vec![0x67, 0x00],
488 [big.clone(), vec![0x90, 0x00]].concat(),
489 ]));
490 let mut mf = MasterFile::new(&mut card);
491 assert_eq!(mf.data_object(tag::CHAIN_UPPER).unwrap(), big);
492 assert_eq!(
493 mf.card().transport().sent,
494 vec![
495 vec![0x00, 0xCA, 0x00, 0xF8, 0x00],
496 vec![0x00, 0xCA, 0x00, 0xF8, 0x00, 0x00, 0x00],
497 ]
498 );
499 }
500
501 #[test]
502 fn selects_the_default_issuer_security_domain() {
503 let mut card = Card::new(MockTransport::new([vec![0x90, 0x00]]));
504 let mut mf = MasterFile::select(&mut card).unwrap();
505 assert_eq!(
506 mf.card().transport().sent,
507 [vec![
508 0x00, 0xA4, 0x04, 0x0C, 0x07, 0xA0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00,
509 ]]
510 );
511 }
512
513 #[test]
514 fn get_data_uses_one_apdu_when_the_object_is_small() {
515 let mut card = Card::new(MockTransport::new([vec![
516 b'1', b'3', b'2', b'2', b'1', 0x90, 0x00,
517 ]]));
518 let mut mf = MasterFile::new(&mut card);
519 assert_eq!(mf.data_object(tag::MUNICIPALITY_CODE).unwrap(), b"13221");
520 assert_eq!(mf.card().transport().sent.len(), 1);
521 }
522
523 #[test]
524 fn a_missing_second_certificate_ends_the_chain_rather_than_failing() {
525 let cert = std::fs::read(format!(
526 "{}/tests/fixtures/mf-do-F8.bin",
527 env!("CARGO_MANIFEST_DIR")
528 ))
529 .unwrap();
530 let mut card = Card::new(MockTransport::new([
531 [cert.clone(), vec![0x90, 0x00]].concat(),
532 vec![0x6A, 0x88],
533 ]));
534 let chain = MasterFile::new(&mut card).certificate_chain().unwrap();
535 assert_eq!(chain.len(), 1);
536 assert_eq!(chain[0].issuer_key_id.number(), "6000020");
537 }
538
539 #[test]
540 fn reads_records_one_at_a_time_when_the_card_rejects_the_multi_record_form() {
541 // No SELECT here: `MasterFile::new` assumes the card-manager state is already current.
542 let mut card = Card::new(MockTransport::new([
543 vec![0x90, 0x00], // SELECT EF 001E
544 vec![0x6A, 0x81], // READ RECORD(S) 1..last: not provided
545 vec![0x00, 0x03, 0x07, 0x0A, 0x02, 0x90, 0x00], // record 1
546 vec![0x6A, 0x83], // record 2: none
547 ]));
548 let mut mf = MasterFile::new(&mut card);
549 let id = mf.card_identifier().unwrap();
550 assert_eq!(id.manufacturer, 0x07);
551
552 assert_eq!(
553 card.transport().sent[0],
554 [0x00, 0xA4, 0x02, 0x0C, 0x02, 0x00, 0x1E]
555 );
556 assert_eq!(card.transport().sent[1], [0x00, 0xB2, 0x01, 0x05, 0x00]);
557 assert_eq!(card.transport().sent[2], [0x00, 0xB2, 0x01, 0x04, 0x00]);
558 }
559}