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>
impl<'a, T: Transmit> MasterFile<'a, T>
Sourcepub fn select(card: &'a mut Card<T>) -> Result<Self>
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.
Sourcepub fn new(card: &'a mut Card<T>) -> Self
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?
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}Sourcepub fn card(&mut self) -> &mut Card<T>
pub fn card(&mut self) -> &mut Card<T>
Borrow the underlying card, for operations this wrapper does not cover.
Sourcepub fn data_object(&mut self, tag: u16) -> Result<Vec<u8>>
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?
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}Sourcepub fn certificate_chain(&mut self) -> Result<Vec<CardVerifiableCertificate>>
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?
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}Sourcepub fn card_identifier(&mut self) -> Result<CardIdentifier>
pub fn card_identifier(&mut self) -> Result<CardIdentifier>
Read the card identifier (Annex B).
Sourcepub fn application_folders(&mut self) -> Result<ApplicationFolders>
pub fn application_folders(&mut self) -> Result<ApplicationFolders>
Read the application folder list of the master file (Annex D).
Sourcepub fn ic_manufacturer_id(&mut self) -> Result<IcManufacturerId>
pub fn ic_manufacturer_id(&mut self) -> Result<IcManufacturerId>
Read the IC manufacturer ID file (Annex F).