laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/36958?usp=email )
Change subject: esim.saip: Implement SecurityDomainSD.{add,has,remove}_key() methods ......................................................................
esim.saip: Implement SecurityDomainSD.{add,has,remove}_key() methods
This way it's possible to programmatically inspect and modify the high-level decoded key material inside a securityDomain profile element.
Change-Id: I18b1444303de80eaddd840a7e0061ea0098a8ba1 --- M pySim/esim/saip/__init__.py 1 file changed, 93 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/58/36958/1
diff --git a/pySim/esim/saip/__init__.py b/pySim/esim/saip/__init__.py index fc86b63..ae733e1 100644 --- a/pySim/esim/saip/__init__.py +++ b/pySim/esim/saip/__init__.py @@ -21,12 +21,13 @@
import asn1tools
-from pySim.utils import bertlv_parse_tag, bertlv_parse_len +from pySim.utils import bertlv_parse_tag, bertlv_parse_len, b2h from pySim.ts_102_221 import FileDescriptor from pySim.construct import build_construct from pySim.esim import compile_asn1_subdir from pySim.esim.saip import templates from pySim.tlv import BER_TLV_IE +from pySim.global_platform import KeyType, KeyUsageQualifier from pySim.global_platform.uicc import UiccSdInstallParams
asn1 = compile_asn1_subdir('saip') @@ -219,6 +220,60 @@ def __str__(self) -> str: return self.type
+class SecurityDomainKeyComponent: + """Representation of a key-component of a key for a security domain.""" + def __init__(self, key_type: str, key_data: bytes, mac_length: int = 8): + self.key_type = key_type + self.key_data = key_data + self.mac_length = mac_length + + def __repr__(self) -> str: + return 'SdKeyComp(type=%s, mac_len=%u, data=%s)' % (self.key_type, self.mac_length, + b2h(self.key_data)) + + + @classmethod + def from_saip_dict(cls, saip: dict) -> 'SecurityDomainKeyComponent': + """Construct instance from the dict as generated by SAIP asn.1 decoder.""" + return cls(KeyType.parse(saip['keyType']), saip['keyData'], saip['macLength']) + + def to_saip_dict(self) -> dict: + """Express instance in the dict format required by SAIP asn.1 encoder.""" + return {'keyType': KeyType.build(self.key_type), + 'keyData': self.key_data, + 'macLength': self.mac_length} + +class SecurityDomainKey: + """Represenation of a key used for SCP access to a security domain.""" + def __init__(self, key_version_number: int, key_id: int, key_usage_qualifier: dict, + key_components: List[SecurityDomainKeyComponent]): + self.key_usage_qualifier = key_usage_qualifier + self.key_identifier = key_id + self.key_version_number = key_version_number + self.key_components = key_components + + def __repr__(self) -> str: + return 'SdKey(KVN=0x%02x, ID=0x%02x, Usage=%s, Comp=%s)' % (self.key_version_number, + self.key_identifier, + self.key_usage_qualifier, + repr(self.key_components)) + + @classmethod + def from_saip_dict(cls, saip: dict) -> 'SecurityDomainKey': + """Construct instance from the dict as generated by SAIP asn.1 decoder.""" + inst = cls(int.from_bytes(saip['keyVersionNumber'], "big"), + int.from_bytes(saip['keyIdentifier'], "big"), + KeyUsageQualifier.parse(saip['keyUsageQualifier']), + [SecurityDomainKeyComponent.from_saip_dict(x) for x in saip['keyComponents']]) + return inst + + def to_saip_dict(self) -> dict: + """Express instance in the dict format required by SAIP asn.1 encoder.""" + return {'keyUsageQualifier': KeyUsageQualifier.build(self.key_usage_qualifier), + 'keyIdentifier': bytes([self.key_identifier]), + 'keyVersionNumber': bytes([self.key_version_number]), + 'keyComponents': [k.to_saip_dict() for k in self.key_components]} + class ProfileElementSD(ProfileElement): """Class representing a securityDomain ProfileElement.""" type = 'securityDomain' @@ -229,8 +284,10 @@ def _post_decode(self): self.usip = self.C9() self.usip.from_bytes(self.decoded['instance']['applicationSpecificParametersC9']) + self.keys = [SecurityDomainKey.from_saip_dict(x) for x in self.decoded['keyList']]
def _pre_encode(self): + self.decoded['keyList'] = [x.to_saip_dict() for x in self.keys] self.decoded['instance']['applicationSpecificParametersC9'] = self.usip.to_bytes()
def has_scp(self, scp: int) -> bool: @@ -248,6 +305,29 @@ self.usip.nested_collection.remove_scp(scp) self._pre_encode()
+ def find_key(self, key_version_number: int, key_id: int) -> Optional[SecurityDomainKey]: + """Find and return (if any) the SecurityDomainKey for given KVN + KID.""" + for k in self.keys: + if k.key_version_number == key_version_number and k.key_identifier == key_id: + return k + return None + + def add_key(self, key: SecurityDomainKey): + """Add a given SecurityDomainKey to the keyList of the securityDomain.""" + if self.find_key(key.key_version_number, key.key_identifier): + raise ValueError('Key for KVN=0x%02x / KID=0x%02x already exists' % (key.key_version_number, + key.key_identifier)) + self.keys.append(key) + self._pre_encode() + + def remove_key(self, key_version_number: int, key_id: int): + key = self.find_key(key.key_version_number, key.key_identifier) + if not key: + raise ValueError('No key for KVN=0x%02x / KID=0x%02x found' % (key.key_version_number, + key.key_identifier)) + self.keys.remove(key) + self._pre_encode() +
def bertlv_first_segment(binary: bytes) -> Tuple[bytes, bytes]: """obtain the first segment of a binary concatenation of BER-TLV objects.