From 4f2e60436b9da20dbbe0b6997cd56a882e8f8876 Mon Sep 17 00:00:00 2001 From: Philipp Maier Date: Thu, 23 Feb 2017 14:47:38 +0100 Subject: [PATCH] sysmo-usim-tool: Add a tool for sysmosom specific tasks This commit adds a tool that is intended to be used with sysmo-usim only if some of the highly propritary parameters, which the regular pysim can not handle, need to be configured. --- card.py | 186 +++++++++++++++++++++++ simcard.py | 216 +++++++++++++++++++++++++++ sysmo-usim-tool.sjs1.py | 183 +++++++++++++++++++++++ sysmo_usimsjs1.py | 324 ++++++++++++++++++++++++++++++++++++++++ utils.py | 50 +++++++ 5 files changed, 959 insertions(+) create mode 100644 card.py create mode 100644 simcard.py create mode 100755 sysmo-usim-tool.sjs1.py create mode 100644 sysmo_usimsjs1.py create mode 100644 utils.py diff --git a/card.py b/card.py new file mode 100644 index 0000000..748a7ff --- /dev/null +++ b/card.py @@ -0,0 +1,186 @@ +#!/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 new file mode 100644 index 0000000..9241a46 --- /dev/null +++ b/simcard.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Simcard 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 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] + +# Files +GSM_SIM_MF = [0x3F, 0x00] +GSM_SIM_DF_TELECOM = [0x7F, 0x10] +GSM_SIM_DF_GSM = [0x7F, 0x20] +GSM_SIM_EF_ADN = [0x6f,0x3A] +GSM_SIM_EF_IMSI = [0x6F, 0x07] +GSM_SIM_EF_ICCID = [0x2F, 0xE2] +GSM_USIM_EF_DIR = [0x2F, 0x00] # See also: 3GPP TS 31.102 Table 105 + +# Card types +GSM_SIM = 0 +GSM_USIM = 1 + +# CHV Types +GSM_CHV1 = 0x01 +GSM_CHV2 = 0x02 + +# Record oriented read modes +GSM_SIM_INS_READ_RECORD_NEXT = 0x02 +GSM_SIM_INS_READ_RECORD_PREV = 0x03 +GSM_SIM_INS_READ_RECORD_ABS = 0x04 + +# Record oriented write modes +GSM_SIM_INS_UPDATE_RECORD_NEXT = 0x02 +GSM_SIM_INS_UPDATE_RECORD_PREV = 0x03 +GSM_SIM_INS_UPDATE_RECORD_ABS = 0x04 + +# 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 + if cardtype == GSM_USIM: + self.usim = True + else: + self.usim = True + + + # 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 + + + # 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) + + + # 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 = 0x08 + + apdu = self.card.apdu(cla, ins, p2 = chv_no, + p3 = length, data = chv) + return self.card.transact(apdu, dry, strict) + + + # 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) + + + # 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) + + + # 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) + + + # 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) + + + + + + + + + + + + diff --git a/sysmo-usim-tool.sjs1.py b/sysmo-usim-tool.sjs1.py new file mode 100755 index 0000000..6391672 --- /dev/null +++ b/sysmo-usim-tool.sjs1.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Commandline interface for sysmoUSIM-SJS1 + +(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 . +""" + +import sys, getopt +from card import * +from simcard import * +from sysmo_usimsjs1 import * + + +def banner(): + print "sysmoUSIM-SJS1 parameterization tool" + print "Copyright (c)2017 Sysmocom s.f.m.c. GmbH" + print "" + + +def helptext(): + print " * Commandline options:" + print " -v, --verbose .................. Enable debug output (trace APDUs)" + print " -a, --adm1 CHV ................. Administrator PIN (e.g 55538407)" + print " -u, --usim ..................... Enable USIM mode" + print " -c, --classic .................. Disable USIM mode (make classic-sim)" + print " -m, --mode ..................... Display mode (classic-sim or USIM?)" + print " -t, --auth ..................... Show Authentication algorithms" + print " -T, --set-auth 2G:3G ........... Set 2G/3G Auth algo (e.g. 3:3)" + print " -l, --milenage ................. Show milenage parameters" + print " -L, --set-milenage HEXSTRING ... Set milenage parameters" + print " -o, --opc ...................... Show OP/c configuration" + print " -O, --set-op HEXSTRING ......... Set OP value" + print " -C, --set-opc HEXSTRING ........ Set OPc value" + print "" + + +def main(argv): + + banner() + getopt_adm1 = None + getopt_write_sim_mode = None # True = USIM, False = classic SIM + getopt_show_sim_mode = False + getopt_verbose = False + getopt_show_auth = False + getopt_write_auth = None + getopt_show_milenage = False + getopt_write_milenage = None + getopt_show_opc = False + getopt_write_op = None + getopt_write_opc = None + + # Analyze commandline options + try: + opts, args = getopt.getopt(argv, + "hva:ucmtT:lL:oO:C:", + ["help","verbose","adm1=","usim","classic", + "mode","auth","set-auth=","milenage", + "set-milenage","opc","set-op=","set-opc="]) + except getopt.GetoptError: + print " * Error: Invalid commandline options" + sys.exit(2) + + for opt, arg in opts: + if opt in ("-h", "--help"): + helptext() + sys.exit() + elif opt in ("-v", "--verbose"): + getopt_verbose = True + elif opt in ("-a", "--adm1"): + getopt_adm1 = ascii_to_list(arg) + elif opt in ("-u", "--usim"): + getopt_write_sim_mode = True + elif opt in ("-c", "--classic"): + getopt_write_sim_mode = False + elif opt in ("-m", "--mode"): + getopt_show_sim_mode = True + elif opt in ("-t", "--auth"): + getopt_show_auth = True + elif opt in ("-T", "--set-auth"): + getopt_write_auth = arg.split(':',1) + elif opt in ("-l", "--milenage"): + getopt_show_milenage = True + elif opt in ("-L", "--set-milenage"): + getopt_write_milenage = asciihex_to_list(arg) + elif opt in ("-o", "--opc"): + getopt_show_opc = True + elif opt in ("-O", "--set-op"): + getopt_write_op = asciihex_to_list(arg) + elif opt in ("-C", "--set-opc"): + getopt_write_opc = asciihex_to_list(arg) + + + if not getopt_adm1: + print " * Error: adm1 parameter missing -- exiting..." + print "" + sys.exit(1) + + + # Claim terminal + print "Initalizing smartcard terminal..." + c = Card(getopt_verbose) + sim = Simcard(c) + print("") + + + # Execute tasks + if getopt_write_sim_mode != None: + print "Programming SIM-Mode..." + sysmo_usim_write_sim_mode(sim, getopt_adm1, + getopt_write_sim_mode) + print("") + + if getopt_show_sim_mode: + print "Reading SIM-Mode..." + sysmo_usim_show_sim_mode(sim, getopt_adm1) + print("") + + if getopt_write_auth: + print "Programming Authentication parameters..." + sysmo_usim_write_auth_params(sim, getopt_adm1, + int(getopt_write_auth[0]), + int(getopt_write_auth[1])) + print("") + + if getopt_show_auth: + print "Reading Authentication parameters..." + sysmo_usim_show_auth_params(sim, getopt_adm1) + print("") + + if getopt_write_milenage: + print "Programming Milenage parameters..." + ef_mlngc = SYSMO_USIMSJS1_FILE_EF_MLNGC(getopt_write_milenage) + sysmo_usim_write_milenage_params(sim, getopt_adm1, ef_mlngc) + print("") + + if getopt_show_milenage: + print "Reading Milenage parameters..." + sysmo_usim_show_milenage_params(sim, getopt_adm1) + print("") + + if getopt_write_op: + print "Writing OP value..." + sysmo_usim_write_opc_params(sim, + getopt_adm1, 0, getopt_write_op) + print("") + + if getopt_write_opc: + print "Writing OPCC value..." + sysmo_usim_write_opc_params(sim, + getopt_adm1, 1, getopt_write_opc) + print("") + + if getopt_show_opc: + print "Reading OPC value..." + sysmo_usim_show_opc_params(sim, getopt_adm1) + print("") + + print "Done!" + + +if __name__ == "__main__": + main(sys.argv[1:]) + + + diff --git a/sysmo_usimsjs1.py b/sysmo_usimsjs1.py new file mode 100644 index 0000000..d022ea2 --- /dev/null +++ b/sysmo_usimsjs1.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Gadgets to modify SYSMO USIM SJS1 parameters + +(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 . +""" + +# Some gadgets to handle functions specific to Sysmo USIM SJS1. The gadgets are +# organized as a loose collection of python functions. Each function serves +# a specific task (e.g. modifiying the auth parameters). For each task two +# functions are implemented sysmo_usim_show_...() to inspect the data that is +# intended to be modified and sysmo_usim_write_...() to perform the actual +# modification task. + +# Partial File tree: +# The following tree is incomplete, it just contains the propritary files we +# need to perform the tasks implemented below: +# +# [MF 0x3F00] +# | +# +--[DF_AUTH 0x7FCC] +# | | +# | +--[EF_AUTH 0x6F00] +# | | +# | +--[EF_MLNGC 0x6F01] +# | +# +--[DF_GSM 0x7F20] +# | +# +--[EF_OPC 0x00F7] +# | +# +--[EF_KI 0x00FF] + + +from card import * +from simcard import * + +# Files (propritary) +SYSMO_USIMSJS1_EF_KI = [0x00, 0xFF] +SYSMO_USIMSJS1_EF_OPC = [0x00, 0xF7] +SYSMO_USIMSJS1_DF_AUTH = [0x7F, 0xCC] #FIXME: Manual does not mention name, just called it "DF_AUTH" might be wrong! +SYSMO_USIMSJS1_EF_AUTH = [0x6F, 0x00] +SYSMO_USIMSJS1_EF_MLNGC = [0x6F, 0x01] + +# CHV Types +SYSMO_USIMSJS1_ADM1 = 0x0A + +# Authentication algorithms (See sysmousim.pdf cap. 8.5) +SYSMO_USIMSJS1_ALGO_MILENAGE = 0x01 +SYSMO_USIMSJS1_ALGO_COMP12V1 = 0x03 +SYSMO_USIMSJS1_ALGO_XOR2G = 0x04 +SYSMO_USIMSJS1_ALGO_COMP128V2 = 0x06 +SYSMO_USIMSJS1_ALGO_COMP128V3 = 0x07 +SYSMO_USIMSJS1_ALGO_XOR3G = 0x08 + +# Application identifier +SYSMO_USIM_AID = [0xa0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02] + +# Default content of record No.1 in EF.DIR +SYSMO_USIM_EF_DIR_REC_1_CONTENT = [0x61, 0x19, 0x4f, 0x10] + SYSMO_USIM_AID + \ + [0xff, 0xff, 0xff, 0xff, 0x89, 0x07, 0x09, 0x00, 0x00, 0x50, 0x05, + 0x55, 0x53, 0x69, 0x6d, 0x31, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff] + +# Abstraction for the file structure of EF.MLNGC, which holds the +# parameters of the milenage authentication algorithm +class SYSMO_USIMSJS1_FILE_EF_MLNGC: + # Default parameters, see also sysmousim-manual.pdf, + # cap. 8.6 "Milenage Configuration (Ci/Ri) + C1 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + C2 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01] + C3 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02] + C4 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04] + C5 = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08] + R1 = 0x40 + R2 = 0x00 + R3 = 0x20 + R4 = 0x40 + R5 = 0x60 + + def __init__(self, content = None): + if content == None: + return + if len(content) != 85: + return + self.C1 = content[0:16] + self.C2 = content[16:32] + self.C3 = content[32:48] + self.C4 = content[48:64] + self.C5 = content[64:80] + self.R1 = content[80] + self.R2 = content[81] + self.R3 = content[82] + self.R4 = content[83] + self.R5 = content[84] + + def __str__(self): + dump = " C1: " + hexdump(self.C1) + "\n" + dump += " C2: " + hexdump(self.C2) + "\n" + dump += " C3: " + hexdump(self.C3) + "\n" + dump += " C4: " + hexdump(self.C4) + "\n" + dump += " C5: " + hexdump(self.C5) + "\n" + dump += " R1: " + str(hex(self.R1)) + "\n" + dump += " R2: " + str(hex(self.R2)) + "\n" + dump += " R3: " + str(hex(self.R3)) + "\n" + dump += " R4: " + str(hex(self.R4)) + "\n" + dump += " R5: " + str(hex(self.R5)) + return dump + + +# Initalize card (select master file) +def sysmo_usim_init(sim): + print " * Initalizing..." + sim.select(GSM_SIM_MF) + + +# Authenticate as administrator +def sysmo_usim_admin_auth(sim, adm1): + print " * Authenticating at card as adminstrator..." + sim.verify_chv(adm1, SYSMO_USIMSJS1_ADM1) + + +# Show current athentication parameters +# (Which algorithim is used for which rat?) +def sysmo_usim_show_auth_params(sim, adm1): + sysmo_usim_init(sim) + sysmo_usim_admin_auth(sim, adm1) + + print " * Reading..." + sim.select(SYSMO_USIMSJS1_DF_AUTH) + sim.select(SYSMO_USIMSJS1_EF_AUTH) + res = sim.read_binary(0x02) + + print " * Current algorithm setting:" + print " 2G: " + str(hex(res.apdu[0])) + print " 3G: " + str(hex(res.apdu[1])) + + +# Program new authentication parameters +def sysmo_usim_write_auth_params(sim, adm1, algo_2g, algo_3g): + print " * New algorithm setting:" + print " 2G: " + str(hex(algo_2g)) + print " 3G: " + str(hex(algo_3g)) + + sysmo_usim_init(sim) + sysmo_usim_admin_auth(sim, adm1) + + print " * Programming..." + sim.select(SYSMO_USIMSJS1_DF_AUTH) + sim.select(SYSMO_USIMSJS1_EF_AUTH) + sim.update_binary([algo_2g,algo_3g]) + + +# Show current milenage parameters +def sysmo_usim_show_milenage_params(sim, adm1): + sysmo_usim_init(sim) + sysmo_usim_admin_auth(sim, adm1) + + +# Show current milenage parameters +def sysmo_usim_show_milenage_params(sim, adm1): + sysmo_usim_init(sim) + sysmo_usim_admin_auth(sim, adm1) + + sim.select(SYSMO_USIMSJS1_DF_AUTH) + sim.select(SYSMO_USIMSJS1_EF_MLNGC) + + print " * Reading..." + ef_mlngc = SYSMO_USIMSJS1_FILE_EF_MLNGC() + res = sim.read_binary(16, offset = 0) + ef_mlngc.C1 = res.apdu + res = sim.read_binary(16, offset = 16) + ef_mlngc.C2 = res.apdu + res = sim.read_binary(16, offset = 32) + ef_mlngc.C3 = res.apdu + res = sim.read_binary(16, offset = 48) + ef_mlngc.C4 = res.apdu + res = sim.read_binary(16, offset = 64) + ef_mlngc.C5 = res.apdu + res = sim.read_binary(1, offset = 80) + ef_mlngc.R1 = res.apdu[0] + res = sim.read_binary(1, offset = 81) + ef_mlngc.R2 = res.apdu[0] + res = sim.read_binary(1, offset = 82) + ef_mlngc.R3 = res.apdu[0] + res = sim.read_binary(1, offset = 83) + ef_mlngc.R4 = res.apdu[0] + res = sim.read_binary(1, offset = 84) + ef_mlngc.R5 = res.apdu[0] + + print " * Current Milenage Parameters in (EF.MLNGC):" + print str(ef_mlngc) + + +# Write new milenage parameters +def sysmo_usim_write_milenage_params(sim, adm1, ef_mlngc): + sysmo_usim_init(sim) + sysmo_usim_admin_auth(sim, adm1) + + print " * New Milenage Parameters for (EF.MLNGC):" + print str(ef_mlngc) + + sim.select(SYSMO_USIMSJS1_DF_AUTH) + sim.select(SYSMO_USIMSJS1_EF_MLNGC) + + print " * Programming..." + sim.update_binary(ef_mlngc.C1, offset = 0) + sim.update_binary(ef_mlngc.C2, offset = 16) + sim.update_binary(ef_mlngc.C3, offset = 32) + sim.update_binary(ef_mlngc.C4, offset = 48) + sim.update_binary(ef_mlngc.C5, offset = 64) + sim.update_binary([ef_mlngc.R1], offset = 80) + sim.update_binary([ef_mlngc.R2], offset = 81) + sim.update_binary([ef_mlngc.R3], offset = 82) + sim.update_binary([ef_mlngc.R4], offset = 83) + sim.update_binary([ef_mlngc.R5], offset = 84) + + +# Show current OPc value +def sysmo_usim_show_opc_params(sim, adm1): + sysmo_usim_init(sim) + sysmo_usim_admin_auth(sim, adm1) + + print " * Reading..." + sim.select(GSM_SIM_DF_GSM) + sim.select(SYSMO_USIMSJS1_EF_OPC) + res = sim.read_binary(17) + + print " * Current OPc setting:" + print " OP: " + str(hex(res.apdu[0])) + print " OP/OPc: " + hexdump(res.apdu[1:]) + + +# Program new OPc value +def sysmo_usim_write_opc_params(sim, adm1, select, op): + print " * New OPc setting:" + print " OP: " + str(hex(select)) + print " OP/OPc: " + hexdump(op) + + sysmo_usim_init(sim) + sysmo_usim_admin_auth(sim, adm1) + + sim.select(GSM_SIM_DF_GSM) + sim.select(SYSMO_USIMSJS1_EF_OPC) + + print " * Programming..." + sim.update_binary([select] + op) + + +# Show the enable status of the USIM application (app is enabled or disabled?) +def sysmo_usim_show_usim_status(sim, adm1): + sysmo_usim_init(sim) + sysmo_usim_admin_auth(sim, adm1) + + print " * Reading..." + sim.select(GSM_USIM_EF_DIR) + res = sim.read_record(0x26, rec_no = 1) + + print " * Current status of Record No.1 in EF.DIR:" + print " " + hexdump(res.apdu) + + +# Show the enable status of the USIM application (app is enabled or disabled?) +def sysmo_usim_show_sim_mode(sim, adm1): + sysmo_usim_init(sim) + sysmo_usim_admin_auth(sim, adm1) + + print " * Reading..." + sim.select(GSM_USIM_EF_DIR) + res = sim.read_record(0x26, rec_no = 1) + + print " * Current status of Record No. 1 in EF.DIR:" + print " " + hexdump(res.apdu) + + if hexdump(SYSMO_USIM_AID) in hexdump(res.apdu): + print " ==> USIM application enabled" + else: + print " ==> USIM application disabled" + + +# Show the enable status of the USIM application (app is enabled or disabled?) +def sysmo_usim_write_sim_mode(sim, adm1, usim_enabled = True): + if usim_enabled: + new_record = SYSMO_USIM_EF_DIR_REC_1_CONTENT + else: + new_record = [0xFF] * len(SYSMO_USIM_EF_DIR_REC_1_CONTENT) + + print " * New status of Record No.1 in EF.DIR:" + print " " + hexdump(new_record) + if hexdump(SYSMO_USIM_AID) in hexdump(new_record): + print " ==> USIM application enabled" + else: + print " ==> USIM application disabled" + + sysmo_usim_init(sim) + sysmo_usim_admin_auth(sim, adm1) + + print " * Programming..." + sim.select(GSM_USIM_EF_DIR) + sim.update_record(new_record, rec_no = 1) + + diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..df47dea --- /dev/null +++ b/utils.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +A collection of useful routines to make this tool work + +(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 . +""" + +# Convert list to an printable ascii hex string +def hexdump(array): + if array: + return ''.join('{:02x}'.format(x) for x in array) + else: + return "(no data)" + + +# Convert ascii string with decimal numbers to numeric ascii-code list +def ascii_to_list(string): + rc = [] + for c in string: + rc.append(ord(c)) + return rc + + +# Convert an ascii hex string to numeric list +def asciihex_to_list(string): + + string = string.translate(None, ':') + try: + return map(ord, string.decode("hex")) + except: + print "Warning: Invalid hex string -- ignored!" + return []