diff --git a/card.py b/card.py deleted file mode 100644 index 748a7ff..0000000 --- a/card.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -Smartcard Terminal IO Class - -(C) 2017 by Sysmocom s.f.m.c. GmbH -All Rights Reserved - -Author: Philipp Maier - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -from smartcard.scard import * -import smartcard.util -from utils import * - -# The following class abstract the terminal, however we reference it as "Card", -# because the terminal itsslef is not in the users interest, we are focussed -# on the card all the time. The abstraction done here is very simple and -# implements only the very basic functionality to communicate with a smartcard -# on APDU level. -# -# The classes Card_res_apdu and Card_apdu are helper classes in order to make -# passing the parameters/results simpler. They are not meant to be created -# anwhere else in the code. All handling is done through the Card class. -# -# The method apdu formats an APDU with its basic features (CLA, INS etc..) -# for the user. The user also may set an expected status word (Default is -# 0x9000). The status word is then checked when the transaction is performed -# using the transact method. -# -# The expected status word is a list of two bytes, each of the two bytes may be -# set to None. If this is the case the byte is not checked. If the whole list -# is set to none, the status word is not checked at all. If the status word -# check fails, the swok flag inside the repsonse apdu is set to false and an -# exception is thwron unless the strict parameter is not set to False. -# -# The user may set a dry-flag when calling the transact, then the transaction -# is not performed. Only the log line is printed. This is to verify if the -# transaction would be sent correctly, as some smartcad operations might -# be risky. - - -# Note: Programmed with the help of a blogpost from Ludovic Rousseau: -# https://ludovicrousseau.blogspot.de/2010/04/pcsc-sample-in-python.html - -# A class to make handling of the responses a little simpler -# Note: Do not use directly, Card object will return responses. -class Card_res_apdu(): - - apdu = None - sw = None - swok = True - def __init__(self, apdu, sw): - self.apdu = apdu - self.sw = sw - - def __str__(self): - dump = "APDU:" + hexdump(self.apdu) - dump += " SW:" + hexdump(self.sw) - return dump - - -# A class to make handling of the card input a little simpler -# Note: Do not use directly, use Card.apdu(...) instead -class Card_apdu(): - - apdu = None - sw = None - def __init__(self, cla, ins, p1, p2, p3, data, sw): - self.apdu = [cla, ins, p1, p2, p3] - if data: - self.apdu += data - self.sw = sw - - def __str__(self): - dump = "APDU:" + hexdump(self.apdu) - return dump - - -# A class to abstract smartcard and terminal -class Card(): - - card = None - protocol = None - verbose = None - - # Constructor: Set up connection to smartcard - def __init__(self, verbose = False): - - self.verbose = verbose - - # Eestablish a smartcard terminal context - hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER) - if hresult != SCARD_S_SUCCESS: - print " * Error: Unable to establish smartcard terminal context -- exiting" - exit(1) - - # Select the next available smartcard terminal - hresult, terminals = SCardListReaders(hcontext, []) - if hresult != SCARD_S_SUCCESS or len(terminals) < 1: - print " * Error: No smartcard terminal found -- exiting" - exit(1) - terminal = terminals[0] - print " * Terminal:", terminal - - # Establish connection to smartcard - hresult, hcard, dwActiveProtocol = SCardConnect(hcontext, - terminal, SCARD_SHARE_SHARED, - SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1) - if hresult != SCARD_S_SUCCESS: - print " * Error: No smartcard detected -- exiting" - exit(1) - print " * Protocol: " + str(dwActiveProtocol) - self.card = hcard - self.protocol = dwActiveProtocol - - - # Print debug info - def __debug_print(self, message): - if self.verbose: - print message - - - # Perform smartcard transaction - def transact(self, apdu, dry = False, strict = True): - - # Dry run - if (dry): - self.__debug_print(" Card transaction: " + str(apdu) - + " ==> Dry run: Transaction not executed") - return Card_res_apdu(None,None) - - # Perform transaction - hresult, response = SCardTransmit(self.card, - self.protocol, apdu.apdu) - if hresult != SCARD_S_SUCCESS: - self.__debug_print(" * Card transaction: " + str(apdu) - + " ==> Error: Smartcard transaction failed") - return Card_res_apdu(None,None) - res = Card_res_apdu(response[:-2],response[-2:]) - - self.__debug_print(" Card transaction: " + str(apdu) - + " ==> " + str(res)) - - # Check status word - if apdu.sw: - if apdu.sw[0] and apdu.sw[0] == res.sw[0]: - res.swok = True - elif apdu.sw[0]: - res.swok = False - self.__debug_print(" * Warning: Unexpected status word (SW1)!") - - if apdu.sw[1] and apdu.sw[1] == res.sw[1]: - res.swok = True - elif apdu.sw[1]: - res.swok = False - self.__debug_print(" * Warning: Unexpected status word (SW2)!") - - # If strict mode is turned on, the status word must match, - # otherwise an exception is thrown - if strict and res.swok == False: - raise ValueError('Transaction failed!') - - return res - - - # set up a new APDU - def apdu(self, cla, ins, p1 = 0x00, p2 = 0x00, p3 = 0x00, - data = None, sw = [0x90, 0x00]): - return Card_apdu(cla, ins, p1, p2, p3, data, sw) - - diff --git a/simcard.py b/simcard.py index f617ca1..915aa94 100644 --- a/simcard.py +++ b/simcard.py @@ -23,59 +23,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from card import * - -# This is an abstraction which offers a set of tools to handle the most basic -# operations that can be performed on a sim card. The implementation is not -# simcard model specific - -# Classes -GSM_SIM_CLA = 0xA0 -GSM_USIM_CLA = 0x00 - -# Instructions, see also GSM 11.11, Table 9 Coding of the commands -GSM_SIM_INS_SELECT = 0xA4 -GSM_SIM_INS_STATUS = 0xF2 -GSM_SIM_INS_READ_BINARY = 0xB0 -GSM_SIM_INS_UPDATE_BINARY = 0xD6 -GSM_SIM_INS_READ_RECORD = 0xB2 -GSM_SIM_INS_UPDATE_RECORD = 0xDC -GSM_SIM_INS_SEEK = 0xA2 -GSM_SIM_INS_INCREASE = 0x32 -GSM_SIM_INS_VERIFY_CHV = 0x20 -GSM_SIM_INS_CHANGE_CHV = 0x24 -GSM_SIM_INS_DISABLE_CHV = 0x26 -GSM_SIM_INS_ENABLE_CHV = 0x28 -GSM_SIM_INS_UNBLOCK_CHV = 0x2C -GSM_SIM_INS_INVALIDATE = 0x04 -GSM_SIM_INS_REHABILITATE = 0x44 -GSM_SIM_INS_RUN_GSM_ALGORITHM = 0x88 -GSM_SIM_INS_SLEEP = 0xFA -GSM_SIM_INS_GET_RESPONSE = 0xC0 -GSM_SIM_INS_TERMINAL_PROFILE = 0x10 -GSM_SIM_INS_ENVELOPE = 0xC2 -GSM_SIM_INS_FETCH = 0x12 -GSM_SIM_INS_TERMINAL_RESPONSE = 0x14 - -# Partial File tree: -# The following tree is incomplete, it just contains the files we have been -# interested in so far. A full SIM card file tree can be found in: -# GSM TS 11.11, Figure 8: "File identifiers and directory structures of GSM" -# 3GPP TS 31.102, cap. 4.7: "Files of USIM" -# -# [MF 0x3F00] -# | -# +--[EF_DIR 0x2F00] -# | -# +--[EF_ICCID 0x2FE2] -# | -# +--[DF_TELECOM 0x7F10] -# | | -# | +-[EF_ADN 0x7F20] -# | -# +--[DF_GSM 0x7F20] -# | -# +-[EF_IMSI 0x6F07] +from card.USIM import USIM +from card.SIM import SIM +from card.utils import * # Files GSM_SIM_MF = [0x3F, 0x00] @@ -104,124 +54,81 @@ GSM_SIM_INS_UPDATE_RECORD_NEXT = 0x02 GSM_SIM_INS_UPDATE_RECORD_PREV = 0x03 GSM_SIM_INS_UPDATE_RECORD_ABS = 0x04 +class Card_res_apdu(): + apdu = None + sw = None + swok = True + + # convert Benoit Michau style result to sysmocom style result + def from_mich(self, mich): + self.apdu = mich[3] + self.sw = [ mich[2][0], mich[2][1] ] + + def __str__(self): + dump = "APDU: " + hexdump(self.apdu) + dump += " SW: " + hexdump(self.sw) + return dump + # A class to abstract a simcard. class Simcard(): card = None - usim = None # Constructor: Create a new simcard object - def __init__(self, terminal, cardtype = GSM_USIM): - - self.card = terminal + def __init__(self, cardtype = GSM_USIM): if cardtype == GSM_USIM: + self.card = USIM() self.usim = True else: - self.usim = True - + self.card = SIM() + self.usim = False # Find the right class byte, depending on the simcard type def __get_cla(self, usim): - - if (usim): - return GSM_USIM_CLA - else: - return GSM_SIM_CLA - + return self.card.CLA # Select a file def select(self, fid, dry = False, strict = True): - - cla = self.__get_cla(self.usim) - ins = GSM_SIM_INS_SELECT - length = 0x02 - - apdu = self.card.apdu(cla, ins, p2 = 0x0C, - p3 = length, data = fid) - return self.card.transact(apdu, dry, strict) - + res = Card_res_apdu() + res.from_mich(self.card.SELECT_FILE(P2 = 0x0C, Data = fid)) + return res # Perform card holder verification def verify_chv(self, chv, chv_no, dry = False, strict = True): - - cla = self.__get_cla(self.usim) - ins = GSM_SIM_INS_VERIFY_CHV - length = len(chv) - apdu = self.card.apdu(cla, ins, p2 = chv_no, - p3 = length, data = chv) - return self.card.transact(apdu, dry, strict) - + res = Card_res_apdu() + res.from_mich(self.card.VERIFY(P2 = chv_no, Data = chv)) + return res # Read CHV retry counter def chv_retrys(self, chv_no, dry = False, strict = True): - - cla = self.__get_cla(self.usim) - ins = GSM_SIM_INS_VERIFY_CHV - length = 0 - apdu = self.card.apdu(cla, ins, p2 = chv_no, - p3 = length, sw=[0x63, None]) - res = self.card.transact(apdu, dry, strict) - return res.sw[1] & 0x0F - + res = self.card.VERIFY(P2 = chv_no) + return res[2][1] & 0x0F # Perform file operation (Write) def update_binary(self, data, offset = 0, dry = False, strict = True): - - cla = self.__get_cla(self.usim) - ins = GSM_SIM_INS_UPDATE_BINARY - length = len(data) offs_high = (offset >> 8) & 0xFF offs_low = offset & 0xFF - - apdu = self.card.apdu(cla, ins, p1 = offs_high, p2 = offs_low, - p3 = length, data = data) - return self.card.transact(apdu, dry, strict) - + res = Card_res_apdu() + res.from_mich(self.card.UPDATE_BINARY(offs_high, offs_low, data)) + return res # Perform file operation (Read, byte oriented) def read_binary(self, length, offset = 0, dry = False, strict = True): - - cla = self.__get_cla(self.usim) - ins = GSM_SIM_INS_READ_BINARY offs_high = (offset >> 8) & 0xFF offs_low = offset & 0xFF - - apdu = self.card.apdu(cla, ins, p1 = offs_high, - p2 = offs_low, p3 = length) - return self.card.transact(apdu, dry, strict) - + res = Card_res_apdu() + res.from_mich(self.card.READ_BINARY(offs_high, offs_low, length)) + return res # Perform file operation (Read, record oriented) def read_record(self, length, rec_no = 0, dry = False, strict = True): - - cla = self.__get_cla(self.usim) - ins = GSM_SIM_INS_READ_RECORD - - apdu = self.card.apdu(cla, ins, p1 = rec_no, - p2 = GSM_SIM_INS_READ_RECORD_ABS, p3 = length) - return self.card.transact(apdu, dry, strict) + res = Card_res_apdu() + res.from_mich(self.card.READ_RECORD(rec_no, GSM_SIM_INS_READ_RECORD_ABS, length)) + return res # Perform file operation (Read, record oriented) def update_record(self, data, rec_no = 0, dry = False, strict = True): - - cla = self.__get_cla(self.usim) - ins = GSM_SIM_INS_UPDATE_RECORD - length = len(data) - - apdu = self.card.apdu(cla, ins, p1 = rec_no, - p2 = GSM_SIM_INS_UPDATE_RECORD_ABS, - p3 = length, data = data) - return self.card.transact(apdu, dry, strict) - - - - - - - - - - - - + res = Card_res_apdu() + res.from_mich(self.card.UPDATE_RECORD(rec_no, GSM_SIM_INS_UPDATE_RECORD_ABS, data)) + return res diff --git a/sysmo-usim-tool.sjs1.py b/sysmo-usim-tool.sjs1.py index dd2af92..1ba436d 100755 --- a/sysmo-usim-tool.sjs1.py +++ b/sysmo-usim-tool.sjs1.py @@ -24,7 +24,7 @@ along with this program. If not, see . """ import sys, getopt -from card import * +from utils import * from simcard import * from sysmo_usimsjs1 import * @@ -131,8 +131,7 @@ def main(argv): # Claim terminal print "Initializing smartcard terminal..." - c = Card(getopt_verbose) - sim = Simcard(c) + sim = Simcard() print("") print "Detected Card ICCID: ", sim.card.get_ICCID() diff --git a/sysmo_usimsjs1.py b/sysmo_usimsjs1.py index 063af57..baf1d82 100644 --- a/sysmo_usimsjs1.py +++ b/sysmo_usimsjs1.py @@ -49,8 +49,10 @@ along with this program. If not, see . # +--[EF_KI 0x00FF] +import sys from card import * from simcard import * +from utils import * # Files (propritary) SYSMO_USIMSJS1_EF_KI = [0x00, 0xFF]