├── 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 | [![DOI](https://zenodo.org/badge/35278621.svg)](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 | --------------------------------------------------------------------------------