Johannes Waigel has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/43205?usp=email )
Change subject: osmo-smdpp: derive the Profile Metadata from the Profile ......................................................................
osmo-smdpp: derive the Profile Metadata from the Profile
The metadata was invented: a hardcoded SPN, the MatchingID as profileName, and always 'operational' as profileClass. Test Profiles were therefore sent as operational ones, which SGP.22 section 2.4.5.3 forbids.
Add ProfileMetadata.from_pes() to read these fields from the Profile. An LPA now shows Test Profiles only in Device Test Mode.
Change-Id: Ie24dd5ac0ef541c1899dd923a9db568209bbfa62 --- M osmo-smdpp.py M pySim/esim/es8p.py 2 files changed, 35 insertions(+), 5 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/05/43205/1
diff --git a/osmo-smdpp.py b/osmo-smdpp.py index 2a8e478..0346afc 100755 --- a/osmo-smdpp.py +++ b/osmo-smdpp.py @@ -629,7 +629,7 @@ # If ctxParams1 contains a ctxParamsForCommonAuthentication data object, the SM-DP+ Shall [...] # TODO: We really do a very simplistic job here, this needs to be properly implemented later, # considering all the various cases, profile state, etc. - iccid_str = None + pes = None if euiccSigned1['ctxParams1'][0] == 'ctxParamsForCommonAuthentication': cpca = euiccSigned1['ctxParams1'][1] matchingId = cpca.get('matchingId', None) @@ -647,15 +647,14 @@ ss.matchingId = matchingId with open(path, 'rb') as f: pes = saip.ProfileElementSequence.from_der(f.read()) - iccid_str = b2h(pes.get_pe_for_type('header').decoded['iccid']) else: # there's currently no other option in the ctxParams1 choice, so this cannot happen raise ApiError('1.3.1', '2.2', 'ctxParams1 missing mandatory ctxParamsForCommonAuthentication')
- # FIXME: we actually want to perform the profile binding herr, and read the profile metadata from the profile + # FIXME: we actually want to perform the profile binding here
# Put together profileMetadata + _bin - ss.profileMetadata = ProfileMetadata(iccid_bin=h2b(swap_nibbles(iccid_str)), spn="OsmocomSPN", profile_name=matchingId) + ss.profileMetadata = ProfileMetadata.from_pes(pes, profile_name=matchingId) # enable notifications for all operations for event in ['enable', 'disable', 'delete']: ss.profileMetadata.add_notification(event, self.server_hostname) diff --git a/pySim/esim/es8p.py b/pySim/esim/es8p.py index 6926cae..48ecfdd 100644 --- a/pySim/esim/es8p.py +++ b/pySim/esim/es8p.py @@ -17,13 +17,14 @@
from typing import Dict, List, Optional from cryptography.hazmat.primitives.asymmetric import ec -from osmocom.utils import b2h, h2b +from osmocom.utils import b2h, h2b, swap_nibbles from osmocom.tlv import bertlv_encode_tag, bertlv_encode_len, bertlv_parse_one_rawtag from osmocom.tlv import bertlv_return_one_rawtlv
import pySim.esim.rsp as rsp from pySim.esim.bsp import BspInstance from pySim.esim import PMO +from pySim.ts_51_011 import EF_SPN
import logging logger = logging.getLogger(__name__) @@ -73,6 +74,18 @@ return rsp.asn1.encode('ReplaceSessionKeysRequest', rsk)
+def spn_from_pes(pes) -> Optional[str]: + """Read the Service Provider Name from EF.SPN of the given Profile, or None if it has none.""" + for pe_type in ['usim', 'gsm-access']: + pe = pes.get_pe_for_type(pe_type) + if not pe or 'ef-spn' not in getattr(pe, 'files', {}): + continue + content = pe.files['ef-spn'].file_content_from_tuples(pe.decoded['ef-spn']) + if content: + return EF_SPN().decode_hex(b2h(content))['spn'] + return None + + class ProfileMetadata: """Representation of Profile metadata. Right now only the mandatory bits are supported, but in general this should follow the StoreMetadataRequest of SGP.22 5.5.3""" @@ -85,6 +98,24 @@ self.icon_type = None self.notifications = []
+ @classmethod + def from_pes(cls, pes, profile_name: Optional[str] = None) -> 'ProfileMetadata': + """Derive Profile metadata from the Profile itself, rather than from a separate source that + can disagree with it. profile_name is only used if the Profile header has no profileType.""" + header = pes.get_pe_for_type('header').decoded + + # SGP.22 Section 2.4.5.3: a Test Profile SHALL have its Profile Class set to 'test'. A + # Profile requiring the USIM test algorithm is one; that is condition 3 of that section. + if 'usim-test-algorithm' in header.get('eUICC-Mandatory-services', {}): + profile_class = 'test' + else: + profile_class = 'operational' + + return cls(iccid_bin=h2b(swap_nibbles(b2h(header['iccid']))), + spn=spn_from_pes(pes) or '', + profile_name=header.get('profileType', profile_name) or '', + profile_class=profile_class) + def set_icon(self, is_png: bool, icon_data: bytes): """Set the icon that is part of the metadata.""" if len(icon_data) > 1024: