gerrit-log
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
October 2026
- 1 participants
- 1245 discussions
[S] Change in libosmo-abis[master]: cosmetic: update trau2rtp_gen documentation
by falconia 11 Oct '26
by falconia 11 Oct '26
11 Oct '26
falconia has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmo-abis/+/43870?usp=email )
Change subject: cosmetic: update trau2rtp_gen documentation
......................................................................
cosmetic: update trau2rtp_gen documentation
The testsuite for libosmo-abis includes a command line utility
named trau2rtp_gen, documented in trau2rtp_gen.notes file. This
utility is used for some unit tests, but it is also useful as a
developer tool when working with E1-based BTS or in-band TFO.
The documentation for trau2rtp_gen has got slightly out of date
since it was written:
* There is a new companion utility named trau2rtp_amr that exercises
the separate libosmotrau API for AMR TRAU frames;
* osmo_trau2rtp() for CSD modes has gained support for RTP output
in TW-TS-007 format in addition to CLEARMODE;
* The hex format emitted by trau2rtp_gen is defined in TW-TS-005,
and there is now a newer version of that spec than the one
previously linked.
Update trau2rtp_gen.notes document for the current reality.
Change-Id: I83f3964dd747932952d13f1e511f3d049d2a204f
---
M tests/trau_conv/trau2rtp_gen.notes
1 file changed, 27 insertions(+), 22 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmo-abis refs/changes/70/43870/1
diff --git a/tests/trau_conv/trau2rtp_gen.notes b/tests/trau_conv/trau2rtp_gen.notes
index 90027f5..b6b3617 100644
--- a/tests/trau_conv/trau2rtp_gen.notes
+++ b/tests/trau_conv/trau2rtp_gen.notes
@@ -57,7 +57,7 @@
but there are also tools that extract TRAU frames from E1 timeslot octet stream
recordings and emit those extracted frames in the present hex format:
-https://www.freecalypso.org/hg/gsm-net-reveng/file/tip/trau-decode
+https://hg.themyscira.org/gsm-net-reveng/file/tip/trau-decode
Example input for FRv1:
@@ -84,7 +84,7 @@
codecs; trau2rtp_gen emits its generated RTP payloads in this hex format.
Spec document:
-https://www.freecalypso.org/specs/tw-ts-005-v010003.txt
+https://www.themyscira.org/specs/tw-ts-005-v010100.txt
What can one do with a TW-TS-005 hex file? Some possibilities:
@@ -101,29 +101,34 @@
can be captured Abis-UL output from an E1 BTS, converted with trau2rtp_gen)
into playable audio.
-No AMR or CSD
-=============
+Separate utility for AMR
+========================
-One limitation of the present trau2rtp_gen utility that should be noted is the
-lack of support for AMR or CSD. The reasons for this limitation are:
+The principal library API exercised by this trau2rtp_gen program, namely
+osmo_trau2rtp(), does not support AMR - instead high-level decoding of AMR TRAU
+frames requires use of the interworking facility of <osmocom/trau/amr_trau.h>.
+At the level of command line utilities, a separate program exists that exercises
+the AMR-specific TRAU to RTP conversion APIs - see tests/amr/trau2rtp_amr.
-* AMR is currently not supported by osmo_trau2rtp() or osmo_rtp2trau()
- functions, and it is not entirely clear if this API would be a good match for
- the much more complex processing that will be needed in a software TRAU or
- TRAU-emulating MGW for AMR.
+No CSD support yet
+==================
-* CSD is supported by the chain of libosmotrau functions exercised by
- trau2rtp_gen, but the resulting bloated RTP payloads cannot be represented in
- TW-TS-005: spec section 4.1 limits each hex line to 80 characters, effectively
- limiting the domain of application for this hex file format to RTP payloads
- that are no larger than 40 bytes.
+When the input is a TRAU frame in one of CSD modes, the output from
+osmo_trau2rtp() can be either a 160-byte CLEARMODE payload or a much more
+compact TW-TS-007 (compressed CSD) payload. The bloated CLEARMODE format cannot
+be represented in TW-TS-005 hex files (it exceeds the line length limit imposed
+by TW-TS-005 section 4.1), but the alternative TW-TS-007 compressed CSD format
+_can_ be represented in TW-TS-005 hex - and the current version of TW-TS-005
+spec linked above includes Annex D that specifies such usage.
-When and if a need arises to represent CSD frame streams in hex, there does not
-seem to be one single solution that would be best for all use cases - instead
-the problem might need to be approached on a case-by-case basis. For the
-specific use case of unit tests, verifying correct operation of osmo_trau2rtp()
-and osmo_rtp2trau() functions in data modes, one possible approach would be to
+trau2rtp_gen utility can be extended to enable TW-TS-007 output and thereby add
+support for at least one representation of GSM CSD in RTP - but no such
+modification has been made yet.
+
+A need may also arise at some point to represent the bloated-but-standard
+CLEARMODE formats in hex as well. TW-TS-005 cannot be used for that purpose,
+but some other ad hoc hex format can be implemented quite easily - for example,
copy what is currently done in raa_prime unit tests: break each 160-byte CSD RTP
payload into 4 hex lines of 80 digits each. In any case, whichever hex format
-is to be chosen for CSD, it will be its own separate test program, independent
-of trau2rtp_gen.
+is to be chosen for non-compressed CSD in CLEARMODE representation, it would
+need to be its own separate test program, independent of trau2rtp_gen.
--
To view, visit https://gerrit.osmocom.org/c/libosmo-abis/+/43870?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: libosmo-abis
Gerrit-Branch: master
Gerrit-Change-Id: I83f3964dd747932952d13f1e511f3d049d2a204f
Gerrit-Change-Number: 43870
Gerrit-PatchSet: 1
Gerrit-Owner: falconia <falcon(a)freecalypso.org>
1
0
09 Oct '26
neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/43866?usp=email )
Change subject: personalization: SUCI: ff-pad remaining file size
......................................................................
personalization: SUCI: ff-pad remaining file size
Change-Id: I9465eac9269e3f76dddd467109f547489a0feb93
Jenkins: skip-card-test
---
M pySim/esim/saip/personalization.py
1 file changed, 9 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/66/43866/1
diff --git a/pySim/esim/saip/personalization.py b/pySim/esim/saip/personalization.py
index a81c61f..a3347cb 100644
--- a/pySim/esim/saip/personalization.py
+++ b/pySim/esim/saip/personalization.py
@@ -1334,7 +1334,15 @@
if not f_sucici:
continue
ef_sucici = EF_SUCI_Calc_Info()
- f_sucici.body = ef_sucici.encode_bin(val)
+ body = ef_sucici.encode_bin(val)
+
+ # 0xff pad up to the existing file size, so that the underlying template doesn't come through
+ is_size = f_sucici.file_size
+ pad_n = is_size - len(body)
+ if pad_n > 0:
+ body = body + b'\xff' * pad_n
+
+ f_sucici.body = body
pe.file2pe(f_sucici)
@classmethod
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/43866?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I9465eac9269e3f76dddd467109f547489a0feb93
Gerrit-Change-Number: 43866
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr(a)sysmocom.de>
1
0
[M] Change in pysim[master]: personalization: EnumParam: implement as value_map, not enum.IntEnum
by neels 09 Oct '26
by neels 09 Oct '26
09 Oct '26
neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/43864?usp=email )
Change subject: personalization: EnumParam: implement as value_map, not enum.IntEnum
......................................................................
personalization: EnumParam: implement as value_map, not enum.IntEnum
Because of (1) and (2) below, using enum.IntEnum is not a good choice
for EnumParam. Use a plain dict as value map.
EnumParam's idea is to be a mapping
human-readable-value-for-UI -> PES-value
and with enum.IntEnum we...
(1) ...we impose python namespace rules onto
human-readable-value-for-UI: "My Value" is not allowed, only "my_value".
By using enum.IntEnum, we make it impossible to use human readable
values like:
- "SUCI-on" (dash not allowed in python identifier)
- "True" (python keyword not allowed)
- "2" (numeric constant cannot be a python identifier)
(2) ...we limit PES-values to int. The mapping should support any type
that the given PES attribute needs (int, bool, bytes, ...).
Future: upcoming ConfigurableParameters depend on this patch:
- MncLen (labels "2" and "3")
- EuiccMandatoryServiceParam (values True and False)
- EfUstServiceParam like SuciInUsim (labels "SUCI-in-UE" and
"SUCI-in-USIM")
History: this is near the first version of EnumParam, before it was
modified by fixeria to use enum.IntEnum, as requested by CR. I realize
now that enum.IntEnum will not work for upcoming eSIM parameters.
Change-Id: I690ceccf0ec7ef7067bcaa5cec1303cdaf0f78a4
Jenkins: skip-card-test
---
M pySim/esim/saip/personalization.py
M tests/unittests/test_configurable_parameters.py
M tests/unittests/xo/test_configurable_parameters
3 files changed, 109 insertions(+), 118 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/64/43864/1
diff --git a/pySim/esim/saip/personalization.py b/pySim/esim/saip/personalization.py
index a136aa8..e9ed709 100644
--- a/pySim/esim/saip/personalization.py
+++ b/pySim/esim/saip/personalization.py
@@ -420,68 +420,70 @@
class EnumParam(ConfigurableParameter):
- """ConfigurableParameter for named integer enumeration values.
+ """ConfigurableParameter for named value enumerations.
- Subclasses must define a nested enum.IntEnum named 'Values' listing all valid names and their
- integer codes. apply_val() and get_values_from_pes() are not implemented here and this must
- be inherited from another mixin."""
-
- class Values(enum.IntEnum):
- pass # subclasses override this
+ Subclasses define an own value_map, and implement their own apply_val() and get_values_from_pes().
+ """
+ value_map = {
+ # For example:
+ #'Meaningful label for value 23': 0x23,
+ # Where 0x23 is a valid value to use for apply_val(), of any valid type.
+ }
+ _value_map_reverse = None
@classmethod
- def validate_val(cls, val) -> int:
- if isinstance(val, int):
- try:
- return int(cls.Values(val))
- except ValueError:
- pass
- elif isinstance(val, str):
- member = cls.map_name_to_val(val, strict=False)
- if member is not None:
- return member
+ def validate_val(cls, val):
+ orig_val = val
+ enum_val = None
+ if isinstance(val, str):
+ enum_name = val
+ enum_val = cls.map_name_to_val(enum_name)
- valid = ', '.join(m.name for m in cls.Values)
- raise ValueError(f"{cls.get_name()}: invalid argument: {val!r}. Valid arguments are: {valid}")
+ # if the str is not one of the known value_map.keys(), is it maybe one of value_map.keys()?
+ if enum_val is None and val in cls.value_map.values():
+ enum_val = val
+
+ if enum_val not in cls.value_map.values():
+ raise ValueError(f"{cls.get_name()}: invalid argument: {orig_val!r}. Valid arguments are:"
+ f" {', '.join(cls.value_map.keys())}")
+
+ return enum_val
@classmethod
- def map_name_to_val(cls, name: str, strict=True) -> int:
- """Return the integer value for a given enum member name. Performs an exact match first,
- then falls back to fuzzy matching (case-insensitive, punctuation-insensitive)."""
- try:
- return int(cls.Values[name])
- except KeyError:
- pass
+ def map_name_to_val(cls, name:str, strict=True):
+ val = cls.value_map.get(name)
+ if val is not None:
+ return val
- clean = cls.clean_name_str(name)
- for member in cls.Values:
- if cls.clean_name_str(member.name) == clean:
- return int(member)
+ clean_name = cls.clean_name_str(name)
+ for k, v in cls.value_map.items():
+ if clean_name == cls.clean_name_str(k):
+ return v
if strict:
- valid = ', '.join(m.name for m in cls.Values)
- raise ValueError(f"{cls.get_name()}: {name!r} is not a known value. Known values are: {valid}")
+ raise ValueError(f"Problem in {cls.get_name()}: {name!r} is not a known value."
+ f" Known values are: {cls.value_map.keys()!r}")
return None
@classmethod
def map_val_to_name(cls, val, strict=False) -> str:
- """Return the enum member name for a given integer value."""
- try:
- return cls.Values(val).name
- except ValueError:
- if strict:
- raise ValueError(f"{cls.get_name()}: {val!r} ({type(val).__name__}) is not a known value.")
- return None
+ if cls._value_map_reverse is None:
+ cls._value_map_reverse = dict((v, k) for k, v in cls.value_map.items())
+
+ name = cls._value_map_reverse.get(val)
+ if name:
+ return name
+ if strict:
+ raise ValueError(f"Problem in {cls.get_name()}: {val!r} ({type(val)}) is not a known value."
+ f" Known values are: {cls.value_map.values()!r}")
+ return None
@classmethod
- def name_normalize(cls, name: str) -> str:
- """Map a (possibly fuzzy) name to its canonical enum member name."""
- return cls.Values(cls.map_name_to_val(name)).name
+ def name_normalize(cls, name:str) -> str:
+ return cls.map_val_to_name(cls.map_name_to_val(name))
@classmethod
- def clean_name_str(cls, val: str) -> str:
- """Strip punctuation and case for fuzzy name comparison.
- Treats hyphens and underscores as equivalent (both removed)."""
+ def clean_name_str(cls, val):
return re.sub('[^0-9A-Za-z]', '', val).lower()
@@ -664,69 +666,57 @@
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'.
- """
+ """MNC length. Must be either 2 or 3. Sets only the MNC length field in EF-AD (Administrative Data)."""
name = 'MNC-LEN'
- example_input = '2'
+ value_map = { '2': 2, '3': 3 }
default_source = param_source.ConstantSource
-
- class Values(enum.IntEnum):
- MNC2 = 2
- MNC3 = 3
+ example_input = '2'
@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):
+ def apply_val(cls, pes: ProfileElementSequence, val):
+ """val must be an int: either 2 or 3"""
for pe in pes.get_pes_for_type('usim'):
- f_ad = cls._get_f_ad(pe)
- if f_ad is None:
+ if not hasattr(pe, 'files'):
+ continue
+ f_ad = pe.files.get('ef-ad')
+ if not f_ad:
continue
# decode existing values
- ef_ad_dec = cls._decode_f_ad(f_ad)
- if ef_ad_dec is None:
+ if not f_ad.body:
+ continue
+ try:
+ ef_ad = EF_AD()
+ ef_ad_dec = ef_ad.decode_bin(f_ad.body)
+ except StreamError:
+ continue
+ if 'mnc_len' not in ef_ad_dec:
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)
+ 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 not hasattr(pe, 'files'):
+ continue
+ f_ad = pe.files.get('ef-ad', None)
if f_ad is None:
continue
- ef_ad_dec = cls._decode_f_ad(f_ad)
- if ef_ad_dec is None:
+
+ try:
+ ef_ad = EF_AD()
+ ef_ad_dec = ef_ad.decode_bin(f_ad.body)
+ except StreamError:
continue
- mnc_len = ef_ad_dec.get('mnc_len')
- yield { cls.name: str(mnc_len) }
+
+ mnc_len = ef_ad_dec.get('mnc_len', None)
+ if mnc_len is None:
+ continue
+
+ yield { cls.name: cls.map_val_to_name(int(mnc_len)) }
class SdKey(BinaryParam):
@@ -1100,17 +1090,17 @@
"""use validate_val() from EnumParam, and apply_val() from AlgoConfig.
In get_values_from_pes(), return enum value names, not raw values."""
name = "Algorithm"
+ # as in pySim/esim/asn1/saip/PE_Definitions-3.3.1.asn
+ value_map = {
+ "Milenage" : 1,
+ "TUAK" : 2,
+ "usim-test" : 3,
+ }
algo_config_key = 'algorithmID'
example_input = "Milenage"
default_source = param_source.ConstantSource
- # as in pySim/esim/asn1/saip/PE_Definitions-3.3.1.asn
- class Values(enum.IntEnum):
- Milenage = 1
- TUAK = 2
- usim_test = 3 # input 'usim-test' also accepted via fuzzy matching
-
- # EnumParam.validate_val() returns the int values from Values
+ # EnumParam.validate_val() returns the int values from value_map
@classmethod
def get_values_from_pes(cls, pes: ProfileElementSequence):
diff --git a/tests/unittests/test_configurable_parameters.py b/tests/unittests/test_configurable_parameters.py
index 47f9eb8..0115640 100755
--- a/tests/unittests/test_configurable_parameters.py
+++ b/tests/unittests/test_configurable_parameters.py
@@ -148,7 +148,7 @@
Paramtest(param_cls=p13n.AlgorithmID,
val='usim-test',
expect_clean_val=3,
- expect_val='usim_test'),
+ expect_val='usim-test'),
Paramtest(param_cls=p13n.AlgorithmID,
val=1,
@@ -161,7 +161,7 @@
Paramtest(param_cls=p13n.AlgorithmID,
val=3,
expect_clean_val=3,
- expect_val='usim_test'),
+ expect_val='usim-test'),
Paramtest(param_cls=p13n.K,
val='01020304050607080910111213141516',
@@ -559,7 +559,7 @@
def test_validate_by_name_exact(self):
self.assertEqual(p13n.AlgorithmID.validate_val('Milenage'), 1)
self.assertEqual(p13n.AlgorithmID.validate_val('TUAK'), 2)
- self.assertEqual(p13n.AlgorithmID.validate_val('usim_test'), 3)
+ self.assertEqual(p13n.AlgorithmID.validate_val('usim-test'), 3)
def test_validate_by_int(self):
self.assertEqual(p13n.AlgorithmID.validate_val(1), 1)
@@ -572,7 +572,6 @@
self.assertEqual(p13n.AlgorithmID.validate_val('tuak'), 2)
def test_validate_fuzzy_hyphen_underscore(self):
- # 'usim-test' has a hyphen; enum member is 'usim_test' — must fuzzy-match
self.assertEqual(p13n.AlgorithmID.validate_val('usim-test'), 3)
def test_validate_invalid_name(self):
@@ -609,7 +608,7 @@
def test_map_val_known(self):
self.assertEqual(p13n.AlgorithmID.map_val_to_name(1), 'Milenage')
self.assertEqual(p13n.AlgorithmID.map_val_to_name(2), 'TUAK')
- self.assertEqual(p13n.AlgorithmID.map_val_to_name(3), 'usim_test')
+ self.assertEqual(p13n.AlgorithmID.map_val_to_name(3), 'usim-test')
def test_map_val_unknown_nonstrict(self):
self.assertIsNone(p13n.AlgorithmID.map_val_to_name(99))
@@ -623,7 +622,9 @@
def test_name_normalize(self):
self.assertEqual(p13n.AlgorithmID.name_normalize('Milenage'), 'Milenage')
self.assertEqual(p13n.AlgorithmID.name_normalize('milenage'), 'Milenage')
- self.assertEqual(p13n.AlgorithmID.name_normalize('usim-test'), 'usim_test')
+ self.assertEqual(p13n.AlgorithmID.name_normalize('usimtest'), 'usim-test')
+ self.assertEqual(p13n.AlgorithmID.name_normalize('usim_test'), 'usim-test')
+ self.assertEqual(p13n.AlgorithmID.name_normalize('USIM Test'), 'usim-test')
# --- clean_name_str ---
diff --git a/tests/unittests/xo/test_configurable_parameters b/tests/unittests/xo/test_configurable_parameters
index 0946979..b2c9a5b 100644
--- a/tests/unittests/xo/test_configurable_parameters
+++ b/tests/unittests/xo/test_configurable_parameters
@@ -86,33 +86,33 @@
ok: TS48v5_SAIP2.1A_NoBERTLV.der AlgorithmID(val='Milenage':str)
clean_val=1:int
- previous value: ['usim_test']
+ previous value: ['usim-test']
read_back_val={'Algorithm': 'Milenage'}:{str}
ok: TS48v5_SAIP2.1A_NoBERTLV.der AlgorithmID(val='TUAK':str)
clean_val=2:int
- previous value: ['usim_test']
+ previous value: ['usim-test']
read_back_val={'Algorithm': 'TUAK'}:{str}
ok: TS48v5_SAIP2.1A_NoBERTLV.der AlgorithmID(val='usim-test':str)
clean_val=3:int
- previous value: ['usim_test']
- read_back_val={'Algorithm': 'usim_test'}:{str}
+ previous value: ['usim-test']
+ read_back_val={'Algorithm': 'usim-test'}:{str}
ok: TS48v5_SAIP2.1A_NoBERTLV.der AlgorithmID(val=1:int)
clean_val=1:int
- previous value: ['usim_test']
+ previous value: ['usim-test']
read_back_val={'Algorithm': 'Milenage'}:{str}
ok: TS48v5_SAIP2.1A_NoBERTLV.der AlgorithmID(val=2:int)
clean_val=2:int
- previous value: ['usim_test']
+ previous value: ['usim-test']
read_back_val={'Algorithm': 'TUAK'}:{str}
ok: TS48v5_SAIP2.1A_NoBERTLV.der AlgorithmID(val=3:int)
clean_val=3:int
- previous value: ['usim_test']
- read_back_val={'Algorithm': 'usim_test'}:{str}
+ previous value: ['usim-test']
+ read_back_val={'Algorithm': 'usim-test'}:{str}
ok: TS48v5_SAIP2.1A_NoBERTLV.der K(val='01020304050607080910111213141516':str)
clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
@@ -961,33 +961,33 @@
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der AlgorithmID(val='Milenage':str)
clean_val=1:int
- previous value: ['usim_test']
+ previous value: ['usim-test']
read_back_val={'Algorithm': 'Milenage'}:{str}
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der AlgorithmID(val='TUAK':str)
clean_val=2:int
- previous value: ['usim_test']
+ previous value: ['usim-test']
read_back_val={'Algorithm': 'TUAK'}:{str}
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der AlgorithmID(val='usim-test':str)
clean_val=3:int
- previous value: ['usim_test']
- read_back_val={'Algorithm': 'usim_test'}:{str}
+ previous value: ['usim-test']
+ read_back_val={'Algorithm': 'usim-test'}:{str}
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der AlgorithmID(val=1:int)
clean_val=1:int
- previous value: ['usim_test']
+ previous value: ['usim-test']
read_back_val={'Algorithm': 'Milenage'}:{str}
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der AlgorithmID(val=2:int)
clean_val=2:int
- previous value: ['usim_test']
+ previous value: ['usim-test']
read_back_val={'Algorithm': 'TUAK'}:{str}
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der AlgorithmID(val=3:int)
clean_val=3:int
- previous value: ['usim_test']
- read_back_val={'Algorithm': 'usim_test'}:{str}
+ previous value: ['usim-test']
+ read_back_val={'Algorithm': 'usim-test'}:{str}
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der K(val='01020304050607080910111213141516':str)
clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/43864?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I690ceccf0ec7ef7067bcaa5cec1303cdaf0f78a4
Gerrit-Change-Number: 43864
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr(a)sysmocom.de>
1
0
[L] Change in pysim[master]: saip/personalization: add SUCI parameters for SAIP 2.3
by neels 09 Oct '26
by neels 09 Oct '26
09 Oct '26
neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/43865?usp=email )
Change subject: saip/personalization: add SUCI parameters for SAIP 2.3
......................................................................
saip/personalization: add SUCI parameters for SAIP 2.3
Change-Id: I7c727b10a850651a631211e4f168df5fec1600e5
Jenkins: skip-card-test
---
M pySim/esim/saip/personalization.py
M tests/unittests/test_configurable_parameters.py
M tests/unittests/xo/test_configurable_parameters
3 files changed, 358 insertions(+), 3 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/65/43865/1
diff --git a/pySim/esim/saip/personalization.py b/pySim/esim/saip/personalization.py
index e9ed709..a81c61f 100644
--- a/pySim/esim/saip/personalization.py
+++ b/pySim/esim/saip/personalization.py
@@ -19,13 +19,14 @@
import enum
import io
import re
+import json
from typing import List, Tuple, Generator, Optional
from construct.core import StreamError
from osmocom.tlv import camel_to_snake
from osmocom.utils import hexstr
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_31_102 import EF_AD, EF_UST, EF_Routing_Indicator, EF_SUCI_Calc_Info
from pySim.ts_51_011 import EF_SMSP
from pySim.esim.saip import param_source
from pySim.esim.saip import ProfileElement, ProfileElementSD, ProfileElementSequence
@@ -1175,3 +1176,202 @@
max_val = 255
example_input = '1'
default_source = param_source.ConstantSource
+
+
+class EfUstServiceParam(EnumParam):
+ """superclass for EF-UST service flag parameters"""
+ service_idx = 0
+ value_map = { 'enabled': True, 'disabled': False }
+ default_source = param_source.ConstantSource
+ example_input = sorted(value_map.keys())[0]
+
+ @classmethod
+ def apply_val(cls, pes: ProfileElementSequence, val):
+ for pe in pes.get_pes_for_type('usim'):
+ f_ust = pe.files['ef-ust']
+ ef_ust = EF_UST()
+ ust = ef_ust.decode_bin(f_ust.body)
+
+ ust[cls.service_idx]['activated'] = val
+
+ f_ust.body = ef_ust.encode_bin(ust)
+ pe.file2pe(f_ust)
+
+ @classmethod
+ def get_values_from_pes(cls, pes: ProfileElementSequence):
+ for pe in pes.get_pes_for_type('usim'):
+ f_ust = pe.files.get('ef-ust', None)
+ if not f_ust:
+ continue
+ ef_ust = EF_UST()
+ try:
+ ust = ef_ust.decode_bin(f_ust.body)
+
+ service_flag = ust[cls.service_idx]['activated']
+ yield { cls.name: cls.map_val_to_name(service_flag) }
+ except:
+ pass
+
+class SuciActive(EfUstServiceParam):
+ """EF-UST service nr 124: enable or disable the SUCI service."""
+ service_idx = 124
+ name = '5G-SUCI-active'
+ value_map = { 'SUCI-off': False, 'SUCI-on': True }
+ example_input = 'SUCI-on'
+
+class SuciInUsim(EfUstServiceParam):
+ """EF-UST service nr 125: calculate SUCI in UE or in USIM"""
+ service_idx = 125
+ name = '5G-SUCI-in-USIM'
+ value_map = { 'SUCI-in-UE': False, 'SUCI-in-USIM': True }
+ example_input = 'SUCI-in-USIM'
+
+class SuciRi(ConfigurableParameter):
+ """SUCI Routing Indicator as in section 4.4.11.11 of 3GPP TS 31.102"""
+ name = '5G-SUCI-RI'
+ allow_chars = '0123456789'
+ min_len = 1
+ max_len = 4
+ allow_types = (str,)
+ example_input = '0'
+ default_source = param_source.ConstantSource
+
+ KEY_RI = "routing_indicator"
+
+ @classmethod
+ def apply_val(cls, pes: ProfileElementSequence, val):
+ for pe in pes.get_pes_for_type('df-5gs'):
+ f_ri = pe.files.get('ef-routing-indicator', None)
+ if f_ri is None:
+ continue
+ ef_ri = EF_Routing_Indicator()
+ ri = ef_ri.decode_bin(f_ri.body)
+
+ ri[cls.KEY_RI] = str(val)
+
+ f_ri.body = ef_ri.encode_bin(ri)
+ pe.file2pe(f_ri)
+
+ @classmethod
+ def get_values_from_pes(cls, pes: ProfileElementSequence):
+ for pe in pes.get_pes_for_type('df-5gs'):
+ f_ri = pe.files.get('ef-routing-indicator', None)
+ if f_ri is None:
+ continue
+ ef_ri = EF_Routing_Indicator()
+ try:
+ ri = ef_ri.decode_bin(f_ri.body)
+ yield { cls.name: ri.get(cls.KEY_RI) }
+ except:
+ pass
+
+class SuciCalcInfoParameter(ConfigurableParameter):
+ """SUCI Calculation Information as in section 4.4.11.8 of 3GPP TS 31.102"""
+ name = '5G-SUCI-CalcInfo'
+ default_source = param_source.ConstantSource
+ allow_types = (str,)
+ max_len = 4096 # to indicate a large input field to UI renderers
+ example_input = '{"prot_scheme_id_list": [], "hnet_pubkey_list": []}'
+
+ PE_IN_UE = ("df-5gs", "ef-suci-calc-info")
+ PE_IN_USIM = ("df-saip", "ef-suci-calc-info-usim")
+ suci_calc_info_pe = None
+
+ @classmethod
+ def validate_val(cls, val):
+ val = super().validate_val(val)
+
+ if not val:
+ val = "{}"
+
+ # check that it is a dict something like
+ # {
+ # "prot_scheme_id_list": [
+ # {"priority": 0, "identifier": 2, "key_index": 1},
+ # {"priority": 1, "identifier": 1, "key_index": 2},
+ # ],
+ # "hnet_pubkey_list": [
+ # {"hnet_pubkey_identifier": 27,
+ # "hnet_pubkey": "0472DA71976234CE833A6907425867B82E074D44EF907DFB4B3E21C1C2256EBCD15A7DED52FCBB097A4ED250E036C7B9C8C7004C4EEDC4F068CD7BF8D3F900E3B4"},
+ # {"hnet_pubkey_identifier": 30,
+ # "hnet_pubkey": "5A8D38864820197C3394B92613B20B91633CBD897119273BF8E4A6F4EEC0A650"},
+ # ],
+ # }
+
+ try:
+ d = json.loads(val)
+ except json.decoder.JSONDecodeError as e:
+ raise ValueError(f"Cannot parse SUCI Calc Info: {e}") from e
+
+ KEY_PSI_LIST = 'prot_scheme_id_list'
+ KEY_HPK_LIST = 'hnet_pubkey_list'
+ KEYS_D = set((KEY_HPK_LIST, KEY_PSI_LIST))
+ KEYS_PSI = set(('identifier', 'key_index', 'priority'))
+ KEYS_HPK = set(('hnet_pubkey_identifier', 'hnet_pubkey'))
+
+ if not d:
+ d = { KEY_PSI_LIST: [], KEY_HPK_LIST: [] }
+
+ if not (isinstance(d, dict)
+ and set(d.keys()) == KEYS_D):
+ raise ValueError(f"Unexpected structure in SUCI Calc Info: expected dict with entries {KEYS_D}")
+
+ psi = d.get(KEY_PSI_LIST, None)
+ if not all((set(e.keys()) == KEYS_PSI) for e in psi):
+ raise ValueError("Unexpected structure in SUCI Calc Info:"
+ f" in {KEY_PSI_LIST}, expected dict with entries {KEYS_PSI}")
+
+ hpk = d.get(KEY_HPK_LIST, None)
+ if not all((set(e.keys()) == KEYS_HPK) for e in hpk):
+ raise ValueError("Unexpected structure in SUCI Calc Info:"
+ f" in {KEY_HPK_LIST}, expected dict with entries {KEYS_HPK}")
+ return d
+
+ @classmethod
+ def _apply_suci(cls, pes: ProfileElementSequence, val, pe_type="df-5gs", pe_file="ef-suci-calc-info"):
+ for pe in pes.get_pes_for_type(pe_type):
+ f_sucici = pe.files.get(pe_file, None)
+ if not f_sucici:
+ continue
+ ef_sucici = EF_SUCI_Calc_Info()
+ f_sucici.body = ef_sucici.encode_bin(val)
+ pe.file2pe(f_sucici)
+
+ @classmethod
+ def apply_val(cls, pes: ProfileElementSequence, val):
+ cls._apply_suci(pes, val, *cls.suci_calc_info_pe)
+
+ @classmethod
+ def _get_suci(cls, pes: ProfileElementSequence, pe_type="df-5gs", pe_file="ef-suci-calc-info"):
+ for pe in pes.get_pes_for_type(pe_type):
+ f_sucici = pe.files.get(pe_file, None)
+ if not f_sucici:
+ continue
+ ef_sucici = EF_SUCI_Calc_Info()
+ sucici = ef_sucici.decode_bin(f_sucici.body)
+
+ if not sucici:
+ sucici = {}
+
+ # normalize to string (bytes cannot go into json)
+ for hnet_pubkey in sucici.get('hnet_pubkey_list', ()):
+ val = hnet_pubkey['hnet_pubkey']
+ if isinstance(val, bytes):
+ val = b2h(val)
+ hnet_pubkey['hnet_pubkey'] = val
+
+ yield { cls.name: json.dumps(sucici) }
+
+ @classmethod
+ def get_values_from_pes(cls, pes: ProfileElementSequence):
+ yield from cls._get_suci(pes, *cls.suci_calc_info_pe)
+
+class SuciCalcInfoUe(SuciCalcInfoParameter):
+ """SUCI Calculation Information as in section 4.4.11.8 of 3GPP TS 31.102, readable by UE (DF-5GS)"""
+ name = '5G-SUCI-CalcInfo-UE'
+ suci_calc_info_pe = SuciCalcInfoParameter.PE_IN_UE
+
+class SuciCalcInfoUsim(SuciCalcInfoParameter):
+ """SUCI Calculation Information as in section 4.4.11.8 of 3GPP TS 31.102, readable only by USIM (DF-SAIP)"""
+ name = '5G-SUCI-CalcInfo-USIM'
+ suci_calc_info_pe = SuciCalcInfoParameter.PE_IN_USIM
diff --git a/tests/unittests/test_configurable_parameters.py b/tests/unittests/test_configurable_parameters.py
index 0115640..33c7647 100755
--- a/tests/unittests/test_configurable_parameters.py
+++ b/tests/unittests/test_configurable_parameters.py
@@ -21,6 +21,7 @@
import io
import sys
import unittest
+import json
from importlib import resources
from osmocom.utils import hexstr
from pySim.esim.saip import ProfileElementSequence
@@ -58,11 +59,15 @@
)
class Paramtest:
- def __init__(self, param_cls, val, expect_val, expect_clean_val=None):
+ iff_present_default = False
+ def __init__(self, param_cls, val, expect_val, expect_clean_val=None, iff_present=None):
self.param_cls = param_cls
self.val = val
self.expect_clean_val = expect_clean_val
self.expect_val = expect_val
+ if iff_present is None:
+ iff_present = Paramtest.iff_present_default
+ self.iff_present = iff_present
param_tests = [
Paramtest(param_cls=p13n.Imsi, val='123456',
@@ -273,8 +278,61 @@
val=3,
expect_clean_val=3,
expect_val='3'),
+ ]
- ]
+ Paramtest.iff_present_default = True
+
+ sucici = {
+ "prot_scheme_id_list": [
+ {"priority": 0, "identifier": 2, "key_index": 1},
+ {"priority": 1, "identifier": 1, "key_index": 2},
+ ],
+ "hnet_pubkey_list": [
+ {"hnet_pubkey_identifier": 27,
+ "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"},
+ {"hnet_pubkey_identifier": 30,
+ "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"},
+ ],
+ }
+
+ param_tests.extend([
+ Paramtest(param_cls=p13n.SuciActive, val='SUCI-on',
+ expect_clean_val=True,
+ expect_val={'5G-SUCI-active': 'SUCI-on'}),
+ Paramtest(param_cls=p13n.SuciActive, val='SUCI-off',
+ expect_clean_val=False,
+ expect_val={'5G-SUCI-active': 'SUCI-off'}),
+
+ Paramtest(param_cls=p13n.SuciInUsim, val='SUCI-in-UE',
+ expect_clean_val=False,
+ expect_val={'5G-SUCI-in-USIM': 'SUCI-in-UE'}),
+ Paramtest(param_cls=p13n.SuciInUsim, val='SUCI-in-USIM',
+ expect_clean_val=True,
+ expect_val={'5G-SUCI-in-USIM': 'SUCI-in-USIM'}),
+
+ Paramtest(param_cls=p13n.SuciRi, val='123',
+ expect_clean_val='123',
+ expect_val={'5G-SUCI-RI': '123'}),
+ Paramtest(param_cls=p13n.SuciRi, val='0',
+ expect_clean_val='0',
+ expect_val={'5G-SUCI-RI': '0'}),
+ Paramtest(param_cls=p13n.SuciRi, val='9999',
+ expect_clean_val='9999',
+ expect_val={'5G-SUCI-RI': '9999'}),
+
+ Paramtest(param_cls=p13n.SuciCalcInfoUe,
+ val=json.dumps(sucici),
+ expect_clean_val=sucici,
+ expect_val={'5G-SUCI-CalcInfo-UE': json.dumps(sucici)}),
+
+ Paramtest(param_cls=p13n.SuciCalcInfoUsim,
+ val=json.dumps(sucici),
+ expect_clean_val=sucici,
+ expect_val={'5G-SUCI-CalcInfo-USIM': json.dumps(sucici)}),
+
+ ])
+
+ Paramtest.iff_present_default = False
for sdkey_cls in (
# thin out the number of tests, as a compromise between completeness and test runtime
@@ -391,6 +449,13 @@
found = list((t.param_cls.get_value_from_pes(pes) or {}).values())
testlog.append(f"previous value: {found}")
+ if t.iff_present and not found:
+ testlog.append("skipping, param not in template.")
+ output = "\nskip: " + "\n ".join(testlog)
+ outputs.append(output)
+ print(output)
+ continue
+
try:
param.apply(pes)
except ValueError as e:
diff --git a/tests/unittests/xo/test_configurable_parameters b/tests/unittests/xo/test_configurable_parameters
index b2c9a5b..822e33e 100644
--- a/tests/unittests/xo/test_configurable_parameters
+++ b/tests/unittests/xo/test_configurable_parameters
@@ -214,6 +214,51 @@
previous value: ['2']
read_back_val={'MNC-LEN': '3'}:{str}
+ok: TS48v5_SAIP2.1A_NoBERTLV.der SuciActive(val='SUCI-on':str)
+ clean_val=True:bool
+ previous value: ['SUCI-on']
+ read_back_val={'5G-SUCI-active': 'SUCI-on'}:{str}
+
+ok: TS48v5_SAIP2.1A_NoBERTLV.der SuciActive(val='SUCI-off':str)
+ clean_val=False:bool
+ previous value: ['SUCI-on']
+ read_back_val={'5G-SUCI-active': 'SUCI-off'}:{str}
+
+ok: TS48v5_SAIP2.1A_NoBERTLV.der SuciInUsim(val='SUCI-in-UE':str)
+ clean_val=False:bool
+ previous value: ['SUCI-in-UE']
+ read_back_val={'5G-SUCI-in-USIM': 'SUCI-in-UE'}:{str}
+
+ok: TS48v5_SAIP2.1A_NoBERTLV.der SuciInUsim(val='SUCI-in-USIM':str)
+ clean_val=True:bool
+ previous value: ['SUCI-in-UE']
+ read_back_val={'5G-SUCI-in-USIM': 'SUCI-in-USIM'}:{str}
+
+skip: TS48v5_SAIP2.1A_NoBERTLV.der SuciRi(val='123':str)
+ clean_val='123':str
+ previous value: []
+ skipping, param not in template.
+
+skip: TS48v5_SAIP2.1A_NoBERTLV.der SuciRi(val='0':str)
+ clean_val='0':str
+ previous value: []
+ skipping, param not in template.
+
+skip: TS48v5_SAIP2.1A_NoBERTLV.der SuciRi(val='9999':str)
+ clean_val='9999':str
+ previous value: []
+ skipping, param not in template.
+
+skip: TS48v5_SAIP2.1A_NoBERTLV.der SuciCalcInfoUe(val='{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}':str)
+ clean_val={'prot_scheme_id_list': [{'priority': 0, 'identifier': 2, 'key_index': 1}, {'priority': 1, 'identifier': 1, 'key_index': 2}], 'hnet_pubkey_list': [{'hnet_pubkey_identifier': 27, 'hnet_pubkey': '0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4'}, {'hnet_pubkey_identifier': 30, 'hnet_pubkey': '5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650'}]}:{list, list}
+ previous value: []
+ skipping, param not in template.
+
+skip: TS48v5_SAIP2.1A_NoBERTLV.der SuciCalcInfoUsim(val='{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}':str)
+ clean_val={'prot_scheme_id_list': [{'priority': 0, 'identifier': 2, 'key_index': 1}, {'priority': 1, 'identifier': 1, 'key_index': 2}], 'hnet_pubkey_list': [{'hnet_pubkey_identifier': 27, 'hnet_pubkey': '0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4'}, {'hnet_pubkey_identifier': 30, 'hnet_pubkey': '5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650'}]}:{list, list}
+ previous value: []
+ skipping, param not in template.
+
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
previous value: []
@@ -1089,6 +1134,51 @@
previous value: ['2']
read_back_val={'MNC-LEN': '3'}:{str}
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der SuciActive(val='SUCI-on':str)
+ clean_val=True:bool
+ previous value: ['SUCI-on']
+ read_back_val={'5G-SUCI-active': 'SUCI-on'}:{str}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der SuciActive(val='SUCI-off':str)
+ clean_val=False:bool
+ previous value: ['SUCI-on']
+ read_back_val={'5G-SUCI-active': 'SUCI-off'}:{str}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der SuciInUsim(val='SUCI-in-UE':str)
+ clean_val=False:bool
+ previous value: ['SUCI-in-USIM']
+ read_back_val={'5G-SUCI-in-USIM': 'SUCI-in-UE'}:{str}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der SuciInUsim(val='SUCI-in-USIM':str)
+ clean_val=True:bool
+ previous value: ['SUCI-in-USIM']
+ read_back_val={'5G-SUCI-in-USIM': 'SUCI-in-USIM'}:{str}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der SuciRi(val='123':str)
+ clean_val='123':str
+ previous value: ['0']
+ read_back_val={'5G-SUCI-RI': '123'}:{hexstr}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der SuciRi(val='0':str)
+ clean_val='0':str
+ previous value: ['0']
+ read_back_val={'5G-SUCI-RI': '0'}:{hexstr}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der SuciRi(val='9999':str)
+ clean_val='9999':str
+ previous value: ['0']
+ read_back_val={'5G-SUCI-RI': '9999'}:{hexstr}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der SuciCalcInfoUe(val='{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}':str)
+ clean_val={'prot_scheme_id_list': [{'priority': 0, 'identifier': 2, 'key_index': 1}, {'priority': 1, 'identifier': 1, 'key_index': 2}], 'hnet_pubkey_list': [{'hnet_pubkey_identifier': 27, 'hnet_pubkey': '0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4'}, {'hnet_pubkey_identifier': 30, 'hnet_pubkey': '5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650'}]}:{list, list}
+ previous value: ['{"prot_scheme_id_list": [{"priority": 0, "identifier": 0, "key_index": 0}], "hnet_pubkey_list": []}']
+ read_back_val={'5G-SUCI-CalcInfo-UE': '{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}'}:{str}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der SuciCalcInfoUsim(val='{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}':str)
+ clean_val={'prot_scheme_id_list': [{'priority': 0, 'identifier': 2, 'key_index': 1}, {'priority': 1, 'identifier': 1, 'key_index': 2}], 'hnet_pubkey_list': [{'hnet_pubkey_identifier': 27, 'hnet_pubkey': '0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4'}, {'hnet_pubkey_identifier': 30, 'hnet_pubkey': '5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650'}]}:{list, list}
+ previous value: ['{"prot_scheme_id_list": [{"priority": 0, "identifier": 0, "key_index": 0}], "hnet_pubkey_list": []}']
+ read_back_val={'5G-SUCI-CalcInfo-USIM': '{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}'}:{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
previous value: []
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/43865?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I7c727b10a850651a631211e4f168df5fec1600e5
Gerrit-Change-Number: 43865
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr(a)sysmocom.de>
1
0
[L] Change in pysim[master]: saip/personalization: add SUCI parameters for SAIP 2.1 (GFM)
by neels 09 Oct '26
by neels 09 Oct '26
09 Oct '26
neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/43869?usp=email )
Change subject: saip/personalization: add SUCI parameters for SAIP 2.1 (GFM)
......................................................................
saip/personalization: add SUCI parameters for SAIP 2.1 (GFM)
Add GfmSuciCalcInfo, GfmSuciRi, and test.
Change-Id: I1b69debf5992aa715171b43b30864dc152dc556f
Jenkins: skip-card-test
---
M pySim/esim/saip/personalization.py
M tests/unittests/test_configurable_parameters.py
M tests/unittests/xo/test_configurable_parameters
3 files changed, 244 insertions(+), 9 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/69/43869/1
diff --git a/pySim/esim/saip/personalization.py b/pySim/esim/saip/personalization.py
index d6fa08d..479df58 100644
--- a/pySim/esim/saip/personalization.py
+++ b/pySim/esim/saip/personalization.py
@@ -26,7 +26,7 @@
from osmocom.tlv import camel_to_snake
from osmocom.utils import hexstr
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, EF_UST, EF_Routing_Indicator, EF_SUCI_Calc_Info
+from pySim.ts_31_102 import EF_AD, EF_UST, EF_Routing_Indicator, EF_SUCI_Calc_Info, DF_USIM_5GS
from pySim.ts_51_011 import EF_SMSP
from pySim.esim.saip import param_source
from pySim.esim.saip import ProfileElement, ProfileElementSD, ProfileElementSequence
@@ -34,6 +34,11 @@
from pySim.esim.saip import SecurityDomainKey, SecurityDomainKeyComponent
from pySim.global_platform import KeyUsageQualifier, KeyType
+# optimization: instantiate class instance to get the fid only once.
+file_path_df_5gs = bytes.fromhex(DF_USIM_5GS().fid)
+fid_ri = bytes.fromhex(EF_Routing_Indicator().fid)
+fid_sucici = bytes.fromhex(EF_SUCI_Calc_Info().fid)
+
def unrpad(s: hexstr, c='f') -> hexstr:
return hexstr(s.rstrip(c))
@@ -1350,6 +1355,21 @@
def apply_val(cls, pes: ProfileElementSequence, val):
cls._apply_suci(pes, val, *cls.suci_calc_info_pe)
+ @staticmethod
+ def normalize_sucici(sucici:dict):
+ """Normalize the CalcInfo dict so it can be json encoded:
+ convert bytes to hex strings."""
+ if not sucici:
+ sucici = {}
+
+ for hnet_pubkey in sucici.get('hnet_pubkey_list', ()):
+ val = hnet_pubkey['hnet_pubkey']
+ if isinstance(val, bytes):
+ val = b2h(val)
+ hnet_pubkey['hnet_pubkey'] = val
+
+ return sucici
+
@classmethod
def _get_suci(cls, pes: ProfileElementSequence, pe_type="df-5gs", pe_file="ef-suci-calc-info"):
for pe in pes.get_pes_for_type(pe_type):
@@ -1359,15 +1379,8 @@
ef_sucici = EF_SUCI_Calc_Info()
sucici = ef_sucici.decode_bin(f_sucici.body)
- if not sucici:
- sucici = {}
-
# normalize to string (bytes cannot go into json)
- for hnet_pubkey in sucici.get('hnet_pubkey_list', ()):
- val = hnet_pubkey['hnet_pubkey']
- if isinstance(val, bytes):
- val = b2h(val)
- hnet_pubkey['hnet_pubkey'] = val
+ sucici = cls.normalize_sucici(sucici)
yield { cls.name: json.dumps(sucici) }
@@ -1385,6 +1398,153 @@
name = '5G-SUCI-CalcInfo-USIM'
suci_calc_info_pe = SuciCalcInfoParameter.PE_IN_USIM
+def gfm_find(pes: ProfileElementSequence, file_path:bytes, ef_fid:bytes):
+ """look through genericFileManagement PE and return the fmc list with start and end indexes as
+ (fmc_list, first_idx, after_last_idx)
+ so that fmc_list[first_idx:after_last_idx] is the slice of file management commands relevant to the given
+ file_path/ef_fid.
+ """
+ for pe in pes.get_pes_for_type('genericFileManagement'):
+ path_match = False
+ creating_fid = False
+
+ for fmc in pe.decoded['fileManagementCMD']:
+ first = None
+ last = None
+
+ for idx in range(len(fmc)):
+ cmd, arg = fmc[idx]
+
+ if cmd == 'filePath':
+ path_match = (arg == file_path)
+ if not path_match:
+ creating_fid = False
+ elif path_match and cmd == 'createFCP':
+ creating_fid = (arg.get('fileID') == ef_fid)
+ if creating_fid:
+ if first is None:
+ first = idx
+ last = idx
+ first = min(first, idx)
+ last = max(last, idx)
+
+ if first is not None:
+ yield fmc, first, last + 1
+
+# genericFileManagement 5G params
+
+def pes_get_adf_fid(pes:ProfileElementSequence, naa_name="usim", adf_name="adf-usim"):
+ adf = pes.get_pe_for_type(naa_name)
+ return adf.decoded[adf_name][0][1]['fileID']
+
+def mk_adf_df_path(pes, naa:str, adf:str, file_path:bytes) -> bytes:
+ adf_file_id = pes_get_adf_fid(pes, naa, adf)
+ return b''.join((adf_file_id, file_path))
+
+def gfm_get_file_content(pes: ProfileElementSequence, naa:str, adf:str, file_path:bytes, ef_fid:bytes) -> bytes:
+ '''find a given file in the genericFileManagement section, and return the bytes from the first fillFileContent
+ item.
+ TODO: implement File.from_gfm() and return the full resulting bytes?
+ '''
+ adf_df_path = mk_adf_df_path(pes, naa, adf, file_path)
+
+ data = []
+ for fmc, first_idx, after_last_idx in gfm_find(pes, adf_df_path, ef_fid):
+ assert fmc[first_idx][0] == 'createFCP'
+ assert after_last_idx > first_idx
+
+ idx = first_idx + 1
+ while idx < after_last_idx:
+ if fmc[idx][0] == 'fillFileContent':
+ data.append(fmc[idx][1])
+ idx += 1
+
+ return data
+
+def gfm_set_file_content(pes: ProfileElementSequence, naa:str, adf:str, file_path:bytes, ef_fid:bytes, file_content:bytes) -> int:
+ adf_df_path = mk_adf_df_path(pes, naa, adf, file_path)
+
+ found = 0
+ for fmc, first_idx, after_last_idx in gfm_find(pes, adf_df_path, ef_fid):
+ assert fmc[first_idx][0] == 'createFCP'
+ assert after_last_idx > first_idx
+
+ new_fmc = [
+ fmc[first_idx],
+ ('fillFileContent', file_content),
+ ]
+ new_fmc[0][1]['efFileSize'] = bytes((len(file_content), ))
+
+ fmc[first_idx:after_last_idx] = new_fmc
+
+ found += 1
+ return found
+
+class GfmSuciRi(SuciRi):
+ """SUCI Routing Indicator as in section 4.4.11.11 of 3GPP TS 31.102,
+ applied via General File Management. Intended for SAIP 2.1 profiles."""
+ name = 'GFM-5G-SUCI-RI'
+
+ @classmethod
+ def apply_val(cls, pes: ProfileElementSequence, val):
+ ri = {
+ "routing_indicator": str(val),
+ "rfu": "ffff"
+ }
+ ef_ri = EF_Routing_Indicator()
+ found = gfm_set_file_content(pes, 'usim', 'adf-usim', file_path_df_5gs, fid_ri,
+ ef_ri.encode_bin(ri))
+ if not found:
+ raise ValueError(f"No target file found, Cannot apply {cls.name} = {ri}")
+
+ data = gfm_get_file_content(pes, 'usim', 'adf-usim', file_path_df_5gs, fid_ri)
+ val = ef_ri.decode_bin(b''.join(data))
+
+ @classmethod
+ def get_values_from_pes(cls, pes: ProfileElementSequence):
+ data = gfm_get_file_content(pes, 'usim', 'adf-usim', file_path_df_5gs, fid_ri)
+ if not data:
+ return
+
+ data = b''.join(data)
+ if not data:
+ return
+
+ ef_ri = EF_Routing_Indicator()
+ ri = ef_ri.decode_bin(data)
+ yield { cls.name: ri.get(cls.KEY_RI) }
+
+class GfmSuciCalcInfoUe(SuciCalcInfoUe):
+ """SUCI Calculation Information as in section 4.4.11.8 of 3GPP TS 31.102, readable by UE (DF-5GS),
+ applied via General File Management. Intended for SAIP 2.1 profiles."""
+ name = 'GFM-5G-SUCI-CalcInfo-UE'
+
+ @classmethod
+ def apply_val(cls, pes: ProfileElementSequence, val):
+ if not isinstance(val, dict):
+ raise ValueError("val should be a dict, after 'val = SuciCalcInfoParameter.validate_val(val)'")
+
+ ef_sucici = EF_SUCI_Calc_Info()
+ body = ef_sucici.encode_bin(val)
+ gfm_set_file_content(pes, 'usim', 'adf-usim', file_path_df_5gs, fid_sucici,
+ body)
+
+ @classmethod
+ def get_values_from_pes(cls, pes: ProfileElementSequence):
+ data = gfm_get_file_content(pes, 'usim', 'adf-usim', file_path_df_5gs, fid_sucici)
+ if not data:
+ return
+
+ data = b''.join(data)
+ if not data:
+ return
+
+ ef_sucici = EF_SUCI_Calc_Info()
+ sucici = ef_sucici.decode_bin(data)
+ sucici = cls.normalize_sucici(sucici)
+ yield { cls.name: json.dumps(sucici) }
+
+
class EuiccMandatoryServiceParam(EnumParam):
"""superclass for managing items of the ProfileHeader / eUICC-Mandatory-services ServicesList"""
service_name = None
diff --git a/tests/unittests/test_configurable_parameters.py b/tests/unittests/test_configurable_parameters.py
index 15f434a..54fe4fc 100755
--- a/tests/unittests/test_configurable_parameters.py
+++ b/tests/unittests/test_configurable_parameters.py
@@ -358,6 +358,21 @@
expect_clean_val=sucici,
expect_val={'5G-SUCI-CalcInfo-USIM': json.dumps(sucici)}),
+ Paramtest(param_cls=p13n.GfmSuciRi, val='123',
+ expect_clean_val='123',
+ expect_val={'GFM-5G-SUCI-RI': '123'}),
+ Paramtest(param_cls=p13n.GfmSuciRi, val='0',
+ expect_clean_val='0',
+ expect_val={'GFM-5G-SUCI-RI': '0'}),
+ Paramtest(param_cls=p13n.GfmSuciRi, val='9999',
+ expect_clean_val='9999',
+ expect_val={'GFM-5G-SUCI-RI': '9999'}),
+
+ Paramtest(param_cls=p13n.GfmSuciCalcInfoUe,
+ val=json.dumps(sucici),
+ expect_clean_val=sucici,
+ expect_val={'GFM-5G-SUCI-CalcInfo-UE': json.dumps(sucici)}),
+
])
Paramtest.iff_present_default = False
diff --git a/tests/unittests/xo/test_configurable_parameters b/tests/unittests/xo/test_configurable_parameters
index e6b87aa..422a347 100644
--- a/tests/unittests/xo/test_configurable_parameters
+++ b/tests/unittests/xo/test_configurable_parameters
@@ -289,6 +289,26 @@
previous value: []
skipping, param not in template.
+ok: SAIP2.1_gfmsuci.der GfmSuciRi(val='123':str)
+ clean_val='123':str
+ previous value: ['0']
+ read_back_val={'GFM-5G-SUCI-RI': '123'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der GfmSuciRi(val='0':str)
+ clean_val='0':str
+ previous value: ['0']
+ read_back_val={'GFM-5G-SUCI-RI': '0'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der GfmSuciRi(val='9999':str)
+ clean_val='9999':str
+ previous value: ['0']
+ read_back_val={'GFM-5G-SUCI-RI': '9999'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der GfmSuciCalcInfoUe(val='{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}':str)
+ clean_val={'prot_scheme_id_list': [{'priority': 0, 'identifier': 2, 'key_index': 1}, {'priority': 1, 'identifier': 1, 'key_index': 2}], 'hnet_pubkey_list': [{'hnet_pubkey_identifier': 27, 'hnet_pubkey': '0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4'}, {'hnet_pubkey_identifier': 30, 'hnet_pubkey': '5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650'}]}:{list, list}
+ previous value: ['{}']
+ read_back_val={'GFM-5G-SUCI-CalcInfo-UE': '{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}'}:{str}
+
ok: SAIP2.1_gfmsuci.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
previous value: []
@@ -1239,6 +1259,26 @@
previous value: []
skipping, param not in template.
+skip: TS48v5_SAIP2.1A_NoBERTLV.der GfmSuciRi(val='123':str)
+ clean_val='123':str
+ previous value: []
+ skipping, param not in template.
+
+skip: TS48v5_SAIP2.1A_NoBERTLV.der GfmSuciRi(val='0':str)
+ clean_val='0':str
+ previous value: []
+ skipping, param not in template.
+
+skip: TS48v5_SAIP2.1A_NoBERTLV.der GfmSuciRi(val='9999':str)
+ clean_val='9999':str
+ previous value: []
+ skipping, param not in template.
+
+skip: TS48v5_SAIP2.1A_NoBERTLV.der GfmSuciCalcInfoUe(val='{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}':str)
+ clean_val={'prot_scheme_id_list': [{'priority': 0, 'identifier': 2, 'key_index': 1}, {'priority': 1, 'identifier': 1, 'key_index': 2}], 'hnet_pubkey_list': [{'hnet_pubkey_identifier': 27, 'hnet_pubkey': '0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4'}, {'hnet_pubkey_identifier': 30, 'hnet_pubkey': '5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650'}]}:{list, list}
+ previous value: []
+ skipping, param not in template.
+
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
previous value: []
@@ -2189,6 +2229,26 @@
previous value: ['{"prot_scheme_id_list": [{"priority": 0, "identifier": 0, "key_index": 0}], "hnet_pubkey_list": []}']
read_back_val={'5G-SUCI-CalcInfo-USIM': '{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}'}:{str}
+skip: TS48v5_SAIP2.3_BERTLV_SUCI.der GfmSuciRi(val='123':str)
+ clean_val='123':str
+ previous value: []
+ skipping, param not in template.
+
+skip: TS48v5_SAIP2.3_BERTLV_SUCI.der GfmSuciRi(val='0':str)
+ clean_val='0':str
+ previous value: []
+ skipping, param not in template.
+
+skip: TS48v5_SAIP2.3_BERTLV_SUCI.der GfmSuciRi(val='9999':str)
+ clean_val='9999':str
+ previous value: []
+ skipping, param not in template.
+
+skip: TS48v5_SAIP2.3_BERTLV_SUCI.der GfmSuciCalcInfoUe(val='{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}':str)
+ clean_val={'prot_scheme_id_list': [{'priority': 0, 'identifier': 2, 'key_index': 1}, {'priority': 1, 'identifier': 1, 'key_index': 2}], 'hnet_pubkey_list': [{'hnet_pubkey_identifier': 27, 'hnet_pubkey': '0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4'}, {'hnet_pubkey_identifier': 30, 'hnet_pubkey': '5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650'}]}:{list, list}
+ previous value: []
+ skipping, param not in template.
+
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
previous value: []
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/43869?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I1b69debf5992aa715171b43b30864dc152dc556f
Gerrit-Change-Number: 43869
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr(a)sysmocom.de>
1
0
[M] Change in pysim[master]: saip/personalization: add EuiccMandatoryServiceParam for 3 services
by neels 09 Oct '26
by neels 09 Oct '26
09 Oct '26
neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/43867?usp=email )
Change subject: saip/personalization: add EuiccMandatoryServiceParam for 3 services
......................................................................
saip/personalization: add EuiccMandatoryServiceParam for 3 services
Even though the eUICC-Mandatory-services are always set by
saip.PES.rebuild_mandatory_services(), these new params are useful to
audit and validate the values as present in a given DER.
Change-Id: Icddeb2488c4a024c6ee5afcc1b6c8cc0e436c43c
Jenkins: skip-card-test
---
M pySim/esim/saip/__init__.py
M pySim/esim/saip/personalization.py
M tests/unittests/test_configurable_parameters.py
M tests/unittests/xo/test_configurable_parameters
4 files changed, 137 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/67/43867/1
diff --git a/pySim/esim/saip/__init__.py b/pySim/esim/saip/__init__.py
index 356800b..01302df 100644
--- a/pySim/esim/saip/__init__.py
+++ b/pySim/esim/saip/__init__.py
@@ -1517,8 +1517,11 @@
def mandatory_service_add(self, service_name):
self.decoded['eUICC-Mandatory-services'][service_name] = None
+ def mandatory_service_present(self, service_name):
+ return service_name in self.decoded['eUICC-Mandatory-services'].keys()
+
def mandatory_service_remove(self, service_name):
- if service_name in self.decoded['eUICC-Mandatory-services'].keys():
+ if self.mandatory_service_present(service_name):
del self.decoded['eUICC-Mandatory-services'][service_name]
else:
raise ValueError("service not in eUICC-Mandatory-services list, cannot remove")
diff --git a/pySim/esim/saip/personalization.py b/pySim/esim/saip/personalization.py
index a3347cb..d6fa08d 100644
--- a/pySim/esim/saip/personalization.py
+++ b/pySim/esim/saip/personalization.py
@@ -30,6 +30,7 @@
from pySim.ts_51_011 import EF_SMSP
from pySim.esim.saip import param_source
from pySim.esim.saip import ProfileElement, ProfileElementSD, ProfileElementSequence
+from pySim.esim.saip import ProfileElementHeader
from pySim.esim.saip import SecurityDomainKey, SecurityDomainKeyComponent
from pySim.global_platform import KeyUsageQualifier, KeyType
@@ -1383,3 +1384,48 @@
"""SUCI Calculation Information as in section 4.4.11.8 of 3GPP TS 31.102, readable only by USIM (DF-SAIP)"""
name = '5G-SUCI-CalcInfo-USIM'
suci_calc_info_pe = SuciCalcInfoParameter.PE_IN_USIM
+
+class EuiccMandatoryServiceParam(EnumParam):
+ """superclass for managing items of the ProfileHeader / eUICC-Mandatory-services ServicesList"""
+ service_name = None
+ value_map = { 'mandatory': True, 'optional': False }
+ default_source = param_source.ConstantSource
+ example_input = sorted(value_map.keys())[0]
+
+ @classmethod
+ def apply_val(cls, pes: ProfileElementSequence, val):
+ for pe in pes.get_pes_for_type('header'):
+ assert isinstance(pe, ProfileElementHeader)
+ if val:
+ pe.mandatory_service_add(cls.service_name)
+ else:
+ # explicitly check to avoid exception when the service is already not-present
+ if pe.mandatory_service_present(cls.service_name):
+ pe.mandatory_service_remove(cls.service_name)
+
+ @classmethod
+ def get_values_from_pes(cls, pes: ProfileElementSequence):
+ for pe in pes.get_pes_for_type('header'):
+ assert isinstance(pe, ProfileElementHeader)
+ val = pe.mandatory_service_present(cls.service_name)
+ yield { cls.name: cls.map_val_to_name(val) }
+
+class EuiccMandatoryServiceGetIdentity(EuiccMandatoryServiceParam):
+ """eUICC Mandatory Services: get-identity. The eUICC must be capable of providing a 5G identity using SUCI-CalcInfo
+ located in the USIM's DF-SAIP, see parameter 5G-SUCI-CalcInfo-USIM."""
+ name = '5G-eUICC-get-identity'
+ service_name = 'get-identity'
+
+class EuiccMandatoryServiceProfileA(EuiccMandatoryServiceParam):
+ """eUICC Mandatory Services: profile-a-x25519. The eUICC must be able to estblish a 5G identity using an X25519 key,
+ as provided in a profile-A ("identifier": 1) key in SUCI-CalcInfo located in the USIM's DF-SAIP, see parameter
+ 5G-SUCI-CalcInfo-USIM."""
+ name = '5G-eUICC-profile-a-x25519'
+ service_name = 'profile-a-x25519'
+
+class EuiccMandatoryServiceProfileB(EuiccMandatoryServiceParam):
+ """eUICC Mandatory Services: profile-b-p256. The eUICC must be able to estblish a 5G identity using a P256 key, as
+ provided in a profile-B ("identifier": 2) key in SUCI-CalcInfo located in the USIM's DF-SAIP, see parameter
+ 5G-SUCI-CalcInfo-USIM."""
+ name = '5G-eUICC-profile-b-p256'
+ service_name = 'profile-b-p256'
diff --git a/tests/unittests/test_configurable_parameters.py b/tests/unittests/test_configurable_parameters.py
index 33c7647..d541925 100755
--- a/tests/unittests/test_configurable_parameters.py
+++ b/tests/unittests/test_configurable_parameters.py
@@ -278,6 +278,33 @@
val=3,
expect_clean_val=3,
expect_val='3'),
+
+ Paramtest(param_cls=p13n.EuiccMandatoryServiceGetIdentity,
+ val='mandatory',
+ expect_clean_val=True,
+ expect_val='mandatory'),
+ Paramtest(param_cls=p13n.EuiccMandatoryServiceGetIdentity,
+ val='optional',
+ expect_clean_val=False,
+ expect_val='optional'),
+
+ Paramtest(param_cls=p13n.EuiccMandatoryServiceProfileA,
+ val='mandatory',
+ expect_clean_val=True,
+ expect_val='mandatory'),
+ Paramtest(param_cls=p13n.EuiccMandatoryServiceProfileA,
+ val='optional',
+ expect_clean_val=False,
+ expect_val='optional'),
+
+ Paramtest(param_cls=p13n.EuiccMandatoryServiceProfileB,
+ val='mandatory',
+ expect_clean_val=True,
+ expect_val='mandatory'),
+ Paramtest(param_cls=p13n.EuiccMandatoryServiceProfileB,
+ val='optional',
+ expect_clean_val=False,
+ expect_val='optional'),
]
Paramtest.iff_present_default = True
diff --git a/tests/unittests/xo/test_configurable_parameters b/tests/unittests/xo/test_configurable_parameters
index 822e33e..0866229 100644
--- a/tests/unittests/xo/test_configurable_parameters
+++ b/tests/unittests/xo/test_configurable_parameters
@@ -214,6 +214,36 @@
previous value: ['2']
read_back_val={'MNC-LEN': '3'}:{str}
+ok: TS48v5_SAIP2.1A_NoBERTLV.der EuiccMandatoryServiceGetIdentity(val='mandatory':str)
+ clean_val=True:bool
+ previous value: ['optional']
+ read_back_val={'5G-eUICC-get-identity': 'mandatory'}:{str}
+
+ok: TS48v5_SAIP2.1A_NoBERTLV.der EuiccMandatoryServiceGetIdentity(val='optional':str)
+ clean_val=False:bool
+ previous value: ['optional']
+ read_back_val={'5G-eUICC-get-identity': 'optional'}:{str}
+
+ok: TS48v5_SAIP2.1A_NoBERTLV.der EuiccMandatoryServiceProfileA(val='mandatory':str)
+ clean_val=True:bool
+ previous value: ['optional']
+ read_back_val={'5G-eUICC-profile-a-x25519': 'mandatory'}:{str}
+
+ok: TS48v5_SAIP2.1A_NoBERTLV.der EuiccMandatoryServiceProfileA(val='optional':str)
+ clean_val=False:bool
+ previous value: ['optional']
+ read_back_val={'5G-eUICC-profile-a-x25519': 'optional'}:{str}
+
+ok: TS48v5_SAIP2.1A_NoBERTLV.der EuiccMandatoryServiceProfileB(val='mandatory':str)
+ clean_val=True:bool
+ previous value: ['optional']
+ read_back_val={'5G-eUICC-profile-b-p256': 'mandatory'}:{str}
+
+ok: TS48v5_SAIP2.1A_NoBERTLV.der EuiccMandatoryServiceProfileB(val='optional':str)
+ clean_val=False:bool
+ previous value: ['optional']
+ read_back_val={'5G-eUICC-profile-b-p256': 'optional'}:{str}
+
ok: TS48v5_SAIP2.1A_NoBERTLV.der SuciActive(val='SUCI-on':str)
clean_val=True:bool
previous value: ['SUCI-on']
@@ -1134,6 +1164,36 @@
previous value: ['2']
read_back_val={'MNC-LEN': '3'}:{str}
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der EuiccMandatoryServiceGetIdentity(val='mandatory':str)
+ clean_val=True:bool
+ previous value: ['mandatory']
+ read_back_val={'5G-eUICC-get-identity': 'mandatory'}:{str}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der EuiccMandatoryServiceGetIdentity(val='optional':str)
+ clean_val=False:bool
+ previous value: ['mandatory']
+ read_back_val={'5G-eUICC-get-identity': 'optional'}:{str}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der EuiccMandatoryServiceProfileA(val='mandatory':str)
+ clean_val=True:bool
+ previous value: ['mandatory']
+ read_back_val={'5G-eUICC-profile-a-x25519': 'mandatory'}:{str}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der EuiccMandatoryServiceProfileA(val='optional':str)
+ clean_val=False:bool
+ previous value: ['mandatory']
+ read_back_val={'5G-eUICC-profile-a-x25519': 'optional'}:{str}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der EuiccMandatoryServiceProfileB(val='mandatory':str)
+ clean_val=True:bool
+ previous value: ['mandatory']
+ read_back_val={'5G-eUICC-profile-b-p256': 'mandatory'}:{str}
+
+ok: TS48v5_SAIP2.3_BERTLV_SUCI.der EuiccMandatoryServiceProfileB(val='optional':str)
+ clean_val=False:bool
+ previous value: ['mandatory']
+ read_back_val={'5G-eUICC-profile-b-p256': 'optional'}:{str}
+
ok: TS48v5_SAIP2.3_BERTLV_SUCI.der SuciActive(val='SUCI-on':str)
clean_val=True:bool
previous value: ['SUCI-on']
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/43867?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: Icddeb2488c4a024c6ee5afcc1b6c8cc0e436c43c
Gerrit-Change-Number: 43867
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr(a)sysmocom.de>
1
0
neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/43868?usp=email )
Change subject: SAIP2.1_gfmsuci.der
......................................................................
SAIP2.1_gfmsuci.der
Change-Id: I69354754013e4a98d86a9a8abdd7a6a36eb95177
Jenkins: skip-card-test
---
A smdpp-data/upp/SAIP2.1_gfmsuci.der
M tests/unittests/test_configurable_parameters.py
M tests/unittests/xo/test_configurable_parameters
3 files changed, 951 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/68/43868/1
diff --git a/smdpp-data/upp/SAIP2.1_gfmsuci.der b/smdpp-data/upp/SAIP2.1_gfmsuci.der
new file mode 100644
index 0000000..4e2913e
--- /dev/null
+++ b/smdpp-data/upp/SAIP2.1_gfmsuci.der
Binary files differ
diff --git a/tests/unittests/test_configurable_parameters.py b/tests/unittests/test_configurable_parameters.py
index d541925..15f434a 100755
--- a/tests/unittests/test_configurable_parameters.py
+++ b/tests/unittests/test_configurable_parameters.py
@@ -54,6 +54,7 @@
def test_parameters(self):
upp_fnames = (
+ 'SAIP2.1_gfmsuci.der',
'TS48v5_SAIP2.1A_NoBERTLV.der',
'TS48v5_SAIP2.3_BERTLV_SUCI.der',
)
diff --git a/tests/unittests/xo/test_configurable_parameters b/tests/unittests/xo/test_configurable_parameters
index 0866229..e6b87aa 100644
--- a/tests/unittests/xo/test_configurable_parameters
+++ b/tests/unittests/xo/test_configurable_parameters
@@ -1,4 +1,954 @@
+ok: SAIP2.1_gfmsuci.der Imsi(val='123456':str)
+ clean_val='123456':str
+ previous value: ['901709800046880', '0040']
+ read_back_val={'IMSI': '123456', 'IMSI-ACC': '0040'}:{str, hexstr}
+
+ok: SAIP2.1_gfmsuci.der Imsi(val=123456:int)
+ clean_val='123456':str
+ previous value: ['901709800046880', '0040']
+ read_back_val={'IMSI': '123456', 'IMSI-ACC': '0040'}:{str, hexstr}
+
+ok: SAIP2.1_gfmsuci.der Imsi(val='123456789012345':str)
+ clean_val='123456789012345':str
+ previous value: ['901709800046880', '0040']
+ read_back_val={'IMSI': '123456789012345', 'IMSI-ACC': '0020'}:{str, hexstr}
+
+ok: SAIP2.1_gfmsuci.der Imsi(val=123456789012345:int)
+ clean_val='123456789012345':str
+ previous value: ['901709800046880', '0040']
+ read_back_val={'IMSI': '123456789012345', 'IMSI-ACC': '0020'}:{str, hexstr}
+
+ok: SAIP2.1_gfmsuci.der Puk1(val='12345678':str)
+ clean_val=b'12345678':bytearray
+ previous value: ['00000000']
+ read_back_val={'PUK1': '12345678'}:{str}
+
+ok: SAIP2.1_gfmsuci.der Puk1(val=12345678:int)
+ clean_val=b'12345678':bytearray
+ previous value: ['00000000']
+ read_back_val={'PUK1': '12345678'}:{str}
+
+ok: SAIP2.1_gfmsuci.der Puk2(val='12345678':str)
+ clean_val=b'12345678':bytearray
+ previous value: ['11111111']
+ read_back_val={'PUK2': '12345678'}:{str}
+
+ok: SAIP2.1_gfmsuci.der Pin1(val='1234':str)
+ clean_val=b'1234\xff\xff\xff\xff':bytearray
+ previous value: ['0000']
+ read_back_val={'PIN1': '1234'}:{str}
+
+ok: SAIP2.1_gfmsuci.der Pin1(val='123456':str)
+ clean_val=b'123456\xff\xff':bytearray
+ previous value: ['0000']
+ read_back_val={'PIN1': '123456'}:{str}
+
+ok: SAIP2.1_gfmsuci.der Pin1(val='12345678':str)
+ clean_val=b'12345678':bytearray
+ previous value: ['0000']
+ read_back_val={'PIN1': '12345678'}:{str}
+
+ok: SAIP2.1_gfmsuci.der Pin1(val=1234:int)
+ clean_val=b'1234\xff\xff\xff\xff':bytearray
+ previous value: ['0000']
+ read_back_val={'PIN1': '1234'}:{str}
+
+ok: SAIP2.1_gfmsuci.der Pin1(val=123456:int)
+ clean_val=b'123456\xff\xff':bytearray
+ previous value: ['0000']
+ read_back_val={'PIN1': '123456'}:{str}
+
+ok: SAIP2.1_gfmsuci.der Pin1(val=12345678:int)
+ clean_val=b'12345678':bytearray
+ previous value: ['0000']
+ read_back_val={'PIN1': '12345678'}:{str}
+
+ok: SAIP2.1_gfmsuci.der Adm1(val='1234':str)
+ clean_val=b'1234\xff\xff\xff\xff':bytearray
+ previous value: ['56780123']
+ read_back_val={'ADM1': '1234'}:{str}
+
+ok: SAIP2.1_gfmsuci.der Adm1(val='123456':str)
+ clean_val=b'123456\xff\xff':bytearray
+ previous value: ['56780123']
+ read_back_val={'ADM1': '123456'}:{str}
+
+ok: SAIP2.1_gfmsuci.der Adm1(val='12345678':str)
+ clean_val=b'12345678':bytearray
+ previous value: ['56780123']
+ read_back_val={'ADM1': '12345678'}:{str}
+
+ok: SAIP2.1_gfmsuci.der Adm1(val=123456:int)
+ clean_val=b'123456\xff\xff':bytearray
+ previous value: ['56780123']
+ read_back_val={'ADM1': '123456'}:{str}
+
+ok: SAIP2.1_gfmsuci.der AlgorithmID(val='Milenage':str)
+ clean_val=1:int
+ previous value: ['Milenage']
+ read_back_val={'Algorithm': 'Milenage'}:{str}
+
+ok: SAIP2.1_gfmsuci.der AlgorithmID(val='TUAK':str)
+ clean_val=2:int
+ previous value: ['Milenage']
+ read_back_val={'Algorithm': 'TUAK'}:{str}
+
+ok: SAIP2.1_gfmsuci.der AlgorithmID(val='usim-test':str)
+ clean_val=3:int
+ previous value: ['Milenage']
+ read_back_val={'Algorithm': 'usim-test'}:{str}
+
+ok: SAIP2.1_gfmsuci.der AlgorithmID(val=1:int)
+ clean_val=1:int
+ previous value: ['Milenage']
+ read_back_val={'Algorithm': 'Milenage'}:{str}
+
+ok: SAIP2.1_gfmsuci.der AlgorithmID(val=2:int)
+ clean_val=2:int
+ previous value: ['Milenage']
+ read_back_val={'Algorithm': 'TUAK'}:{str}
+
+ok: SAIP2.1_gfmsuci.der AlgorithmID(val=3:int)
+ clean_val=3:int
+ previous value: ['Milenage']
+ read_back_val={'Algorithm': 'usim-test'}:{str}
+
+ok: SAIP2.1_gfmsuci.der K(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['67a5d3de32f13a15fb9e9792b3e7a5d2']
+ read_back_val={'K': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der K(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
+ previous value: ['67a5d3de32f13a15fb9e9792b3e7a5d2']
+ read_back_val={'K': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der K(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['67a5d3de32f13a15fb9e9792b3e7a5d2']
+ read_back_val={'K': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der K(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['67a5d3de32f13a15fb9e9792b3e7a5d2']
+ read_back_val={'K': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der K(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['67a5d3de32f13a15fb9e9792b3e7a5d2']
+ read_back_val={'K': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der Opc(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['8b739be390ccab78ab7bbb6a45babf77']
+ read_back_val={'OPc': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der Opc(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
+ previous value: ['8b739be390ccab78ab7bbb6a45babf77']
+ read_back_val={'OPc': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der Opc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['8b739be390ccab78ab7bbb6a45babf77']
+ read_back_val={'OPc': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der Opc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['8b739be390ccab78ab7bbb6a45babf77']
+ read_back_val={'OPc': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SmspTpScAddr(val='+1234567':str)
+ clean_val=(True, '1234567'):tuple
+ previous value: ['+001555']
+ read_back_val={'SMSP-TP-SC-ADDR': '+1234567'}:{str}
+
+ok: SAIP2.1_gfmsuci.der SmspTpScAddr(val=1234567:int)
+ clean_val=(False, '1234567'):tuple
+ previous value: ['+001555']
+ read_back_val={'SMSP-TP-SC-ADDR': '1234567'}:{str}
+
+ok: SAIP2.1_gfmsuci.der TuakNumberOfKeccak(val='123':str)
+ clean_val=123:int
+ previous value: ['1']
+ read_back_val={'KECCAK-N': '123'}:{str}
+
+ok: SAIP2.1_gfmsuci.der TuakNumberOfKeccak(val=123:int)
+ clean_val=123:int
+ previous value: ['1']
+ read_back_val={'KECCAK-N': '123'}:{str}
+
+ok: SAIP2.1_gfmsuci.der MilenageRotationConstants(val='0a 0b 0c 01 02':str)
+ clean_val=b'\n\x0b\x0c\x01\x02':bytes
+ previous value: ['4000204060']
+ read_back_val={'MilenageRotation': '0a0b0c0102'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der MilenageRotationConstants(val=b'\n\x0b\x0c\x01\x02':bytes)
+ clean_val=b'\n\x0b\x0c\x01\x02':bytes
+ previous value: ['4000204060']
+ read_back_val={'MilenageRotation': '0a0b0c0102'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der MilenageRotationConstants(val=b'\n\x0b\x0c\x01\x02':bytearray)
+ clean_val=b'\n\x0b\x0c\x01\x02':bytes
+ previous value: ['4000204060']
+ read_back_val={'MilenageRotation': '0a0b0c0102'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der MilenageXoringConstants(val='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb cccccccccccccccccccccccccccccccc 11111111111111111111111111111111 22222222222222222222222222222222':str)
+ 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
+ previous value: ['0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000020000000000000000000000000000000400000000000000000000000000000008']
+ read_back_val={'MilenageXOR': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc1111111111111111111111111111111122222222222222222222222222222222'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der MilenageXoringConstants(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
+ previous value: ['0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000020000000000000000000000000000000400000000000000000000000000000008']
+ read_back_val={'MilenageXOR': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc1111111111111111111111111111111122222222222222222222222222222222'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der MncLen(val='2':str)
+ clean_val=2:int
+ previous value: ['2']
+ read_back_val={'MNC-LEN': '2'}:{str}
+
+ok: SAIP2.1_gfmsuci.der MncLen(val=3:int)
+ clean_val=3:int
+ previous value: ['2']
+ read_back_val={'MNC-LEN': '3'}:{str}
+
+ok: SAIP2.1_gfmsuci.der EuiccMandatoryServiceGetIdentity(val='mandatory':str)
+ clean_val=True:bool
+ previous value: ['optional']
+ read_back_val={'5G-eUICC-get-identity': 'mandatory'}:{str}
+
+ok: SAIP2.1_gfmsuci.der EuiccMandatoryServiceGetIdentity(val='optional':str)
+ clean_val=False:bool
+ previous value: ['optional']
+ read_back_val={'5G-eUICC-get-identity': 'optional'}:{str}
+
+ok: SAIP2.1_gfmsuci.der EuiccMandatoryServiceProfileA(val='mandatory':str)
+ clean_val=True:bool
+ previous value: ['optional']
+ read_back_val={'5G-eUICC-profile-a-x25519': 'mandatory'}:{str}
+
+ok: SAIP2.1_gfmsuci.der EuiccMandatoryServiceProfileA(val='optional':str)
+ clean_val=False:bool
+ previous value: ['optional']
+ read_back_val={'5G-eUICC-profile-a-x25519': 'optional'}:{str}
+
+ok: SAIP2.1_gfmsuci.der EuiccMandatoryServiceProfileB(val='mandatory':str)
+ clean_val=True:bool
+ previous value: ['optional']
+ read_back_val={'5G-eUICC-profile-b-p256': 'mandatory'}:{str}
+
+ok: SAIP2.1_gfmsuci.der EuiccMandatoryServiceProfileB(val='optional':str)
+ clean_val=False:bool
+ previous value: ['optional']
+ read_back_val={'5G-eUICC-profile-b-p256': 'optional'}:{str}
+
+ok: SAIP2.1_gfmsuci.der SuciActive(val='SUCI-on':str)
+ clean_val=True:bool
+ previous value: ['SUCI-off']
+ read_back_val={'5G-SUCI-active': 'SUCI-on'}:{str}
+
+ok: SAIP2.1_gfmsuci.der SuciActive(val='SUCI-off':str)
+ clean_val=False:bool
+ previous value: ['SUCI-off']
+ read_back_val={'5G-SUCI-active': 'SUCI-off'}:{str}
+
+ok: SAIP2.1_gfmsuci.der SuciInUsim(val='SUCI-in-UE':str)
+ clean_val=False:bool
+ previous value: ['SUCI-in-UE']
+ read_back_val={'5G-SUCI-in-USIM': 'SUCI-in-UE'}:{str}
+
+ok: SAIP2.1_gfmsuci.der SuciInUsim(val='SUCI-in-USIM':str)
+ clean_val=True:bool
+ previous value: ['SUCI-in-UE']
+ read_back_val={'5G-SUCI-in-USIM': 'SUCI-in-USIM'}:{str}
+
+skip: SAIP2.1_gfmsuci.der SuciRi(val='123':str)
+ clean_val='123':str
+ previous value: []
+ skipping, param not in template.
+
+skip: SAIP2.1_gfmsuci.der SuciRi(val='0':str)
+ clean_val='0':str
+ previous value: []
+ skipping, param not in template.
+
+skip: SAIP2.1_gfmsuci.der SuciRi(val='9999':str)
+ clean_val='9999':str
+ previous value: []
+ skipping, param not in template.
+
+skip: SAIP2.1_gfmsuci.der SuciCalcInfoUe(val='{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}':str)
+ clean_val={'prot_scheme_id_list': [{'priority': 0, 'identifier': 2, 'key_index': 1}, {'priority': 1, 'identifier': 1, 'key_index': 2}], 'hnet_pubkey_list': [{'hnet_pubkey_identifier': 27, 'hnet_pubkey': '0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4'}, {'hnet_pubkey_identifier': 30, 'hnet_pubkey': '5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650'}]}:{list, list}
+ previous value: []
+ skipping, param not in template.
+
+skip: SAIP2.1_gfmsuci.der SuciCalcInfoUsim(val='{"prot_scheme_id_list": [{"priority": 0, "identifier": 2, "key_index": 1}, {"priority": 1, "identifier": 1, "key_index": 2}], "hnet_pubkey_list": [{"hnet_pubkey_identifier": 27, "hnet_pubkey": "0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4"}, {"hnet_pubkey_identifier": 30, "hnet_pubkey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650"}]}':str)
+ clean_val={'prot_scheme_id_list': [{'priority': 0, 'identifier': 2, 'key_index': 1}, {'priority': 1, 'identifier': 1, 'key_index': 2}], 'hnet_pubkey_list': [{'hnet_pubkey_identifier': 27, 'hnet_pubkey': '0472da71976234ce833a6907425867b82e074d44ef907dfb4b3e21c1c2256ebcd15a7ded52fcbb097a4ed250e036c7b9c8c7004c4eedc4f068cd7bf8d3f900e3b4'}, {'hnet_pubkey_identifier': 30, 'hnet_pubkey': '5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650'}]}:{list, list}
+ previous value: []
+ skipping, param not in template.
+
+ok: SAIP2.1_gfmsuci.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
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn20AesDek(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
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn20AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn20AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn20AesDek(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn20AesDek(val='010203040506070809101112131415161718192021222324':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn20AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn20AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn20AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn20AesDek(val='0102030405060708091011121314151617181920212223242526272829303132':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn20AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn20AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn20AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN20-AES-DEK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(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
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(val='010203040506070809101112131415161718192021222324':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(val='0102030405060708091011121314151617181920212223242526272829303132':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn21AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN21-AES-ENC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(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
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(val='010203040506070809101112131415161718192021222324':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(val='0102030405060708091011121314151617181920212223242526272829303132':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp02Kvn22AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP02-KVN22-AES-MAC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(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
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(val='010203040506070809101112131415161718192021222324':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(val='0102030405060708091011121314151617181920212223242526272829303132':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn30AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: ['afb03b1ee26476001eb87de8d5913c91']
+ read_back_val={'SCP03-KVN30-AES-DEK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(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
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(val='010203040506070809101112131415161718192021222324':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(val='0102030405060708091011121314151617181920212223242526272829303132':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn31AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN31-AES-ENC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(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
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(val='010203040506070809101112131415161718192021222324':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(val='0102030405060708091011121314151617181920212223242526272829303132':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp03Kvn32AesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP03-KVN32-AES-MAC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn01DesDek(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN01-DES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn01DesDek(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
+ previous value: []
+ read_back_val={'SCP80-KVN01-DES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn01DesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN01-DES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn01DesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN01-DES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn01DesDek(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN01-DES-DEK': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(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
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(val='010203040506070809101112131415161718192021222324':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(val='0102030405060708091011121314151617181920212223242526272829303132':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02AesEnc(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-AES-ENC': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02DesMac(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-DES-MAC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02DesMac(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
+ previous value: []
+ read_back_val={'SCP80-KVN02-DES-MAC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02DesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-DES-MAC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02DesMac(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-DES-MAC': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn02DesMac(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN02-DES-MAC': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn03DesDek(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN03-DES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn03DesDek(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
+ previous value: []
+ read_back_val={'SCP80-KVN03-DES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn03DesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN03-DES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn03DesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN03-DES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp80Kvn03DesDek(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP80-KVN03-DES-DEK': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(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
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(val='010203040506070809101112131415161718192021222324':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(val='0102030405060708091011121314151617181920212223242526272829303132':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn40AesDek(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: ['84d4a08582213074d0168bae85b9fac2']
+ read_back_val={'SCP81-KVN40-AES-DEK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(val='01020304050607080910111213141516':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(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
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '01020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(val=11020304050607080910111213141516:int)
+ clean_val=b'\x11\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16':bytes
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '11020304050607080910111213141516'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(val='010203040506070809101112131415161718192021222324':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$':bytes
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '010203040506070809101112131415161718192021222324'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(val='0102030405060708091011121314151617181920212223242526272829303132':str)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytearray)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
+ok: SAIP2.1_gfmsuci.der SdKeyScp81Kvn41Tlspsk(val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':BytesIO)
+ clean_val=b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19 !"#$%&\'()012':bytes
+ previous value: []
+ read_back_val={'SCP81-KVN41-TLSPSK': '0102030405060708091011121314151617181920212223242526272829303132'}:{hexstr}
+
ok: TS48v5_SAIP2.1A_NoBERTLV.der Imsi(val='123456':str)
clean_val='123456':str
previous value: ['001010123456063', '0001']
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/43868?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I69354754013e4a98d86a9a8abdd7a6a36eb95177
Gerrit-Change-Number: 43868
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr(a)sysmocom.de>
1
0
09 Oct '26
Attention is currently required from: fixeria, neels.
pespin has posted comments on this change by fixeria. ( https://gerrit.osmocom.org/c/osmo-bts/+/43863?usp=email )
Change subject: rsl: support Early IA with frequency hopping
......................................................................
Patch Set 1: Code-Review+1
--
To view, visit https://gerrit.osmocom.org/c/osmo-bts/+/43863?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: comment
Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Change-Id: I632e46cccbf1151a88d2dfaf8cb42ce2647206a5
Gerrit-Change-Number: 43863
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: neels <nhofmeyr(a)sysmocom.de>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
Gerrit-CC: Jenkins Builder
Gerrit-Attention: neels <nhofmeyr(a)sysmocom.de>
Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Comment-Date: Fri, 09 Oct 2026 20:49:10 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
1
0
[S] Change in osmo-ttcn3-hacks[master]: bts: drop mp_l1_supports_gprs, run all GPRS tests
by pespin 09 Oct '26
by pespin 09 Oct '26
09 Oct '26
Attention is currently required from: fixeria.
pespin has posted comments on this change by fixeria. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/43862?usp=email )
Change subject: bts: drop mp_l1_supports_gprs, run all GPRS tests
......................................................................
Patch Set 1:
(1 comment)
Patchset:
PS1:
May this still be needed to run tests using real C123 hardware? (OS#3155)
--
To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/43862?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: comment
Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Change-Id: Ibe8db314c2eb676d536779d5875456d17ecb60e8
Gerrit-Change-Number: 43862
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-CC: pespin <pespin(a)sysmocom.de>
Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Comment-Date: Fri, 09 Oct 2026 20:45:22 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
1
0
09 Oct '26
fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-bts/+/43863?usp=email )
Change subject: rsl: support Early IA with frequency hopping
......................................................................
rsl: support Early IA with frequency hopping
When osmo-bsc sends the RR Immediate Assignment before the target
lchan is active (early IA), osmo-bts holds it back until the lchan
is activated. The target TRX is currently found by the ARFCN in the
Channel Description, so this only works if hopping is disabled (H=0).
With hopping enabled (H=1), the Channel Description has MAIO/HSN
and no ARFCN, so the IMM ASS is sent to the MS right away.
For H=1, find the target TRX whose timeslot hops with the given
MAIO/HSN instead. If more than one TRX matches (different hopping
groups sharing the same MAIO/HSN on the same timeslot), don't hold
back the IMM ASS, the same as when no TRX matches.
This makes BTS_Tests.TC_early_immediate_assignment pass when run
in the frequency hopping configuration.
Change-Id: I632e46cccbf1151a88d2dfaf8cb42ce2647206a5
AI-Assisted: yes (Claude)
Related: SYS#5559
---
M src/common/rsl.c
1 file changed, 45 insertions(+), 19 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-bts refs/changes/63/43863/1
diff --git a/src/common/rsl.c b/src/common/rsl.c
index 88a5f17..428a3a9 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -394,6 +394,28 @@
return NULL;
}
+/* Find the TRX whose timeslot tn hops with the given MAIO/HSN.
+ * Returns NULL if no TRX matches, or if more than one does. */
+static struct gsm_bts_trx *trx_lookup_by_maio_hsn(struct gsm_bts *bts, uint8_t tn,
+ uint8_t maio, uint8_t hsn)
+{
+ struct gsm_bts_trx *trx, *found = NULL;
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ const struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+ if (!ts->hopping.enabled)
+ continue;
+ if (ts->hopping.maio != maio || ts->hopping.hsn != hsn)
+ continue;
+ if (found) {
+ LOGP(DRSL, LOGL_NOTICE, "Ambiguous MAIO=%u/HSN=%u on TS%u "
+ "(TRX%u and TRX%u)\n", maio, hsn, tn, found->nr, trx->nr);
+ return NULL;
+ }
+ found = trx;
+ }
+ return found;
+}
+
static struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
const char *log_name)
{
@@ -1409,29 +1431,33 @@
* but just send the RR message to the MS as-is.
* 'trx' here is the TRX of the BCCH channel. To find the correct TRX for the IMM ASS target, we need to look up
* the ARFCN that is contained in the IMM ASS message. When frequency hopping is enabled, there will not be an
- * ARFCN, so we cannot support early-IA with frequency hopping enabled. */
+ * ARFCN, so instead we look up the TRX whose timeslot hops with the given MAIO/HSN. If this is ambiguous
+ * (different hopping groups sharing the same MAIO/HSN on the same timeslot), don't do the RR IA caching. */
if (msg->len >= sizeof(struct gsm48_imm_ass)) {
- struct gsm48_imm_ass *rr_ia = (void*)msg->data;
- if (rr_ia->chan_desc.h0.h == 0) {
- /* hopping is disabled. */
- struct gsm_bts_trx *ia_target_trx;
- uint16_t arfcn;
- arfcn = (rr_ia->chan_desc.h0.arfcn_high << 8) + rr_ia->chan_desc.h0.arfcn_low;
+ const struct gsm48_imm_ass *rr_ia = (void *)msg->data;
+ const struct gsm48_chan_desc *cd = &rr_ia->chan_desc;
+ struct gsm_bts_trx *ia_target_trx;
+ if (cd->h0.h == 0) { /* hopping is disabled */
+ uint16_t arfcn = (cd->h0.arfcn_high << 8) + cd->h0.arfcn_low;
ia_target_trx = trx_lookup_by_arfcn(&trx->bts->trx_list, arfcn);
- if (ia_target_trx) {
- /* found the ARFCN's trx */
- struct gsm_lchan *ia_target_lchan;
- ia_target_lchan = lchan_lookup(ia_target_trx, rr_ia->chan_desc.chan_nr, "Early IA check: ");
- if (ia_target_lchan && ia_target_lchan->state != LCHAN_S_ACTIVE) {
- /* Target lchan is not yet active. Cache the IA.
- * If a previous IA is still lingering, free it. */
- msgb_free(ia_target_lchan->early_rr_ia);
- ia_target_lchan->early_rr_ia = msg;
+ } else { /* hopping is enabled */
+ uint8_t maio = (cd->h1.maio_high << 2) + cd->h1.maio_low;
+ ia_target_trx = trx_lookup_by_maio_hsn(trx->bts, cd->chan_nr & 0x07, maio, cd->h1.hsn);
+ }
- /* return 1 means: don't msgb_free() the msg */
- return 1;
- }
+ if (ia_target_trx) {
+ /* found the target trx */
+ struct gsm_lchan *ia_target_lchan;
+ ia_target_lchan = lchan_lookup(ia_target_trx, cd->chan_nr, "Early IA check: ");
+ if (ia_target_lchan && ia_target_lchan->state != LCHAN_S_ACTIVE) {
+ /* Target lchan is not yet active. Cache the IA.
+ * If a previous IA is still lingering, free it. */
+ msgb_free(ia_target_lchan->early_rr_ia);
+ ia_target_lchan->early_rr_ia = msg;
+
+ /* return 1 means: don't msgb_free() the msg */
+ return 1;
}
}
}
--
To view, visit https://gerrit.osmocom.org/c/osmo-bts/+/43863?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Change-Id: I632e46cccbf1151a88d2dfaf8cb42ce2647206a5
Gerrit-Change-Number: 43863
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de>
1
0