diff options
author | Harald Welte <laforge@osmocom.org> | 2024-02-04 18:55:47 +0100 |
---|---|---|
committer | Harald Welte <laforge@osmocom.org> | 2024-02-05 01:39:39 +0100 |
commit | 7156a40187325259a023312747d4c55ec530747e (patch) | |
tree | 1c1486def4ee395132bf01ef8b988f04aa0da027 /pySim/construct.py | |
parent | cd8e16fdfec03b4124e5087930b4dc7cdb668826 (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.py | 31 |
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.""" |