Skip to main content

MasterFile

Struct MasterFile 

Source
pub struct MasterFile<'a, T> { /* private fields */ }
Expand description

The master file, selected on a card.

Implementations§

Source§

impl<'a, T: Transmit> MasterFile<'a, T>

Source

pub fn select(card: &'a mut Card<T>) -> Result<Self>

Select the GlobalPlatform Issuer Security Domain and work with the power-on card-manager state.

The Individual Number Card does not implement a trustworthy ISO SELECT MF command, but its default Issuer Security Domain AID is selectable. On the surveyed card this restores the same GET DATA objects as a cold reset, even after an application DF was current.

Source

pub fn new(card: &'a mut Card<T>) -> Self

Work with the master file as the current DF.

This issues no SELECT. A card reset makes the card-manager state current on every logical channel (JICSAP 4.5); MasterFile::select can restore it later through the GlobalPlatform Issuer Security Domain AID.

The caller is responsible for ensuring that state: reset the card, select the Issuer Security Domain, or use this before selecting any application. If an application DF is current instead, every read here silently comes from that application.

Examples found in repository?
examples/read_card.rs (line 58)
39fn main() -> Result<(), Box<dyn std::error::Error>> {
40    let mut args: HashMap<String, String> = HashMap::new();
41    let mut rest = std::env::args().skip(1);
42    while let (Some(k), Some(v)) = (rest.next(), rest.next()) {
43        args.insert(k.trim_start_matches("--").to_owned(), v);
44    }
45    let get = |k: &str| args.get(k).map(String::as_str);
46    let out = std::path::PathBuf::from(get("out").unwrap_or("."));
47    // Kept so the 券面事項確認AP can be checked against it: two files, one municipality code,
48    // and nothing signs either of them.
49    let municipality;
50
51    let mut card = pcsc::connect_any(Sharing::Shared)?;
52
53    // The master file level answers only while no application is selected, so it goes first — and
54    // a power cycle is what puts the card back in that state.
55    println!("== master file ==");
56    {
57        card.transport_mut().power_cycle()?;
58        let mut mf = MasterFile::new(&mut card);
59        println!(
60            "  card number  {}",
61            String::from_utf8_lossy(&mf.data_object(mf::tag::CARD_IDENTIFICATION)?)
62                .trim_matches(|c: char| !c.is_ascii_graphic())
63                .to_owned()
64        );
65        let chain = mf.certificate_chain()?;
66        for (index, cert) in chain.iter().enumerate() {
67            println!(
68                "  chain[{index}]     {} -> {}",
69                cert.issuer_key_id, cert.subject_key_id
70            );
71        }
72        // Only the root needs a key from the table; the rest chain off it.
73        println!(
74            "  chain        [{}]",
75            outcome(CardVerifiableCertificate::verify_chain(&chain))
76        );
77    }
78
79    println!("\n== 共通カードAP ==");
80    {
81        let mut common = CommonAp::select(&mut card)?;
82        // Answers here on some cards and at the master file level on others.
83        let atr = common.card().contact_atr()?;
84        println!(
85            "  contact ATR  {}",
86            atr.iter()
87                .map(|b| format!("{b:02X}"))
88                .collect::<Vec<_>>()
89                .join(" ")
90        );
91        let info = common.read_card_info()?;
92        println!("  serial       {}", info.serial);
93        println!(
94            "  municipality {} (prefecture {})",
95            info.municipality_code,
96            info.prefecture_code()
97        );
98        println!("  expires      {}", info.expiry);
99        municipality = info.municipality_code.clone();
100    }
101
102    println!("\n== 券面入力補助AP ==");
103    {
104        let mut text = TextAp::select(&mut card)?;
105        let cert = text.read_certificate()?;
106        println!(
107            "  certificate  被証明者鍵ID {}, issued under {}  [{}]",
108            cert.subject_key_id,
109            cert.issuer_key_id,
110            outcome(cert.verify())
111        );
112
113        // Free to read. The key it names is not the one the certificate above certifies, and
114        // what it is for is not established.
115        let basic = text.read_ap_basic_data()?;
116        println!(
117            "  AP basic     names key {}, {} B trailing",
118            basic.public_key_id,
119            basic.trailing.len()
120        );
121
122        if let Some(pin) = get("pin") {
123            text.verify_pin(&Pin::numeric(pin)?)?;
124
125            // Signed by the key the certificate certifies, and gated on the same credential.
126            let signed_key = text.read_signed_public_key()?;
127            println!(
128                "  signed key   {} bit  [{}]",
129                signed_key.public_key.bits(),
130                outcome(signed_key.verify(&cert.public_key))
131            );
132
133            println!("  個人番号     {}", text.read_my_number()?.as_str());
134            let a = text.read_attributes()?;
135            println!("  氏名         {}", a.name);
136            println!("  住所         {}", a.address);
137            println!(
138                "  生年月日     {}{}",
139                a.birth_date,
140                match a.birth_date.to_era() {
141                    Some((era, year)) => format!(" ({}{}年)", era.name(), year),
142                    None => String::new(),
143                }
144            );
145            println!("  性別         {:?}", a.sex);
146
147            // EF 0003 signs a digest of each of the two files above, so it ties them together.
148            let integrity = text.read_integrity_record()?;
149            let my_number_file = text.read_my_number_file()?;
150            let attributes_file = text.read_ef(myna_card::ap::text::ef::ATTRIBUTES)?;
151            println!(
152                "  integrity    signature [{}], 個人番号 digest [{}], 基本4情報 digest [{}]",
153                outcome(integrity.verify(&cert.public_key)),
154                if integrity.matches_my_number_file(&my_number_file) {
155                    "ok"
156                } else {
157                    "MISMATCH"
158                },
159                match integrity.matches_attributes_file(&attributes_file) {
160                    Ok(true) => "ok",
161                    Ok(false) => "MISMATCH",
162                    Err(_) => "unreadable",
163                }
164            );
165        } else {
166            println!("  (pass --pin to read the 個人番号 and 基本4情報)");
167        }
168    }
169
170    println!("\n== 券面事項確認AP ==");
171    {
172        let mut surface = SurfaceAp::select(&mut card)?;
173        let cert = surface.read_certificate()?;
174        println!(
175            "  certificate  被証明者鍵ID {}, issued under {}  [{}]",
176            cert.subject_key_id,
177            cert.issuer_key_id,
178            outcome(cert.verify())
179        );
180        let issuer = &cert.public_key;
181
182        // Also free to read, and it repeats the municipality code. Neither copy is independently
183        // authenticated here, so the agreement is only a consistency check.
184        let basic = surface.read_ap_basic_data()?;
185        println!(
186            "  AP basic     municipality {}{}, DF35 key reference {}",
187            basic.municipality_code,
188            if municipality == basic.municipality_code {
189                " (agrees with 共通カードAP)"
190            } else {
191                " (DISAGREES with 共通カードAP)"
192            },
193            basic.encrypted_reference_number.key_id
194        );
195
196        if let Some(dob) = get("birth-date") {
197            surface.verify_birth_date(&Pin::numeric(dob)?)?;
198            // The age verification record: the one field this credential is meant to reveal.
199            let age = surface.read_age_record()?;
200            println!(
201                "  年齢確認     生年月日 {}  [{}]",
202                age.birth_date,
203                outcome(age.verify(issuer))
204            );
205        }
206        if let Some(code) = get("code-a").or_else(|| get("code-b")) {
207            let pin = Pin::numeric(code)?;
208            if get("code-a").is_some() {
209                surface.verify_code_a(&pin)?;
210            } else {
211                surface.verify_code_b(&pin)?;
212            }
213            let face = surface.read_card_face()?;
214            println!(
215                "  券面         生年月日 {}, 有効期限 {}, 性別 {:?}  [{}]",
216                face.birth_date,
217                face.expiry,
218                face.sex,
219                outcome(face.verify(issuer))
220            );
221            for (label, image) in [
222                ("name", &face.name_image),
223                ("address", &face.address_image),
224                ("photo", &face.photo),
225            ] {
226                let path = out.join(format!("{label}.{}", image.format.extension()));
227                std::fs::write(&path, &image.data)?;
228                println!(
229                    "  {label:<12} {:?}, {} bytes -> {}",
230                    image.format,
231                    image.data.len(),
232                    path.display()
233                );
234            }
235            // The record proves the data is authentic; a fresh signature proves the card that
236            // holds the matching private key is the one in the reader right now.
237            let challenge = surface.card().get_challenge(16)?;
238            let signature = surface.sign(&challenge)?;
239            println!(
240                "  challenge    16 bytes signed by the card key  [{}]",
241                outcome(SignatureScheme::Sha256DigestInfo.verify(
242                    &face.public_key,
243                    &challenge,
244                    &signature
245                ))
246            );
247
248            if get("code-a").is_some() {
249                let n = surface.read_my_number_image()?;
250                println!("  my-number    signature [{}]", outcome(n.verify(issuer)));
251                let path = out.join(format!("my-number.{}", n.image.format.extension()));
252                std::fs::write(&path, &n.image.data)?;
253                println!(
254                    "  my-number    {:?}, {} bytes -> {}",
255                    n.image.format,
256                    n.image.data.len(),
257                    path.display()
258                );
259            }
260        }
261    }
262
263    println!("\n== 公的個人認証AP ==");
264    {
265        let mut jpki = JpkiAp::select(&mut card)?;
266        println!("  token        {:?}", jpki.read_token_type()?);
267        for (label, der) in [
268            ("auth", jpki.read_auth_certificate_der()?),
269            ("auth-ca", jpki.read_auth_ca_certificate_der()?),
270        ] {
271            let path = out.join(format!("{label}.der"));
272            std::fs::write(&path, &der)?;
273            println!("  {label:<12} {} bytes -> {}", der.len(), path.display());
274        }
275        // Two checks that look alike and are not. The first ends at the CA certificate in EF
276        // 000B — the same card, so it says only that the card is internally consistent. The
277        // second ends at a root the crate carries, which the card had no say in.
278        let chain = [
279            jpki.read_auth_certificate()?,
280            jpki.read_auth_ca_certificate()?,
281        ];
282        let (issued, _) = chain[0].validity();
283        println!(
284            "  card's CA    auth <- auth-ca  [{}]",
285            outcome(Certificate::verify_chain(&chain, issued))
286        );
287        println!(
288            "  to a root    production only  [{}]",
289            outcome(chain[0].verify_to_root(issued, Accept::ProductionOnly))
290        );
291        // A test card reaches no published root; asking for the test hierarchy is how you say so
292        // out loud. Never do this where the answer decides whether to believe a cardholder.
293        println!(
294            "               test accepted   [{}]",
295            outcome(chain[0].verify_to_root(issued, Accept::ProductionAndTest))
296        );
297
298        // Reported, never guessed at: an empty VERIFY costs nothing.
299        for (label, r) in [
300            ("利用者証明用", jpki.auth_pin_retries()?),
301            ("署名用", jpki.sign_pin_retries()?),
302        ] {
303            println!(
304                "  {label} retries {}",
305                match r {
306                    Retries::Remaining(n) => n.to_string(),
307                    Retries::Blocked => "blocked".into(),
308                    Retries::Unlimited => "unlimited".into(),
309                    Retries::NotReported => "not reported".into(),
310                }
311            );
312        }
313    }
314
315    Ok(())
316}
Source

pub fn card(&mut self) -> &mut Card<T>

Borrow the underlying card, for operations this wrapper does not cover.

Source

pub fn data_object(&mut self, tag: u16) -> Result<Vec<u8>>

Retrieve one of the MF level data objects; see tag.

This is GET DATA, not a file read, so it works even though the MF holds no readable EF.

Examples found in repository?
examples/read_card.rs (line 61)
39fn main() -> Result<(), Box<dyn std::error::Error>> {
40    let mut args: HashMap<String, String> = HashMap::new();
41    let mut rest = std::env::args().skip(1);
42    while let (Some(k), Some(v)) = (rest.next(), rest.next()) {
43        args.insert(k.trim_start_matches("--").to_owned(), v);
44    }
45    let get = |k: &str| args.get(k).map(String::as_str);
46    let out = std::path::PathBuf::from(get("out").unwrap_or("."));
47    // Kept so the 券面事項確認AP can be checked against it: two files, one municipality code,
48    // and nothing signs either of them.
49    let municipality;
50
51    let mut card = pcsc::connect_any(Sharing::Shared)?;
52
53    // The master file level answers only while no application is selected, so it goes first — and
54    // a power cycle is what puts the card back in that state.
55    println!("== master file ==");
56    {
57        card.transport_mut().power_cycle()?;
58        let mut mf = MasterFile::new(&mut card);
59        println!(
60            "  card number  {}",
61            String::from_utf8_lossy(&mf.data_object(mf::tag::CARD_IDENTIFICATION)?)
62                .trim_matches(|c: char| !c.is_ascii_graphic())
63                .to_owned()
64        );
65        let chain = mf.certificate_chain()?;
66        for (index, cert) in chain.iter().enumerate() {
67            println!(
68                "  chain[{index}]     {} -> {}",
69                cert.issuer_key_id, cert.subject_key_id
70            );
71        }
72        // Only the root needs a key from the table; the rest chain off it.
73        println!(
74            "  chain        [{}]",
75            outcome(CardVerifiableCertificate::verify_chain(&chain))
76        );
77    }
78
79    println!("\n== 共通カードAP ==");
80    {
81        let mut common = CommonAp::select(&mut card)?;
82        // Answers here on some cards and at the master file level on others.
83        let atr = common.card().contact_atr()?;
84        println!(
85            "  contact ATR  {}",
86            atr.iter()
87                .map(|b| format!("{b:02X}"))
88                .collect::<Vec<_>>()
89                .join(" ")
90        );
91        let info = common.read_card_info()?;
92        println!("  serial       {}", info.serial);
93        println!(
94            "  municipality {} (prefecture {})",
95            info.municipality_code,
96            info.prefecture_code()
97        );
98        println!("  expires      {}", info.expiry);
99        municipality = info.municipality_code.clone();
100    }
101
102    println!("\n== 券面入力補助AP ==");
103    {
104        let mut text = TextAp::select(&mut card)?;
105        let cert = text.read_certificate()?;
106        println!(
107            "  certificate  被証明者鍵ID {}, issued under {}  [{}]",
108            cert.subject_key_id,
109            cert.issuer_key_id,
110            outcome(cert.verify())
111        );
112
113        // Free to read. The key it names is not the one the certificate above certifies, and
114        // what it is for is not established.
115        let basic = text.read_ap_basic_data()?;
116        println!(
117            "  AP basic     names key {}, {} B trailing",
118            basic.public_key_id,
119            basic.trailing.len()
120        );
121
122        if let Some(pin) = get("pin") {
123            text.verify_pin(&Pin::numeric(pin)?)?;
124
125            // Signed by the key the certificate certifies, and gated on the same credential.
126            let signed_key = text.read_signed_public_key()?;
127            println!(
128                "  signed key   {} bit  [{}]",
129                signed_key.public_key.bits(),
130                outcome(signed_key.verify(&cert.public_key))
131            );
132
133            println!("  個人番号     {}", text.read_my_number()?.as_str());
134            let a = text.read_attributes()?;
135            println!("  氏名         {}", a.name);
136            println!("  住所         {}", a.address);
137            println!(
138                "  生年月日     {}{}",
139                a.birth_date,
140                match a.birth_date.to_era() {
141                    Some((era, year)) => format!(" ({}{}年)", era.name(), year),
142                    None => String::new(),
143                }
144            );
145            println!("  性別         {:?}", a.sex);
146
147            // EF 0003 signs a digest of each of the two files above, so it ties them together.
148            let integrity = text.read_integrity_record()?;
149            let my_number_file = text.read_my_number_file()?;
150            let attributes_file = text.read_ef(myna_card::ap::text::ef::ATTRIBUTES)?;
151            println!(
152                "  integrity    signature [{}], 個人番号 digest [{}], 基本4情報 digest [{}]",
153                outcome(integrity.verify(&cert.public_key)),
154                if integrity.matches_my_number_file(&my_number_file) {
155                    "ok"
156                } else {
157                    "MISMATCH"
158                },
159                match integrity.matches_attributes_file(&attributes_file) {
160                    Ok(true) => "ok",
161                    Ok(false) => "MISMATCH",
162                    Err(_) => "unreadable",
163                }
164            );
165        } else {
166            println!("  (pass --pin to read the 個人番号 and 基本4情報)");
167        }
168    }
169
170    println!("\n== 券面事項確認AP ==");
171    {
172        let mut surface = SurfaceAp::select(&mut card)?;
173        let cert = surface.read_certificate()?;
174        println!(
175            "  certificate  被証明者鍵ID {}, issued under {}  [{}]",
176            cert.subject_key_id,
177            cert.issuer_key_id,
178            outcome(cert.verify())
179        );
180        let issuer = &cert.public_key;
181
182        // Also free to read, and it repeats the municipality code. Neither copy is independently
183        // authenticated here, so the agreement is only a consistency check.
184        let basic = surface.read_ap_basic_data()?;
185        println!(
186            "  AP basic     municipality {}{}, DF35 key reference {}",
187            basic.municipality_code,
188            if municipality == basic.municipality_code {
189                " (agrees with 共通カードAP)"
190            } else {
191                " (DISAGREES with 共通カードAP)"
192            },
193            basic.encrypted_reference_number.key_id
194        );
195
196        if let Some(dob) = get("birth-date") {
197            surface.verify_birth_date(&Pin::numeric(dob)?)?;
198            // The age verification record: the one field this credential is meant to reveal.
199            let age = surface.read_age_record()?;
200            println!(
201                "  年齢確認     生年月日 {}  [{}]",
202                age.birth_date,
203                outcome(age.verify(issuer))
204            );
205        }
206        if let Some(code) = get("code-a").or_else(|| get("code-b")) {
207            let pin = Pin::numeric(code)?;
208            if get("code-a").is_some() {
209                surface.verify_code_a(&pin)?;
210            } else {
211                surface.verify_code_b(&pin)?;
212            }
213            let face = surface.read_card_face()?;
214            println!(
215                "  券面         生年月日 {}, 有効期限 {}, 性別 {:?}  [{}]",
216                face.birth_date,
217                face.expiry,
218                face.sex,
219                outcome(face.verify(issuer))
220            );
221            for (label, image) in [
222                ("name", &face.name_image),
223                ("address", &face.address_image),
224                ("photo", &face.photo),
225            ] {
226                let path = out.join(format!("{label}.{}", image.format.extension()));
227                std::fs::write(&path, &image.data)?;
228                println!(
229                    "  {label:<12} {:?}, {} bytes -> {}",
230                    image.format,
231                    image.data.len(),
232                    path.display()
233                );
234            }
235            // The record proves the data is authentic; a fresh signature proves the card that
236            // holds the matching private key is the one in the reader right now.
237            let challenge = surface.card().get_challenge(16)?;
238            let signature = surface.sign(&challenge)?;
239            println!(
240                "  challenge    16 bytes signed by the card key  [{}]",
241                outcome(SignatureScheme::Sha256DigestInfo.verify(
242                    &face.public_key,
243                    &challenge,
244                    &signature
245                ))
246            );
247
248            if get("code-a").is_some() {
249                let n = surface.read_my_number_image()?;
250                println!("  my-number    signature [{}]", outcome(n.verify(issuer)));
251                let path = out.join(format!("my-number.{}", n.image.format.extension()));
252                std::fs::write(&path, &n.image.data)?;
253                println!(
254                    "  my-number    {:?}, {} bytes -> {}",
255                    n.image.format,
256                    n.image.data.len(),
257                    path.display()
258                );
259            }
260        }
261    }
262
263    println!("\n== 公的個人認証AP ==");
264    {
265        let mut jpki = JpkiAp::select(&mut card)?;
266        println!("  token        {:?}", jpki.read_token_type()?);
267        for (label, der) in [
268            ("auth", jpki.read_auth_certificate_der()?),
269            ("auth-ca", jpki.read_auth_ca_certificate_der()?),
270        ] {
271            let path = out.join(format!("{label}.der"));
272            std::fs::write(&path, &der)?;
273            println!("  {label:<12} {} bytes -> {}", der.len(), path.display());
274        }
275        // Two checks that look alike and are not. The first ends at the CA certificate in EF
276        // 000B — the same card, so it says only that the card is internally consistent. The
277        // second ends at a root the crate carries, which the card had no say in.
278        let chain = [
279            jpki.read_auth_certificate()?,
280            jpki.read_auth_ca_certificate()?,
281        ];
282        let (issued, _) = chain[0].validity();
283        println!(
284            "  card's CA    auth <- auth-ca  [{}]",
285            outcome(Certificate::verify_chain(&chain, issued))
286        );
287        println!(
288            "  to a root    production only  [{}]",
289            outcome(chain[0].verify_to_root(issued, Accept::ProductionOnly))
290        );
291        // A test card reaches no published root; asking for the test hierarchy is how you say so
292        // out loud. Never do this where the answer decides whether to believe a cardholder.
293        println!(
294            "               test accepted   [{}]",
295            outcome(chain[0].verify_to_root(issued, Accept::ProductionAndTest))
296        );
297
298        // Reported, never guessed at: an empty VERIFY costs nothing.
299        for (label, r) in [
300            ("利用者証明用", jpki.auth_pin_retries()?),
301            ("署名用", jpki.sign_pin_retries()?),
302        ] {
303            println!(
304                "  {label} retries {}",
305                match r {
306                    Retries::Remaining(n) => n.to_string(),
307                    Retries::Blocked => "blocked".into(),
308                    Retries::Unlimited => "unlimited".into(),
309                    Retries::NotReported => "not reported".into(),
310                }
311            );
312        }
313    }
314
315    Ok(())
316}
Source

pub fn certificate_chain(&mut self) -> Result<Vec<CardVerifiableCertificate>>

The card-verifiable certificates at the MF level, root first.

One or two, depending on the card: tag::CHAIN_UPPER is always there, and tag::CHAIN_LOWER is missing on older cards. Consecutive entries chain — the second is signed by the key the first certifies — and only the first needs a CA key from crate::ca, which is what makes the pair self-contained.

Examples found in repository?
examples/read_card.rs (line 65)
39fn main() -> Result<(), Box<dyn std::error::Error>> {
40    let mut args: HashMap<String, String> = HashMap::new();
41    let mut rest = std::env::args().skip(1);
42    while let (Some(k), Some(v)) = (rest.next(), rest.next()) {
43        args.insert(k.trim_start_matches("--").to_owned(), v);
44    }
45    let get = |k: &str| args.get(k).map(String::as_str);
46    let out = std::path::PathBuf::from(get("out").unwrap_or("."));
47    // Kept so the 券面事項確認AP can be checked against it: two files, one municipality code,
48    // and nothing signs either of them.
49    let municipality;
50
51    let mut card = pcsc::connect_any(Sharing::Shared)?;
52
53    // The master file level answers only while no application is selected, so it goes first — and
54    // a power cycle is what puts the card back in that state.
55    println!("== master file ==");
56    {
57        card.transport_mut().power_cycle()?;
58        let mut mf = MasterFile::new(&mut card);
59        println!(
60            "  card number  {}",
61            String::from_utf8_lossy(&mf.data_object(mf::tag::CARD_IDENTIFICATION)?)
62                .trim_matches(|c: char| !c.is_ascii_graphic())
63                .to_owned()
64        );
65        let chain = mf.certificate_chain()?;
66        for (index, cert) in chain.iter().enumerate() {
67            println!(
68                "  chain[{index}]     {} -> {}",
69                cert.issuer_key_id, cert.subject_key_id
70            );
71        }
72        // Only the root needs a key from the table; the rest chain off it.
73        println!(
74            "  chain        [{}]",
75            outcome(CardVerifiableCertificate::verify_chain(&chain))
76        );
77    }
78
79    println!("\n== 共通カードAP ==");
80    {
81        let mut common = CommonAp::select(&mut card)?;
82        // Answers here on some cards and at the master file level on others.
83        let atr = common.card().contact_atr()?;
84        println!(
85            "  contact ATR  {}",
86            atr.iter()
87                .map(|b| format!("{b:02X}"))
88                .collect::<Vec<_>>()
89                .join(" ")
90        );
91        let info = common.read_card_info()?;
92        println!("  serial       {}", info.serial);
93        println!(
94            "  municipality {} (prefecture {})",
95            info.municipality_code,
96            info.prefecture_code()
97        );
98        println!("  expires      {}", info.expiry);
99        municipality = info.municipality_code.clone();
100    }
101
102    println!("\n== 券面入力補助AP ==");
103    {
104        let mut text = TextAp::select(&mut card)?;
105        let cert = text.read_certificate()?;
106        println!(
107            "  certificate  被証明者鍵ID {}, issued under {}  [{}]",
108            cert.subject_key_id,
109            cert.issuer_key_id,
110            outcome(cert.verify())
111        );
112
113        // Free to read. The key it names is not the one the certificate above certifies, and
114        // what it is for is not established.
115        let basic = text.read_ap_basic_data()?;
116        println!(
117            "  AP basic     names key {}, {} B trailing",
118            basic.public_key_id,
119            basic.trailing.len()
120        );
121
122        if let Some(pin) = get("pin") {
123            text.verify_pin(&Pin::numeric(pin)?)?;
124
125            // Signed by the key the certificate certifies, and gated on the same credential.
126            let signed_key = text.read_signed_public_key()?;
127            println!(
128                "  signed key   {} bit  [{}]",
129                signed_key.public_key.bits(),
130                outcome(signed_key.verify(&cert.public_key))
131            );
132
133            println!("  個人番号     {}", text.read_my_number()?.as_str());
134            let a = text.read_attributes()?;
135            println!("  氏名         {}", a.name);
136            println!("  住所         {}", a.address);
137            println!(
138                "  生年月日     {}{}",
139                a.birth_date,
140                match a.birth_date.to_era() {
141                    Some((era, year)) => format!(" ({}{}年)", era.name(), year),
142                    None => String::new(),
143                }
144            );
145            println!("  性別         {:?}", a.sex);
146
147            // EF 0003 signs a digest of each of the two files above, so it ties them together.
148            let integrity = text.read_integrity_record()?;
149            let my_number_file = text.read_my_number_file()?;
150            let attributes_file = text.read_ef(myna_card::ap::text::ef::ATTRIBUTES)?;
151            println!(
152                "  integrity    signature [{}], 個人番号 digest [{}], 基本4情報 digest [{}]",
153                outcome(integrity.verify(&cert.public_key)),
154                if integrity.matches_my_number_file(&my_number_file) {
155                    "ok"
156                } else {
157                    "MISMATCH"
158                },
159                match integrity.matches_attributes_file(&attributes_file) {
160                    Ok(true) => "ok",
161                    Ok(false) => "MISMATCH",
162                    Err(_) => "unreadable",
163                }
164            );
165        } else {
166            println!("  (pass --pin to read the 個人番号 and 基本4情報)");
167        }
168    }
169
170    println!("\n== 券面事項確認AP ==");
171    {
172        let mut surface = SurfaceAp::select(&mut card)?;
173        let cert = surface.read_certificate()?;
174        println!(
175            "  certificate  被証明者鍵ID {}, issued under {}  [{}]",
176            cert.subject_key_id,
177            cert.issuer_key_id,
178            outcome(cert.verify())
179        );
180        let issuer = &cert.public_key;
181
182        // Also free to read, and it repeats the municipality code. Neither copy is independently
183        // authenticated here, so the agreement is only a consistency check.
184        let basic = surface.read_ap_basic_data()?;
185        println!(
186            "  AP basic     municipality {}{}, DF35 key reference {}",
187            basic.municipality_code,
188            if municipality == basic.municipality_code {
189                " (agrees with 共通カードAP)"
190            } else {
191                " (DISAGREES with 共通カードAP)"
192            },
193            basic.encrypted_reference_number.key_id
194        );
195
196        if let Some(dob) = get("birth-date") {
197            surface.verify_birth_date(&Pin::numeric(dob)?)?;
198            // The age verification record: the one field this credential is meant to reveal.
199            let age = surface.read_age_record()?;
200            println!(
201                "  年齢確認     生年月日 {}  [{}]",
202                age.birth_date,
203                outcome(age.verify(issuer))
204            );
205        }
206        if let Some(code) = get("code-a").or_else(|| get("code-b")) {
207            let pin = Pin::numeric(code)?;
208            if get("code-a").is_some() {
209                surface.verify_code_a(&pin)?;
210            } else {
211                surface.verify_code_b(&pin)?;
212            }
213            let face = surface.read_card_face()?;
214            println!(
215                "  券面         生年月日 {}, 有効期限 {}, 性別 {:?}  [{}]",
216                face.birth_date,
217                face.expiry,
218                face.sex,
219                outcome(face.verify(issuer))
220            );
221            for (label, image) in [
222                ("name", &face.name_image),
223                ("address", &face.address_image),
224                ("photo", &face.photo),
225            ] {
226                let path = out.join(format!("{label}.{}", image.format.extension()));
227                std::fs::write(&path, &image.data)?;
228                println!(
229                    "  {label:<12} {:?}, {} bytes -> {}",
230                    image.format,
231                    image.data.len(),
232                    path.display()
233                );
234            }
235            // The record proves the data is authentic; a fresh signature proves the card that
236            // holds the matching private key is the one in the reader right now.
237            let challenge = surface.card().get_challenge(16)?;
238            let signature = surface.sign(&challenge)?;
239            println!(
240                "  challenge    16 bytes signed by the card key  [{}]",
241                outcome(SignatureScheme::Sha256DigestInfo.verify(
242                    &face.public_key,
243                    &challenge,
244                    &signature
245                ))
246            );
247
248            if get("code-a").is_some() {
249                let n = surface.read_my_number_image()?;
250                println!("  my-number    signature [{}]", outcome(n.verify(issuer)));
251                let path = out.join(format!("my-number.{}", n.image.format.extension()));
252                std::fs::write(&path, &n.image.data)?;
253                println!(
254                    "  my-number    {:?}, {} bytes -> {}",
255                    n.image.format,
256                    n.image.data.len(),
257                    path.display()
258                );
259            }
260        }
261    }
262
263    println!("\n== 公的個人認証AP ==");
264    {
265        let mut jpki = JpkiAp::select(&mut card)?;
266        println!("  token        {:?}", jpki.read_token_type()?);
267        for (label, der) in [
268            ("auth", jpki.read_auth_certificate_der()?),
269            ("auth-ca", jpki.read_auth_ca_certificate_der()?),
270        ] {
271            let path = out.join(format!("{label}.der"));
272            std::fs::write(&path, &der)?;
273            println!("  {label:<12} {} bytes -> {}", der.len(), path.display());
274        }
275        // Two checks that look alike and are not. The first ends at the CA certificate in EF
276        // 000B — the same card, so it says only that the card is internally consistent. The
277        // second ends at a root the crate carries, which the card had no say in.
278        let chain = [
279            jpki.read_auth_certificate()?,
280            jpki.read_auth_ca_certificate()?,
281        ];
282        let (issued, _) = chain[0].validity();
283        println!(
284            "  card's CA    auth <- auth-ca  [{}]",
285            outcome(Certificate::verify_chain(&chain, issued))
286        );
287        println!(
288            "  to a root    production only  [{}]",
289            outcome(chain[0].verify_to_root(issued, Accept::ProductionOnly))
290        );
291        // A test card reaches no published root; asking for the test hierarchy is how you say so
292        // out loud. Never do this where the answer decides whether to believe a cardholder.
293        println!(
294            "               test accepted   [{}]",
295            outcome(chain[0].verify_to_root(issued, Accept::ProductionAndTest))
296        );
297
298        // Reported, never guessed at: an empty VERIFY costs nothing.
299        for (label, r) in [
300            ("利用者証明用", jpki.auth_pin_retries()?),
301            ("署名用", jpki.sign_pin_retries()?),
302        ] {
303            println!(
304                "  {label} retries {}",
305                match r {
306                    Retries::Remaining(n) => n.to_string(),
307                    Retries::Blocked => "blocked".into(),
308                    Retries::Unlimited => "unlimited".into(),
309                    Retries::NotReported => "not reported".into(),
310                }
311            );
312        }
313    }
314
315    Ok(())
316}
Source

pub fn card_identifier(&mut self) -> Result<CardIdentifier>

Read the card identifier (Annex B).

Source

pub fn application_folders(&mut self) -> Result<ApplicationFolders>

Read the application folder list of the master file (Annex D).

Source

pub fn ic_manufacturer_id(&mut self) -> Result<IcManufacturerId>

Read the IC manufacturer ID file (Annex F).

Source

pub fn read_all_records(&mut self, id: u16) -> Result<Vec<u8>>

Read every record of a record structured EF, concatenated.

Tries the multi-record form of READ RECORD(S) first and falls back to reading one record at a time, since JICSAP 6.4.4 lets a card answer 6A81 to the multi-record form.

Trait Implementations§

Source§

impl<'a, T: Debug> Debug for MasterFile<'a, T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'a, T> !UnwindSafe for MasterFile<'a, T>

§

impl<'a, T> Freeze for MasterFile<'a, T>

§

impl<'a, T> RefUnwindSafe for MasterFile<'a, T>
where T: RefUnwindSafe,

§

impl<'a, T> Send for MasterFile<'a, T>
where T: Send,

§

impl<'a, T> Sync for MasterFile<'a, T>
where T: Sync,

§

impl<'a, T> Unpin for MasterFile<'a, T>

§

impl<'a, T> UnsafeUnpin for MasterFile<'a, T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V