From 56912caac7545276082bd9e48980e0ba33d84007 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 30 May 2024 19:23:15 +0200 Subject: [PATCH] osmo-smdpp: Don't re-encode euiccSigned1/euiccSigned2 We used to re-encode those parts of a decoded ASN.1 struct that is cryptographically signed in the GSMA SGP.22 specification. However, if the received data follows a later spec and contains new/unknown records, then our poor-man's attempt at re-encoding will render a different binary, which in turn means the signature check will fail. Let's instead do a manual step-by-step raw decode of the DER TLV structure to extract the actual binary information of parts of ASN.1 objects. Change-Id: I4e31fd4b23ec3be15b9d07c2c30a3e31e22bdda1 Closes: OS#6473 --- osmo-smdpp.py | 6 ++---- pySim/esim/rsp.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/osmo-smdpp.py b/osmo-smdpp.py index 3dc6131a..9ed1e39f 100755 --- a/osmo-smdpp.py +++ b/osmo-smdpp.py @@ -292,8 +292,7 @@ class SmDppHttpServer: r_ok = authenticateServerResp[1] euiccSigned1 = r_ok['euiccSigned1'] - # TODO: use original data, don't re-encode? - euiccSigned1_bin = rsp.asn1.encode('EuiccSigned1', euiccSigned1) + euiccSigned1_bin = rsp.extract_euiccSigned1(authenticateServerResp_bin) euiccSignature1_bin = r_ok['euiccSignature1'] euiccCertificate_dec = r_ok['euiccCertificate'] # TODO: use original data, don't re-encode? @@ -422,8 +421,7 @@ class SmDppHttpServer: # Verify the euiccSignature2 computed over euiccSigned2 and smdpSignature2 using the PK.EUICC.SIG attached to the ongoing RSP session euiccSigned2 = r_ok['euiccSigned2'] - # TODO: use original data, don't re-encode? - euiccSigned2_bin = rsp.asn1.encode('EUICCSigned2', euiccSigned2) + euiccSigned2_bin = rsp.extract_euiccSigned2(prepDownloadResp_bin) if not self._ecdsa_verify(ss.euicc_cert, r_ok['euiccSignature2'], euiccSigned2_bin + ss.smdpSignature2_do): raise ApiError('8.1', '6.1', 'eUICC signature is invalid') diff --git a/pySim/esim/rsp.py b/pySim/esim/rsp.py index ec317fc0..a0320318 100644 --- a/pySim/esim/rsp.py +++ b/pySim/esim/rsp.py @@ -24,6 +24,7 @@ from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.serialization import Encoding from cryptography import x509 +from pySim.utils import bertlv_parse_one, bertlv_encode_tag, bertlv_encode_len, b2h from pySim.esim import compile_asn1_subdir asn1 = compile_asn1_subdir('rsp') @@ -96,3 +97,41 @@ class RspSessionState: class RspSessionStore(shelve.DbfilenameShelf): """A derived class as wrapper around the database-backed non-volatile storage 'shelve', in case we might need to extend it in the future. We use it to store RspSessionState objects indexed by transactionId.""" + +def extract_euiccSigned1(authenticateServerResponse: bytes) -> bytes: + """Extract the raw, DER-encoded binary euiccSigned1 field from the given AuthenticateServerResponse. This + is needed due to the very peculiar SGP.22 notion of signing sections of DER-encoded ASN.1 objects.""" + tdict, l, v, remainder = bertlv_parse_one(authenticateServerResponse) + rawtag = bertlv_encode_tag(tdict) + if len(remainder): + raise ValueError('Excess data at end of TLV') + if b2h(rawtag) != 'bf38': + raise ValueError('Unexpected outer tag: %s' % b2h(rawtag)) + tdict, l, v1, remainder = bertlv_parse_one(v) + rawtag = bertlv_encode_tag(tdict) + if b2h(rawtag) != 'a0': + raise ValueError('Unexpected tag where CHOICE was expected') + tdict, l, v2, remainder = bertlv_parse_one(v1) + rawtag = bertlv_encode_tag(tdict) + if b2h(rawtag) != '30': + raise ValueError('Unexpected tag where SEQUENCE was expected') + return rawtag + bertlv_encode_len(l) + v2 + +def extract_euiccSigned2(prepareDownloadResponse: bytes) -> bytes: + """Extract the raw, DER-encoded binary euiccSigned2 field from the given prepareDownloadrResponse. This is + needed due to the very peculiar SGP.22 notion of signing sections of DER-encoded ASN.1 objects.""" + tdict, l, v, remainder = bertlv_parse_one(prepareDownloadResponse) + rawtag = bertlv_encode_tag(tdict) + if len(remainder): + raise ValueError('Excess data at end of TLV') + if b2h(rawtag) != 'bf21': + raise ValueError('Unexpected outer tag: %s' % b2h(rawtag)) + tdict, l, v1, remainder = bertlv_parse_one(v) + rawtag = bertlv_encode_tag(tdict) + if b2h(rawtag) != 'a0': + raise ValueError('Unexpected tag where CHOICE was expected') + tdict, l, v2, remainder = bertlv_parse_one(v1) + rawtag = bertlv_encode_tag(tdict) + if b2h(rawtag) != '30': + raise ValueError('Unexpected tag where SEQUENCE was expected') + return rawtag + bertlv_encode_len(l) + v2