laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/37168?usp=email )
Change subject: saip-tool: Add new 'info' action to print general information ......................................................................
saip-tool: Add new 'info' action to print general information
It will print something like this:
SAIP Profile Version: 2.1 Profile Type: 'GSMA Generic eUICC Test Profile' ICCID: 8949449999999990023f Mandatory Services: usim, isim, csim, javacard, usim-test-algorithm
NAAs: mf[1], usim[1], csim[1], isim[1] NAA mf NAA usim (a0000000871002ff49ff0589) IMSI: 001010123456063 NAA csim NAA isim (a0000000871004ff49ff0589)
Number of applications: 0
Change-Id: I107d457c3313a766229b569453c18a8d69134bec --- M contrib/saip-tool.py M pySim/esim/saip/__init__.py 2 files changed, 91 insertions(+), 3 deletions(-)
Approvals: laforge: Looks good to me, approved fixeria: Looks good to me, but someone else must approve Jenkins Builder: Verified
diff --git a/contrib/saip-tool.py b/contrib/saip-tool.py index 9bbaddf..68072fa 100755 --- a/contrib/saip-tool.py +++ b/contrib/saip-tool.py @@ -54,6 +54,7 @@ parser_rn.add_argument('--naa-type', required=True, choices=NAAs.keys(), help='Network Access Application type to remove') # TODO: add an --naa-index or the like, so only one given instance can be removed
+parser_info = subparsers.add_parser('info', help='Display information about the profile')
def do_split(pes: ProfileElementSequence, opts): i = 0 @@ -136,6 +137,45 @@ with open(opts.output_file, 'wb') as f: f.write(pes.to_der())
+def do_info(pes: ProfileElementSequence, opts): + def get_naa_count(pes: ProfileElementSequence) -> dict: + """return a dict with naa-type (usim, isim) as key and the count of NAA instances as value.""" + ret = {} + for naa_type in pes.pes_by_naa: + ret[naa_type] = len(pes.pes_by_naa[naa_type]) + return ret + + pe_hdr_dec = pes.pe_by_type['header'][0].decoded + print() + print("SAIP Profile Version: %u.%u" % (pe_hdr_dec['major-version'], pe_hdr_dec['minor-version'])) + print("Profile Type: '%s'" % pe_hdr_dec['profileType']) + print("ICCID: %s" % b2h(pe_hdr_dec['iccid'])) + print("Mandatory Services: %s" % ', '.join(pe_hdr_dec['eUICC-Mandatory-services'].keys())) + print() + naa_strs = ["%s[%u]" % (k, v) for k, v in get_naa_count(pes).items()] + print("NAAs: %s" % ', '.join(naa_strs)) + for naa_type in pes.pes_by_naa: + for naa_inst in pes.pes_by_naa[naa_type]: + first_pe = naa_inst[0] + adf_name = '' + if hasattr(first_pe, 'adf_name'): + adf_name = '(' + first_pe.adf_name + ')' + print("NAA %s %s" % (first_pe.type, adf_name)) + if hasattr(first_pe, 'imsi'): + print("\tIMSI: %s" % first_pe.imsi) + + # applications + print() + apps = pes.pe_by_type.get('application', []) + print("Number of applications: %u" % len(apps)) + for app_pe in apps: + print("App Load Package AID: %s" % b2h(app_pe.decoded['loadBlock']['loadPackageAID'])) + print("\tMandated: %s" % ('mandated' in app_pe.decoded['app-Header'])) + print("\tLoad Block Size: %s" % len(app_pe.decoded['loadBlock']['loadBlockObject'])) + for inst in app_pe.decoded.get('instanceList', []): + print("\tInstance AID: %s" % b2h(inst['instanceAID'])) + pass +
if __name__ == '__main__': opts = parser.parse_args() @@ -155,3 +195,5 @@ do_remove_pe(pes, opts) elif opts.command == 'remove-naa': do_remove_naa(pes, opts) + elif opts.command == 'info': + do_info(pes, opts) diff --git a/pySim/esim/saip/__init__.py b/pySim/esim/saip/__init__.py index ee2538c..278b5cf 100644 --- a/pySim/esim/saip/__init__.py +++ b/pySim/esim/saip/__init__.py @@ -22,7 +22,7 @@
import asn1tools
-from pySim.utils import bertlv_parse_tag, bertlv_parse_len, b2h, h2b +from pySim.utils import bertlv_parse_tag, bertlv_parse_len, b2h, h2b, dec_imsi from pySim.ts_102_221 import FileDescriptor from pySim.construct import build_construct from pySim.esim import compile_asn1_subdir @@ -263,10 +263,15 @@
@classmethod def from_der(cls, der: bytes) -> 'ProfileElement': + class4petype = { + 'securityDomain': ProfileElementSD, + 'usim': ProfileElementUSIM, + 'isim': ProfileElementISIM, + } """Construct an instance from given raw, DER encoded bytes.""" pe_type, decoded = asn1.decode('ProfileElement', der) - if pe_type == 'securityDomain': - inst = ProfileElementSD(decoded) + if pe_type in class4petype: + inst = class4petype[pe_type](decoded) else: inst = ProfileElement(decoded) inst.type = pe_type @@ -437,6 +442,22 @@ 'uiccToolkitApplicationSpecificParametersField': h2b('01000001000000020112036C756500'), }
+class ProfileElementUSIM(ProfileElement): + type = 'usim' + @property + def adf_name(self) -> str: + return b2h(self.decoded['adf-usim'][0][1]['dfName']) + @property + def imsi(self) -> Optional[str]: + f = File('ef-imsi', self.decoded['ef-imsi']) + return dec_imsi(b2h(f.stream.getvalue())) + +class ProfileElementISIM(ProfileElement): + type = 'isim' + @property + def adf_name(self) -> str: + return b2h(self.decoded['adf-isim'][0][1]['dfName']) + def bertlv_first_segment(binary: bytes) -> Tuple[bytes, bytes]: """obtain the first segment of a binary concatenation of BER-TLV objects. Returns: tuple of first TLV and remainder."""