From ee7be445285334ccce1734e003c3222662d922a0 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 2 Jun 2024 12:15:13 +0200 Subject: [PATCH] utils: Introduce BER-TLV parsers that return raw tag or even raw TLV In the eSIM RSP univers there are some rather ugly layering violatoins where ASN.1 cannot be parsed but we have to mess with raw TLVs and the details of DER encoding. Let's add two funtions that make it more convenient to work with this: They return the raw tag as integer, or even the entire encoded TLV rather than the value part only. Change-Id: I1e68a4003b833e86e9282c77325afa86ce144b98 --- pySim/esim/rsp.py | 36 +++++++++++++++--------------------- pySim/utils.py | 26 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/pySim/esim/rsp.py b/pySim/esim/rsp.py index 85dacee4..c2a163ba 100644 --- a/pySim/esim/rsp.py +++ b/pySim/esim/rsp.py @@ -24,7 +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.utils import bertlv_parse_one_rawtag, bertlv_return_one_rawtlv, b2h from pySim.esim import compile_asn1_subdir asn1 = compile_asn1_subdir('rsp') @@ -101,37 +101,31 @@ class RspSessionStore(shelve.DbfilenameShelf): 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) + rawtag, l, v, remainder = bertlv_parse_one_rawtag(authenticateServerResponse) if len(remainder): raise ValueError('Excess data at end of TLV') - if b2h(rawtag) != 'bf38': + if rawtag != 0xbf38: 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': + rawtag, l, v1, remainder = bertlv_parse_one_rawtag(v) + if rawtag != 0xa0: 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': + rawtag, l, tlv2, remainder = bertlv_return_one_rawtlv(v1) + if rawtag != 0x30: raise ValueError('Unexpected tag where SEQUENCE was expected') - return rawtag + bertlv_encode_len(l) + v2 + return tlv2 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) + rawtag, l, v, remainder = bertlv_parse_one_rawtag(prepareDownloadResponse) if len(remainder): raise ValueError('Excess data at end of TLV') - if b2h(rawtag) != 'bf21': + if rawtag != 0xbf21: 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': + rawtag, l, v1, remainder = bertlv_parse_one_rawtag(v) + if rawtag != 0xa0: 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': + rawtag, l, tlv2, remainder = bertlv_return_one_rawtlv(v1) + if rawtag != 0x30: raise ValueError('Unexpected tag where SEQUENCE was expected') - return rawtag + bertlv_encode_len(l) + v2 + return tlv2 diff --git a/pySim/utils.py b/pySim/utils.py index afa476b2..2362b591 100644 --- a/pySim/utils.py +++ b/pySim/utils.py @@ -359,6 +359,32 @@ def bertlv_parse_one(binary: bytes) -> Tuple[dict, int, bytes, bytes]: remainder = remainder[length:] return (tagdict, length, value, remainder) +def bertlv_parse_one_rawtag(binary: bytes) -> Tuple[int, int, bytes, bytes]: + """Parse a single TLV IE at the start of the given binary data; return tag as raw integer. + Args: + binary : binary input data of BER-TLV length field + Returns: + Tuple of (tag:int, len:int, remainder:bytes) + """ + (tag, remainder) = bertlv_parse_tag_raw(binary) + (length, remainder) = bertlv_parse_len(remainder) + value = remainder[:length] + remainder = remainder[length:] + return (tag, length, value, remainder) + +def bertlv_return_one_rawtlv(binary: bytes) -> Tuple[int, int, bytes, bytes]: + """Return one single [encoded] TLV IE at the start of the given binary data. + Args: + binary : binary input data of BER-TLV length field + Returns: + Tuple of (tag:int, len:int, tlv:bytes, remainder:bytes) + """ + (tag, remainder) = bertlv_parse_tag_raw(binary) + (length, remainder) = bertlv_parse_len(remainder) + tl_length = len(binary) - len(remainder) + value = binary[:tl_length] + remainder[:length] + remainder = remainder[length:] + return (tag, length, value, remainder) def dgi_parse_tag_raw(binary: bytes) -> Tuple[int, bytes]: # In absence of any clear spec guidance we assume it's always 16 bit