]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - tests/test_pyderasn.py
NumericString sanitizes its value against numbers
[pyderasn.git] / tests / test_pyderasn.py
index 829741590dab147bf3d199aba4d82b7ad6f6c8be..f8377e8881e6e4454758372c28196edac0f57c80 100644 (file)
@@ -18,6 +18,7 @@
 
 from datetime import datetime
 from string import ascii_letters
 
 from datetime import datetime
 from string import ascii_letters
+from string import digits
 from string import printable
 from string import whitespace
 from unittest import TestCase
 from string import printable
 from string import whitespace
 from unittest import TestCase
@@ -48,16 +49,18 @@ from six import int2byte
 from six import iterbytes
 from six import PY2
 from six import text_type
 from six import iterbytes
 from six import PY2
 from six import text_type
+from six import unichr as six_unichr
 
 from pyderasn import _pp
 
 from pyderasn import _pp
+from pyderasn import abs_decode_path
 from pyderasn import Any
 from pyderasn import BitString
 from pyderasn import BMPString
 from pyderasn import Boolean
 from pyderasn import BoundsError
 from pyderasn import Choice
 from pyderasn import Any
 from pyderasn import BitString
 from pyderasn import BMPString
 from pyderasn import Boolean
 from pyderasn import BoundsError
 from pyderasn import Choice
-from pyderasn import decode_path_defby
 from pyderasn import DecodeError
 from pyderasn import DecodeError
+from pyderasn import DecodePathDefBy
 from pyderasn import Enumerated
 from pyderasn import GeneralizedTime
 from pyderasn import GeneralString
 from pyderasn import Enumerated
 from pyderasn import GeneralizedTime
 from pyderasn import GeneralString
@@ -528,8 +531,9 @@ class TestBoolean(CommonMixin, TestCase):
         booleans(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         booleans(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
+        binary(max_size=5),
     )
     )
