Skip to main content

ImageFormat

Enum ImageFormat 

Source
pub enum ImageFormat {
    Png,
    Jpeg2000,
    Unknown,
}
Expand description

The format of an image the card stores.

Recognised from the magic bytes, because the card gives no other indication and the two are mixed within one file: the rendered text fields are PNG and the photograph is JPEG 2000.

Variants§

§

Png

PNG. The rendered card-face fields are 1-bit greyscale.

§

Jpeg2000

JPEG 2000, in the JP2 container. The photograph.

§

Unknown

Not recognised.

Implementations§

Source§

impl ImageFormat

Source

pub fn detect(data: &[u8]) -> Self

Identify an image by its leading bytes.

Source

pub const fn extension(self) -> &'static str

The usual file extension.

Examples found in repository?
examples/read_card.rs (line 226)
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}

Trait Implementations§

Source§

impl Clone for ImageFormat

Source§

fn clone(&self) -> ImageFormat

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for ImageFormat

Source§

impl Debug for ImageFormat

Source§

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

Formats the value using the given formatter. Read more
Source§

impl Eq for ImageFormat

Source§

impl PartialEq for ImageFormat

Source§

fn eq(&self, other: &ImageFormat) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for ImageFormat

Auto Trait Implementations§

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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