aboutsummaryrefslogtreecommitdiffstats
path: root/cards/iso_card.py
blob: fb3b016206681d082da10bf9067fe76e84b42a2e (plain)
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import smartcard
import TLV_utils, crypto_utils, utils, binascii, fnmatch, re, time
from generic_card import Card
from utils import C_APDU, R_APDU

class ISO_Card(Card):
    DRIVER_NAME = ["ISO"]
    COMMAND_GET_RESPONSE = C_APDU(ins=0xc0)
    
    APDU_VERIFY_PIN = C_APDU(ins=0x20)
    
    ## Map for check_sw()
    STATUS_MAP = {
        Card.PURPOSE_SUCCESS: ("\x90\x00", ),
        Card.PURPOSE_GET_RESPONSE: ("61??", ), ## If this is received then GET RESPONSE should be called with SW2
        Card.PURPOSE_SM_OK: ("\x90\x00",),
        Card.PURPOSE_RETRY: (), ## Theoretically this would contain "6C??", but I dare not automatically resending a command for _all_ card types
        ## Instead, card types for which this is safe should set it in their own STATUS_MAP
    }
    
    ATRS = list(Card.ATRS)
    STOP_ATRS = list(Card.STOP_ATRS)

    ## Note: a key in this dictionary may either be a one- or two-byte string containing
    ## a binary status word, or a two or four-byte string containing a hexadecimal
    ## status word, possibly with ? characters marking variable nibbles. 
    ## Hexadecimal characters MUST be in uppercase. The values that two- or four-byte
    ## strings map to may be either format strings, that can make use of the 
    ## keyword substitutions for SW1 and SW2 or a callable accepting two arguments 
    ## (SW1, SW2) that returns a string.
    STATUS_WORDS = { 
        '\x90\x00': "Normal execution",
        '61??': "%(SW2)i (0x%(SW2)02x) bytes of response data can be retrieved with GetResponse.",
        '6C??': "Bad value for LE, 0x%(SW2)02x is the correct value.",
        '63C?': lambda SW1,SW2: "The counter has reached the value '%i'" % (SW2%16)
    }
    ## For the format of this dictionary of dictionaries see TLV_utils.tags
    TLV_OBJECTS = dict(Card.TLV_OBJECTS)
    DEFAULT_CONTEXT = None
    
    ## Format: "AID (binary)": ("name", [optional: description, {more information}])
    APPLICATIONS = {
        "\xa0\x00\x00\x01\x67\x45\x53\x49\x47\x4e": ("DF.ESIGN", ),
        "\xa0\x00\x00\x00\x63\x50\x4b\x43\x53\x2d\x31\x35": ("DF_PKCS15", ),
        "\xD2\x76\x00\x01\x24\x01": ("DF_OpenPGP", "OpenPGP card",                             {"significant_length": 6} ),
        "\xa0\x00\x00\x02\x47\x10\x01": ("DF_LDS", "Machine Readable Travel Document",         {"alias": ("mrtd",)}),
        ## The following are from 0341a.pdf: BSI-DSZ-CC-0341-2006
        "\xD2\x76\x00\x00\x66\x01":             ("DF_SIG",            "Signature application", {"fid": "\xAB\x00"}),
        "\xD2\x76\x00\x00\x25\x5A\x41\x02\x00": ("ZA_MF_NEU",         "Zusatzanwendungen",     {"fid": "\xA7\x00"}),
        "\xD2\x76\x00\x00\x25\x45\x43\x02\x00": ("DF_EC_CASH_NEU",    "ec-Cash",               {"fid": "\xA1\x00"}),
        "\xD2\x76\x00\x00\x25\x45\x50\x02\x00": ("DF_BOERSE_NEU",     "Geldkarte",             {"fid": "\xA2\x00", "alias": ("geldkarte",)}),
        "\xD2\x76\x00\x00\x25\x47\x41\x01\x00": ("DF_GA_MAESTRO",     "GA-Maestro",            {"fid": "\xAC\x00"}),
        "\xD2\x76\x00\x00\x25\x54\x44\x01\x00": ("DF_TAN",            "TAN-Anwendung",         {"fid": "\xAC\x02"}),
        "\xD2\x76\x00\x00\x25\x4D\x01\x02\x00": ("DF_MARKTPLATZ_NEU", "Marktplatz",            {"fid": "\xB0\x01"}),
        "\xD2\x76\x00\x00\x25\x46\x53\x02\x00": ("DF_FAHRSCHEIN_NEU", "Fahrschein",            {"fid": "\xB0\x00"}),
        "\xD2\x76\x00\x00\x25\x48\x42\x02\x00": ("DF_BANKING_20" ,    "HBCI",                  {"fid": "\xA6\x00"}),
        "\xD2\x76\x00\x00\x25\x4E\x50\x01\x00": ("DF_NOTEPAD",        "Notepad",               {"fid": "\xA6\x10"}),
        
        "\xd2\x76\x00\x00\x85\x01\x00":         ("NFC_TYPE_4",        "NFC NDEF Application on tag type 4", {"alias": ("nfc",)}, ),
        
        # From TR-03110_v201_pdf.pdf
        "\xE8\x07\x04\x00\x7f\x00\x07\x03\x02": ("DF_eID", "eID application"),
        
        "\xd2\x76\x00\x00\x25\x4b\x41\x4e\x4d\x30\x31\x00": ("VRS_TICKET", "VRS Ticket", {"fid": "\xad\x00", "alias": ("vrs",)}, ),
        "\xd2\x76\x00\x01\x35\x4b\x41\x4e\x4d\x30\x31\x00": ("VRS_TICKET", "VRS Ticket", {"fid": "\xad\x00",}, ),
    }
    # Alias for DF_BOERSE_NEU
    APPLICATIONS["\xA0\x00\x00\x00\x59\x50\x41\x43\x45\x01\x00"] = APPLICATIONS["\xD2\x76\x00\x00\x25\x45\x50\x02\x00"]
    # Alias for DF_GA_MAESTRO
    APPLICATIONS["\xA0\x00\x00\x00\x04\x30\x60"] = APPLICATIONS["\xD2\x76\x00\x00\x25\x47\x41\x01\x00"]
    
    ## Format: "RID (binary)": ("vendor name", [optional: {more information}])
    VENDORS = {
        "\xD2\x76\x00\x01\x24": ("Free Software Foundation Europe", ),
        "\xD2\x76\x00\x00\x25": ("Bankenverlag", ),
        "\xD2\x76\x00\x00\x60": ("Wolfgang Rankl", ),
        "\xD2\x76\x00\x00\x05": ("Giesecke & Devrient", ),
        "\xD2\x76\x00\x00\x40": ("Zentralinstitut fuer die Kassenaerztliche Versorgung in der Bundesrepublik Deutschland", ), # hpc-use-cases-01.pdf
        "\xa0\x00\x00\x02\x47": ("ICAO", ),
        "\xa0\x00\x00\x03\x06": ("PC/SC Workgroup", ),
    }
    
    TLV_OBJECTS[TLV_utils.context_FCP] = {
        0x84: (Card.decode_df_name, "DF name"),
    }
    TLV_OBJECTS[TLV_utils.context_FCI] = TLV_OBJECTS[TLV_utils.context_FCP]
    
    def post_merge(self):
        ## Called after cards.__init__.Cardmultiplexer._merge_attributes
        self.TLV_OBJECTS[TLV_utils.context_FCP][0x84] = (self._decode_df_name, "DF name")
        self.TLV_OBJECTS[TLV_utils.context_FCI][0x84] = (self._decode_df_name, "DF name")
    
    def verify_pin(self, pin_number, pin_value):
        apdu = C_APDU(self.APDU_VERIFY_PIN, P2 = pin_number,
            data = pin_value)
        result = self.send_apdu(apdu)
        return self.check_sw(result.sw)
    
    def cmd_verify(self, pin_number, pin_value):
        """Verify a PIN."""
        pin_number = int(pin_number, 0)
        pin_value = binascii.a2b_hex("".join(pin_value.split()))
        self.verify_pin(pin_number, pin_value)
    
    COMMANDS = {
        "verify": cmd_verify,
    }