1use std::collections::HashMap;
21
22use myna_card::Certificate;
23use myna_card::ap::jpki::SignatureScheme;
24use myna_card::ap::{common::CommonAp, jpki::JpkiAp, surface::SurfaceAp, text::TextAp};
25use myna_card::certificate::roots::Accept;
26use myna_card::data::CardVerifiableCertificate;
27use myna_card::mf::{self, MasterFile};
28use myna_card::transport::pcsc::Sharing;
29use myna_card::{Pin, Retries, transport::pcsc};
30
31fn outcome(result: Result<(), myna_card::Error>) -> String {
33 match result {
34 Ok(()) => "verified".to_owned(),
35 Err(err) => format!("{err}"),
36 }
37}
38
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 let municipality;
50
51 let mut card = pcsc::connect_any(Sharing::Shared)?;
52
53 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 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 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 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 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 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 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 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 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 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 println!(
294 " test accepted [{}]",
295 outcome(chain[0].verify_to_root(issued, Accept::ProductionAndTest))
296 );
297
298 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}