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
This commit is contained in:
Harald Welte
2024-05-30 19:23:15 +02:00
parent 3dabbafdba
commit 56912caac7
2 changed files with 41 additions and 4 deletions

View File

@@ -292,8 +292,7 @@ class SmDppHttpServer:
r_ok = authenticateServerResp[1] r_ok = authenticateServerResp[1]
euiccSigned1 = r_ok['euiccSigned1'] euiccSigned1 = r_ok['euiccSigned1']
# TODO: use original data, don't re-encode? euiccSigned1_bin = rsp.extract_euiccSigned1(authenticateServerResp_bin)
euiccSigned1_bin = rsp.asn1.encode('EuiccSigned1', euiccSigned1)
euiccSignature1_bin = r_ok['euiccSignature1'] euiccSignature1_bin = r_ok['euiccSignature1']
euiccCertificate_dec = r_ok['euiccCertificate'] euiccCertificate_dec = r_ok['euiccCertificate']
# TODO: use original data, don't re-encode? # 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 # Verify the euiccSignature2 computed over euiccSigned2 and smdpSignature2 using the PK.EUICC.SIG attached to the ongoing RSP session
euiccSigned2 = r_ok['euiccSigned2'] euiccSigned2 = r_ok['euiccSigned2']
# TODO: use original data, don't re-encode? euiccSigned2_bin = rsp.extract_euiccSigned2(prepDownloadResp_bin)
euiccSigned2_bin = rsp.asn1.encode('EUICCSigned2', euiccSigned2)
if not self._ecdsa_verify(ss.euicc_cert, r_ok['euiccSignature2'], euiccSigned2_bin + ss.smdpSignature2_do): 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') raise ApiError('8.1', '6.1', 'eUICC signature is invalid')

View File

@@ -24,6 +24,7 @@ from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import Encoding from cryptography.hazmat.primitives.serialization import Encoding
from cryptography import x509 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 from pySim.esim import compile_asn1_subdir
asn1 = compile_asn1_subdir('rsp') asn1 = compile_asn1_subdir('rsp')
@@ -96,3 +97,41 @@ class RspSessionState:
class RspSessionStore(shelve.DbfilenameShelf): class RspSessionStore(shelve.DbfilenameShelf):
"""A derived class as wrapper around the database-backed non-volatile storage 'shelve', in case we might """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.""" 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