laforge has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/pysim/+/35733?usp=email )


Change subject: WIP: profile processing; merging with templates
......................................................................

WIP: profile processing; merging with templates

Change-Id: Ib1674920e488ade9597cb039e4e2047dcbc7864e
---
M pySim/esim/saip/__init__.py
M pySim/esim/saip/validation.py
A saip-test.py
3 files changed, 261 insertions(+), 0 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/33/35733/1

diff --git a/pySim/esim/saip/__init__.py b/pySim/esim/saip/__init__.py
index 9b182e5..d8bc4d7 100644
--- a/pySim/esim/saip/__init__.py
+++ b/pySim/esim/saip/__init__.py
@@ -16,12 +16,16 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.

 import abc
+import io
 from typing import Tuple, List, Optional, Dict

 import asn1tools

 from pySim.utils import bertlv_parse_tag, bertlv_parse_len
+from pySim.ts_102_221 import FileDescriptor
+from pySim.construct import build_construct
 from pySim.esim import compile_asn1_subdir
+import pySim.esim.saip.templates as templates

 asn1 = compile_asn1_subdir('saip')

@@ -81,8 +85,104 @@
     IoT_default = eOID("2.17")
     IoT_default = eOID("2.18")

+class File:
+    """Internal representation of a file in a profile filesystem."""
+    def __init__(self, pename: str, l: Optional[List[Tuple]] = None, template: 
Optional[templates.FileTemplate] = None):
+        self.pe_name = pename
+        self.template = template
+        self.fileDescriptor = {}
+        self.stream = None
+        # apply some defaults from profile
+        if self.template:
+            self.from_template(self.template)
+        print("after template: %s" % repr(self))
+        if l:
+            self.from_tuples(l)
+
+    def from_template(self, template: templates.FileTemplate):
+        """Determine defaults for file based on given FileTemplate."""
+        fdb_dec = {}
+        self.rec_len = None
+        if template.fid:
+            self.fileDescriptor['fileID'] = template.fid.to_bytes(2, 'big')
+        if template.sfi:
+            self.fileDescriptor['shortEFID'] = bytes([template.sfi])
+        if template.arr:
+            self.fileDescriptor['securityAttributesReferenced'] = 
bytes([template.arr])
+        # All the files defined in the templates shall have, by default, 
shareable/not-shareable bit in the file descriptor set to "shareable".
+        fdb_dec['shareable'] = True
+        if template.file_type in ['LF', 'CY']:
+            fdb_dec['file_type'] = 'working_ef'
+            if template.rec_len:
+                self.record_len = template.rec_len
+            if template.nb_rec and template.rec_len:
+                self.fileDescriptor['efFileSize'] = (template.nb_rec * 
template.rec_len).to_bytes(2, 'big') # FIXME
+            if template.file_type == 'LF':
+                fdb_dec['structure'] = 'linear_fixed'
+            elif template.file_type == 'CY':
+                fdb_dec['structure'] = 'cyclic'
+        elif template.file_type in ['TR', 'BT']:
+            fdb_dec['file_type'] = 'working_ef'
+            if template.file_size:
+                self.fileDescriptor['efFileSize'] = 
template.file_size.to_bytes(2, 'big') # FIXME
+            if template.file_type == 'BT':
+                fdb_dec['structure'] = 'ber_tlv'
+            elif template.file_type == 'TR':
+                fdb_dec['structure'] = 'transparent'
+        elif template.file_type in ['MF', 'DF', 'ADF']:
+            fdb_dec['file_type'] = 'df'
+            fdb_dec['structure'] = 'no_info_given'
+        # build file descriptor based on above input data
+        fd_dict = {'file_descriptor_byte': fdb_dec}
+        if self.rec_len:
+            fd_dict['record_len'] = self.rec_len
+        self.fileDescriptor['fileDescriptor'] = 
build_construct(FileDescriptor._construct, fd_dict)
+        # FIXME: default_val
+        # FIXME: high_update
+        # FIXME: params?
+
+    def from_tuples(self, l:List[Tuple]):
+        """Parse a list of fileDescriptor, fillFileContent, fillFileOffset 
tuples into this instance."""
+        def get_fileDescriptor(l:List[Tuple]):
+            for k, v in l:
+                if k == 'fileDescriptor':
+                    return v
+        fd = get_fileDescriptor(l)
+        if not fd:
+            raise ValueError("No fileDescriptor found")
+        self.fileDescriptor.update(dict(fd))
+        self.stream = self.linearize_file_content(l)
+
+    def to_tuples() -> List[Tuple]:
+        """Generate a list of fileDescriptor, fillFileContent, fillFileOffset 
tuples into this instance."""
+        raise NotImplementedError
+
+    @staticmethod
+    def linearize_file_content(l: List[Tuple]) -> Optional[io.BytesIO]:
+        """linearize a list of fillFileContent + fillFileOffset tuples."""
+        stream = io.BytesIO()
+        for k, v in l:
+            if k == 'doNotCreate':
+                return None
+            if k == 'fileDescriptor':
+                pass
+            elif k == 'fillFileOffset':
+                stream.write(b'\xff' * v)
+            elif k == 'fillFileContent':
+                stream.write(v)
+            else:
+                return ValueError("Unknown key '%s' in tuple list" % k)
+        return stream
+
+    def __str__(self) -> str:
+        return "File(%s)" % self.pe_name
+
+    def __repr__(self) -> str:
+        return "File(%s): %s" % (self.pe_name, self.fileDescriptor)

 class ProfileElement:
