personalization: make sense of SdKey subclasses

After a call with Harald, I think I finally understand what SdKey
subclasses we need.

Change-Id: I8c9e6095e200103d2e1779964be06fff63c5cebf
This commit is contained in:
Neels Hofmeyr
2025-03-01 23:07:34 +01:00
parent 52e84a0bad
commit dc97895447
2 changed files with 312 additions and 59 deletions

View File

@@ -495,8 +495,9 @@ class SdKey(BinaryParam):
"""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
@@ -515,7 +516,7 @@ class SdKey(BinaryParam):
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,109 +537,323 @@ class SdKey(BinaryParam):
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 SdKeyScp80_01Kik(SdKeyScp80_01):
class SdKeyScp80Kvn01Dek(SdKeyScp80Kvn01):
is_abstract = False
name = SdKeyScp80Kvn01.name + ' DEK'
key_id = 0x03
key_usage_qual = 0x48
class SdKeyScp81_01(SdKey):
kvn = 0x81 # FIXME
class SdKeyScp81_01Psk(SdKeyScp81_01):
class SdKeyScp80Kvn02(SdKeyScp80):
name = 'SCP80 KVN02'
kvn = 0x02
class SdKeyScp80Kvn02Enc(SdKeyScp80Kvn02):
is_abstract = False
name = SdKeyScp80Kvn02.name + ' ENC'
key_id = 0x01
key_type = 0x85
key_usage_qual = 0x3C
class SdKeyScp81_01Dek(SdKeyScp81_01):
key_usage_qual = 0x18
class SdKeyScp80Kvn02Mac(SdKeyScp80Kvn02):
is_abstract = False
name = SdKeyScp80Kvn02.name + ' MAC'
key_id = 0x02
key_type = 0x88
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 SdKeyScp02_20(SdKey):
# "omitting" SdKeyScp80Kvn04 ... Kvn0f
class SdKeyScp81(SdKeyAes):
name = 'SCP81'
reserved_kvn = tuple(range(0x81, 0x8f + 1))
class SdKeyScp81Kvn81(SdKeyScp81):
name = 'SCP81 KVN81'
kvn = 0x81
class SdKeyScp81Kvn81Enc(SdKeyScp81Kvn81):
is_abstract = False
name = SdKeyScp81Kvn81.name + ' ENC'
key_id = 0x01
key_usage_qual = 0x18
class SdKeyScp81Kvn81Mac(SdKeyScp81Kvn81):
is_abstract = False
name = SdKeyScp81Kvn81.name + ' MAC'
key_id = 0x02
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
# "omitting" SdKeyScp81Kvn84 ... Kvn8f
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
# "omitting" SdKeyScp02Kvn23 ... Kvn2f
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
# "omitting" SdKeyScp03Kvn33 ... Kvn3f
def obtain_all_pe_from_pelist(l: List[ProfileElement], wanted_type: str) -> ProfileElement:
return (pe for pe in l if pe.type == wanted_type)

View File

@@ -63,6 +63,44 @@ class SaipTest(unittest.TestCase):
# 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,