This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
laforge gerrit-no-reply at lists.osmocom.orglaforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/25784 ) Change subject: pysim-Shell: Add sysmocom SJA2 card specific bits ...................................................................... pysim-Shell: Add sysmocom SJA2 card specific bits Depending on the ATR, we register the various sysmocom SJA2 card model specific files [or not]. Change-Id: Id410489841bb9020ddbf74de9114d808b1d5adb6 --- M pySim-shell.py M pySim/filesystem.py A pySim/sysmocom_sja2.py 3 files changed, 278 insertions(+), 1 deletion(-) Approvals: Jenkins Builder: Verified laforge: Looks good to me, approved diff --git a/pySim-shell.py b/pySim-shell.py index f821e41..871c45e 100755 --- a/pySim-shell.py +++ b/pySim-shell.py @@ -42,12 +42,14 @@ from pySim.utils import dec_st, sanitize_pin_adm, tabulate_str_list, is_hex, boxed_heading_str from pySim.card_handler import CardHandler -from pySim.filesystem import CardMF, RuntimeState, CardDF, CardADF +from pySim.filesystem import CardMF, RuntimeState, CardDF, CardADF, CardModel from pySim.ts_51_011 import CardProfileSIM, DF_TELECOM, DF_GSM from pySim.ts_102_221 import CardProfileUICC from pySim.ts_31_102 import CardApplicationUSIM from pySim.ts_31_103 import CardApplicationISIM +import pySim.sysmocom_sja2 + from pySim.card_key_provider import CardKeyProviderCsv, card_key_provider_register, card_key_provider_get_field def init_card(sl): @@ -84,6 +86,8 @@ rs.mf.add_file(DF_TELECOM()) rs.mf.add_file(DF_GSM()) + CardModel.apply_matching_models(scc, rs) + # inform the transport that we can do context-specific SW interpretation sl.set_sw_interpreter(rs) diff --git a/pySim/filesystem.py b/pySim/filesystem.py index 170429b..5cdac56 100644 --- a/pySim/filesystem.py +++ b/pySim/filesystem.py @@ -27,6 +27,8 @@ import code import tempfile import json +import abc +import inspect import cmd2 from cmd2 import CommandSet, with_default_category, with_argparser @@ -34,10 +36,13 @@ from typing import cast, Optional, Iterable, List, Any, Dict, Tuple +from smartcard.util import toBytes + from pySim.utils import sw_match, h2b, b2h, i2h, is_hex, auto_int, bertlv_parse_one, Hexstr from pySim.construct import filter_dict, parse_construct from pySim.exceptions import * from pySim.jsonpath import js_path_find, js_path_modify +from pySim.commands import SimCardCommands class CardFile(object): """Base class for all objects in the smart card filesystem. @@ -1427,3 +1432,30 @@ Tuple of two strings """ return interpret_sw(self.sw, sw) + + +class CardModel(abc.ABC): + """A specific card model, typically having some additional vendor-specific files""" + _atrs = [] + + @classmethod + @abc.abstractmethod + def add_files(cls, rs:RuntimeState): + """Add model specific files to given RuntimeState.""" + + @classmethod + def match(cls, scc:SimCardCommands) -> bool: + """Test if given card matches this model.""" + card_atr = scc.get_atr() + for atr in cls._atrs: + atr_bin = toBytes(atr) + if atr_bin == card_atr: + print("Detected CardModel:", cls.__name__) + return True + return False + + @staticmethod + def apply_matching_models(scc:SimCardCommands, rs:RuntimeState): + for m in CardModel.__subclasses__(): + if m.match(scc): + m.add_files(rs) diff --git a/pySim/sysmocom_sja2.py b/pySim/sysmocom_sja2.py new file mode 100644 index 0000000..337eca7 --- /dev/null +++ b/pySim/sysmocom_sja2.py @@ -0,0 +1,241 @@ +# coding=utf-8 +"""Utilities / Functions related to sysmocom SJA2 cards + +(C) 2021 by Harald Welte <laforge at osmocom.org> + +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 pytlv.TLV import * +from struct import pack, unpack +from pySim.utils import * +from pySim.filesystem import * +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): + def __init__(self, fid='6f21', name='EF.MILENAGE_CFG', desc='Milenage connfiguration'): + super().__init__(fid, name=name, desc=desc) + def _decode_bin(self, raw_bin_data): + u = unpack('!BBBBB16s16s16s16s16s', raw_bin_data) + return {'r1': u[0], 'r2': u[1], 'r3': u[2], 'r4': u[3], 'r5': u[4], + 'c1': u[5].hex(), + 'c2': u[6].hex(), + 'c3': u[7].hex(), + 'c4': u[8].hex(), + 'c5': u[9].hex(), + } + +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}) + def _decode_record_bin(self, raw_bin_data): + u = unpack('!BBB', raw_bin_data[0:3]) + key_algo = (u[2] >> 6) & 1 + key_length = ((u[2] >> 3) & 3) * 8 + return {'sec_domain': u[0], + 'key_set_version': u[1], + 'key_type': key_type2str[u[2] & 3], + 'key_length': key_length, + 'algorithm': key_algo2str[key_algo], + 'mac_length': mac_length[(u[2] >> 7)], + 'key': raw_bin_data[3:key_length].hex() + } + +class EF_0348_COUNT(LinFixedEF): + 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}) + def _decode_record_bin(self, raw_bin_data): + u = unpack('!BB5s', raw_bin_data) + return {'sec_domain': u[0], 'key_set_version': u[1], 'counter': u[2]} + +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): + def __init__(self, fid='6f26', name='EF.GP_COUNT', desc='GP SCP02 Counters'): + super().__init__(fid, name=name, desc=desc, rec_len={5,5}) + def _decode_record_bin(self, raw_bin_data): + u = unpack('!BBHB', raw_bin_data) + return {'sec_domain': u[0], 'key_set_version': u[1], 'counter': u[2], 'rfu': u[3]} + +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): + 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): + def __init__(self, fid='6f20', name='EF.SIM_AUTH_KEY'): + super().__init__(fid, name=name, desc='USIM authentication key') + CfgByte = BitStruct(Bit[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'/Bytes(16), + 'op'/If(this.cfg.algorithm=='milenage' and not this.cfg.use_opc_instead_of_op, Bytes(16)), + 'opc'/If(this.cfg.algorithm=='milenage' and this.cfg.use_opc_instead_of_op, 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.decode_select_response(resp_hex) + +class EF_USIM_SQN(TransparentEF): + 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'/HexAdapter(GreedyBytes)) + +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') + CfgByte = BitStruct(Bit, '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, sha1_aka=5, xor=15)) + self._construct = Struct('cfg'/CfgByte, + 'key'/Bytes(16), + 'op'/If(this.cfg.algorithm=='milenage' and not this.cfg.use_opc_instead_of_op, Bytes(16)), + 'opc'/If(this.cfg.algorithm=='milenage' and this.cfg.use_opc_instead_of_op, Bytes(16)) + ) +class EF_USIM_AUTH_KEY_2G(TransparentEF): + 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(Bit, '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)) + self._construct = Struct('cfg'/CfgByte, + 'key'/Bytes(16), + 'op'/If(this.cfg.algorithm=='milenage' and not this.cfg.use_opc_instead_of_op, Bytes(16)), + 'opc'/If(this.cfg.algorithm=='milenage' and this.cfg.use_opc_instead_of_op, 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) -- To view, visit https://gerrit.osmocom.org/c/pysim/+/25784 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: Id410489841bb9020ddbf74de9114d808b1d5adb6 Gerrit-Change-Number: 25784 Gerrit-PatchSet: 2 Gerrit-Owner: laforge <laforge at osmocom.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge at osmocom.org> Gerrit-CC: fixeria <vyanitskiy at sysmocom.de> Gerrit-MessageType: merged -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20211014/bdae6fbf/attachment.htm>