]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - tests/test_pyderasn.py
BER usage documentation
[pyderasn.git] / tests / test_pyderasn.py
index a44906838ea9af54a14729fc75409a8ae1e92854..8b93827d1e14a4d28155925dd5660015704aff0e 100644 (file)
@@ -1,5 +1,5 @@
 # coding: utf-8
-# PyDERASN -- Python ASN.1 DER codec with abstract structures
+# PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
 # Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
 #
 # This program is free software: you can redistribute it and/or modify
@@ -43,6 +43,7 @@ from hypothesis.strategies import sets
 from hypothesis.strategies import text
 from hypothesis.strategies import tuples
 from six import assertRaisesRegex
+from six import binary_type
 from six import byte2int
 from six import indexbytes
 from six import int2byte
@@ -62,6 +63,8 @@ from pyderasn import Choice
 from pyderasn import DecodeError
 from pyderasn import DecodePathDefBy
 from pyderasn import Enumerated
+from pyderasn import EOC
+from pyderasn import EOC_LEN
 from pyderasn import GeneralizedTime
 from pyderasn import GeneralString
 from pyderasn import GraphicString
@@ -604,6 +607,54 @@ class TestBoolean(CommonMixin, TestCase):
         )
         self.assertTrue(bool(obj))
         self.assertTrue(obj.bered)
+        self.assertFalse(obj.lenindef)
+
+    @given(
+        integers(min_value=1).map(tag_ctxc),
+        lists(
+            booleans(),
+            min_size=1,
+            max_size=5,
+        ),
+    )
+    def test_ber_expl(self, expl, values):
+        encoded = b""
+        for value in values:
+            encoded += (
+                expl +
+                b"\x80" +
+                Boolean(value).encode() +
+                EOC
+            )
+        encoded = SequenceOf.tag_default + len_encode(len(encoded)) + encoded
+
+        class SeqOf(SequenceOf):
+            schema = Boolean(expl=expl)
+        seqof, tail = SeqOf().decode(encoded, ctx={"bered": True})
+        self.assertSequenceEqual(tail, b"")
+        self.assertSequenceEqual([bool(v) for v in seqof], values)
+        self.assertSetEqual(
+            set(
+                (
+                    v.tlvlen,
+                    v.expl_tlvlen,
+                    v.expl_tlen,
+                    v.expl_llen,
+                    v.bered,
+                    v.lenindef,
+                    v.expl_lenindef,
+                ) for v in seqof
+            ),
+            set(((
+                3 + EOC_LEN,
+                len(expl) + 1 + 3 + EOC_LEN,
+                len(expl),
+                1,
+                False,
+                False,
+                True,
+            ),)),
+        )
 
 
 @composite
@@ -1396,6 +1447,91 @@ class TestBitString(CommonMixin, TestCase):
         self.assertTrue(obj[9])
         self.assertFalse(obj[17])
 
+    @given(
+        integers(min_value=1, max_value=30),
+        lists(
+            one_of(
+                binary(min_size=1, max_size=5),
+                lists(
+                    binary(min_size=1, max_size=5),
+                    min_size=1,
+                    max_size=3,
+                ),
+            ),
+            min_size=0,
+            max_size=3,
+        ),
+        lists(booleans(), min_size=1),
+    )
+    def test_constructed(self, impl, chunk_inputs, chunk_last_bits):
+        def chunk_constructed(contents):
+            return (
+                tag_encode(form=TagFormConstructed, num=3) +
+                b"\x80" +
+                b"".join(BitString(content).encode() for content in contents) +
+                EOC
+            )
+        chunks = []
+        payload_expected = b""
+        bit_len_expected = 0
+        for chunk_input in chunk_inputs:
+            if isinstance(chunk_input, binary_type):
+                chunks.append(BitString(chunk_input).encode())
+                payload_expected += chunk_input
+                bit_len_expected += len(chunk_input) * 8
+            else:
+                chunks.append(chunk_constructed(chunk_input))
+                payload = b"".join(chunk_input)
+                payload_expected += payload
+                bit_len_expected += len(payload) * 8
+        chunk_last = BitString("'%s'B" % "".join(
+            "1" if bit else "0" for bit in chunk_last_bits
+        ))
+        payload_expected += bytes(chunk_last)
+        bit_len_expected += chunk_last.bit_len
+        encoded_indefinite = (
+            tag_encode(form=TagFormConstructed, num=impl) +
+            b"\x80" +
+            b"".join(chunks) +
+            chunk_last.encode() +
+            EOC
+        )
+        encoded_definite = (
+            tag_encode(form=TagFormConstructed, num=impl) +
+            len_encode(len(b"".join(chunks) + chunk_last.encode())) +
+            b"".join(chunks) +
+            chunk_last.encode()
+        )
+        with assertRaisesRegex(self, DecodeError, "unallowed BER"):
+            BitString(impl=tag_encode(impl)).decode(encoded_indefinite)
+        for lenindef_expected, encoded in (
+                (True, encoded_indefinite),
+                (False, encoded_definite),
+        ):
+            obj, tail = BitString(impl=tag_encode(impl)).decode(
+                encoded, ctx={"bered": True}
+            )
+            self.assertSequenceEqual(tail, b"")
+            self.assertEqual(obj.bit_len, bit_len_expected)
+            self.assertSequenceEqual(bytes(obj), payload_expected)
+            self.assertTrue(obj.bered)
+            self.assertEqual(obj.lenindef, lenindef_expected)
+            self.assertEqual(len(encoded), obj.tlvlen)
+
+    def test_x690_vector(self):
+        vector = BitString("'0A3B5F291CD'H")
+        obj, tail = BitString().decode(hexdec("0307040A3B5F291CD0"))
+        self.assertSequenceEqual(tail, b"")
+        self.assertEqual(obj, vector)
+        obj, tail = BitString().decode(
+            hexdec("23800303000A3B0305045F291CD00000"),
+            ctx={"bered": True},
+        )
+        self.assertSequenceEqual(tail, b"")
+        self.assertEqual(obj, vector)
+        self.assertTrue(obj.bered)
+        self.assertTrue(obj.lenindef)
+
 
 @composite
 def octet_string_values_strategy(draw, do_expl=False):
