Compare commits

...

6 Commits

Author SHA1 Message Date
Philipp Maier 25e43e1540 pySim/ara_m: move code from do_aram_ to static methods
The method do_aram_store_ref_ar_do and do_aram_delete_all, which
are part of the nested AddlShellCommands class, may be moved into
the parent class as a static method, just like the already existing
get_config method.

This makes the functionality re-usable to callers that do not use
the CMD2 API.

Change-Id: Icd1b08ec707dd939bc9e8524d7f9431aa4daae7c
Related: SYS#6959
2026-07-13 12:13:04 +02:00
Neels Hofmeyr 6e10da4c55 saip/personalization: add MncLen configurable parameter
Add a new ConfigurableParameter that represents the MNC length
(2 or 3 digits) in EF.AD (Administrative Data).

Change-Id: I6c600faeab00ffb072acbe94c9a8b2d1397c07d3
Co-authored-by: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
Jenkins: skip-card-test
2026-07-06 17:24:47 +00:00
Vadim Yanitskiy 973d6eb2cc pySim.log: fix E0611: No name 'style' in module 'cmd2'
Change-Id: I191ea56f4c6e4e1916369f69fe2e1653e1d92df1
Fixes: 597f1e0 ("pySim.log, pySim-shell: fix compatibility with cmd2 >= 3.0.0")
2026-07-01 16:47:08 +07:00
Vadim Yanitskiy 597f1e0398 pySim.log, pySim-shell: fix compatibility with cmd2 >= 3.0.0
Some Linux distributions (e.g. Arch Linux) already ship cmd2 3.x.x,
which removed the style()/Fg/Bg API in favor of stylize()/Color.

Add a version guard to select the right API at runtime.
Adjust the upper bound cap in requirements.txt and setup.py.

Change-Id: Ibf2ac7847933296fb06665c87f53ed6e1f315d27
2026-06-26 02:47:45 +07:00
Vadim Yanitskiy 45d37ed959 pySim-shell: drop backwards compat quirks for cmd2 < 2.6.2
Remove version guards for cmd2 < 2.0.0 and < 2.3.0, the Cmd2Compat
and Settable2Compat wrapper classes, and the old fg/bg color API -
none of these are needed since both requirements.txt and setup.py
already mandate cmd2 >= 2.6.2.

Change-Id: Ifd1c484ab66d74323d10e946347daa637cf6f5d8
2026-06-25 22:51:03 +07:00
Harald Welte 757c7d048e setup.py: Align cmd2 minimum version with requirements.txt
As pointed out in the commit-log of Change-Id
I5186f242dbc1b770e3ab8cdca7f27d2a1029fff6 we had different minimum
versions for cmd2 in requirements.txt vs setup.py.  Let's align that.

