Change in pysim[master]: pysim-Shell: Add sysmocom SJA2 card specific bits

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.org
Thu Oct 14 14:06:00 UTC 2021


laforge has uploaded this change for review. ( 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, 275 insertions(+), 1 deletion(-)



  git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/84/25784/1

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..f46c88b 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,28 @@
             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 = []
+
+    @abc.abstractmethod
+    def add_files(self, rs:RuntimeState):
+        """Add model specific files to given RuntimeState."""
+
+    def match(self, scc:SimCardCommands) -> bool:
+        """Test if given card matches this model."""
+        card_atr = scc.get_atr()
+        for atr in self._atrs:
+            atr_bin = toBytes(atr)
+            if atr_bin == card_atr:
+                print("Detected CardModel:", self.__name__)
+                return True
+        return False
+
+    @staticmethod
+    def apply_matching_models(scc:SimCardCommands, rs:RuntimeState):
+        for m in CardModel.__subclasses__():
+            if m.match(m, scc):
+                m.add_files(m, rs)
diff --git a/pySim/sysmocom_sja2.py b/pySim/sysmocom_sja2.py
new file mode 100644
index 0000000..e4d1c42
--- /dev/null
+++ b/pySim/sysmocom_sja2.py
@@ -0,0 +1,240 @@
+# 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" ]
+    def add_files(self, 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: 1
Gerrit-Owner: laforge <laforge at osmocom.org>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20211014/19372a0c/attachment.htm>


More information about the gerrit-log mailing list