aboutsummaryrefslogtreecommitdiffstats
path: root/pySim/construct.py
diff options
context:
space:
mode:
authorRobert Falkenberg <robert.falkenberg@tu-dortmund.de>2021-05-07 15:23:20 +0200
committerRobert Falkenberg <robert.falkenberg@tu-dortmund.de>2021-05-10 06:15:39 +0200
commitb07a3e9c87fc742aab2515ce1e6f9e4d20c91cae (patch)
treea73e9b021eeeeede426702535923a6eb3a933d4c /pySim/construct.py
parentc957ce8adc4ecd5084cd8f7ed86b8c4e200c22f6 (diff)
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
Diffstat (limited to 'pySim/construct.py')
-rw-r--r--pySim/construct.py51
1 files changed, 51 insertions, 0 deletions
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 @@ class BcdAdapter(Adapter):
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 @@ def BytesRFU(n=1):
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')