neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/40205?usp=email )
Change subject: personalization: make sense of SdKey subclasses ......................................................................
personalization: make sense of SdKey subclasses
After a call with Harald, I think I finally understand what SdKey subclasses we need.
Change-Id: I8c9e6095e200103d2e1779964be06fff63c5cebf --- M pySim/esim/saip/personalization.py M tests/unittests/test_esim_saip.py 2 files changed, 304 insertions(+), 56 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/05/40205/1
diff --git a/pySim/esim/saip/personalization.py b/pySim/esim/saip/personalization.py index 0eb0671..78ea5a8 100644 --- a/pySim/esim/saip/personalization.py +++ b/pySim/esim/saip/personalization.py @@ -494,8 +494,9 @@ """Configurable Security Domain (SD) Key. Value is presented as bytes.""" # these will be set by subclasses key_type = None - key_id = None kvn = None + reserved_kvn = tuple() # tuple of all reserved kvn for a given SCPxx + key_id = None key_usage_qual = None default_source = param_source.RandomHexDigitSource
@@ -514,7 +515,7 @@ key = SecurityDomainKey( key_version_number=cls.kvn, key_id=cls.key_id, - key_usage_qualifier=KeyUsageQualifier.build(cls.key_usage_qual), + key_usage_qualifier=cls.key_usage_qual, key_components=set_components, ) pe.add_key(key) @@ -536,106 +537,315 @@ if kc: yield { cls.name: b2h(kc) }
-class SdKeyScp80_01(SdKey): +# Offer these Security Domain Keys: +# +# security domain | reserved KVN range +# ---------------------------- +# SCP80 | 0x01 .. 0x0f +# SCP81 | 0x81 .. 0x8f +# SCP02 | 0x20 .. 0x2f, 0xff +# SCP03 | 0x30 .. 0x3f +# +# The KVN allows adding multiple security domains of the same type. +# +# Also, for each security domain, there are three keys: ENC, MAC and DEK, indicated by key_id. +# key | alternate name | key_id | key_usage_qual +#----------------------------------------------- +# ENC | KIC | 0x01 | 0x18 +# MAC | KID | 0x02 | 0x14 +# DEK | KIK | 0x03 | 0x48 +# +# For each, offer a couple of separate SdKey subclasses, only partially covering the reserved KVN range. For KVN, again +# a separate subclass for eack key_id for ENC, MAC and DEK. +# +# All of these are AES keys. +# +# For example, for SCP80 we have: +# SdKeyAes +# SdKeyScp80Kvn01 +# SdKeyScp80Kvn01Enc +# SdKeyScp80Kvn01Mac +# SdKeyScp80Kvn01Dek +# SdKeyScp80Kvn02 +# SdKeyScp80Kvn02Enc +# SdKeyScp80Kvn02Mac +# SdKeyScp80Kvn02Dek +# SdKeyScp80Kvn03 +# SdKeyScp80Kvn03Enc +# SdKeyScp80Kvn03Mac +# SdKeyScp80Kvn03Dek +# +# (Only the leaf nodes with ...Enc/Mac/Dek are returned by +# ConfigurableParameter.get_all_implementations(allow_abstract=False)) + +class SdKeyAes(SdKey): + key_type = KeyType.aes + allow_len = (16,24,32) + default_value = '00' * 32 + + +class SdKeyScp80(SdKeyAes): + name = 'SCP80' + reserved_kvn = tuple(range(0x01, 0x0f + 1)) + +class SdKeyScp80Kvn01(SdKeyScp80): + name = 'SCP80 KVN01' kvn = 0x01 - key_type = 0x88 # AES key type - allow_len = (16,24,32) - -class SdKeyScp80_01Kic(SdKeyScp80_01): +class SdKeyScp80Kvn01Enc(SdKeyScp80Kvn01): + is_abstract = False + name = SdKeyScp80Kvn01.name + ' ENC' key_id = 0x01 - key_usage_qual = 0x18 # FIXME: ordering? - -class SdKeyScp80_01Kid(SdKeyScp80_01): + key_usage_qual = 0x18 +class SdKeyScp80Kvn01Mac(SdKeyScp80Kvn01): + is_abstract = False + name = SdKeyScp80Kvn01.name + ' MAC' key_id = 0x02 key_usage_qual = 0x14 +class SdKeyScp80Kvn01Dek(SdKeyScp80Kvn01): + is_abstract = False + name = SdKeyScp80Kvn01.name + ' DEK' + key_id = 0x03 + key_usage_qual = 0x48
-class SdKeyScp80_01Kik(SdKeyScp80_01): +class SdKeyScp80Kvn02(SdKeyScp80): + name = 'SCP80 KVN02' + kvn = 0x02 +class SdKeyScp80Kvn02Enc(SdKeyScp80Kvn02): + is_abstract = False + name = SdKeyScp80Kvn02.name + ' ENC' + key_id = 0x01 + key_usage_qual = 0x18 +class SdKeyScp80Kvn02Mac(SdKeyScp80Kvn02): + is_abstract = False + name = SdKeyScp80Kvn02.name + ' MAC' + key_id = 0x02 + key_usage_qual = 0x14 +class SdKeyScp80Kvn02Dek(SdKeyScp80Kvn02): + is_abstract = False + name = SdKeyScp80Kvn02.name + ' DEK' + key_id = 0x03 + key_usage_qual = 0x48 + +class SdKeyScp80Kvn03(SdKeyScp80): + name = 'SCP80 KVN03' + kvn = 0x03 +class SdKeyScp80Kvn03Enc(SdKeyScp80Kvn03): + is_abstract = False + name = SdKeyScp80Kvn03.name + ' ENC' + key_id = 0x01 + key_usage_qual = 0x18 +class SdKeyScp80Kvn03Mac(SdKeyScp80Kvn03): + is_abstract = False + name = SdKeyScp80Kvn03.name + ' MAC' + key_id = 0x02 + key_usage_qual = 0x14 +class SdKeyScp80Kvn03Dek(SdKeyScp80Kvn03): + is_abstract = False + name = SdKeyScp80Kvn03.name + ' DEK' key_id = 0x03 key_usage_qual = 0x48
-class SdKeyScp81_01(SdKey): - kvn = 0x81 # FIXME +class SdKeyScp81(SdKeyAes): + name = 'SCP81' + reserved_kvn = tuple(range(0x81, 0x8f + 1))
-class SdKeyScp81_01Psk(SdKeyScp81_01): +class SdKeyScp81Kvn81(SdKeyScp81): + name = 'SCP81 KVN81' + kvn = 0x81 +class SdKeyScp81Kvn81Enc(SdKeyScp81Kvn81): + is_abstract = False + name = SdKeyScp81Kvn81.name + ' ENC' key_id = 0x01 - key_type = 0x85 - key_usage_qual = 0x3C - -class SdKeyScp81_01Dek(SdKeyScp81_01): + key_usage_qual = 0x18 +class SdKeyScp81Kvn81Mac(SdKeyScp81Kvn81): + is_abstract = False + name = SdKeyScp81Kvn81.name + ' MAC' key_id = 0x02 - key_type = 0x88 + key_usage_qual = 0x14 +class SdKeyScp81Kvn81Dek(SdKeyScp81Kvn81): + is_abstract = False + name = SdKeyScp81Kvn81.name + ' DEK' + key_id = 0x03 + key_usage_qual = 0x48 + +class SdKeyScp81Kvn82(SdKeyScp81): + name = 'SCP81 KVN82' + kvn = 0x82 +class SdKeyScp81Kvn82Enc(SdKeyScp81Kvn82): + is_abstract = False + name = SdKeyScp81Kvn82.name + ' ENC' + key_id = 0x01 + key_usage_qual = 0x18 +class SdKeyScp81Kvn82Mac(SdKeyScp81Kvn82): + is_abstract = False + name = SdKeyScp81Kvn82.name + ' MAC' + key_id = 0x02 + key_usage_qual = 0x14 +class SdKeyScp81Kvn82Dek(SdKeyScp81Kvn82): + is_abstract = False + name = SdKeyScp81Kvn82.name + ' DEK' + key_id = 0x03 + key_usage_qual = 0x48 + +class SdKeyScp81Kvn83(SdKeyScp81): + name = 'SCP81 KVN83' + kvn = 0x83 +class SdKeyScp81Kvn83Enc(SdKeyScp81Kvn83): + is_abstract = False + name = SdKeyScp81Kvn83.name + ' ENC' + key_id = 0x01 + key_usage_qual = 0x18 +class SdKeyScp81Kvn83Mac(SdKeyScp81Kvn83): + is_abstract = False + name = SdKeyScp81Kvn83.name + ' MAC' + key_id = 0x02 + key_usage_qual = 0x14 +class SdKeyScp81Kvn83Dek(SdKeyScp81Kvn83): + is_abstract = False + name = SdKeyScp81Kvn83.name + ' DEK' + key_id = 0x03 key_usage_qual = 0x48
-class SdKeyScp02_20(SdKey): +class SdKeyScp02(SdKeyAes): + name = 'SCP02' + reserved_kvn = tuple(range(0x20, 0x2f + 1)) + (0xff, ) +class SdKeyScp02Kvn20(SdKeyScp02): + name = 'SCP02 20' kvn = 0x20 - key_type = 0x88 # AES key type - allow_len = (16,24,32) - -class SdKeyScp02_20Enc(SdKeyScp02_20): +class SdKeyScp02Kvn20Enc(SdKeyScp02Kvn20): + is_abstract = False + name = SdKeyScp02Kvn20.name + ' ENC' key_id = 0x01 key_usage_qual = 0x18 - -class SdKeyScp02_20Mac(SdKeyScp02_20): +class SdKeyScp02Kvn20Mac(SdKeyScp02Kvn20): + is_abstract = False + name = SdKeyScp02Kvn20.name + ' MAC' key_id = 0x02 key_usage_qual = 0x14 +class SdKeyScp02Kvn20Dek(SdKeyScp02Kvn20): + is_abstract = False + name = SdKeyScp02Kvn20.name + ' DEK' + key_id = 0x03 + key_usage_qual = 0x48
-class SdKeyScp02_20Dek(SdKeyScp02_20): +class SdKeyScp02Kvn21(SdKeyScp02): + name = 'SCP02 21' + kvn = 0x21 +class SdKeyScp02Kvn21Enc(SdKeyScp02Kvn21): + is_abstract = False + name = SdKeyScp02Kvn21.name + ' ENC' + key_id = 0x01 + key_usage_qual = 0x18 +class SdKeyScp02Kvn21Mac(SdKeyScp02Kvn21): + is_abstract = False + name = SdKeyScp02Kvn21.name + ' MAC' + key_id = 0x02 + key_usage_qual = 0x14 +class SdKeyScp02Kvn21Dek(SdKeyScp02Kvn21): + is_abstract = False + name = SdKeyScp02Kvn21.name + ' DEK' + key_id = 0x03 + key_usage_qual = 0x48 + +class SdKeyScp02Kvn22(SdKeyScp02): + name = 'SCP02 22' + kvn = 0x22 +class SdKeyScp02Kvn22Enc(SdKeyScp02Kvn22): + is_abstract = False + name = SdKeyScp02Kvn22.name + ' ENC' + key_id = 0x01 + key_usage_qual = 0x18 +class SdKeyScp02Kvn22Mac(SdKeyScp02Kvn22): + is_abstract = False + name = SdKeyScp02Kvn22.name + ' MAC' + key_id = 0x02 + key_usage_qual = 0x14 +class SdKeyScp02Kvn22Dek(SdKeyScp02Kvn22): + is_abstract = False + name = SdKeyScp02Kvn22.name + ' DEK' + key_id = 0x03 + key_usage_qual = 0x48 + +class SdKeyScp02Kvnff(SdKeyScp02): + name = 'SCP02 ff' + kvn = 0xff +class SdKeyScp02KvnffEnc(SdKeyScp02Kvnff): + is_abstract = False + name = SdKeyScp02Kvnff.name + ' ENC' + key_id = 0x01 + key_usage_qual = 0x18 +class SdKeyScp02KvnffMac(SdKeyScp02Kvnff): + is_abstract = False + name = SdKeyScp02Kvnff.name + ' MAC' + key_id = 0x02 + key_usage_qual = 0x14 +class SdKeyScp02KvnffDek(SdKeyScp02Kvnff): + is_abstract = False + name = SdKeyScp02Kvnff.name + ' DEK' key_id = 0x03 key_usage_qual = 0x48
-class SdKeyScp03_30(SdKey): +class SdKeyScp03(SdKeyAes): + name = 'SCP03 30' + reserved_kvn = tuple(range(0x30, 0x3f + 1)) + +class SdKeyScp03Kvn30(SdKeyScp03): + name = 'SCP03 30' kvn = 0x30 - key_type = 0x88 # AES key type - allow_len = (16,24,32) - -class SdKeyScp03_30Enc(SdKeyScp03_30): +class SdKeyScp03Kvn30Enc(SdKeyScp03Kvn30): + is_abstract = False + name = SdKeyScp03Kvn30.name + ' ENC' key_id = 0x01 key_usage_qual = 0x18 - -class SdKeyScp03_30Mac(SdKeyScp03_30): +class SdKeyScp03Kvn30Mac(SdKeyScp03Kvn30): + is_abstract = False + name = SdKeyScp03Kvn30.name + ' MAC' key_id = 0x02 key_usage_qual = 0x14 - -class SdKeyScp03_30Dek(SdKeyScp03_30): +class SdKeyScp03Kvn30Dek(SdKeyScp03Kvn30): + is_abstract = False + name = SdKeyScp03Kvn30.name + ' DEK' key_id = 0x03 key_usage_qual = 0x48
- -class SdKeyScp03_31(SdKey): +class SdKeyScp03Kvn31(SdKeyScp03): + name = 'SCP03 31' kvn = 0x31 - key_type = 0x88 # AES key type - allow_len = (16,24,32) - -class SdKeyScp03_31Enc(SdKeyScp03_31): +class SdKeyScp03Kvn31Enc(SdKeyScp03Kvn31): + is_abstract = False + name = SdKeyScp03Kvn31.name + ' ENC' key_id = 0x01 key_usage_qual = 0x18 - -class SdKeyScp03_31Mac(SdKeyScp03_31): +class SdKeyScp03Kvn31Mac(SdKeyScp03Kvn31): + is_abstract = False + name = SdKeyScp03Kvn31.name + ' MAC' key_id = 0x02 key_usage_qual = 0x14 - -class SdKeyScp03_31Dek(SdKeyScp03_31): +class SdKeyScp03Kvn31Dek(SdKeyScp03Kvn31): + is_abstract = False + name = SdKeyScp03Kvn31.name + ' DEK' key_id = 0x03 key_usage_qual = 0x48
- -class SdKeyScp03_32(SdKey): +class SdKeyScp03Kvn32(SdKeyScp03): + name = 'SCP03 32' kvn = 0x32 - key_type = 0x88 # AES key type - allow_len = (16,24,32) - -class SdKeyScp03_32Enc(SdKeyScp03_32): +class SdKeyScp03Kvn32Enc(SdKeyScp03Kvn32): + is_abstract = False + name = SdKeyScp03Kvn32.name + ' ENC' key_id = 0x01 key_usage_qual = 0x18 - -class SdKeyScp03_32Mac(SdKeyScp03_32): +class SdKeyScp03Kvn32Mac(SdKeyScp03Kvn32): + is_abstract = False + name = SdKeyScp03Kvn32.name + ' MAC' key_id = 0x02 key_usage_qual = 0x14 - -class SdKeyScp03_32Dek(SdKeyScp03_32): +class SdKeyScp03Kvn32Dek(SdKeyScp03Kvn32): + is_abstract = False + name = SdKeyScp03Kvn32.name + ' DEK' key_id = 0x03 key_usage_qual = 0x48
diff --git a/tests/unittests/test_esim_saip.py b/tests/unittests/test_esim_saip.py index e7e324d..6c2cdd1 100755 --- a/tests/unittests/test_esim_saip.py +++ b/tests/unittests/test_esim_saip.py @@ -63,6 +63,44 @@ # TODO: we don't actually test the results here, but we just verify there is no exception pes.to_der()
+ def test_personalization2(self): + """Test some of the personalization operations.""" + pes = ProfileElementSequence.from_der(self.per_input) + prev_val = set(SdKeyScp80_01Kic.get_values_from_pes(pes)) + print(f'{prev_val=}') + self.assertTrue(prev_val) + + set_val = '42342342342342342342342342342342' + param = SdKeyScp80_01Kic(set_val) + param.validate() + param.apply(pes) + + get_val1 = set(SdKeyScp80_01Kic.get_values_from_pes(pes)) + print(f'{get_val1=} {set_val=}') + self.assertEqual(get_val1, set((set_val,))) + + get_val1b = set(SdKeyScp80_01Kic.get_values_from_pes(pes)) + print(f'{get_val1b=} {set_val=}') + self.assertEqual(get_val1b, set((set_val,))) + + print("HELLOO") + der = pes.to_der() + print("DONEDONE") + + get_val1c = set(SdKeyScp80_01Kic.get_values_from_pes(pes)) + print(f'{get_val1c=} {set_val=}') + self.assertEqual(get_val1c, set((set_val,))) + + # assertTrue to not dump the entire der. + # Expecting the modified DER to be different. If this assertion fails, then no change has happened in the output + # DER and the ConfigurableParameter subclass is buggy. + self.assertTrue(der != self.per_input) + + pes2 = ProfileElementSequence.from_der(der) + get_val2 = set(SdKeyScp80_01Kic.get_values_from_pes(pes2)) + print(f'{get_val2=} {set_val=}') + self.assertEqual(get_val2, set((set_val,))) + def test_constructor_encode(self): """Test that DER-encoding of PE created by "empty" constructor works without raising exception.""" for cls in [ProfileElementMF, ProfileElementPuk, ProfileElementPin, ProfileElementTelecom,