aboutsummaryrefslogtreecommitdiffstats
path: root/pySim/construct.py
diff options
context:
space:
mode:
authorHarald Welte <laforge@osmocom.org>2024-02-04 18:55:47 +0100
committerHarald Welte <laforge@osmocom.org>2024-02-05 01:39:39 +0100
commit7156a40187325259a023312747d4c55ec530747e (patch)
tree1c1486def4ee395132bf01ef8b988f04aa0da027 /pySim/construct.py
parentcd8e16fdfec03b4124e5087930b4dc7cdb668826 (diff)
construct: Add StripTrailerAdapter
In smart cards, we every so often encounter data types that contain a bit-mask whose length depends on whether or not there are any of the least-significant bits are set. So far we worked around this with some kind of Struct('byte1', 'byte2'/COptional, 'byte3'/COptional) approach. Let's do thisin a generic way using the new StripTrailerAdapter. Change-Id: I659aa7247c57c680895b0bf8412f9e477fc3587d
Diffstat (limited to 'pySim/construct.py')
-rw-r--r--pySim/construct.py31
1 files changed, 31 insertions, 0 deletions
diff --git a/pySim/construct.py b/pySim/construct.py
index 988dbf3..8ef7941 100644
--- a/pySim/construct.py
+++ b/pySim/construct.py
@@ -365,6 +365,37 @@ class Ipv6Adapter(Adapter):
ia = ipaddress.IPv6Address(obj)
return ia.packed
+class StripTrailerAdapter(Adapter):
+ """
+ Encoder removes all trailing bytes matching the default_value
+ Decoder pads input data up to total_length with default_value
+
+ This is used in constellations like "FlagsEnum(StripTrailerAdapter(GreedyBytes, 3), ..."
+ where you have a bit-mask that may have 1, 2 or 3 bytes, depending on whether or not any
+ of the LSBs are actually set.
+ """
+ def __init__(self, subcon, total_length:int, default_value=b'\x00', min_len=1):
+ super().__init__(subcon)
+ assert len(default_value) == 1
+ self.total_length = total_length
+ self.default_value = default_value
+ self.min_len = min_len
+
+ def _decode(self, obj, context, path):
+ assert type(obj) == bytes
+ # pad with suppressed/missing bytes
+ if len(obj) < self.total_length:
+ obj += self.default_value * (self.total_length - len(obj))
+ return int.from_bytes(obj, 'big')
+
+ def _encode(self, obj, context, path):
+ assert type(obj) == int
+ obj = obj.to_bytes(self.total_length, 'big')
+ # remove trailing bytes if they are zero
+ while len(obj) > self.min_len and obj[-1] == self.default_value[0]:
+ obj = obj[:-1]
+ return obj
+
def filter_dict(d, exclude_prefix='_'):
"""filter the input dict to ensure no keys starting with 'exclude_prefix' remain."""