├── llsmartcard
├── __init__.py
├── helper.py
├── parser.py
├── apdu.py
└── card.py
├── .gitignore
├── docs
├── EMVThesis.pdf
├── Commands.java.html
├── RID_list.txt
└── apdus.txt
├── setup_dev_environment.sh
├── SPDX.spdx
├── install_dependencies.sh
├── setup.py
├── examples
├── template.py
├── fuzzing
│ ├── admin_probe.py
│ └── smartcard_fuzzer.py
├── general
│ ├── secure_channel.py
│ ├── pin_gemalto.py
│ └── scrath.py
├── credit_cards
│ └── read_info.py
└── common_access_card
│ ├── cac_info.py
│ └── cac_crypto.py
├── LICENSE
└── README.md
/llsmartcard/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | .*
3 |
4 |
--------------------------------------------------------------------------------
/docs/EMVThesis.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mit-ll/LL-Smartcard/HEAD/docs/EMVThesis.pdf
--------------------------------------------------------------------------------
/setup_dev_environment.sh:
--------------------------------------------------------------------------------
1 | echo "export PYTHONPATH=\$PYTHONPATH:$PWD" >> ~/.bashrc
2 | source ~/.bashrc
3 |
--------------------------------------------------------------------------------
/SPDX.spdx:
--------------------------------------------------------------------------------
1 | SPDXVersion: SPDX-2.1
2 | PackageName: LL-Smartcard
3 | PackageOriginator: MIT Lincoln Laboratory
4 | PackageHomePage: https://github.com/mit-ll/LL-Smartcard
5 | PackageLicenseDeclared: BSD-3-Clause
6 |
--------------------------------------------------------------------------------
/install_dependencies.sh:
--------------------------------------------------------------------------------
1 | echo "Installing pyDes..."
2 | wget http://twhiteman.netfirms.com/pyDES/pyDes-2.0.1.zip
3 | unzip pyDes-2.0.1.zip
4 | rm pyDes-2.0.1.zip
5 | cd pyDes-2.0.1
6 | sudo python setup.py install
7 |
8 | echo "Installing pyscard..."
9 | sudo apt-get install python-pyscard
10 |
11 | echo "Installing PC/SC"
12 | sudo apt-get install pcsc-tools pcscd
13 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from distutils.core import setup
4 |
5 | setup(name='LLSmartcard',
6 | version='1.0',
7 | description='Module for easily interacting with smartcards.',
8 | author='Chad Spensky',
9 | author_email='chad.spensky@ll.mit.eduasd',
10 | url='https://github.com/cspensky/llsmartcard',
11 | packages=['llsmartcard'],
12 | )
--------------------------------------------------------------------------------
/llsmartcard/helper.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
2 | # Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
3 | # SPDX-License-Identifier: BSD-3-Clause
4 |
5 | def write_binary(data, filename):
6 | """
7 | Write binary data to a file on disk
8 | """
9 |
10 | import struct
11 |
12 | # Create file and write to it
13 | f = open(filename, "wb+")
14 |
15 | f.write(struct.pack("%dB" % len(data), *data))
16 |
17 | f.close()
18 |
19 | def read_binary(filename):
20 | """
21 | Write binary data to a file on disk
22 | """
23 | import struct
24 |
25 | data = []
26 |
27 | # Create file and write to it
28 | f = open(filename, "rb")
29 |
30 | byte = f.read(1)
31 | while byte != b"":
32 |
33 | data.append(ord(byte))
34 |
35 | # Do stuff with byte.
36 | byte = f.read(1)
37 |
38 |
39 | f.close()
40 |
41 | return data
42 |
--------------------------------------------------------------------------------
/examples/template.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
2 | # Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
3 | # SPDX-License-Identifier: BSD-3-Clause
4 |
5 | """
6 | This a nice simple reference implementation when creating new smartcard
7 | programs using the LL-Smartcard API
8 | """
9 | # Navtive
10 | import logging
11 | import optparse
12 |
13 | # LL Smartcard
14 | from llsmartcard.card import SmartCard, VisaCard, CAC
15 |
16 | def process_card(connection, options):
17 | """
18 | Implement your function here
19 | """
20 |
21 | # Open card
22 | card = SmartCard(connection)
23 | #
24 | # DO SOMETHING HERE
25 | #
26 |
27 |
28 | if __name__ == "__main__":
29 |
30 | # Import our command line parser
31 | from llsmartcard import parser
32 | opts = optparse.OptionParser()
33 |
34 | # Add any options we want here
35 | opts.add_option("-s", "--sample", action="store_true",
36 | dest="sample", default=False,
37 | help="Sample")
38 |
39 | # parse user arguments
40 | parser.command_line(opts, process_card)
41 |
--------------------------------------------------------------------------------
/examples/fuzzing/admin_probe.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
3 | Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
4 | SPDX-License-Identifier: BSD-3-Clause
5 |
6 | This is just a simple program to test all instructions in a given class
7 | """
8 | # Navtive
9 | import logging
10 | import optparse
11 |
12 | # LL Smartcard
13 | import llsmartcard.apdu as APDU
14 | from llsmartcard.card import SmartCard, VisaCard, CAC
15 |
16 | # Globals
17 | log_level = logging.ERROR
18 |
19 | def process_card(connection, options):
20 | global log_level
21 |
22 | # Which CLA to probe?
23 | PROBE_CLA = 0xFF
24 |
25 | # Open card
26 | card = CAC(connection)
27 |
28 | print "Trying some interesting APDUs"
29 |
30 | for i in range(0xff+1):
31 | for j in range(0xff+1):
32 | apdu_data = APDU.READ_BINARY(i, j, CLA=PROBE_CLA)
33 | data, sw1, sw2 = card._send_apdu(apdu_data)
34 | if sw1 == 0x90:
35 | print "Success at %x %x"%(i,j)
36 | print data
37 |
38 | # apdu_data = APDU.GET_DATA(0x00, 0x01, CLA=0xFF)
39 | # card._send_apdu(apdu_data)
40 | if __name__ == "__main__":
41 |
42 | # Import our command line parser
43 | from llsmartcard import parser
44 | opts = optparse.OptionParser()
45 |
46 | # parse user arguments
47 | parser.command_line(opts, process_card)
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Massachusetts Institute of Technology (MIT)
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 | * Neither the name of the Massachusetts Institute of Technology nor the
12 | names of its contributors may be used to endorse or promote products
13 | derived from this software without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL MIT BE LIABLE FOR ANY
19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/examples/general/secure_channel.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
3 | Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
4 | SPDX-License-Identifier: BSD-3-Clause
5 |
6 | This program is meant to handle secure channel communications using default
7 | keys.
8 | """
9 | # Navtive
10 | import logging
11 | import optparse
12 |
13 | # LL Smartcard
14 | import llsmartcard.apdu as APDU
15 | from llsmartcard.card import CAC
16 |
17 | # Globals
18 | log_level = logging.ERROR
19 |
20 | def process_card(connection, options):
21 | """
22 | Implement your function here
23 | """
24 | global log_level
25 |
26 | # Open card
27 | card = CAC(connection, log_level=log_level)
28 |
29 | # Select GP Manager
30 | card.apdu_select_application(APDU.APPLET.SECURITY_GEMALTO)
31 | # Open our secure channel
32 | card.open_secure_channel(APDU.APPLET.SECURITY_GEMALTO, APDU.AUTH_KEYS.GEMALTO)
33 |
34 | # Try locking the card
35 | # card.apdu_set_status(APDU.SET_STATUS_PARAM.TYPE.SECURITY_DOMAIN,
36 | # APDU.SET_STATUS_PARAM.STATE_CARD.LOCKED)
37 |
38 |
39 | # List all applications
40 | card.print_applications()
41 |
42 |
43 | if __name__ == "__main__":
44 |
45 | # Import our command line parser
46 | from llsmartcard import parser
47 | opts = optparse.OptionParser()
48 |
49 | # Add any options we want here
50 | opts.add_option("-s", "--sample", action="store_true",
51 | dest="sample", default=False,
52 | help="Sample")
53 |
54 | # parse user arguments
55 | parser.command_line(opts, process_card)
56 |
--------------------------------------------------------------------------------
/llsmartcard/parser.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
2 | # Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
3 | # SPDX-License-Identifier: BSD-3-Clause
4 |
5 | # Native
6 | import logging
7 | import sys
8 | logger = logging.getLogger(__name__)
9 |
10 | # 3rd Party
11 | import smartcard
12 | from smartcard.System import readers
13 |
14 | def command_line(opts, callback, args=None):
15 |
16 | opts.add_option("-l", "--listreaders", action="store_true",
17 | dest="listreaders", default=False,
18 | help="List Available Readers")
19 |
20 | opts.add_option("-r", "--reader", action="store", type="int",
21 | dest="reader", default= -1,
22 | help="Reader number from --list or -1 for all.")
23 |
24 | opts.add_option("-d", "--debug", action="store_true",
25 | dest="debug", default=False,
26 | help="Enable DEBUG")
27 | # Get arguments
28 | (options, positionals) = opts.parse_args(args)
29 | # Enumerate Readers
30 | reader_list = readers()
31 |
32 | # Get our log level
33 | if options.debug:
34 | # log_level = logging.DEBUG
35 | logging.basicConfig(level=logging.DEBUG)
36 | else:
37 | logging.basicConfig()
38 |
39 | if len(reader_list) == 0:
40 | logger.error("No readers found.")
41 |
42 | if options.listreaders:
43 | print "Available readers: "
44 | for i in range(len(reader_list)):
45 | print " %d: %s" % (i, reader_list[i])
46 | return
47 |
48 | # Walk over all readers
49 | for i in range(len(reader_list)):
50 | if options.reader == i or options.reader < 0:
51 | try:
52 | # Connect to Reader
53 | print "Using: %s" % reader_list[i]
54 | connection = reader_list[i].createConnection()
55 | connection.connect()
56 | # process card in reader
57 | callback(connection, options)
58 | break
59 |
60 | except smartcard.Exceptions.CardConnectionException:
61 | print "ERROR: Couldn't connect to card in %s" % reader_list[i]
62 | sys.exit(0)
63 |
64 |
--------------------------------------------------------------------------------
/examples/general/pin_gemalto.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
3 | Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
4 | SPDX-License-Identifier: BSD-3-Clause
5 |
6 | This program will attempt PIN authentication, and the use the secure channel
7 | to reset the PN retry counter.
8 | """
9 | # Navtive
10 | import logging
11 | logger = logging.getLogger(__name__)
12 | import optparse
13 |
14 | # LL Smartcard
15 | import llsmartcard.apdu as APDU
16 | from llsmartcard.card import SmartCard
17 |
18 |
19 | def process_card(connection, options):
20 | """
21 | Implement your function here
22 | """
23 | global log_level
24 |
25 | # Open card
26 | card = SmartCard(connection)
27 |
28 |
29 | # Try sending a valid PIN
30 | PIN = "1234"
31 | PIN_DATA = []
32 | for c in PIN:
33 | PIN_DATA.append(ord(c))
34 |
35 | PIN_DATA2 = list(PIN_DATA)
36 | PIN_DATA2[0] = 0x30
37 |
38 | print "Set PIN data:"
39 | print PIN
40 | print PIN_DATA
41 |
42 |
43 | # try global pin
44 | print "TRYING GLOBAL PIN"
45 | (data, sw1, sw2) = card.apdu_verify_pin([], 0x00, 0)
46 | (data, sw1, sw2) = card.apdu_verify_pin(PIN_DATA, 0x00)
47 |
48 |
49 | print "\n\n\n"
50 |
51 |
52 | ## # Lock Card
53 | # for x in range(3):
54 | # (data, sw1, sw2) = card.apdu_verify_pin(PIN_DATA, 0x80)
55 | #
56 | # for x in range(3):
57 | # (data, sw1, sw2) = card.apdu_reset_retry_counter(PIN_DATA, 0x80, PIN_DATA)
58 |
59 |
60 | print "Opening Secure Channel"
61 |
62 | # Select GP Manager
63 | card.apdu_select_application(APDU.APPLET.SECURITY_GEMALTO)
64 | # Open our secure channel
65 | card.open_secure_channel(APDU.APPLET.SECURITY_GEMALTO,
66 | APDU.AUTH_KEYS.GEMALTO,
67 | security_level=APDU.SECURE_CHANNEL.MODE.NONE)
68 |
69 |
70 | # Reset our retry counter
71 | print "Resetting retry counter..."
72 | # card.apdu_change_reference_data(0x00, [], PIN_DATA, first=True)
73 | card.apdu_change_reference_data(0x80, PIN_DATA, PIN_DATA)
74 | card.apdu_reset_retry_counter(PIN_DATA, 0x80, PIN_DATA)
75 |
76 | # Print applications on the card
77 | print "Printing card applications..."
78 | card.print_applications()
79 |
80 |
81 | if __name__ == "__main__":
82 |
83 | # Import our command line parser
84 | from llsmartcard import parser
85 | opts = optparse.OptionParser()
86 |
87 | # parse user arguments
88 | parser.command_line(opts, process_card)
89 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | _ _ _____ _ _____ _
2 | | | | | / ____| | | / ____| | |
3 | | | | | _____| (___ _ __ ___ __ _ _ __| |_| | __ _ _ __ __| |
4 | | | | | |______\___ \| '_ ` _ \ / _` | '__| __| | / _` | '__/ _` |
5 | | |____| |____ ____) | | | | | | (_| | | | |_| |___| (_| | | | (_| |
6 | |______|______| |_____/|_| |_| |_|\__,_|_| \__|\_____\__,_|_| \__,_|
7 |
8 | Authors: Chad Spensky (chad.spensky@ll.mit.edu)
9 | Hongyi Hu (hongyi.hu@ll.mit.edu)
10 |
11 |
12 | # Contents
13 |
14 | * examples/
15 | Some example scripts on how to use the library to interact with various
16 | smartcards
17 |
18 | * docs/
19 | Contains some useful documents when working with smart cards that
20 | define some of the APDUs and RIDs.
21 |
22 | # Install
23 |
24 | * Install [pyDes](https://pypi.python.org/pypi/pyDes/) python library
25 |
26 | * Install [pyscard](http://pyscard.sourceforge.net/) python library
27 |
28 | * Install PC/SC
29 | >$ sudo apt-get install pcsc-tools pcscd
30 |
31 | * To install all of these just run:
32 | >$ ./install_dependencies.sh
33 |
34 |
35 | # Usage
36 |
37 | * For developing your own smart card application using llsmartcard, see
38 | template.py
39 |
40 | * See `examples/`
41 |
42 |
43 | # Certificates
44 |
45 | This section discusses how to work with the certificates on the CAC.
46 |
47 | * Extract Certificates
48 | python cac_crypto.py -x test
49 |
50 | * Working with certs (Referenced from [here](http://www.devco.net/archives/2006/02/13/public_-_private_key_encryption_using_openssl.php)).
51 |
52 | - Encrypt
53 | >$ openssl pkeyutl -encrypt -in -pubin -inkey [input public key] -out [output file]
54 |
55 | - Extract Public Key
56 | >$ openssl x509 -inform DER -pubkey -in [input certificate] > output.key
57 |
58 |
59 | * Example using certs:
60 |
61 | >$ echo "Hello World!" > input.txt
62 |
63 | >$ python cac_crypto.py -E -k test/cac/cac_pki_enc.pub -i input.txt -o input_encrypted.ssl
64 |
65 | >$ python cac_crypto.py -D -i input_encrypted.ssl -c KEY_PKI_ENC -o input_decrypted.txt -p 77777777
66 |
67 | # Notes
68 |
69 | * Certificates are returned in gzipped form.
70 | > $ gunzip [cert.gz]
71 |
72 | * Certificates are in DER form
73 | > $ openssl x509 -inform DER -in [cert]
74 | > $ openssl x509 -issuer -email -startdate -enddate -inform DER -in [cert]
75 | > $ openssl x509 -inform DER -noout -text -in [cert]
76 |
77 | # Citation
78 | Please use this DOI number reference, published on [Zenodo](https://zenodo.org), when citing the software:
79 | [](https://zenodo.org/badge/latestdoi/35278621)
80 |
81 | # Disclaimer
82 |
83 | This work is sponsored by the Defense Information Systems Agency under Air Force Contract #FA8721-05-C-0002. Opinions, interpretations, conclusions and recommendations are those of the author and are not necessarily endorsed by the United States Government.
84 |
85 | © 2015 Massachusetts Institute of Technology
86 |
87 |
--------------------------------------------------------------------------------
/examples/credit_cards/read_info.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
2 | # Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
3 | # SPDX-License-Identifier: BSD-3-Clause
4 |
5 | """
6 | This a nice simple reference implementation when creating new smartcard
7 | programs using the LL-Smartcard API
8 | """
9 | # Navtive
10 | import logging
11 | logger = logging.getLogger(__name__)
12 | import sys
13 | import optparse
14 |
15 | # LL Smartcard
16 | from llsmartcard.card import CreditCard
17 |
18 |
19 | # Service code decoder
20 | service_first = {
21 | '1': 'International interchange OK',
22 | '2': 'International interchange, use IC (chip) where feasible',
23 | '5': 'National interchange only except under bilateral agreement',
24 | '6': 'National interchange only except under bilateral agreement, use IC (chip) where feasible',
25 | '7': 'No interchange except under bilateral agreement (closed loop)',
26 | '9': 'Test'
27 | }
28 | service_second = {
29 | '0': 'Normal',
30 | '2': 'Contact issuer via online means',
31 | '4': 'Contact issuer via online means except under bilateral agreement'
32 | }
33 | service_third = {
34 | '0': 'No restrictions, PIN required',
35 | '1': 'No restrictions',
36 | '2': 'Goods and services only (no cash)',
37 | '3': 'ATM only, PIN required',
38 | '4': 'Cash only',
39 | '5': 'Goods and services only (no cash), PIN required',
40 | '6': 'No restrictions, use PIN where feasible',
41 | '7': 'Goods and services only (no cash), use PIN where feasible'
42 | }
43 |
44 |
45 | def process_card(connection, options):
46 | """
47 | Implement your function here
48 | """
49 |
50 | # Open card
51 | card = CreditCard(connection)
52 |
53 | # Select
54 | if options.card_type == "V":
55 | print "* Reading VISA card..."
56 | card.select_visa_applet()
57 | elif options.card_type == "M":
58 | print "* Reading MasterCard..."
59 | card.select_mastercard_applet()
60 | else:
61 | logger.error("Unrecognized card type.")
62 | return
63 | # card.dump_records()
64 |
65 | cc_info = card.read_card_info()
66 |
67 | if cc_info is not None:
68 | print "Bank card info: "
69 | print " * Name: %s %s"%(cc_info['first_name'],cc_info['last_name'])
70 | print " * Account #: %s"%(cc_info['account_number'])
71 | print " * Expiration: %s/%s"%(cc_info['exp_month'], cc_info['exp_year'])
72 | print " * Service: %s"%(service_first[cc_info['service_first']])
73 | print " %s"%(service_second[cc_info['service_second']])
74 | print " %s"%(service_third[cc_info['service_third']])
75 |
76 |
77 | if __name__ == "__main__":
78 |
79 | # Import our command line parser
80 | from llsmartcard import parser
81 | opts = optparse.OptionParser()
82 |
83 | # Add any options we want here
84 | opts.add_option("-t", "--card_type", action="store",
85 | dest="card_type", default="V",
86 | help="Type of card (V - VISA, M - MasterCard")
87 |
88 | # parse user arguments
89 | parser.command_line(opts, process_card)
90 |
91 |
92 |
--------------------------------------------------------------------------------
/examples/common_access_card/cac_info.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
3 | Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
4 | SPDX-License-Identifier: BSD-3-Clause
5 |
6 | This program dumps general information about a CAC card.
7 | """
8 | # Navtive
9 | import logging
10 | import optparse
11 |
12 | # LL Smartcard
13 | import llsmartcard.apdu as APDU
14 | from llsmartcard.card import CAC
15 |
16 | def process_card(connection, options):
17 | """
18 | Will dump all of the interesting information from a CAC card to standard
19 | out
20 |
21 | WARNING: The PIN verify command will be sent multiple times. If the
22 | PIN is wrong, it will lock your CAC card!
23 | """
24 |
25 | # Open card
26 | card = CAC(connection)
27 |
28 | # Set this to your PIN. Please be very careful with this!
29 | PIN = None #[0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37]
30 |
31 | # Print NIST PIV Objects
32 | print "Printing NIST PIV Objects..."
33 | card.print_object(APDU.APPLET.NIST_PIV,
34 | APDU.OBJ_NIST_PIV.CHUID)
35 | card.print_object(APDU.APPLET.NIST_PIV,
36 | APDU.OBJ_NIST_PIV.CCC)
37 | card.print_object(APDU.APPLET.NIST_PIV,
38 | APDU.OBJ_NIST_PIV.KEY_DIG_SIG)
39 | card.print_object(APDU.APPLET.NIST_PIV,
40 | APDU.OBJ_NIST_PIV.KEY_MNG)
41 | card.print_object(APDU.APPLET.NIST_PIV,
42 | APDU.OBJ_NIST_PIV.KEY_PIV_ATH)
43 | card.print_object(APDU.APPLET.NIST_PIV,
44 | APDU.OBJ_NIST_PIV.SEC_OBJ)
45 |
46 | # Print DOD PIV Object
47 | print "Printing DoD PIV Objects..."
48 | card.print_object(APDU.APPLET.DOD_PIV,
49 | APDU.OBJ_DOD_PIV.CCC)
50 |
51 | card.print_object(APDU.APPLET.DOD_PIV,
52 | APDU.OBJ_DOD_PIV.FNGR_PRNT,
53 | pix=APDU.PIX_CAC.PIV_TRNS_APLT)
54 | card.print_object(APDU.APPLET.DOD_PIV,
55 | APDU.OBJ_DOD_PIV.CHUID)
56 |
57 | # Print DOD CAC Objects
58 | print "Printing DoD CAC Objects..."
59 | card.print_object(APDU.APPLET.DOD_CAC,
60 | APDU.OBJ_DOD_CAC.KEY_PKI_ENC)
61 | card.print_object(APDU.APPLET.DOD_CAC,
62 | APDU.OBJ_DOD_CAC.KEY_PKI_ID)
63 | card.print_object(APDU.APPLET.DOD_CAC,
64 | APDU.OBJ_DOD_CAC.KEY_PKI_SIG)
65 |
66 |
67 | # Do we have a PIN to access authenticated information?
68 | if PIN is not None:
69 | print "Printing NIST PIV Objects... (PIN PROTECTED)"
70 | card.print_object(APDU.APPLET.NIST_PIV,
71 | APDU.OBJ_NIST_PIV.KEY_CRD_ATH,
72 | pin=PIN)
73 | card.print_object(APDU.APPLET.NIST_PIV,
74 | APDU.OBJ_NIST_PIV.FACE,
75 | pin=PIN)
76 | card.print_object(APDU.APPLET.NIST_PIV,
77 | APDU.OBJ_NIST_PIV.FNGR_P1,
78 | pin=PIN)
79 | card.print_object(APDU.APPLET.NIST_PIV,
80 | APDU.OBJ_NIST_PIV.FNGR_P2,
81 | pin=PIN)
82 |
83 | print "Printing DoD PIV Objects... (PIN PROTECTED)"
84 | card.print_object(APDU.APPLET.DOD_PIV,
85 | APDU.OBJ_DOD_PIV.SEC_OBJ,
86 | pix=APDU.PIX_CAC.PIV_TRNS_APLT,
87 | pin=PIN)
88 | card.print_object(APDU.APPLET.DOD_PIV,
89 | APDU.OBJ_DOD_PIV.FACE,
90 | pix=APDU.PIX_CAC.PIV_TRNS_APLT,
91 | pin=PIN)
92 |
93 | print "Printing DoD CAC Objects... (PIN PROTECTED)"
94 | card.print_object(APDU.APPLET.DOD_CAC,
95 | APDU.OBJ_DOD_CAC.CAC_PERSON,
96 | pin=PIN)
97 | card.print_object(APDU.APPLET.DOD_CAC,
98 | APDU.OBJ_DOD_CAC.CAC_PERSONEL,
99 | pin=PIN)
100 |
101 |
102 |
103 | if __name__ == "__main__":
104 |
105 | # Import our command line parser
106 | from llsmartcard import parser
107 | opts = optparse.OptionParser()
108 |
109 | # parse user arguments
110 | parser.command_line(opts, process_card)
111 |
--------------------------------------------------------------------------------
/examples/common_access_card/cac_crypto.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
3 | Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
4 | SPDX-License-Identifier: BSD-3-Clause
5 |
6 | This application was made to interact with the cryptographic functions
7 | on the CAC cards. While it does not folow our template format, the
8 | features of llsmartcard utilized are still relevant.
9 | """
10 | import sys
11 | import logging
12 | logger = logging.getLogger(__name__)
13 | import optparse
14 | import subprocess
15 | import os
16 | import struct
17 | from subprocess import call
18 |
19 | # 3rd Party
20 | import smartcard
21 | from smartcard.System import readers
22 | import llsmartcard.apdu as APDU
23 | import llsmartcard.helper as HELPER
24 | from llsmartcard.apdu import APPLET
25 |
26 | # LL Smartcard
27 | from llsmartcard.card import CAC
28 |
29 | CAC_KEYS = []
30 | CAC_APPLET_OBJECTS = APDU.OBJ_DOD_CAC
31 | for var in CAC_APPLET_OBJECTS.__dict__:
32 | if var.startswith("KEY"):
33 | CAC_KEYS.append(var)
34 |
35 | def extract_certs(card, path):
36 | """
37 | This function will extract all of the certificates froma CAC to a given directory
38 |
39 | @param card - SmartCard object
40 | @param path - Path to dump raw certs to
41 | """
42 |
43 | logger.info("Dumping Certificates from CAC...")
44 |
45 | try:
46 | os.makedirs(path)
47 | except:
48 | pass
49 |
50 | # Dump CHUID Cert
51 | cert_filename = os.path.join(path,
52 | "chuid.crt")
53 | card.extract_cert(APPLET.NIST_PIV,
54 | APDU.OBJ_NIST_PIV.CHUID,
55 | cert_filename)
56 |
57 | # Create our output directories
58 | nist_dir = os.path.join(path, "piv")
59 | cac_dir = os.path.join(path, "cac")
60 | try:
61 | os.makedirs(nist_dir)
62 | os.makedirs(cac_dir)
63 | except:
64 | pass
65 |
66 | """
67 | NIST PIV CERTS
68 | These are all gzipped and DER format
69 | """
70 | # Dig Sig
71 | nist_dig_sig = os.path.join(nist_dir, "nist_dig_sig.crt")
72 | card.save_nist_cert(APDU.OBJ_NIST_PIV.KEY_DIG_SIG, nist_dig_sig)
73 |
74 | # PIV Auth
75 | nist_auth = os.path.join(nist_dir, "nist_piv_auth.crt")
76 | card.save_nist_cert(APDU.OBJ_NIST_PIV.KEY_PIV_ATH, nist_auth)
77 |
78 | # Key Management
79 | nist_mng = os.path.join(nist_dir, "nist_mng.crt")
80 | card.save_nist_cert(APDU.OBJ_NIST_PIV.KEY_MNG, nist_mng)
81 |
82 | """
83 | DoD CAC Certs
84 | Unsure of the format of these...
85 | """
86 | # PKI Encryption Key (Same as NIST Key Mng Key)
87 | cac_enc = os.path.join(cac_dir, "cac_pki_enc.crt")
88 | card.extract_cert(APPLET.DOD_CAC,
89 | APDU.OBJ_DOD_CAC.KEY_PKI_ENC,
90 | cac_enc)
91 | # PKI ID key
92 | cac_id = os.path.join(cac_dir, "cac_pki_id.crt")
93 | card.extract_cert(APPLET.DOD_CAC,
94 | APDU.OBJ_DOD_CAC.KEY_PKI_ID,
95 | cac_id)
96 | # PKI Dig Sig
97 | cac_pki_sig = os.path.join(cac_dir, "cac_pki_sig.crt")
98 | card.extract_cert(APPLET.DOD_CAC,
99 | APDU.OBJ_DOD_CAC.KEY_PKI_SIG,
100 | cac_pki_sig)
101 |
102 | # Symbolically link the identical keys (CAC End-Piont Implementation Guide v1.22)
103 | subprocess.Popen(["ln", "-s", "-f", "../../" + nist_dig_sig + ".pub", cac_pki_sig + ".pub"])
104 | subprocess.Popen(["ln", "-s", "-f", "../../" + nist_mng + ".pub", cac_enc + ".pub"])
105 |
106 |
107 | def main(args=None):
108 |
109 | opts = optparse.OptionParser()
110 |
111 | opts.add_option("-r", "--reader", action="store", type="int",
112 | dest="reader", default= -1,
113 | help="Reader number from --list or -1 for all.")
114 |
115 | opts.add_option("-R", "--listreaders", action="store_true",
116 | dest="listreaders", default=False,
117 | help="List Available Readers")
118 |
119 | opts.add_option("-E", "--encrypt", action="store_true",
120 | dest="encrypt", default=False,
121 | help="Do a public key encryption.")
122 |
123 | opts.add_option("-D", "--decrypt", action="store_true",
124 | dest="decrypt", default=False,
125 | help="SIGN/DECRYPT using the smartcard.")
126 |
127 | opts.add_option("-S", "--signd", action="store_true",
128 | dest="sign", default=False,
129 | help="SIGN/DECRYPT using the smartcard.")
130 |
131 |
132 | opts.add_option("-d", "--debug", action="store_true",
133 | dest="debug", default=False,
134 | help="Enable DEBUG")
135 |
136 | opts.add_option("-x", "--certs", action="store", type="string",
137 | dest="savecerts", default=None,
138 | help="Extract all of the certificates to specified directory.")
139 |
140 | opts.add_option("-i", "--input", action="store", type="string",
141 | dest="input", default=None,
142 | help="Input file.")
143 |
144 | opts.add_option("-o", "--output", action="store", type="string",
145 | dest="output", default=None,
146 | help="Output file.")
147 |
148 | opts.add_option("-k", "--pubkey", action="store", type="string",
149 | dest="pubkey", default=None,
150 | help="Public key to use for crytographic operations.")
151 |
152 | opts.add_option("-c", "--cert", action="store", type="string",
153 | dest="cert", default=None,
154 | help="Certificate to use for SIGN/DECRYPT command. %s" % CAC_KEYS)
155 |
156 | opts.add_option("-p", "--pin", action="store", type="string",
157 | dest="pin", default=None,
158 | help="PIN for the CAC card. (WARNING: 3 failed attempts will lock the card.)")
159 |
160 | (options, positionals) = opts.parse_args(args)
161 |
162 | # List our readers
163 | reader_list = readers()
164 | if options.listreaders:
165 | print "Available readers: "
166 | for i in range(len(reader_list)):
167 | print " %d: %s" % (i, reader_list[i])
168 | return
169 |
170 | # Set our logging level
171 | log_level = logging.ERROR
172 | if options.debug:
173 | log_level = logging.DEBUG
174 |
175 | for i in range(len(reader_list)):
176 | if options.reader == i or options.reader < 0:
177 | try:
178 | print "Using: %s" % reader_list[i]
179 |
180 | connection = reader_list[i].createConnection()
181 | connection.connect()
182 | card = CAC(connection, log_level=log_level)
183 |
184 | # Enter the PIN to use for authorized APDUs
185 | PIN = [0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37]
186 | if options.pin is not None:
187 | PIN = []
188 | for a in range(len(options.pin)):
189 | PIN.append(ord(options.pin[a]))
190 |
191 | # What function are we performing?
192 | if options.savecerts is not None:
193 | extract_certs(card, options.savecerts)
194 |
195 | # Encrypt a file using a public key?
196 | if options.encrypt:
197 | # Check params
198 | if options.input is None:
199 | print "ERROR: No input file given."
200 | opts.print_usage()
201 | sys.exit()
202 | if options.pubkey is None:
203 | print "ERROR: No public key file given."
204 | opts.print_usage()
205 | sys.exit()
206 | if options.output is None:
207 | print "ERROR: No output file given."
208 | opts.print_usage()
209 | sys.exit()
210 |
211 | # Use openssl
212 | call(["openssl", "pkeyutl", "-encrypt",
213 | "-in", options.input,
214 | "-pubin",
215 | "-inkey", options.pubkey,
216 | "-out", options.output])
217 |
218 | print "Encrypted %s using %s -> %s." % (options.input,
219 | options.pubkey,
220 | options.output)
221 |
222 | if options.decrypt or options.sign:
223 | # Check params
224 | if options.input is None:
225 | print "ERROR: No input file given."
226 | opts.print_usage()
227 | sys.exit()
228 | if options.cert is None:
229 | print "ERROR: No CAC certificate selected."
230 | opts.print_usage()
231 | sys.exit()
232 | if options.output is None:
233 | print "ERROR: No output file given."
234 | opts.print_usage()
235 | sys.exit()
236 | if options.cert not in CAC_KEYS:
237 | print "ERROR: not valid key selected."
238 | opts.print_usage()
239 | sys.exit()
240 | if options.pin is None or len(PIN) < 4:
241 | print "ERROR: No PIN given to authenticate to card."
242 | opts.print_usage()
243 | sys.exit()
244 |
245 | # VERIFY PIN
246 | logger.info("Verfying PIN...")
247 | data, sw1, sw2 = card.apdu_verify_pin(PIN, 0x00)
248 |
249 | # Select CAC Applet
250 | logger.info("Selecting CAC Applet...")
251 | card.apdu_select_application(APDU.APPLET.DOD_CAC)
252 |
253 | # Select appropriate key
254 | logger.info("Selecting appropriate key...")
255 | cur_key = CAC_APPLET_OBJECTS.__dict__[options.cert]
256 | card.apdu_select_object(cur_key)
257 |
258 | # Read input
259 | sign_data = HELPER.read_binary(options.input)
260 |
261 | data, sw1, sw2 = card.apdu_sign_decrypt(sign_data)
262 |
263 | HELPER.write_binary(data, options.output)
264 |
265 | print "Decrypted %s -> %s." % (options.input, options.output)
266 |
267 | for i in range(len(data)):
268 | if data[i] == 0x00 and i != 0:
269 | print "ASCII: %s" % APDU.get_str(data[i:-1])
270 |
271 |
272 | except smartcard.Exceptions.CardConnectionException as ex:
273 | print "ERROR: Couldn't connect to card in %s" % reader_list[i]
274 |
275 |
276 | if __name__ == "__main__":
277 | main()
278 |
--------------------------------------------------------------------------------
/examples/fuzzing/smartcard_fuzzer.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
3 | Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
4 | SPDX-License-Identifier: BSD-3-Clause
5 |
6 | This application was made to enumerate all of the APDUs on a card
7 |
8 | This specific example first selects the CAC's PIV applet before fuzzing.
9 | """
10 |
11 | # Native
12 | import sys
13 | import logging
14 | #logging.basicConfig(level=logging.DEBUG)
15 |
16 | # 3rd party (PyScard)
17 | from smartcard.System import readers
18 | from smartcard.sw.ISO7816_4ErrorChecker import ISO7816_4ErrorChecker
19 | from smartcard.sw.ISO7816_8ErrorChecker import ISO7816_8ErrorChecker
20 | from smartcard.sw.ISO7816_9ErrorChecker import ISO7816_9ErrorChecker
21 | from smartcard.sw.ErrorCheckingChain import ErrorCheckingChain
22 | from smartcard.sw.SWExceptions import SWException
23 |
24 | # LL Smartcard
25 | import llsmartcard.apdu as APDU
26 | from llsmartcard.apdu import APDU_STATUS, APPLET
27 | from llsmartcard.card import SmartCard, CAC
28 |
29 | # Setup our error chain
30 | errorchain = []
31 | errorchain = [ ErrorCheckingChain(errorchain, ISO7816_9ErrorChecker()),
32 | ErrorCheckingChain(errorchain, ISO7816_8ErrorChecker()),
33 | ErrorCheckingChain(errorchain, ISO7816_4ErrorChecker()) ]
34 |
35 | # List of valid classes and insructions
36 | valid_cla = []
37 | valid_ins = []
38 |
39 | # Hash tables to help aggregate findings
40 | cla_sw_ins = {}
41 | sw_ins_cla = {}
42 |
43 | """
44 | Functions to support hash table insertion
45 | """
46 | def insert_success(cla, ins, p1, p2, sw1, sw2):
47 | """
48 | Insert a succesful response into our valid list
49 | """
50 | global valid_ins
51 | valid_ins.append((cla, ins, (p1, p2), (sw1, sw2)))
52 | successful_apdu = "%04s %04s %04s %04s %04s %04s" % (hex(cla),
53 | hex(ins),
54 | hex(p1),
55 | hex(p2),
56 | hex(sw1),
57 | hex(sw2)
58 | )
59 | print "Got Success: %s" % successful_apdu
60 |
61 |
62 | def insert_trial(cla, ins, sw1, sw2):
63 | """
64 | Insert a trial with status word response into our structures
65 | """
66 | global cla_sw_ins, sw_ins_cla
67 |
68 | sw = sw1 << 8 | sw2
69 |
70 | # Depth = 1
71 | if cla not in cla_sw_ins:
72 | cla_sw_ins[cla] = {}
73 | if sw not in sw_ins_cla:
74 | sw_ins_cla[sw] = {}
75 |
76 | # Depth = 2
77 | if ins not in sw_ins_cla[sw]:
78 | sw_ins_cla[sw][ins] = []
79 | if ins not in cla_sw_ins[cla]:
80 | cla_sw_ins[cla][sw] = []
81 |
82 | # Add the nugget
83 | sw_ins_cla[sw][ins].append(cla)
84 | cla_sw_ins[cla][sw].append(ins)
85 |
86 |
87 | """
88 | Functions to handle output
89 | """
90 | def open_file(filename):
91 | """
92 | Open the given filename for writing or default to standard out
93 | """
94 | if filename is None:
95 | output = sys.stdout
96 | else:
97 | try:
98 | output = open(filename, "w+")
99 | except:
100 | logging.error("Couldn't open %s." % filename)
101 | output = sys.stdout
102 |
103 | return output
104 |
105 | def print_cla_sw_ins(filename=None):
106 | """
107 | Print CLAss, Status Word, INStruction into a tab-delimited file
108 | """
109 | output = open_file(filename)
110 |
111 | output.write("%04s\t%06s\t%s\n" % ("CLA", "SW", "INS(s)"))
112 | for cla in cla_sw_ins:
113 | for sw in cla_sw_ins[cla]:
114 | output.write("%04s\t%06s\t" % (hex(cla), hex(sw)))
115 | for ins in cla_sw_ins[cla][sw]:
116 | output.write("%s " % hex(ins))
117 | output.write("\n")
118 |
119 | if output != sys.stdout:
120 | output.close()
121 |
122 |
123 |
124 | def print_sw_ins_cla(filename=None):
125 | """
126 | Print Status Word, INStruction, CLAss into a tab-delimited file
127 | """
128 | output = open_file(filename)
129 |
130 | output.write("%06s\t%04s\t%s\n" % ("SW", "INS", "CLA(s)"))
131 | for sw in sw_ins_cla:
132 | for ins in sw_ins_cla[sw]:
133 | output.write("%06s\t%04s\t" % (hex(sw), hex(ins)))
134 | for cla in sw_ins_cla[sw][ins]:
135 | output.write("%s " % hex(cla))
136 | output.write("\n")
137 |
138 | if output != sys.stdout:
139 | output.close()
140 |
141 | def print_success(filename=None):
142 | """
143 | Print all successful responses to filename
144 | """
145 |
146 | output = open_file(filename)
147 |
148 | output.write("%04s %04s %04s %04s %04s %04s\n" % ("CLA", "INS", "P1", "P2", "SW1", "SW2"))
149 |
150 | for valid in valid_ins:
151 | (cla, ins, (p1, p2), (sw1, sw2)) = valid
152 | successful_apdu = "%04s %04s %04s %04s %04s %04s" % (hex(cla),
153 | hex(ins),
154 | hex(p1),
155 | hex(p2),
156 | hex(sw1),
157 | hex(sw2)
158 | )
159 | output.write(successful_apdu + "\n")
160 |
161 | if output != sys.stdout:
162 | output.close()
163 |
164 | """
165 | Functions for interacting with the card
166 | """
167 | def send_apdu(card, apdu_to_send):
168 | """
169 | Send an APDU to the card, and hadle errors appropriately
170 | """
171 | str = "Trying : ", [hex(i) for i in apdu_to_send]
172 | logging.debug(str)
173 | try:
174 | (data, sw1, sw2) = card._send_apdu(apdu_to_send)
175 | errorchain[0]([], sw1, sw2)
176 |
177 | except SWException, e:
178 | # Did we get an unsuccessful attempt?
179 | logging.info(e)
180 | except:
181 | logging.warn("Oh No! Pyscard crashed...")
182 | (data, sw1, sw2) = ([], 0xFF, 0xFF)
183 |
184 | str = "Got : ", data, hex(sw1), hex(sw2)
185 | logging.debug(str)
186 |
187 | return (data, sw1, sw2)
188 |
189 | def fuzzer(card, args=None):
190 | """
191 | Enumerate all valid classes, and brute force all instructions on those
192 | classes, recording the results
193 | """
194 | # First, determine all possible valid command classes
195 | print "Enumerating valid classes..."
196 | for cla in range(0xFF + 1):
197 | # CLS INS P1 P2
198 | apdu_to_send = [cla, 0x00, 0x00, 0x00]
199 |
200 | (data, sw1, sw2) = send_apdu(card, apdu_to_send)
201 |
202 | # unsupported class is 0x6E00
203 | if (sw1 == 0x6E) and (sw2 == 0x00):
204 | continue
205 | else:
206 | valid_cla.append(cla)
207 |
208 | # Print our valid classes
209 | print "Found %d valid command classes: " % len(valid_cla),
210 | for cla in valid_cla:
211 | print "%s" % hex(cla),
212 | print ""
213 |
214 | # Try our best not to lock up the card
215 | BAD_INSTRUCTIONS = [APDU.APDU_CMD.VERIFY, APDU.APDU_CMD.CHANGE_REF_DATA]
216 |
217 | # Next, try all possible instruction values for each valid command class
218 | print "Brute forcing every command for each class..."
219 | for cla in range(0xFF + 1):
220 | for ins in range(0xFF + 1):
221 | if ins in BAD_INSTRUCTIONS:
222 | continue
223 |
224 | # Start our parameters at 0x00
225 | p1 = 0x00
226 | p2 = 0x00
227 |
228 | # CLS INS P1 P2
229 | apdu_to_send = [cla, ins, p1, p2]
230 |
231 | # Send APDU
232 | (data, sw1, sw2) = send_apdu(card, apdu_to_send)
233 |
234 | # What values do we consider a success?
235 | SUCCESS_LIST = [0x90, # Success
236 | 0x61, # More Data
237 | 0x67, # Wrong Length
238 | 0x6c, # Wrong Length
239 | 0x6a, # Referenced Data not found
240 | # 0x69 # Access Violation (Not sure about this)
241 | ]
242 |
243 | SUCCESS_BAD_PARAM = [(0x6a, 0x86) #Incorrect Paramters
244 | ]
245 | SUCCESS_FAIL = [(0x6a, 0x81) # Funciton not supported
246 | ]
247 |
248 | # Success?
249 | if sw1 in SUCCESS_LIST:
250 | if (sw1, sw2) not in SUCCESS_FAIL:
251 | insert_success(cla, ins, p1, p2, sw1, sw2)
252 |
253 | # Check to see if the command was "successful" and tweak permissions
254 | # until we get a 0x9000
255 | if (sw1, sw2) in SUCCESS_BAD_PARAM:
256 | logging.info("Got partial success, trying to find proper parameters..")
257 |
258 | # # Brute force Parameters
259 | # for p1 in range(0xff + 1):
260 | # for p2 in range(0xff + 1):
261 | #
262 | # # CLS INS P1 P2
263 | # apdu_to_send = [cla, ins, p1, p2]
264 | #
265 | # # Send APDU
266 | # (data, sw1, sw2) = send_apdu(apdu_to_send)
267 | #
268 | # # Check status
269 | # if sw1 in SUCCESS_LIST:
270 | # valid_ins.append((cla, ins, (p1, p2), (sw1, sw2)))
271 |
272 |
273 |
274 | # unsupported command is 0x6d00
275 | # if (sw1 == 0x6d) and (sw2 == 0x00):
276 | # continue
277 | # if (sw1, sw2) not in [(0x6d, 0x00), (0x68, 0x84)]:
278 | # valid_ins.append((cla, ins))
279 |
280 | # Add response to hash tables
281 | insert_trial(cla, ins, sw1, sw2)
282 |
283 | print "Found %d valid instructions." % len(valid_ins)
284 |
285 | print "Saving results..."
286 |
287 | print_cla_sw_ins("cla_sw_ins.tsv")
288 | print_sw_ins_cla("sw_ins_cla.tsv")
289 | print_success("successes.txt")
290 |
291 | print "Done."
292 |
293 |
294 |
295 | if __name__ == "__main__":
296 | # get readers
297 | reader_list = readers()
298 |
299 | # Let the user the select a reader
300 | if len(reader_list) > 1:
301 | print "Please select a reader"
302 | idx = 0
303 | for r in reader_list:
304 | print " %d - %s"%(idx,r)
305 | idx += 1
306 |
307 | reader_idx = -1
308 | while reader_idx < 0 or reader_idx > len(reader_list)-1:
309 | reader_idx = int(raw_input("Reader[%d-%d]: "%(0,len(reader_list)-1)))
310 |
311 | reader = reader_list[reader_idx]
312 | else:
313 | reader = reader_list[0]
314 |
315 | print "Using: %s" % reader
316 |
317 | # create connection
318 | connection = reader.createConnection()
319 | connection.connect()
320 |
321 | # do stuff with CAC
322 | card = CAC(connection)
323 | card.select_nist_piv()
324 |
325 | # Call our fuzzer
326 | fuzzer(card)
327 |
328 |
--------------------------------------------------------------------------------
/llsmartcard/apdu.py:
--------------------------------------------------------------------------------
1 |
2 | """
3 | Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
4 | Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
5 | SPDX-License-Identifier: BSD-3-Clause
6 |
7 | Return codes
8 | ref: http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_6_basic_interindustry_commands.aspx
9 | ref: http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_7_transmission_interindustry_commands.aspx
10 | """
11 |
12 | import logging
13 |
14 | class AUTH_KEYS:
15 | # http://www.cryptoshop.com/products/smartcards/gemalto-idcore-10-gemalto-top-im-gx4.html?___store=english&___from_store=default
16 | GEMALTO = [
17 | [0x47, 0x45, 0x4D, 0x58, 0x50, 0x52, 0x45, 0x53, 0x53, 0x4F, 0x53, 0x41, 0x4D, 0x50, 0x4C, 0x45],
18 | [0x47, 0x45, 0x4D, 0x58, 0x50, 0x52, 0x45, 0x53, 0x53, 0x4F, 0x53, 0x41, 0x4D, 0x50, 0x4C, 0x45],
19 | [0x47, 0x45, 0x4D, 0x58, 0x50, 0x52, 0x45, 0x53, 0x53, 0x4F, 0x53, 0x41, 0x4D, 0x50, 0x4C, 0x45]
20 | ]
21 | GEMALTO_MODUS_VISA2 = [0xA0, 0x00, 0x00, 0x00, 0x18, 0x43, 0x4D, 0x00]
22 |
23 | class AUTH_KEY_IDX:
24 | AUTH = 0
25 | MAC = 1
26 | ENC = 2
27 |
28 | class SECURE_CHANNEL:
29 | class DIVERSIFY:
30 | VISA2 = 0x00
31 | class MODE:
32 | NONE = 0x00
33 | MAC = 0X01
34 | MAC_ENC = 03
35 |
36 |
37 | class SET_STATUS_PARAM:
38 | class TYPE:
39 | SECURITY_DOMAIN = 0b10000000
40 | APPLICATION = 0b01000000
41 |
42 | class STATE_CARD:
43 | # Card State
44 | OP_READY = 0b00000001
45 | INITIALIZED = 0b00000111
46 | SECURED = 0b00001111
47 | LOCKED = 0b01111111
48 | TERMINATED = 0b11111111
49 |
50 | class STATE_SEC_DOM:
51 | # Security Domain
52 | INSTALLED = 0b000011
53 | SELECTABLE = 0b00000111
54 | PERSONALIZED = 0b00001111
55 | LOCKED = 0b10000011
56 |
57 |
58 | class STATE_APP:
59 | # Application
60 | INSTALLED = 0b00000011
61 | LOCKED = 0b10000000
62 | UNLOCKED = 0b00000000
63 |
64 |
65 | class SEARCH_CRITERIA:
66 | AID = [0x4F, 0x00]
67 | # APDU Definitions
68 | class APDU_CMD:
69 | """
70 | Lookup class for ADPU command values
71 |
72 | Reference: http://www.informit.com/articles/article.aspx?p=29265&seqNum=6
73 | Reference: http://techmeonline.com/most-used-smart-card-commands-apdu/
74 | """
75 | # Administrative
76 | GET_RESPONSE = 0xC0
77 | MANAGE_CHANNEL = 0x70
78 | ENVELOPE = 0xC2
79 | GET_DATA = 0xCA
80 | PUT_DATA = 0xDA
81 | GET_STATUS = 0xF2
82 | SET_STATUS = 0xF0
83 | # Data
84 | SELECT = 0xA4
85 | READ_RECORD = 0xB2
86 | WRITE_RECORD = 0xD2
87 | APPEND_RECORD = 0xE2
88 | UPDATE_RECORD = 0xDC
89 | READ_BUFFER = 0x52
90 | GET_DATA_PIV = 0xCB
91 | READ_BINARY = 0xB0
92 | WRITE_BINARY = 0xD0
93 | UPDATE_BINARY = 0xD6
94 | ERASE_BINARY = 0x0E
95 | # Security
96 | INIT_UPDATE = 0x50
97 | VERIFY = 0x20
98 | RESET_RETRY = 0x2C
99 | CHANGE_REF_DATA = 0x24
100 | SIGN_DECRYPT = 0x42
101 | EXTERNAL_AUTH = 0x82
102 | INTERNAL_AUTH = 0x88
103 | GET_CHALLENGE = 0x84
104 |
105 |
106 | TEST_CLASSES = [0x00, 0xC0, 0xF0, 0x80, 0xBC, 0x01]
107 |
108 | class STATUS_WORDS:
109 | """
110 | Loockup class for common Status Words
111 | """
112 | SUCCESS = (0x90, 0x00)
113 | # Secure Channel
114 | AUTH_FAIL = (0x63, 0x00)
115 | NOT_FOUND = (0x6a, 0x88)
116 | COND_NOT_SATISFIED = (0x69, 0x85)
117 |
118 | # APDU Return Status Codes
119 | class APDU_STATUS:
120 | """
121 | Lookup class for common APDU SW1
122 | """
123 | MORE_DATA = 0x61
124 | WRONG_LENGTH = 0x6C
125 | SUCCESS = 0x90
126 |
127 | class PIX_CAC:
128 | """
129 | Lookup class for PIX addresses on the CAC
130 | """
131 | PKI_APLT = [0x01, 0x00]
132 | PKI_APLT2 = [0x01, 0x02]
133 | PKI_APLT3 = [0x01, 0x01]
134 | GC_APLT = [0x02, 0x00]
135 | GC_APLT2 = [0x02, 0x01]
136 | AXS_CTL_APLT = [0x01, 0x00]
137 |
138 | CCC = [0xDB, 0x00]
139 |
140 | PIV_TRNS_APLT = [0x30, 0x00]
141 |
142 | PIV_END_PNT = [0x00, 0x00, 0x10, 0x00, 0x01, 0x00]
143 |
144 | # Known Applet Identification Numbers
145 | class APPLET:
146 | # Credit Cards
147 | MASTERCARD = [0xA0, 0x00, 0x00, 0x00, 0x04, 0x10, 0x10]
148 | VISA = [0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10]
149 |
150 | # CAC
151 | NIST_PIV = [0xA0, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00] + [0x10, 0x00, 0x01, 0x00]
152 | DOD_PIV = [0xA0, 0x00, 0x00, 0x01, 0x16] #, 0xDB, 0x00]
153 | DOD_CAC = [0xA0, 0x00, 0x00, 0x00, 0x79] + [0x01, 0x00]
154 |
155 | # Other
156 | HELLO = [0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0x01]
157 |
158 | # Security Domains
159 | SECURITY_GEMALTO = [0xA0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00]
160 |
161 | class OBJ_NIST_PIV:
162 | # Ref: Cac End-Point Implementation Guide v1.22 / Page 33
163 | # Keys
164 | KEY_PIV_ATH = [0x5f, 0xc1, 0x05]
165 | KEY_DIG_SIG = [0x5f, 0xc1, 0x0A]
166 | KEY_MNG = [0x5f, 0xc1, 0x0B]
167 | KEY_CRD_ATH = [0x5f, 0xc1, 0x01]
168 | # Other
169 | CHUID = [0x5F, 0xC1, 0x02]
170 | CCC = [0x5F, 0xC1, 0x07]
171 | SEC_OBJ = [0x5f, 0xc1, 0x06]
172 | # Biometrics
173 | FNGR_P1 = [0x5F, 0xC1, 0x03]
174 | FNGR_P2 = [0x5F, 0xC1, 0x04]
175 | FACE = [0x5F, 0xC1, 0x08]
176 |
177 | class OBJ_DOD_PIV:
178 | # Ref: Cac End-Point Implementation Guide v1.22 / Page 33
179 | # Keys
180 | # KEY_PIV_ATH = [0xA0, 0x01]
181 | # KEY_DIG_SIG = [0x01, 0x00]
182 | # KEY_MNG = [0x01, 0x02]
183 | # KEY_CRD_ATH = [0x05, 0x00]
184 | # Other
185 | CHUID = [0x30, 0x00]
186 | CCC = [0xDB, 0x00]
187 | SEC_OBJ = [0x90, 0x00]
188 | FACE = [0x60, 0x30]
189 | FNGR_PRNT = [0x60, 0x10]
190 |
191 | class OBJ_DOD_CAC:
192 | # Ref: Cac End-Point Implementation Guide v1.22 / Page 33
193 | # Keys
194 | KEY_PKI_SIG = [0x01, 0x01] # Mapped to PIV Key Mgmt Key & PIV Digital Sign Key
195 | KEY_PKI_ID = [0x01, 0x00]
196 | KEY_PKI_ENC = [0x01, 0x02] # Mapped to PIV Key Mgmt Key & PIV Digital Sign Key
197 | # Other
198 | CAC_PERSON = [0x02, 0x00]
199 | CAC_PERSONEL = [0x02, 0x01]
200 | ACCESS_CONTROL = [0x02, 0x01]
201 |
202 |
203 | # APDU Construction functions
204 |
205 | def SIGN_DECRYPT(data, CLA=0x80, P1=0x00, P2=0x00):
206 | """
207 | CLA INS P1 P2 Lc DATA Le
208 | P1 - 0b1000000 (more blocks to follow), or 0
209 | P2 - 0x00
210 | Lc - length of data
211 | Le - expected length of returned data
212 | """
213 | return [CLA, APDU_CMD.SIGN_DECRYPT, P1, P2] + [len(data)] + data + [0x00]
214 |
215 |
216 |
217 | def SELECT(data, CLA=0x00, P1=0x04, P2=0x00):
218 | """
219 | CLA INS P1 P2 Le DATA...
220 | P1 and P2: http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_6_basic_interindustry_commands.aspx#table58
221 | """
222 | return [CLA, APDU_CMD.SELECT, P1, P2] + [len(data)] + data + [0x00]
223 |
224 |
225 | def GET_DATA(P1, P2, CLA=0x80, Lc=0x00):
226 | """
227 | CLA INS P1 P2 Le
228 | Set Le to 0x00 then update when we get the return code
229 |
230 | @param P1: Most significant byte of address
231 | @param P2: Least significant byte of address
232 | @param CLA: Class
233 | @param Lc: Length to read
234 | """
235 | return [CLA, APDU_CMD.GET_DATA, P1, P2, Lc]
236 |
237 |
238 | def READ_BINARY(P1,P2, CLA=0x00, Lc=0x00):
239 | """
240 | CLA INS P1 P2 Le
241 |
242 | @param P1: If bit8=1 in P1, then bit7-6 are set to 0. bit3-1 of P1 are a short EF (Elementary File)
243 | @param P2: The offset of the first byte to be read in date units from the beginning of the file
244 | @param CLA: Class
245 | @param Lc: Length to read
246 | """
247 | return [CLA, APDU_CMD.READ_BINARY, P1, P2, Lc]
248 |
249 | def GET_DATA_PIV(address):
250 | """
251 | CLA INS P1 P2 Lc DATA Le
252 | Set Le to 0x00 then update when we get the return code
253 |
254 | @param address: Address of PIV object to read
255 | """
256 | P1 = 0x3F
257 | P2 = 0xFF
258 | tag_list = [0x5c, len(address)] + address
259 | Lc = len(tag_list)
260 | Le = 0x00
261 | return [0x00, APDU_CMD.GET_DATA_PIV, P1, P2, Lc] + tag_list + [Le]
262 |
263 |
264 | def READ_RECORD(P1, P2, CLA=0x00, Le=0x00):
265 | """
266 | CLA INS P1 P2 Le
267 | Set Le to 0x00 then update when we get the return code
268 |
269 | @param CLA: Class
270 | @param P1: Record Number
271 | @param P2: Reference Control (http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_6_basic_interindustry_commands.aspx#table36)
272 | @param Le: Bytes to read
273 | """
274 | return [CLA, APDU_CMD.READ_RECORD] + [P1, P2, Le]
275 |
276 |
277 | def READ_BUFFER(P1, P2, buffer_type, read_length=64, Lc=0x02, CLA=0x80):
278 | """
279 | CLA INS P1 P2 Lc DATA_FIELD Le
280 |
281 | @param P1: MSB of offset
282 | @param P2 LSB of offset
283 | @param buffer_type: 0x01 (Type-Length buffer), 0x02 (Value buffer)
284 | @param read_length: Number of bytes to read
285 | @param CLA: Class
286 | @return: byte list with constructed APDU command
287 |
288 | """
289 | return [CLA, APDU_CMD.READ_BUFFER] + [P1, P2] + [Lc] + [buffer_type, read_length]
290 |
291 |
292 | def INIT_UPDATE(P1, P2, challenge, CLA=0x80, Le=0x00):
293 | """
294 | CLA INS P1 P2 Lc DATA_FIELD Le
295 |
296 | @param P1: Key version number (Default: 0)
297 | @param P1: Key identifier (Default: 0)
298 | @param challenge: List of 8 bytes to be send as the Nonce
299 | @return: byte list with constructed APDU command
300 | """
301 | return [CLA, APDU_CMD.INIT_UPDATE] + [P1, P2] + [len(challenge)] + challenge + [Le]
302 |
303 | def EXTERNAL_AUTHENTICATE(P1, cryptogram, mac, P2=0x00, CLA=0x84, Le=0x00):
304 | """
305 | CLA INS P1 P2 Lc DATA_FIELD Le
306 |
307 | @param P1: Security Level: 0x00 - None, 0x01, C-MAC, 0x03, C-DECRYPTION and C-MAC
308 | @param P2: Always 0x00
309 | @param cryptogram: Host cryptogram to send to card
310 | @param mac: C-MAC for this APDU
311 | @return: byte list with constructed APDU command
312 | """
313 | Lc = len(cryptogram)
314 | return [CLA, APDU_CMD.EXTERNAL_AUTH] + [P1, P2] + [Lc] + cryptogram + mac + [Le]
315 |
316 | def GET_STATUS(P1, P2, search_criteria, Lc=None, CLA=0x80, Le=0x00):
317 | """
318 | CLA INS P1 P2 Lc DATA_FIELD Le
319 |
320 | @param P1: 80 - Issuer Security Domain
321 | 40 - Application Security Domain
322 | 20 - Executable Load Files only
323 | 10 - Executable Load Files and their Executable Modules only
324 | @param P2: 0bx0 - get all/first occurrence(s)
325 | 0bx1 - get next
326 | 0b0x - Response Structure 1
327 | 0b1x - Response Structure 2
328 | @param search_criteria: 4f00 used to indicated AID
329 | Reference: GP 2.1.1/ page 114
330 | """
331 | if Lc is None:
332 | Lc = len(search_criteria)
333 | return [CLA, APDU_CMD.GET_STATUS, P1, P2, Lc] + search_criteria + [ Le]
334 |
335 | def SET_STATUS(P1, P2, data, CLA=0x80):
336 | """
337 | CLA INS P1 P2 Lc DATA_FIELD Le
338 |
339 | @param P1: Status Type
340 | 0x80 Security Domain
341 | 0x40 Application
342 | @param P2: State Coapdu_ntrol
343 | 0x80 Locked
344 | 0x00 Unlocked
345 | (See Table 9-5)
346 | @param data: AID if setting application status
347 | @param CLA: 0x80 or 0x84
348 |
349 | Reference: GP 2.1.1/11.10 page 163
350 | """
351 | Le = 0
352 | return [CLA, APDU_CMD.SET_STATUS, P1, P2, len(data)] + data + [Le]
353 |
354 |
355 | def VERIFY_PIN(P2, PIN, P1=0x00, CLA=0x00):
356 | """
357 | @param pin: list of bytes (length 4-8 bytes)
358 | @param p1: 0x00 is only valid
359 | @param p2: Key location
360 | @return (data, sw1, sw2)
361 | """
362 | return [CLA, APDU_CMD.VERIFY, P1, P2, len(PIN)] + PIN
363 |
364 |
365 | def RESET_RETRY_COUNT(P1, P2, puk, new_pin, CLA=0x00):
366 | """
367 | @param puk: list of bytes (length 4-8 bytes)
368 | @param new_pin: list of bytes (length 4-8 bytes)
369 | @param p1: 0x00, 0x01, or 0x02
370 | @param p2: Key location
371 | @return (data, sw1, sw2)
372 |
373 | Reference: ISO 7816-4 8.5.9
374 | Refere SP800-73-3 Pat 2
375 | """
376 | data = []
377 | if puk != None:
378 | data += puk
379 | if new_pin is not None:
380 | data += new_pin
381 |
382 | return [CLA, APDU_CMD.RESET_RETRY, P1, P2, len(data)] + data
383 |
384 | def CHANGE_REFERENCE_DATA(P1, P2, old_pin, new_pin, CLA=0x00):
385 | """
386 | @param puk: list of bytes (length 4-8 bytes)
387 | @param new_pin: list of bytes (length 4-8 bytes)
388 | @param p1: 0x00, or 0x01 for the first time
389 | @param p2: Reference Data ID
390 | 0x00 - Global PIN
391 | 0x80- Application PIN
392 | 0x81 - Application PUK
393 | @return (data, sw1, sw2)
394 |
395 | Reference: ISO 7816-4 8.5.6
396 | """
397 | data = []
398 | data += old_pin
399 | data += new_pin
400 |
401 | return [CLA, APDU_CMD.CHANGE_REF_DATA, P1, P2, len(data)] + data
402 |
403 | def GET_RESPONSE(Le):
404 | """
405 | CLA INS P1 P2 Le
406 | """
407 | return [0x00, APDU_CMD.GET_RESPONSE, 0x00, 0x00, Le]
408 |
409 |
410 | # Supplemntary Functions
411 |
412 | def get_hex(input_list):
413 | """
414 | Convert a list of bytes into hex string
415 | """
416 | if input_list is None:
417 | return ""
418 | o = ""
419 | for i in input_list:
420 | o += (hex(i)) + " "
421 | return o[:-1]
422 |
423 |
424 | def get_str(input_list):
425 | """
426 | Convert list of bytes into a string
427 | """
428 | o = ""
429 | for i in input_list:
430 | o += (chr(i))
431 | return o
432 |
433 |
434 |
435 |
--------------------------------------------------------------------------------
/docs/Commands.java.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Commands.java
4 |
5 |
6 |
7 |
8 |
9 | |
10 |
11 | Commands.java
12 |
13 |
14 | |
15 | //---------------------------------------------------------------------------------------
16 | // Useful tools for decoding smart card commands
17 | // file: Commands.java
18 | // used by: ScanCmds.java
19 | // For comments see header of ScanCmds.java.
20 | //---------------------------------------------------------------------------------------
21 |
22 | package scpack;
23 |
24 | public class Commands {
25 | // coding and description of typical smart cards commands
26 | static final String CMDTABLE[] = {("0x00 0x44 ACTIVATE FILE ISO/IEC 7816-9"),
27 | ("0x00 0xE2 APPEND RECORD ISO/IEC 7816-4"),
28 | ("0x80 0x1E APPLICATION BLOCK EMV"),
29 | ("0x80 0x18 APPLICATION UNBLOCK EMV"),
30 | ("0xA0 0x24 CHANGE CHV GSM 11.11"),
31 | ("0x00 0x24 CHANGE REFERENCE DATA ISO/IEC 7816-8"),
32 | ("0x00 0xE0 CREATE FILE ISO/IEC 7816-9"),
33 | ("0x00 0x04 DEACTIVATE FILE ISO/IEC 7816-9"),
34 | ("0x00 0x44 DEACTIVATE FILE ISO/IEC 7816-9"),
35 | ("0x80 0xE4 DELETE OP"),
36 | ("0x00 0xE4 DELETE FILE ISO/IEC 7816-9"),
37 | ("0xA0 0x26 DISABLE CHV GSM 11.11"),
38 | ("0x00 0x26 DISABLE VERIFICATION REQUIREMENT ISO/IEC 7816-8"),
39 | ("0xA0 0x28 ENABLE CHV GSM 11.11"),
40 | ("0x00 0x28 ENABLE VERIFICATION REQUIREMENT ISO/IEC 7816-8"),
41 | ("0x00 0xC2 ENVELOPE ISO/IEC 7816-4"),
42 | ("0x00 0x0E ERASE BINARY ISO/IEC 7816-4"),
43 | ("0x00 0x82 EXTERNAL AUTHENTICATE ISO/IEC 7816-4"),
44 | ("0x80 0xAE GENERATE AUTHORISATION CRYPTOGRAM EMV"),
45 | ("0x00 0x46 GENERATE PUBLIC KEY PAIR ISO/IEC 7816-8"),
46 | ("0x00 0x84 GET CHALLENGE ISO/IEC 7816-4"),
47 | ("0x00 0xCA GET DATA ISO/IEC 7816-4"),
48 | ("0xA0 0xC0 GET RESPONSE GSM 11.11"),
49 | ("0x00 0xC0 GET RESPONSE ISO/IEC 7816-4"),
50 | ("0x80 0xF2 GET STATUS OP"),
51 | ("0xA0 0x32 INCREASE GSM 11.11"),
52 | ("0x80 0xE6 INSTALL OP"),
53 | ("0x00 0x88 INTERNAL AUTHENTICATE ISO/IEC 7816-4"),
54 | ("0xA0 0x04 INVALIDATE GSM 11.11"),
55 | ("0x80 0x82 ISSUER AUTHENTICATE EMV"),
56 | ("0x80 0xE8 LOAD OP"),
57 | ("0x00 0x70 MANAGE CHANNEL ISO/IEC 7816-4"),
58 | ("0x00 0x22 MANAGE SECURITY ENVIRONMENT ISO/IEC 7816-8"),
59 | ("0x00 0x82 MUTUAL AUTHENTICATE ISO/IEC 7816-8"),
60 | ("0x00 0x10 PERFORM SCQL OPERATION ISO/IEC 7816-7"),
61 | ("0x00 0x2A PERFORM SECURITY OPERATION ISO/IEC 7816-8"),
62 | ("0x00 0x12 PERFORM TRANSACTION OPERATION ISO/IEC 7816-7"),
63 | ("0x00 0x14 PERFORM USER OPERATION ISO/IEC 7816-7"),
64 | ("0x00 0xDA PUT DATA ISO/IEC 7816-4"),
65 | ("0x80 0xD8 PUT KEY OP"),
66 | ("0xA0 0xB0 READ BINARY GSM 11.11"),
67 | ("0x00 0xB0 READ BINARY ISO/IEC 7816-4"),
68 | ("0xA0 0xB2 READ RECORD GSM 11.11"),
69 | ("0x00 0xB2 READ RECORD ISO/IEC 7816-4"),
70 | ("0xA0 0x44 REHABILITATE GSM 11.11"),
71 | ("0x00 0x2C RESET RETRY COUNTER ISO/IEC 7816-8"),
72 | ("0xA0 0x88 RUN GSM ALGORITHM GSM 11.11"),
73 | ("0x00 0xA0 SEARCH BINARY ISO/IEC 7816-9"),
74 | ("0x00 0xA2 SEARCH RECORD ISO/IEC 7816-9"),
75 | ("0xA0 0xA2 SEEK GSM 11.11"),
76 | ("0xA0 0xA4 SELECT GSM 11.11"),
77 | ("0x00 0xA4 SELECT ISO/IEC 7816-4"),
78 | ("0x80 0xF0 SET STATUS OP"),
79 | ("0xA0 0xFA SLEEP GSM 11.11"),
80 | ("0xA0 0xF2 STATUS GSM 11.11"),
81 | ("0x00 0xFE TERMINATE CARD USAGE ISO/IEC 7816-9"),
82 | ("0x00 0xE6 TERMINATE DF ISO/IEC 7816-9"),
83 | ("0x00 0xE8 TERMINATE EF ISO/IEC 7816-9"),
84 | ("0xA0 0x2C UNBLOCK CHV GSM 11.11"),
85 | ("0xA0 0xD6 UPDATE BINARY GSM 11.11"),
86 | ("0x00 0xD6 UPDATE BINARY ISO/IEC 7816-4"),
87 | ("0xA0 0xDC UPDATE RECORD GSM 11.11"),
88 | ("0x00 0xDC UPDATE RECORD ISO/IEC 7816-4"),
89 | ("0x00 0x20 VERIFY ISO/IEC 7816-4"),
90 | ("0x80 0x20 VERIFY EMV"),
91 | ("0xA0 0x20 VERIFY CHV GSM 11.11"),
92 | ("0x00 0xD0 WRITE BINARY ISO/IEC 7816-4"),
93 | ("0x00 0xD2 WRITE RECORD ISO/IEC 7816-4")};
94 |
95 | /** decode a smart card on the basis of class and instruction
96 | * @param cla class byte
97 | * @param ins instruction byte
98 | * @return string with the decoded command
99 | */
100 | public static String decodeCmd(int cla, int ins) {
101 | int i, n;
102 | String command = "", clains, s;
103 |
104 | clains = "0x" + ScanCmds.IntTo2CharHexString(cla);
105 | clains = clains + " 0x" + ScanCmds.IntTo2CharHexString(ins);
106 |
107 | for (i = 0; i < CMDTABLE.length; i++) {
108 | if (CMDTABLE[i].startsWith(clains) == true) command = CMDTABLE[i];
109 | } // for
110 |
111 | return command;
112 | } // decodeCmd
113 |
114 | } // class
115 | //---------------------------------------------------------------------------------------
116 |
117 |
118 |
119 |
120 |
121 |
122 | |
123 |
124 | Commands.java
126 | |
--------------------------------------------------------------------------------
/docs/RID_list.txt:
--------------------------------------------------------------------------------
1 | # RID; Applicant; Country
2 | A000000001; PBS Danmønt A/S; Denmark
3 | A000000002; Mondex International Ltd.; UK
4 | A000000003; Visa International; US
5 | A000000004; Mastercard International; US
6 | A000000005; Switch Card Services Ltd.; UK
7 | A000000006; Cardintell S.A.; Switzerland
8 | A000000007; National Westminster Bank Plc; UK
9 | A000000008; GyD Iberica; Spain
10 | A000000009; ETSI; France
11 | A000000010; Europay International; Belgium
12 | A000000011; TSB Bank Plc; UK
13 | A000000012; BT; UK
14 | A000000013; OTB Card Company Limited; Hong Kong
15 | A000000014; Touch Technology Inc.; US
16 | A000000015; Pocket Key S.R.L.; Italy
17 | A000000016; Computercentrum C. van de Velden B:V (CCV); Netherlands
18 | A000000017; Veron SpA; Italy
19 | A000000018; GEMPLUS; France
20 | A000000019; ID Data Systems Ltd.; UK
21 | A000000020; Laurel Intelligent Systems Co. Ltd.; Japan
22 | A000000021; National CacheCard Co.; US
23 | A000000022; G.I.E. SESAM-VITALE; France
24 | A000000023; ATA/IATA; US
25 | A000000024; Midland Bank Plc; UK
26 | A000000025; American Express; UK
27 | A000000026; Nationwide Building Society; UK
28 | A000000027; OSI Plus Corporation; Japan
29 | A000000028; MONET+; Czech Republic
30 | A000000029; LINK Interchange Network Ltd; UK
31 | A000000030; Schlumberger Industries Identif d'Encarteur PR050; France
32 | A000000031; Kokusai Denshin Denwa Co., Ltd.; Japan
33 | A000000032; De Post Belgie; Belgium
34 | A000000033; Banque Carrefour de la Securite Sociale; Belgium
35 | A000000034; Bank of Scotland; UK
36 | A000000035; Sistema 4B; Spain
37 | A000000036; The Co-operative Bank; UK
38 | A000000037; SEIS; Sweden
39 | A000000038; Delhaize De Leeuw; Belgium
40 | A000000039; Acorn Group; UK
41 | A000000040; ICL; US
42 | A000000041; Timelox AB; Sweden
43 | A000000042; Groupement des Cartes Bancaires "CB"; France
44 | A000000043; Eclipse Medi Save America Corp.; US
45 | A000000044; GSS (Global Specification for Short Range Communication); Germany
46 | A000000045; Barclays; UK
47 | A000000046; Transtex S.A.; Argentina
48 | A000000047; Security Dynamics Techn. Inc.; Sweden
49 | A000000048; CARDAG, Karten und Technologie; Switzerland
50 | A000000049; Clydesdale Bank P.L.C.; UK
51 | A000000050; Yorkshire Bank P.L.C.; UK
52 | A000000051; Northern Bank Limited; UK
53 | A000000052; Sainsbury Supermarkets Ltd. (ATC-payments); UK
54 | A000000053; Sani Group Italia S.R.L.; Italy
55 | A000000054; Alacarte Engineering; Belgium
56 | A000000055; BankomatCentralen AB; Sweden
57 | A000000056; Datakey, Inc.; US
58 | A000000057; Global Chipcard Alliance (GCA); US
59 | A000000058; AB InTech; Estonia
60 | A000000059; Zentraler Kreditausschuss (ZKA); Germany
61 | A000000060; Applied Card Technologies Ltd; UK
62 | A000000061; Mycal Card Incorporated; Japan
63 | A000000062; Sun Microsystems Inc.; US
64 | A000000063; RSA Laboratories; US
65 | A000000064; Schindler Elevator Corp.; US
66 | A000000065; JCB CO., LTD.; Japan
67 | A000000066; Secunet Security Networks GmbH; Germany
68 | A000000067; Iris Technologies Sdn. Bhd.; Malaysia
69 | A000000068; CardLogix Corporation; US
70 | A000000069; Société Européenne de Monnaie Electronique SEME; France
71 | A000000070; Prism Card Technologies; South Africa
72 | A000000071; Euro Information; France
73 | A000000072; Royal Bank of Canada; Canada
74 | A000000073; Netlink Consortium; France
75 | A000000074; Smart Card Solutions (Development Services) Ltd.; UK
76 | A000000075; Open Interactive Limited; UK
77 | A000000077; Oberthur Technologies; France
78 | A000000078; Welcome Real-Time; France
79 | A000000079; Activcard Europe S.A.; France
80 | A000000080; Smart TV; US
81 | A000000081; Worldcard (2000) Ltd.; UK
82 | A000000082; Union Internationale des Chemins de Fer; France
83 | A000000083; Percomad B.V.; Netherlands
84 | A000000084; Precise Biometrics AB; Sweden
85 | A000000085; USIS Management Inc.; US
86 | A000000086; Strålfors AB; Sweden
87 | A000000087; Third Generation Partnership Project (3GPP); France
88 | A000000088; Buypass AS; Norway
89 | A000000089; American Association of Motor Vehicle Administrators; US
90 | A000000090; Smart Card Solutions, LIC; US
91 | A000000091; Cybermark; US
92 | A000000092; Mobile-Mind; US
93 | A000000093; Ask S.A.; France
94 | A000000094; MULTOS Promotion Association; Japan
95 | A000000095; Incard S.p.A.; Italy
96 | A000000096; Sa Proton World International N.V.; Belgium
97 | A000000097; CIC GLOBAL; New Zealand
98 | A000000098; Visa USA; US
99 | A000000099; Cryptocard Corporation; Canada
100 | A000000100; Net Time Corp.; Japan
101 | A000000101; New Financial Systems, Services & Products, Dept. Financial Systems Division, Hitachi Ltd.; Japan
102 | A000000102; Syntactics Limited; UK
103 | A000000103; Catuity Incorporated; US
104 | A000000104; Israel Credit Cards Ltd.; Israel
105 | A000000105; Digital Pockets, LLC; US
106 | A000000106; Protective Technology; Norway
107 | A000000107; HSB Cards & Card Systems B.V.; Netherlands
108 | A000000108; AETeurope b.v.; Netherlands
109 | A000000109; Citicorp Development Center Inc.; US
110 | A000000110; The Boots Company PLC; UK
111 | A000000111; Die Post Postfinance; Switzerland
112 | A000000112; Lironic, INC.; US
113 | A000000113; Datacard Platform Seven Limited; UK
114 | A000000114; Sila Communications Scandinavia Aps; Denmark
115 | A000000115; NTT Communications Corporation; Japan
116 | A000000116; GSA - TFCS; US
117 | A000000117; Airtel Movil S.A.; Spain
118 | A000000118; Austria Card; Austria
119 | A000000119; Logico Smartcard Solutions GmbH.; Austria
120 | A000000120; PBS Data A/S; Denmark
121 | A000000121; PBS Danmark A/S; Denmark
122 | A000000122; Humetrix.com, Inc.; US
123 | A000000123; Smartcard EZ.com, Inc.; US
124 | A000000124; The Brodie Group; US
125 | A000000125; Department of Veterans Affairs; US
126 | A000000126; Shonkh Technologies International Limited; India
127 | A000000127; SecureNet; Australia
128 | A000000128; Infineer Ltd; UK
129 | A000000129; Carde Systems Inc.; Canada
130 | A000000130; Global Consumer Technologies, Inc.; Canada
131 | A000000131; Mxi Technologies Ltd.; Canada
132 | A000000132; Java Card Forum; US
133 | A000000133; St. Geroge Bank Australia; Australia
134 | A000000134; Smartix International Corporation; US
135 | A000000135; Goldkey Technology Corporation; Taiwan
136 | A000000136; Worldsmart Technology; Australia
137 | A000000137; BZA Bargeldlose Zahlungs- und Abrechnungssysteme GmbH; Germany
138 | A000000138; Acer Internet Services, Inc.; Taiwan
139 | A000000139; Japan Point Anex Co., Ltd.; Japan
140 | A000000140; TDS TODOS DATA SYSTEM AB; Sweden
141 | A000000141; Associazione Bancaria Italiana; Italy
142 | A000000142; Toppan Printing Co. Ltd.; Japan
143 | A000000143; ANZ Banking Group; Australia
144 | A000000144; Multos Consortium; UK
145 | A000000145; Yokosuka City; Japan
146 | A000000146; Keihin Electric Express Railway Co., Ltd.; Japan
147 | A000000147; Defense Manpower Data Center; US
148 | A000000149; CISRA (Canon Research Systems Research Australia Pty Ltd); Australia
149 | A000000150; Smart Chip Limited ; India
150 | A000000151; GlobalPlatform, Inc.; US
151 | A000000152; Diners Club International Ltd.; US
152 | A000000153; National Credit Card Center of R.O.C.; Taiwan
153 | A000000154; Banrisul - Banco do Estado do Rio Grande do SUL - S.A.; Brazil
154 | A000000155; Deltaknot SDN. BHD.; Malaysia
155 | A000000156; HOB electronic GmbH & Co. KG; Germany
156 | A000000157; Zühlke Engineering AG; Switzerland
157 | A000000158; Cornish Key; UK
158 | A000000159; Hyweb Technology Co., Ltd.; Taiwan
159 | A000000160; Chinatrust Commercial Bank; Taiwan
160 | A000000161; Advanced Wave; UK
161 | A000000162; Europay (Switzerland) SA; Switzerland
162 | A000000163; Prepayment Cards Limited; UK
163 | A000000164; Athena Smartcard Ltd; UK
164 | A000000165; S-Card A/S; Denmark
165 | A000000166; Identix Incorporated; US
166 | A000000167; IBM; Germany
167 | A000000168; Dai Nippon Printing Co., Ltd. (DNP); Japan
168 | A000000169; Smart Chip Technologies, LLC; US
169 | A000000170; The Chinese Bank; Taiwan
170 | A000000171; Decru, Inc.; US
171 | A000000172; Financial Information Service Co. Ltd.; Taiwan
172 | A000000174; Sodexho-ccs; France
173 | A000000175; Panini Italia Srl; Italy
174 | A000000176; Banksys; Belgium
175 | A000000177; Ministère de L'Intérieur; Belgium
176 | A000000178; Europay Austria Zahlungsverkehrssysteme GmbH; Austria
177 | A000000179; Hong Kong University of Science and Technology; Hong Kong
178 | A000000180; Cheque Dejeuner; France
179 | A000000181; BGP E-Services S.A.; Belgium
180 | A000000182; Thalys International; Belgium
181 | A000000183; Changent Systems Inc.; US
182 | A000000184; DSI Direccao dos Servicos de Identificacao; Macao SAR
183 | A000000185; Post Office Limited; UK
184 | A000000186; Oberthur Card systems Ltd.; UK
185 | A000000187; Privacom B.V.; Netherlands
186 | A000000188; Diners Club Switzerland Ltd; Switzerland
187 | A000000189; Consolidated Computer Services; Australia
188 | A000000190; Cardis Research & Development; Israel
189 | A000000191; Syscom Technologies Limited; India
190 | A000000192; Syscom Corporation; India
191 | A000000193; AccessArena AG; Switzerland
192 | A000000194; Logos Smart Card A/S; Denmark
193 | A000000195; Amity Software Inc.; US
194 | A000000196; Capita Business Services Ltd; UK
195 | A000000197; European Committee for Standardization - CEN; Belgium
196 | A000000198; Israeli Ministry of Treasure; Israel
197 | A000000199; Ichikawa City; Japan
198 | A000000200; NonProfit Organization Ichikawa Life Network Club; Japan
199 | A000000201; Funabashi City; Japan
200 | A000000202; Kishware Co.; Iran
201 | A000000203; National Australia Group Europe Ltd.; UK
202 | A000000204; Association for Payment Clearing Services; UK
203 | A000000206; Nordea Bank Sweden AB; Sweden
204 | A000000207; Cardtronic Technology Inc.; US
205 | A000000209; Cassis International Pte. Ltd.; Singapore
206 | A000000210; TheTitan Corporation, Titan Secure Solutions; US
207 | A000000211; Saflink Corporation; US
208 | A000000212; Cyberpro Technologies Inc.; Canada
209 | A000000213; Sharp Corporation; Japan
210 | A000000214; Landis + Gyr; South Africa
211 | A000000215; HiSmarTech Co., Ltd.; Republic of Korea
212 | A000000216; ITSO Ltd.; UK
213 | A000000217; WAY Systems, Inc.; US
214 | A000000218; WEBUNDCO GmbH & Co KG; Austria
215 | A000000219; National Bank of Greece S.A.; Greece
216 | A000000220; Samsung SDS Co., Ltd.; Republic of Korea
217 | A000000221; E.F.G. Eurobank Ergasias S.A.; Greece
218 | A000000222; Easycard Ltd; UK
219 | A000000223; Seagate Technology; US
220 | A000000224; Tokyu Corporation; Japan
221 | A000000225; Laser Card Services Ltd.; Ireland
222 | A000000226; Irish Payment Services Organisation; Ireland
223 | A000000227; KFTC (Korea Financial Telecommunications & Clearings Institute); Republic of Korea
224 | A000000228; Saudi Arabian Monetary Agency (SAMA); Kingdom of Saudi Arabia
225 | A000000229; ALIASLAB S.P.A.; Italy
226 | A000000230; ECard Pty Ltd; Australia
227 | A000000231; National Police Agency of Japan; Japan
228 | A000000232; COREGATE. CO., Ltd; Republic of Korea
229 | A000000233; Professional Performance Systems; Australia
230 | A000000234; Thames Card Technology; UK
231 | A000000235; MCS Microsystems SDN BHD; Malaysia
232 | A000000236; Pacific Research Pty Ltd; Australia
233 | A000000237; Keycorp Limited; Australia
234 | A000000238; A.Little.World Pvt. Ltd.; India
235 | A000000239; Cornwall County Council; UK
236 | A000000240; Paypoint Network Ltd.; UK
237 | A000000241; Visa International; US
238 | A000000242; NagraCard S.A.; Switzerland
239 | A000000243; SAGEM SA; France
240 | A000000244; Vasco Data Security NV/SA; Belgium
241 | A000000245; Korea Highway Corporation; Republic of Korea
242 | A000000246; U.S. Department of Agriculture, Food and Nutrition Service; US
243 | A000000247; ISO JTC1/SC17/WG3; UK
244 | A000000248; ISO JTC1/SC17/WG10; UK
245 | A000000249; Giesecke & Devrient Asia Pte Ltd; Singapore
246 | A000000250; Föreningssparbanken; Sweden
247 | A000000251; Korea Electronic Banking Technology Co., Ltd.; Republic of Korea
248 | A000000252; Federal Reserve Bank of Boston; US
249 | A000000253; Maas International B.V.; Netherlands
250 | A000000254; Boston Communications Group, Inc.; US
251 | A000000255; TellSyn.co.LTD; Republic of Korea
252 | A000000256; Hitachi America Ltd.; US
253 | A000000257; Jatcom Oy; Finland
254 | A000000258; NWI Markkinointi Oy; Finland
255 | A000000259; Softcard Systems, Inc.; US
256 | A000000260; Canon Inc.; Japan
257 | A000000261; Smart Card Laboratory, Inc.; Republic of Korea
258 | A000000262; Transport Administration Corporation; Australia
259 | A000000263; Astute Pte Ltd; Singapore
260 | A000000264; BC Card Co., Ltd.; Republic of Korea
261 | A000000265; RICIOH Company, Ltd.; Japan
262 | A000000266; Sodexho - Pass; Belgium
263 | A000000267; Honda Motor Co., Ltd.; Japan
264 | A000000268; City of Sunderland Council; UK
265 | A000000269; Thales e-Security Limited; UK
266 | A000000270; Oneempower Pte Ltd; Singapore
267 | A000000271; The Bank of Tokyo-Mitsubishi UFJ Ltd.; Japan
268 | A000000272; Bank of the Philippine Islands; Philippines
269 | A000000273; Cabcharge Australia Ltd; Australia
270 | A000000274; Glenburn Technologies Private Limited; India
271 | A000000275; Takenaka Corporation; Japan
272 | A000000276; MARX CryptoTech LP; US
273 | A000000277; Interac Association; Canada
274 | A000000278; MOPASS Consortium; Japan
275 | A000000279; Poslovno Informacioni Sistemi d.o.o.; Serbia and Montenegro
276 | A000000280; Soci?t? d'Automatisation des Fr?res Bouadou - SATIM -; Algeria
277 | A000000281; Chequespread Plc; UK
278 | A000000282; Sentry Project Management PTY Ltd; Australia
279 | A000000283; Chungwa Telecom Co., Ltd. Telecommunication Laboratories; Taiwan
280 | A000000284; AXA, a.s., Bratislava; Slovakia
281 | A000000285; WLAN Smart Card Consortium; France
282 | A000000286; Thai Smart Card Co., Ltd.; Thailand
283 | A000000287; A4 Vision Inc.; US
284 | A000000288; Central Bank of the Islamic Republic of Iran; Iran
285 | A000000289; Publicenter Spa; Italy
286 | A000000290; National Council for Prescription Drug Programs; US
287 | A000000291; Spirtech; France
288 | A000000292; Sumitomo Mitsui Banking Corporation; Japan
289 | A000000293; Videosystem Srl; Italy
290 | A000000294; Banco INBURSA S.A.; Mexico
291 | A000000295; Irish Life and Permanent - Trading as Permanent TSB; Ireland
292 | A000000296; INSIG2 d.o.o.; Croatia
293 | A000000297; Silex technology, Inc; Japan
294 | A000000298; Vodafone global Services; Germany
295 | A000000299; ViVOtech, Inc.; US
296 | A000000300; Unigram Systems Pty Ltd; Australia
297 | A000000301; Arab Islamic Bank; Palestinian National Authorities
298 | A000000302; Cambridgeshire County Council; UK
299 | A000000303; Global Information Technology L.L.C.; United Arab Emirates
300 | A000000304; Far Eastern Electronic Toll collection Co., Ltd.; Taiwan, R.O.C.
301 | A000000305; Sarion Systems Research; Japan
302 | A000000306; PS/SC Workgroup; US
303 | A000000307; Smart Card Integrators, Tnc.; US
304 | A000000308; National Institute of Standards and Technology; US
305 | A000000309; Leo Smart Loyalty Management Sdn. Bhd.; Malaysia
306 | A000000310; Smart Platforms Co., Ltd.; Korea
307 | A000000311; Ubnics Co., Ltd.; Korea
308 | A000000312; Aladdin Knowledge Systems, Inc.; US
309 | A000000313; Point Vista Software; US
310 | A000000314; Integrated Engineering; The Netherlands
311 | A000000315; Currence Holding/PIN BV; The Netherlands
312 | A000000316; One PIN, Inc.; US
313 | A000000317; Sony Corporation; Japan
314 | A000000318; Minoh City Office; Japan
315 | A000000319; Findomestic Banca S.p.A.; Italy
316 | A000000320; Banque Accord; France
317 | A000000321; SK Telecom Co, Ltd; Rep. of Korea
318 | A000000322; Taipei Smart Card Corp.; Taiwan
319 | A000000323; Identity Alliance; US
320 | A000000324; Discover Financial Services LLC; US
321 | A000000325; Goddard Technology Corporation; US
322 | A000000326; CRYPTAS it-Security & Media GmbH; Austria
323 | A000000327; J & B Computing Services bv; The Netherlands
324 | A000000328; The Minato Bank, Ltd; Japan
325 | A000000329; Irdeto Access B.V.; The Netherlands
326 | A000000330; Volvofinans Konto AB; Sweden
327 | A000000331; TV-Card; France
328 | A000000332; XponCard AB; Sweden
329 | A000000333; China Unionpay Co. Ltd; China
330 | A000000334; oNDS Ltd; England
331 | A000000335; Seeker Wireless Pty Ltd; Australia
332 | A000000336; Sagem Denmark A/S; Danmark
333 | A000000337; Groupment Interbancaire Monétique de l' UEMOA; Senegal
334 | A000000338; Seceti S.p.A; Italy
335 | A000000339; Dexit Inc; Canada
336 | A000000340; SMEG Co Ltd; Korea
337 | A000000341; InfoComm Development Authority of Signapore; Singapore
338 | A000000342; TTA (Telecommunications Technology Association); Korea
339 | A000000343; Telecommunication Industry Association; USA
340 | A000000344; C-SAM Inc; USA
341 | A000000345; Broadnet Engineering Limited; Kina
342 | A000000346; ATOS WORLDLINE; England
343 | A000000347; Cryptomatic A/S; Denmark
344 | A000000348; Security Biometric Clearing Network; USA
345 | A000000349; MUTOH Industries LTD; Japan
346 | A000000350; Hyundai Motor Company; Korea 137-938
347 | A000000351; Sorefi; France
348 | A000000352; Groupement Interbancaire de monetique et des transactions electroniques (GIMTEL); England
349 | A000000353; Ompay, LLC; U.S.A
350 | A000000354; Nokia Corporation; Finland
351 | A000000355; University of Zagres Faculty of electrical engineering and computing; Croatia
352 | A000000356; Transport Ticketinng Authority; Austalia
353 | A000000357; LEGIC Identsystems AG; Switzerland
354 | A000000358; Consorzio Triveneto; Italy
355 | A000000359; Euro Alliance of Payment Schemes s.c.r.l. - EAPS; Belgium
356 | A000000360; ESP Systex Ltd.; UK
357 | A000000361; Atos Worldline; France
358 | A000000362; AMADEUS IT GROUP S.A.; Spain
359 | A000000363; IOPTE; France
360 | A000000364; Banque Internationale pour le Commerce et l'industrie du Gabon (BICIG); Gabon
361 | A000000365; Savvy Technologies ptd Limited; Australia
362 | A000000366; Poste Italiane S.P.A; Italy
363 | A000000367; TWIC Program; USA
364 | A000000368; Lloyds TSB Bank Plc; UK
365 | A000000369; OMV Refining and Marketing GmbH; Austria
366 | A000000370; Toshiba Corporation; Japan
367 | A000000371; Interswitch Limited; Nigeria
368 | A000000372; Turkish Armed Forces Smartcard Management Center; Turkey
369 | A000000373; Quality Equipment Benelux B.V; The Netherlands
370 | A000000374; CPS Technologies; France
371 | A000000375; Reference Point Ltd; United Kingdom
372 | A000000376; ECEBS Ltd; United Kingdom
373 | A000000377; Texas Health and Human Services Commission; United States
374 | A000000378; Texas Health and Human Services Commission; United States
375 | A000000379; (SOK) Suomen Osuuskauppojen Keskuskunta; Finland
376 | A000000380; E-Kencana; Maylaysia
377 | A000000381; Arval Ltd; United Kingdom
378 | A000000382; HID Global; United States
379 | A000000383; Cetelem; France
380 | A000000384; Australian Payments Clearing Association Ltd; Australia
381 | A000000385; NATIXIS PAIEMENTS; France
382 | A000000386; AIRTAG; FRANCE
383 | A000000387; Association Professionnelle des Emetteurs de Titres-Repas Asbl. (A.P.E.T.R); Belgium
384 | A000000388; Ticket Servicos S/A; Brazil
385 | A000000389; LINEA S.p.A.; Italy
386 | A000000390; Kamsoft; Poland
387 | A000000391; Ministry of Health, Labour and Welfare Office of Medical Devices and Information, Research and Deve; Japan
388 | A000000392; Laser Cofinoga; France
389 | A000000393; Florida Department of Transportation Sunpass Program; USA
390 | A000000394; OMAC (Office Monétique de l'Afrique Centrale); Cameroun
391 | A000000395; Mobile Money S.A; Cameroon
392 | A000000396; NXP Semiconductors Germany GmbH; Germany
393 | A000000397; Microsoft Corporation; USA
394 | A000000398; Queensland Transport; Australia
395 | A000000399; Helixion Ltd.; Scotland
396 | A000000400; BlueSky Positioning Centre International de Communication Avancee; France
397 | A000000401; BlueSky Positioning; France
398 | A000000402; Mxtran Inc.; Taiwan R.O.C
399 | A000000403; Jelmoli Bonus Card AG; Schweiz
400 | A000000404; Setra/BNEVT; France
401 | A000000405; European Payment Solutions NV-SA; Belgium
402 | A000000406; Aventra Oy; Finland
403 | A000000407; Fujitsu Services Oy; Finland
404 | A000000408; Svenska Handelsbanken AB; Sweden
405 | A000000409; Banque Casino; France
406 | A000000410; AUSTROADS; Australia
407 | #End
408 |
--------------------------------------------------------------------------------
/examples/general/scrath.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
2 | # Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
3 | # SPDX-License-Identifier: BSD-3-Clause
4 |
5 | # Navtive
6 | import sys
7 | import logging
8 | import optparse
9 | import subprocess
10 | from subprocess import call
11 |
12 | # 3rd Party
13 | import smartcard
14 | from smartcard.System import readers
15 | import llsmartcard.apdu as APDU
16 | from llsmartcard.apdu import APDU_STATUS, APPLET
17 | # LL Smartcard
18 | from llsmartcard.card import SmartCard, VisaCard, CAC
19 |
20 |
21 | class CARD_TYPE:
22 | VISA = 0
23 | CAC = 1
24 | HELLO_WORLD = 2
25 | EXPERIMENT = 3
26 | SECURE = 4
27 |
28 | CARD_TYPES = { "V":CARD_TYPE.VISA,
29 | "C":CARD_TYPE.CAC,
30 | "H":CARD_TYPE.HELLO_WORLD,
31 | "E":CARD_TYPE.EXPERIMENT,
32 | "S":CARD_TYPE.SECURE}
33 |
34 | def write_binary(data, filename):
35 | """
36 | Write binary data to a file on disk
37 | """
38 |
39 | import struct
40 |
41 | # Create file and write to it
42 | f = open(filename, "wb+")
43 |
44 | f.write(struct.pack("%dB" % len(data), *data))
45 |
46 | f.close()
47 |
48 | def read_binary(filename):
49 | """
50 | Write binary data to a file on disk
51 | """
52 | import struct
53 |
54 | data = []
55 |
56 | # Create file and write to it
57 | f = open(filename, "rb")
58 |
59 | byte = f.read(1)
60 | while byte != b"":
61 |
62 | data.append(ord(byte))
63 |
64 | # Do stuff with byte.
65 | byte = f.read(1)
66 |
67 |
68 | f.close()
69 |
70 | return data
71 |
72 | def main(args=None):
73 |
74 | opts = optparse.OptionParser()
75 |
76 | opts.add_option("-t", "--cardtype", action="store", type="string",
77 | dest="cardtype", default=CARD_TYPE.CAC,
78 | help="Card Type (V - Visa, C - CAC, H - Hello World)")
79 |
80 | opts.add_option("-l", "--listreaders", action="store_true",
81 | dest="listreaders", default=False,
82 | help="List Available Readers")
83 |
84 | opts.add_option("-s", "--savecerts", action="store", type="string",
85 | dest="savecerts", default=None,
86 | help="Save certificates to disk.")
87 |
88 | opts.add_option("-r", "--reader", action="store", type="int",
89 | dest="reader", default= -1,
90 | help="Reader number from --list or -1 for all.")
91 |
92 | opts.add_option("-d", "--debug", action="store_true",
93 | dest="debug", default=False,
94 | help="Enable DEBUG")
95 |
96 |
97 |
98 | (options, positionals) = opts.parse_args(args)
99 |
100 | if options.cardtype in CARD_TYPES:
101 | card_type = CARD_TYPES[options.cardtype]
102 | else:
103 | print "Card type not recognized."
104 | card_type = CARD_TYPE.CAC
105 |
106 | reader_list = readers()
107 | if options.listreaders:
108 | print "Available readers: "
109 | for i in range(len(reader_list)):
110 | print " %d: %s" % (i, reader_list[i])
111 | return
112 |
113 | log_level = logging.ERROR
114 | if options.debug:
115 | log_level = logging.DEBUG
116 |
117 | # by default we use the first reader
118 | for i in range(len(reader_list)):
119 | if options.reader == i or options.reader < 0:
120 | try:
121 | print "Using: %s" % reader_list[i]
122 |
123 | connection = reader_list[i].createConnection()
124 | connection.connect()
125 |
126 |
127 | if card_type == CARD_TYPE.SECURE:
128 |
129 | card = SmartCard(connection, log_level=log_level)
130 | card.apdu_select_application(APDU.APPLET.SECURITY_GEMALTO)
131 | card.open_secure_channel(APDU.APPLET.SECURITY_GEMALTO, APDU.AUTH_KEYS.GEMALTO)
132 | card.print_applications()
133 | for p1 in range(0xff):
134 | for p2 in range(0xff):
135 |
136 | data, sw1, sw2 = card.apdu_get_data([p1, p2])
137 |
138 | if (sw1, sw2) != APDU.STATUS_WORDS.NOT_FOUND and (sw1, sw2) != APDU.STATUS_WORDS.COND_NOT_SATISFIED:
139 | print "(%s,%s) %s %s: %s" % (hex(sw1), hex(sw2),
140 | hex(p1), hex(p2),
141 | APDU.get_hex(data))
142 |
143 | if card_type == CARD_TYPE.VISA:
144 | card = VisaCard(connection, log_level=log_level)
145 | card.select_visa_applet()
146 | card.read_visa()
147 |
148 | if card_type == CARD_TYPE.HELLO_WORLD:
149 | card = SmartCard(connection, log_level=log_level)
150 | card.apdu_select_application(APDU.APPLET.HELLO)
151 | data, sw1, sw2 = card.apdu_read_record(0x00, 0x00)
152 | print "Data: %s" % APDU.get_str(data)
153 |
154 | if card_type == CARD_TYPE.CAC:
155 | card = CAC(connection, log_level=log_level)
156 |
157 | card.print_object(APPLET.NIST_PIV,
158 | APDU.OBJ_NIST_PIV.CHUID)
159 | card.print_object(APPLET.NIST_PIV,
160 | APDU.OBJ_NIST_PIV.CCC)
161 |
162 | if options.savecerts is not None:
163 | print "Dumping Certs"
164 |
165 | import os
166 | import struct
167 |
168 | try:
169 | os.makedirs(options.savecerts)
170 | except:
171 | pass
172 |
173 | # Dump CHUID Cert
174 | cert_filename = os.path.join(options.savecerts,
175 | "chuid.crt")
176 | card.extract_cert(APPLET.NIST_PIV,
177 | APDU.OBJ_NIST_PIV.CHUID,
178 | cert_filename)
179 |
180 |
181 | # NIST PIV CERTS
182 | # These are all gzipped
183 | def extract_nist(oid, cert_filename):
184 |
185 | card.extract_cert(APPLET.NIST_PIV,
186 | oid,
187 | cert_filename)
188 | # ungzip it
189 | call(["gunzip", "-f", cert_filename])
190 | cert_filename = cert_filename[0:-3]
191 | # extract public cert
192 |
193 | p = subprocess.Popen(["openssl", "x509",
194 | "-inform", "DER",
195 | "-pubkey",
196 | "-in", cert_filename,
197 | "-out", cert_filename + ".pem"],
198 | stdout=subprocess.PIPE)
199 | out, err = p.communicate()
200 | f = open(cert_filename + ".pub", "w+")
201 | f.write(out)
202 | f.close()
203 |
204 | nist_dir = os.path.join(options.savecerts, "piv")
205 | try:
206 | os.makedirs(nist_dir)
207 | except:
208 | pass
209 |
210 |
211 | # Dig Sig
212 | cert_filename = os.path.join(nist_dir,
213 | "nist_dig_sig.crt.gz")
214 | extract_nist(APDU.OBJ_NIST_PIV.KEY_DIG_SIG, cert_filename)
215 |
216 |
217 | # call(, "> %s.pub" % cert_filename])
218 |
219 | # PIV Auth
220 | cert_filename = os.path.join(nist_dir,
221 | "nist_piv_auth.crt.gz")
222 | extract_nist(APDU.OBJ_NIST_PIV.KEY_PIV_ATH, cert_filename)
223 |
224 | # Doesn't seem to exist...
225 | # # Cred Auth
226 | # cert_filename = os.path.join(nist_dir,
227 | # "nist_crd_auth.crt.gz")
228 | # extract_nist(APDU.OBJ_NIST_PIV.KEY_CRD_ATH, cert_filename)
229 |
230 |
231 | # Key Management
232 | cert_filename = os.path.join(nist_dir,
233 | "nist_mng.crt.gz")
234 | extract_nist(APDU.OBJ_NIST_PIV.KEY_MNG, cert_filename)
235 |
236 | #
237 | # DoD CAC Certs
238 | #
239 | cac_dir = os.path.join(options.savecerts, "cac")
240 | try:
241 | os.makedirs(cac_dir)
242 | except:
243 | pass
244 |
245 |
246 | cert_filename = os.path.join(cac_dir,
247 | "cac_pki_enc.pub")
248 | card.extract_cert(APPLET.DOD_CAC,
249 | APDU.OBJ_DOD_CAC.KEY_PKI_ENC,
250 | cert_filename)
251 |
252 | cert_filename = os.path.join(cac_dir,
253 | "cac_pki_id.pub")
254 | card.extract_cert(APPLET.DOD_CAC,
255 | APDU.OBJ_DOD_CAC.KEY_PKI_ID,
256 | cert_filename)
257 |
258 | cert_filename = os.path.join(cac_dir,
259 | "cac_pki_sig.pub")
260 | card.extract_cert(APPLET.DOD_CAC,
261 | APDU.OBJ_DOD_CAC.KEY_PKI_SIG,
262 | cert_filename)
263 |
264 | # nist_dig_sig_f = open(nist_dig_sig, "wb")
265 | #
266 | # # NIST PIV
267 | # data = card.read_object(APPLET.NIST_PIV,
268 | # APDU.OBJ_NIST_PIV.KEY_DIG_SIG)
269 | # print APDU.get_hex(data[0][1])
270 | # for i in data[0][1]:
271 | # nist_dig_sig_f.write(struct.pack("B", i))
272 | #
273 | # nist_dig_sig_f.close()
274 |
275 | # card.print_object(APPLET.NIST_PIV,
276 | # APDU.OBJ_NIST_PIV.KEY_MNG)
277 | card.print_object(APPLET.NIST_PIV,
278 | APDU.OBJ_NIST_PIV.KEY_PIV_ATH)
279 | # card.print_object(APPLET.NIST_PIV,
280 | # APDU.OBJ_NIST_PIV.SEC_OBJ)
281 | #
282 | # # DoD CAC
283 | card.print_object(APDU.APPLET.DOD_CAC,
284 | APDU.OBJ_DOD_CAC.KEY_PKI_ENC)
285 | card.print_object(APDU.APPLET.DOD_CAC,
286 | APDU.OBJ_DOD_CAC.KEY_PKI_ID)
287 | card.print_object(APDU.APPLET.DOD_CAC,
288 | APDU.OBJ_DOD_CAC.KEY_PKI_SIG)
289 |
290 |
291 |
292 |
293 | if card_type == CARD_TYPE.EXPERIMENT:
294 |
295 | card = CAC(connection, log_level=log_level)
296 |
297 | PIN = [0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37]
298 |
299 |
300 | data, sw1, sw2 = card.apdu_verify_pin(PIN, p2=0x00)
301 |
302 |
303 | card.apdu_select_application(APDU.APPLET.DOD_CAC)
304 | card.apdu_select_object(APDU.OBJ_DOD_CAC.KEY_PKI_ID)
305 |
306 | # data, sw1, sw2 = card.apdu_verify_pin(PIN, p2=0x80)
307 |
308 | sign_data = []
309 | for i in range(100):
310 | sign_data.append(0x41)
311 |
312 | sign_data = read_binary("input.ssl")
313 |
314 | print sign_data
315 |
316 | print "Signing: %s" % APDU.get_hex(sign_data)
317 | data, sw1, sw2 = card.apdu_sign_decrypt(sign_data)
318 |
319 | write_binary(data, "data.enc")
320 |
321 |
322 |
323 |
324 | print "Signed: %s" % APDU.get_hex(data)
325 |
326 | # card.apdu_select_object(APDU.OBJ_DOD_CAC.KEY_PKI_SIG)
327 | #
328 | # data, sw1, sw2 = card.apdu_sign_decrypt(data)
329 | #
330 | # print "Decrypted: %s" % APDU.get_hex(data)
331 |
332 |
333 | # card.read_x509_piv(card.X509.PIV_ATH)
334 | #
335 | # card.read_x509_piv(card.X509.DIG_SIG)
336 | # card.read_x509_piv(card.X509.KEY_MNG)
337 |
338 | # card.read_chuid(APDU.APPLET.NIST_PIV)
339 | # card.read_chuid(APDU.APPLET.DOD_PIV)
340 | #
341 | # card.read_card_capability_container(APDU.APPLET.NIST_PIV)
342 | # card.read_card_capability_container(APDU.APPLET.DOD_PIV)
343 |
344 | # card.apdu_select_application(APDU.APPLET.DOD_PIV, pix=APDU.OBJ_DOD_PIV.CHUID)
345 | # card.apdu_select_object(APDU.OBJ_DOD_PIV.KEY_PIV_ATH)
346 | # card.read_tl_v_buffer(0x0000)
347 | # card.apdu_select_object(APDU.OBJ_DOD_PIV.SEC_OBJ)
348 | #
349 | if False:
350 | print "Printing NIST PIV Objects..."
351 |
352 | # Print NIST PIV Objects
353 | card.print_object(APPLET.NIST_PIV,
354 | APDU.OBJ_NIST_PIV.CHUID)
355 | card.print_object(APPLET.NIST_PIV,
356 | APDU.OBJ_NIST_PIV.CCC)
357 | card.print_object(APPLET.NIST_PIV,
358 | APDU.OBJ_NIST_PIV.KEY_DIG_SIG)
359 | card.print_object(APPLET.NIST_PIV,
360 | APDU.OBJ_NIST_PIV.KEY_MNG)
361 | card.print_object(APPLET.NIST_PIV,
362 | APDU.OBJ_NIST_PIV.KEY_PIV_ATH)
363 | card.print_object(APPLET.NIST_PIV,
364 | APDU.OBJ_NIST_PIV.SEC_OBJ)
365 |
366 | # Objects that require PIN
367 | card.print_object(APPLET.NIST_PIV,
368 | APDU.OBJ_NIST_PIV.KEY_CRD_ATH)
369 | card.print_object(APPLET.NIST_PIV,
370 | APDU.OBJ_NIST_PIV.FACE)
371 | card.print_object(APPLET.NIST_PIV,
372 | APDU.OBJ_NIST_PIV.FNGR_P1)
373 | card.print_object(APPLET.NIST_PIV,
374 | APDU.OBJ_NIST_PIV.FNGR_P2)
375 |
376 | print "Printing DoD PIV Objects..."
377 |
378 | # Print DOD PIV Object
379 | card.print_object(APPLET.DOD_PIV,
380 | APDU.OBJ_DOD_PIV.CCC)
381 | card.print_object(APPLET.DOD_PIV,
382 | APDU.OBJ_DOD_PIV.SEC_OBJ,
383 | pix=APDU.PIX_CAC.PIV_TRNS_APLT)
384 | card.print_object(APPLET.DOD_PIV,
385 | APDU.OBJ_DOD_PIV.FACE,
386 | pix=APDU.PIX_CAC.PIV_TRNS_APLT)
387 | card.print_object(APPLET.DOD_PIV,
388 | APDU.OBJ_DOD_PIV.FNGR_PRNT,
389 | pix=APDU.PIX_CAC.PIV_TRNS_APLT)
390 |
391 | # We need a PIN for the CHUID?
392 | card.print_object(APPLET.DOD_PIV,
393 | APDU.OBJ_DOD_PIV.CHUID)
394 |
395 | print "Printing DoD CAC Objects..."
396 |
397 | # Print DOD CAC Objects
398 | card.print_object(APDU.APPLET.DOD_CAC,
399 | APDU.OBJ_DOD_CAC.KEY_PKI_ENC)
400 | card.print_object(APDU.APPLET.DOD_CAC,
401 | APDU.OBJ_DOD_CAC.KEY_PKI_ID)
402 | card.print_object(APDU.APPLET.DOD_CAC,
403 | APDU.OBJ_DOD_CAC.KEY_PKI_SIG)
404 |
405 | # Not yet sure what information is here.
406 | card.print_object(APDU.APPLET.DOD_CAC,
407 | APDU.OBJ_DOD_CAC.CAC_PERSON)
408 | card.print_object(APDU.APPLET.DOD_CAC,
409 | APDU.OBJ_DOD_CAC.CAC_PERSONEL)
410 |
411 | sys.exit(0)
412 |
413 | # PIN REQUIRED FOR THESE
414 |
415 |
416 |
417 | # VERIFY PIN
418 | card.apdu_select_application(APDU.APPLET.DOD_PIV, APDU.OBJ_DOD_PIV.CHUID)
419 | tv_list = card.read_tl_v_buffer(0x0000)
420 | for tv in tv_list:
421 | print hex(tv[0])
422 | print APDU.get_hex(tv[1])
423 | # data, sw1, sw2 = card.apdu_read_buffer(0x00, 0x00, 01, read_length=20)
424 | # data, sw1, sw2 = card.apdu_verify_pin([0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37], p2=0x01)
425 | # card.select_object([0xa0, 01])
426 |
427 | sys.exit(0)
428 |
429 | PIN = [0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37]
430 | card.apdu_select_application(APDU.APPLET.NIST_PIV)
431 | card.read_chuid()
432 |
433 | # card.read_fingerprint(PIN)
434 | # card.read_facial_info(PIN)
435 |
436 | # Try to sign/decrypt
437 | # card.apdu_select_application(APDU.APPLET.NIST_PIV)
438 | # card.apdu_verify_pin(PIN, p2=0x00)
439 | # card.apdu_sign_decrypt([0x41, 0x41, 0x41, 0x41, 0x41, 0x41])
440 |
441 | # # Card Auth X.509
442 | # card.read_x509_piv(card.X509.CRD_ATH)
443 | #
444 | # # Fingerprint I
445 | # card.get_data_piv([0x5F, 0xC1, 0x03])
446 | #
447 | # # Fingerprint II
448 | # (6a,82) Not Found
449 | # card.get_data_piv([0x5F, 0xC1, 0x04])
450 | #
451 | # # Printed Information
452 | # (6a,82) Not Found
453 | # card.get_data_piv([0x5F, 0xC1, 0x09])
454 | #
455 | # # Facial Image
456 | # card.get_data_piv([0x5F, 0xC1, 0x08])
457 |
458 |
459 |
460 | # card.apdu_select_application(APDU.APPLET.PIV)
461 | #
462 | # # Read Security Object
463 | # card.get_data_piv([0x5F, 0xC1, 0x06])
464 |
465 |
466 |
467 |
468 | # card.apdu_select_application(APDU.APPLET.PIV)
469 | ## card.select_ef([0xdb, 0x00])
470 | ## card.select_ef([0x30, 0x00])
471 | # data, sw1, sw2 = card.get_data_piv([0x5f, 0xc1, 0x06])
472 | # print APDU.get_hex(data)
473 |
474 |
475 | # card.apdu_select_application(APDU.APPLET.PIV)
476 | # data, sw1, sw2 = card.get_data_piv([0x5f, 0xc1, 0x05])
477 |
478 |
479 | # Input PIN
480 |
481 |
482 | # card.apdu_select_application(APPLET.CSG_CCC) #, p1=0x02, p2=0x00)
483 | # data, sw1, sw2 = card.read_buffer(0, 0, 0x01)
484 | #
485 | # print "Length: %d" % data[0]
486 | # card.read_buffer(0, 2, 0x01, read_length=data[0])
487 |
488 | # card.apdu_select_application(APDU.APPLET.CSG_CCC)
489 | # card.apdu_verify_pin([0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37], p2=0x00)
490 | # card.select_mf([0x3f, 0x00], p1=0x00, p2=0x00)
491 |
492 | # card.read_card_capability_piv()
493 | # card.read_chuid()
494 |
495 | # Enter PIN to PIV Applet
496 | # card.apdu_select_application(APPLET.PIV)
497 | # card.apdu_verify_pin([0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37], p2=0x80)
498 |
499 | # card.read_card_capability_csg()
500 | # card.read_card_capability_piv()
501 |
502 | # card.read_card_capability()
503 |
504 | # for j in range(255):
505 | # card.read_buffer(0, j, 0x01)
506 |
507 |
508 | # for i in range(255):
509 | # for j in range(255):
510 | # card.read_buffer(i, j, 0x01)
511 |
512 | #
513 | # card.read_card_capability()
514 |
515 | # card.apdu_verify_pin([0x31, 0x32, 0x33, 0x34, 0xFF, 0xFF, 0xFF, 0xFF], p2=80)
516 |
517 | # card.read_chuid()
518 |
519 | # card.read_printed_info()
520 |
521 |
522 | # card.get_data(0x30, 0x00)
523 | # card.dump_records()
524 | # visa = VisaCard(connection)
525 | # if visa is not None:
526 | # print visa
527 | # visa.read_visa()
528 |
529 | except smartcard.Exceptions.CardConnectionException as ex:
530 | print "ERROR: Couldn't connect to card in %s" % reader_list[i]
531 | # raise
532 |
533 |
534 |
535 | if __name__ == "__main__":
536 | main()
537 |
--------------------------------------------------------------------------------
/docs/apdus.txt:
--------------------------------------------------------------------------------
1 | #------------+------------------------+------------------------+----------------------+--------------------------------+
2 | |ClaIns P1 P2|Lc Send Data |Le Recv Data | Specification | Description |
3 | +------------+------------------------+------------------------+----------------------+--------------------------------+
4 | | 04 | ISO 7816-9 6.3 | DEACTIVATE FILE |
5 | | A0 04 00 00 00 | 3GPP TS 11.11 | INVALIDATE |
6 | | A0 04 00 00 00 | SAGEM SCT U34 6.15 | INVALIDATE |
7 | +------------+------------------------+------------------------+----------------------+--------------------------------+
8 | | 80 0D xx xx 08 xxxx xxxx xxxx xxxx | SAGEM SCT U34 | VERIFY TRANSPORT CODE |
9 | +------------+------------------------+------------------------+----------------------+--------------------------------+
10 | | 0C | ISO 7816-4 7.3.6 | ERASE RECORD (S) |
11 | | 80 0C 00 xx xx | SAGEM SCT U34 8.1.2 | CHECK (flash) |
12 | | 80 0C 01 xx xx | SAGEM SCT U34 8.1.2 | CHECK (EEPROM) |
13 | | 80 0C 02 xx xx | SAGEM SCT U34 8.1.2 | CHECK (checksum of file) |
14 | +------------+------------------------+------------------------+----------------------+--------------------------------+
15 | | 0E | ISO 7816-4 8.2.4 | ERASE BINARY |
16 | +------------+------------------------+------------------------+----------------------+--------------------------------+
17 | | 10 | ISO 7816-7 | PERFORM SCQL OPERATION |
18 | | 00 10 00 80 xx table name, ... | ISO 7816-7 7.1 | CREATE TABLE |
19 | | 00 10 00 81 xx view name, table name | ISO 7816-7 7.2 | CREATE VIEW |
20 | | 00 10 00 82 xx dictionary name | ISO 7816-7 7.3 | CREATE DICTIONARY |
21 | | 00 10 00 83 xx table name | ISO 7816-7 7.4 | DROP TABLE |
22 | | 00 10 00 84 xx view or dictionary | ISO 7816-7 7.5 | DROP VIEW |
23 | | 00 10 00 85 xx privileges | ISO 7816-7 7.6 | GRANT |
24 | | 00 10 00 86 xx privileges | ISO 7816-7 7.7 | REVOKE |
25 | | 00 10 00 87 xx data | ISO 7816-7 7.8 | DECLARE CURSOR |
26 | | 00 10 00 88 | ISO 7816-7 7.9 | OPEN |
27 | | 00 10 00 89 | ISO 7816-7 7.10 | NEXT |
28 | | 00 10 00 8A xx D, fixing N (columns)| ISO 7816-7 7.11 | FETCH |
29 | | 00 10 00 8B xx D, fixing N (columns)| ISO 7816-7 7.12 | FETCH NEXT |
30 | | 00 10 00 8C xx data | ISO 7816-7 7.13 | INSERT |
31 | | 00 10 00 8D xx data | ISO 7816-7 7.14 | UPDATE |
32 | | 00 10 00 8E | ISO 7816-7 7.15 | DELETE |
33 | +------------+------------------------+------------------------+----------------------+--------------------------------+
34 | | 12 | ISO 7816-7 | PERFORM TRANSACTION OPERATION |
35 | | 00 12 00 80 | ISO 7816-7 8.2.1 | BEGIN |
36 | | 00 12 00 81 | ISO 7816-7 8.2.2 | COMMIT |
37 | | 00 12 00 82 | ISO 7816-7 8.2.3 | ROLLBACK |
38 | +------------+------------------------+------------------------+----------------------+--------------------------------+
39 | | 14 | ISO 7816-7 | PERFORM USER OPERATION |
40 | | 00 14 00 80 xx User ID, ... | ISO 7816-7 9.2.1 | PRESENT USER |
41 | | 00 14 00 81 xx User ID, profile, ... | ISO 7816-7 9.2.2 | CREATE USER |
42 | | 00 14 00 82 xx User ID | ISO 7816-7 9.2.3 | DELETE USER |
43 | | 80 14 xx xx 00 | GEMPLUS MPCOS-EMV | Switch Protocol |
44 | +------------+------------------------+------------------------+----------------------+--------------------------------+
45 | | 84 16 00 00 xx MAC | VSDC | CARD BLOCK |
46 | | 80 16 0X 00 05 xxxx xxxx xx | GEMPLUS MPCOS-EMV | Freeze Access Conditions |
47 | | 84 16 0X 00 08 xxxx xxxx xxxx xxxx | GEMPLUS MPCOS-EMV | Freeze Access Conditions |
48 | +------------+------------------------+------------------------+----------------------+--------------------------------+
49 | | 84 18 00 00 xx MAC | VSDC | APPLICATION UNBLOCK |
50 | +------------+------------------------+------------------------+----------------------+--------------------------------+
51 | | 84 1E 00 00 xx MAC | VSDC | APPLICATION BLOCK |
52 | +------------+------------------------+------------------------+----------------------+--------------------------------+
53 | | 20 | ISO 7816-4 8.5.5 | VERIFY |
54 | | 00 20 00 80 08 xxxx xxxx xxxx xxxx | VSDC | VERIFY (Transaction PIN data) |
55 | | A0 20 00 xx 08 CHV Value | 3GPP TS 11.11 | VERIFY |
56 | | A0 20 00 xx 08 CHV Value | SAGEM SCT U34 6.10 | VERIFY |
57 | | 80 20 00 xx 08 ADM Value | SAGEM SCT U34 8.1.4 | VERIFY ADM |
58 | +------------+------------------------+------------------------+----------------------+--------------------------------+
59 | | 80 21 00 xx 08 ADM Value | SAGEM SCT U34 8.1.4 | VERIFY ADM |
60 | +------------+------------------------+------------------------+----------------------+--------------------------------+
61 | | 22 | ISO 7816-4 8.5.10 | MANAGE SECURITY ENVIRONMENT |
62 | +------------+------------------------+------------------------+----------------------+--------------------------------+
63 | | 24 | ISO 7816-4 8.5.6 | CHANGE CHV |
64 | | 84 24 00 00 xx PIN data + MAC | VSDC | PIN CHANGE/UNBLOCK |
65 | | A0 24 00 xx 10 Old CHV, New CHV | 3GPP TS 11.11 | CHANGE CHV |
66 | | A0 24 00 xx 10 Old CHV, New CHV | SAGEM SCT U34 6.11 | CHANGE CHV |
67 | +------------+------------------------+------------------------+----------------------+--------------------------------+
68 | | 26 | ISO 7816-4 8.5.8 | DISABLE CHV1 |
69 | | A0 26 00 01 08 CHV1 value | 3GPP TS 11.11 | DISABLE CHV1 |
70 | | A0 26 00 01 08 CHV1 value | SAGEM SCT U32 6.12 | DISABLE CHV1 |
71 | +------------+------------------------+------------------------+----------------------+--------------------------------+
72 | | 28 | ISO 7816-4 8.5.7 | ENABLE CHV1 |
73 | | A0 28 00 01 08 CHV1 value | 3GPP TS 11.11 | ENABLE CHV1 |
74 | | A0 28 00 01 08 CHV1 value | SAGEM SCT U34 6.13 | ENABLE CHV1 |
75 | +------------+------------------------+------------------------+----------------------+--------------------------------+
76 | | 2A | ISO 7816-8 5.2 | PERFORM SECURITY OPERATION |
77 | +------------+------------------------+------------------------+----------------------+--------------------------------+
78 | | 2C | ISO 7816-4 8.5.9 | UNBLOCK CHV |
79 | | A0 2C 00 xx 10 Unblock CHV(PUK), New CHV | 3GPP TS 11.11 | UNBLOCK CHV |
80 | | A0 2C 00 xx 10 Unblock CHV(PUK), New CHV | SAGEM SCT U34 6.14 | UNBLOCK CHV |
81 | +------------+------------------------+------------------------+----------------------+--------------------------------+
82 | | A0 2E 00 0# 01 Data | 3GPP TS 11.11 | WRITE CODE STATUS |
83 | +------------+------------------------+------------------------+----------------------+--------------------------------+
84 | | A0 32 00 00 03 Value to be added. | 3GPP TS 11.11 | INCREASE |
85 | | A0 32 00 00 03 Value to be added. | SAGEM SCT U34 6.9 | INCREASE |
86 | +------------+------------------------+------------------------+----------------------+--------------------------------+
87 | | 39 | | java Authentificate User Comman|
88 | +------------+------------------------+------------------------+----------------------+--------------------------------+
89 | | 44 | ISO 7816-9 6.4 | ACTIVATE FILE |
90 | | A0 44 00 00 00 | 3GPP TS 11.11 | REHABILIDATE |
91 | | A0 44 00 00 00 | SAGEM SCT U34 6.16 | REHABILIDATE |
92 | +------------+------------------------+------------------------+----------------------+--------------------------------+
93 | | 46 | ISO 7816-8 5.1 | GENERATE ASYMMETRIC KEY PAIR |
94 | +------------+------------------------+------------------------+----------------------+--------------------------------+
95 | | 80 50 xx xx 08 Host challenge 00 | GlobalPlatform | INITIALIZE UPDATE then [C0] |
96 | +------------+------------------------+------------------------+----------------------+--------------------------------+
97 | | 70 | ISO 7816-4 8.1.2 | MANAGE CHANNEL |
98 | | 00 70 xx xx xx | GlobalPlatform | MANAGE CHANNEL |
99 | +------------+------------------------+------------------------+----------------------+--------------------------------+
100 | | 80 78 00 03 xx | GlobalPlatform | END R-MAC SESSION |
101 | +------------+------------------------+------------------------+----------------------+--------------------------------+
102 | | 80 7A xx 01 xx Data and C-MAC, if needed | GlobalPlatform | BEGIN R-MAC SESSION |
103 | +------------+------------------------+------------------------+----------------------+--------------------------------+
104 | | 82 | ISO 7816-4 8.5.3 | EXTERNAL AUTHENTICATE |
105 | | 84 82 00 00 10 Host cryptogram and MAC | GlobalPlatform | EXTERNAL AUTHENTICATE |
106 | | 84 82 00 00 0A Authentication-related data | VSDC | EXTERNAL AUTHENTICATE |
107 | | 00 82 00 xx 06 Manual | GEMPLUS MPCOS-EMV | EXTERNAL AUTHENTICATE |
108 | +------------+------------------------+------------------------+----------------------+--------------------------------+
109 | | 84 | ISO 7816-4 8.5.2 | GET CHALLENGE |
110 | | 00 84 00 00 08 Rnd Num | VSDC | GET CHALLENGE |
111 | | 00 84 xx xx 08 Rnd Num | GEMPLUS MPCOS-EMV | GET CHALLENGE |
112 | +------------+------------------------+------------------------+----------------------+--------------------------------+
113 | | 86 | ISO 7816-4 8.5.4 | GENERAL AUTHENTICATE |
114 | +------------+------------------------+------------------------+----------------------+--------------------------------+
115 | | 88 | ISO 7816-4 8.5.1 | INTERNAL AUTHENTICATE |
116 | | 00 88 XX xx 0A Manual | GEMPLUS MPCOS-EMV | INTERNAL AUTHENTICATE |
117 | | A0 88 00 00 10 RAND : Rnd num xx SRES( 4B) , Kc (8B) | 3GPP TS 11.11 | RUN GSM ALGORITHM |
118 | | A0 88 00 00 10 RAND : Rnd num xx SRES( 4B) , Kc (8B) | SAGEM SCT U34 6.17 | RUN GSM ALGORITHM |
119 | +------------+------------------------+------------------------+----------------------+--------------------------------+
120 | | A0 | ISO 7816-4 8.2.5 | SEARCH BINARY |
121 | +------------+------------------------+------------------------+----------------------+--------------------------------+
122 | | A2 | ISO 7816-4 8.3.5 | SEEK |
123 | | A0 A2 00 xx xx Pattern xx | 3GPP TS 11.11 | SEEK |
124 | | A0 A2 00 xx xx Pattern xx | SAGEM SCT U34 6.8 | SEEK |
125 | +------------+------------------------+------------------------+----------------------+--------------------------------+
126 | | A4 | ISO 7816-4 8.1.1 | SELECT |
127 | | 00 A4 04 00 xx AID 00 | GlobalPlatform | SELECT |
128 | | 00 A4 00 xx xx File ID || Name 00 Manual | VSDC | SELECT |
129 | | A0 A4 00 00 02 File ID | 3GPP TS 11.11 | SELECT |
130 | | A0 A4 00 00 02 File ID | SAGEM SCT U34 6.1 | SELECT |
131 | +------------+------------------------+------------------------+----------------------+--------------------------------+
132 | | 80 A8 00 00 00 00 | VSDC | GET PROCESSING OPTIONS |
133 | +------------+------------------------+------------------------+----------------------+--------------------------------+
134 | | 80 AE 00 xx Transaction-related data | VSDC | |
135 | +------------+------------------------+------------------------+----------------------+--------------------------------+
136 | | B0 | ISO 7816-4 8.2.1 | READ BINARY |
137 | | 00 B0 xx xx xx | GEMPLUS MPCOS-EMV | READ BINARY |
138 | | A0 B0 xx xx xx | 3GPP TS 11.11 | READ BINARY |
139 | | A0 B0 xx xx xx | SAGEM SCT U34 6.4 | READ BINARY |
140 | +------------+------------------------+------------------------+----------------------+--------------------------------+
141 | | B2 | ISO 7816-4 8.3.1 | READ RECORD |
142 | | 00 B2 xx 00 | VSDC | READ RECORD |
143 | | A0 B2 xx xx xx | 3GPP TS 11.11 | READ RECORD |
144 | | A0 B2 xx xx xx | SAGEM SCT U34 6.6 | READ RECORD |
145 | +------------+------------------------+------------------------+----------------------+--------------------------------+
146 | | B4 | | java Component Data |
147 | +------------+------------------------+------------------------+----------------------+--------------------------------+
148 | | B8 | | java Create Applet |
149 | +------------+------------------------+------------------------+----------------------+--------------------------------+
150 | | BA | | java CAP end |
151 | +------------+------------------------+------------------------+----------------------+--------------------------------+
152 | | BC | | java Component end |
153 | +------------+------------------------+------------------------+----------------------+--------------------------------+
154 | | BE 04 Data | GEMPLUS GemClub-MEMO | READ |
155 | +------------+------------------------+------------------------+----------------------+--------------------------------+
156 | | C0 | ISO 7816-4 8.6.1 | GET RESPONSE |
157 | | 00 C0 1C Key Info | GlobalPlatform | GET RESPONSE |
158 | | 00 C0 00 00 00 | VSDC | GET RESPONSE |
159 | | 80 C0 00 00 xx | GEMPLUS MPCOS-EMV | Get Info on Get Response |
160 | | 80 C0 02 A0 08 Chip SN | GEMPLUS MPCOS-EMV | Get Info |
161 | | 80 C0 02 A1 08 Card SN | GEMPLUS MPCOS-EMV | Get Info |
162 | | 80 C0 02 A2 08 Issuer SN | GEMPLUS MPCOS-EMV | Get Info |
163 | | 80 C0 02 A3 04 Iss.Ref.N | GEMPLUS MPCOS-EMV | Get Info |
164 | | 80 C0 02 A4 0D Chip Inf | GEMPLUS MPCOS-EMV | Get Info |
165 | | 80 C0 02 A5 xx Keys | GEMPLUS MPCOS-EMV | Get Info |
166 | | 80 C0 02 A6 02 Last DF/EF | GEMPLUS MPCOS-EMV | Get Info |
167 | | A0 C0 00 00 xx | 3GPP TS 11.11 | GET RESPONSE |
168 | | A0 C0 00 00 xx | SAGEM SCT U34 6.3 | GET RESPONSE |
169 | +------------+------------------------+------------------------+----------------------+--------------------------------+
170 | | C2 | ISO 7816-4 8.6.2 | ENVELOPE |
171 | +------------+------------------------+------------------------+----------------------+--------------------------------+
172 | | C4 | | java Delete Applets |
173 | +------------+------------------------+------------------------+----------------------+--------------------------------+
174 | | CA | ISO 7816-4 8.4.1 | GET DATA |
175 | | 00 CA 00 xx xx MAC, if present | GlobalPlatform | GET DATA |
176 | | 80 CA xx xx xx | VSDC | GET DATA |
177 | +------------+------------------------+------------------------+----------------------+--------------------------------+
178 | | D0 | ISO 7816-4 8.2.2 | WRITE BINARY |
179 | | 80 D0 xx xx xx Data to be written in EEPROM | VSDC | LOAD STRUCTURE |
180 | +------------+------------------------+------------------------+----------------------+--------------------------------+
181 | | D2 | ISO 7816-4 8.3.2 | WRITE RECORD |
182 | +------------+------------------------+------------------------+----------------------+--------------------------------+
183 | | D6 | ISO 7816-4 8.2.3 | UPDATE BINARY |
184 | | A0 D6 xx xx xx Data to be written in EEPROM | 3GPP TS 11.11 | UPDATE BINARY |
185 | | A0 D6 xx xx xx Data to be written in EEPROM | SAGEM SCT U34 6.5 | UPDATE BINARY |
186 | +------------+------------------------+------------------------+----------------------+--------------------------------+
187 | | 80 D8 xx xx xx KEY Date (and MAC) 00 | GlobalPlatform | PUT KEY |
188 | | D8 | EMV | Set Card Status(personalization|
189 | +------------+------------------------+------------------------+----------------------+--------------------------------+
190 | | DA | ISO 7816-4 8.4.2 | PUT DATA |
191 | | 00 DA xx xx xx Data | VSDC | PUT DATA |
192 | +------------+------------------------+------------------------+----------------------+--------------------------------+
193 | | DC | ISO 7816-4 | UPDATE RECORD |
194 | | 00 DC xx xx xx Data (and MAC) | VSDC | UPDATE RECORD |
195 | | A0 DC xx xx xx Data to be written in EEPROM | 3GPP TS 11.11 | UPDATE RECORD |
196 | | A0 DC xx xx xx Data to be written in EEPROM | SAGEM SCT U34 6.7 | UPDATE RECORD |
197 | +------------+------------------------+------------------------+----------------------+--------------------------------+
198 | | DE 04 Data | GEMPLUS GemClub-MEMO | UPDATE |
199 | | A0 DE 00 00 03 Data | 3GPP TS 11.11 | LOAD AoC(SICAP) |
200 | +------------+------------------------+------------------------+----------------------+--------------------------------+
201 | | E0 | ISO 7816-9 6.1 | CREATE FILE |
202 | | 80 E0 02 00 0C Manual | GEMPLUS MPCOS-EMV | CREATE FILE |
203 | | 80 E0 xx xx xx FCI length | 3GPP TS 11.11 | CREATE FILE |
204 | | 80 E0 xx xx xx FCI length | SAGEM SCT U34 | CREATE FILE |
205 | +------------+------------------------+------------------------+----------------------+--------------------------------+
206 | | E2 | ISO 7816-4 8.3.4 | APPEND RECORD |
207 | | 80 E2 00 00 xx Record (and MAC) | GlobalPlatform | APPEND RECORD |
208 | | 00 E2 00 00 xx Record | VSDC | APPEND RECORD |
209 | | 00 E2 00 00 xx Record | GEMPLUS MPCOS-EMV | APPEND RECORD |
210 | | 00 E2 00 00 xx Record | 3GPP TS 11.11 | APPEND RECORD |
211 | +------------+------------------------+------------------------+----------------------+--------------------------------+
212 | | E4 | ISO 7816-9 6.2 | DELETE FILE |
213 | | 80 E4 00 00 xx TLV coded name | GlobalPlatform | DELETE FILE |
214 | | A0 E4 00 00 02 xx xx | 3GPP TS 11.11 | DELETE FILE |
215 | +------------+------------------------+------------------------+----------------------+--------------------------------+
216 | | E6 | ISO 7816-9 6.5 | TERMINATE DF |
217 | | 80 E6 xx 00 xx Manual | GlobalPlatform | INSTALL |
218 | | A0 E6 xx xx 00 | 3GPP TS 11.11 | LOCK RECORD |
219 | +------------+------------------------+------------------------+----------------------+--------------------------------+
220 | | E8 | ISO 7816-9 6.6 | TERMINATE EF |
221 | | 80 E8 00 00 xx Record | GlobalPlatform | LOAD |
222 | | A0 E8 00 xx 10 Data | 3GPP TS 11.11 | READ DIRECTORY |
223 | +------------+------------------------+------------------------+----------------------+--------------------------------+
224 | | 80 EA 00 00 xx Data | 3GPP TS 11.11 | CREATE BINARY |
225 | | 80 EA 00 00 xx Data | SAGEM SCT U34 | CREATE BINARY |
226 | +------------+------------------------+------------------------+----------------------+--------------------------------+
227 | | 80 EE 00 xx 00 | VSDC | WRITE LOCK |
228 | +------------+------------------------+------------------------+----------------------+--------------------------------+
229 | | 80 F0 xx xx xx AID of Application (and MAC) | GlobalPlatform | SET STATUS |
230 | +------------+------------------------+------------------------+----------------------+--------------------------------+
231 | | A0 F2 00 00 xx | 3GPP TS 11.11 | GET STATUS |
232 | | A0 F2 00 00 xx | SAGEM SCT U34 6.2 | GET STATUS |
233 | | 80 F2 xx xx | GlobalPlatform | GET STATUS |
234 | +------------+------------------------+------------------------+----------------------+--------------------------------+
235 | | 80 F8 xx xx xx | SAGEM SCT U34 8.1.1 | DIR |
236 | +------------+------------------------+------------------------+----------------------+--------------------------------+
237 | | A0 FA 00 00 00 | 3GPP TS 11.11 | SLEEP |
238 | | A0 FA 00 00 00 | SAGEM SCT U34 6.18 | SLEEP |
239 | +------------+------------------------+------------------------+----------------------+--------------------------------+
240 | | 80 FB xx xx xx | SAGEM SCT U34 8.1.1 | DIR |
241 | +------------+------------------------+------------------------+----------------------+--------------------------------+
242 | | 80 FC xx xx 10 | SAGEM SCT U34 8.1.3 | READ INFO |
243 | +------------+------------------------+------------------------+----------------------+--------------------------------+
244 | | FE | ISO 7816-9 6.7 | TERMINATE CARD USAGE |
245 | | 80 FE xx xx 00 | SAGEM SCT U34 | BLOW FUSE |
246 | +------------+------------------------+------------------------+----------------------+--------------------------------+
247 |
248 |
--------------------------------------------------------------------------------
/llsmartcard/card.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015, MASSACHUSETTS INSTITUTE OF TECHNOLOGY
2 | # Subject to FAR 52.227-11 – Patent Rights – Ownership by the Contractor (May 2014).
3 | # SPDX-License-Identifier: BSD-3-Clause
4 |
5 | # Native
6 | import logging
7 | logger = logging.getLogger(__name__)
8 | import struct
9 | import subprocess
10 | import os
11 |
12 | # Pysmartcard
13 | from smartcard.sw.ISO7816_4ErrorChecker import ISO7816_4ErrorChecker
14 | from smartcard.sw.ISO7816_8ErrorChecker import ISO7816_8ErrorChecker
15 | from smartcard.sw.ISO7816_9ErrorChecker import ISO7816_9ErrorChecker
16 | from smartcard.sw.ErrorCheckingChain import ErrorCheckingChain
17 | from smartcard.sw.SWExceptions import SWException
18 |
19 | # Pydes
20 | import pyDes
21 |
22 | # LL Smartcard
23 | import apdu as APDU
24 |
25 | class SmartCard:
26 |
27 | # Setup our error chain
28 | errorchain = []
29 | errorchain = [ ErrorCheckingChain(errorchain, ISO7816_9ErrorChecker()),
30 | ErrorCheckingChain(errorchain, ISO7816_8ErrorChecker()),
31 | ErrorCheckingChain(errorchain, ISO7816_4ErrorChecker()) ]
32 |
33 | def __init__(self, connection, log_level=None):
34 |
35 | self.SECURE_CHANNEL = False
36 | self.session_keys = None
37 | if log_level is not None:
38 | logging.basicConfig(level=log_level)
39 | self.__conn = connection
40 |
41 | def _log_apdu(self, apdu_data):
42 | logger.debug("APDU: " + APDU.get_hex(apdu_data))
43 |
44 |
45 | def _log_result(self, data, sw1, sw2):
46 | logger.debug("RESULT: (%s,%s) %s (str: %s)" % (hex(sw1),
47 | hex(sw2),
48 | APDU.get_hex(data),
49 | APDU.get_str(data)))
50 | # See if our status word was an error
51 | try:
52 | self.errorchain[0]([], sw1, sw2)
53 | except SWException, e:
54 | # Did we get an unsuccessful attempt?
55 | logger.debug(e)
56 |
57 |
58 | def _send_apdu(self, apdu_data):
59 | """
60 | Send the proper APDU, depending on what mode we are operating in.
61 |
62 | @param apdu_data: RAW APDU data to send
63 |
64 | @return: (data, sw1, sw2)
65 | """
66 | if self.SECURE_CHANNEL is False or self.SECURE_CHANNEL == APDU.SECURE_CHANNEL.MODE.NONE:
67 | return self._send_apdu_raw(apdu_data)
68 | elif self.SECURE_CHANNEL == APDU.SECURE_CHANNEL.MODE.MAC:
69 | return self._send_apdu_mac(apdu_data)
70 | else:
71 | logger.error("This Secure Channel APDU mode is not currently supported.")
72 |
73 |
74 | def _send_apdu_raw(self, apdu_data):
75 | """
76 | Send APDU to card and return the data and status words.
77 | If the result has more data, this will also retrieve the additional
78 | data.
79 |
80 | @param apdu_data: RAW APDU to send stored in a list
81 | @return: (data, sw1, sw2)
82 | """
83 | # Send the APDU
84 | self._log_apdu(apdu_data)
85 | data, sw1, sw2 = self.__conn.transmit(apdu_data)
86 | self._log_result(data, sw1, sw2)
87 |
88 | # Is there more data in the response?
89 | while sw1 == APDU.APDU_STATUS.MORE_DATA:
90 | apdu_get_response = APDU.GET_RESPONSE(sw2)
91 | self._log_apdu(apdu_get_response)
92 | data2, sw1, sw2 = self.__conn.transmit(apdu_get_response)
93 | data += data2
94 | self._log_result(data2, sw1, sw2)
95 |
96 | # Return our status and data
97 | return (data, sw1, sw2)
98 |
99 | def _send_apdu_mac(self, apdu_data):
100 | """
101 | Send a secure APDU. This is done by calculating a C-MAC and
102 | appending it to the end of the message
103 |
104 | IMPORTANT: This will automatically adjust Lc for you!
105 |
106 | @param apdu_data: APDU data to send
107 | @return: data, sw1, sw2
108 | """
109 | if self.session_keys is None:
110 | logger.error("A secure session has not been established.")
111 | return
112 |
113 | if apdu_data[0] != 0x84:
114 | logger.warn("Class is not 0x84 in secure message.")
115 | apdu_data[0] = 0x84
116 |
117 |
118 | # Trim Le if needed
119 | Le = 0x00
120 | if len(apdu_data) > 5 + apdu_data[4]:
121 | Le = apdu_data[-1]
122 | apdu_data = apdu_data[:-1]
123 |
124 | # Increment Lc
125 | apdu_data[4] = apdu_data[4] + 8
126 |
127 |
128 | # Use our MAC key
129 | mac_key = self.session_keys[APDU.AUTH_KEY_IDX.MAC]
130 |
131 | # Setup our 3-DES MAC instance
132 | des3_mac = pyDes.triple_des(mac_key, mode=pyDes.CBC,
133 | IV="\x00\x00\x00\x00\x00\x00\x00\x00")
134 |
135 | # Add padding and pack it up for pyDes
136 | apdu_extern_auth_packed = [struct.pack('B', x) for x in self._pad_plaintext(apdu_data)]
137 |
138 | c_mac = des3_mac.encrypt(apdu_extern_auth_packed)[-8:]
139 | c_mac = list(struct.unpack('%dB' % len(c_mac), c_mac))
140 |
141 | logger.debug("C-MAC: %s" % APDU.get_hex(c_mac))
142 |
143 | # Append MAC to APDU
144 | apdu_data += c_mac + [Le]
145 |
146 | # Send appended APDU
147 | return self._send_apdu_raw(apdu_data)
148 |
149 |
150 | def _report_error(self, sw1, sw2, error):
151 | """ Print Error """
152 | # @todo: Figure out the SW1 SW2 meaning
153 |
154 | print "ERROR (%s,%s): %s" % (hex(sw1), hex(sw2), error)
155 |
156 |
157 | def _generate_random(self, length):
158 | """
159 | Generate a list of random bytes
160 | @param length: Number of bytes to generate
161 | @return: List of random bytes
162 | """
163 | rtn = []
164 | for i in range(length):
165 | rtn.append(ord(os.urandom(1)))
166 |
167 | return rtn
168 |
169 |
170 | def _pad_plaintext(self, plaintext):
171 | """
172 | Pad out any plaintext to be fed into MAC functions
173 |
174 | @param plaintext: plaintext to pad
175 | @return: a copy of plaintext with padding appended
176 | """
177 | # ensure the plaintext is divisible by 8 and that at least some padding is added
178 | pad = False
179 | plaintext = list(plaintext)
180 | while len(plaintext) % 8 != 0 or not pad:
181 | if pad:
182 | plaintext.append(0x00)
183 | else:
184 | plaintext.append(0x80)
185 | pad = True
186 |
187 | return plaintext
188 |
189 |
190 | def _str_privs(self, privs):
191 | """
192 |
193 | """
194 | out = []
195 | if 0b10000000 & privs:
196 | out.append("Security Domain")
197 | if 0b01000000 & privs:
198 | out.append("DAP DES Verification")
199 | if 0b00100000 & privs:
200 | out.append("RFU")
201 | if 0b00010000 & privs:
202 | out.append("Card Manager Lock Privilege")
203 | if 0b00001000 & privs:
204 | out.append("Card Terminate Privilege")
205 | if 0b00000100 & privs:
206 | out.append("Default Selected Applet")
207 | if 0b00000010 & privs:
208 | out.append("PIN Change")
209 | if 0b00000001 & privs:
210 | out.append("RFU")
211 |
212 | return out
213 |
214 |
215 | def _print_gp_registry_data(self, input_data):
216 | """
217 | Decode Applicaiton Data
218 | Reference: GP 2.1.1 / page 115
219 | """
220 | offset = 0
221 | while offset < len(input_data):
222 | t = input_data[offset]
223 | if t == 0x9F and input_data[offset + 1] == 0x70:
224 | t = 0x9f70
225 | offset += 1
226 | length = input_data[offset + 1]
227 | value = input_data[offset + 2:offset + 2 + length]
228 |
229 | if t == 0x4f:
230 | print "AID: %s" % APDU.get_hex(value)
231 | elif t == 0x9f70:
232 | print " Life Cycle State: %08s" % '{0:08b}'.format(value[0])
233 | elif t == 0xc5:
234 | print " Application Privleges: %s %s" % (APDU.get_hex(value),
235 | self._str_privs(value[0]))
236 | elif t == 0x84:
237 | print " Executable Module ID: %s" % (APDU.get_hex(value))
238 | else:
239 | print " UNKNOWN: t:%s, l:%s, v:%s" % (hex(t), hex(length),
240 | APDU.get_hex(value))
241 |
242 | offset += length + 2
243 |
244 |
245 | def apdu_select_application(self, application_id, pix=[], p1=0x04, p2=0x00):
246 | """
247 | Send APDU to select a Java Applet and return results
248 |
249 | @param application_id: The AID of the application on the card in a list
250 | @return: (data, sw1, sw2)
251 | """
252 | apdu_select = APDU.SELECT(application_id + pix, P1=p1, P2=p2)
253 |
254 | data, sw1, sw2 = self._send_apdu(apdu_select)
255 |
256 | if sw1 != APDU.APDU_STATUS.SUCCESS:
257 | print "ERROR (%s,%s): SELECT failed." % (hex(sw1), hex(sw2))
258 |
259 | return (data, sw1, sw2)
260 |
261 |
262 | def apdu_select_object(self, object_id, p1=0x02, p2=0x00):
263 | """
264 | Send APDU to select a Java Applet and return results
265 |
266 | @param object_id: The OID of the object on the card in a list
267 | @return: (data, sw1, sw2)
268 | """
269 | apdu_select = APDU.SELECT(object_id, P1=p1, P2=p2)
270 |
271 | data, sw1, sw2 = self._send_apdu(apdu_select)
272 |
273 | if sw1 != APDU.APDU_STATUS.SUCCESS:
274 | print "ERROR (%s,%s): SELECT failed." % (hex(sw1), hex(sw2))
275 |
276 | return (data, sw1, sw2)
277 |
278 |
279 | def apdu_select_df(self, file_id, p1=0x01, p2=0x00):
280 | """
281 | Send APDU to select a Directory File (Within master file)
282 |
283 | @param file_id: 2 byte file ID to select
284 | @param p2: 0x00 or 0x0c
285 | @return: (data, sw1, sw2)
286 | """
287 | apdu_select = APDU.SELECT(file_id, P1=p1, P2=p2)
288 |
289 | data, sw1, sw2 = self._send_apdu(apdu_select)
290 |
291 | if sw1 != APDU.APDU_STATUS.SUCCESS:
292 | print "ERROR (%s,%s): SELECT failed." % (hex(sw1), hex(sw2))
293 |
294 | return (data, sw1, sw2)
295 |
296 |
297 | def apdu_select_ef(self, file_id, p1=0x02, p2=0x00):
298 | """
299 | Send APDU to select a Elementary File (or Object)
300 |
301 | @param file_id: 2 byte file ID to select
302 | @param p2: 0x00 or 0x0c
303 | @return: (data, sw1, sw2)
304 | """
305 | apdu_select = APDU.SELECT(file_id, P1=p1, P2=p2)
306 |
307 | data, sw1, sw2 = self._send_apdu(apdu_select)
308 |
309 | if sw1 != APDU.APDU_STATUS.SUCCESS:
310 | print "ERROR (%s,%s): SELECT failed." % (hex(sw1), hex(sw2))
311 |
312 | return (data, sw1, sw2)
313 |
314 |
315 | def apdu_select_mf(self, file_id, p1=0x03, p2=0x00):
316 | """
317 | Send APDU to select a Master File
318 |
319 | @param file_id: 2 byte file ID to select
320 | @param p2: 0x00 or 0x0c
321 | @return: (data, sw1, sw2)
322 | """
323 | # @todo: Try all classes
324 | apdu_select = APDU.SELECT(file_id, CLA=0x80, P1=p1, P2=p2)
325 |
326 | data, sw1, sw2 = self._send_apdu(apdu_select)
327 |
328 | if sw1 != APDU.APDU_STATUS.SUCCESS:
329 | print "ERROR (%s,%s): SELECT failed." % (hex(sw1), hex(sw2))
330 |
331 | return (data, sw1, sw2)
332 |
333 |
334 | def apdu_get_data(self, address):
335 | """
336 | Send APDU to get data and return results
337 |
338 | @param p1: high order byte
339 | @param p2: low order byte
340 | @return: (data, sw1, sw2)
341 | """
342 | p1 = address[0]
343 | p2 = address[1]
344 | apdu_get_data = APDU.GET_DATA(p1, p2)
345 | data, sw1, sw2 = self._send_apdu(apdu_get_data)
346 |
347 | if sw1 == APDU.APDU_STATUS.WRONG_LENGTH:
348 | apdu_get_data2 = APDU.GET_DATA(p1, p2, Lc=sw2)
349 | return self._send_apdu(apdu_get_data2)
350 |
351 | return (data, sw1, sw2)
352 |
353 |
354 | def apdu_read_record(self, p1, p2, cla=0x00):
355 | """
356 | Send APDU to get data and return results
357 |
358 | Reference: GP 2.1.1 / D.4.1
359 |
360 | @param p1: high order byte
361 | @param p2: low order byte
362 | @return: (data, sw1, sw2)
363 | """
364 | apdu_read_record = APDU.READ_RECORD(p1, p2, CLA=cla)
365 |
366 | data, sw1, sw2 = self._send_apdu(apdu_read_record)
367 |
368 | if sw1 == APDU.APDU_STATUS.WRONG_LENGTH:
369 | apdu_read_record2 = APDU.READ_RECORD(p1, p2, CLA=cla, Le=sw2)
370 | data, sw1, sw2 = self._send_apdu(apdu_read_record2)
371 | return (data, sw1, sw2)
372 |
373 |
374 | def apdu_init_update(self, p1, p2, challenge=None):
375 | """
376 | Send Initialize Update APDU to initialize a new secure channel.
377 |
378 | @param p1: Key version number (Default: 0)
379 | @param p2: Key identifier (Default: 0)
380 | @param challenge: 8 byte random number
381 | """
382 | # Generate a new random number?
383 | if challenge is None:
384 | challenge = self._generate_random(8)
385 |
386 | apdu_init_update = APDU.INIT_UPDATE(p1, p2, challenge)
387 |
388 | data, sw1, sw2 = self._send_apdu(apdu_init_update)
389 |
390 | return (data, sw1, sw2)
391 |
392 |
393 | def print_applications(self):
394 | """
395 | Once a secure channel is opened, list all Applications on the card.
396 | """
397 | for domain in [0x80, 0x40, 0x20, 0x10]:
398 |
399 | # Get Next
400 | apdu = APDU.GET_STATUS(domain, 0x02, APDU.SEARCH_CRITERIA.AID)
401 | data, sw1, sw2 = self._send_apdu(apdu)
402 | offset = 0
403 | while offset < len(data):
404 | t = data[offset]
405 | length = data[offset + 1]
406 | value = data[offset + 2:offset + 2 + length]
407 | if t == 0xE3:
408 | self._print_gp_registry_data(value)
409 | else:
410 | logger.error("Invalid data returned.")
411 | offset += length + 2
412 |
413 |
414 | return (data, sw1, sw2)
415 |
416 |
417 | def apdu_get_status(self, p1, p2, search_criteria):
418 | """
419 | Send Get Status APDU
420 |
421 | @param P1: 80 - Issuer Security Domain
422 | 40 - Application Security Domain
423 | 20 - Executable Load Files only
424 | 10 - Executable Load Files and their Executable Modules only
425 | @param P2: 0bx0 - get all/first occurrence(s)
426 | 0bx1 - get next
427 | 0b0x - Response Structure 1
428 | 0b1x - Response Structure 2
429 | @param search_criteria: 4f00 used to indicated AID
430 |
431 | """
432 | apdu = APDU.GET_STATUS(p1, p2, search_criteria)
433 | data, sw1, sw2 = self._send_apdu(apdu)
434 | return (data, sw1, sw2)
435 |
436 |
437 | def apdu_authenticate(self, card_challenge, rand_challenge, cryptogram,
438 | security_level=APDU.SECURE_CHANNEL.MODE.NONE):
439 | """
440 | Given both of our Nonces, send back our authentication apdu
441 |
442 | @param card_challege: Nonce from card
443 | @param rand_challege: Nonce generated by host
444 | @param cryptogram: Cryptogram sent by card
445 | @return: data, sw1, sw2
446 | """
447 |
448 | if self.session_keys is None:
449 | logger.error("Secure Channel hasn't be opened yet.")
450 | return
451 |
452 | # Get ready for authentication
453 | auth_key = self.session_keys[APDU.AUTH_KEY_IDX.AUTH]
454 |
455 | # Setup our 3-DES MAC instance
456 | des3_auth = pyDes.triple_des(auth_key, mode=pyDes.CBC,
457 | IV="\x00\x00\x00\x00\x00\x00\x00\x00")
458 |
459 | #
460 | # Validate our cryptogram
461 | #
462 | # Generate our plaintext
463 | card_cryptogram_plaintext = rand_challenge + card_challenge
464 | # Pad appropriately
465 | card_cryptogram_plaintext = self._pad_plaintext(card_cryptogram_plaintext)
466 | # Pack up for pyDes
467 | card_cryptogram_plaintext = [struct.pack('B', x) for x in card_cryptogram_plaintext]
468 |
469 | # Generate our cryptogram
470 | cryptogram_host_ciphertext = des3_auth.encrypt(card_cryptogram_plaintext)
471 | cryptogram_host_mac = cryptogram_host_ciphertext[-8:]
472 | cryptogram_host = struct.unpack('%dB' % len(cryptogram_host_mac), cryptogram_host_mac)
473 | cryptogram_host = list(cryptogram_host)
474 |
475 | # Check our cryptogram
476 | if cryptogram_host != cryptogram:
477 | logger.error("Cryptogram Invalid for this card!")
478 | return False
479 |
480 | #
481 | # Generate our authentication response
482 | #
483 | # Generate Plaintext
484 | card_auth_plaintext = card_challenge + rand_challenge
485 |
486 | # Pad appropriately
487 | card_auth_plaintext = self._pad_plaintext(card_auth_plaintext)
488 |
489 | # Pack up for pyDes
490 | card_auth_plaintext = [struct.pack('B', x) for x in card_auth_plaintext]
491 |
492 | # Generate our authentication response
493 | auth_host_ciphertext = des3_auth.encrypt(card_auth_plaintext)[-8:]
494 | auth_cryptogram = list(struct.unpack('%dB' % len(auth_host_ciphertext), auth_host_ciphertext))
495 |
496 | logger.debug("Authentication Cryptogram: %s" % APDU.get_hex(auth_cryptogram))
497 |
498 | # Generate our C-MAC for the response
499 | apdu_extern_auth = APDU.EXTERNAL_AUTHENTICATE(security_level,
500 | auth_cryptogram, [])
501 |
502 | # Send the APDU in C-MAC mode
503 | return self._send_apdu_mac(apdu_extern_auth)
504 |
505 |
506 | def open_secure_channel(self, aid, keys,
507 | security_level=APDU.SECURE_CHANNEL.MODE.NONE):
508 | """
509 | Open secure channel to allow security functions
510 |
511 | @param keys: Keys to use for this channel
512 | @return: True/False
513 | """
514 | self.keys = keys
515 |
516 | #
517 | # Define some supplementary functions
518 | #
519 | def fill_data(diversify_data, idx, diver_type=APDU.SECURE_CHANNEL.DIVERSIFY.VISA2):
520 | """
521 | Given the diversity data from the card and the index for the key
522 | to be generated, this will fill out the data appropriately to
523 | generate a diversified key
524 |
525 | @param diversify_data: diversity data from the card
526 | @param idx: key index to generate the plaintext for
527 | @diver_type: Type of diversification, VISA2 or default
528 | """
529 | data = []
530 | if diver_type == APDU.SECURE_CHANNEL.DIVERSIFY.VISA2:
531 | # VISA2
532 | data.append(diversify_data[0])
533 | data.append(diversify_data[1])
534 | data.append(diversify_data[4])
535 | data.append(diversify_data[5])
536 | data.append(diversify_data[6])
537 | data.append(diversify_data[7])
538 | data.append(0xF0)
539 | data.append(idx)
540 | data.append(diversify_data[0])
541 | data.append(diversify_data[1])
542 | data.append(diversify_data[4])
543 | data.append(diversify_data[5])
544 | data.append(diversify_data[6])
545 | data.append(diversify_data[7])
546 | data.append(0x0F)
547 | data.append(idx)
548 |
549 | elif diver_type is None:
550 | # EMV
551 | data.append(diversify_data[4])
552 | data.append(diversify_data[5])
553 | data.append(diversify_data[6])
554 | data.append(diversify_data[7])
555 | data.append(diversify_data[8])
556 | data.append(diversify_data[9])
557 | data.append(0xF0)
558 | data.append(idx)
559 | data.append(diversify_data[4])
560 | data.append(diversify_data[5])
561 | data.append(diversify_data[6])
562 | data.append(diversify_data[7])
563 | data.append(diversify_data[8])
564 | data.append(diversify_data[9])
565 | data.append(0x0F)
566 | data.append(idx)
567 | else:
568 | return None
569 |
570 | return data
571 |
572 |
573 | def get_diversified_keys(keys, diversify_data, diver_type=APDU.SECURE_CHANNEL.DIVERSIFY.VISA2):
574 | """
575 | Given the keys and diversity data from the card, generate the
576 | diversified keys.
577 |
578 | @param keys: keys to be diversified
579 | @param diversify_data: Diversity data from the card
580 | @param diver_type: VISA or default
581 | @return: List of diversified keys
582 | """
583 |
584 | logger.debug("Diversifying keys...")
585 |
586 | # Diversify each key
587 | for i in range(len(keys)):
588 | # Get the data to encrypt
589 | data = fill_data(diversify_data, i + 1, diver_type)
590 |
591 | logger.debug("data: %s" % data)
592 | logger.debug("key: %s" % keys[i])
593 |
594 | # Unpack in the form that pyDes Expects
595 | data = [struct.pack('B', x) for x in data]
596 |
597 | # Encrypt data to get new key
598 | des3 = pyDes.triple_des(keys[i])
599 | keys[i] = des3.encrypt(data)
600 |
601 | return keys
602 |
603 | def get_session_keys(keys, rand_challenge, card_challenge):
604 | """
605 | Derive the session keys using the two nonces and the input keys
606 |
607 | @param keys: Keys to use for this session
608 | @param rand_challenge: Nonce sent from host
609 | @para card_challenge: Nonce sent from card
610 |
611 | @return: list of session keys
612 | """
613 | derivation_data = card_challenge[4:] + \
614 | rand_challenge[0:4] + \
615 | card_challenge[0:4] + \
616 | rand_challenge[4:]
617 |
618 | logger.debug("Deriving session keys..")
619 | logger.debug("derivData: %s" % derivation_data)
620 |
621 | # Unpack in the form that pyDes Expects
622 | derivation_data = [struct.pack('B', x) for x in derivation_data]
623 |
624 | session_keys = []
625 |
626 | for i in range(len(keys) - 1):
627 | # Pack in the form that pyDes Expects
628 | des3 = pyDes.triple_des(keys[i])
629 | session_key = des3.encrypt(derivation_data)
630 |
631 | # session_key = struct.unpack('%dB' % len(session_key), session_key)
632 |
633 | session_keys.append(session_key)
634 |
635 |
636 | # The last key remains the same
637 | session_keys.append(keys[2])
638 |
639 | logger.debug("Session keys: %s" % session_keys)
640 |
641 | return session_keys
642 |
643 |
644 | #
645 | # Begin actual function
646 | #
647 | logger.debug("Opening secure channel...")
648 |
649 | # Save our keys
650 | self.keys = []
651 | for k in keys:
652 | self.keys.append([struct.pack('B', x) for x in k])
653 |
654 | # Generate an 8 byte nonce
655 | rand_challenge = self._generate_random(8)
656 |
657 | # Initialize our authentication
658 | (data, sw1, sw2) = self.apdu_init_update(0, 0, challenge=rand_challenge)
659 |
660 | # Override results for debugging?
661 | # rand_challenge = [0xA6, 0x1E, 0xF6, 0x6D, 0x6A, 0x27, 0x0E, 0x9A]
662 | # data = [0x00, 0x00, 0x21, 0x80, 0x88, 0x10, 0x0B, 0x15, 0x20, 0xCB, 0x01, 0x01, 0x23, 0xCC, 0x76, 0xF2, 0xB2, 0x88, 0x01, 0x73, 0x07, 0xAD, 0xEF, 0xAD, 0x97, 0xAA, 0xFC, 0x0B, 0x90, 0x00]
663 |
664 | if (sw1, sw2) != APDU.STATUS_WORDS.SUCCESS:
665 | logger.error("INIT UPDATE failed.")
666 | return
667 |
668 | # Extract the parameters from our data
669 | key_diversification_data = data[0:10]
670 | key_info = data[10:12]
671 | card_challenge = data[12:20]
672 | cryptogram_card = data[20:28]
673 |
674 | # Log some stuff
675 | logger.debug("Key Diversification: %s" % APDU.get_hex(key_diversification_data))
676 | logger.debug("Key Info: %s" % APDU.get_hex(key_info))
677 | logger.debug("Card Challenge: %s" % APDU.get_hex(card_challenge))
678 | logger.debug("Card Cryptogram: %s" % APDU.get_hex(cryptogram_card))
679 |
680 | # Diversify our keys
681 | diversified_keys = get_diversified_keys(self.keys, key_diversification_data)
682 |
683 | # Derive session keys
684 | self.session_keys = get_session_keys(diversified_keys, rand_challenge, card_challenge)
685 |
686 | # Authenticate to the card
687 | self.apdu_authenticate(card_challenge, rand_challenge, cryptogram_card,
688 | security_level=security_level)
689 |
690 | logger.debug("Secure Channel Opened!")
691 |
692 | self.SECURE_CHANNEL = security_level
693 |
694 |
695 | def apdu_verify_pin(self, pin, location, pad_pin=8):
696 | """
697 | Send a VERIFY PIN for smartcard
698 |
699 | @param pin: pin to enter
700 | @param location: location of pin (1 byte)
701 | @param pad_pin: Number of bytes to pad pin to (padding is 0xff)
702 | """
703 | # Do we need to pad the pin?
704 | while len(pin) < pad_pin:
705 | pin.append(0xff)
706 |
707 | apdu = APDU.VERIFY_PIN(location, pin)
708 |
709 | data, sw1, sw2 = self._send_apdu(apdu)
710 |
711 | if sw1 != APDU.APDU_STATUS.SUCCESS:
712 | print "ERROR (%s,%s): VERIFY PIN failed." % (hex(sw1), hex(sw2))
713 | if sw1 == 0x63 and sw2 & 0xc0 == 0xc0:
714 | print "WARNING: %d tries remaining!" % (sw2 & 0x0F)
715 | else:
716 | print "* Key Authentication Successful!"
717 | return (data, sw1, sw2)
718 |
719 |
720 | def apdu_change_reference_data(self, location, old_pin, new_pin,
721 | pad_pin=8, first=False):
722 | """
723 | Change the reference data on the card, e.g. PIN
724 |
725 | @param location: 0x00 - Global,
726 | 0x80 - Application,
727 | 0x81, Application PUK
728 | @param old_pin: Existing PIN
729 | @param new_pin: New PIN
730 | @param pad_pin: How many bytes should the PIN be?
731 | @param first: Is this the first time setting the PIN?
732 |
733 | @return (data, sw1, sw2)
734 | """
735 | # Do we need to pad the pin?
736 | while len(new_pin) < pad_pin:
737 | new_pin.append(0xff)
738 | while len(old_pin) < pad_pin:
739 | old_pin.append(0xff)
740 |
741 | if first:
742 | P1 = 0x01
743 | old_pin = []
744 | else:
745 | P1 = 0x00
746 | P2 = location
747 |
748 | apdu = APDU.CHANGE_REFERENCE_DATA(P1, P2, old_pin, new_pin)
749 |
750 | data, sw1, sw2 = self._send_apdu(apdu)
751 |
752 | return (data, sw1, sw2)
753 |
754 |
755 | def apdu_reset_retry_counter(self, puk, location, new_pin, pad_pin=8):
756 | """
757 | Reset the retry counter using the PUK
758 | """
759 |
760 | # Do we need to pad the pin?
761 | if puk is not None:
762 | while len(puk) < pad_pin:
763 | puk.append(0xff)
764 |
765 | if new_pin is not None:
766 | while len(new_pin) < pad_pin:
767 | new_pin.append(0xff)
768 |
769 | # This is according to the ISO spec, but doesn't seem to work
770 | if new_pin is None and puk is None:
771 | P1 = 0x03
772 | elif new_pin is None:
773 | P1 = 0x01
774 | elif puk is None:
775 | P1 = 0x02
776 | else:
777 | P1 = 0x00
778 |
779 | if self.SECURE_CHANNEL is False:
780 | P1 = 0x00
781 | else:
782 | P1 = 0x00
783 |
784 |
785 | apdu = APDU.RESET_RETRY_COUNT(P1, location, puk, new_pin, CLA=0x00)
786 |
787 | data, sw1, sw2 = self._send_apdu(apdu)
788 |
789 | if sw1 != APDU.APDU_STATUS.SUCCESS:
790 | print "ERROR (%s,%s): VERIFY PIN failed." % (hex(sw1), hex(sw2))
791 | if sw1 == 0x63 and sw2 & 0xc0 == 0xc0:
792 | print "WARNING: %d tries remaining!" % (sw2 & 0x0F)
793 | else:
794 | print "* PIN retry has been reset."
795 | return (data, sw1, sw2)
796 |
797 |
798 | def apdu_set_status(self, status_type, status_state, aid=[]):
799 | """
800 | Set the status of an application on the smartcard
801 | """
802 |
803 | apdu = APDU.SET_STATUS(status_type, status_state, aid)
804 |
805 | return self._send_apdu(apdu)
806 |
807 |
808 |
809 | def dump_records(self):
810 | for sfi in range(32):
811 | for rec in range (17):
812 | logger.debug("REC %d SFI %d" % (rec, (sfi << 3) | 4))
813 | data, sw1, sw2 = self.apdu_read_record(rec, (sfi << 3) | 4)
814 | if sw1 == APDU.APDU_STATUS.SUCCESS:
815 | print "REC %d SFI %d" % (rec, (sfi << 3) | 4)
816 | print "Hex: %s" % APDU.get_hex(data)
817 | print "Str: %s" % APDU.get_str(data)
818 |
819 |
820 | def _decode_bcd(self, bcd_num):
821 | """
822 | Given a 5 bit Binary Coded Decimal, decode back to the appropriate string
823 |
824 | @param bcd_num : 5 bit Binary Coded Decimal number
825 | @return: Character represenation
826 | """
827 | bcd_table = {'0':0b00001,
828 | '1':0b10000,
829 | '2':0b01000,
830 | '3':0b11001,
831 | '4':0b00100,
832 | '5':0b10101,
833 | '6':0b01101,
834 | '7':0b11100,
835 | '8':0b00010,
836 | '9':0b10011,
837 | 'SS':0b11010,
838 | 'FS':0b10110,
839 | 'ES':0b11111}
840 | for char in bcd_table:
841 | if bcd_table[char] == bcd_num:
842 | return char
843 |
844 | return None
845 |
846 |
847 | def _get_ber_tlv(self, data, offset=0):
848 | """
849 | Get the next BER-TLV value from data
850 |
851 | @param data: Data encoded with BER-TLV
852 | @param offset: Offset into data buffer
853 | @return: [type, length, value, next_tlv]
854 | """
855 |
856 | tlv_type = data[offset]
857 |
858 | if data[offset + 1] == 0x81:
859 | tlv_length = data[offset + 2]
860 | tlv_value = data[offset + 3:offset + 3 + tlv_length]
861 | next_tlv = tlv_length + 3
862 |
863 | elif data[offset + 1] == 0x82:
864 | tlv_length = data[offset + 2] << 8 | data[offset + 3]
865 | tlv_value = data[offset + 4:offset + 4 + tlv_length]
866 | next_tlv = tlv_length + 4
867 |
868 | else:
869 | tlv_length = data[offset + 1]
870 | tlv_value = data[offset + 2:offset + 2 + tlv_length]
871 | next_tlv = tlv_length + 2
872 |
873 | return [tlv_type, tlv_value, next_tlv]
874 |
875 |
876 | def _decode_ber_tlv(self, data):
877 | """
878 | Read BER-TLV data and return list of [type, data] pairs
879 |
880 | Ref: CAC Endpoint Implementation Guide v1
881 |
882 | @param data:
883 | @return: list of [type, data] pairs
884 | """
885 | offset = 0
886 |
887 | rtn_list = []
888 | while offset < len(data):
889 | # Get Data
890 | tlv_type, tlv_value, next_tlv = self._get_ber_tlv(data, offset)
891 | # Update pointer into buffer
892 | offset += next_tlv
893 | # append to our results
894 | rtn_list.append([tlv_type, tlv_value])
895 |
896 | # If its tag type 0x53, just return the data
897 | tlv = self._get_ber_tlv(data)
898 | if tlv[0] == 0x53:
899 | data = tlv[1]
900 | return self._decode_ber_tlv(data)
901 | else:
902 | return rtn_list
903 |
904 |
905 | class CAC(SmartCard):
906 | """
907 | This class implements some knwown functionality for the DoD CAC smartcard.
908 | """
909 |
910 | def _lookup_agency(self, code):
911 | """
912 | Converts agency code string in CHUID to the actual name of the agency
913 |
914 | ref: http://csrc.nist.gov/publications/nistpubs/800-87-Rev1/SP800-87_Rev1-April2008Final.pdf
915 |
916 | @param code: String code of Agency
917 | @return: String name of Agency or Uknown
918 | """
919 | agency_table = {'9700':'DEFENSE, Department of (except military departments)',
920 | '5700':'AIR FORCE, Department of the (Headquarters, USAF) '}
921 | if code in agency_table:
922 | return agency_table[code]
923 | else:
924 | return "Unknown (See: SP800-87)"
925 |
926 |
927 | def _lookup_oc(self, code):
928 | """
929 | Convert organization code in CHUID to name of organization
930 |
931 | @param code: character code of organization
932 | @return: Name of organization
933 | """
934 | table = {'1':'Federal Government Agency',
935 | '2':'State Government Agency',
936 | '3':'Commercial Enterprise',
937 | '4':'Foreign Government'}
938 | if code in table:
939 | return "%s (%s)" % (table[code], int(code))
940 | else:
941 | return "Unknown (See: SP800-87)"
942 |
943 | def _lookup_poa(self, code):
944 | """
945 | Convert Personal Association Category code in CHUID to string name
946 |
947 | @param code: Character poa code
948 | @return: String of association
949 | """
950 | table = {'1': 'Employee',
951 | '2': 'Civil',
952 | '3': 'Executive Staff',
953 | '4': 'Uniformed Service',
954 | '5': 'Contractor',
955 | '6': 'Organizational Affiliate',
956 | '7': 'Organizational Beneficiary'}
957 | if code in table:
958 | return "%s (%s)" % (table[code], int(code))
959 | else:
960 | return "%s (See: (See: SP800-87)" % hex(code)
961 |
962 |
963 | def _lookup_card_app_type(self, code):
964 | """
965 | Lookup Card Application Type from CardURL
966 |
967 | @param code: Byte encoding app type
968 | @return: Application type
969 | """
970 | table = {0x01: 'genericContainer',
971 | 0x02: 'ski',
972 | 0x04: 'ski'}
973 | if code in table:
974 | return "%s (%s)" % (table[code], hex(code))
975 | else:
976 | return "%s (See: GSC-IS 7-1)" % hex(code)
977 |
978 |
979 | def _lookup_card_object_id(self, code):
980 | """
981 | Lookup Card Object ID from CardURL
982 |
983 | @param code: Byte encoding object ID
984 | @return: Object Name
985 | """
986 | code = code[0] << 8 | code[1]
987 | table = { 0x2000:'generalInfo',
988 | 0x2100:'proPersonalInfo',
989 | 0x3000:'accessControl',
990 | 0x4000:'login',
991 | 0x5000:'cardInfo',
992 | 0x6000:'biometrics',
993 | 0x7000:'digitalSigCert',
994 | # -- CAC data model definitions
995 | 0x0200:'personInstance',
996 | 0x0202:'benefitsInfo',
997 | 0x0203:'otherBenefits',
998 | 0x0201:'personnel',
999 | 0x0300:'loginInfo',
1000 | 0x02FE:'pkiCert',
1001 | # -- Common definitions
1002 | 0x0007:'SEIWG'}
1003 | if code in table:
1004 | return "%s (%s)" % (table[code], hex(code))
1005 | else:
1006 | return "%s (See: GSC-IS 7-1)" % hex(code)
1007 |
1008 | def _lookup_key_crypto(self, code):
1009 | """
1010 | Lookup Key Crypto Algorithm from CardURL
1011 |
1012 | @param code: Byte encoding of Key Crypto Algorithm
1013 | @return: Crypto Algorithm
1014 | """
1015 | table = { 0x00:'DES3-16-ECB',
1016 | 0x01:'DES3-16-CBC',
1017 | 0x02:'DES-ECB',
1018 | 0x03:'DES-CBC',
1019 | 0x04:'RSA512',
1020 | 0x05:'RSA768',
1021 | 0x06:'RSA1024',
1022 | 0x07:'RSA2048',
1023 | 0x08:'AES128-ECB',
1024 | 0x09:'AES128-CBC',
1025 | 0x0a:'AES192-ECB',
1026 | 0x0b:'AES192-CBC',
1027 | 0x0c:'AES256-ECB',
1028 | 0x0d:'AES256-CBC'}
1029 | if code in table:
1030 | return "%s (%s)" % (table[code], hex(code))
1031 | else:
1032 | return "%s (See: GSC-IS 7-1)" % hex(code)
1033 |
1034 | def _lookup_card_type(self, code):
1035 | """
1036 | Lookup Card Type
1037 |
1038 | @param code: Byte encoding of Key Crypto Algorithm
1039 | @return: Crypto Algorithm
1040 | """
1041 | table = { 0x01:'File System',
1042 | 0x02:'Java Card'
1043 | }
1044 | if code in table:
1045 | return "%s (%s)" % (table[code], hex(code))
1046 | else:
1047 | return "%s (See: CAC End-Point Impelementation Guide)" % hex(code)
1048 |
1049 |
1050 | def _lookup_cert(self, registered_id, object_id):
1051 | """
1052 | Lookup the name of the certificate given its RID and OID
1053 |
1054 | @param registered_id: RID of cert in question
1055 | @param object_id: Object ID of cert in question
1056 | @return: Name of the cert being references
1057 | """
1058 |
1059 | table = { APDU.get_hex(APDU.OBJ_NIST_PIV.KEY_CRD_ATH):'Card Authentication (NIST)',
1060 | APDU.get_hex(APDU.OBJ_NIST_PIV.KEY_DIG_SIG):'Digital Signature (NIST)',
1061 | APDU.get_hex(APDU.OBJ_NIST_PIV.KEY_MNG):'Key Management (NIST)',
1062 | APDU.get_hex(APDU.OBJ_NIST_PIV.KEY_PIV_ATH):'PIV Authentication (NIST)',
1063 | APDU.get_hex(APDU.OBJ_DOD_CAC.KEY_PKI_ENC):'Encryption (CaC)',
1064 | APDU.get_hex(APDU.OBJ_DOD_CAC.KEY_PKI_ID):'Identification (CaC)',
1065 | APDU.get_hex(APDU.OBJ_DOD_CAC.KEY_PKI_SIG):'Signature (CaC'}
1066 |
1067 | object_id = APDU.get_hex(object_id)
1068 | if object_id in table:
1069 | return "%s (%s)" % (table[object_id], object_id)
1070 | else:
1071 | return "Unknown (%s)" % object_id
1072 |
1073 |
1074 | def _splash(self, string):
1075 | """ Used to keep output pretty """
1076 | print "--------------------- %s ---------------------" % string
1077 |
1078 |
1079 | def _print_fasc_n(self, data):
1080 | """
1081 | Will print the FASC-N in human-readable form
1082 |
1083 | @param data: 25 byte bytestring containting FASC-N
1084 | """
1085 | # Frist combine into 1 binary string
1086 | fasc_n = 0
1087 | for i in range(len(data)):
1088 | fasc_n = fasc_n << 8 | data[i]
1089 |
1090 | # Now break out the 5 bit individual numbers
1091 | fasc_n_list = []
1092 | for i in reversed(range(40)):
1093 | mask = 0b11111 << i * 5
1094 | bcd_num = (fasc_n & mask) >> i * 5
1095 | # Decode and validate parity bits
1096 | fasc_n_list.append(self._decode_bcd(bcd_num))
1097 |
1098 | # Extract all of the fields
1099 | agency_code = "".join(fasc_n_list[1:5])
1100 | system_code = "".join(fasc_n_list[6:10])
1101 | credential_number = "".join(fasc_n_list[11:17])
1102 | cs = fasc_n_list[18]
1103 | ici = fasc_n_list[20]
1104 | pi = "".join(fasc_n_list[22:32])
1105 | oc = fasc_n_list[32]
1106 | oi = "".join(fasc_n_list[33:37])
1107 | poa = fasc_n_list[37]
1108 |
1109 | # print in nice format
1110 | print " FASC-N (SEIWG-012): %s" % hex(fasc_n)
1111 | print " Agency Code: %s / %s" % (agency_code, self._lookup_agency(agency_code))
1112 | print " System Code: %s" % system_code
1113 | print " Credential Number: %s" % credential_number
1114 | print " Credential Series: %s" % cs
1115 | print " Individual Credential Issue: %s" % ici
1116 | print " Person Identifier: %s" % pi
1117 | print " Organizational Category: %s / %s" % (oc, self._lookup_oc(oc))
1118 | print " Organizational Identifier: %s / %s" % (oi, self._lookup_agency(oi))
1119 | print " Person Association Category: %s / %s" % (poa, self._lookup_poa(poa))
1120 |
1121 |
1122 | def _print_ccc(self, tv_data, applet=None, object_id=None):
1123 | """
1124 | Prints Card Capability Container
1125 |
1126 | Ref: SP800-73-1
1127 | Ref: GSC-IS / Page 6-5
1128 |
1129 | @param tv_data: Type/Value data returned from a read_object call
1130 | """
1131 |
1132 | # Print results to terminal
1133 | self._splash("CCC (%s)" % APDU.get_hex(applet))
1134 | # Loop over type/value pairs
1135 | for tv in tv_data:
1136 | tlv_type = tv[0]
1137 | tlv_value = tv[1]
1138 | if tlv_type == 0xf0:
1139 | print " Card Identifier [%s]" % APDU.get_hex(tlv_value)
1140 | print " GSC-RID: %s" % APDU.get_hex(tlv_value[0:5])
1141 | print " Manufacturer ID: %s" % hex(tlv_value[5])
1142 | print " Card Type: %s" % self._lookup_card_type(tlv_value[6])
1143 | print " Card ID: %s | %s" % (APDU.get_hex(tlv_value[7:17]),
1144 | APDU.get_hex(tlv_value[17:22]))
1145 | elif tlv_type == 0xf1:
1146 | print " Capability Container version number: %s" % APDU.get_hex(tlv_value)
1147 | elif tlv_type == 0xf2:
1148 | print " Capability Grammar version number: %s" % APDU.get_hex(tlv_value)
1149 | elif tlv_type == 0xf3:
1150 | print " Applications CardURL [%s]" % APDU.get_hex(tlv_value)
1151 | print " RID: %s" % APDU.get_hex(tlv_value[0:5])
1152 | print " Application Type: %s" % self._lookup_card_app_type(tlv_value[5])
1153 | print " Object ID: %s" % self._lookup_card_object_id(tlv_value[6:8])
1154 | print " Application ID: %s" % APDU.get_hex(tlv_value[8:10])
1155 | print " AccProfile: %s" % hex(tlv_value[10])
1156 | print " PIN ID: %s" % hex(tlv_value[11])
1157 | print " AccKey Info: %s" % APDU.get_hex(tlv_value[12:16])
1158 | print " -- Alternate ---"
1159 | print " PIN ID: %s" % hex(tlv_value[8])
1160 | print " Key File ID: %s" % APDU.get_hex(tlv_value[9:11])
1161 | print " Key Number: %s" % hex(tlv_value[11])
1162 | print " Key Algorithm: %s" % self._lookup_key_crypto(tlv_value[12])
1163 | print " Key Algorithm: %s" % self._lookup_key_crypto(tlv_value[13])
1164 |
1165 | elif tlv_type == 0xf4:
1166 | print " PKCS#15: %s" % APDU.get_hex(tlv_value)
1167 | elif tlv_type == 0xf5:
1168 | print " Registered Data Model number: %s" % APDU.get_hex(tlv_value)
1169 | elif tlv_type == 0xf6:
1170 | print " Access Control Rule Table: %s" % APDU.get_hex(tlv_value)
1171 | elif tlv_type == 0xf7:
1172 | print " CARD APDUs: %s" % APDU.get_hex(tlv_value)
1173 | elif tlv_type == 0xfa:
1174 | print " Redirection Tag: %s" % APDU.get_hex(tlv_value)
1175 | elif tlv_type == 0xfb:
1176 | print " Capability Tuples (CTs): %s" % APDU.get_hex(tlv_value)
1177 | elif tlv_type == 0xfc:
1178 | print " Status Tuples (STs): %s" % APDU.get_hex(tlv_value)
1179 | elif tlv_type == 0xfd:
1180 | print " Next CCC: %s" % APDU.get_hex(tlv_value)
1181 | elif tlv_type == 0xfe:
1182 | print " Error Detection Code: %s" % APDU.get_hex(tlv_value)
1183 | else:
1184 | print " [TLV] Type: %s, Length: %d " % (hex(tlv_type), len(tlv_value))
1185 |
1186 | self._splash("CCC (%s)" % APDU.get_hex(applet))
1187 |
1188 |
1189 | def _print_chuid(self, tv_data, applet=None, object_id=None):
1190 | """
1191 | Will take CHUID data and print extracted information
1192 |
1193 | Reference:
1194 | http://fips201ep.cio.gov/documents/TIG_SCEPACS_v2.2.pdf (Page 9)
1195 | http://csrc.nist.gov/publications/nistpubs/800-73-3/sp800-73-3_PART1_piv-card-applic-namespace-date-model-rep.pdf (Page 5)
1196 |
1197 | @param tv_data: Type/Value data returned from a read_object call
1198 | """
1199 | # Print results to terminal
1200 | self._splash("CHUID (%s)" % APDU.get_hex(applet))
1201 | # Loop over extracted data and print nicely formatted
1202 | for tv in tv_data:
1203 | tlv_type = tv[0]
1204 | tlv_value = tv[1]
1205 |
1206 | if tlv_type == 0x30:
1207 | self._print_fasc_n(tlv_value)
1208 | print ""
1209 | elif tlv_type == 0x31:
1210 | print " Agency Code: %s" % APDU.get_hex(tlv_value)
1211 | elif tlv_type == 0x34:
1212 | print " GUID: %s" % APDU.get_hex(tlv_value)
1213 | elif tlv_type == 0x35:
1214 | print " Expiration Date (YYYYMMDD): %s" % APDU.get_str(tlv_value)
1215 | elif tlv_type == 0x3E:
1216 | print " Asymmetric Signature: [length: %d]" % len(tlv_value)
1217 | elif tlv_type == 0xFE:
1218 | print "Error Detection Code Found."
1219 | else:
1220 | print " [TLV] Type: %s, Length: %d " % (hex(tlv_type),
1221 | len(tlv_value))
1222 |
1223 | self._splash("CHUID (%s)" % APDU.get_hex(applet))
1224 |
1225 |
1226 | def _print_x509_cert(self, tv_data, registered_id, object_id):
1227 | """
1228 | Read and decode the X.509 Certificate for PIV Authentication
1229 |
1230 | X.509 Certificate for PIV Authentication 5FC105 M
1231 | X.509 Certificate for Digital Signature 5FC10A O
1232 | X.509 Certificate for Key Management 5FC10B O
1233 | X.509 Certificate for Card Authentication 5FC101 O
1234 |
1235 | Ref: SP80-73-1 / Appendix A
1236 |
1237 | @param cert_address: Address of certificate to read
1238 | """
1239 |
1240 | cert_name = self._lookup_cert(registered_id, object_id)
1241 |
1242 |
1243 |
1244 | self._splash("X.509 %s Certificate (%s)" % (cert_name, APDU.get_hex(registered_id)))
1245 |
1246 | # Loop over extracted data and print nicely formatted
1247 | for tv in tv_data:
1248 | tlv_type = tv[0]
1249 | tlv_value = tv[1]
1250 |
1251 | if tlv_type == 0x70:
1252 | print "Certificate: [length: %d]" % len(tlv_value)
1253 | elif tlv_type == 0x71:
1254 | print "Certificate Info: %s" % APDU.get_hex(tlv_value)
1255 | elif tlv_type == 0x72:
1256 | print "MSCUID: %s" % APDU.get_hex(tlv_value)
1257 | elif tlv_type == 0xFE:
1258 | print "Error Detction Code Found."
1259 | else:
1260 | print "[TLV] %s : %s : %s" % (hex(tlv_type),
1261 | len(tlv_value),
1262 | APDU.get_hex(tlv_value))
1263 |
1264 | self._splash("X.509 %s Certificate (%s)" % (cert_name, APDU.get_hex(registered_id)))
1265 |
1266 |
1267 | def _print_sec_obj(self, tv_data, registered_id, object_id):
1268 | """
1269 | Print the Security Object (Ref: SP800-73-1)
1270 | """
1271 | self._splash("Security Object (%s)" % (APDU.get_hex(object_id)))
1272 |
1273 | # Loop over extracted data and print nicely formatted
1274 | for tv in tv_data:
1275 | tlv_type = tv[0]
1276 | tlv_value = tv[1]
1277 |
1278 | if tlv_type == 0xBA:
1279 | print "Mapping of DG to ContainerID: %s" % APDU.get_hex(tlv_value)
1280 | elif tlv_type == 0xBB:
1281 | print "Security Object: [length: %d]" % len(tlv_value)
1282 | elif tlv_type == 0xFE:
1283 | print "Error Detction Code Found."
1284 | else:
1285 | print "[TLV] %s : %s : %s" % (hex(tlv_type),
1286 | len(tlv_value),
1287 | APDU.get_hex(tlv_value))
1288 |
1289 |
1290 | self._splash("Security Object (%s)" % (APDU.get_hex(object_id)))
1291 |
1292 |
1293 | def _print_fingerprint(self, tv_data, registered_id, object_id):
1294 | """
1295 | Print Fingerprint data from PIV
1296 |
1297 | REQUIRES PIN!
1298 |
1299 | Ref: SP800-73-1
1300 | """
1301 |
1302 | self._splash("Fingerprint")
1303 | # Loop over extracted data and print nicely formatted
1304 | for tv in tv_data:
1305 | tlv_type = tv[0]
1306 | tlv_value = tv[1]
1307 |
1308 | if tlv_type == 0xBC:
1309 | print "Fingerprint: [length %d]" % len(tlv_value)
1310 | elif tlv_type == 0xFE:
1311 | print "Error Detection Code Found."
1312 | else:
1313 | print "[TLV] %s : %s : %s" % (hex(tlv_type),
1314 | len(tlv_value),
1315 | APDU.get_hex(tlv_value))
1316 | self._splash("Fingerprint")
1317 |
1318 |
1319 | def _print_facial_info(self, tv_data, registered_id, object_id):
1320 | """
1321 | Print Facial Information from PIV
1322 |
1323 | REQUIRES PIN!
1324 |
1325 | Ref: SP800-73-1
1326 | """
1327 |
1328 | self._splash("Facial Information")
1329 | for tv in tv_data:
1330 | tlv_type = tv[0]
1331 | tlv_value = tv[1]
1332 |
1333 | if tlv_type == 0xBC:
1334 | print "Facial Image: [length %d]" % len(tlv_value)
1335 | elif tlv_type == 0xFE:
1336 | print "Error Detection Code Found."
1337 | else:
1338 | print "[TLV] %s : %s : %s" % (hex(tlv_type),
1339 | len(tlv_value),
1340 | APDU.get_hex(tlv_value))
1341 |
1342 | self._splash("Facial Information")
1343 |
1344 |
1345 | def _print_person_info(self, tv_data, registered_id, object_id):
1346 | """
1347 | Print Person Information
1348 |
1349 | REQUIRES PIN!
1350 |
1351 | Ref: NISTIR-6887
1352 | """
1353 |
1354 | self._splash("Person Instance Container")
1355 | for tv in tv_data:
1356 | tlv_type = tv[0]
1357 | tlv_value = tv[1]
1358 |
1359 | if tlv_type == 0x01:
1360 | print "First Name: %s" % APDU.get_str(tlv_value)
1361 | elif tlv_type == 0x02:
1362 | print "Middle Name: %s" % APDU.get_str(tlv_value)
1363 | elif tlv_type == 0x03:
1364 | print "Last Name: %s" % APDU.get_str(tlv_value)
1365 | elif tlv_type == 0x04:
1366 | print "Candency Name: %s" % APDU.get_str(tlv_value)
1367 | elif tlv_type == 0x05:
1368 | print "Personal Identifier: %s" % APDU.get_str(tlv_value)
1369 | elif tlv_type == 0x06:
1370 | print "DOB: %s" % APDU.get_str(tlv_value)
1371 | elif tlv_type == 0x07:
1372 | print "Sex: %s" % APDU.get_str(tlv_value)
1373 | elif tlv_type == 0x08:
1374 | print "PI Type Code: %s" % APDU.get_str(tlv_value)
1375 | elif tlv_type == 0x11:
1376 | print "Blood Type: %s" % APDU.get_str(tlv_value)
1377 | elif tlv_type == 0x17:
1378 | print "DoD EDI PI: %s" % APDU.get_str(tlv_value)
1379 | elif tlv_type == 0x18:
1380 | print "Organ Donor: %s" % APDU.get_str(tlv_value)
1381 | elif tlv_type == 0x62:
1382 | print "Card Issue Date: %s" % APDU.get_str(tlv_value)
1383 | elif tlv_type == 0x63:
1384 | print "Card Expiration Date: %s" % APDU.get_str(tlv_value)
1385 | elif tlv_type == 0x65:
1386 | print "Date Demographic Data Loaded: %s" % APDU.get_str(tlv_value)
1387 | elif tlv_type == 0x66:
1388 | print "Date Demographic Data Expires: %s" % APDU.get_str(tlv_value)
1389 | elif tlv_type == 0x67:
1390 | print "Card Instance ID: %s" % APDU.get_str(tlv_value)
1391 | else:
1392 | print "[TLV] %s : %s : %s" % (hex(tlv_type),
1393 | len(tlv_value),
1394 | APDU.get_hex(tlv_value))
1395 |
1396 | self._splash("Person Instance Container")
1397 |
1398 |
1399 | def _print_personnel(self, tv_data, registered_id, object_id):
1400 | """
1401 | Print Person Information
1402 |
1403 | REQUIRES PIN!
1404 |
1405 | Ref: NISTIR-6887
1406 | """
1407 |
1408 | self._splash("Personnel Information")
1409 | for tv in tv_data:
1410 | tlv_type = tv[0]
1411 | tlv_value = tv[1]
1412 |
1413 | if tlv_type == 0x19:
1414 | print "Contractor Function Code: %s" % APDU.get_str(tlv_value)
1415 | elif tlv_type == 0x20:
1416 | print "Gov Agency/Subagency Code: %s" % APDU.get_str(tlv_value)
1417 | elif tlv_type == 0x24:
1418 | print "Branch: %s" % APDU.get_str(tlv_value)
1419 | elif tlv_type == 0x25:
1420 | print "Pay Grade: %s" % APDU.get_str(tlv_value)
1421 | elif tlv_type == 0x26:
1422 | print "Rank Code: %s" % APDU.get_str(tlv_value)
1423 | elif tlv_type == 0x34:
1424 | print "Personnel Category Code: %s" % APDU.get_str(tlv_value)
1425 | elif tlv_type == 0x35:
1426 | print "Non-US Gov Agency/Subagency Code: %s" % APDU.get_str(tlv_value)
1427 | elif tlv_type == 0x36:
1428 | print "Pay Plan Code: %s" % APDU.get_str(tlv_value)
1429 | elif tlv_type == 0xD3:
1430 | print "Personnel Entitlement Condition Code: %s" % APDU.get_str(tlv_value)
1431 | else:
1432 | print "[TLV] %s : %s : %s" % (hex(tlv_type),
1433 | len(tlv_value),
1434 | APDU.get_hex(tlv_value))
1435 | self._splash("Personnel Information")
1436 |
1437 |
1438 | def split_address(self, address):
1439 | """ Split a 2 byte address into 2 individual bytes """
1440 | P1 = (0b1111111100000000 & address) >> 8
1441 | P2 = 0b11111111 & address
1442 | return [P1, P2]
1443 |
1444 |
1445 | def read_tl_v_buffer(self, address):
1446 | """
1447 | Read Type-Length buffer and Value buffer, concatenating all of the results
1448 | and returning a contiguous binary string
1449 |
1450 | @param address: Address of buffer. Likely [0x00, 0x00]
1451 | @return: Binary string of buffers merged into one
1452 | """
1453 |
1454 | addr = self.split_address(address)
1455 | data, sw1, sw2 = self.apdu_read_buffer(addr[0], addr[1], 0x01, read_length=0x03)
1456 |
1457 | if sw1 != APDU.APDU_STATUS.SUCCESS:
1458 | self._report_error(sw1, sw2, "Buffer read failed.")
1459 |
1460 | return
1461 |
1462 | # Figure out our length and where the data starts
1463 | # @todo: Confirm that 0x81 and 0x82 apply here.
1464 | if data[0] == 0x81:
1465 | buffer_length = data[1]
1466 | next_address = address + 3
1467 |
1468 | elif data[0] == 0x82:
1469 | buffer_length = data[1] << 8 | data[2]
1470 | next_address = address + 4
1471 |
1472 | else:
1473 | buffer_length = data[0]
1474 | next_address = address + 2
1475 |
1476 | # Read Type-Length values (0x01)
1477 | addr = self.split_address(next_address)
1478 | tl_buffer, sw1, sw2 = self.apdu_read_buffer(addr[0], addr[1], 0x01, read_length=buffer_length)
1479 | tl_offset = 2
1480 |
1481 | rtn_list = []
1482 | for i in range(buffer_length / 2):
1483 | addr = self.split_address(next_address)
1484 | tlv_type = tl_buffer[i * 2]
1485 | tlv_length = tl_buffer[i * 2 + 1]
1486 |
1487 | data = []
1488 | # Read data (0x02)
1489 | if tlv_length > 0:
1490 |
1491 | data, sw1, sw2 = self.apdu_read_buffer(0, tl_offset, 0x02, read_length=tlv_length)
1492 | tl_offset += tlv_length
1493 |
1494 | rtn_list.append([tlv_type, data])
1495 |
1496 | return rtn_list
1497 |
1498 |
1499 | def apdu_read_buffer(self, p1, p2, buffer_type, read_length=64):
1500 | """
1501 | Send READ BUFFER APDU
1502 |
1503 | @param p1: MSB of argument
1504 | @param p2: LSB of argument
1505 | @param buffer_type: 0x01 - Read Type-Length buffer,
1506 | 0x02 - Read Value buffer
1507 | @param read_length: How many bytes to read
1508 |
1509 | """
1510 |
1511 | apdu_read = APDU.READ_BUFFER(p1, p2, buffer_type, read_length=read_length)
1512 |
1513 | data, sw1, sw2 = self._send_apdu(apdu_read)
1514 |
1515 | if sw1 != APDU.APDU_STATUS.SUCCESS:
1516 | print "ERROR (%s,%s): READ BUFFER failed." % (hex(sw1), hex(sw2))
1517 |
1518 | return (data, sw1, sw2)
1519 |
1520 |
1521 | def apdu_get_data_piv(self, address):
1522 | """
1523 | GET DATA APDU
1524 |
1525 | Ref: SP800-73-1 / Table 6
1526 | Card Capability Container 5FC107 M
1527 | Card Holder Unique Identifier 5FC102 M
1528 | X.509 Certificate for PIV Authentication 5FC105 M
1529 | Card Holder Fingerprint I 5FC103 M
1530 | Card Holder Fingerprint II 5FC104 M
1531 | Printed Information 5FC109 O
1532 | Card Holder Facial Image 5FC108 O
1533 | X.509 Certificate for Digital Signature 5FC10A O
1534 | X.509 Certificate for Key Management 5FC10B O
1535 | X.509 Certificate for Card Authentication 5FC101 O
1536 | Security Object 5FC106 M
1537 |
1538 | @param address: 3 byte address list
1539 | @return (data,sw1,sw2)
1540 | """
1541 |
1542 | apdu_get_data = APDU.GET_DATA_PIV(address)
1543 | # Get returned data
1544 | data, sw1, sw2 = self._send_apdu(apdu_get_data)
1545 | if sw1 != APDU.APDU_STATUS.SUCCESS:
1546 | print "ERROR (%s,%s): GET DATA failed." % (hex(sw1), hex(sw2))
1547 |
1548 | return data, sw1, sw2
1549 |
1550 |
1551 | def apdu_sign_decrypt(self, input_data):
1552 | """
1553 | Send data to the CAC to be signed or decrypted.
1554 |
1555 | Ref: SP800-73-1 / Page 14
1556 |
1557 | @param data: Data to be signed or decrypted
1558 | @return: (data, sw1, sw2) - Returns data decrypted or signed if successful
1559 | """
1560 |
1561 | PADDING = 0xFF
1562 | MAX_LEN = 128 # must be a divisor of 256
1563 |
1564 | # Pad the data
1565 | while len(input_data) % 256 != 0:
1566 | input_data.append(PADDING)
1567 |
1568 | chunk_count = len(input_data) / MAX_LEN
1569 |
1570 | for i in range(chunk_count):
1571 | # Set P1 to indicate more data is coming
1572 | P1 = 0b10000000
1573 | if i == chunk_count - 1:
1574 | P1 = 0x0
1575 | # Exract the chunk of the data we are sending
1576 | data_chunk = input_data[MAX_LEN * i:MAX_LEN * i + MAX_LEN]
1577 |
1578 | # Send the APDU
1579 | apdu_cmd = APDU.SIGN_DECRYPT(data_chunk, P1=P1)
1580 | # Get returned data
1581 | data, sw1, sw2 = self._send_apdu(apdu_cmd)
1582 | if sw1 != APDU.APDU_STATUS.SUCCESS:
1583 | print "ERROR (%s,%s): SIGN/DECRYPT failed." % (hex(sw1), hex(sw2))
1584 | break
1585 |
1586 | return (data, sw1, sw2)
1587 |
1588 |
1589 | def select_nist_piv(self):
1590 | """ SELECT NIST PIV """
1591 | data, sw1, sw2 = self.apdu_select_application(APDU.APPLET.NIST_PIV)
1592 | if sw1 != APDU.APDU_STATUS.SUCCESS:
1593 | print "ERROR (%s,%s): SELECT PIV RID (%s) failed." % (hex(sw1),
1594 | hex(sw2),
1595 | APDU.get_hex(APDU.APPLET.NIST_PIV))
1596 | return
1597 |
1598 |
1599 | def read_object(self, registered_id, object_id, pix=[], pin=None):
1600 | """
1601 | Read a an Object from a given Applet (resource id) and return the
1602 | BER-TLV decoded data.
1603 |
1604 | 1. SELECTs the Applet/PIX
1605 | 2. GET DATA from the Object
1606 | 3. decode the BER-TLV format
1607 |
1608 | @param registered_id: Applet's Registered ID (5 bytes)
1609 | @param pix: Proprietary Identifier Extension (2-11 bytes)
1610 | @param object_id: Object ID within the Resource
1611 | """
1612 |
1613 | # Select object from the Applet
1614 | if registered_id == APDU.APPLET.NIST_PIV:
1615 | # Select the Transitional PIV, then select the appropriate Object
1616 | self.apdu_select_application(registered_id)
1617 | if pin is not None:
1618 | data, sw1, sw2 = self.apdu_verify_pin(pin, 0x80)
1619 | data, sw1, sw2 = self.apdu_get_data_piv(object_id)
1620 | elif registered_id == APDU.APPLET.DOD_PIV or registered_id == APDU.APPLET.DOD_CAC:
1621 | # Select Applet and Object using SELECT applciation apdu
1622 | self.apdu_select_application(registered_id + pix)
1623 | if pin is not None:
1624 | data, sw1, sw2 = self.apdu_verify_pin(pin, 0x00)
1625 | data, sw1, sw2 = self.apdu_select_object(object_id)
1626 | else:
1627 | self._report_error(sw1, sw2, "Could not read Object (%s) from Applet (%s)." % (APDU.get_hex(registered_id),
1628 | APDU.get_hex(object_id))
1629 | )
1630 | return None
1631 |
1632 | # @todo: Handle error cases
1633 | if sw1 != APDU.APDU_STATUS.SUCCESS:
1634 | self._report_error(sw1, sw2, "Read Object failed. (RID:%s, OID:%s)" % (APDU.get_hex(registered_id),
1635 | APDU.get_hex(object_id)))
1636 | return
1637 |
1638 | # Extract our data
1639 | if registered_id == APDU.APPLET.NIST_PIV:
1640 | tv_data = self._decode_ber_tlv(data)
1641 | elif registered_id == APDU.APPLET.DOD_PIV or registered_id == APDU.APPLET.DOD_CAC:
1642 | tv_data = self.read_tl_v_buffer(0x000)
1643 |
1644 | return tv_data
1645 |
1646 | def extract_cert(self, registered_id, object_id, cert_filename):
1647 | """
1648 | Will extract the certificate from the card and save it to a file on
1649 | disk.
1650 |
1651 | @param registered_id: RID of the applet to extract the cert from.
1652 | @param object_id: Object ID of the cert to be extracted
1653 | @param cert_filename: Filename to save the cert as on disk
1654 | """
1655 |
1656 | # Read the data from the object
1657 | data = self.read_object(registered_id, object_id)
1658 |
1659 | if data == None:
1660 | logger.error("Failed to extract %s." % self._lookup_cert(registered_id, object_id))
1661 | return
1662 |
1663 | # We know that all certs have the same format and where the cert is
1664 | # Ref: SP800-73-1 / Page 47
1665 | cert_data = None
1666 | for tv in data:
1667 | # 0x70 for certificates
1668 | # 0x3E for CHUID cert
1669 | if tv[0] == 0x70 or tv[0] == 0x3E:
1670 | cert_data = tv[1]
1671 | break
1672 |
1673 | if cert_data is None:
1674 | logger.error("Certificate %s not found in APDU response." % self._lookup_cert(registered_id, object_id))
1675 | return
1676 |
1677 | # Create file and write to it
1678 | cert_f = open(cert_filename, "wb+")
1679 |
1680 | cert_f.write(struct.pack("%dB" % len(cert_data), *cert_data))
1681 |
1682 | cert_f.close()
1683 |
1684 | print "Saved %s to %s" % (self._lookup_cert(registered_id, object_id),
1685 | cert_filename)
1686 |
1687 |
1688 | def save_nist_cert(self, oid, cert_filename):
1689 | """
1690 | Function to extract NIST certificates from the DoD CaC and save
1691 | it as a file to disk. This will also extract the PEM version and
1692 | the public key with the appropriate file extensions.
1693 |
1694 | This function calls shell functions. It's not the nicest way to do
1695 | it but I see no reason to require more Python modules.
1696 |
1697 | @param oid: Object ID of the cert to be extracted
1698 | @param cert_filename: Filename to save the cert to disk as.
1699 | """
1700 |
1701 | # Extract the cert to disk (with gzip extension
1702 | self.extract_cert(APDU.APPLET.NIST_PIV,
1703 | oid,
1704 | cert_filename + ".gz")
1705 |
1706 | # ungzip it (remember the gzip extension was appeneded)
1707 | subprocess.Popen(["gunzip", "-f", cert_filename + ".gz"])
1708 |
1709 | # extract public cert and PEM version of the cert
1710 | p = subprocess.Popen(["openssl", "x509",
1711 | "-inform", "DER",
1712 | "-pubkey",
1713 | "-in", cert_filename,
1714 | "-out", cert_filename + ".pem"],
1715 | stdout=subprocess.PIPE)
1716 | out, err = p.communicate()
1717 |
1718 | # Write our output file
1719 | f = open(cert_filename + ".pub", "w+")
1720 | f.write(out)
1721 | f.close()
1722 |
1723 |
1724 | def print_object(self, registered_id, object_id, pix=[], pin=None):
1725 | """
1726 | Will read the Object from the given Applet/PIX and then print
1727 | the results in human readable form.
1728 |
1729 | @param registered_id: Applet's Registered ID (5 bytes)
1730 | @param pix: Proprietary Identifier Extension (2-11 bytes)
1731 | @param object_id: Object ID within the Resource
1732 | """
1733 |
1734 | # Read the data from the object
1735 | tv_data = self.read_object(registered_id, object_id, pix=pix, pin=pin)
1736 |
1737 | if tv_data == None:
1738 | logger.error("Could not retrive Object (%s) from Applet (%s)." % (APDU.get_hex(object_id),
1739 | APDU.get_hex(registered_id))
1740 | )
1741 | return
1742 |
1743 | # See if we have a print function for this object
1744 | # CHUID
1745 | if object_id in [APDU.OBJ_DOD_PIV.CHUID, APDU.OBJ_NIST_PIV.CHUID]:
1746 | self._print_chuid(tv_data, registered_id, object_id)
1747 | # CCC
1748 | elif object_id in [APDU.OBJ_DOD_PIV.CCC, APDU.OBJ_NIST_PIV.CCC]:
1749 | self._print_ccc(tv_data, registered_id, object_id)
1750 |
1751 | # X.509 PIV Cred Auth
1752 | elif object_id in [APDU.OBJ_NIST_PIV.KEY_CRD_ATH]:
1753 | self._print_x509_cert(tv_data, registered_id, object_id)
1754 | # X.509 Dig Sign
1755 | elif object_id in [APDU.OBJ_NIST_PIV.KEY_DIG_SIG]:
1756 | self._print_x509_cert(tv_data, registered_id, object_id)
1757 | # X.509 Key Management
1758 | elif object_id in [APDU.OBJ_NIST_PIV.KEY_MNG]:
1759 | self._print_x509_cert(tv_data, registered_id, object_id)
1760 | # X.509 PIV Auth
1761 | elif object_id in [APDU.OBJ_NIST_PIV.KEY_PIV_ATH]:
1762 | self._print_x509_cert(tv_data, registered_id, object_id)
1763 | # CAC PKI
1764 | elif object_id in [APDU.OBJ_DOD_CAC.KEY_PKI_ENC, APDU.OBJ_DOD_CAC.KEY_PKI_ID, APDU.OBJ_DOD_CAC.KEY_PKI_SIG]:
1765 | self._print_x509_cert(tv_data, registered_id, object_id)
1766 | # Security Object
1767 | elif object_id in [APDU.OBJ_DOD_PIV.SEC_OBJ, APDU.OBJ_NIST_PIV.SEC_OBJ]:
1768 | self._print_sec_obj(tv_data, registered_id, object_id)
1769 | # Fingerprints
1770 | elif object_id in [APDU.OBJ_DOD_PIV.FNGR_PRNT, APDU.OBJ_NIST_PIV.FNGR_P1, APDU.OBJ_NIST_PIV.FNGR_P2]:
1771 | self._print_fingerprint(tv_data, registered_id, object_id)
1772 | # Facial Image
1773 | elif object_id in [APDU.OBJ_DOD_PIV.FACE, APDU.OBJ_NIST_PIV.FACE]:
1774 | self._print_facial_info(tv_data, registered_id, object_id)
1775 | # Person Info
1776 | elif object_id in [APDU.OBJ_DOD_CAC.CAC_PERSON]:
1777 | self._print_person_info(tv_data, registered_id, object_id)
1778 | # Personnel Info
1779 | elif object_id in [APDU.OBJ_DOD_CAC.CAC_PERSONEL]:
1780 | self._print_personnel(tv_data, registered_id, object_id)
1781 | else:
1782 | logger.error("No function implemented to print Object (%s) from Applet (%s)." % (APDU.get_hex(object_id),
1783 | APDU.get_hex(registered_id))
1784 | )
1785 | print tv_data
1786 | return
1787 |
1788 |
1789 |
1790 |
1791 |
1792 |
1793 | class CreditCard(SmartCard):
1794 | """
1795 | Implements some known features of Visa smartcards
1796 | """
1797 | INFO_REC = 1
1798 | INFO_SFI = 12
1799 |
1800 | def _parse_applet_info(self, data):
1801 | """
1802 | Parse the data we get back from selecting the applet
1803 | """
1804 | # Is this a FCI template?
1805 | if data[0] == 0x6f:
1806 | tlv = self._decode_ber_tlv(data)
1807 | logger.info("FCI Template")
1808 |
1809 | template = self._decode_ber_tlv(tlv[0][1])
1810 | # Parse template info
1811 | for t in template:
1812 | if t[0] == 0x84:
1813 | df_name = "".join(["%02X"%x for x in t[1]])
1814 | logger.info("DF Name: %s"%df_name)
1815 |
1816 | if t[0] == 0xa5:
1817 | logger.info(" FCI Proprietary Template")
1818 | prop_template = self._decode_ber_tlv(t[1])
1819 |
1820 | # Parse embedded info
1821 | for pt in prop_template:
1822 | if pt[0] == 0x50:
1823 | app_label = "".join([str(unichr(x)) for x in pt[1]])
1824 | logger.info(" Application Label: %s"%app_label)
1825 | if pt[0] == 0x87:
1826 | logger.info(" Application Priority Indicator: %s"%pt[1][0])
1827 |
1828 |
1829 | def select_visa_applet(self):
1830 | """
1831 | Will send the appropriate APDU to select the Visa applet
1832 | """
1833 | data, sw1, sw2 = self.apdu_select_application(APDU.APPLET.VISA)
1834 | if sw1 != APDU.APDU_STATUS.SUCCESS:
1835 | print "ERROR: This does not appear to be a valid VISA card!"
1836 | else:
1837 | self._parse_applet_info(data)
1838 |
1839 |
1840 | def select_mastercard_applet(self):
1841 | """
1842 | Will send the appropriate APDU to select the Visa applet
1843 | """
1844 | data, sw1, sw2 = self.apdu_select_application(APDU.APPLET.MASTERCARD)
1845 | if sw1 != APDU.APDU_STATUS.SUCCESS:
1846 | print "ERROR: This does not appear to be a valid MasterCard!"
1847 | else:
1848 | self._parse_applet_info(data)
1849 |
1850 |
1851 |
1852 | def read_card_info(self):
1853 | """
1854 | Read known paramaters from a Visa smartcard
1855 | """
1856 |
1857 | # REad the record from teh card
1858 | data, sw1, sw2 = self.apdu_read_record(self.INFO_REC, self.INFO_SFI, cla=0x00)
1859 |
1860 | # Was it a succes?
1861 | if sw1 == APDU.APDU_STATUS.SUCCESS:
1862 |
1863 | # Setup our dict
1864 | info = {}
1865 |
1866 | tlv = self._decode_ber_tlv(data)
1867 |
1868 | # Is this application data?
1869 | if tlv[0][0] == 0x70:
1870 |
1871 | # Parse the data in the application
1872 | cc_info = self._decode_ber_tlv(tlv[0][1])
1873 | for field in cc_info:
1874 | # Is it a name field?
1875 | if field[0] == 0x5f:
1876 | cc_data = "".join([chr(x) for x in field[1]])
1877 |
1878 | cc_data = cc_data.split("/")
1879 | info['last_name'] = cc_data[0].strip()
1880 | info['first_name'] = cc_data[1].strip()
1881 |
1882 | # Account info field?
1883 | if field[0] == 0x57:
1884 | cc_data = "".join(["%02X"%x for x in field[1]])
1885 |
1886 | k = cc_data.find('D')
1887 |
1888 | info['account_number'] = cc_data[:k]
1889 |
1890 |
1891 | info['exp_year'] = cc_data[k+1:k+3]
1892 | info['exp_month'] = cc_data[k+3:k+5]
1893 | info['service_first'] = cc_data[k+5]
1894 | info['service_second'] = cc_data[k+6]
1895 | info['service_third'] = cc_data[k+7]
1896 |
1897 | return info
1898 |
1899 | else:
1900 | logger.error("Couldn't read card data.")
1901 | return None
1902 |
1903 |
1904 |
1905 |
--------------------------------------------------------------------------------