diff --git a/docs/osmo-smdpp.rst b/docs/osmo-smdpp.rst index a84ebeea..ad7d902d 100644 --- a/docs/osmo-smdpp.rst +++ b/docs/osmo-smdpp.rst @@ -21,8 +21,9 @@ osmo-smdpp currently * uses test certificates copied from GSMA SGP.26 into `./smdpp-data/certs`, assuming that your osmo-smdppp would be running at the host name `testsmdpplus1.example.com` -* always provides the exact same profile to every request. The profile always has the same IMSI and - ICCID. +* doesn't understand profile state. Any profile can always be downloaded any number of times, irrespective + of the EID or whether it was donwloaded before +* doesn't perform any personalization, so the IMSI/ICCID etc. are always identical * **is absolutely insecure**, as it * does not perform any certificate verification @@ -83,7 +84,8 @@ and it will bind its plain-HTTP ES9+ interface to local TCP port 8000. The `smdpp-data/certs`` directory contains the DPtls, DPauth and DPpb as well as CI certificates used; they are copied from GSMA SGP.26 v2. -The `smdpp-data/upp` directory contains the UPP (Unprotected Profile Package) used. +The `smdpp-data/upp` directory contains the UPP (Unprotected Profile Package) used. The file names (without +.der suffix) are looked up by the matchingID parameter from the activation code presented by the LPA. DNS setup for your LPA diff --git a/osmo-smdpp.py b/osmo-smdpp.py index cfcd5f8b..8cb00824 100755 --- a/osmo-smdpp.py +++ b/osmo-smdpp.py @@ -135,6 +135,7 @@ class SmDppHttpServer: def __init__(self, server_hostname: str, ci_certs_path: str, use_brainpool: bool = False): self.server_hostname = server_hostname + self.upp_dir = os.path.realpath(os.path.join(DATA_DIR, 'upp')) self.ci_certs = self.load_certs_from_path(ci_certs_path) # load DPauth cert + key self.dp_auth = CertAndPrivkey(oid.id_rspRole_dp_auth_v2) @@ -344,6 +345,27 @@ class SmDppHttpServer: if euiccSigned1['serverChallenge'] != ss.serverChallenge: raise ApiError('8.1', '6.1', 'Verification failed') + # 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. + if euiccSigned1['ctxParams1'][0] == 'ctxParamsForCommonAuthentication': + cpca = euiccSigned1['ctxParams1'][1] + matchingId = cpca.get('matchingId', None) + if not matchingId: + # TODO: check if any pending profile downloads for the EID + raise ApiError('8.2.6', '3.8', 'Refused') + if matchingId: + # look up profile based on matchingID. We simply check if a given file exists for now.. + path = os.path.join(self.upp_dir, matchingId) + '.der' + # prevent directory traversal attack + if os.path.commonprefix((os.path.realpath(path),self.upp_dir)) != self.upp_dir: + raise ApiError('8.2.6', '3.8', 'Refused') + if not os.path.isfile(path) or not os.access(path, os.R_OK): + raise ApiError('8.2.6', '3.8', 'Refused') + ss.matchingId = matchingId + + # FIXME: we actually want to perform the profile binding herr, and read the profile metadat from the profile + # Put together profileMetadata + _bin ss.profileMetadata = ProfileMetadata(iccid_bin= h2b(swap_nibbles('89000123456789012358')), spn="OsmocomSPN", profile_name="OsmocomProfile") profileMetadata_bin = ss.profileMetadata.gen_store_metadata_request() @@ -425,7 +447,7 @@ class SmDppHttpServer: # TODO: Check if this order requires a Confirmation Code verification # Perform actual protection + binding of profile package (or return pre-bound one) - with open(os.path.join(DATA_DIR, 'upp', 'TS48 V2 eSIM_GTP_SAIP2.1_NoBERTLV.rename2der'), 'rb') as f: + with open(os.path.join(self.upp_dir, ss.matchingId)+'.der', 'rb') as f: upp = UnprotectedProfilePackage.from_der(f.read(), metadata=ss.profileMetadata) # HACK: Use empty PPP as we're still debuggin the configureISDP step, and we want to avoid # cluttering the log with stuff happening after the failure diff --git a/smdpp-data/upp/TS48 V2 eSIM_GTP_SAIP2.1_NoBERTLV.rename2der b/smdpp-data/upp/TS48v2_SAIP2.1_NoBERTLV.der similarity index 100% rename from smdpp-data/upp/TS48 V2 eSIM_GTP_SAIP2.1_NoBERTLV.rename2der rename to smdpp-data/upp/TS48v2_SAIP2.1_NoBERTLV.der diff --git a/smdpp-data/upp/TS48 V2 eSIM_GTP_SAIP2.3_NoBERTLV.rename2der b/smdpp-data/upp/TS48v2_SAIP2.3_NoBERTLV.der similarity index 100% rename from smdpp-data/upp/TS48 V2 eSIM_GTP_SAIP2.3_NoBERTLV.rename2der rename to smdpp-data/upp/TS48v2_SAIP2.3_NoBERTLV.der diff --git a/tests/test_esim_saip.py b/tests/test_esim_saip.py index 14c086b3..9e7afb2f 100755 --- a/tests/test_esim_saip.py +++ b/tests/test_esim_saip.py @@ -26,7 +26,7 @@ from pprint import pprint as pp class SaipTest(unittest.TestCase): - with open('smdpp-data/upp/TS48 V2 eSIM_GTP_SAIP2.3_NoBERTLV.rename2der', 'rb') as f: + with open('smdpp-data/upp/TS48v2_SAIP2.3_NoBERTLV.der', 'rb') as f: per_input = f.read() pes = ProfileElementSequence.from_der(per_input) expected_pet_list = ['header', 'mf', 'pukCodes', 'pinCodes', 'telecom', 'pinCodes', 'genericFileManagement', 'usim', 'opt-usim', 'pinCodes', 'akaParameter', 'gsm-access', 'df-5gs', 'df-saip','csim', 'opt-csim', 'pinCodes', 'cdmaParameter', 'isim', 'opt-isim', 'pinCodes', 'akaParameter', 'genericFileManagement', 'genericFileManagement', 'securityDomain', 'rfm', 'rfm', 'rfm', 'rfm', 'end']