-    def test_symmetric(self, values, value, tag_expl, offset):
+    def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
         for klass in (Boolean, BooleanInherited):
             _, _, _, default, optional, _decoded = values
             obj = klass(
         for klass in (Boolean, BooleanInherited):
             _, _, _, default, optional, _decoded = values
             obj = klass(
@@ -547,10 +551,13 @@ class TestBoolean(CommonMixin, TestCase):
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
-            obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+            obj_decoded, tail = obj_expled.decode(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+            )
             repr(obj_decoded)
             pprint(obj_decoded)
             repr(obj_decoded)
             pprint(obj_decoded)
-            self.assertEqual(tail, b"")
+            self.assertEqual(tail, tail_junk)
             self.assertEqual(obj_decoded, obj_expled)
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(bool(obj_decoded), bool(obj_expled))
             self.assertEqual(obj_decoded, obj_expled)
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(bool(obj_decoded), bool(obj_expled))
@@ -903,8 +910,9 @@ class TestInteger(CommonMixin, TestCase):
         integers(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         integers(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
+        binary(max_size=5),
     )
     )
-    def test_symmetric(self, values, value, tag_expl, offset):
+    def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
         for klass in (Integer, IntegerInherited):
             _, _, _, _, default, optional, _, _decoded = values
             obj = klass(
         for klass in (Integer, IntegerInherited):
             _, _, _, _, default, optional, _, _decoded = values
             obj = klass(
@@ -922,10 +930,13 @@ class TestInteger(CommonMixin, TestCase):
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
-            obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+            obj_decoded, tail = obj_expled.decode(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+            )
             repr(obj_decoded)
             pprint(obj_decoded)
             repr(obj_decoded)
             pprint(obj_decoded)
-            self.assertEqual(tail, b"")
+            self.assertEqual(tail, tail_junk)
             self.assertEqual(obj_decoded, obj_expled)
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(int(obj_decoded), int(obj_expled))
             self.assertEqual(obj_decoded, obj_expled)
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(int(obj_decoded), int(obj_expled))
@@ -1278,6 +1289,7 @@ class TestBitString(CommonMixin, TestCase):
             optional,
             _decoded,
         ) = d.draw(bit_string_values_strategy(value_required=True))
             optional,
             _decoded,
         ) = d.draw(bit_string_values_strategy(value_required=True))
+        tail_junk = d.draw(binary(max_size=5))
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
         for klass in (BitString, BitStringInherited):
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
         for klass in (BitString, BitStringInherited):
@@ -1298,10 +1310,13 @@ class TestBitString(CommonMixin, TestCase):
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
-            obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+            obj_decoded, tail = obj_expled.decode(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+            )
             repr(obj_decoded)
             pprint(obj_decoded)
             repr(obj_decoded)
             pprint(obj_decoded)
-            self.assertEqual(tail, b"")
+            self.assertEqual(tail, tail_junk)
             self.assertEqual(obj_decoded, obj_expled)
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
             self.assertEqual(obj_decoded, obj_expled)
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
@@ -1636,8 +1651,9 @@ class TestOctetString(CommonMixin, TestCase):
         binary(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
+        binary(max_size=5),
     )
     )
-    def test_symmetric(self, values, value, tag_expl, offset):
+    def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
         for klass in (OctetString, OctetStringInherited):
             _, _, _, _, default, optional, _decoded = values
             obj = klass(
         for klass in (OctetString, OctetStringInherited):
             _, _, _, _, default, optional, _decoded = values
             obj = klass(
@@ -1655,10 +1671,13 @@ class TestOctetString(CommonMixin, TestCase):
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
-            obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+            obj_decoded, tail = obj_expled.decode(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+            )
             repr(obj_decoded)
             pprint(obj_decoded)
             repr(obj_decoded)
             pprint(obj_decoded)
-            self.assertEqual(tail, b"")
+            self.assertEqual(tail, tail_junk)
             self.assertEqual(obj_decoded, obj_expled)
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
             self.assertEqual(obj_decoded, obj_expled)
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
@@ -1815,8 +1834,9 @@ class TestNull(CommonMixin, TestCase):
         null_values_strategy(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         null_values_strategy(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
+        binary(max_size=5),
     )
     )
-    def test_symmetric(self, values, tag_expl, offset):
+    def test_symmetric(self, values, tag_expl, offset, tail_junk):
         for klass in (Null, NullInherited):
             _, _, optional, _decoded = values
             obj = klass(optional=optional, _decoded=_decoded)
         for klass in (Null, NullInherited):
             _, _, optional, _decoded = values
             obj = klass(optional=optional, _decoded=_decoded)
@@ -1829,10 +1849,13 @@ class TestNull(CommonMixin, TestCase):
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
-            obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+            obj_decoded, tail = obj_expled.decode(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+            )
             repr(obj_decoded)
             pprint(obj_decoded)
             repr(obj_decoded)
             pprint(obj_decoded)
-            self.assertEqual(tail, b"")
+            self.assertEqual(tail, tail_junk)
             self.assertEqual(obj_decoded, obj_expled)
             self.assertNotEqual(obj_decoded, obj)
             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
             self.assertEqual(obj_decoded, obj_expled)
             self.assertNotEqual(obj_decoded, obj)
             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
@@ -2135,8 +2158,9 @@ class TestObjectIdentifier(CommonMixin, TestCase):
         oid_strategy(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         oid_strategy(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
+        binary(max_size=5),
     )
     )
-    def test_symmetric(self, values, value, tag_expl, offset):
+    def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
             _, _, _, default, optional, _decoded = values
             obj = klass(
         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
             _, _, _, default, optional, _decoded = values
             obj = klass(
@@ -2154,10 +2178,13 @@ class TestObjectIdentifier(CommonMixin, TestCase):
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
-            obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+            obj_decoded, tail = obj_expled.decode(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+            )
             repr(obj_decoded)
             pprint(obj_decoded)
             repr(obj_decoded)
             pprint(obj_decoded)
-            self.assertEqual(tail, b"")
+            self.assertEqual(tail, tail_junk)
             self.assertEqual(obj_decoded, obj_expled)
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(tuple(obj_decoded), tuple(obj_expled))
             self.assertEqual(obj_decoded, obj_expled)
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(tuple(obj_decoded), tuple(obj_expled))
@@ -2412,6 +2439,7 @@ class TestEnumerated(CommonMixin, TestCase):
         tag_expl = d.draw(integers(min_value=1).map(tag_ctxc))
         offset = d.draw(integers(min_value=0))
         value = d.draw(sampled_from(sorted([v for _, v in schema_input])))
         tag_expl = d.draw(integers(min_value=1).map(tag_ctxc))
         offset = d.draw(integers(min_value=0))
         value = d.draw(sampled_from(sorted([v for _, v in schema_input])))
+        tail_junk = d.draw(binary(max_size=5))
 
         class E(Enumerated):
             schema = schema_input
 
         class E(Enumerated):
             schema = schema_input
@@ -2430,10 +2458,13 @@ class TestEnumerated(CommonMixin, TestCase):
         repr(obj_expled)
         pprint(obj_expled)
         obj_expled_encoded = obj_expled.encode()
         repr(obj_expled)
         pprint(obj_expled)
         obj_expled_encoded = obj_expled.encode()
-        obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+        obj_decoded, tail = obj_expled.decode(
+            obj_expled_encoded + tail_junk,
+            offset=offset,
+        )
         repr(obj_decoded)
         pprint(obj_decoded)
         repr(obj_decoded)
         pprint(obj_decoded)
-        self.assertEqual(tail, b"")
+        self.assertEqual(tail, tail_junk)
         self.assertEqual(obj_decoded, obj_expled)
         self.assertNotEqual(obj_decoded, obj)
         self.assertEqual(int(obj_decoded), int(obj_expled))
         self.assertEqual(obj_decoded, obj_expled)
         self.assertNotEqual(obj_decoded, obj)
         self.assertEqual(int(obj_decoded), int(obj_expled))
@@ -2723,6 +2754,7 @@ class StringMixin(object):
         value = d.draw(text(alphabet=self.text_alphabet()))
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
         value = d.draw(text(alphabet=self.text_alphabet()))
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
+        tail_junk = d.draw(binary(max_size=5))
         _, _, _, _, default, optional, _decoded = values
         obj = self.base_klass(
             value=value,
         _, _, _, _, default, optional, _decoded = values
         obj = self.base_klass(
             value=value,
@@ -2739,10 +2771,13 @@ class StringMixin(object):
         repr(obj_expled)
         pprint(obj_expled)
         obj_expled_encoded = obj_expled.encode()
         repr(obj_expled)
         pprint(obj_expled)
         obj_expled_encoded = obj_expled.encode()
-        obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+        obj_decoded, tail = obj_expled.decode(
+            obj_expled_encoded + tail_junk,
+            offset=offset,
+        )
         repr(obj_decoded)
         pprint(obj_decoded)
         repr(obj_decoded)
         pprint(obj_decoded)
-        self.assertEqual(tail, b"")
+        self.assertEqual(tail, tail_junk)
         self.assertEqual(obj_decoded, obj_expled)
         self.assertNotEqual(obj_decoded, obj)
         self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
         self.assertEqual(obj_decoded, obj_expled)
         self.assertNotEqual(obj_decoded, obj)
         self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
@@ -2769,35 +2804,110 @@ class TestUTF8String(StringMixin, CommonMixin, TestCase):
     base_klass = UTF8String
 
 
     base_klass = UTF8String
 
 
+class UnicodeDecodeErrorMixin(object):
+    @given(text(
+        alphabet=''.join(six_unichr(i) for i in list(range(0x0410, 0x044f + 1))),
+        min_size=1,
+        max_size=5,
+    ))
+    def test_unicode_decode_error(self, cyrillic_text):
+        with self.assertRaises(DecodeError):
+            self.base_klass(cyrillic_text)
+
+
 class TestNumericString(StringMixin, CommonMixin, TestCase):
     base_klass = NumericString
 
 class TestNumericString(StringMixin, CommonMixin, TestCase):
     base_klass = NumericString
 
+    def text_alphabet(self):
+        return digits
+
+    @given(text(alphabet=ascii_letters, min_size=1, max_size=5))
+    def test_non_numeric(self, cyrillic_text):
+        with assertRaisesRegex(self, DecodeError, "non-numeric"):
+            self.base_klass(cyrillic_text)
 
 
-class TestPrintableString(StringMixin, CommonMixin, TestCase):
+    @given(
+        sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
+        integers(min_value=0),
+        lists(integers()),
+    )
+    def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
+        decode_path = tuple(str(i) for i in decode_path)
+        value, bound_min = list(sorted(ints))
+
+        class String(self.base_klass):
+            bounds = (bound_min, bound_min)
+        with self.assertRaises(DecodeError) as err:
+            String().decode(
+                self.base_klass(b"1" * value).encode(),
+                offset=offset,
+                decode_path=decode_path,
+            )
+        repr(err.exception)
+        self.assertEqual(err.exception.offset, offset)
+        self.assertEqual(err.exception.decode_path, decode_path)
+
+
+class TestPrintableString(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = PrintableString
 
 
     base_klass = PrintableString
 
 
-class TestTeletexString(StringMixin, CommonMixin, TestCase):
+class TestTeletexString(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = TeletexString
 
 
     base_klass = TeletexString
 
 
-class TestVideotexString(StringMixin, CommonMixin, TestCase):
+class TestVideotexString(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = VideotexString
 
 
     base_klass = VideotexString
 
 
-class TestIA5String(StringMixin, CommonMixin, TestCase):
+class TestIA5String(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = IA5String
 
 
     base_klass = IA5String
 
 
-class TestGraphicString(StringMixin, CommonMixin, TestCase):
+class TestGraphicString(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = GraphicString
 
 
     base_klass = GraphicString
 
 
-class TestVisibleString(StringMixin, CommonMixin, TestCase):
+class TestVisibleString(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = VisibleString
 
 
     base_klass = VisibleString
 
 
-class TestGeneralString(StringMixin, CommonMixin, TestCase):
+class TestGeneralString(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = GeneralString
 
 
     base_klass = GeneralString
 
 
@@ -3007,6 +3117,7 @@ class TimeMixin(object):
         ))
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
         ))
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
+        tail_junk = d.draw(binary(max_size=5))
         _, _, _, default, optional, _decoded = values
         obj = self.base_klass(
             value=value,
         _, _, _, default, optional, _decoded = values
         obj = self.base_klass(
             value=value,
@@ -3023,10 +3134,13 @@ class TimeMixin(object):
         repr(obj_expled)
         pprint(obj_expled)
         obj_expled_encoded = obj_expled.encode()
         repr(obj_expled)
         pprint(obj_expled)
         obj_expled_encoded = obj_expled.encode()
-        obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+        obj_decoded, tail = obj_expled.decode(
+            obj_expled_encoded + tail_junk,
+            offset=offset,
+        )
         repr(obj_decoded)
         pprint(obj_decoded)
         repr(obj_decoded)
         pprint(obj_decoded)
-        self.assertEqual(tail, b"")
+        self.assertEqual(tail, tail_junk)
         self.assertEqual(obj_decoded, obj_expled)
         self.assertEqual(obj_decoded.todatetime(), obj_expled.todatetime())
         self.assertEqual(obj_decoded.todatetime(), obj.todatetime())
         self.assertEqual(obj_decoded, obj_expled)
         self.assertEqual(obj_decoded.todatetime(), obj_expled.todatetime())
         self.assertEqual(obj_decoded.todatetime(), obj.todatetime())
@@ -3325,8 +3439,9 @@ class TestAny(CommonMixin, TestCase):
         integers().map(lambda x: Integer(x).encode()),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         integers().map(lambda x: Integer(x).encode()),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
+        binary(max_size=5),
     )
     )
-    def test_symmetric(self, values, value, tag_expl, offset):
+    def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
         for klass in (Any, AnyInherited):
             _, _, optional, _decoded = values
             obj = klass(value=value, optional=optional, _decoded=_decoded)
         for klass in (Any, AnyInherited):
             _, _, optional, _decoded = values
             obj = klass(value=value, optional=optional, _decoded=_decoded)
@@ -3339,10 +3454,13 @@ class TestAny(CommonMixin, TestCase):
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
             repr(obj_expled)
             pprint(obj_expled)
             obj_expled_encoded = obj_expled.encode()
-            obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+            obj_decoded, tail = obj_expled.decode(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+            )
             repr(obj_decoded)
             pprint(obj_decoded)
             repr(obj_decoded)
             pprint(obj_decoded)
-            self.assertEqual(tail, b"")
+            self.assertEqual(tail, tail_junk)
             self.assertEqual(obj_decoded, obj_expled)
             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
             self.assertEqual(bytes(obj_decoded), bytes(obj))
             self.assertEqual(obj_decoded, obj_expled)
             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
             self.assertEqual(bytes(obj_decoded), bytes(obj))
@@ -3569,6 +3687,7 @@ class TestChoice(CommonMixin, TestCase):
         )
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
         )
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
+        tail_junk = d.draw(binary(max_size=5))
 
         class Wahl(self.base_klass):
             schema = _schema
 
         class Wahl(self.base_klass):
             schema = _schema
@@ -3587,10 +3706,13 @@ class TestChoice(CommonMixin, TestCase):
         repr(obj_expled)
         pprint(obj_expled)
         obj_expled_encoded = obj_expled.encode()
         repr(obj_expled)
         pprint(obj_expled)
         obj_expled_encoded = obj_expled.encode()
-        obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+        obj_decoded, tail = obj_expled.decode(
+            obj_expled_encoded + tail_junk,
+            offset=offset,
+        )
         repr(obj_decoded)
         pprint(obj_decoded)
         repr(obj_decoded)
         pprint(obj_decoded)
-        self.assertEqual(tail, b"")
+        self.assertEqual(tail, tail_junk)
         self.assertEqual(obj_decoded, obj_expled)
         self.assertEqual(obj_decoded.choice, obj_expled.choice)
         self.assertEqual(obj_decoded.value, obj_expled.value)
         self.assertEqual(obj_decoded, obj_expled)
         self.assertEqual(obj_decoded.choice, obj_expled.choice)
         self.assertEqual(obj_decoded.value, obj_expled.value)
@@ -4056,14 +4178,15 @@ class SeqMixing(object):
     @given(data_strategy())
     def test_symmetric(self, d):
         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
     @given(data_strategy())
     def test_symmetric(self, d):
         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
+        tail_junk = d.draw(binary(max_size=5))
         self.assertTrue(seq.ready)
         self.assertFalse(seq.decoded)
         self._assert_expects(seq, expects)
         repr(seq)
         pprint(seq)
         seq_encoded = seq.encode()
         self.assertTrue(seq.ready)
         self.assertFalse(seq.decoded)
         self._assert_expects(seq, expects)
         repr(seq)
         pprint(seq)
         seq_encoded = seq.encode()
-        seq_decoded, tail = seq.decode(seq_encoded)
-        self.assertEqual(tail, b"")
+        seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
+        self.assertEqual(tail, tail_junk)
         self.assertTrue(seq.ready)
         self._assert_expects(seq_decoded, expects)
         self.assertEqual(seq, seq_decoded)
         self.assertTrue(seq.ready)
         self._assert_expects(seq_decoded, expects)
         self.assertEqual(seq, seq_decoded)
@@ -4591,8 +4714,9 @@ class SeqOfMixing(object):
         lists(integers().map(Integer)),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         lists(integers().map(Integer)),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
+        binary(max_size=5),
     )
     )
-    def test_symmetric(self, values, value, tag_expl, offset):
+    def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
         _, _, _, _, _, default, optional, _decoded = values
 
         class SeqOf(self.base_klass):
         _, _, _, _, _, default, optional, _decoded = values
 
         class SeqOf(self.base_klass):
@@ -4612,10 +4736,13 @@ class SeqOfMixing(object):
         repr(obj_expled)
         pprint(obj_expled)
         obj_expled_encoded = obj_expled.encode()
         repr(obj_expled)
         pprint(obj_expled)
         obj_expled_encoded = obj_expled.encode()
-        obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+        obj_decoded, tail = obj_expled.decode(
+            obj_expled_encoded + tail_junk,
+            offset=offset,
+        )
         repr(obj_decoded)
         pprint(obj_decoded)
         repr(obj_decoded)
         pprint(obj_decoded)
-        self.assertEqual(tail, b"")
+        self.assertEqual(tail, tail_junk)
         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
@@ -4902,9 +5029,9 @@ class TestOIDDefines(TestCase):
             max_size=len(value_names),
         ))
         _schema = [
             max_size=len(value_names),
         ))
         _schema = [
-            ("type", ObjectIdentifier(defines=(value_name_chosen, {
+            ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
                 oid: Integer() for oid in oids[:-1]
                 oid: Integer() for oid in oids[:-1]
-            }))),
+            }),))),
         ]
         for i, value_name in enumerate(value_names):
             _schema.append((value_name, Any(expl=tag_ctxp(i))))
         ]
         for i, value_name in enumerate(value_names):
             _schema.append((value_name, Any(expl=tag_ctxp(i))))