Change-Id: I71cee0ec3ed2abec68ec567beaab13c868721dad
2026-06-25 21:43:52 +07:00
8 changed files with 200 additions and 90 deletions
+30 -48
View File
@@ -24,21 +24,21 @@ import traceback
import re import re
import cmd2 import cmd2
from packaging import version from packaging import version
from cmd2 import style
import logging import logging
from pySim.log import PySimLogger from pySim.log import PySimLogger
from osmocom.utils import auto_uint8 from osmocom.utils import auto_uint8
# cmd2 >= 2.3.0 has deprecated the bg/fg in favor of Bg/Fg :( # cmd2 >= 3.0 replaced Fg + style() with Color + stylize()
if version.parse(cmd2.__version__) < version.parse("2.3.0"): if version.parse(cmd2.__version__) >= version.parse("3.0.0"):
from cmd2 import fg, bg # pylint: disable=no-name-in-module from cmd2 import Color, stylize # pylint: disable=no-name-in-module
RED = fg.red RED = Color.RED
YELLOW = fg.yellow YELLOW = Color.YELLOW
LIGHT_RED = fg.bright_red LIGHT_RED = Color.BRIGHT_RED
LIGHT_GREEN = fg.bright_green LIGHT_GREEN = Color.BRIGHT_GREEN
def style(text, fg=None, bg=None, bold=False): # pylint: disable=function-redefined
return stylize(text, fg) if fg else text
else: else:
from cmd2 import Fg, Bg # pylint: disable=no-name-in-module from cmd2 import style, Fg # pylint: disable=no-name-in-module
RED = Fg.RED RED = Fg.RED
YELLOW = Fg.YELLOW YELLOW = Fg.YELLOW
LIGHT_RED = Fg.LIGHT_RED LIGHT_RED = Fg.LIGHT_RED
@@ -76,43 +76,19 @@ from pySim.app import init_card
log = PySimLogger.get(Path(__file__).stem) log = PySimLogger.get(Path(__file__).stem)
class Cmd2Compat(cmd2.Cmd): class PysimApp(cmd2.Cmd):
"""Backwards-compatibility wrapper around cmd2.Cmd to support older and newer
releases. See https://github.com/python-cmd2/cmd2/blob/master/CHANGELOG.md"""
def run_editor(self, file_path: Optional[str] = None) -> None:
if version.parse(cmd2.__version__) < version.parse("2.0.0"):
return self._run_editor(file_path) # pylint: disable=no-member
else:
return super().run_editor(file_path) # pylint: disable=no-member
class Settable2Compat(cmd2.Settable):
"""Backwards-compatibility wrapper around cmd2.Settable to support older and newer
releases. See https://github.com/python-cmd2/cmd2/blob/master/CHANGELOG.md"""
def __init__(self, name, val_type, description, settable_object, **kwargs):
if version.parse(cmd2.__version__) < version.parse("2.0.0"):
super().__init__(name, val_type, description, **kwargs) # pylint: disable=no-value-for-parameter
else:
super().__init__(name, val_type, description, settable_object, **kwargs) # pylint: disable=too-many-function-args
class PysimApp(Cmd2Compat):
CUSTOM_CATEGORY = 'pySim Commands' CUSTOM_CATEGORY = 'pySim Commands'
BANNER = """Welcome to pySim-shell! BANNER = """Welcome to pySim-shell!
(C) 2021-2023 by Harald Welte, sysmocom - s.f.m.c. GmbH and contributors (C) 2021-2023 by Harald Welte, sysmocom - s.f.m.c. GmbH and contributors
Online manual available at https://downloads.osmocom.org/docs/pysim/master/html/shell.html """ Online manual available at https://downloads.osmocom.org/docs/pysim/master/html/shell.html """
def __init__(self, verbose, card, rs, sl, ch, script=None): def __init__(self, verbose, card, rs, sl, ch, script=None):
if version.parse(cmd2.__version__) < version.parse("2.0.0"):
kwargs = {'use_ipython': True}
else:
kwargs = {'include_ipy': True}
self.verbose = verbose self.verbose = verbose
PySimLogger.setup(self.poutput, {logging.WARN: YELLOW}) PySimLogger.setup(self.poutput, {logging.WARN: YELLOW})
self._onchange_verbose('verbose', False, self.verbose) self._onchange_verbose('verbose', False, self.verbose)
# pylint: disable=unexpected-keyword-arg
super().__init__(persistent_history_file='~/.pysim_shell_history', allow_cli_args=False, super().__init__(persistent_history_file='~/.pysim_shell_history', allow_cli_args=False,
auto_load_commands=False, startup_script=script, **kwargs) auto_load_commands=False, startup_script=script, include_ipy=True)
self.intro = style(self.BANNER, fg=RED) self.intro = style(self.BANNER, fg=RED)
self.default_category = 'pySim-shell built-in commands' self.default_category = 'pySim-shell built-in commands'
self.card = None self.card = None
@@ -128,18 +104,24 @@ Online manual available at https://downloads.osmocom.org/docs/pysim/master/html/
self.apdu_trace = False self.apdu_trace = False
self.apdu_strict = False self.apdu_strict = False
self.add_settable(Settable2Compat('numeric_path', bool, 'Print File IDs instead of names', self, self.add_settable(cmd2.Settable('numeric_path', bool,
onchange_cb=self._onchange_numeric_path)) 'Print File IDs instead of names',
self.add_settable(Settable2Compat('conserve_write', bool, 'Read and compare before write', self, self, onchange_cb=self._onchange_numeric_path))
onchange_cb=self._onchange_conserve_write)) self.add_settable(cmd2.Settable('conserve_write', bool,
self.add_settable(Settable2Compat('json_pretty_print', bool, 'Pretty-Print JSON output', self)) 'Read and compare before write',
self.add_settable(Settable2Compat('apdu_trace', bool, 'Trace and display APDUs exchanged with card', self, self, onchange_cb=self._onchange_conserve_write))
onchange_cb=self._onchange_apdu_trace)) self.add_settable(cmd2.Settable('json_pretty_print', bool,
self.add_settable(Settable2Compat('apdu_strict', bool, 'Pretty-Print JSON output',
'Strictly apply APDU format according to ISO/IEC 7816-3, table 12', self)) self))
self.add_settable(Settable2Compat('verbose', bool, self.add_settable(cmd2.Settable('apdu_trace', bool,
'Enable/disable verbose logging', self, 'Trace and display APDUs exchanged with card',
onchange_cb=self._onchange_verbose)) self, onchange_cb=self._onchange_apdu_trace))
self.add_settable(cmd2.Settable('apdu_strict', bool,
'Strictly apply APDU format according to ISO/IEC 7816-3, table 12',
self))
self.add_settable(cmd2.Settable('verbose', bool,
'Enable/disable verbose logging',
self, onchange_cb=self._onchange_verbose))
self.equip(card, rs) self.equip(card, rs)
def equip(self, card, rs): def equip(self, card, rs):
+49 -38
View File
@@ -300,6 +300,51 @@ class ADF_ARAM(CardADF):
'major': v_major, 'minor': v_minor, 'patch': v_patch}}]) 'major': v_major, 'minor': v_minor, 'patch': v_patch}}])
return ADF_ARAM.xceive_apdu_tlv(scc, '80cadf21', cmd_do, ResponseAramConfigDO) return ADF_ARAM.xceive_apdu_tlv(scc, '80cadf21', cmd_do, ResponseAramConfigDO)
@staticmethod
def store_ref_ar_do(scc, aid:Hexstr, aid_empty:bool, device_app_id:Hexstr, pkg_ref:str,
apdu_filter:Hexstr, apdu_never:bool, apdu_always:bool,
nfc_always:bool, nfc_never:bool, android_permissions:Hexstr):
# REF
ref_do_content = []
if aid is not None:
ref_do_content += [{'aid_ref_do': aid}]
elif aid_empty:
ref_do_content += [{'aid_ref_empty_do': None}]
ref_do_content += [{'dev_app_id_ref_do': device_app_id}]
if pkg_ref:
ref_do_content += [{'pkg_ref_do': {'package_name_string': pkg_ref}}]
# AR
ar_do_content = []
if apdu_never:
ar_do_content += [{'apdu_ar_do': {'generic_access_rule': 'never'}}]
elif apdu_always:
ar_do_content += [{'apdu_ar_do': {'generic_access_rule': 'always'}}]
elif apdu_filter:
if len(apdu_filter) % 16:
raise ValueError(f'Invalid non-modulo-16 length of APDU filter: {len(apdu_filter)}')
offset = 0
apdu_filter_list = []
while offset < len(apdu_filter):
apdu_filter_list += [{'header': apdu_filter[offset:offset+8],
'mask': apdu_filter[offset+8:offset+16]}]
offset += 16 # Move offset to the beginning of the next apdu_filter object
ar_do_content += [{'apdu_ar_do': {'apdu_filter': apdu_filter_list}}]
if nfc_never:
ar_do_content += [{'nfc_ar_do': {'nfc_event_access_rule': 'never'}}]
elif nfc_always:
ar_do_content += [{'nfc_ar_do': {'nfc_event_access_rule': 'always'}}]
if android_permissions:
ar_do_content += [{'perm_ar_do': {'permissions': android_permissions}}]
d = [{'ref_ar_do': [{'ref_do': ref_do_content}, {'ar_do': ar_do_content}]}]
csrado = CommandStoreRefArDO()
csrado.from_val_dict(d)
return ADF_ARAM.store_data(scc, csrado)
@staticmethod
def aram_delete_all(scc):
deldo = CommandDelete()
return ADF_ARAM.store_data(scc, deldo)
@with_default_category('Application-Specific Commands') @with_default_category('Application-Specific Commands')
class AddlShellCommands(CommandSet): class AddlShellCommands(CommandSet):
def do_aram_get_all(self, _opts): def do_aram_get_all(self, _opts):
@@ -344,48 +389,15 @@ class ADF_ARAM(CardADF):
@cmd2.with_argparser(store_ref_ar_do_parse) @cmd2.with_argparser(store_ref_ar_do_parse)
def do_aram_store_ref_ar_do(self, opts): def do_aram_store_ref_ar_do(self, opts):
"""Perform STORE DATA [Command-Store-REF-AR-DO] to store a (new) access rule.""" """Perform STORE DATA [Command-Store-REF-AR-DO] to store a (new) access rule."""
# REF res_do = ADF_ARAM.store_ref_ar_do(self._cmd.lchan.scc, opts.aid, opts.aid_empty, opts.device_app_id,
ref_do_content = [] opts.pkg_ref, opts.apdu_filter, opts.apdu_never, opts.apdu_always,
if opts.aid is not None: opts.nfc_always, opts.nfc_never, opts.android_permissions)
ref_do_content += [{'aid_ref_do': opts.aid}]
elif opts.aid_empty:
ref_do_content += [{'aid_ref_empty_do': None}]
ref_do_content += [{'dev_app_id_ref_do': opts.device_app_id}]
if opts.pkg_ref:
ref_do_content += [{'pkg_ref_do': {'package_name_string': opts.pkg_ref}}]
# AR
ar_do_content = []
if opts.apdu_never:
ar_do_content += [{'apdu_ar_do': {'generic_access_rule': 'never'}}]
elif opts.apdu_always:
ar_do_content += [{'apdu_ar_do': {'generic_access_rule': 'always'}}]
elif opts.apdu_filter:
if len(opts.apdu_filter) % 16:
raise ValueError(f'Invalid non-modulo-16 length of APDU filter: {len(opts.apdu_filter)}')
offset = 0
apdu_filter = []
while offset < len(opts.apdu_filter):
apdu_filter += [{'header': opts.apdu_filter[offset:offset+8],
'mask': opts.apdu_filter[offset+8:offset+16]}]
offset += 16 # Move offset to the beginning of the next apdu_filter object
ar_do_content += [{'apdu_ar_do': {'apdu_filter': apdu_filter}}]
if opts.nfc_always:
ar_do_content += [{'nfc_ar_do': {'nfc_event_access_rule': 'always'}}]
elif opts.nfc_never:
ar_do_content += [{'nfc_ar_do': {'nfc_event_access_rule': 'never'}}]
if opts.android_permissions:
ar_do_content += [{'perm_ar_do': {'permissions': opts.android_permissions}}]
d = [{'ref_ar_do': [{'ref_do': ref_do_content}, {'ar_do': ar_do_content}]}]
csrado = CommandStoreRefArDO()
csrado.from_val_dict(d)
res_do = ADF_ARAM.store_data(self._cmd.lchan.scc, csrado)
if res_do: if res_do:
self._cmd.poutput_json(res_do.to_dict()) self._cmd.poutput_json(res_do.to_dict())
def do_aram_delete_all(self, _opts): def do_aram_delete_all(self, _opts):
"""Perform STORE DATA [Command-Delete[all]] to delete all access rules.""" """Perform STORE DATA [Command-Delete[all]] to delete all access rules."""
deldo = CommandDelete() res_do = ADF_ARAM.aram_delete_all(self._cmd.lchan.scc)
res_do = ADF_ARAM.store_data(self._cmd.lchan.scc, deldo)
if res_do: if res_do:
self._cmd.poutput_json(res_do.to_dict()) self._cmd.poutput_json(res_do.to_dict())
@@ -394,7 +406,6 @@ class ADF_ARAM(CardADF):
(Proprietary feature that is specific to sysmocom's fork of Bertrand Martels ARA-M implementation.)""" (Proprietary feature that is specific to sysmocom's fork of Bertrand Martels ARA-M implementation.)"""
self._cmd.lchan.scc.send_apdu_checksw('80e2900001A1', '9000') self._cmd.lchan.scc.send_apdu_checksw('80e2900001A1', '9000')
# SEAC v1.1 Section 4.1.2.2 + 5.1.2.2 # SEAC v1.1 Section 4.1.2.2 + 5.1.2.2
sw_aram = { sw_aram = {
'ARA-M': { 'ARA-M': {
+68
View File
@@ -21,9 +21,11 @@ import io
import re import re
from typing import List, Tuple, Generator, Optional from typing import List, Tuple, Generator, Optional
from construct.core import StreamError
from osmocom.tlv import camel_to_snake from osmocom.tlv import camel_to_snake
from osmocom.utils import hexstr from osmocom.utils import hexstr
from pySim.utils import enc_iccid, dec_iccid, enc_imsi, dec_imsi, h2b, b2h, rpad, sanitize_iccid from pySim.utils import enc_iccid, dec_iccid, enc_imsi, dec_imsi, h2b, b2h, rpad, sanitize_iccid
from pySim.ts_31_102 import EF_AD
from pySim.ts_51_011 import EF_SMSP from pySim.ts_51_011 import EF_SMSP
from pySim.esim.saip import param_source from pySim.esim.saip import param_source
from pySim.esim.saip import ProfileElement, ProfileElementSD, ProfileElementSequence from pySim.esim.saip import ProfileElement, ProfileElementSD, ProfileElementSequence
@@ -660,6 +662,72 @@ class SmspTpScAddr(ConfigurableParameter):
yield { cls.name: cls.tuple_to_str((international, digits)) } yield { cls.name: cls.tuple_to_str((international, digits)) }
class MncLen(EnumParam):
"""MNC length. Sets only the MNC length field in EF.AD (Administrative Data).
Accepted values: integer 2 or 3, digit strings '2' or '3', or enum names 'MNC2'/'MNC3'.
"""
name = 'MNC-LEN'
example_input = '2'
default_source = param_source.ConstantSource
class Values(enum.IntEnum):
MNC2 = 2
MNC3 = 3
@classmethod
def validate_val(cls, val):
if isinstance(val, str) and val.isdigit():
val = int(val)
return super().validate_val(val)
@classmethod
def _get_f_ad(cls, pe: ProfileElement):
if not hasattr(pe, 'files'):
return None
f_ad = pe.files.get('ef-ad', None)
if f_ad and f_ad.body:
return f_ad
return None
@classmethod
def _decode_f_ad(cls, f_ad):
try:
ef_ad_dec = EF_AD().decode_bin(f_ad.body)
except StreamError:
return None
if 'mnc_len' not in ef_ad_dec:
return None
return ef_ad_dec
@classmethod
def apply_val(cls, pes: ProfileElementSequence, val: int):
for pe in pes.get_pes_for_type('usim'):
f_ad = cls._get_f_ad(pe)
if f_ad is None:
continue
# decode existing values
ef_ad_dec = cls._decode_f_ad(f_ad)
if ef_ad_dec is None:
continue
# change mnc_len
ef_ad_dec['mnc_len'] = val
# re-encode into the File body
f_ad.body = EF_AD().encode_bin(ef_ad_dec)
pe.file2pe(f_ad)
@classmethod
def get_values_from_pes(cls, pes: ProfileElementSequence):
for pe in pes.get_pes_for_type('usim'):
f_ad = cls._get_f_ad(pe)
if f_ad is None:
continue
ef_ad_dec = cls._decode_f_ad(f_ad)
if ef_ad_dec is None:
continue
mnc_len = ef_ad_dec.get('mnc_len')
yield { cls.name: str(mnc_len) }
class SdKey(BinaryParam): class SdKey(BinaryParam):
"""Configurable Security Domain (SD) Key. Value is presented as bytes. """Configurable Security Domain (SD) Key. Value is presented as bytes.
Non-abstract implementations are generated in SdKey.generate_sd_key_classes""" Non-abstract implementations are generated in SdKey.generate_sd_key_classes"""
+10 -2
View File
@@ -24,7 +24,15 @@
# #
import logging import logging
from cmd2 import style import cmd2
from packaging import version
if version.parse(cmd2.__version__) >= version.parse("3.0.0"):
from cmd2 import stylize as _stylize # pylint: disable=no-name-in-module
def _style(text, fg=None): # pylint: disable=function-redefined
return _stylize(text, fg) if fg else text
else: # cmd2>=2.6.2
from cmd2 import style as _style # pylint: disable=no-name-in-module
class _PySimLogHandler(logging.Handler): class _PySimLogHandler(logging.Handler):
def __init__(self, log_callback): def __init__(self, log_callback):
@@ -121,7 +129,7 @@ class PySimLogger:
if isinstance(color, str): if isinstance(color, str):
PySimLogger.print_callback(color + formatted_message + "\033[0m") PySimLogger.print_callback(color + formatted_message + "\033[0m")
else: else:
PySimLogger.print_callback(style(formatted_message, fg = color)) PySimLogger.print_callback(_style(formatted_message, fg = color))
else: else:
PySimLogger.print_callback(formatted_message) PySimLogger.print_callback(formatted_message)
+1 -1
View File
@@ -1,7 +1,7 @@
pyscard pyscard
pyserial pyserial
pytlv pytlv
cmd2>=2.6.2,<3.0 cmd2>=2.6.2,<4.0
jsonpath-ng jsonpath-ng
construct>=2.10.70 construct>=2.10.70
bidict bidict
+1 -1
View File
@@ -21,7 +21,7 @@ setup(
"pyscard", "pyscard",
"pyserial", "pyserial",
"pytlv", "pytlv",
"cmd2 >= 1.5.0, < 3.0", "cmd2 >= 2.6.2, < 4.0",
"jsonpath-ng", "jsonpath-ng",
"construct >= 2.10.70", "construct >= 2.10.70",
"bidict", "bidict",
@@ -267,6 +267,15 @@ class ConfigurableParameterTest(unittest.TestCase):
'11111111111111111111111111111111' '11111111111111111111111111111111'
'22222222222222222222222222222222'), '22222222222222222222222222222222'),
Paramtest(param_cls=p13n.MncLen,
val='2',
expect_clean_val=2,
expect_val='2'),
Paramtest(param_cls=p13n.MncLen,
val=3,
expect_clean_val=3,
expect_val='3'),
] ]
for sdkey_cls in ( for sdkey_cls in (
@@ -163,6 +163,14 @@ ok: TS48v5_SAIP2.1A_NoBERTLV.der MilenageXoringConstants(val= b'\xaa\xaa\xaa\xaa
clean_val= b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11""""""""""""""""':bytes clean_val= b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11""""""""""""""""':bytes
read_back_val= {'MilenageXOR': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc1111111111111111111111111111111122222222222222222222222222222222'}:{hexstr} read_back_val= {'MilenageXOR': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc1111111111111111111111111111111122222222222222222222222222222222'}:{hexstr}
ok: TS48v5_SAIP2.1A_NoBERTLV.der MncLen(val= '2':str)
clean_val= 2:int
read_back_val= {'MNC-LEN': '2'}:{str}
ok: TS48v5_SAIP2.1A_NoBERTLV.der MncLen(val= 3:int)
clean_val= 3:int
read_back_val= {'MNC-LEN': '3'}:{str}
ok: TS48v5_SAIP2.1A_NoBERTLV.der SdKeyScp02Kvn20AesDek(val= '01020304050607080910111213141516':str) ok: TS48v5_SAIP2.1A_NoBERTLV.der SdKeyScp02Kvn20AesDek(val= '01020304050607080910111213141516':str)
clean_val= b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes clean_val= b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
read_back_val= {'SCP02-KVN20-AES-DEK': '01020304050607080910111213141516'}:{hexstr} read_back_val= {'SCP02-KVN20-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
@@ -855,6 +863,14 @@ ok: TS48v5_SAIP2.3_BERTLV_SUCI.der MilenageXoringConstants(val= b'\xaa\xaa\xaa\x
clean_val= b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11""""""""""""""""':bytes clean_val= b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11""""""""""""""""':bytes
read_back_val= {'MilenageXOR': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc1111111111111111111111111111111122222222222222222222222222222222'}:{hexstr} read_back_val= {'MilenageXOR': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc1111111111111111111111111111111122222222222222222222222222222222'}:{hexstr}
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der MncLen(val= '2':str)
clean_val= 2:int
read_back_val= {'MNC-LEN': '2'}:{str}
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der MncLen(val= 3:int)
clean_val= 3:int
read_back_val= {'MNC-LEN': '3'}:{str}
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der SdKeyScp02Kvn20AesDek(val= '01020304050607080910111213141516':str) ok: TS48v5_SAIP2.3_BERTLV_SUCI.der SdKeyScp02Kvn20AesDek(val= '01020304050607080910111213141516':str)
clean_val= b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes clean_val= b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
read_back_val= {'SCP02-KVN20-AES-DEK': '01020304050607080910111213141516'}:{hexstr} read_back_val= {'SCP02-KVN20-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
@@ -1547,6 +1563,14 @@ ok: TS48v5_SAIP2.1B_NoBERTLV.der MilenageXoringConstants(val= b'\xaa\xaa\xaa\xaa
clean_val= b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11""""""""""""""""':bytes clean_val= b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11""""""""""""""""':bytes
read_back_val= {'MilenageXOR': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc1111111111111111111111111111111122222222222222222222222222222222'}:{hexstr} read_back_val= {'MilenageXOR': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc1111111111111111111111111111111122222222222222222222222222222222'}:{hexstr}
ok: TS48v5_SAIP2.1B_NoBERTLV.der MncLen(val= '2':str)
clean_val= 2:int
read_back_val= {'MNC-LEN': '2'}:{str}
ok: TS48v5_SAIP2.1B_NoBERTLV.der MncLen(val= 3:int)
clean_val= 3:int
read_back_val= {'MNC-LEN': '3'}:{str}
ok: TS48v5_SAIP2.1B_NoBERTLV.der SdKeyScp02Kvn20AesDek(val= '01020304050607080910111213141516':str) ok: TS48v5_SAIP2.1B_NoBERTLV.der SdKeyScp02Kvn20AesDek(val= '01020304050607080910111213141516':str)
clean_val= b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes clean_val= b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
read_back_val= {'SCP02-KVN20-AES-DEK': '01020304050607080910111213141516'}:{hexstr} read_back_val= {'SCP02-KVN20-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
@@ -2239,6 +2263,14 @@ ok: TS48v5_SAIP2.3_NoBERTLV.der MilenageXoringConstants(val= b'\xaa\xaa\xaa\xaa\
clean_val= b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11""""""""""""""""':bytes clean_val= b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11""""""""""""""""':bytes
read_back_val= {'MilenageXOR': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc1111111111111111111111111111111122222222222222222222222222222222'}:{hexstr} read_back_val= {'MilenageXOR': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc1111111111111111111111111111111122222222222222222222222222222222'}:{hexstr}
ok: TS48v5_SAIP2.3_NoBERTLV.der MncLen(val= '2':str)
clean_val= 2:int
read_back_val= {'MNC-LEN': '2'}:{str}
ok: TS48v5_SAIP2.3_NoBERTLV.der MncLen(val= 3:int)
clean_val= 3:int
read_back_val= {'MNC-LEN': '3'}:{str}
ok: TS48v5_SAIP2.3_NoBERTLV.der SdKeyScp02Kvn20AesDek(val= '01020304050607080910111213141516':str) ok: TS48v5_SAIP2.3_NoBERTLV.der SdKeyScp02Kvn20AesDek(val= '01020304050607080910111213141516':str)
clean_val= b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes clean_val= b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
read_back_val= {'SCP02-KVN20-AES-DEK': '01020304050607080910111213141516'}:{hexstr} read_back_val= {'SCP02-KVN20-AES-DEK': '01020304050607080910111213141516'}:{hexstr}