use std::fmt;
use crate::error::{Error, Result};
use crate::pin::Pin;
use crate::tlv::ber;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Date {
pub year: u16,
pub month: u8,
pub day: u8,
}
impl Date {
pub fn parse(bytes: &[u8]) -> Result<Self> {
let text = std::str::from_utf8(bytes)
.ok()
.filter(|s| s.len() == 8 && s.bytes().all(|b| b.is_ascii_digit()))
.ok_or_else(|| malformed(&format!("expected 8 digits, got {}", hex(bytes))))?;
let date = Date {
year: text[0..4].parse().unwrap(),
month: text[4..6].parse().unwrap(),
day: text[6..8].parse().unwrap(),
};
if !(1..=12).contains(&date.month) || !(1..=31).contains(&date.day) {
return Err(malformed(&format!("not a calendar date: {date}")));
}
Ok(date)
}
pub fn from_unix_seconds(seconds: i64) -> Self {
let days = seconds.div_euclid(86_400) + 719_468;
let era = days.div_euclid(146_097);
let doe = days.rem_euclid(146_097);
let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let day = (doy - (153 * mp + 2) / 5 + 1) as u8;
let month = if mp < 10 { mp + 3 } else { mp - 9 } as u8;
let year = (yoe + era * 400 + i64::from(month <= 2)) as u16;
Date { year, month, day }
}
pub fn to_era(self) -> Option<(Era, u16)> {
let key = (self.year, self.month, self.day);
let era = match key {
k if k >= (2019, 5, 1) => Era::Reiwa,
k if k >= (1989, 1, 8) => Era::Heisei,
k if k >= (1926, 12, 25) => Era::Showa,
k if k >= (1912, 7, 30) => Era::Taisho,
k if k >= (1868, 1, 25) => Era::Meiji,
_ => return None,
};
Some((era, self.year - era.first_gregorian_year() + 1))
}
}
impl fmt::Display for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(missing_docs)]
pub enum Era {
Meiji,
Taisho,
Showa,
Heisei,
Reiwa,
}
impl Era {
pub const fn first_gregorian_year(self) -> u16 {
match self {
Era::Meiji => 1868,
Era::Taisho => 1912,
Era::Showa => 1926,
Era::Heisei => 1989,
Era::Reiwa => 2019,
}
}
pub const fn name(self) -> &'static str {
match self {
Era::Meiji => "明治",
Era::Taisho => "大正",
Era::Showa => "昭和",
Era::Heisei => "平成",
Era::Reiwa => "令和",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Sex {
Male,
Female,
Unknown,
NotApplicable,
Other(u8),
}
impl Sex {
pub const fn from_byte(b: u8) -> Self {
match b {
b'0' => Sex::Unknown,
b'1' => Sex::Male,
b'2' => Sex::Female,
b'9' => Sex::NotApplicable,
other => Sex::Other(other),
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct MyNumber([u8; 12]);
impl MyNumber {
pub fn parse(bytes: &[u8]) -> Result<Self> {
let digits: [u8; 12] = bytes
.try_into()
.ok()
.filter(|d: &[u8; 12]| d.iter().all(u8::is_ascii_digit))
.ok_or_else(|| malformed(&format!("個人番号 must be 12 digits, got {}", hex(bytes))))?;
Ok(MyNumber(digits))
}
pub fn as_bytes(&self) -> &[u8; 12] {
&self.0
}
pub fn as_str(&self) -> &str {
std::str::from_utf8(&self.0).expect("digits are ASCII")
}
pub fn as_verification_code_a(&self) -> Result<Pin> {
Pin::numeric(self.0)
}
}
impl fmt::Debug for MyNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MyNumber(<12 digits redacted>)")
}
}
pub fn verification_code_b(
birth_date: Date,
expiry_year: u16,
security_code: &[u8],
) -> Result<Pin> {
let (_, era_year) = birth_date
.to_era()
.ok_or_else(|| malformed(&format!("{birth_date} predates the Meiji era")))?;
if era_year > 99 {
return Err(malformed(&format!(
"era year {era_year} does not fit in two digits"
)));
}
if security_code.len() != 4 || !security_code.iter().all(u8::is_ascii_digit) {
return Err(Error::InvalidPin("security code must be 4 digits"));
}
let text = format!(
"{:02}{:02}{:02}{:04}{}",
era_year,
birth_date.month,
birth_date.day,
expiry_year,
std::str::from_utf8(security_code).expect("digits are ASCII"),
);
Pin::numeric(text)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RsaPublicKey {
pub exponent: Vec<u8>,
pub modulus: Vec<u8>,
}
impl RsaPublicKey {
pub const TAG_EXPONENT: u32 = 0x90;
pub const TAG_MODULUS: u32 = 0x91;
pub fn parse(data: &[u8]) -> Result<Self> {
let mut exponent = None;
let mut modulus = None;
for tlv in ber::iter(data) {
let tlv = tlv?;
match tlv.tag {
Self::TAG_EXPONENT => exponent = Some(tlv.value.to_vec()),
Self::TAG_MODULUS => modulus = Some(tlv.value.to_vec()),
_ => {}
}
}
Ok(RsaPublicKey {
exponent: exponent.ok_or_else(|| malformed("no public exponent (tag 90)"))?,
modulus: modulus.ok_or_else(|| malformed("no modulus (tag 91)"))?,
})
}
pub fn bits(&self) -> usize {
match self.modulus.iter().position(|&b| b != 0) {
Some(first) => {
(self.modulus.len() - first) * 8 - self.modulus[first].leading_zeros() as usize
}
None => 0,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct KeyId([u8; Self::LEN]);
impl KeyId {
pub const LEN: usize = 16;
pub fn parse(bytes: &[u8]) -> Result<Self> {
let bytes: [u8; Self::LEN] = bytes.try_into().map_err(|_| {
malformed(&format!(
"key identifier must be 16 bytes, got {}",
bytes.len()
))
})?;
if !bytes[..7].iter().all(u8::is_ascii_digit)
|| !bytes[9..12].iter().all(u8::is_ascii_digit)
{
return Err(malformed("key identifier is not digits where it should be"));
}
Ok(KeyId(bytes))
}
pub fn number(&self) -> &str {
std::str::from_utf8(&self.0[..7]).unwrap_or("???????")
}
pub fn group(&self) -> &str {
std::str::from_utf8(&self.0[9..12]).unwrap_or("???")
}
pub fn as_bytes(&self) -> &[u8; Self::LEN] {
&self.0
}
}
impl fmt::Display for KeyId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.number(), self.group())
}
}
impl fmt::Debug for KeyId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "KeyId({self}")?;
for byte in &self.0[12..] {
write!(f, " {byte:02X}")?;
}
write!(f, ")")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CardVerifiableCertificate {
pub issuer_key_id: KeyId,
pub subject_key_id: KeyId,
pub public_key: RsaPublicKey,
pub signature: Vec<u8>,
pub signed_data: Vec<u8>,
}
impl CardVerifiableCertificate {
pub const TAG: u32 = 0x7F21;
pub const TAG_BODY: u32 = 0x5F4E;
pub const TAG_SIGNATURE: u32 = 0x5F37;
pub const KEY_ID_LEN: usize = 16;
pub const BODY_LEN: usize = 297;
pub fn parse(data: &[u8]) -> Result<Self> {
let contents = if data.starts_with(&[0x7F, 0x21]) {
let outer = ber::parse(data)?;
if outer.tag != Self::TAG {
return Err(malformed(&format!(
"expected tag 7F21, got {:04X}",
outer.tag
)));
}
outer.value
} else {
data
};
let mut body = None;
let mut signature = None;
for tlv in ber::iter(contents) {
let tlv = tlv?;
match tlv.tag {
Self::TAG_BODY => body = Some(tlv.value),
Self::TAG_SIGNATURE => signature = Some(tlv.value.to_vec()),
_ => {}
}
}
let body = body.ok_or_else(|| malformed("certificate has no body (tag 5F4E)"))?;
if body.len() != Self::BODY_LEN {
return Err(malformed(&format!(
"certificate body must be {} bytes, got {}",
Self::BODY_LEN,
body.len()
)));
}
let ids = 2 * Self::KEY_ID_LEN;
Ok(CardVerifiableCertificate {
issuer_key_id: KeyId::parse(&body[..Self::KEY_ID_LEN])?,
subject_key_id: KeyId::parse(&body[Self::KEY_ID_LEN..ids])?,
public_key: RsaPublicKey::parse(&body[ids..])?,
signature: signature
.ok_or_else(|| malformed("certificate has no signature (tag 5F37)"))?,
signed_data: body.to_vec(),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ImageFormat {
Png,
Jpeg2000,
Unknown,
}
impl ImageFormat {
pub fn detect(data: &[u8]) -> Self {
if data.starts_with(b"\x89PNG\r\n\x1a\n") {
ImageFormat::Png
} else if data.len() >= 8 && &data[4..8] == b"jP " {
ImageFormat::Jpeg2000
} else {
ImageFormat::Unknown
}
}
pub const fn extension(self) -> &'static str {
match self {
ImageFormat::Png => "png",
ImageFormat::Jpeg2000 => "jp2",
ImageFormat::Unknown => "bin",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Image {
pub data: Vec<u8>,
pub format: ImageFormat,
}
impl Image {
pub fn new(data: Vec<u8>) -> Self {
let format = ImageFormat::detect(&data);
Image { data, format }
}
}
pub(crate) fn check_offsets(file: &[u8], table: &[u8], starts: &[usize]) -> Result<()> {
if table.len() != starts.len() * 2 {
return Err(malformed(&format!(
"offset table is {} bytes for {} objects",
table.len(),
starts.len()
)));
}
for (i, (chunk, &start)) in table.chunks_exact(2).zip(starts).enumerate() {
let declared = usize::from(u16::from_be_bytes([chunk[0], chunk[1]]));
if declared != start {
return Err(malformed(&format!(
"offset {i} says {declared:#06X} but the object starts at {start:#06X}"
)));
}
}
let _ = file;
Ok(())
}
pub(crate) struct TlvFields<'a> {
items: Vec<(u32, &'a [u8], &'a [u8])>,
}
impl<'a> TlvFields<'a> {
pub(crate) fn parse(
raw: &'a [u8],
expected_tag: u32,
offset_table: Option<u32>,
) -> Result<Self> {
let outer = ber::parse(raw)?;
if outer.tag != expected_tag {
return Err(malformed(&format!(
"expected tag {expected_tag:04X}, got {:04X}",
outer.tag
)));
}
let mut pos = ber::parse_header(raw)?.header_len;
let mut rest = outer.value;
let mut offsets = None;
let mut items = Vec::new();
let mut starts = Vec::new();
while let Some(&first) = rest.first() {
if first == 0x00 || first == 0xFF {
break;
}
let header = ber::parse_header(rest)?;
let end = header.total_len();
let value = rest
.get(header.header_len..end)
.ok_or_else(|| malformed("a field runs past the end of the file"))?;
if Some(header.tag) == offset_table {
offsets = Some(value);
} else {
items.push((header.tag, value, &rest[..end]));
starts.push(pos);
}
pos += end;
rest = &rest[end..];
}
if let Some(table) = offsets {
check_offsets(raw, table, &starts)?;
}
Ok(TlvFields { items })
}
pub(crate) fn get(&self, tag: u32) -> Result<&'a [u8]> {
self.items
.iter()
.find(|(t, _, _)| *t == tag)
.map(|(_, v, _)| *v)
.ok_or_else(|| malformed(&format!("no field with tag {tag:04X}")))
}
pub(crate) fn bytes_before(&self, tag: u32) -> Result<Vec<u8>> {
let end = self
.items
.iter()
.position(|(t, _, _)| *t == tag)
.ok_or_else(|| malformed(&format!("no field with tag {tag:04X}")))?;
Ok(self.items[..end]
.iter()
.flat_map(|(_, _, raw)| *raw)
.copied()
.collect())
}
pub(crate) fn bytes_of(&self, tags: &[u32]) -> Result<Vec<u8>> {
let mut out = Vec::new();
for tag in tags {
let raw = self
.items
.iter()
.find(|(t, _, _)| t == tag)
.map(|(_, _, raw)| *raw)
.ok_or_else(|| malformed(&format!("no field with tag {tag:04X}")))?;
out.extend_from_slice(raw);
}
Ok(out)
}
}
pub(crate) fn malformed(what: &str) -> Error {
Error::Malformed(what.to_owned())
}
fn hex(bytes: &[u8]) -> String {
bytes
.iter()
.map(|b| format!("{b:02X}"))
.collect::<Vec<_>>()
.join(" ")
}
pub fn sha256_digest_info(digest: &[u8]) -> Vec<u8> {
const ALGORITHM: [u8; 15] = [
0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00,
];
let inner = ALGORITHM.len() + 2 + digest.len();
let mut out = vec![0x30];
if inner < 0x80 {
out.push(inner as u8);
} else {
out.push(0x81);
out.push(inner as u8);
}
out.extend_from_slice(&ALGORITHM);
out.push(0x04);
out.push(digest.len() as u8);
out.extend_from_slice(digest);
out
}
#[cfg(feature = "verify")]
impl RsaPublicKey {
fn to_rsa(&self) -> Result<rsa::RsaPublicKey> {
rsa::RsaPublicKey::new(
rsa::BigUint::from_bytes_be(&self.modulus),
rsa::BigUint::from_bytes_be(&self.exponent),
)
.map_err(|_| Error::SignatureInvalid("the public key is not usable"))
}
pub fn verify_pkcs1(&self, digest_info: &[u8], signature: &[u8]) -> Result<()> {
self.to_rsa()?
.verify(rsa::Pkcs1v15Sign::new_unprefixed(), digest_info, signature)
.map_err(|_| Error::SignatureInvalid("PKCS #1 v1.5 signature does not verify"))
}
pub fn verify_pkcs1_sha256(&self, message: &[u8], signature: &[u8]) -> Result<()> {
use rsa::sha2::Digest as _;
let digest = rsa::sha2::Sha256::digest(message);
self.verify_pkcs1(&sha256_digest_info(&digest), signature)
}
pub fn verify_pss_sha256(&self, message: &[u8], signature: &[u8]) -> Result<()> {
use rsa::sha2::Digest as _;
self.verify_pss_prehashed(&rsa::sha2::Sha256::digest(message), signature)
}
pub fn verify_pss_prehashed(&self, digest: &[u8], signature: &[u8]) -> Result<()> {
self.to_rsa()?
.verify(rsa::Pss::new::<rsa::sha2::Sha256>(), digest, signature)
.map_err(|_| Error::SignatureInvalid("PSS signature does not verify"))
}
}
#[cfg(feature = "verify")]
pub fn sha256(data: &[u8]) -> [u8; 32] {
use rsa::sha2::Digest as _;
rsa::sha2::Sha256::digest(data).into()
}
#[cfg(all(test, feature = "verify"))]
mod verify_tests {
use super::*;
#[test]
fn builds_digest_infos_of_both_lengths() {
let one = sha256_digest_info(&[0xAA; 32]);
assert_eq!(&one[..2], &[0x30, 0x31]);
assert_eq!(&one[17..19], &[0x04, 0x20]);
assert_eq!(one.len(), 51);
let three = sha256_digest_info(&[0xAA; 96]);
assert_eq!(&three[..2], &[0x30, 0x71]);
assert_eq!(&three[17..19], &[0x04, 0x60]);
assert_eq!(three.len(), 115);
}
}
#[cfg(feature = "verify")]
impl CardVerifiableCertificate {
pub fn verify(&self) -> Result<()> {
let ca = crate::ca::find(&self.issuer_key_id)
.ok_or(Error::UnknownCertificateAuthority(self.issuer_key_id))?;
self.verify_with(&ca.to_public_key())
}
pub fn verify_chain(chain: &[Self]) -> Result<()> {
let (first, rest) = chain
.split_first()
.ok_or_else(|| malformed("an empty chain verifies nothing"))?;
first.verify()?;
let mut issuer = first;
for cert in rest {
if cert.issuer_key_id != issuer.subject_key_id {
return Err(malformed(
"chain is broken: a certificate names an issuer the one above does not certify",
));
}
cert.verify_with(&issuer.public_key)?;
issuer = cert;
}
Ok(())
}
pub fn verify_with(&self, ca_key: &RsaPublicKey) -> Result<()> {
ca_key.verify_pkcs1_sha256(&self.signed_data, &self.signature)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_a_date() {
assert_eq!(
Date::parse(b"19800217").unwrap(),
Date {
year: 1980,
month: 2,
day: 17
}
);
assert_eq!(Date::parse(b"19800217").unwrap().to_string(), "1980-02-17");
assert!(Date::parse(b"1980021").is_err());
assert!(Date::parse(b"19801317").is_err());
assert!(Date::parse(b"1980-2-17").is_err());
}
#[test]
fn converts_to_japanese_eras() {
assert_eq!(
Date::parse(b"19800217").unwrap().to_era(),
Some((Era::Showa, 55))
);
assert_eq!(
Date::parse(b"19890107").unwrap().to_era(),
Some((Era::Showa, 64))
);
assert_eq!(
Date::parse(b"19890108").unwrap().to_era(),
Some((Era::Heisei, 1))
);
assert_eq!(
Date::parse(b"20190430").unwrap().to_era(),
Some((Era::Heisei, 31))
);
assert_eq!(
Date::parse(b"20190501").unwrap().to_era(),
Some((Era::Reiwa, 1))
);
assert_eq!(Date::parse(b"18670101").unwrap().to_era(), None);
assert_eq!(Era::Showa.name(), "昭和");
}
#[test]
fn builds_verification_code_b() {
let dob = Date::parse(b"19800217").unwrap();
let code = verification_code_b(dob, 2035, b"2285").unwrap();
assert_eq!(code.as_bytes(), b"55021720352285");
assert_eq!(code.len(), 14);
}
#[test]
fn rejects_a_code_b_it_cannot_build() {
let dob = Date::parse(b"19800217").unwrap();
assert!(verification_code_b(dob, 2035, b"228").is_err());
assert!(verification_code_b(dob, 2035, b"22X5").is_err());
assert!(verification_code_b(Date::parse(b"18000101").unwrap(), 2035, b"2285").is_err());
}
#[test]
fn my_number_is_also_verification_code_a() {
let n = MyNumber::parse(b"537686677188").unwrap();
assert_eq!(n.as_str(), "537686677188");
assert_eq!(
n.as_verification_code_a().unwrap().as_bytes(),
b"537686677188"
);
assert!(!format!("{n:?}").contains("5376"));
assert!(MyNumber::parse(b"53768667718").is_err());
assert!(MyNumber::parse(b"53768667718X").is_err());
}
#[test]
fn parses_a_public_key() {
let mut data = vec![0x90, 0x03, 0x01, 0x00, 0x01, 0x91, 0x82, 0x01, 0x00];
data.push(0xC9);
data.extend(std::iter::repeat_n(0xAA, 255));
let key = RsaPublicKey::parse(&data).unwrap();
assert_eq!(key.exponent, [0x01, 0x00, 0x01]);
assert_eq!(key.modulus.len(), 256);
assert_eq!(key.bits(), 2048);
}
#[test]
fn detects_image_formats() {
assert_eq!(
ImageFormat::detect(b"\x89PNG\r\n\x1a\n\x00"),
ImageFormat::Png
);
assert_eq!(
ImageFormat::detect(b"\x00\x00\x00\x0CjP \r\n"),
ImageFormat::Jpeg2000
);
assert_eq!(ImageFormat::detect(b"nope"), ImageFormat::Unknown);
assert_eq!(ImageFormat::Png.extension(), "png");
}
fn cv_certificate() -> Vec<u8> {
let mut body = b"9200073\x08\x050010000".to_vec();
body.extend_from_slice(b"9299774\x08\x050010000");
body.extend_from_slice(&[0x90, 0x03, 0x01, 0x00, 0x01, 0x91, 0x82, 0x01, 0x00]);
body.push(0xC9);
body.extend(std::iter::repeat_n(0xAA, 255));
assert_eq!(body.len(), CardVerifiableCertificate::BODY_LEN);
let mut inner = vec![0x5F, 0x4E, 0x82];
inner.extend_from_slice(&(body.len() as u16).to_be_bytes());
inner.extend_from_slice(&body);
inner.extend_from_slice(&[0x5F, 0x37, 0x82, 0x01, 0x00]);
inner.extend(std::iter::repeat_n(0xBC, 256));
let mut cert = vec![0x7F, 0x21, 0x82];
cert.extend_from_slice(&(inner.len() as u16).to_be_bytes());
cert.extend_from_slice(&inner);
cert
}
#[test]
fn parses_a_card_verifiable_certificate() {
let parsed = CardVerifiableCertificate::parse(&cv_certificate()).unwrap();
assert_eq!(parsed.issuer_key_id.to_string(), "9200073/001");
assert_eq!(parsed.subject_key_id.to_string(), "9299774/001");
assert_eq!(parsed.public_key.bits(), 2048);
assert_eq!(parsed.signature.len(), 256);
assert_eq!(
parsed.signed_data.len(),
CardVerifiableCertificate::BODY_LEN
);
assert!(parsed.signed_data.starts_with(b"9200073"));
}
#[test]
fn rejects_a_body_of_the_wrong_size() {
let mut cert = cv_certificate();
let body_len = CardVerifiableCertificate::BODY_LEN - 1;
cert[8] = (body_len >> 8) as u8;
cert[9] = body_len as u8;
cert.remove(10 + body_len);
cert[3] = ((cert.len() - 5) >> 8) as u8;
cert[4] = (cert.len() - 5) as u8;
let err = CardVerifiableCertificate::parse(&cert).unwrap_err();
assert!(format!("{err}").contains("297"), "{err}");
}
#[test]
fn offset_table_mismatch_is_an_error() {
assert!(check_offsets(&[], &[0x00, 0x0E, 0x00, 0x20], &[14, 32]).is_ok());
assert!(check_offsets(&[], &[0x00, 0x0E, 0x00, 0x20], &[14, 33]).is_err());
assert!(check_offsets(&[], &[0x00, 0x0E], &[14, 32]).is_err());
}
}