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.
This commit is contained in:
commit
4f2e60436b
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
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)
|
||||
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
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:])
|
||||
|
||||
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
# 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)
|
||||
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
# 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 []
|
Loading…
Reference in New Issue