@@ -1707,6 +1843,65 @@ class TestOctetString(CommonMixin, TestCase):
             )
             self.assertEqual(obj_decoded.expl_offset, offset)
 
+    @given(
+        integers(min_value=1, max_value=30),
+        lists(
+            one_of(
+                binary(min_size=1, max_size=5),
+                lists(
+                    binary(min_size=1, max_size=5),
+                    min_size=1,
+                    max_size=3,
+                ),
+            ),
+            min_size=1,
+            max_size=3,
+        ),
+    )
+    def test_constructed(self, impl, chunk_inputs):
+        def chunk_constructed(contents):
+            return (
+                tag_encode(form=TagFormConstructed, num=4) +
+                b"\x80" +
+                b"".join(OctetString(content).encode() for content in contents) +
+                EOC
+            )
+        chunks = []
+        payload_expected = b""
+        for chunk_input in chunk_inputs:
+            if isinstance(chunk_input, binary_type):
+                chunks.append(OctetString(chunk_input).encode())
+                payload_expected += chunk_input
+            else:
+                chunks.append(chunk_constructed(chunk_input))
+                payload = b"".join(chunk_input)
+                payload_expected += payload
+        encoded_indefinite = (
+            tag_encode(form=TagFormConstructed, num=impl) +
+            b"\x80" +
+            b"".join(chunks) +
+            EOC
+        )
+        encoded_definite = (
+            tag_encode(form=TagFormConstructed, num=impl) +
+            len_encode(len(b"".join(chunks))) +
+            b"".join(chunks)
+        )
+        with assertRaisesRegex(self, DecodeError, "unallowed BER"):
+            OctetString(impl=tag_encode(impl)).decode(encoded_indefinite)
+        for lenindef_expected, encoded in (
+                (True, encoded_indefinite),
+                (False, encoded_definite),
+        ):
+            obj, tail = OctetString(impl=tag_encode(impl)).decode(
+                encoded, ctx={"bered": True}
+            )
+            self.assertSequenceEqual(tail, b"")
+            self.assertSequenceEqual(bytes(obj), payload_expected)
+            self.assertTrue(obj.bered)
+            self.assertEqual(obj.lenindef, lenindef_expected)
+            self.assertEqual(len(encoded), obj.tlvlen)
+
 
 @composite
 def null_values_strategy(draw, do_expl=False):
@@ -2918,24 +3113,29 @@ class TestVisibleString(
     base_klass = VisibleString
 
     def test_x690_vector(self):
-        self.assertEqual(
-            str(VisibleString().decode(hexdec("1A054A6F6E6573"))[0]),
-            "Jones",
-        )
-        self.assertEqual(
-            str(VisibleString().decode(
-                hexdec("3A0904034A6F6E04026573"),
-                ctx={"bered": True},
-            )[0]),
-            "Jones",
+        obj, tail = VisibleString().decode(hexdec("1A054A6F6E6573"))
+        self.assertSequenceEqual(tail, b"")
+        self.assertEqual(str(obj), "Jones")
+        self.assertFalse(obj.bered)
+        self.assertFalse(obj.lenindef)
+
+        obj, tail = VisibleString().decode(
+            hexdec("3A0904034A6F6E04026573"),
+            ctx={"bered": True},
         )
-        self.assertEqual(
-            str(VisibleString().decode(
-                hexdec("3A8004034A6F6E040265730000"),
-                ctx={"bered": True},
-            )[0]),
-            "Jones",
+        self.assertSequenceEqual(tail, b"")
+        self.assertEqual(str(obj), "Jones")
+        self.assertTrue(obj.bered)
+        self.assertFalse(obj.lenindef)
+
+        obj, tail = VisibleString().decode(
+            hexdec("3A8004034A6F6E040265730000"),
+            ctx={"bered": True},
         )
+        self.assertSequenceEqual(tail, b"")
+        self.assertEqual(str(obj), "Jones")
+        self.assertTrue(obj.bered)
+        self.assertTrue(obj.lenindef)
 
 
 class TestGeneralString(