+    FILE_BEARING = ['mf', 'cd', 'telecom', 'usim', 'opt-usim', 'isim', 
'opt-isim', 'phonebook', 'gsm-access',
+                    'csim', 'opt-csim', 'eap', 'df-5gs', 'df-saip', 'df-snpn', 
'df-5gprose', 'iot', 'opt-iot']
     def _fixup_sqnInit_dec(self):
         """asn1tools has a bug when working with SEQUENCE OF that have DEFAULT 
values. Let's work around
         this."""
@@ -116,6 +216,29 @@
         # work around asn1tools bug regarding DEFAULT for a SEQUENCE OF
         self._fixup_sqnInit_dec()

+    @property
+    def header_name(self) -> str:
+        # unneccessarry compliaction by inconsistent naming :(
+        if self.type.startswith('opt-'):
+            return self.type.replace('-','') + '-header'
+        else:
+            return self.type + '-header'
+
+    @property
+    def header(self):
+        return self.decoded.get(self.header_name, None)
+
+    @property
+    def templateID(self):
+        return self.decoded.get('templateID', None)
+
+    @property
+    def files(self):
+        """Return dict of decoded 'File' ASN.1 items."""
+        if not self.type in self.FILE_BEARING:
+            return {}
+        return {k:v for (k,v) in self.decoded.items() if k not in 
['templateID', self.header_name]}
+
     @classmethod
     def from_der(cls, der: bytes) -> 'ProfileElement':
         """Construct an instance from given raw, DER encoded bytes."""
