Adding support for the new SJA2 simcard type requires comprensive refactoring of sysmo-usim-tool. We resolve the commonalities of both simcard models in a class based approach so that code duplication is reduced to a minium. - Refactor sysmo-usim-tool into a class based model - Add support for SJA2 simcard models - Add tests for SJA2 simcard models Change-Id: I0feab71bdaab079b35737bb4e0e2a417c4a1f96b Related: SYS#4466for/master/ret
parent
08623baa4a
commit
8dd52e04ac
@ -0,0 +1,209 @@ |
||||
#!/usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
|
||||
""" |
||||
User interface parts that are common for all tools |
||||
|
||||
(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 utils import * |
||||
import sys, getopt |
||||
|
||||
COMMON_GETOPTS = "hfa:J:nN:lL:kK:tT:oO:C:sSi" |
||||
COMMON_GETOPTS_LONG = ["help", "force", "adm1=", "set-imsi", "mnclen", |
||||
"set-mnclen", "milenage", "set-milenage", "ki", |
||||
"set-ki=", "auth", "set-auth=", "opc", "set-op=", |
||||
"set-opc=", "seq-parameters", "reset-seq-parameters" |
||||
"iccid"] |
||||
|
||||
# Parse common commandline options and keep them as flags |
||||
class Common(): |
||||
|
||||
sim = None |
||||
|
||||
show_helptext = None |
||||
force = False |
||||
adm1 = None |
||||
write_imsi = None |
||||
write_mnclen = None |
||||
show_mnclen = None |
||||
show_milenage = False |
||||
write_milenage = None |
||||
show_ki = None |
||||
write_ki = None |
||||
show_auth = False |
||||
write_auth = None |
||||
show_opc = False |
||||
write_op = None |
||||
write_opc = None |
||||
show_seq_par = False |
||||
reset_seq_par = False |
||||
show_iccid = False |
||||
|
||||
def __init__(self, argv, getopts, getopts_long): |
||||
|
||||
self._banner() |
||||
|
||||
# Analyze commandline options |
||||
try: |
||||
opts, args = getopt.getopt(argv, COMMON_GETOPTS + getopts, |
||||
COMMON_GETOPTS_LONG + getopts_long) |
||||
except getopt.GetoptError: |
||||
print " * Error: Invalid commandline options" |
||||
sys.exit(2) |
||||
|
||||
# Set flags for common options |
||||
for opt, arg in opts: |
||||
if opt in ("-h", "--help"): |
||||
self.__common_helptext() |
||||
sys.exit(0) |
||||
elif opt in ("-f", "--force"): |
||||
self.force = True |
||||
elif opt in ("-a", "--adm1"): |
||||
self.adm1 = ascii_to_list(arg) |
||||
elif opt in ("-J", "--set-imsi"): |
||||
self.write_imsi = asciihex_to_list(pad_asciihex(arg, True, '9')) |
||||
elif opt in ("-n", "--mnclen"): |
||||
self.show_mnclen = True |
||||
elif opt in ("-N", "--set-mnclen"): |
||||
self.write_mnclen = asciihex_to_list(arg) |
||||
elif opt in ("-l", "--milenage"): |
||||
self.show_milenage = True |
||||
elif opt in ("-L", "--set-milenage"): |
||||
self.write_milenage = asciihex_to_list(arg) |
||||
elif opt in ("-k", "--ki"): |
||||
self.show_ki = True |
||||
elif opt in ("-K", "--set-ki"): |
||||
self.write_ki = asciihex_to_list(arg) |
||||
elif opt in ("-t", "--auth"): |
||||
self.show_auth = True |
||||
elif opt in ("-T", "--set-auth"): |
||||
self.write_auth = arg.split(':',1) |
||||
elif opt in ("-o", "--opc"): |
||||
self.show_opc = True |
||||
elif opt in ("-O", "--set-op"): |
||||
self.write_op = asciihex_to_list(arg) |
||||
elif opt in ("-C", "--set-opc"): |
||||
self.write_opc = asciihex_to_list(arg) |
||||
elif opt in ("-s", "--sqe-parameters"): |
||||
self.show_seq_par = True |
||||
elif opt in ("-S", "--reset-sqe-parameters"): |
||||
self.reset_seq_par = True |
||||
elif opt in ("-i", "--iccid"): |
||||
self.show_iccid = True |
||||
|
||||
# Check for ADM1 key |
||||
if not self.adm1: |
||||
print " * Error: adm1 parameter missing -- exiting..." |
||||
print "" |
||||
sys.exit(1) |
||||
|
||||
# Set flags for specific options |
||||
self._options(opts) |
||||
|
||||
# Initialize |
||||
self._init() |
||||
|
||||
# Execute tasks |
||||
self.__common_execute() |
||||
|
||||
|
||||
# Print the part of the helptext that is common for all tools |
||||
def __common_helptext(self): |
||||
print(" * Commandline options:") |
||||
print(" -h, --help ..................... Show this screen") |
||||
print(" -f, --force .................... Enforce authentication after failure") |
||||
print(" -a, --adm1 CHV ................. Administrator PIN (e.g 55538407)") |
||||
print(" -J, --set-imsi ................. Set IMSI value") |
||||
print(" -n, --mnclen ................... Show MNC length value") |
||||
print(" -N, --set-mnclen ............... Set MNC length value") |
||||
print(" -l, --milenage ................. Show milenage parameters") |
||||
print(" -L, --set-milenage HEXSTRING ... Set milenage parameters") |
||||
print(" -k, --ki ....................... Show KI value") |
||||
print(" -K, --set-ki ................... Set KI value") |
||||
print(" -t, --auth ..................... Show Authentication algorithms") |
||||
print(" -T, --set-auth 2G:3G ........... Set 2G/3G Auth algo (e.g. COMP128v1:COMP128v1)") |
||||
print(" -o, --opc ...................... Show OP/c configuration") |
||||
print(" -O, --set-op HEXSTRING ......... Set OP value") |
||||
print(" -C, --set-opc HEXSTRING ........ Set OPc value") |
||||
print(" -s --seq-parameters ........... Show MILENAGE SEQ/SQN parameters") |
||||
print(" -S --reset-seq-parameters ..... Reset MILENAGE SEQ/SQN parameters to default") |
||||
print(" -i --iccid .................... Show ICCID") |
||||
self._helptext() |
||||
|
||||
|
||||
# Execute common tasks |
||||
def __common_execute(self): |
||||
|
||||
# Autnetnication is a primary task that must always run before |
||||
# any other task is carried out |
||||
if self.sim.admin_auth(self.adm1, self.force) == False: |
||||
exit(1) |
||||
|
||||
# First run the card specific tasks |
||||
self._execute() |
||||
|
||||
# And then the common tasks |
||||
if self.write_imsi: |
||||
self.sim.write_imsi(self.write_imsi) |
||||
|
||||
if self.show_mnclen: |
||||
self.sim.show_mnclen() |
||||
|
||||
if self.write_mnclen: |
||||
self.sim.write_mnclen(self.write_mnclen) |
||||
|
||||
if self.write_milenage: |
||||
self.sim.write_milenage_params(self.write_milenage) |
||||
|
||||
if self.show_milenage: |
||||
self.sim.show_milenage_params() |
||||
|
||||
if self.write_ki: |
||||
self.sim.write_ki_params(self.write_ki) |
||||
|
||||
if self.show_ki: |
||||
self.sim.show_ki_params() |
||||
|
||||
if self.show_auth: |
||||
self.sim.show_auth_params() |
||||
|
||||
if self.write_auth: |
||||
self.sim.write_auth_params(self.write_auth[0], self.write_auth[1]) |
||||
|
||||
if self.show_opc: |
||||
self.sim.show_opc_params() |
||||
|
||||
if self.write_op: |
||||
self.sim.write_opc_params(0, self.write_op) |
||||
|
||||
if self.write_opc: |
||||
self.sim.write_opc_params(1, self.write_opc) |
||||
|
||||
if self.show_seq_par: |
||||
self.sim.show_milenage_sqn_params() |
||||
|
||||
if self.reset_seq_par: |
||||
self.sim.reset_milenage_sqn_params() |
||||
|
||||
if self.show_iccid: |
||||
self.sim.show_iccid() |
||||
|
||||
print "Done!" |
@ -0,0 +1,75 @@ |
||||
#!/usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
|
||||
from utils import * |
||||
from sysmo_isim_sja2 import * |
||||
|
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
|
||||
print("HELLO EF_SIM_AUTH_KEY!") |
||||
myfile = SYSMO_ISIMSJA2_FILE_EF_SIM_AUTH_KEY() |
||||
print myfile |
||||
myfile.use_opc = True |
||||
myfile.sres_dev_func = 2 |
||||
myfile.opc = [0x23] * 16 |
||||
myfile.key = [0x42] * 16 |
||||
print myfile |
||||
myfile2 = SYSMO_ISIMSJA2_FILE_EF_SIM_AUTH_KEY(myfile.encode()) |
||||
print myfile2 |
||||
|
||||
print("=========================================================") |
||||
|
||||
|
||||
|
||||
myfile = SYSMO_ISIMSJA2_FILE_EF_USIM_AUTH_KEY_2G() |
||||
print myfile |
||||
|
||||
myfile.algo = SYSMO_ISIMSJA2_ALGO_MILENAGE; |
||||
myfile.sres_dev_func = 2 |
||||
myfile.opc = [0x23] * 16 |
||||
myfile.key = [0x42] * 16 |
||||
print myfile |
||||
|
||||
myfile2 = SYSMO_ISIMSJA2_FILE_EF_USIM_AUTH_KEY_2G(myfile.encode()) |
||||
print myfile2 |
||||
|
||||
|
||||
print("==========================================================") |
||||
|
||||
|
||||
|
||||
myfile = SYSMO_ISIMSJA2_FILE_EF_MILENAGE_CFG() |
||||
print myfile |
||||
|
||||
myfile.R1 = 0xAA |
||||
myfile.R2 = 0xBB |
||||
myfile.R3 = 0xCC |
||||
myfile.R4 = 0xDD |
||||
myfile.R5 = 0xEE |
||||
myfile.C1 = [0xDE] * 16 |
||||
myfile.C2 = [0xAD] * 16 |
||||
myfile.C3 = [0xBE] * 16 |
||||
myfile.C4 = [0xEF] * 16 |
||||
myfile.C5 = [0x12] * 16 |
||||
|
||||
|
||||
print myfile |
||||
|
||||
myfile2 = SYSMO_ISIMSJA2_FILE_EF_MILENAGE_CFG(myfile.encode()) |
||||
print myfile2 |
||||
|
||||
|
||||
print("==========================================================") |
||||
|
||||
myfile = SYSMO_ISIMSJA2_FILE_EF_USIM_SQN() |
||||
print myfile |
||||
print myfile.encode() |
||||
|
||||
myfile2 = SYSMO_ISIMSJA2_FILE_EF_USIM_SQN(myfile.encode()) |
||||
print myfile2 |
||||
print myfile2.encode() |
||||
|
||||
|
||||
|
@ -0,0 +1,78 @@ |
||||
#!/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 utils import * |
||||
from simcard import * |
||||
from sysmo_isim_sja2 import * |
||||
from common import * |
||||
|
||||
class Application(Common): |
||||
|
||||
getopt_dump = False |
||||
|
||||
|
||||
# Automatically executed by superclass |
||||
def _banner(self): |
||||
print("sysmoISIM-SJA2 parameterization tool") |
||||
print("Copyright (c)2019 Sysmocom s.f.m.c. GmbH") |
||||
print("") |
||||
|
||||
|
||||
# Automatically executed by superclass |
||||
def _options(self, opts): |
||||
for opt, arg in opts: |
||||
if opt in ("-d", "--dump"): |
||||
self.getopt_dump = True |
||||
|
||||
|
||||
# Automatically executed by superclass when -h or --help is supplied as option |
||||
def _helptext(self): |
||||
print(" -d, --dump ..................... Dump propritary file contents") |
||||
print("") |
||||
print(" For Option -T, the following algorithms are valid:") |
||||
print('\n'.join([' %d %s' % entry for entry in sysmo_isimsja2_algorithms])) |
||||
print("") |
||||
|
||||
|
||||
# Automatically executed by superclass before _execute() is called |
||||
def _init(self): |
||||
self.sim = Sysmo_isim_sja2() |
||||
|
||||
|
||||
# Automatically executed by superclass |
||||
def _execute(self): |
||||
|
||||
if self.getopt_dump: |
||||
self.sim.dump() |
||||
|
||||
|
||||
def main(argv): |
||||
|
||||
Application(argv, "d", ["dump"]) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
main(sys.argv[1:]) |
@ -0,0 +1,839 @@ |
||||
#!/usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
|
||||
""" |
||||
Gadgets to modify SYSMO USIM SJA2 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/>. |
||||
""" |
||||
|
||||
import sys |
||||
from utils import * |
||||
from sysmo_usim import * |
||||
import math |
||||
|
||||
# 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_SYSTEM 0xA515] |
||||
# | | |
||||
# | +--[EF_SIM_AUTH_KEY 0x6F20] (regular file) |
||||
# | |
||||
# +--[ADF_USIM] |
||||
# | | |
||||
# | +--[USIM_AUTH_KEY 0xAF20] (regular file) |
||||
# | | |
||||
# | +--[EF_USIM_AUTH_KEY_2G 0xAF22] (link to DF_SYSTEM/EF_SIM_AUTH_KEY) |
||||
# | |
||||
# +--[ADF_ISIM] |
||||
# | |
||||
# +--[USIM_AUTH_KEY 0xAF20] (regular file) |
||||
# | |
||||
# +--[EF_USIM_AUTH_KEY_2G 0xAF22] (link to DF_SYSTEM/EF_SIM_AUTH_KEY) |
||||
# |
||||
# Note: EF_MILENAGE_CFG and EF_USIM_SQN not yet listed here. |
||||
|
||||
# Propritary files |
||||
SYSMO_ISIMSJA2_DF_SYSTEM = [0xA5, 0x15] |
||||
SYSMO_ISIMSJA2_EF_SIM_AUTH_KEY = [0x6F, 0x20] # DF_SYSTEM |
||||
SYSMO_ISIMSJA2_EF_USIM_AUTH_KEY = [0xAF, 0x20] # ADF.USIM |
||||
SYSMO_ISIMSJA2_EF_USIM_AUTH_KEY_2G = [0xAF, 0x22] # ADF.USIM |
||||
SYSMO_ISIMSJA2_EF_USIM_AUTH_KEY_GBA = [0xAF, 0x23] # ADF.USIM |
||||
SYSMO_ISIMSJA2_EF_MILENAGE_CFG = [0xAF, 0x21] # ADF.USIM |
||||
SYSMO_ISIMSJA2_EF_USIM_SQN = [0xAF, 0x30] # ADF.USIM |
||||
SYSMO_ISIMSJA2_EF_GBA_SK = [0xAF, 0x31] # ADF.USIM |
||||
SYSMO_ISIMSJA2_EF_GBA_REC_LIST = [0xAF, 0x32] # ADF.USIM |
||||
SYSMO_ISIMSJA2_EF_GBA_INT_KEY = [0xAF, 0x32] # ADF.USIM |
||||
|
||||
# Authentication algorithms |
||||
SYSMO_ISIMSJA2_ALGO_COMP12V1 = 0x01 |
||||
SYSMO_ISIMSJA2_ALGO_COMP12V2 = 0x02 |
||||
SYSMO_ISIMSJA2_ALGO_COMP12V3 = 0x03 |
||||
SYSMO_ISIMSJA2_ALGO_MILENAGE = 0x04 |
||||
SYSMO_ISIMSJA2_ALGO_SHA1AKA = 0x05 |
||||
SYSMO_ISIMSJA2_ALGO_XOR = 0x0F |
||||
|
||||
sysmo_isimsja2_algorithms = ( |
||||
(SYSMO_ISIMSJA2_ALGO_COMP12V1, 'COMP128v1'), |
||||
(SYSMO_ISIMSJA2_ALGO_COMP12V2, 'COMP128v2'), |
||||
(SYSMO_ISIMSJA2_ALGO_COMP12V3, 'XOR-2G'), |
||||
(SYSMO_ISIMSJA2_ALGO_MILENAGE, 'MILENAGE'), |
||||
(SYSMO_ISIMSJA2_ALGO_SHA1AKA , 'SHA1-AKA'), |
||||
(SYSMO_ISIMSJA2_ALGO_XOR, 'XOR'), |
||||
) |
||||
|
||||
class SYSMO_ISIMSJA2_FILE_EF_XSIM_AUTH_KEY: |
||||
""" |
||||
Superclass model that generates that handles the header byte of |
||||
SYSMO_ISIMSJA2_EF_USIM_AUTH_KEY, SYSMO_ISIMSJA2_EF_USIM_AUTH_KEY_2G |
||||
and SYSMO_ISIMSJA2_EF_USIM_AUTH_KEY_GBA. |
||||
""" |
||||
|
||||
algo = SYSMO_ISIMSJA2_ALGO_COMP12V1 |
||||
use_opc = False |
||||
sres_dev_func = 1 |
||||
|
||||
def __init__(self, content = None): |
||||
if content == None: |
||||
return |
||||
|
||||
header = content[0] |
||||
self.algo = header & 0x0F |
||||
self.use_opc = bool((header >> 4) & 1) |
||||
|
||||
if (header >> 5) & 1: |
||||
self.sres_dev_func = 2 |
||||
else: |
||||
self.sres_dev_func = 1 |
||||
|
||||
|
||||
def __str__(self): |
||||
dump = "" |
||||
pfx = " " |
||||
|
||||
dump += pfx + "Algorithm: " |
||||
dump += id_to_str(sysmo_isimsja2_algorithms, self.algo) |
||||
dump += "\n" |
||||
|
||||
if self.use_opc == True: |
||||
dump += pfx + "Milenage: use OPc\n" |
||||
else: |
||||
dump += pfx + "Milenage: use OP\n" |
||||
|
||||
dump += pfx + "Milenage: use SRES deviation function " + str(self.sres_dev_func) + "\n" |
||||
|
||||
return dump |
||||
|
||||
|
||||
def encode(self): |
||||
out = [0x00] |
||||
out[0] = self.algo & 0x0F |
||||
if self.use_opc == True: |
||||
out[0] |= 1 << 4 |
||||
out[0] |= ((self.sres_dev_func-1) & 1) << 5 |
||||
return out |
||||
|
||||
|
||||
class SYSMO_ISIMSJA2_FILE_EF_SIM_AUTH_KEY(SYSMO_ISIMSJA2_FILE_EF_XSIM_AUTH_KEY): |
||||
|
||||
key = [0xAA] * 16 |
||||
opc = [0xBB] * 16 |
||||
|
||||
def __init__(self, content = None): |
||||
if content == None: |
||||
return |
||||
|
||||
SYSMO_ISIMSJA2_FILE_EF_XSIM_AUTH_KEY.__init__(self, content) |
||||
self.key = content[1:17] |
||||
self.opc = content[17:33] |
||||
|
||||
|
||||
def __str__(self): |
||||
dump = "" |
||||
pfx = " " |
||||
|
||||
dump += SYSMO_ISIMSJA2_FILE_EF_XSIM_AUTH_KEY.__str__(self) |
||||
|
||||
if self.algo == SYSMO_ISIMSJA2_ALGO_MILENAGE: |
||||
dump += pfx + "Key: " + hexdump(self.key) + "\n" |
||||
dump += pfx + "OPc: " + hexdump(self.opc) |
||||
elif self.algo == SYSMO_ISIMSJA2_ALGO_XOR: |
||||
dump += pfx + "Key: " + hexdump(self.key) + "\n" |
||||
dump += pfx + "OPc: " + hexdump(self.opc) |
||||
elif self.algo == SYSMO_ISIMSJA2_ALGO_SHA1AKA: |
||||
dump += pfx + "Root key: " + hexdump(self.key) + "\n" |
||||
dump += pfx + "OPc: " + hexdump(self.opc) + " (unused)" |
||||
else: |
||||
dump += pfx + "Key: " + hexdump(self.key) + "\n" |
||||
dump += pfx + "OPc: " + hexdump(self.opc) + " (unused)" |
||||
|
||||
return dump |
||||
|
||||
|
||||
def encode(self): |
||||
out = SYSMO_ISIMSJA2_FILE_EF_XSIM_AUTH_KEY.encode(self) |
||||
out += self.key + self.opc |
||||
return out |
||||
|
||||
|
||||
class SYSMO_ISIMSJA2_FILE_EF_USIM_AUTH_KEY(SYSMO_ISIMSJA2_FILE_EF_XSIM_AUTH_KEY): |
||||
|
||||
full_res = True # Return full 8-byte RES or first 4 bytes only |
||||
ext_res = False # Return 16 byte RES (ignores full_res, only valid with 3G XOR) |
||||
|
||||
key = [0x00] * 16 |
||||
opc = [0x00] * 16 # Only for Milenage |
||||
|
||||
def __init__(self, content = None): |
||||
if content == None: |
||||
return |
||||
|
||||
SYSMO_ISIMSJA2_FILE_EF_XSIM_AUTH_KEY.__init__(self, content) |
||||
header = content[0] |
||||
|
||||
self.full_res = bool((header >> 6) & 1) |
||||
self.ext_res = bool((header >> 7) & 1) |
||||
|
||||
self.key = content[1:17] |
||||
if len(content) > 17: |
||||
self.opc = content[17:33] |
||||
|
||||
|
||||
def __str__(self): |
||||
dump = "" |
||||
pfx = " " |
||||
|
||||
dump += SYSMO_ISIMSJA2_FILE_EF_XSIM_AUTH_KEY.__str__(self) |
||||
if self.full_res == True and self.ext_res == False: |
||||
dump += pfx + "3G: Return full 8-byte RES\n" |
||||
elif self.full_res == False and self.ext_res == False: |
||||
dump += pfx + "3G: Return first four bytes of RES\n" |
||||
elif self.ext_res == True: |
||||
dump += pfx + "3G: Return 16-byte RES (XOR 3G only)\n" |
||||
else: |
||||
dump += pfx + "(invalid RES length setting)" |
||||
|
||||
if self.algo != SYSMO_ISIMSJA2_ALGO_XOR and self.ext_res: |
||||
dump += pfx + "Warning: 16-byte RES is only valid with XOR 3G!\n" |
||||
|
||||
if self.algo == SYSMO_ISIMSJA2_ALGO_MILENAGE: |
||||
dump += pfx + "Key: " + hexdump(self.key) + "\n" |
||||
dump += pfx + "OPc: " + hexdump(self.opc) |
||||
elif self.algo == SYSMO_ISIMSJA2_ALGO_XOR: |
||||
dump += pfx + "Key: " + hexdump(self.key) + "\n" |
||||
dump += pfx + "OPc: " + hexdump(self.opc) |
||||
elif self.algo == SYSMO_ISIMSJA2_ALGO_SHA1AKA: |
||||
dump += pfx + "Root key: " + hexdump(self.key) + "\n" |
||||
dump += pfx + "OPc: " + hexdump(self.opc) + " (unused)" |
||||
else: |
||||
dump += pfx + "Key: " + hexdump(self.key) + "\n" |
||||
dump += pfx + "OPc: " + hexdump(self.opc) + " (unused)" |
||||
return dump |
||||
|
||||
|
||||
def encode(self): |
||||
out = SYSMO_ISIMSJA2_FILE_EF_XSIM_AUTH_KEY.encode(self) |
||||
if self.full_res == True: |
||||
out[0] |= 1 << 6 |
||||
if self.ext_res == True: |
||||
out[0] |= 1 << 7 |
||||
out += self.key |
||||
|
||||
# Note: Normally an OPc is only used with milenage, but lets |
||||
# write the value anyway, even if it is not used. |
||||
out += self.opc |
||||
return out |
||||
|
||||
|
||||
# EF_USIM_AUTH_KEY_2G and EF_USIM_AUTH_KEY_GBA have the same layout as |
||||
# EF_USIM_AUTH_KEY, so there is nothing to specialize other than the class name |
||||
class SYSMO_ISIMSJA2_FILE_EF_USIM_AUTH_KEY_2G(SYSMO_ISIMSJA2_FILE_EF_USIM_AUTH_KEY): |
||||
pass |
||||
|
||||
|
||||
class SYSMO_ISIMSJA2_FILE_EF_USIM_AUTH_KEY_GBA(SYSMO_ISIMSJA2_FILE_EF_USIM_AUTH_KEY): |
||||
pass |
||||
|
||||
|
||||
class SYSMO_ISIMSJA2_FILE_EF_MILENAGE_CFG: |
||||
R1 = 0x40 |
||||
R2 = 0x00 |
||||
R3 = 0x20 |
||||
R4 = 0x40 |
||||
R5 = 0x60 |
||||
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] |
||||
|
||||
|
||||
def __init__(self, content = None): |
||||
if content == None: |
||||
return |
||||
if len(content) != 85: |
||||
return |
||||
self.R1 = content[0] |
||||
self.R2 = content[1] |
||||
self.R3 = content[2] |
||||
self.R4 = content[3] |
||||
self.R5 = content[4] |
||||
self.C1 = content[5:5+16] |
||||
self.C2 = content[21:37] |
||||
self.C3 = content[37:53] |
||||
self.C4 = content[53:69] |
||||
self.C5 = content[69:85] |
||||
|
||||
|
||||
def __str__(self): |
||||
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)) + "\n" |
||||
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) |
||||
return dump |
||||
|
||||
|
||||
def encode(self): |
||||
out = [self.R1, self.R2, self.R3, self.R4, self.R5] |
||||
out += self.C1 + self.C2 + self.C3 + self.C4 + self.C5 |
||||
return out |
||||
|
||||
|
||||
class SYSMO_ISIMSJA2_FILE_EF_USIM_SQN: |
||||
|
||||
# Flag1: |
||||
ind_size_bits = 5 # speficy file length by 2^ind_len |
||||
sqn_check_enabled = True # perform SQN checks below |
||||
sqn_age_limit_enabled = False # perform age limit check: (SQNms-SQN) <= AGE_LIMIT) |
||||
sqn_max_delta_enabled = True # perform delta max check: (SWN-SQNms) <= DELTA MAX) |
||||
sqn_check_skip_first = True # accept any SQN on the first authentication |
||||
|
||||
# Flag2: |
||||
conceal_autn = True # Conceal the value of AUTN |
||||
conceal_auts = True # Conceal the value of AUTS |
||||
no_amf_clear = False # Do not clear AMF when computing MAC-S |
||||
|
||||
# Data: |
||||
max_delta = 2**28 << ind_size_bits |
||||
age_limit = 2**28 << ind_size_bits |
||||
freshness_data = [0x00] * (6*2**ind_size_bits) # initalize to zero |
||||
|
||||
def __init__(self, content = None): |
||||
if content == None: |
||||
return |
||||
|
||||
# Check if we have at least the header |
||||
if len(content) <= 2: |
||||
raise ValueError("unexpected length of %u bytes", len(content)) |
||||
|
||||
flag1 = content[0] |
||||
self.ind_size_bits = flag1 & 0xf |
||||
|
||||
# The parameter ind_size_bits is not user configurable, |
||||
# its a fixed configuration that is specific to the |
||||
# card profile and it can be determined by looking at the |
||||
# file length (length of the freshness data). If we find |
||||
# an ind_size_bits that is intconstant to the file length, |
||||
# we automatically set the value to the correct length |
||||
ind_size_bits_calculated = int(math.log((len(content) - 14) / 6, 2)) |
||||
if ind_size_bits_calculated != self.ind_size_bits: |
||||
print " Warning: SQN Parameter ind_size_bits is set to " + str(self.ind_size_bits) + ", resetting it to " + str(ind_size_bits_calculated) + "!" |
||||
self.ind_size_bits = ind_size_bits_calculated |
||||
|
||||
self.reset() #ensure freshness data is correctly reset |
||||
self.sqn_check_enabled = bool((flag1 >> 4) & 1) |
||||
self.sqn_age_limit_enabled = bool((flag1 >> 5) & 1) |
||||
self.sqn_max_delta_enabled = bool((flag1 >> 6) & 1) |
||||
self.sqn_check_skip_first = bool((flag1 >> 7) & 1) |
||||
|
||||
flag2 = content[1] |
||||
self.conceal_autn = bool(flag2 & 1) |
||||
self.conceal_auts = bool((flag2 >> 1) & 1) |
||||
self.no_amf_clear = bool((flag2 >> 2) & 1) |
||||
|
||||
# Check if the data body is complete |
||||
if len(content) < 14+(6*2**self.ind_size_bits): |
||||
raise ValueError("unexpected length of %u bytes" % len(content)) |
||||
|
||||
self.max_delta = list_to_int(content[2:8]) |
||||
self.age_limit = list_to_int(content[8:14]) |
||||
self.freshness_data = content[15:(6*2**self.ind_size_bits)] |
||||
|
||||
|
||||
def __str__(self): |
||||
pfx = " " |
||||
dump = "" |
||||
|
||||
dump += "%sIND (bits): %u\n" % (pfx, self.ind_size_bits) |
||||
if self.sqn_check_enabled: |
||||
dump += "%sSQN Check enabled\n" % pfx |
||||
else: |
||||
dump += "%sSQN Check disabled\n" % pfx |
||||
if self.sqn_age_limit_enabled: |
||||
dump += "%sSQN Age Limit enabled\n" % pfx |
||||
else: |
||||
dump += "%sSQN Age Limit disabled\n" % pfx |
||||
if self.sqn_max_delta_enabled: |
||||
dump += "%sSQN Max Delta enabled\n" % pfx |
||||
else: |
||||
dump += "%sSQN Max Delta disabled\n" % pfx |
||||
if self.sqn_check_skip_first: |
||||
dump += "%sSQN Skip first enabled\n" % pfx |
||||
else: |
||||
dump += "%sSQN Skip first disabled\n" % pfx |
||||
if self.conceal_autn: |
||||
dump += "%sSQN Conceal AUTN enabled\n" % pfx |
||||
else: |
||||
dump += "%sSQN Conceal AUTN disabled\n" % pfx |
||||
if self.conceal_auts: |
||||
dump += "%sSQN Conceal AUTS enabled\n" % pfx |
||||
else: |
||||
dump += "%sSQN Conceal AUTS disabled\n" % pfx |
||||
if self.no_amf_clear: |
||||
dump += "%sSQN No AMF clear enabled\n" % pfx |
||||
else: |
||||
dump += "%sSQN No AMF clear disabled\n" % pfx |
||||
dump += "%sMax Delta: %u\n" % (pfx, self.max_delta) |
||||
dump += "%sAge Limit: %u\n" % (pfx, self.age_limit) |
||||
dump += pfx + "Freshness Data:\n" + hexdump(self.freshness_data, True) |
||||
|
||||
return dump |
||||
|
||||
|
||||
def encode(self): |
||||
out = [0x00, 0x00] |
||||
|
||||
# Flag1: |
||||
out[0] = self.ind_size_bits & 0x0f |
||||
if self.sqn_check_enabled: |
||||
out[0] |= 1 << 4 |
||||
if self.sqn_age_limit_enabled: |
||||
out[0] |= 1 << 5 |
||||
if self.sqn_max_delta_enabled: |
||||
out[0] |= 1 << 6 |
||||
if self.sqn_check_skip_first: |
||||
out[0] |= 1 << 7 |
||||
|
||||
# Flag2: |
||||
if self.conceal_autn: |
||||
out[1] |= 1 << 0 |
||||
if self.conceal_auts: |
||||
out[1] |= 1 << 1 |
||||
if self.no_amf_clear: |
||||
out[1] |= 1 << 2 |
||||
|
||||
# Data: |
||||
out += int_to_list(self.max_delta, 6) |
||||
out += int_to_list(self.age_limit, 6) |
||||
out += self.freshness_data |
||||
return out |
||||
|
||||
|
||||
def reset(self): |
||||
self.freshness_data = [0x00] * (6*2**self.ind_size_bits) |
||||
|
||||
|
||||
|
||||
class Sysmo_isim_sja2(Sysmo_usim): |
||||
|
||||
def __init__(self): |
||||
card_detected = False |
||||
|
||||
# Try card model #1 |
||||
try: |
||||
atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 30 34 05 4B A9" |
||||
print "Trying to find card with ATR: " + atr |
||||
Sysmo_usim.__init__(self, atr) |
||||
card_detected = True |
||||
except: |
||||
print " * Card not detected!" |
||||
|
||||
if card_detected == True: |
||||
return |
||||
|
||||
|
||||
# Try card model #2 |
||||
try: |
||||
atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 31 33 02 51 B2" |
||||
print "Trying to find card with ATR: " + atr |
||||
Sysmo_usim.__init__(self, atr) |
||||
card_detected = True |
||||
except: |
||||
print " * Card not detected!" |
||||
|
||||
if card_detected == True: |
||||
return |
||||
|
||||
|
||||
# Exit when we are not able to detect the card |
||||
if card_detected != True: |
||||
sys.exit(1) |
||||
|
||||
|
||||
# Show current milenage parameters |
||||
def show_milenage_params(self): |
||||
print("Reading Milenage parameters...") |
||||
self._init() |
||||
|
||||
print(" * Reading...") |
||||
self.sim.card.SELECT_ADF_USIM() |
||||
self.sim.select(SYSMO_ISIMSJA2_EF_MILENAGE_CFG) |
||||
res = self._read_binary(85) |
||||
ef = SYSMO_ISIMSJA2_FILE_EF_MILENAGE_CFG(res.apdu) |
||||
|
||||
print(" * Current Milenage Parameters:") |
||||
print(str(ef)) |
||||
print("") |
||||
|
||||
|
||||
# Write new milenage parameters |
||||
def write_milenage_params(self, params): |
||||
|
||||
print("Programming Milenage parameters...") |
||||