# coding=utf-8 """Utilities / Functions related to sysmocom SJA2/SJA5 cards (C) 2021-2023 by Harald Welte This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ from pytlv.TLV import * from struct import pack, unpack from pySim.utils import * from pySim.filesystem import * from pySim.runtime import RuntimeState from pySim.ts_102_221 import CardProfileUICC from pySim.construct import * from construct import * import pySim key_type2str = { 0: 'kic', 1: 'kid', 2: 'kik', 3: 'any', } key_algo2str = { 0: 'des', 1: 'aes' } mac_length = { 0: 8, 1: 4 } class EF_PIN(TransparentEF): def __init__(self, fid, name): super().__init__(fid, name=name, desc='%s PIN file' % name) def _decode_bin(self, raw_bin_data): u = unpack('!BBB8s', raw_bin_data[:11]) res = {'enabled': (True, False)[u[0] & 0x01], 'initialized': (True, False)[u[0] & 0x02], 'disable_able': (False, True)[u[0] & 0x10], 'unblock_able': (False, True)[u[0] & 0x20], 'change_able': (False, True)[u[0] & 0x40], 'valid': (False, True)[u[0] & 0x80], 'attempts_remaining': u[1], 'maximum_attempts': u[2], 'pin': u[3].hex(), } if len(raw_bin_data) == 21: u2 = unpack('!BB8s', raw_bin_data[11:10]) res['attempts_remaining_puk'] = u2[0] res['maximum_attempts_puk'] = u2[1] res['puk'] = u2[2].hex() return res class EF_MILENAGE_CFG(TransparentEF): _test_de_encode = [ ( '40002040600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000020000000000000000000000000000000400000000000000000000000000000008', {"r1": 64, "r2": 0, "r3": 32, "r4": 64, "r5": 96, "c1": "00000000000000000000000000000000", "c2": "00000000000000000000000000000001", "c3": "00000000000000000000000000000002", "c4": "00000000000000000000000000000004", "c5": "00000000000000000000000000000008"} ), ] def __init__(self, fid='6f21', name='EF.MILENAGE_CFG', desc='Milenage connfiguration'): super().__init__(fid, name=name, desc=desc) self._construct = Struct('r1'/Int8ub, 'r2'/Int8ub, 'r3'/Int8ub, 'r4'/Int8ub, 'r5'/Int8ub, 'c1'/HexAdapter(Bytes(16)), 'c2'/HexAdapter(Bytes(16)), 'c3'/HexAdapter(Bytes(16)), 'c4'/HexAdapter(Bytes(16)), 'c5'/HexAdapter(Bytes(16))) class EF_0348_KEY(LinFixedEF): def __init__(self, fid='6f22', name='EF.0348_KEY', desc='TS 03.48 OTA Keys'): super().__init__(fid, name=name, desc=desc, rec_len=(27, 35)) KeyLenAndType = BitStruct('mac_length'/Mapping(Bit, {8:0, 4:1}), 'algorithm'/Enum(Bit, des=0, aes=1), 'key_length'/MultiplyAdapter(BitsInteger(3), 8), '_rfu'/BitsRFU(1), 'key_type'/Enum(BitsInteger(2), kic=0, kid=1, kik=2, any=3)) self._construct = Struct('security_domain'/Int8ub, 'key_set_version'/Int8ub, 'key_len_and_type'/KeyLenAndType, 'key'/HexAdapter(Bytes(this.key_len_and_type.key_length))) class EF_0348_COUNT(LinFixedEF): _test_de_encode = [ ( 'fe010000000000', {"sec_domain": 254, "key_set_version": 1, "counter": "0000000000"} ), ] def __init__(self, fid='6f23', name='EF.0348_COUNT', desc='TS 03.48 OTA Counters'): super().__init__(fid, name=name, desc=desc, rec_len=(7, 7)) self._construct = Struct('sec_domain'/Int8ub, 'key_set_version'/Int8ub, 'counter'/HexAdapter(Bytes(5))) class EF_SIM_AUTH_COUNTER(TransparentEF): def __init__(self, fid='af24', name='EF.SIM_AUTH_COUNTER'): super().__init__(fid, name=name, desc='Number of remaining RUN GSM ALGORITHM executions') self._construct = Struct('num_run_gsm_algo_remain'/Int32ub) class EF_GP_COUNT(LinFixedEF): _test_de_encode = [ ( '0070000000', {"sec_domain": 0, "key_set_version": 112, "counter": 0, "rfu": 0} ), ] def __init__(self, fid='6f26', name='EF.GP_COUNT', desc='GP SCP02 Counters'): super().__init__(fid, name=name, desc=desc, rec_len=(5, 5)) self._construct = Struct('sec_domain'/Int8ub, 'key_set_version'/Int8ub, 'counter'/Int16ub, 'rfu'/Int8ub) class EF_GP_DIV_DATA(LinFixedEF): def __init__(self, fid='6f27', name='EF.GP_DIV_DATA', desc='GP SCP02 key diversification data'): super().__init__(fid, name=name, desc=desc, rec_len=(12, 12)) def _decode_record_bin(self, raw_bin_data, **kwargs): u = unpack('!BB8s', raw_bin_data) return {'sec_domain': u[0], 'key_set_version': u[1], 'key_div_data': u[2].hex()} class EF_SIM_AUTH_KEY(TransparentEF): _test_de_encode = [ ( '14000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', {"cfg": {"use_sres_deriv_func_2": 0, "use_opc_instead_of_op": 1, "algorithm": "milenage"}, "key": "000102030405060708090a0b0c0d0e0f", "op_opc": "101112131415161718191a1b1c1d1e1f"} ), ] def __init__(self, fid='6f20', name='EF.SIM_AUTH_KEY'): super().__init__(fid, name=name, desc='USIM authentication key') CfgByte = BitStruct(Padding(2), 'use_sres_deriv_func_2'/Bit, 'use_opc_instead_of_op'/Bit, 'algorithm'/Enum(Nibble, milenage=4, comp128v1=1, comp128v2=2, comp128v3=3)) self._construct = Struct('cfg'/CfgByte, 'key'/HexAdapter(Bytes(16)), 'op_opc' /HexAdapter(Bytes(16))) class DF_SYSTEM(CardDF): def __init__(self): super().__init__(fid='a515', name='DF.SYSTEM', desc='CardOS specifics') files = [ EF_PIN('6f01', 'EF.CHV1'), EF_PIN('6f81', 'EF.CHV2'), EF_PIN('6f0a', 'EF.ADM1'), EF_PIN('6f0b', 'EF.ADM2'), EF_PIN('6f0c', 'EF.ADM3'), EF_PIN('6f0d', 'EF.ADM4'), EF_MILENAGE_CFG(), EF_0348_KEY(), EF_SIM_AUTH_COUNTER(), EF_SIM_AUTH_KEY(), EF_0348_COUNT(), EF_GP_COUNT(), EF_GP_DIV_DATA(), ] self.add_files(files) def decode_select_response(self, resp_hex): return pySim.ts_102_221.CardProfileUICC.decode_select_response(resp_hex) class EF_USIM_SQN(TransparentEF): _test_de_encode = [ ( 'd503000200000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', {"flag1": {"skip_next_sqn_check": 1, "delta_max_check": 1, "age_limit_check": 0, "sqn_check": 1, "ind_len": 5}, "flag2": {"rfu": 0, "dont_clear_amf_for_macs": 0, "aus_concealed": 1, "autn_concealed": 1}, "delta_max": 8589934592, "age_limit": 8589934592, "freshness": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]} ), ] def __init__(self, fid='af30', name='EF.USIM_SQN'): super().__init__(fid, name=name, desc='SQN parameters for AKA') Flag1 = BitStruct('skip_next_sqn_check'/Bit, 'delta_max_check'/Bit, 'age_limit_check'/Bit, 'sqn_check'/Bit, 'ind_len'/BitsInteger(4)) Flag2 = BitStruct('rfu'/BitsRFU(5), 'dont_clear_amf_for_macs'/Bit, 'aus_concealed'/Bit, 'autn_concealed'/Bit) self._construct = Struct('flag1'/Flag1, 'flag2'/Flag2, 'delta_max' / BytesInteger(6), 'age_limit'/BytesInteger(6), 'freshness'/GreedyRange(BytesInteger(6))) class EF_USIM_AUTH_KEY(TransparentEF): def __init__(self, fid='af20', name='EF.USIM_AUTH_KEY'): super().__init__(fid, name=name, desc='USIM authentication key') Algorithm = Enum(Nibble, milenage=4, sha1_aka=5, tuak=6, xor=15) CfgByte = BitStruct(Padding(1), 'only_4bytes_res_in_3g'/Bit, 'sres_deriv_func_2_in_3g'/Mapping(Bit, {1:0, 2:1}), 'use_opc_instead_of_op'/Mapping(Bit, {False:0, True:1}), 'algorithm'/Algorithm) self._construct = Struct('cfg'/CfgByte, 'key'/HexAdapter(Bytes(16)), 'op_opc' /HexAdapter(Bytes(16))) # TUAK has a rather different layout for the data, so we define a different # construct below and use explicit _{decode,encode}_bin() methods for separating # the TUAK and non-TUAK situation CfgByteTuak = BitStruct(Padding(1), 'key_length'/Mapping(Bit, {128:0, 256:1}), 'sres_deriv_func_in_3g'/Mapping(Bit, {1:0, 2:1}), 'use_opc_instead_of_op'/Mapping(Bit, {False:0, True:1}), 'algorithm'/Algorithm) TuakCfgByte = BitStruct(Padding(1), 'ck_and_ik_size'/Mapping(Bit, {128:0, 256:1}), 'mac_size'/Mapping(BitsInteger(3), {64:0, 128:1, 256:2}), 'res_size'/Mapping(BitsInteger(3), {32:0, 64:1, 128:2, 256:3})) self._constr_tuak = Struct('cfg'/CfgByteTuak, 'tuak_cfg'/TuakCfgByte, 'num_of_keccak_iterations'/Int8ub, 'op_opc'/HexAdapter(Bytes(32)), 'k'/HexAdapter(Bytes(this.cfg.key_length//8))) def _decode_bin(self, raw_bin_data: bytearray) -> dict: if raw_bin_data[0] & 0x0F == 0x06: return parse_construct(self._constr_tuak, raw_bin_data) else: return parse_construct(self._construct, raw_bin_data) def _encode_bin(self, abstract_data: dict) -> bytearray: if abstract_data['cfg']['algorithm'] == 'tuak': return self._constr_tuak.build(abstract_data) else: return self._construct.build(abstract_data) class EF_USIM_AUTH_KEY_2G(TransparentEF): _test_de_encode = [ ( '14000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', {"cfg": {"only_4bytes_res_in_3g": 0, "use_sres_deriv_func_2_in_3g": 0, "use_opc_instead_of_op": 1, "algorithm": "milenage"}, "key": "000102030405060708090a0b0c0d0e0f", "op_opc": "101112131415161718191a1b1c1d1e1f"} ), ] def __init__(self, fid='af22', name='EF.USIM_AUTH_KEY_2G'): super().__init__(fid, name=name, desc='USIM authentication key in 2G context') CfgByte = BitStruct(Padding(1), 'only_4bytes_res_in_3g'/Bit, 'use_sres_deriv_func_2_in_3g'/Bit, 'use_opc_instead_of_op'/Bit, 'algorithm'/Enum(Nibble, milenage=4, comp128v1=1, comp128v2=2, comp128v3=3, xor=14)) self._construct = Struct('cfg'/CfgByte, 'key'/HexAdapter(Bytes(16)), 'op_opc' /HexAdapter(Bytes(16))) class EF_GBA_SK(TransparentEF): def __init__(self, fid='af31', name='EF.GBA_SK'): super().__init__(fid, name=name, desc='Secret key for GBA key derivation') self._construct = GreedyBytes class EF_GBA_REC_LIST(TransparentEF): def __init__(self, fid='af32', name='EF.GBA_REC_LIST'): super().__init__(fid, name=name, desc='Secret key for GBA key derivation') # integers representing record numbers in EF-GBANL self._construct = GreedyRange(Int8ub) class EF_GBA_INT_KEY(LinFixedEF): def __init__(self, fid='af33', name='EF.GBA_INT_KEY'): super().__init__(fid, name=name, desc='Secret key for GBA key derivation', rec_len=(32, 32)) self._construct = GreedyBytes class SysmocomSJA2(CardModel): _atrs = ["3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 30 34 05 4B A9", "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 31 33 02 51 B2", "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 52 75 31 04 51 D5"] @classmethod def add_files(cls, rs: RuntimeState): """Add sysmocom SJA2 specific files to given RuntimeState.""" rs.mf.add_file(DF_SYSTEM()) # optional USIM application if 'a0000000871002' in rs.mf.applications: usim_adf = rs.mf.applications['a0000000871002'] files_adf_usim = [ EF_USIM_AUTH_KEY(), EF_USIM_AUTH_KEY_2G(), EF_GBA_SK(), EF_GBA_REC_LIST(), EF_GBA_INT_KEY(), EF_USIM_SQN(), ] usim_adf.add_files(files_adf_usim) # optional ISIM application if 'a0000000871004' in rs.mf.applications: isim_adf = rs.mf.applications['a0000000871004'] files_adf_isim = [ EF_USIM_AUTH_KEY(name='EF.ISIM_AUTH_KEY'), EF_USIM_AUTH_KEY_2G(name='EF.ISIM_AUTH_KEY_2G'), EF_USIM_SQN(name='EF.ISIM_SQN'), ] isim_adf.add_files(files_adf_isim) class SysmocomSJA5(CardModel): _atrs = ["3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 35 75 30 35 02 51 CC", "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 35 75 30 35 02 65 F8", "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 35 75 30 35 02 59 C4"] @classmethod def add_files(cls, rs: RuntimeState): """Add sysmocom SJA2 specific files to given RuntimeState.""" rs.mf.add_file(DF_SYSTEM()) # optional USIM application if 'a0000000871002' in rs.mf.applications: usim_adf = rs.mf.applications['a0000000871002'] files_adf_usim = [ EF_USIM_AUTH_KEY(), EF_USIM_AUTH_KEY_2G(), EF_GBA_SK(), EF_GBA_REC_LIST(), EF_GBA_INT_KEY(), EF_USIM_SQN(), ] usim_adf.add_files(files_adf_usim) # optional ISIM application if 'a0000000871004' in rs.mf.applications: isim_adf = rs.mf.applications['a0000000871004'] files_adf_isim = [ EF_USIM_AUTH_KEY(name='EF.ISIM_AUTH_KEY'), EF_USIM_AUTH_KEY_2G(name='EF.ISIM_AUTH_KEY_2G'), EF_USIM_SQN(name='EF.ISIM_SQN'), ] isim_adf.add_files(files_adf_isim)