Skip to main content

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}