Skip to content

Commit fda173b

Browse files
authored
Merge pull request #2 from my-number/wip
v0.3.0
2 parents 0f23d31 + 100ffe5 commit fda173b

File tree

5 files changed

+175
-69
lines changed

5 files changed

+175
-69
lines changed

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
[package]
22
name = "myna"
3-
version = "0.2.0"
3+
version = "0.3.0"
44
authors = ["yuki-js <[email protected]>"]
55
edition = "2018"
66
license = "Apache-2.0"
7-
description = "Japan ID card(mynumber card) manipulation library"
7+
description = "Japanese ID card(mynumber card) manipulation library"
88
repository = "https://github.com/my-number/myna"
99
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1010

src/card.rs

+143-42
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1+
use crate::error::ApduError as Error;
2+
use crate::utils::{check_password, check_pin, make_apdu};
13
use alloc::vec::Vec;
24
use der_parser::der::der_read_element_header;
3-
use crate::error::ApduError as Error;
4-
use crate::utils::{make_apdu, check_pin};
5+
56
type Request = Vec<u8>;
67
type Response = Vec<u8>;
78
type BinaryData = Vec<u8>;
89
type Hash<'a> = &'a [u8];
910
type FileId<'a> = &'a [u8];
1011

11-
pub trait Apdu {
12+
#[derive(Clone, Copy, Debug)]
13+
pub enum KeyType {
14+
UserAuth,
15+
DigitalSign,
16+
}
1217

18+
pub trait Apdu {
1319
/// (Required) Error type that transmit() returns when failed to read card
1420
type TransErr;
1521
/// (Required) Implement card communication here.
@@ -20,7 +26,10 @@ pub trait Apdu {
2026
match self.transmit(data) {
2127
Ok(apdu) => {
2228
let len = apdu.len();
23-
let sw1 = apdu[len - 2];
29+
if len < 2 {
30+
return Err(Error::Fatal("No response"));
31+
}
32+
let sw1 = apdu[len - 2]; // overflow
2433
let sw2 = apdu[len - 1];
2534
if (sw1 == 0x90 || sw1 == 0x91) && sw2 == 0x00 {
2635
return Ok(apdu[0..(len - 2)].to_vec());
@@ -30,51 +39,70 @@ pub trait Apdu {
3039
Err(e) => Err(Error::Transmission(e)),
3140
}
3241
}
33-
42+
/// SELECT DF
3443
fn select_df(&self, dfid: FileId) -> Result<(), Error<Self::TransErr>> {
3544
match self.transmit_checked(make_apdu(0x00, 0xa4, (0x04, 0x0c), dfid, None)) {
3645
Ok(_) => Ok(()),
37-
_ => Err(Error::Execution("Failed to SELECT DF")),
46+
Err(e) => Err(e.check_err("Failed to SELECT DF")),
3847
}
3948
}
49+
/// SELECT EF
50+
///
51+
/// Call it with DF selected
4052
fn select_ef(&self, efid: FileId) -> Result<(), Error<Self::TransErr>> {
4153
match self.transmit_checked(make_apdu(0x00, 0xa4, (0x02, 0x0c), efid, None)) {
4254
Ok(_) => Ok(()),
43-
_ => Err(Error::Execution("Failed to SELECT EF")),
55+
Err(e) => Err(e.check_err("Failed to SELECT EF")),
4456
}
4557
}
46-
fn select_jpki_ap(&self) -> Result<(), Error<Self::TransErr>> {
47-
self.select_df(b"\xD3\x92\xf0\x00\x26\x01\x00\x00\x00\x01")
48-
}
49-
fn select_jpki_token(&self) -> Result<(), Error<Self::TransErr>> {
50-
self.select_ef(b"\x00\x06")
51-
}
52-
fn select_jpki_cert_auth(&self) -> Result<(), Error<Self::TransErr>> {
53-
self.select_ef(b"\x00\x0a")
54-
}
5558

56-
fn select_jpki_auth_pin(&self) -> Result<(), Error<Self::TransErr>> {
57-
self.select_ef(b"\x00\x18")
58-
}
59-
fn select_jpki_auth_key(&self) -> Result<(), Error<Self::TransErr>> {
60-
self.select_ef(b"\x00\x17")
61-
}
59+
/// GET CHALLENGE
6260
fn get_challenge(&self, size: u8) -> Result<Response, Error<Self::TransErr>> {
6361
match self.transmit_checked(make_apdu(0x00, 0x84, (0, 0), &[], Some(size))) {
6462
Ok(s) => Ok(s),
65-
_ => Err(Error::Execution("GET CHALLENGE failed")),
63+
Err(e) => Err(e.check_err("GET CHALLENGE failed")),
6664
}
6765
}
68-
fn verify_pin(&self, pin: &str) -> Result<(), Error<Self::TransErr>> {
69-
if !check_pin(pin) {
70-
return Err(Error::Execution("PIN is invalid"))
71-
}
66+
67+
/// VERIFY PIN
68+
///
69+
/// Call it with PIN EF selected
70+
fn verify_pin(&self, pin: &str, key_type: KeyType) -> Result<(), Error<Self::TransErr>> {
71+
match key_type {
72+
KeyType::UserAuth => {
73+
if !check_pin(pin) {
74+
return Err(Error::Execution("PIN is invalid"));
75+
}
76+
}
77+
KeyType::DigitalSign => {
78+
if !check_password(pin) {
79+
return Err(Error::Execution("PIN is invalid"));
80+
}
81+
}
82+
};
83+
7284
match self.transmit_checked(make_apdu(0x00, 0x20, (0x00, 0x80), pin.as_bytes(), None)) {
7385
Ok(_) => Ok(()),
74-
_ => Err(Error::Execution("VERIFY PIN failed")),
86+
Err(e) => Err(e.pin_err()),
87+
}
88+
}
89+
90+
/// VERIFY PIN without PIN
91+
///
92+
/// check PIN retry counter
93+
fn lookup_pin(&self) -> Result<u8, Error<Self::TransErr>> {
94+
if let Err(e) = self.transmit_checked(make_apdu(0x00, 0x20, (0x00, 0x80), &[], None)) {
95+
if let Error::PinIncorrect(remaining) = e.pin_err() {
96+
return Ok(remaining);
97+
}
7598
}
99+
return Err(Error::Fatal("Unexpected Error"));
76100
}
77-
fn compute_sig(&self, hash_pkcs1: Hash) -> Result<Response, Error<Self::TransErr>> {
101+
102+
/// COMPUTE DIGITAL SIGNATURE
103+
///
104+
/// Call it with Private Key EF selected
105+
fn compute_digital_signature(&self, hash_pkcs1: Hash) -> Result<Response, Error<Self::TransErr>> {
78106
match self.transmit_checked(make_apdu(
79107
0x80,
80108
0x2a,
@@ -84,19 +112,13 @@ pub trait Apdu {
84112
)) {
85113
// zero, the value of Le probably means 256. it overflowed.
86114
Ok(sig) => Ok(sig),
87-
_ => Err(Error::Execution("COMPUTE DIGITAL SIGNATURE failed")),
115+
Err(e) => Err(e.check_err("COMPUTE DIGITAL SIGNATURE failed")),
88116
}
89117
}
90-
91-
fn read_binary(&self) -> Result<BinaryData, Error<Self::TransErr>> {
92-
let header = match self.transmit_checked(make_apdu(0x00, 0xb0, (0u8, 0u8), &[], Some(7u8))) {
93-
Ok(s) => s,
94-
_ => return Err(Error::Execution("READ BINARY failed")),
95-
};
96-
97-
let parsed = der_read_element_header(&header[..]).unwrap();
98-
let length = parsed.1.len as usize + header.len() - parsed.0.len();
99-
118+
/// READ BINARY
119+
///
120+
/// Call it with readable object selected
121+
fn read_binary(&self, length: usize) -> Result<BinaryData, Error<Self::TransErr>> {
100122
let mut data: Vec<u8> = Vec::with_capacity(length);
101123
loop {
102124
let current_size = data.len();
@@ -115,10 +137,89 @@ pub trait Apdu {
115137
return Ok(data);
116138
}
117139
}
118-
_ => return Err(Error::Execution("READ BINARY failed")),
140+
Err(e) => return Err(e.check_err("READ BINARY failed")),
119141
}
120142
}
121143
}
122-
}
144+
145+
/// Read binary as X.509 Certificate
146+
fn read_cert(&self) -> Result<BinaryData, Error<Self::TransErr>> {
147+
let header = self.read_binary(8)?;
148+
149+
let parsed = der_read_element_header(&header[..])
150+
.map_err(|_| Error::Execution("Failed to parse Certificate header"))?;
151+
let length = parsed.1.len as usize + header.len() - parsed.0.len();
152+
153+
return self.read_binary(length);
154+
}
155+
156+
/// Selects JPKI AP DF
157+
fn select_jpki_ap(&self) -> Result<(), Error<Self::TransErr>> {
158+
self.select_df(b"\xD3\x92\xf0\x00\x26\x01\x00\x00\x00\x01")
159+
}
160+
161+
/// Selects JPKI Token EF
162+
fn select_jpki_token(&self) -> Result<(), Error<Self::TransErr>> {
163+
self.select_ef(b"\x00\x06")
164+
}
123165

166+
/// Selects JPKI Certificate EF
167+
fn select_jpki_cert(&self, key_type: KeyType) -> Result<(), Error<Self::TransErr>> {
168+
match key_type {
169+
KeyType::UserAuth => self.select_ef(b"\x00\x0a"),
170+
KeyType::DigitalSign => self.select_ef(b"\x00\x01"),
171+
}
172+
}
124173

174+
/// Selects JPKI PIN EF
175+
fn select_jpki_pin(&self, key_type: KeyType) -> Result<(), Error<Self::TransErr>> {
176+
match key_type {
177+
KeyType::UserAuth => self.select_ef(b"\x00\x18"),
178+
KeyType::DigitalSign => self.select_ef(b"\x00\x1B"),
179+
}
180+
}
181+
182+
/// Selects JPKI Private Key EF
183+
fn select_jpki_key(&self, key_type: KeyType) -> Result<(), Error<Self::TransErr>> {
184+
match key_type {
185+
KeyType::UserAuth => self.select_ef(b"\x00\x17"),
186+
_ => unimplemented!(),
187+
}
188+
}
189+
190+
/// Checks if it is mynumber card
191+
fn is_mynumber_card(&self) -> Result<bool, Error<Self::TransErr>> {
192+
self.select_jpki_ap()?;
193+
self.select_jpki_token()?;
194+
let jpki_token = self.read_binary(32)?;
195+
Ok(&jpki_token[..] == b"JPKIAPICCTOKEN2 ")
196+
}
197+
198+
/// Gets Certificate
199+
fn get_cert(&self, key_type: KeyType) -> Result<BinaryData, Error<Self::TransErr>> {
200+
self.select_jpki_ap()?;
201+
self.select_jpki_key(key_type)?;
202+
self.select_jpki_cert(key_type)?;
203+
204+
let cert = self.read_cert()?;
205+
Ok(cert)
206+
}
207+
208+
/// Computes signature
209+
fn compute_sig(&self, pin: &str, hash: Hash, key_type: KeyType) -> Result<Response, Error<Self::TransErr>> {
210+
self.select_jpki_ap()?;
211+
self.select_jpki_pin(key_type)?;
212+
self.verify_pin(pin, key_type)?;
213+
self.select_jpki_key(key_type)?;
214+
let sig = self.compute_digital_signature(hash)?;
215+
Ok(sig)
216+
}
217+
218+
/// Gets PIN retry counter
219+
fn get_retry_counter(&self, key_type: KeyType) -> Result<u8, Error<Self::TransErr>> {
220+
self.select_jpki_ap()?;
221+
self.select_jpki_pin(key_type)?;
222+
let count = self.lookup_pin()?;
223+
Ok(count)
224+
}
225+
}

src/crypto.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ extern crate alloc;
55
extern crate sha2;
66
use der_parser::{ber::BerObjectContent, error::BerError, oid::Oid, parse_der};
77
use rsa::{
8-
errors::Error as RSAError, hash::Hashes, BigUint, PaddingScheme, PublicKey, RSAPublicKey,
8+
errors::Error as RSAError, hash::Hashes, BigUint, PaddingScheme, PublicKey,
99
};
10+
pub use rsa::RSAPublicKey;
1011
use sha2::{
1112
digest::{FixedOutput, Input},
1213
Sha256,

src/error.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
/// APDU Error
32
#[derive(Debug)]
43
pub enum ApduError<T> {
@@ -8,8 +7,27 @@ pub enum ApduError<T> {
87
Command(u8, u8),
98
/// Error when execution failure detected
109
Execution(&'static str),
10+
/// Fatal error that seems not to be able to recover
11+
Fatal(&'static str),
12+
/// PIN Incorrect Error with remaining retries
13+
PinIncorrect(u8)
14+
}
15+
impl<T> ApduError<T> {
16+
pub fn check_err(self, err_msg: &'static str) -> Self {
17+
match self {
18+
Self::Command(_, _) => Self::Execution(err_msg),
19+
_ => self,
20+
}
21+
}
22+
pub fn pin_err(self) -> Self {
23+
if let Self::Command(sw1, sw2) = self {
24+
if sw1 == 0x63 && (sw2 & 0xF0 == 0xC0) {
25+
return Self::PinIncorrect(sw2 & 0x0F);
26+
}
27+
}
28+
return self;
29+
}
1130
}
12-
1331
#[derive(Debug)]
1432
pub enum CryptoError {
1533
ParseError,

src/utils.rs

+8-22
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,11 @@ const fn is_digit(ch: u8) -> bool {
4141
/// PIN ^[0-9]{4}$ validation
4242
pub fn check_pin(pin_str: &str) -> bool {
4343
let pin = pin_str.as_bytes();
44-
pin.len() == 4
45-
&& is_digit(pin[0])
46-
&& is_digit(pin[1])
47-
&& is_digit(pin[2])
48-
&& is_digit(pin[3])
44+
pin.len() == 4 && is_digit(pin[0]) && is_digit(pin[1]) && is_digit(pin[2]) && is_digit(pin[3])
45+
}
46+
pub fn check_password(password: &str) -> bool {
47+
unimplemented!()
4948
}
50-
5149
#[cfg(test)]
5250
mod tests {
5351
use super::*;
@@ -69,30 +67,18 @@ mod tests {
6967
}
7068
#[test]
7169
fn it_checks_valid_pin() {
72-
assert_eq!(
73-
check_pin("1919"),
74-
true
75-
);
70+
assert_eq!(check_pin("1919"), true);
7671
}
7772
#[test]
7873
fn it_checks_non_digit_pin() {
79-
assert_eq!(
80-
check_pin("pien"),
81-
false
82-
);
74+
assert_eq!(check_pin("pien"), false);
8375
}
8476
#[test]
8577
fn it_checks_digit_non_digit_pin() {
86-
assert_eq!(
87-
check_pin("pa0n"),
88-
false
89-
);
78+
assert_eq!(check_pin("pa0n"), false);
9079
}
9180
#[test]
9281
fn it_checks_too_long_pin() {
93-
assert_eq!(
94-
check_pin("114514"),
95-
false
96-
);
82+
assert_eq!(check_pin("114514"), false);
9783
}
9884
}

0 commit comments

Comments
 (0)