1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0

//! 4.2.1 Application Identifier (AID)

use nom::{bytes::complete as bytes, number::complete as number};
use std::convert::TryFrom;

use crate::card_do::{complete, ApplicationIdentifier};

fn parse(input: &[u8]) -> nom::IResult<&[u8], ApplicationIdentifier> {
    let (input, _) = bytes::tag([0xd2, 0x76, 0x0, 0x1, 0x24])(input)?;

    let (input, application) = number::u8(input)?;
    let (input, version) = number::be_u16(input)?;
    let (input, manufacturer) = number::be_u16(input)?;
    let (input, serial) = number::be_u32(input)?;

    let (input, _) = nom::combinator::all_consuming(bytes::tag([0x0, 0x0]))(input)?;

    Ok((
        input,
        ApplicationIdentifier {
            application,
            version,
            manufacturer,
            serial,
        },
    ))
}

impl TryFrom<&[u8]> for ApplicationIdentifier {
    type Error = crate::Error;

    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
        complete(parse(data))
    }
}

impl ApplicationIdentifier {
    pub fn application(&self) -> u8 {
        self.application
    }

    pub fn version(&self) -> u16 {
        self.version
    }

    pub fn manufacturer(&self) -> u16 {
        self.manufacturer
    }

    pub fn serial(&self) -> u32 {
        self.serial
    }

    /// This ident is constructed as the concatenation of manufacturer
    /// id, a colon, and the card serial (in hexadecimal representation,
    /// with uppercase hex digits).
    ///
    /// It is a more easily human-readable, shorter form of the full
    /// 16-byte AID ("Application Identifier").
    ///
    /// Example: "1234:5678ABCD".
    pub fn ident(&self) -> String {
        format!("{:04X}:{:08X}", self.manufacturer, self.serial)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_gnuk() {
        let data = [
            0xd2, 0x76, 0x0, 0x1, 0x24, 0x1, 0x2, 0x0, 0xff, 0xfe, 0x43, 0x19, 0x42, 0x40, 0x0, 0x0,
        ];

        let aid =
            ApplicationIdentifier::try_from(&data[..]).expect("failed to parse application id");

        assert_eq!(
            aid,
            ApplicationIdentifier {
                application: 0x1,
                version: 0x200,
                manufacturer: 0xfffe,
                serial: 0x43194240,
            }
        );
    }
}