diff --git a/pySim/esim/saip/validation.py b/pySim/esim/saip/validation.py
index 2acc413..acf1864 100644
--- a/pySim/esim/saip/validation.py
+++ b/pySim/esim/saip/validation.py
@@ -94,3 +94,36 @@
             raise ProfileError('profile-a-x25519 mandatory, but no usim or 
isim')
         if 'profile-a-p256' in m_svcs and not not ('usim' in m_svcs or 'isim' 
in m_svcs):
             raise ProfileError('profile-a-p256 mandatory, but no usim or isim')
+
+FileChoiceList = List[Tuple]
+
+class FileError(ProfileError):
+    pass
+
+class FileConstraintChecker:
+    def check(self, l: FileChoiceList):
+        for name in dir(self):
+            if name.startswith('check_'):
+                method = getattr(self, name)
+                method(l)
+
+class FileCheckBasicStructure(FileConstraintChecker):
+    def check_seqence(self, l: FileChoiceList):
+        by_type = {}
+        for k, v in l:
+            if k in by_type:
+                by_type[k].append(v)
+            else:
+                by_type[k] = [v]
+        if 'doNotCreate' in by_type:
+            if len(l) != 1:
+                raise FileError("doNotCreate must be the only element")
+        if 'fileDescriptor' in by_type:
+            if len(by_type['fileDescriptor']) != 1:
+                raise FileError("fileDescriptor must be the only element")
+            if l[0][0] != 'fileDescriptor':
+                raise FileError("fileDescriptor must be the first element")
+
+    def check_forbidden(self, l: FileChoiceList):
+        """Perform checks for forbidden parameters as described in Section 
8.3.3."""
+
diff --git a/saip-test.py b/saip-test.py
new file mode 100755
index 0000000..223bad7
--- /dev/null
+++ b/saip-test.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+
+from pySim.utils import b2h, h2b
+from pySim.esim.saip import *
+from pySim.esim.saip.validation import *
+
+from pySim.pprint import HexBytesPrettyPrinter
+
+pp = HexBytesPrettyPrinter(indent=4,width=500)
+
+import abc
+
+
+
+
+with open('smdpp-data/upp/TS48 V2 eSIM_GTP_SAIP2.3_NoBERTLV.rename2der', 'rb') 
as f:
+    pes = ProfileElementSequence.from_der(f.read())
+
+if False:
+    for pe in pes.pe_list:
+        print("="*70 + " " + pe.type)
+        pp.pprint(pe.decoded)
+
+if False:
+    for pe in pes:
+        print("="*70 + " " + pe.type)
+        pp.pprint(pe.decoded)
+
+if False:
+    for pe_type in pes.pe_by_type.keys():
+        print("="*70 + " " + pe_type)
+        for pe in pes.pe_by_type[pe_type]:
+            pp.pprint(pe)
+            pp.pprint(pe.decoded)
+
+checker = CheckBasicStructure()
+checker.check(pes)
+
+if False:
+    for naa in pes.pes_by_naa:
+        i = 0
+        for naa_instance in pes.pes_by_naa[naa]:
+            print("="*70 + " " + naa + str(i))
+            i += 1
+            for pe in naa_instance:
+                pp.pprint(pe.type)
+                for d in pe.decoded:
+                    print("    %s" % d)
+                    #pp.pprint(pe.decoded[d])
+                #if pe.type in ['akaParameter', 'pinCodes', 'pukCodes']:
+                #    pp.pprint(pe.decoded)
+
+
+from pySim.esim.saip.personalization import *
+
+params = [Iccid(h2b('98494400000000000001')),
+          Puk1(value=b'01234567'), Puk2(value=b'98765432'), Pin1(b'1111'), 
Pin2(b'2222'), Adm1(b'11111111'),
+          K(h2b('000102030405060708090a0b0c0d0e0f')), 
Opc(h2b('101112131415161718191a1b1c1d1e1f'))]
+
+from pySim.esim.saip.templates import *
+import pySim.esim.saip.templates.v31
+
+for p in params:
+    p.apply(pes)
+
+if False:
+    for pe in pes:
+        pp.pprint(pe.decoded)
+        pass
+
+naas = pes.pes_by_naa.keys()
+for naa in naas:
+    for pe in pes.pes_by_naa[naa][0]:
+        print(pe)
+        #pp.pprint(pe.decoded)
+        #print(pe.header)
+        tpl_id = pe.templateID
+        if tpl_id:
+            prof = ProfileTemplateRegistry.get_by_oid(tpl_id)
+            print(prof)
+            #pp.pprint(pe.decoded)
+        for fname, fdata in pe.files.items():
+            print()
+            print("============== %s" % fname)
+            ftempl = None
+            if prof:
+                ftempl = prof.files_by_pename[fname]
+            print("Template: %s" % repr(ftempl))
+            print("Data: %s" % fdata)
+            file = File(fname, fdata, ftempl)
+            print(repr(file))
+            #pp.pprint(pe.files)
+
+
+
+#print(ProfileTemplateRegistry.by_oid)

--
To view, visit https://gerrit.osmocom.org/c/pysim/+/35733?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: Ib1674920e488ade9597cb039e4e2047dcbc7864e
Gerrit-Change-Number: 35733
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <[email protected]>
Gerrit-MessageType: newchange

Reply via email to