laforge has uploaded this change for review.

View Change

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.

To view, visit change 36958. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I18b1444303de80eaddd840a7e0061ea0098a8ba1
Gerrit-Change-Number: 36958
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge@osmocom.org>
Gerrit-MessageType: newchange