Change in pysim[master]: Add codecs for EF_SPN and GSM strings via construct

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

laforge gerrit-no-reply at lists.osmocom.org
Thu May 13 17:56:36 UTC 2021


laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/24180 )

Change subject: Add codecs for EF_SPN and GSM strings via construct
......................................................................

Add codecs for EF_SPN and GSM strings via construct

This will replace the hand-crafted codec for EF_SPN
by a struct definition using the construct library.
Old encoders are updated and kept for API compatibility
but are not used internally anymore.

New data structures:
* Rpad(Adapter): Right-padded bytestring (0xff, adjustable)
* GsmStringAdapter(Adapter): Codec for "SMS default 7-bit
	coded alphabet as defined int TS 23.038" using
	the gsm0338 library.
* GsmString(n): Convenient wrapper of both above

Adjustments:
* utils: update+deprecate old dec_spn(), enc_spn()
* remove refs to deprecated functions

Change-Id: Ia1d3a3835933bac0002b7c52511481dd8094b994
---
M contrib/jenkins.sh
M pySim-read.py
M pySim/cards.py
M pySim/construct.py
M pySim/ts_51_011.py
M pySim/utils.py
M requirements.txt
M setup.py
8 files changed, 97 insertions(+), 27 deletions(-)

Approvals:
  Jenkins Builder: Verified
  dexter: Looks good to me, but someone else must approve
  laforge: Looks good to me, approved



diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh
index ed5ba25..27f0245 100755
--- a/contrib/jenkins.sh
+++ b/contrib/jenkins.sh
@@ -23,6 +23,7 @@
 pip install jsonpath-ng
 pip install construct
 pip install bidict
+pip install gsm0338
 
 # Execute automatically discovered unit tests first
 python -m unittest discover -v -s tests/
diff --git a/pySim-read.py b/pySim-read.py
index ebe0e29..9feef9d 100755
--- a/pySim-read.py
+++ b/pySim-read.py
@@ -36,7 +36,7 @@
 from pySim.transport import init_reader, argparse_add_reader_args
 from pySim.cards import card_detect, Card, UsimCard, IsimCard
 from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid, dec_msisdn
