2022-03-30 03:27:58 +00:00
|
|
|
#!/usr/bin/env python3
|
2019-11-07 13:51:41 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
"""
|
2023-06-22 13:56:17 +00:00
|
|
|
Gadgets to modify sysmoISIM-SJA2/sysmoISIM-SJA5 parameters
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-22 13:56:17 +00:00
|
|
|
(C) 2017-2023 by sysmocom - s.f.m.c. GmbH
|
2019-11-07 13:51:41 +00:00
|
|
|
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]
|
|
|
|
# | |
|
2023-06-22 13:57:24 +00:00
|
|
|
# | +--[EF_USIM_AUTH_KEY 0xAF20] (regular file)
|
2019-11-07 13:51:41 +00:00
|
|
|
# | |
|
|
|
|
# | +--[EF_USIM_AUTH_KEY_2G 0xAF22] (link to DF_SYSTEM/EF_SIM_AUTH_KEY)
|
|
|
|
# |
|
|
|
|
# +--[ADF_ISIM]
|
|
|
|
# |
|
2023-06-22 13:57:24 +00:00
|
|
|
# +--[EF_ISIM_AUTH_KEY 0xAF20] (regular file)
|
2019-11-07 13:51:41 +00:00
|
|
|
# |
|
2023-06-22 13:57:24 +00:00
|
|
|
# +--[EF_ISIM_AUTH_KEY_2G 0xAF22] (link to DF_SYSTEM/EF_SIM_AUTH_KEY)
|
2019-11-07 13:51:41 +00:00
|
|
|
#
|
|
|
|
# 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
|
2023-05-26 14:09:25 +00:00
|
|
|
SYSMO_ISIMSJA5_ALGO_TUAK = 0x06
|
|
|
|
SYSMO_ISIMSJA5_ALGO_XOR_2G = 0x0E
|
2019-11-07 13:51:41 +00:00
|
|
|
SYSMO_ISIMSJA2_ALGO_XOR = 0x0F
|
|
|
|
|
2023-06-22 14:55:19 +00:00
|
|
|
# Algorithms that are supported by sysmo-isim-sja2 (and also sysmo-isim-sja5)
|
2023-05-26 14:09:25 +00:00
|
|
|
sysmo_isimsja2_algorithms = [
|
2019-11-07 13:51:41 +00:00
|
|
|
(SYSMO_ISIMSJA2_ALGO_COMP12V1, 'COMP128v1'),
|
|
|
|
(SYSMO_ISIMSJA2_ALGO_COMP12V2, 'COMP128v2'),
|
2023-05-26 14:08:26 +00:00
|
|
|
(SYSMO_ISIMSJA2_ALGO_COMP12V3, 'COMP128v3'),
|
2019-11-07 13:51:41 +00:00
|
|
|
(SYSMO_ISIMSJA2_ALGO_MILENAGE, 'MILENAGE'),
|
|
|
|
(SYSMO_ISIMSJA2_ALGO_SHA1AKA , 'SHA1-AKA'),
|
|
|
|
(SYSMO_ISIMSJA2_ALGO_XOR, 'XOR'),
|
2023-05-26 14:09:25 +00:00
|
|
|
]
|
|
|
|
|
2023-06-22 14:55:19 +00:00
|
|
|
# Algorithms that are supported by sysmo-isim-sja5. This also includes all
|
|
|
|
# algorithms supported by sysmo-isim-sja2y
|
2023-05-26 14:09:25 +00:00
|
|
|
sysmo_isimsja5_algorithms = sysmo_isimsja2_algorithms + [
|
|
|
|
(SYSMO_ISIMSJA5_ALGO_XOR_2G, 'XOR-2G'),
|
|
|
|
(SYSMO_ISIMSJA5_ALGO_TUAK, 'TUAK'),
|
|
|
|
]
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
# Algorithms that use a 16 byte Key in the familiar format of sysmo-isim-sja2
|
|
|
|
sysmo_isimsjax_16_byte_key_algorithms = [
|
|
|
|
SYSMO_ISIMSJA2_ALGO_COMP12V1,
|
|
|
|
SYSMO_ISIMSJA2_ALGO_COMP12V2,
|
|
|
|
SYSMO_ISIMSJA2_ALGO_COMP12V3,
|
|
|
|
SYSMO_ISIMSJA2_ALGO_MILENAGE,
|
|
|
|
SYSMO_ISIMSJA2_ALGO_SHA1AKA,
|
|
|
|
SYSMO_ISIMSJA2_ALGO_XOR,
|
|
|
|
SYSMO_ISIMSJA5_ALGO_XOR_2G,
|
|
|
|
]
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 11:39:50 +00:00
|
|
|
# TUAK configuration byte
|
|
|
|
SYSMO_ISIMSJA5_TUAK_RES_SIZE_32_BIT = 0
|
|
|
|
SYSMO_ISIMSJA5_TUAK_RES_SIZE_64_BIT = 1
|
|
|
|
SYSMO_ISIMSJA5_TUAK_RES_SIZE_128_BIT = 2
|
|
|
|
SYSMO_ISIMSJA5_TUAK_RES_SIZE_256_BIT = 3
|
|
|
|
SYSMO_ISIMSJA5_TUAK_MAC_SIZE_64_BIT = 0
|
|
|
|
SYSMO_ISIMSJA5_TUAK_MAC_SIZE_128_BIT = 1
|
|
|
|
SYSMO_ISIMSJA5_TUAK_MAC_SIZE_256_BIT = 2
|
|
|
|
SYSMO_ISIMSJA5_TUAK_CKIK_SIZE_128_BIT = 0
|
|
|
|
SYSMO_ISIMSJA5_TUAK_CKIK_SIZE_256_BIT = 1
|
|
|
|
sysmo_isimsja5_res_sizes = [
|
|
|
|
(SYSMO_ISIMSJA5_TUAK_RES_SIZE_32_BIT, "32"),
|
|
|
|
(SYSMO_ISIMSJA5_TUAK_RES_SIZE_64_BIT, "64"),
|
|
|
|
(SYSMO_ISIMSJA5_TUAK_RES_SIZE_128_BIT, "128"),
|
|
|
|
(SYSMO_ISIMSJA5_TUAK_RES_SIZE_256_BIT, "256")
|
|
|
|
]
|
|
|
|
sysmo_isimsja5_mac_sizes = [
|
|
|
|
(SYSMO_ISIMSJA5_TUAK_MAC_SIZE_64_BIT, "64"),
|
|
|
|
(SYSMO_ISIMSJA5_TUAK_MAC_SIZE_128_BIT, "128"),
|
|
|
|
(SYSMO_ISIMSJA5_TUAK_MAC_SIZE_256_BIT, "256")
|
|
|
|
]
|
|
|
|
sysmo_isimsja5_ckik_sizes = [
|
|
|
|
(SYSMO_ISIMSJA5_TUAK_CKIK_SIZE_128_BIT, "128"),
|
|
|
|
(SYSMO_ISIMSJA5_TUAK_CKIK_SIZE_256_BIT, "256")
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
sysmo_isimsjax_op_opc = [
|
|
|
|
(True, 'OPc'),
|
|
|
|
(False, 'OP'),
|
|
|
|
]
|
2023-06-23 11:39:50 +00:00
|
|
|
sysmo_isimsja5_top_topc = [
|
|
|
|
(True, 'TOPc'),
|
|
|
|
(False, 'TOP'),
|
|
|
|
]
|
2023-06-23 10:17:14 +00:00
|
|
|
|
|
|
|
class SYSMO_ISIMSJAX_ALGO_PARS_MILENAGE:
|
2019-11-07 13:51:41 +00:00
|
|
|
use_opc = False
|
|
|
|
sres_dev_func = 1
|
2023-06-23 10:17:14 +00:00
|
|
|
four_byte_res = 0 #sysmo-usim-sja5 only
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
def __init__(self, content = None):
|
|
|
|
if content == None:
|
|
|
|
return
|
|
|
|
header = content[0]
|
|
|
|
self.use_opc = bool((header >> 4) & 1)
|
|
|
|
if (header >> 5) & 1:
|
|
|
|
self.sres_dev_func = 2
|
2023-06-23 10:17:14 +00:00
|
|
|
self.four_byte_res = bool((header >> 6) & 1)
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-22 14:49:25 +00:00
|
|
|
def __str__(self) -> str:
|
2019-11-07 13:51:41 +00:00
|
|
|
dump = ""
|
|
|
|
pfx = " "
|
|
|
|
if self.use_opc == True:
|
2023-06-23 10:17:14 +00:00
|
|
|
dump += pfx + "use OPc\n"
|
2019-11-07 13:51:41 +00:00
|
|
|
else:
|
2023-06-23 10:17:14 +00:00
|
|
|
dump += pfx + "use OP\n"
|
|
|
|
dump += pfx + "use SRES deviation function " + str(self.sres_dev_func) + "\n"
|
|
|
|
if self.four_byte_res:
|
|
|
|
dump += pfx + "Return 4 byte RES\n"
|
|
|
|
else:
|
|
|
|
dump += pfx + "Return full 8 byte RES\n"
|
2019-11-07 13:51:41 +00:00
|
|
|
return dump
|
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
def encode(self) -> int:
|
|
|
|
out = 0x00
|
2019-11-07 13:51:41 +00:00
|
|
|
if self.use_opc == True:
|
2023-06-23 10:17:14 +00:00
|
|
|
out |= 1 << 4
|
|
|
|
out |= ((self.sres_dev_func-1) & 1) << 5
|
|
|
|
out |= ((self.four_byte_res) & 1) << 6
|
2019-11-07 13:51:41 +00:00
|
|
|
return out
|
|
|
|
|
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
class SYSMO_ISIMSJAX_ALGO_PARS_SHA1AKA:
|
|
|
|
four_byte_res = 0 #sysmo-usim-sja5 only
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
def __init__(self, content = None):
|
|
|
|
if content == None:
|
|
|
|
return
|
2023-06-23 10:17:14 +00:00
|
|
|
header = content[0]
|
|
|
|
self.four_byte_res = bool((header >> 6) & 1)
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-22 14:49:25 +00:00
|
|
|
def __str__(self) -> str:
|
2019-11-07 13:51:41 +00:00
|
|
|
dump = ""
|
|
|
|
pfx = " "
|
2023-06-23 10:17:14 +00:00
|
|
|
if self.four_byte_res:
|
|
|
|
dump += pfx + "Return 4 byte RES\n"
|
|
|
|
else:
|
|
|
|
dump += pfx + "Return full 8 byte RES (default)\n"
|
|
|
|
return dump
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
def encode(self) -> int:
|
|
|
|
out = 0x00
|
|
|
|
out |= ((self.four_byte_res) & 1) << 6
|
|
|
|
return out
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
|
|
|
|
class SYSMO_ISIMSJAX_ALGO_PARS_XOR:
|
|
|
|
sres_dev_func = 1
|
|
|
|
four_byte_res = 0
|
|
|
|
sixteen_byte_res = 0 #Return 16 byte RES (ignores full_res)
|
|
|
|
|
|
|
|
def __init__(self, content = None):
|
|
|
|
if content == None:
|
|
|
|
return
|
|
|
|
header = content[0]
|
|
|
|
if (header >> 5) & 1:
|
|
|
|
self.sres_dev_func = 2
|
|
|
|
self.four_byte_res = bool((header >> 6) & 1)
|
|
|
|
self.sixteen_byte_res = bool((header >> 7) & 1)
|
|
|
|
|
|
|
|
def __str__(self) -> str:
|
|
|
|
dump = ""
|
|
|
|
pfx = " "
|
|
|
|
dump += pfx + "use SRES deviation function" + str(self.sres_dev_func) + "\n"
|
|
|
|
if self.sixteen_byte_res:
|
|
|
|
dump += pfx + "Return extended 16 byte RES\n"
|
|
|
|
elif self.four_byte_res:
|
|
|
|
dump += pfx + "Return 4 byte RES\n"
|
2019-11-07 13:51:41 +00:00
|
|
|
else:
|
2023-06-23 10:17:14 +00:00
|
|
|
dump += pfx + "Return full 8 byte RES (default)\n"
|
|
|
|
return dump
|
|
|
|
|
|
|
|
def encode(self) -> int:
|
|
|
|
out = 0x00
|
|
|
|
out |= ((self.sres_dev_func-1) & 1) << 5
|
|
|
|
out |= ((self.four_byte_res) & 1) << 6
|
|
|
|
out |= ((self.sixteen_byte_res) & 1) << 7
|
|
|
|
return out
|
|
|
|
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
class SYSMO_ISIMSJA5_ALGO_PARS_TUAK:
|
|
|
|
use_topc = False
|
|
|
|
sres_dev_func = 1
|
|
|
|
use_256_bit_key = False
|
|
|
|
|
|
|
|
def __init__(self, content = None):
|
|
|
|
if content == None:
|
|
|
|
return
|
|
|
|
header = content[0]
|
|
|
|
self.use_topc = bool((header >> 4) & 1)
|
|
|
|
if (header >> 5) & 1:
|
|
|
|
self.sres_dev_func = 2
|
|
|
|
self.use_256_bit_key = bool((header >> 6) & 1)
|
|
|
|
|
|
|
|
def __str__(self) -> str:
|
|
|
|
dump = ""
|
|
|
|
pfx = " "
|
|
|
|
if self.use_topc == True:
|
|
|
|
dump += pfx + "use TOPc\n"
|
|
|
|
else:
|
|
|
|
dump += pfx + "use TOP\n"
|
|
|
|
dump += pfx + "use SRES deviation function " + str(self.sres_dev_func) + "\n"
|
|
|
|
if self.use_256_bit_key:
|
|
|
|
dump += pfx + "256 bit key length\n"
|
|
|
|
else:
|
|
|
|
dump += pfx + "128 bit key length\n"
|
2019-11-07 13:51:41 +00:00
|
|
|
return dump
|
|
|
|
|
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
def encode(self) -> int:
|
|
|
|
out = 0x00
|
|
|
|
if self.use_topc == True:
|
|
|
|
out |= 1 << 4
|
|
|
|
out |= ((self.sres_dev_func-1) & 1) << 5
|
|
|
|
out |= ((self.use_256_bit_key) & 1) << 6
|
2019-11-07 13:51:41 +00:00
|
|
|
return out
|
|
|
|
|
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
class SYSMO_ISIMSJAX_FILE_EF_XSIM_AUTH_KEY:
|
|
|
|
"""
|
|
|
|
Superclass model that generates and parses 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.
|
|
|
|
"""
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
algo = SYSMO_ISIMSJA2_ALGO_COMP12V1
|
|
|
|
algo_pars = None
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
def __init__(self, content = None):
|
|
|
|
if content == None:
|
|
|
|
return
|
|
|
|
header = content[0]
|
|
|
|
self.algo = header & 0x0F
|
|
|
|
if self.algo == SYSMO_ISIMSJA2_ALGO_MILENAGE:
|
|
|
|
self.algo_pars = SYSMO_ISIMSJAX_ALGO_PARS_MILENAGE(content)
|
|
|
|
elif self.algo == SYSMO_ISIMSJA2_ALGO_SHA1AKA:
|
|
|
|
self.algo_pars = SYSMO_ISIMSJAX_ALGO_PARS_SHA1AKA(content)
|
|
|
|
elif self.algo == SYSMO_ISIMSJA2_ALGO_XOR:
|
|
|
|
self.algo_pars = SYSMO_ISIMSJAX_ALGO_PARS_XOR(content)
|
2023-06-23 11:39:50 +00:00
|
|
|
elif self.algo == SYSMO_ISIMSJA5_ALGO_TUAK:
|
|
|
|
self.algo_pars = SYSMO_ISIMSJA5_ALGO_PARS_TUAK(content)
|
2023-06-23 10:17:14 +00:00
|
|
|
|
|
|
|
def __str__(self) -> str:
|
|
|
|
dump = ""
|
|
|
|
pfx = " "
|
|
|
|
dump += pfx + "Algorithm: "
|
|
|
|
dump += id_to_str(sysmo_isimsja5_algorithms, self.algo)
|
|
|
|
dump += "\n"
|
|
|
|
if self.algo_pars:
|
|
|
|
dump += str(self.algo_pars)
|
|
|
|
return dump
|
|
|
|
|
|
|
|
def encode(self):
|
|
|
|
out = [0x00]
|
|
|
|
out[0] = self.algo & 0x0F
|
|
|
|
if self.algo_pars:
|
|
|
|
out[0] |= self.algo_pars.encode()
|
|
|
|
return out
|
|
|
|
|
|
|
|
class SYSMO_ISIMSJAX_ALGO_KEY_COMP128:
|
|
|
|
|
|
|
|
ki = [0x00] * 16
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
def __init__(self, content = None):
|
|
|
|
if content == None:
|
|
|
|
return
|
2023-06-23 10:17:14 +00:00
|
|
|
self.ki = content[1:17]
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
def __str__(self) -> str:
|
|
|
|
dump = ""
|
|
|
|
pfx = " "
|
|
|
|
dump += pfx + "Ki: " + hexdump(self.ki)
|
|
|
|
return dump
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
def encode(self) -> list:
|
|
|
|
return self.ki
|
|
|
|
|
|
|
|
|
|
|
|
#XOR has the same key length COMP128 (16 byte, no extra data)
|
|
|
|
class SYSMO_ISIMSJAX_ALGO_KEY_XOR(SYSMO_ISIMSJAX_ALGO_KEY_COMP128):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
#SHA1AKA has the same key length COMP128 (16 byte, no extra data)
|
|
|
|
class SYSMO_ISIMSJAX_ALGO_KEY_SHA1AKA(SYSMO_ISIMSJAX_ALGO_KEY_COMP128):
|
|
|
|
pass
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
|
|
|
|
#Milenage adds a 16 byte OP/c
|
|
|
|
class SYSMO_ISIMSJAX_ALGO_KEY_MILENAGE(SYSMO_ISIMSJAX_ALGO_KEY_COMP128):
|
|
|
|
|
|
|
|
opc = [0x00] * 16
|
|
|
|
|
|
|
|
def __init__(self, content = None):
|
|
|
|
if content == None:
|
|
|
|
return
|
|
|
|
super().__init__(content)
|
|
|
|
self.opc = content[17:33]
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-22 14:49:25 +00:00
|
|
|
def __str__(self) -> str:
|
2019-11-07 13:51:41 +00:00
|
|
|
dump = ""
|
|
|
|
pfx = " "
|
2023-06-23 10:17:14 +00:00
|
|
|
dump += super().__str__()
|
|
|
|
dump += "\n"
|
|
|
|
dump += pfx + "OPc: " + hexdump(self.opc)
|
|
|
|
return dump
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
def encode(self) -> list:
|
|
|
|
return super().encode() + self.opc
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
|
2023-06-23 11:39:50 +00:00
|
|
|
class SYSMO_ISIMSJAX_ALGO_KEY_TUAK:
|
|
|
|
|
|
|
|
res_size = 0 #3 bit value
|
|
|
|
mac_size = 0 #3 bit value
|
|
|
|
ckik_size = 0 #1 bit value
|
|
|
|
num_keccak = 0 #1 byte value
|
|
|
|
topc = [0x00] * 32
|
|
|
|
key = [0x00] * 32
|
|
|
|
|
|
|
|
def __init__(self, content = None):
|
|
|
|
if content == None:
|
|
|
|
return
|
|
|
|
self.res_size = int(content[1] & 7)
|
|
|
|
self.mac_size = int((content[1] >> 3) & 7)
|
|
|
|
self.ckik_size = bool((content[1] >> 6) & 1)
|
|
|
|
self.num_keccak = content[2]
|
|
|
|
self.topc = content[3:35]
|
|
|
|
self.key = content[35:67]
|
|
|
|
|
|
|
|
def __str__(self) -> str:
|
|
|
|
dump = ""
|
|
|
|
pfx = " "
|
|
|
|
dump += pfx + "RES size: %s bit" % id_to_str(sysmo_isimsja5_res_sizes, self.res_size) + "\n"
|
|
|
|
dump += pfx + "MAC-A/MAC-S size: %s bit" % id_to_str(sysmo_isimsja5_mac_sizes, self.mac_size) + "\n"
|
|
|
|
dump += pfx + "Keccak iterations: %d" % self.num_keccak + "\n"
|
|
|
|
dump += pfx + "TOPc: " + hexdump(self.topc) + "\n"
|
|
|
|
#TODO: Keys may be 128 or 256 bits long. The key length is defined
|
|
|
|
#in the header of the file, which means we cannot access this bit
|
|
|
|
#from here but it would be nice to display the key in its correct
|
|
|
|
#length though.
|
|
|
|
dump += pfx + "Key: " + hexdump(self.key)
|
|
|
|
return dump
|
|
|
|
|
|
|
|
def encode(self) -> list:
|
|
|
|
param_byte = self.res_size & 7
|
|
|
|
param_byte |= (self.res_size & 7) << 3
|
|
|
|
param_byte |= (self.ckik_size & 1) << 6
|
|
|
|
out = [param_byte]
|
|
|
|
out += [self.num_keccak]
|
|
|
|
out += self.topc
|
|
|
|
out += self.key
|
|
|
|
return out
|
|
|
|
|
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
class SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(SYSMO_ISIMSJAX_FILE_EF_XSIM_AUTH_KEY):
|
|
|
|
|
|
|
|
algo_key = None
|
|
|
|
|
|
|
|
def __init__(self, content = None):
|
|
|
|
# The superclass constructor must ensure that a valid algo and
|
|
|
|
# algo parameters are set since we need this information to pick
|
|
|
|
# the key configuration below.
|
|
|
|
super().__init__(content)
|
|
|
|
if self.algo == SYSMO_ISIMSJA2_ALGO_COMP12V1 or \
|
|
|
|
self.algo == SYSMO_ISIMSJA2_ALGO_COMP12V2 or \
|
|
|
|
self.algo == SYSMO_ISIMSJA2_ALGO_COMP12V3:
|
|
|
|
self.algo_key = SYSMO_ISIMSJAX_ALGO_KEY_COMP128(content)
|
|
|
|
elif self.algo == SYSMO_ISIMSJA2_ALGO_MILENAGE:
|
|
|
|
self.algo_key = SYSMO_ISIMSJAX_ALGO_KEY_MILENAGE(content)
|
2019-11-07 13:51:41 +00:00
|
|
|
elif self.algo == SYSMO_ISIMSJA2_ALGO_SHA1AKA:
|
2023-06-23 10:17:14 +00:00
|
|
|
self.algo_key = SYSMO_ISIMSJAX_ALGO_KEY_SHA1AKA(content)
|
|
|
|
elif self.algo == SYSMO_ISIMSJA2_ALGO_XOR or \
|
|
|
|
self.algo == SYSMO_ISIMSJA5_ALGO_XOR_2G:
|
|
|
|
self.algo_key = SYSMO_ISIMSJAX_ALGO_KEY_XOR(content)
|
2023-06-23 11:39:50 +00:00
|
|
|
elif self.algo == SYSMO_ISIMSJA5_ALGO_TUAK:
|
|
|
|
self.algo_key = SYSMO_ISIMSJAX_ALGO_KEY_TUAK(content)
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
def __str__(self) -> str:
|
|
|
|
dump = ""
|
|
|
|
dump += super().__str__()
|
|
|
|
dump += str(self.algo_key)
|
|
|
|
return dump
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-22 14:49:25 +00:00
|
|
|
def encode(self) -> list:
|
2023-06-23 10:17:14 +00:00
|
|
|
out = super().encode()
|
|
|
|
if self.algo_key:
|
|
|
|
out += self.algo_key.encode()
|
|
|
|
else:
|
|
|
|
raise ValueError("key data encoding not supported for selected algorithm!")
|
2019-11-07 13:51:41 +00:00
|
|
|
return out
|
|
|
|
|
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
# EF_USIM_AUTH_KEY_2G, EF_SIM_AUTH_KEY and EF_USIM_AUTH_KEY_GBA have the same layout as
|
2019-11-07 13:51:41 +00:00
|
|
|
# EF_USIM_AUTH_KEY, so there is nothing to specialize other than the class name
|
2023-06-23 10:17:14 +00:00
|
|
|
class SYSMO_ISIMSJA2_FILE_EF_SIM_AUTH_KEY(SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2023-06-22 15:34:26 +00:00
|
|
|
class SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY_2G(SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY):
|
2019-11-07 13:51:41 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
2023-06-22 15:34:26 +00:00
|
|
|
class SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY_GBA(SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY):
|
2019-11-07 13:51:41 +00:00
|
|
|
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]
|
|
|
|
|
2023-06-22 14:49:25 +00:00
|
|
|
def __str__(self) -> str:
|
2019-11-07 13:51:41 +00:00
|
|
|
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
|
|
|
|
|
2023-06-22 14:49:25 +00:00
|
|
|
def encode(self) -> list:
|
2019-11-07 13:51:41 +00:00
|
|
|
out = [self.R1, self.R2, self.R3, self.R4, self.R5]
|
|
|
|
out += self.C1 + self.C2 + self.C3 + self.C4 + self.C5
|
|
|
|
return out
|
|
|
|
|
|
|
|
|
2023-06-22 15:34:26 +00:00
|
|
|
class SYSMO_ISIMSJAX_FILE_EF_USIM_SQN:
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
# Flag1:
|
|
|
|
ind_size_bits = 5 # speficy file length by 2^ind_len
|
|
|
|
sqn_check_enabled = True # perform SQN checks below
|
2020-03-17 13:24:02 +00:00
|
|
|
sqn_age_limit_enabled = False # perform age limit check: (SQNms-SQN) <= AGE_LIMIT)
|
2019-11-07 13:51:41 +00:00
|
|
|
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
|
2020-03-17 13:24:02 +00:00
|
|
|
no_amf_clear = False # Do not clear AMF when computing MAC-S
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
# Data:
|
2020-03-17 13:24:02 +00:00
|
|
|
max_delta = 2**28 << ind_size_bits
|
|
|
|
age_limit = 2**28 << ind_size_bits
|
2019-11-07 13:51:41 +00:00
|
|
|
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:
|
2020-08-31 12:22:27 +00:00
|
|
|
print(" Warning: SQN Parameter ind_size_bits is set to " + str(self.ind_size_bits) + ", resetting it to " + str(ind_size_bits_calculated) + "!")
|
2019-11-07 13:51:41 +00:00
|
|
|
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)]
|
|
|
|
|
2023-06-22 14:49:25 +00:00
|
|
|
def __str__(self) -> str:
|
2019-11-07 13:51:41 +00:00
|
|
|
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
|
|
|
|
|
2023-06-22 14:49:25 +00:00
|
|
|
def encode(self) -> list:
|
2019-11-07 13:51:41 +00:00
|
|
|
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):
|
2023-05-26 14:09:25 +00:00
|
|
|
algorithms = sysmo_isimsja2_algorithms
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
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"
|
2020-08-31 12:22:27 +00:00
|
|
|
print("Trying to find card with ATR: " + atr)
|
2019-11-07 13:51:41 +00:00
|
|
|
Sysmo_usim.__init__(self, atr)
|
|
|
|
card_detected = True
|
|
|
|
except:
|
2020-08-31 12:22:27 +00:00
|
|
|
print(" * Card not detected!")
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
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"
|
2020-08-31 12:22:27 +00:00
|
|
|
print("Trying to find card with ATR: " + atr)
|
2019-11-07 13:51:41 +00:00
|
|
|
Sysmo_usim.__init__(self, atr)
|
2020-02-09 15:52:43 +00:00
|
|
|
card_detected = True
|
|
|
|
except:
|
2020-08-31 12:22:27 +00:00
|
|
|
print(" * Card not detected!")
|
2020-02-09 15:52:43 +00:00
|
|
|
|
|
|
|
if card_detected == True:
|
|
|
|
return
|
|
|
|
|
|
|
|
# Try card model #3 (sysmoTSIM)
|
|
|
|
try:
|
|
|
|
atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 52 75 31 04 51 D5"
|
2020-08-31 12:22:27 +00:00
|
|
|
print("Trying to find card with ATR: " + atr)
|
2020-02-09 15:52:43 +00:00
|
|
|
Sysmo_usim.__init__(self, atr)
|
2019-11-07 13:51:41 +00:00
|
|
|
card_detected = True
|
|
|
|
except:
|
2020-08-31 12:22:27 +00:00
|
|
|
print(" * Card not detected!")
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
if card_detected == True:
|
|
|
|
return
|
|
|
|
|
|
|
|
# Exit when we are not able to detect the card
|
|
|
|
if card_detected != True:
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
def show_milenage_params(self):
|
2023-06-22 15:49:26 +00:00
|
|
|
"""
|
|
|
|
Show current milenage parameters
|
|
|
|
"""
|
2019-11-07 13:51:41 +00:00
|
|
|
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("")
|
|
|
|
|
|
|
|
def write_milenage_params(self, params):
|
2023-06-22 15:49:26 +00:00
|
|
|
"""
|
|
|
|
Write new milenage parameters
|
|
|
|
"""
|
2019-11-07 13:51:41 +00:00
|
|
|
print("Programming Milenage parameters...")
|
|
|
|
|
|
|
|
if (len(params) < 85):
|
|
|
|
print("Error: Short milenage parameters!")
|
|
|
|
return
|
|
|
|
params_swapped = params[80:85] + params[0:80]
|
|
|
|
|
|
|
|
self._init()
|
|
|
|
|
|
|
|
print(" * New Milenage Parameters for (EF.MILENAGE_CFG):")
|
|
|
|
ef_milenage_cfg = SYSMO_ISIMSJA2_FILE_EF_MILENAGE_CFG(params_swapped)
|
2020-08-31 12:22:27 +00:00
|
|
|
print(str(ef_milenage_cfg))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
print(" * Programming...")
|
|
|
|
# Note: The milenage configuration file in ADF_USIM and
|
|
|
|
# ADF_ISIM are linked, however we write to both locations,
|
|
|
|
# just to be sure.
|
|
|
|
self.sim.card.SELECT_ADF_USIM()
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_MILENAGE_CFG)
|
|
|
|
self.sim.update_binary(ef_milenage_cfg.encode())
|
2020-03-11 12:37:46 +00:00
|
|
|
if self.sim.has_isim:
|
2023-05-26 14:10:26 +00:00
|
|
|
self.sim.card.SELECT_ADF_ISIM()
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_MILENAGE_CFG)
|
|
|
|
self.sim.update_binary(ef_milenage_cfg.encode())
|
2019-11-07 13:51:41 +00:00
|
|
|
print("")
|
|
|
|
|
|
|
|
# Select DF_SYSTEM/EF_SIM_AUTH_KEY
|
|
|
|
def __select_ef_sim_auth_key(self):
|
|
|
|
self.sim.select(GSM_SIM_MF)
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_DF_SYSTEM)
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_SIM_AUTH_KEY)
|
|
|
|
|
|
|
|
# Authentication keys exist in various different files, which are
|
|
|
|
# similar, thie method simplifies the selection of those files
|
|
|
|
def __select_xsim_auth_key(self, isim = False, _2G = False):
|
|
|
|
self.sim.select(GSM_SIM_MF)
|
|
|
|
if isim:
|
|
|
|
self.sim.card.SELECT_ADF_ISIM()
|
|
|
|
else:
|
|
|
|
self.sim.card.SELECT_ADF_USIM()
|
|
|
|
|
|
|
|
if _2G:
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_USIM_AUTH_KEY_2G)
|
|
|
|
else:
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_USIM_AUTH_KEY)
|
|
|
|
|
|
|
|
# In the SJA2 model the key material and the algorithm configuration
|
|
|
|
# is distributed over multiple files, which may also have redundant
|
|
|
|
# contents. Files can also be hard linked to other files so that
|
|
|
|
# changes in one file may appear in another file as well. The dump
|
|
|
|
# method provides an overview of contents of all files at once in
|
|
|
|
# order to help debugging problems
|
|
|
|
def dump(self):
|
|
|
|
print("Reading propritary files...")
|
|
|
|
self._init()
|
|
|
|
|
|
|
|
# DF_SYSTEM/EF_SIM_AUTH_KEY:
|
|
|
|
self.__select_ef_sim_auth_key()
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2020-08-31 12:22:27 +00:00
|
|
|
print(" * DF_SYSTEM/EF_SIM_AUTH_KEY:")
|
|
|
|
print(SYSMO_ISIMSJA2_FILE_EF_SIM_AUTH_KEY(res.apdu))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
# ADF_USIM/EF_USIM_AUTH_KEY_2G:
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = True)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2020-08-31 12:22:27 +00:00
|
|
|
print(" * ADF_USIM/EF_USIM_AUTH_KEY_2G:")
|
2023-06-22 15:34:26 +00:00
|
|
|
print(SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY_2G(res.apdu))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2020-03-11 12:37:46 +00:00
|
|
|
if self.sim.has_isim:
|
2023-05-26 14:10:26 +00:00
|
|
|
# ADF_ISIM/EF_ISIM_AUTH_KEY_2G:
|
|
|
|
self.__select_xsim_auth_key(isim = True, _2G = True)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
print(" * ADF_ISIM/EF_ISIM_AUTH_KEY_2G:")
|
2023-06-22 15:34:26 +00:00
|
|
|
print(SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY_2G(res.apdu))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
# ADF_USIM/EF_USIM_AUTH_KEY:
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = False)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2020-08-31 12:22:27 +00:00
|
|
|
print(" * ADF_USIM/EF_USIM_AUTH_KEY:")
|
2023-06-22 15:34:26 +00:00
|
|
|
print(SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2020-03-11 12:37:46 +00:00
|
|
|
if self.sim.has_isim:
|
2023-05-26 14:10:26 +00:00
|
|
|
# ADF_ISIM/EF_ISIM_AUTH_KEY:
|
|
|
|
self.__select_xsim_auth_key(isim = True, _2G = False)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
print(" * ADF_ISIM/EF_ISIM_AUTH_KEY:")
|
2023-06-22 15:34:26 +00:00
|
|
|
print(SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
# ADF_USIM/EF_MILENAGE_CFG:
|
|
|
|
self.sim.select(GSM_SIM_MF)
|
|
|
|
self.sim.card.SELECT_ADF_USIM()
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_MILENAGE_CFG)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2020-08-31 12:22:27 +00:00
|
|
|
print(" * ADF_USIM/EF_MILENAGE_CFG:")
|
|
|
|
print(SYSMO_ISIMSJA2_FILE_EF_MILENAGE_CFG(res.apdu))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2020-03-11 12:37:46 +00:00
|
|
|
if self.sim.has_isim:
|
2023-05-26 14:10:26 +00:00
|
|
|
# ADF_ISIM/EF_MILENAGE_CFG:
|
|
|
|
self.sim.select(GSM_SIM_MF)
|
|
|
|
self.sim.card.SELECT_ADF_ISIM()
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_MILENAGE_CFG)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
print(" * ADF_ISIM/EF_MILENAGE_CFG:")
|
|
|
|
print(SYSMO_ISIMSJA2_FILE_EF_MILENAGE_CFG(res.apdu))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
# ADF_USIM/EF_USIM_SQN:
|
|
|
|
self.sim.select(GSM_SIM_MF)
|
|
|
|
self.sim.card.SELECT_ADF_USIM()
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_USIM_SQN)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2020-08-31 12:22:27 +00:00
|
|
|
print(" * ADF_USIM/EF_USIM_SQN:")
|
2023-06-22 15:34:26 +00:00
|
|
|
print(SYSMO_ISIMSJAX_FILE_EF_USIM_SQN(res.apdu))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2020-03-11 12:37:46 +00:00
|
|
|
if self.sim.has_isim:
|
2023-05-26 14:10:26 +00:00
|
|
|
# ADF_USIM/EF_ISIM_SQN:
|
|
|
|
self.sim.select(GSM_SIM_MF)
|
|
|
|
self.sim.card.SELECT_ADF_ISIM()
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_USIM_SQN)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
print(" * ADF_ISIM/EF_ISIM_SQN:")
|
2023-06-22 15:34:26 +00:00
|
|
|
print(SYSMO_ISIMSJAX_FILE_EF_USIM_SQN(res.apdu))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
def __display_key(self, ef, gen:str):
|
|
|
|
"""
|
|
|
|
Helper method to display key
|
|
|
|
"""
|
|
|
|
if ef.algo in sysmo_isimsjax_16_byte_key_algorithms:
|
|
|
|
print(" %s: Key: %s" % (gen, hexdump(ef.algo_key.ki)))
|
2023-06-23 11:39:50 +00:00
|
|
|
elif ef.algo is SYSMO_ISIMSJA5_ALGO_TUAK:
|
|
|
|
if not ef.algo_pars.use_256_bit_key:
|
|
|
|
print(" %s: Key: %s" % (gen, hexdump(ef.algo_key.key[0:16])))
|
|
|
|
else:
|
|
|
|
print(" %s: Key: %s" % (gen, hexdump(ef.algo_key.key)))
|
2023-06-23 10:17:14 +00:00
|
|
|
else:
|
|
|
|
print(" * %s: Key not applicable for selected algorithm." % gen)
|
|
|
|
|
2023-06-22 13:43:38 +00:00
|
|
|
def show_key_params(self):
|
|
|
|
"""
|
|
|
|
Show current Key value
|
|
|
|
"""
|
|
|
|
print("Reading Key value...")
|
2019-11-07 13:51:41 +00:00
|
|
|
self._init()
|
|
|
|
|
|
|
|
print(" * Reading...")
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = True)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2023-06-23 10:17:14 +00:00
|
|
|
ef_2g = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
|
|
|
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = False)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
ef_3g = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
|
|
|
|
|
|
|
if self.sim.has_isim:
|
|
|
|
self.__select_xsim_auth_key(isim = True, _2G = False)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
ef_4g5g = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
|
|
|
else:
|
|
|
|
ef_4g5g = None
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-22 13:43:38 +00:00
|
|
|
print(" * Current Key setting:")
|
2023-06-23 10:17:14 +00:00
|
|
|
self.__display_key(ef_2g, "2g")
|
|
|
|
self.__display_key(ef_3g, "3g")
|
|
|
|
if ef_4g5g:
|
|
|
|
self.__display_key(ef_4g5g, "4g5g")
|
|
|
|
|
2019-11-07 13:51:41 +00:00
|
|
|
print("")
|
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
def __program_key(self, key, gen:str):
|
|
|
|
"""
|
|
|
|
Helper method to program key, EF must be selected first
|
|
|
|
"""
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
ef = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
|
|
|
if ef.algo in sysmo_isimsjax_16_byte_key_algorithms:
|
|
|
|
ef.algo_key.ki = key
|
|
|
|
self.sim.update_binary(ef.encode())
|
|
|
|
print(" * %s: Key programmed." % gen)
|
2023-06-23 11:39:50 +00:00
|
|
|
elif ef.algo is SYSMO_ISIMSJA5_ALGO_TUAK:
|
|
|
|
ef.algo_key.key = key
|
|
|
|
ef.algo_pars.use_256_bit_key = False
|
|
|
|
if len(key) > 16:
|
|
|
|
ef.algo_pars.use_256_bit_key = True
|
|
|
|
self.sim.update_binary(ef.encode())
|
|
|
|
print(" * %s: Key programmed." % gen)
|
2023-06-23 10:17:14 +00:00
|
|
|
else:
|
|
|
|
print(" * %s: Key not applicable for selected algorithm." % gen)
|
|
|
|
|
2023-06-22 13:43:38 +00:00
|
|
|
def write_key_params(self, key):
|
|
|
|
"""
|
|
|
|
Program new Key value
|
|
|
|
"""
|
|
|
|
print("Writing Key value...")
|
2019-11-07 13:51:41 +00:00
|
|
|
self._init()
|
2023-06-22 13:43:38 +00:00
|
|
|
print(" * New Key setting:")
|
|
|
|
print(" Key: " + hexdump(key))
|
2019-11-07 13:51:41 +00:00
|
|
|
print(" * Programming...")
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = True)
|
2023-06-23 10:17:14 +00:00
|
|
|
self.__program_key(key, "2g")
|
2019-11-07 13:51:41 +00:00
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = False)
|
2023-06-23 10:17:14 +00:00
|
|
|
self.__program_key(key, "3g")
|
2020-03-11 12:37:46 +00:00
|
|
|
if self.sim.has_isim:
|
2023-05-26 14:10:26 +00:00
|
|
|
self.__select_xsim_auth_key(isim = True, _2G = False)
|
2023-06-23 10:17:14 +00:00
|
|
|
self.__program_key(key, "4g5g")
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
print("")
|
|
|
|
|
|
|
|
def show_auth_params(self):
|
2023-06-22 15:49:26 +00:00
|
|
|
"""
|
|
|
|
Show current authentication parameters
|
|
|
|
"""
|
2019-11-07 13:51:41 +00:00
|
|
|
print("Reading Authentication parameters...")
|
|
|
|
self._init()
|
|
|
|
|
|
|
|
print(" * Reading...")
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = True)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2023-06-22 15:34:26 +00:00
|
|
|
ef = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY_2G(res.apdu)
|
2019-11-07 13:51:41 +00:00
|
|
|
algo_2g = ef.algo
|
|
|
|
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = False)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2023-06-22 15:34:26 +00:00
|
|
|
ef = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
2019-11-07 13:51:41 +00:00
|
|
|
algo_3g = ef.algo
|
|
|
|
|
2023-06-23 09:11:26 +00:00
|
|
|
if self.sim.has_isim:
|
|
|
|
self.__select_xsim_auth_key(isim = True, _2G = False)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
ef = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
|
|
|
algo_4g5g = ef.algo
|
|
|
|
else:
|
|
|
|
algo_4g5g = algo_3g
|
|
|
|
|
2019-11-07 13:51:41 +00:00
|
|
|
print(" * Current algorithm setting:")
|
2023-06-23 09:11:26 +00:00
|
|
|
print(" 2g: %d=%s" % (algo_2g, id_to_str(self.algorithms, algo_2g)))
|
|
|
|
print(" 3g: %d=%s" % (algo_3g, id_to_str(self.algorithms, algo_3g)))
|
|
|
|
print(" 4g5g: %d=%s" % (algo_3g, id_to_str(self.algorithms, algo_4g5g)))
|
2019-11-07 13:51:41 +00:00
|
|
|
print("")
|
|
|
|
|
2023-06-23 09:11:26 +00:00
|
|
|
def write_auth_params(self, algo_2g_str, algo_3g_str, algo_4g5g_str = None):
|
2023-06-22 15:49:26 +00:00
|
|
|
"""
|
|
|
|
Write new authentication parameters
|
|
|
|
"""
|
2019-11-07 13:51:41 +00:00
|
|
|
print("Programming Authentication parameters...")
|
|
|
|
self._init()
|
|
|
|
|
|
|
|
if algo_2g_str.isdigit():
|
|
|
|
algo_2g = int(algo_2g_str)
|
|
|
|
else:
|
2023-05-26 14:09:25 +00:00
|
|
|
algo_2g = str_to_id(self.algorithms, algo_2g_str)
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
if algo_3g_str.isdigit():
|
|
|
|
algo_3g = int(algo_3g_str)
|
|
|
|
else:
|
2023-05-26 14:09:25 +00:00
|
|
|
algo_3g = str_to_id(self.algorithms, algo_3g_str)
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 09:11:26 +00:00
|
|
|
if algo_4g5g_str:
|
|
|
|
if algo_4g5g_str.isdigit():
|
|
|
|
algo_4g5g = int(algo_4g5g_str)
|
|
|
|
else:
|
|
|
|
algo_4g5g = str_to_id(self.algorithms, algo_4g5g_str)
|
|
|
|
else:
|
|
|
|
algo_4g5g = algo_3g
|
|
|
|
|
2019-11-07 13:51:41 +00:00
|
|
|
print(" * New algorithm setting:")
|
2023-06-23 09:11:26 +00:00
|
|
|
print(" 2g: %d=%s" % (algo_2g, id_to_str(self.algorithms, algo_2g)))
|
|
|
|
print(" 3g: %d=%s" % (algo_3g, id_to_str(self.algorithms, algo_3g)))
|
|
|
|
print(" 4g5g: %d=%s" % (algo_4g5g, id_to_str(self.algorithms, algo_4g5g)))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
print(" * Programming...")
|
|
|
|
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = True)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2023-06-22 15:34:26 +00:00
|
|
|
ef = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
2019-11-07 13:51:41 +00:00
|
|
|
ef.algo = algo_2g
|
|
|
|
self.sim.update_binary(ef.encode())
|
|
|
|
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = False)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2023-06-22 15:34:26 +00:00
|
|
|
ef = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
2019-11-07 13:51:41 +00:00
|
|
|
ef.algo = algo_3g
|
|
|
|
self.sim.update_binary(ef.encode())
|
|
|
|
|
2020-03-11 12:37:46 +00:00
|
|
|
if self.sim.has_isim:
|
2023-05-26 14:10:26 +00:00
|
|
|
self.__select_xsim_auth_key(isim = True, _2G = False)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2023-06-22 15:34:26 +00:00
|
|
|
ef = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
2023-06-23 09:11:26 +00:00
|
|
|
ef.algo = algo_4g5g
|
2023-05-26 14:10:26 +00:00
|
|
|
self.sim.update_binary(ef.encode())
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
print("")
|
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
def __display_opc(self, ef, gen:str):
|
|
|
|
"""
|
|
|
|
Helper method to display OP/OPc
|
|
|
|
"""
|
|
|
|
if ef.algo is SYSMO_ISIMSJA2_ALGO_MILENAGE:
|
|
|
|
print(" %s: %s: %s" % (gen, id_to_str(sysmo_isimsjax_op_opc, ef.algo_pars.use_opc), \
|
|
|
|
hexdump(ef.algo_key.opc)))
|
2023-06-23 11:39:50 +00:00
|
|
|
elif ef.algo is SYSMO_ISIMSJA5_ALGO_TUAK:
|
|
|
|
print(" %s: %s: %s" % (gen, id_to_str(sysmo_isimsja5_top_topc, ef.algo_pars.use_topc), \
|
|
|
|
hexdump(ef.algo_key.topc)))
|
2023-06-23 10:17:14 +00:00
|
|
|
else:
|
|
|
|
print(" * %s: OP/OPc not applicable for selected algorithm." % gen)
|
|
|
|
|
2019-11-07 13:51:41 +00:00
|
|
|
def show_opc_params(self):
|
2023-06-22 15:49:26 +00:00
|
|
|
"""
|
|
|
|
Show OP/OPc current configuration. (see also method: write_opc_params).
|
|
|
|
"""
|
2019-11-07 13:51:41 +00:00
|
|
|
print("Reading OP/c value...")
|
|
|
|
self._init()
|
|
|
|
|
|
|
|
print(" * Reading...")
|
2023-06-23 10:17:14 +00:00
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = True)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
ef_2g = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
|
|
|
|
2019-11-07 13:51:41 +00:00
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = False)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2023-06-23 10:17:14 +00:00
|
|
|
ef_3g = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
if self.sim.has_isim:
|
|
|
|
self.__select_xsim_auth_key(isim = True, _2G = False)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
ef_4g5g = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
2019-11-07 13:51:41 +00:00
|
|
|
else:
|
2023-06-23 10:17:14 +00:00
|
|
|
ef_4g5g = None
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
print(" * Current OP/OPc setting:")
|
2023-06-23 10:17:14 +00:00
|
|
|
self.__display_opc(ef_2g, "2g")
|
|
|
|
self.__display_opc(ef_3g, "3g")
|
|
|
|
if ef_4g5g:
|
2023-06-23 11:40:16 +00:00
|
|
|
self.__display_opc(ef_4g5g, "4g5g")
|
2023-06-23 10:17:14 +00:00
|
|
|
|
2019-11-07 13:51:41 +00:00
|
|
|
print("")
|
|
|
|
|
2023-06-23 10:17:14 +00:00
|
|
|
def __program_opc(self, select:bool, op, gen:str):
|
2023-06-22 15:49:26 +00:00
|
|
|
"""
|
2023-06-23 10:17:14 +00:00
|
|
|
Helper method to program OP/OPc, EF must be selected first
|
2023-06-22 15:49:26 +00:00
|
|
|
"""
|
2023-06-23 10:17:14 +00:00
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
ef = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
|
|
|
if ef.algo is SYSMO_ISIMSJA2_ALGO_MILENAGE:
|
|
|
|
ef.algo_key.opc = op
|
|
|
|
ef.algo_pars.use_opc = bool(select)
|
|
|
|
self.sim.update_binary(ef.encode())
|
|
|
|
print(" %s %s programmed." % (gen, id_to_str(sysmo_isimsjax_op_opc, bool(select))));
|
2023-06-23 11:39:50 +00:00
|
|
|
elif ef.algo is SYSMO_ISIMSJA5_ALGO_TUAK and len(op) is 32:
|
|
|
|
ef.algo_key.topc = op
|
|
|
|
ef.algo_pars.use_topc = bool(select)
|
|
|
|
self.sim.update_binary(ef.encode())
|
|
|
|
print(" %s %s programmed." % (gen, id_to_str(sysmo_isimsja5_top_topc, bool(select))));
|
2019-11-07 13:51:41 +00:00
|
|
|
else:
|
2023-06-23 10:17:14 +00:00
|
|
|
print(" %s OP/OPc not applicable for selected algorithm, skipping..." % gen)
|
|
|
|
|
|
|
|
def write_opc_params(self, select:bool, op):
|
|
|
|
"""
|
|
|
|
Program new OP/OPc value. The new OP/OPc value is programmed into all files where the algorithm is
|
|
|
|
configured to Milenage. When Milenage is not configured, then the respective file is not touched.
|
|
|
|
As a simplification we program the same OP/OPc configuration to all files (2G, 3G, 4G/5G). Even though
|
|
|
|
the cards would permit a different setting in each file, it is extremly unlikely that any HLR/HSS would
|
|
|
|
use such a configuration.
|
|
|
|
"""
|
|
|
|
print("Writing %s value..." % id_to_str(sysmo_isimsjax_op_opc, bool(select)))
|
2019-11-07 13:51:41 +00:00
|
|
|
self._init()
|
|
|
|
|
|
|
|
print(" * New OPc setting:")
|
2023-06-23 10:17:14 +00:00
|
|
|
print(" %s: %s" % (id_to_str(sysmo_isimsjax_op_opc, bool(select)), hexdump(op)))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
print(" * Programming...")
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = True)
|
2023-06-23 10:17:14 +00:00
|
|
|
self.__program_opc(select, op, "2g")
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = False)
|
|
|
|
self.__program_opc(select, op, "3g")
|
2020-03-11 12:37:46 +00:00
|
|
|
if self.sim.has_isim:
|
2023-05-26 14:10:26 +00:00
|
|
|
self.__select_xsim_auth_key(isim = True, _2G = False)
|
2023-06-23 10:17:14 +00:00
|
|
|
self.__program_opc(select, op, "4g5g")
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
print("")
|
|
|
|
|
|
|
|
def show_milenage_sqn_params(self):
|
2023-06-22 15:49:26 +00:00
|
|
|
"""
|
|
|
|
Show current milenage SQN parameters
|
|
|
|
"""
|
2019-11-07 13:51:41 +00:00
|
|
|
print("Reading Milenage Sequence parameters...")
|
|
|
|
self._init()
|
|
|
|
|
|
|
|
print(" * Current SQN Configuration for ADF_USIM:")
|
|
|
|
self.sim.select(GSM_SIM_MF)
|
|
|
|
self.sim.card.SELECT_ADF_USIM()
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_USIM_SQN)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2023-06-22 15:34:26 +00:00
|
|
|
print(SYSMO_ISIMSJAX_FILE_EF_USIM_SQN(res.apdu))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
2020-03-11 12:37:46 +00:00
|
|
|
if self.sim.has_isim:
|
2023-05-26 14:10:26 +00:00
|
|
|
print(" * Current SQN Configuration for ADF_ISIM:")
|
|
|
|
self.sim.select(GSM_SIM_MF)
|
|
|
|
self.sim.card.SELECT_ADF_ISIM()
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_USIM_SQN)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
2023-06-22 15:34:26 +00:00
|
|
|
print(SYSMO_ISIMSJAX_FILE_EF_USIM_SQN(res.apdu))
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
print("")
|
|
|
|
|
|
|
|
def reset_milenage_sqn_params(self):
|
2023-06-22 15:49:26 +00:00
|
|
|
"""
|
|
|
|
Reset milenage SQN configuration
|
|
|
|
"""
|
2019-11-07 13:51:41 +00:00
|
|
|
print(" * Resetting SQN Configuration to defaults...")
|
|
|
|
self._init()
|
|
|
|
|
|
|
|
print(" * Resetting...")
|
|
|
|
self.sim.select(GSM_SIM_MF)
|
|
|
|
|
|
|
|
self.sim.card.SELECT_ADF_USIM()
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_USIM_SQN)
|
2023-06-22 15:34:26 +00:00
|
|
|
ef = SYSMO_ISIMSJAX_FILE_EF_USIM_SQN()
|
2019-11-07 13:51:41 +00:00
|
|
|
self.sim.update_binary(ef.encode())
|
|
|
|
|
2020-03-11 12:37:46 +00:00
|
|
|
if self.sim.has_isim:
|
2023-05-26 14:10:26 +00:00
|
|
|
self.sim.card.SELECT_ADF_ISIM()
|
|
|
|
self.sim.select(SYSMO_ISIMSJA2_EF_USIM_SQN)
|
2023-06-22 15:34:26 +00:00
|
|
|
ef = SYSMO_ISIMSJAX_FILE_EF_USIM_SQN()
|
2023-05-26 14:10:26 +00:00
|
|
|
self.sim.update_binary(ef.encode())
|
2019-11-07 13:51:41 +00:00
|
|
|
|
|
|
|
print("")
|
2023-05-26 14:09:25 +00:00
|
|
|
|
2023-06-23 11:39:50 +00:00
|
|
|
def __display_tuak_cfg(self, ef, gen:str):
|
|
|
|
"""
|
|
|
|
Helper method to display key
|
|
|
|
"""
|
|
|
|
if ef.algo is SYSMO_ISIMSJA5_ALGO_TUAK:
|
|
|
|
print(" %s: TUAK configuration:" % gen)
|
|
|
|
print(" RES size: %s bit" % id_to_str(sysmo_isimsja5_res_sizes, ef.algo_key.res_size))
|
|
|
|
print(" MAC-A/MAC-S size: %s bit" % id_to_str(sysmo_isimsja5_mac_sizes, ef.algo_key.mac_size))
|
|
|
|
print(" CK/IK size: %s bit" % id_to_str(sysmo_isimsja5_ckik_sizes, ef.algo_key.ckik_size))
|
|
|
|
print(" Keccak iterations: %d" % ef.algo_key.num_keccak)
|
|
|
|
else:
|
|
|
|
print(" * %s: TUAK configuration not applicable for selected algorithm." % gen)
|
|
|
|
|
|
|
|
def show_tuak_cfg(self):
|
|
|
|
print("Reading TUAK configuration...")
|
|
|
|
self._init()
|
|
|
|
|
|
|
|
print(" * Reading...")
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = True)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
ef_2g = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
|
|
|
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = False)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
ef_3g = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
|
|
|
|
|
|
|
if self.sim.has_isim:
|
|
|
|
self.__select_xsim_auth_key(isim = True, _2G = False)
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
ef_4g5g = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY(res.apdu)
|
|
|
|
else:
|
|
|
|
ef_4g5g = None
|
|
|
|
|
|
|
|
print(" * Current TUAK configuration:")
|
|
|
|
self.__display_tuak_cfg(ef_2g, "2g")
|
|
|
|
self.__display_tuak_cfg(ef_3g, "3g")
|
|
|
|
if ef_4g5g:
|
|
|
|
self.__display_tuak_cfg(ef_4g5g, "4g5g")
|
|
|
|
print("")
|
|
|
|
|
|
|
|
def __program_tuak_cfg(self, res_size:int, mac_size:int, ckik_size:int, num_keccak:int, gen:str):
|
|
|
|
"""
|
|
|
|
Helper method to program key, EF must be selected first
|
|
|
|
"""
|
|
|
|
res = self._read_binary(self.sim.filelen)
|
|
|
|
ef = SYSMO_ISIMSJAX_FILE_EF_USIM_AUTH_KEY_2G(res.apdu)
|
|
|
|
if ef.algo is SYSMO_ISIMSJA5_ALGO_TUAK:
|
|
|
|
ef.algo_key.res_size = res_size
|
|
|
|
ef.algo_key.mac_size = mac_size
|
|
|
|
ef.algo_key.ckik_size = bool(ckik_size)
|
|
|
|
ef.algo_key.num_keccak = num_keccak
|
|
|
|
self.sim.update_binary(ef.encode())
|
|
|
|
print(" %s TUAK configuration programmed." % gen);
|
|
|
|
else:
|
|
|
|
print(" %s TUAK configuration not applicable for selected algorithm, skipping..." % gen)
|
|
|
|
|
|
|
|
def write_tuak_cfg(self, res_size_str:str, mac_size_str:str, ckik_size_str:str, num_keccak_str:str):
|
|
|
|
|
|
|
|
print("Writing TUAK configuration...")
|
|
|
|
self._init()
|
|
|
|
|
|
|
|
print(" * New TUAK configuration:")
|
|
|
|
|
|
|
|
res_size = str_to_id(sysmo_isimsja5_res_sizes, res_size_str, -1)
|
|
|
|
if res_size < 0:
|
|
|
|
print(" * Invalid TUAK configuration, RES-Size must be 32, 64, 128 or 256 bit!")
|
|
|
|
print("")
|
|
|
|
return
|
|
|
|
|
|
|
|
mac_size = str_to_id(sysmo_isimsja5_mac_sizes, mac_size_str, -1)
|
|
|
|
if mac_size < 0:
|
|
|
|
print(" * Invalid TUAK configuration, MAC-Size must be 64, 128 or 256 bit!")
|
|
|
|
print("")
|
|
|
|
return
|
|
|
|
|
|
|
|
ckik_size = str_to_id(sysmo_isimsja5_ckik_sizes, ckik_size_str, -1)
|
|
|
|
if ckik_size < 0:
|
|
|
|
print(" * Invalid TUAK configuration, MAC-Size must be 128 or 256 bit!")
|
|
|
|
print("")
|
|
|
|
return
|
|
|
|
|
|
|
|
num_keccak = int(num_keccak_str)
|
|
|
|
if num_keccak > 255:
|
|
|
|
print(" * Invalid TUAK configuration, number of Keccak iterations must not exceed 256!")
|
|
|
|
print("")
|
|
|
|
return
|
|
|
|
|
|
|
|
print(" RES size: %s bit" % id_to_str(sysmo_isimsja5_res_sizes, res_size))
|
|
|
|
print(" MAC-A/MAC-S size: %s bit" % id_to_str(sysmo_isimsja5_mac_sizes, mac_size))
|
|
|
|
print(" CK/IK size: %s bit" % id_to_str(sysmo_isimsja5_ckik_sizes, ckik_size))
|
|
|
|
print(" Keccak iterations: %d" % num_keccak)
|
|
|
|
|
|
|
|
print(" * Programming...")
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = True)
|
|
|
|
self.__program_tuak_cfg(res_size, mac_size, ckik_size, num_keccak, "2g")
|
|
|
|
self.__select_xsim_auth_key(isim = False, _2G = False)
|
|
|
|
self.__program_tuak_cfg(res_size, mac_size, ckik_size, num_keccak, "3g")
|
|
|
|
if self.sim.has_isim:
|
|
|
|
self.__select_xsim_auth_key(isim = True, _2G = False)
|
|
|
|
self.__program_tuak_cfg(res_size, mac_size, ckik_size, num_keccak, "4g5g")
|
|
|
|
|
|
|
|
print("")
|
|
|
|
|
2023-05-26 14:09:25 +00:00
|
|
|
class Sysmo_isim_sja5(Sysmo_isim_sja2):
|
|
|
|
algorithms = sysmo_isimsja5_algorithms
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
card_detected = False
|
|
|
|
|
|
|
|
# Try card model #1: sysmoISIM-SJA5 (9FV)
|
|
|
|
try:
|
|
|
|
atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 35 75 30 35 02 59 C4"
|
|
|
|
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: sysmoISIM-SJA5 (SLM17)
|
|
|
|
try:
|
|
|
|
atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 35 75 30 35 02 65 F8"
|
|
|
|
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 #3: sysmoISIM-SJA5 (3FJ)
|
|
|
|
try:
|
|
|
|
atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 35 75 30 35 02 51 CC"
|
|
|
|
print("Trying to find card with ATR: " + atr)
|
|
|
|
Sysmo_usim.__init__(self, atr)
|
|
|
|
card_detected = True
|
|
|
|
except:
|
|
|
|
print(" * Card not detected!")
|
|
|
|
|
|
|
|
# Exit when we are not able to detect the card
|
|
|
|
if card_detected != True:
|
|
|
|
sys.exit(1)
|