@@ -4927,7 +5054,7 @@ class TestOIDDefines(TestCase):
 
 
 class TestDefinesByPath(TestCase):
 
 
 class TestDefinesByPath(TestCase):
-    def runTest(self):
+    def test_generated(self):
         class Seq(Sequence):
             schema = (
                 ("type", ObjectIdentifier()),
         class Seq(Sequence):
             schema = (
                 ("type", ObjectIdentifier()),
@@ -4990,27 +5117,39 @@ class TestDefinesByPath(TestCase):
         seq_integered, _ = Seq().decode(seq_integered_raw)
         self.assertIsNone(seq_integered["value"].defined)
         defines_by_path.append(
         seq_integered, _ = Seq().decode(seq_integered_raw)
         self.assertIsNone(seq_integered["value"].defined)
         defines_by_path.append(
-            (("type",), ("value", {
+            (("type",), ((("value",), {
                 type_integered: Integer(),
                 type_sequenced: SeqInner(),
                 type_integered: Integer(),
                 type_sequenced: SeqInner(),
-            }))
+            }),))
+        )
+        seq_integered, _ = Seq().decode(
+            seq_integered_raw,
+            ctx={"defines_by_path": defines_by_path},
         )
         )
-        seq_integered, _ = Seq().decode(seq_integered_raw, defines_by_path=defines_by_path)
         self.assertIsNotNone(seq_integered["value"].defined)
         self.assertEqual(seq_integered["value"].defined[0], type_integered)
         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
         self.assertIsNotNone(seq_integered["value"].defined)
         self.assertEqual(seq_integered["value"].defined[0], type_integered)
         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
+        self.assertTrue(seq_integered_raw[
+            seq_integered["value"].defined[1].offset:
+        ].startswith(Integer(123).encode()))
 
 
-        seq_sequenced, _ = Seq().decode(seq_sequenced_raw, defines_by_path=defines_by_path)
+        seq_sequenced, _ = Seq().decode(
+            seq_sequenced_raw,
+            ctx={"defines_by_path": defines_by_path},
+        )
         self.assertIsNotNone(seq_sequenced["value"].defined)
         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
         seq_inner = seq_sequenced["value"].defined[1]
         self.assertIsNone(seq_inner["valueInner"].defined)
 
         defines_by_path.append((
         self.assertIsNotNone(seq_sequenced["value"].defined)
         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
         seq_inner = seq_sequenced["value"].defined[1]
         self.assertIsNone(seq_inner["valueInner"].defined)
 
         defines_by_path.append((
-            ("value", decode_path_defby(type_sequenced), "typeInner"),
-            ("valueInner", {type_innered: Pairs()}),
+            ("value", DecodePathDefBy(type_sequenced), "typeInner"),
+            ((("valueInner",), {type_innered: Pairs()}),),
         ))
         ))
-        seq_sequenced, _ = Seq().decode(seq_sequenced_raw, defines_by_path=defines_by_path)
+        seq_sequenced, _ = Seq().decode(
+            seq_sequenced_raw,
+            ctx={"defines_by_path": defines_by_path},
+        )
         self.assertIsNotNone(seq_sequenced["value"].defined)
         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
         seq_inner = seq_sequenced["value"].defined[1]
         self.assertIsNotNone(seq_sequenced["value"].defined)
         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
         seq_inner = seq_sequenced["value"].defined[1]
@@ -5023,18 +5162,21 @@ class TestDefinesByPath(TestCase):
         defines_by_path.append((
             (
                 "value",
         defines_by_path.append((
             (
                 "value",
-                decode_path_defby(type_sequenced),
+                DecodePathDefBy(type_sequenced),
                 "valueInner",
                 "valueInner",
-                decode_path_defby(type_innered),
+                DecodePathDefBy(type_innered),
                 any,
                 "type",
             ),
                 any,
                 "type",
             ),
-            ("value", {
+            ((("value",), {
                 type_integered: Integer(),
                 type_octet_stringed: OctetString(),
                 type_integered: Integer(),
                 type_octet_stringed: OctetString(),
-            }),
+            }),),
         ))
         ))
-        seq_sequenced, _ = Seq().decode(seq_sequenced_raw, defines_by_path=defines_by_path)
+        seq_sequenced, _ = Seq().decode(
+            seq_sequenced_raw,
+            ctx={"defines_by_path": defines_by_path},
+        )
         self.assertIsNotNone(seq_sequenced["value"].defined)
         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
         seq_inner = seq_sequenced["value"].defined[1]
         self.assertIsNotNone(seq_sequenced["value"].defined)
         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
         seq_inner = seq_sequenced["value"].defined[1]
@@ -5044,3 +5186,82 @@ class TestDefinesByPath(TestCase):
         for pair_input, pair_got in zip(pairs_input, pairs_got):
             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
         for pair_input, pair_got in zip(pairs_input, pairs_got):
             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
+
+    @given(oid_strategy(), integers())
+    def test_simple(self, oid, tgt):
+        class Inner(Sequence):
+            schema = (
+                ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
+                    ObjectIdentifier(oid): Integer(),
+                }),))),
+            )
+
+        class Outer(Sequence):
+            schema = (
+                ("inner", Inner()),
+                ("tgt", OctetString()),
+            )
+
+        inner = Inner()
+        inner["oid"] = ObjectIdentifier(oid)
+        outer = Outer()
+        outer["inner"] = inner
+        outer["tgt"] = OctetString(Integer(tgt).encode())
+        decoded, _ = Outer().decode(outer.encode())
+        self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
+
+
+class TestAbsDecodePath(TestCase):
+    @given(
+        lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
+        lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
+    )
+    def test_concat(self, decode_path, rel_path):
+        self.assertSequenceEqual(
+            abs_decode_path(decode_path, rel_path),
+            decode_path + rel_path,
+        )
+
+    @given(
+        lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
+        lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
+    )
+    def test_abs(self, decode_path, rel_path):
+        self.assertSequenceEqual(
+            abs_decode_path(decode_path, ("/",) + rel_path),
+            rel_path,
+        )
+
+    @given(
+        lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
+        integers(min_value=1, max_value=3),
+        lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
+    )
+    def test_dots(self, decode_path, number_of_dots, rel_path):
+        self.assertSequenceEqual(
+            abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
+            decode_path[:-number_of_dots] + rel_path,
+        )
+
+
+class TestStrictDefaultExistence(TestCase):
+    @given(data_strategy())
+    def runTest(self, d):
+        count = d.draw(integers(min_value=1, max_value=10))
+        chosen = d.draw(integers(min_value=0, max_value=count - 1))
+        _schema = [
+            ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
+            for i in range(count)
+        ]
+
+        class Seq(Sequence):
+            schema = _schema
+        seq = Seq()
+        for i in range(count):
+            seq["int%d" % i] = Integer(123)
+        raw = seq.encode()
+        chosen = "int%d" % chosen
+        seq.specs[chosen] = seq.specs[chosen](default=123)
+        seq.decode(raw)
+        with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+            seq.decode(raw, ctx={"strict_default_existence": True})