-from pySim.utils import format_xplmn_w_act, dec_spn, dec_st, dec_addr_tlv
+from pySim.utils import format_xplmn_w_act, dec_st, dec_addr_tlv
 from pySim.utils import h2s, format_ePDGSelection
 
 option_parser = argparse.ArgumentParser(prog='pySim-read',
diff --git a/pySim/cards.py b/pySim/cards.py
index 30857b3..dcba26c 100644
--- a/pySim/cards.py
+++ b/pySim/cards.py
@@ -25,7 +25,7 @@
 from typing import Optional, Dict, Tuple
 import abc
 
-from pySim.ts_51_011 import EF, DF, EF_AD
+from pySim.ts_51_011 import EF, DF, EF_AD, EF_SPN
 from pySim.ts_31_102 import EF_USIM_ADF_map
 from pySim.ts_31_103 import EF_ISIM_ADF_map
 from pySim.utils import *
@@ -203,15 +203,24 @@
 		return sw
 
 	def read_spn(self):
-		(spn, sw) = self._scc.read_binary(EF['SPN'])
+		(content, sw) = self._scc.read_binary(EF['SPN'])
 		if sw == '9000':
-			return (dec_spn(spn), sw)
+			abstract_data = EF_SPN().decode_hex(content)
+			show_in_hplmn = abstract_data['show_in_hplmn']
+			hide_in_oplmn = abstract_data['hide_in_oplmn']
+			name = abstract_data['spn']
+			return ((name, show_in_hplmn, hide_in_oplmn), sw)
 		else:
 			return (None, sw)
 
-	def update_spn(self, name, hplmn_disp=False, oplmn_disp=False):
-		content = enc_spn(name, hplmn_disp, oplmn_disp)
-		data, sw = self._scc.update_binary(EF['SPN'], rpad(content, 32))
+	def update_spn(self, name="", show_in_hplmn=False, hide_in_oplmn=False):
+		abstract_data = {
+			'hide_in_oplmn' : hide_in_oplmn,
+			'show_in_hplmn' : show_in_hplmn,
+			'spn' : name,
+		}
+		content = EF_SPN().encode_hex(abstract_data)
+		data, sw = self._scc.update_binary(EF['SPN'], content)
 		return sw
 
 	def read_binary(self, ef, length=None, offset=0):
@@ -915,8 +924,7 @@
 
 		# set Service Provider Name
 		if p.get('name') is not None:
-			content = enc_spn(p['name'], True, True)
-			data, sw = self._scc.update_binary('6F46', rpad(content, 32))
+			self.update_spn(p['name'], True, True)
 
 		if p.get('acc') is not None:
 			self.update_acc(p['acc'])
@@ -1310,8 +1318,7 @@
 
 		# set Service Provider Name
 		if p.get('name') is not None:
-			content = enc_spn(p['name'], True, True)
-			data, sw = self._scc.update_binary('6F46', rpad(content, 32))
+			self.update_spn(p['name'], True, True)
 
 		# write EF.IMSI
 		if p.get('imsi'):
diff --git a/pySim/construct.py b/pySim/construct.py
index b0f03b7..a903305 100644
--- a/pySim/construct.py
+++ b/pySim/construct.py
@@ -1,5 +1,6 @@
 from construct import *
 from pySim.utils import b2h, h2b, swap_nibbles
+import gsm0338
 
 """Utility code related to the integration of the 'construct' declarative parser."""
 
@@ -33,6 +34,42 @@
     def _encode(self, obj, context, path):
         return h2b(swap_nibbles(obj))
 
+class Rpad(Adapter):
+    """
+    Encoder appends padding bytes (b'\\xff') up to target size.
+    Decoder removes trailing padding bytes.
+
+    Parameters:
+        subcon: Subconstruct as defined by construct library
+        pattern: set padding pattern (default: b'\\xff')
+    """
+
+    def __init__(self, subcon, pattern=b'\xff'):
+        super().__init__(subcon)
+        self.pattern = pattern
+
+    def _decode(self, obj, context, path):
+        return obj.rstrip(self.pattern)
+
+    def _encode(self, obj, context, path):
+        if len(obj) > self.sizeof():
+            raise SizeofError("Input ({}) exceeds target size ({})".format(len(obj), self.sizeof()))
+        return obj + self.pattern * (self.sizeof() - len(obj))
+
+class GsmStringAdapter(Adapter):
+    """Convert GSM 03.38 encoded bytes to a string."""
+
+    def __init__(self, subcon, codec='gsm03.38', err='strict'):
+        super().__init__(subcon)
+        self.codec = codec
+        self.err = err
+
+    def _decode(self, obj, context, path):
+        return obj.decode(self.codec)
+
+    def _encode(self, obj, context, path):
+        return obj.encode(self.codec, self.err)
+
 def filter_dict(d, exclude_prefix='_'):
     """filter the input dict to ensure no keys starting with 'exclude_prefix' remain."""
     res = {}
@@ -88,3 +125,17 @@
         n (Integer): Number of bytes (default: 1)
     '''
     return Default(Bytes(n), __RFU_VALUE)
+
+def GsmString(n):
+    '''
+    GSM 03.38 encoded byte string of fixed length n.
+    Encoder appends padding bytes (b'\\xff') to maintain
+    length. Decoder removes those trailing bytes.
+
+    Exceptions are raised for invalid characters
+    and length excess.
+
+    Parameters:
+        n (Integer): Fixed length of the encoded byte string
+    '''
+    return GsmStringAdapter(Rpad(Bytes(n), pattern=b'\xff'), codec='gsm03.38')
diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py
index dba0369..6ab07f0 100644
--- a/pySim/ts_51_011.py
+++ b/pySim/ts_51_011.py
@@ -323,7 +323,7 @@
 from struct import pack, unpack
 from construct import *
 from construct import Optional as COptional
-from pySim.construct import HexAdapter, BcdAdapter, FlagRFU, ByteRFU, GreedyBytesRFU, BitsRFU, BytesRFU
+from pySim.construct import *
 import enum
 
 from pySim.filesystem import *
@@ -519,11 +519,14 @@
 class EF_SPN(TransparentEF):
     def __init__(self, fid='6f46', sfid=None, name='EF.SPN', desc='Service Provider Name', size={17,17}):
         super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size)
-    def _decode_hex(self, raw_hex):
-        return {'spn': dec_spn(raw_hex)}
-    def _encode_hex(self, abstract):
-        spn = abstract['spn']
-        return enc_spn(spn[0], spn[1], spn[2])
+        self._construct = BitStruct(
+            # Byte 1
+            'rfu'/BitsRFU(6),
+            'hide_in_oplmn'/Flag,
+            'show_in_hplmn'/Flag,
+            # Bytes 2..17
+            'spn'/Bytewise(GsmString(16))
+        )
 
 # TS 51.011 Section 10.3.13
 class EF_CBMI(TransRecEF):
diff --git a/pySim/utils.py b/pySim/utils.py
index a3cd1b5..3a8ddac 100644
--- a/pySim/utils.py
+++ b/pySim/utils.py
@@ -248,17 +248,23 @@
 	return res
 
 def dec_spn(ef):
-	byte1 = int(ef[0:2])
-	hplmn_disp = (byte1&0x01 == 0x01)
-	oplmn_disp = (byte1&0x02 == 0x02)
-	name = h2s(ef[2:])
-	return (name, hplmn_disp, oplmn_disp)
+	"""Obsolete, kept for API compatibility"""
+	from ts_51_011 import EF_SPN
+	abstract_data = EF_SPN().decode_hex(ef)
+	show_in_hplmn = abstract_data['show_in_hplmn']
+	hide_in_oplmn = abstract_data['hide_in_oplmn']
+	name = abstract_data['spn']
+	return (name, show_in_hplmn, hide_in_oplmn)
 
-def enc_spn(name:str, hplmn_disp=False, oplmn_disp=False):
-	byte1 = 0x00
-	if hplmn_disp: byte1 = byte1|0x01
-	if oplmn_disp: byte1 = byte1|0x02
-	return i2h([byte1])+s2h(name)
+def enc_spn(name:str, show_in_hplmn=False, hide_in_oplmn=False):
+	"""Obsolete, kept for API compatibility"""
+	from ts_51_011 import EF_SPN
+	abstract_data = {
+		'hide_in_oplmn' : hide_in_oplmn,
+		'show_in_hplmn' : show_in_hplmn,
+		'spn' : name,
+	}
+	return EF_SPN().encode_hex(abstract_data)
 
 def hexstr_to_Nbytearr(s, nbytes):
 	return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2)) ]
diff --git a/requirements.txt b/requirements.txt
index 6e8336c..10e9667 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,3 +5,4 @@
 jsonpath-ng
 construct
 bidict
+gsm0338
diff --git a/setup.py b/setup.py
index 08608dc..2ef498f 100644
--- a/setup.py
+++ b/setup.py
@@ -16,6 +16,7 @@
         "jsonpath-ng",
         "construct >= 2.9",
         "bidict",
+        "gsm0338",
     ],
     scripts=[
         'pySim-prog.py',

-- 
To view, visit https://gerrit.osmocom.org/c/pysim/+/24180
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: Ia1d3a3835933bac0002b7c52511481dd8094b994
Gerrit-Change-Number: 24180
Gerrit-PatchSet: 2
Gerrit-Owner: Falkenber9 <robert.falkenberg at tu-dortmund.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-CC: fixeria <vyanitskiy at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210513/2eb77519/attachment.htm>


More information about the gerrit-log mailing list