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
This commit is contained in:
@@ -21,12 +21,13 @@ from typing import Tuple, List, Optional, Dict, Union
|
||||
|
||||
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 @@ class ProfileElement:
|
||||
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 @@ class ProfileElementSD(ProfileElement):
|
||||
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,28 @@ class ProfileElementSD(ProfileElement):
|
||||
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_version_number, key_id)
|
||||
if not key:
|
||||
raise ValueError('No key for KVN=0x%02x / KID=0x%02x found' % (key_version_number, key_id))
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user