laforge submitted this change.

View Change

Approvals: laforge: Looks good to me, approved Jenkins Builder: Verified
osmocom.construct.Asn1DerInteger

This is a 'construct' type which can be used for encoding/decoding
integer values according to ASN.1 DER encoding rules.

Related: SYS#7094
Change-Id: I0cfe97daf957919de86453d6d44f9c99ab3075ac
---
M src/osmocom/construct.py
M tests/test_construct.py
2 files changed, 73 insertions(+), 0 deletions(-)

diff --git a/src/osmocom/construct.py b/src/osmocom/construct.py
index 7c0f034..5a78246 100644
--- a/src/osmocom/construct.py
+++ b/src/osmocom/construct.py
@@ -600,6 +600,19 @@
stream_write(stream, data, length, path)
return obj

+class Asn1DerInteger(Construct):
+ """A signed integer value using ASN.1 DER encoding rules (see also ITU-T X.690 8.3)"""
+ def _parse(self, stream, context, path):
+ data = stream_read_entire(stream, path)
+ return int.from_bytes(data, byteorder='big', signed=True)
+
+ def _build(self, obj, stream, context, path):
+ if not isinstance(obj, integertypes):
+ raise IntegerError(f"value {obj} is not an integer", path=path)
+ val = obj.to_bytes(int_bytes_required(obj, signed=True), byteorder='big', signed=True)
+ stream_write(stream, val, len(val), path)
+ return obj
+
# merged definitions of 24.008 + 23.040
TypeOfNumber = Enum(BitsInteger(3), unknown=0, international=1, national=2, network_specific=3,
short_code=4, alphanumeric=5, abbreviated=6, reserved_for_extension=7)
diff --git a/tests/test_construct.py b/tests/test_construct.py
index c2aeece..ba4f8f4 100755
--- a/tests/test_construct.py
+++ b/tests/test_construct.py
@@ -114,6 +114,66 @@
self.assertEqual(re_enc, enc)


+class TestAsn1DerInteger(unittest.TestCase):
+
+ tests = [
+ # positive numbers
+ ( b'\x00', 0 ),
+ ( b'\x01', 1 ),
+ ( b'\x02', 2 ),
+ ( b'\x7f', 127 ),
+ ( b'\x00\x80', 128 ),
+ ( b'\x00\x81', 129 ),
+ ( b'\x00\xfe', 254 ),
+ ( b'\x01\x00', 256 ),
+ ( b'\x01\x01', 257 ),
+ ( b'\x7f\xff', 32767 ),
+ ( b'\x00\x80\x00', 32768 ),
+ ( b'\x00\x80\x01', 32769 ),
+ ( b'\x00\xff\xfe', 65534 ),
+ ( b'\x00\xff\xff', 65535 ),
+ ( b'\x01\x00\x00', 65536 ),
+
+ # negative numbers
+ ( b'\x00', -0 ),
+ ( b'\xff', -1 ),
+ ( b'\xfe', -2 ),
+ ( b'\x81', -127 ),
+ ( b'\x80', -128 ),
+ ( b'\xff\x7f', -129 ),
+ ( b'\xff\x02', -254 ),
+ ( b'\xff\x00', -256 ),
+ ( b'\xfe\xff', -257 ),
+ ( b'\x80\x01', -32767 ),
+ ( b'\x80\x00', -32768 ),
+ ( b'\xff\x7f\xff', -32769 ),
+ ( b'\xff\x00\x02', -65534 ),
+ ( b'\xff\x00\x01', -65535 ),
+ ( b'\xff\x00\x00', -65536 ),
+ ]
+
+ def test_encode(self):
+ adi = Asn1DerInteger()
+
+ # Verfiy with chosen numbers
+ for t in self.tests:
+ self.assertEqual(t[0], adi.build(t[1]))
+
+ # Verify that ITU-T X.690 8.3.2 is always complied with (for standard two's
+ # complement that should always be the case)
+ for i in range(-100000,100000):
+ res = adi.build(i)
+ if len(res) > 1:
+ self.assertFalse(int(res[0]) == 0xff and int(res[1]) & 0x80 == 0x80)
+ self.assertFalse(int(res[0]) == 0x00 and int(res[1]) & 0x80 == 0x00)
+
+ def test_decode(self):
+ adi = Asn1DerInteger()
+
+ # Verfiy with chosen numbers
+ for t in self.tests:
+ self.assertEqual(t[1], adi.parse(t[0]))
+

if __name__ == "__main__":
unittest.main()

To view, visit change 38272. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: merged
Gerrit-Project: python/pyosmocom
Gerrit-Branch: master
Gerrit-Change-Id: I0cfe97daf957919de86453d6d44f9c99ab3075ac
Gerrit-Change-Number: 38272
Gerrit-PatchSet: 8
Gerrit-Owner: laforge <laforge@osmocom.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: dexter <pmaier@sysmocom.de>
Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de>
Gerrit-Reviewer: laforge <laforge@osmocom.org>
Gerrit-Reviewer: osmith <osmith@sysmocom.de>