X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=tests%2Ftest_pyderasn.py;h=29a3cb2da95a79a7a1f5d861d2fbc3c4bcac1697;hb=333d098f0af80eae5481c99b86419d25ea927d22;hp=0a439f8eb9ebf2ef5b9d91e882609485bc23c289;hpb=a441b706391d96088555054663cafa5aa244c592;p=pyderasn.git diff --git a/tests/test_pyderasn.py b/tests/test_pyderasn.py index 0a439f8..29a3cb2 100644 --- a/tests/test_pyderasn.py +++ b/tests/test_pyderasn.py @@ -1,11 +1,10 @@ # coding: utf-8 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures -# Copyright (C) 2017-2018 Sergey Matveev +# Copyright (C) 2017-2020 Sergey Matveev # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. +# published by the Free Software Foundation, version 3 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -16,11 +15,15 @@ # License along with this program. If not, see # . +from copy import copy +from copy import deepcopy from datetime import datetime +from importlib import import_module from string import ascii_letters from string import digits from string import printable from string import whitespace +from time import time from unittest import TestCase from hypothesis import assume @@ -51,6 +54,9 @@ from six import iterbytes from six import PY2 from six import text_type from six import unichr as six_unichr +from six.moves.cPickle import dumps as pickle_dumps +from six.moves.cPickle import HIGHEST_PROTOCOL as pickle_proto +from six.moves.cPickle import loads as pickle_loads from pyderasn import _pp from pyderasn import abs_decode_path @@ -65,6 +71,7 @@ from pyderasn import DecodePathDefBy from pyderasn import Enumerated from pyderasn import EOC from pyderasn import EOC_LEN +from pyderasn import ExceedingData from pyderasn import GeneralizedTime from pyderasn import GeneralString from pyderasn import GraphicString @@ -132,6 +139,25 @@ tag_forms = sampled_from((TagFormConstructed, TagFormPrimitive)) decode_path_strat = lists(integers(), max_size=3).map( lambda decode_path: tuple(str(dp) for dp in decode_path) ) +ctx_dummy = dictionaries(integers(), integers(), min_size=2, max_size=4).example() +copy_funcs = ( + copy, + lambda obj: pickle_loads(pickle_dumps(obj, pickle_proto)), +) +self_module = import_module(__name__) + + +def register_class(klass): + klassname = klass.__name__ + str(time()).replace(".", "") + klass.__name__ = klassname + klass.__qualname__ = klassname + setattr(self_module, klassname, klass) + + +def assert_exceeding_data(self, call, junk): + if len(junk) > 0: + with assertRaisesRegex(self, ExceedingData, "%d trailing bytes" % len(junk)): + call() class TestHex(TestCase): @@ -448,8 +474,9 @@ class TestBoolean(CommonMixin, TestCase): def test_copy(self, values): for klass in (Boolean, BooleanInherited): obj = klass(*values) - obj_copied = obj.copy() - self.assert_copied_basic_fields(obj, obj_copied) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assert_copied_basic_fields(obj, obj_copied) @given( booleans(), @@ -560,11 +587,14 @@ class TestBoolean(CommonMixin, TestCase): repr(obj_expled) list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) - obj_expled_encoded = obj_expled.encode() - obj_decoded, tail = obj_expled.decode( - obj_expled_encoded + tail_junk, + obj_expled_hex_encoded = obj_expled.hexencode() + ctx_copied = deepcopy(ctx_dummy) + obj_decoded, tail = obj_expled.hexdecode( + obj_expled_hex_encoded + hexenc(tail_junk), offset=offset, + ctx=ctx_copied, ) + self.assertDictEqual(ctx_copied, ctx_dummy) repr(obj_decoded) list(obj_decoded.pps()) pprint(obj_decoded, big_blobs=True, with_decode_path=True) @@ -573,7 +603,7 @@ class TestBoolean(CommonMixin, TestCase): self.assertNotEqual(obj_decoded, obj) self.assertEqual(bool(obj_decoded), bool(obj_expled)) self.assertEqual(bool(obj_decoded), bool(obj)) - self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded) + self.assertSequenceEqual(obj_decoded.hexencode(), obj_expled_hex_encoded) self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl) self.assertEqual(obj_decoded.expl_tlen, len(tag_expl)) self.assertEqual( @@ -587,6 +617,11 @@ class TestBoolean(CommonMixin, TestCase): offset + obj_decoded.expl_tlen + obj_decoded.expl_llen, ) self.assertEqual(obj_decoded.expl_offset, offset) + assert_exceeding_data( + self, + lambda: obj_expled.hexdecod(obj_expled_hex_encoded + hexenc(tail_junk)), + tail_junk, + ) @given(integers(min_value=2)) def test_invalid_len(self, l): @@ -617,6 +652,10 @@ class TestBoolean(CommonMixin, TestCase): self.assertTrue(obj.ber_encoded) self.assertFalse(obj.lenindef) self.assertTrue(obj.bered) + obj = copy(obj) + self.assertTrue(obj.ber_encoded) + self.assertFalse(obj.lenindef) + self.assertTrue(obj.bered) @given( integers(min_value=1).map(tag_ctxc), @@ -636,6 +675,11 @@ class TestBoolean(CommonMixin, TestCase): self.assertFalse(obj.lenindef) self.assertFalse(obj.ber_encoded) self.assertTrue(obj.bered) + obj = copy(obj) + self.assertTrue(obj.expl_lenindef) + self.assertFalse(obj.lenindef) + self.assertFalse(obj.ber_encoded) + self.assertTrue(obj.bered) self.assertSequenceEqual(tail, junk) repr(obj) list(obj.pps()) @@ -929,12 +973,13 @@ class TestInteger(CommonMixin, TestCase): def test_copy(self, values): for klass in (Integer, IntegerInherited): obj = klass(*values) - obj_copied = obj.copy() - self.assert_copied_basic_fields(obj, obj_copied) - self.assertEqual(obj.specs, obj_copied.specs) - self.assertEqual(obj._bound_min, obj_copied._bound_min) - self.assertEqual(obj._bound_max, obj_copied._bound_max) - self.assertEqual(obj._value, obj_copied._value) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assert_copied_basic_fields(obj, obj_copied) + self.assertEqual(obj.specs, obj_copied.specs) + self.assertEqual(obj._bound_min, obj_copied._bound_min) + self.assertEqual(obj._bound_max, obj_copied._bound_max) + self.assertEqual(obj._value, obj_copied._value) @given( integers(), @@ -1041,10 +1086,13 @@ class TestInteger(CommonMixin, TestCase): list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) obj_expled_encoded = obj_expled.encode() + ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, offset=offset, + ctx=ctx_copied, ) + self.assertDictEqual(ctx_copied, ctx_dummy) repr(obj_decoded) list(obj_decoded.pps()) pprint(obj_decoded, big_blobs=True, with_decode_path=True) @@ -1067,6 +1115,11 @@ class TestInteger(CommonMixin, TestCase): offset + obj_decoded.expl_tlen + obj_decoded.expl_llen, ) self.assertEqual(obj_decoded.expl_offset, offset) + assert_exceeding_data( + self, + lambda: obj_expled.decod(obj_expled_encoded + tail_junk), + tail_junk, + ) def test_go_vectors_valid(self): for data, expect in (( @@ -1121,7 +1174,7 @@ def bit_string_values_strategy(draw, schema=None, value_required=False, do_expl= def _value(value_required): if not value_required and draw(booleans()): - return + return None generation_choice = 0 if value_required: generation_choice = draw(sampled_from((1, 2, 3))) @@ -1130,9 +1183,9 @@ def bit_string_values_strategy(draw, schema=None, value_required=False, do_expl= sampled_from(("0", "1")), max_size=len(schema), ))) - elif generation_choice == 2 or draw(booleans()): + if generation_choice == 2 or draw(booleans()): return draw(binary(max_size=len(schema) // 8)) - elif generation_choice == 3 or draw(booleans()): + if generation_choice == 3 or draw(booleans()): return tuple(draw(lists(sampled_from([name for name, _ in schema])))) return None value = _value(value_required) @@ -1326,6 +1379,7 @@ class TestBitString(CommonMixin, TestCase): class BS(klass): schema = _schema + register_class(BS) obj = BS( value=value, impl=impl, @@ -1334,10 +1388,11 @@ class TestBitString(CommonMixin, TestCase): optional=optional or False, _decoded=_decoded, ) - obj_copied = obj.copy() - self.assert_copied_basic_fields(obj, obj_copied) - self.assertEqual(obj.specs, obj_copied.specs) - self.assertEqual(obj._value, obj_copied._value) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assert_copied_basic_fields(obj, obj_copied) + self.assertEqual(obj.specs, obj_copied.specs) + self.assertEqual(obj._value, obj_copied._value) @given( binary(), @@ -1424,10 +1479,13 @@ class TestBitString(CommonMixin, TestCase): list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) obj_expled_encoded = obj_expled.encode() + ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, offset=offset, + ctx=ctx_copied, ) + self.assertDictEqual(ctx_copied, ctx_dummy) repr(obj_decoded) list(obj_decoded.pps()) pprint(obj_decoded, big_blobs=True, with_decode_path=True) @@ -1454,6 +1512,11 @@ class TestBitString(CommonMixin, TestCase): self.assertSetEqual(set(value), set(obj_decoded.named)) for name in value: obj_decoded[name] + assert_exceeding_data( + self, + lambda: obj_expled.decod(obj_expled_encoded + tail_junk), + tail_junk, + ) @given(integers(min_value=1, max_value=255)) def test_bad_zero_value(self, pad_size): @@ -1573,6 +1636,10 @@ class TestBitString(CommonMixin, TestCase): self.assertTrue(obj.ber_encoded) self.assertEqual(obj.lenindef, lenindef_expected) self.assertTrue(obj.bered) + obj = copy(obj) + self.assertTrue(obj.ber_encoded) + self.assertEqual(obj.lenindef, lenindef_expected) + self.assertTrue(obj.bered) self.assertEqual(len(encoded), obj.tlvlen) @given( @@ -1705,6 +1772,10 @@ class TestBitString(CommonMixin, TestCase): self.assertTrue(obj.ber_encoded) self.assertTrue(obj.lenindef) self.assertTrue(obj.bered) + obj = copy(obj) + self.assertTrue(obj.ber_encoded) + self.assertTrue(obj.lenindef) + self.assertTrue(obj.bered) @composite @@ -1898,11 +1969,12 @@ class TestOctetString(CommonMixin, TestCase): def test_copy(self, values): for klass in (OctetString, OctetStringInherited): obj = klass(*values) - obj_copied = obj.copy() - self.assert_copied_basic_fields(obj, obj_copied) - self.assertEqual(obj._bound_min, obj_copied._bound_min) - self.assertEqual(obj._bound_max, obj_copied._bound_max) - self.assertEqual(obj._value, obj_copied._value) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assert_copied_basic_fields(obj, obj_copied) + self.assertEqual(obj._bound_min, obj_copied._bound_min) + self.assertEqual(obj._bound_max, obj_copied._bound_max) + self.assertEqual(obj._value, obj_copied._value) @given( binary(), @@ -2002,10 +2074,13 @@ class TestOctetString(CommonMixin, TestCase): list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) obj_expled_encoded = obj_expled.encode() + ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, offset=offset, + ctx=ctx_copied, ) + self.assertDictEqual(ctx_copied, ctx_dummy) repr(obj_decoded) list(obj_decoded.pps()) pprint(obj_decoded, big_blobs=True, with_decode_path=True) @@ -2028,6 +2103,11 @@ class TestOctetString(CommonMixin, TestCase): offset + obj_decoded.expl_tlen + obj_decoded.expl_llen, ) self.assertEqual(obj_decoded.expl_offset, offset) + assert_exceeding_data( + self, + lambda: obj_expled.decod(obj_expled_encoded + tail_junk), + tail_junk, + ) @given( integers(min_value=1, max_value=30), @@ -2089,6 +2169,10 @@ class TestOctetString(CommonMixin, TestCase): self.assertTrue(obj.ber_encoded) self.assertEqual(obj.lenindef, lenindef_expected) self.assertTrue(obj.bered) + obj = copy(obj) + self.assertTrue(obj.ber_encoded) + self.assertEqual(obj.lenindef, lenindef_expected) + self.assertTrue(obj.bered) self.assertEqual(len(encoded), obj.tlvlen) @given( @@ -2225,8 +2309,9 @@ class TestNull(CommonMixin, TestCase): optional=optional or False, _decoded=_decoded, ) - obj_copied = obj.copy() - self.assert_copied_basic_fields(obj, obj_copied) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assert_copied_basic_fields(obj, obj_copied) @given(integers(min_value=1).map(tag_encode)) def test_stripped(self, tag_impl): @@ -2299,10 +2384,13 @@ class TestNull(CommonMixin, TestCase): list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) obj_expled_encoded = obj_expled.encode() + ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, offset=offset, + ctx=ctx_copied, ) + self.assertDictEqual(ctx_copied, ctx_dummy) repr(obj_decoded) list(obj_decoded.pps()) pprint(obj_decoded, big_blobs=True, with_decode_path=True) @@ -2323,6 +2411,11 @@ class TestNull(CommonMixin, TestCase): offset + obj_decoded.expl_tlen + obj_decoded.expl_llen, ) self.assertEqual(obj_decoded.expl_offset, offset) + assert_exceeding_data( + self, + lambda: obj_expled.decod(obj_expled_encoded + tail_junk), + tail_junk, + ) @given(integers(min_value=1)) def test_invalid_len(self, l): @@ -2393,6 +2486,7 @@ class TestObjectIdentifier(CommonMixin, TestCase): repr(err.exception) obj = ObjectIdentifier(value) self.assertTrue(obj.ready) + self.assertFalse(obj.ber_encoded) repr(obj) list(obj.pps()) pprint(obj, big_blobs=True, with_decode_path=True) @@ -2492,9 +2586,10 @@ class TestObjectIdentifier(CommonMixin, TestCase): optional=optional, _decoded=_decoded, ) - obj_copied = obj.copy() - self.assert_copied_basic_fields(obj, obj_copied) - self.assertEqual(obj._value, obj_copied._value) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assert_copied_basic_fields(obj, obj_copied) + self.assertEqual(obj._value, obj_copied._value) @settings(max_examples=LONG_TEST_MAX_EXAMPLES) @given( @@ -2631,10 +2726,13 @@ class TestObjectIdentifier(CommonMixin, TestCase): list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) obj_expled_encoded = obj_expled.encode() + ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, offset=offset, + ctx=ctx_copied, ) + self.assertDictEqual(ctx_copied, ctx_dummy) repr(obj_decoded) list(obj_decoded.pps()) pprint(obj_decoded, big_blobs=True, with_decode_path=True) @@ -2657,6 +2755,11 @@ class TestObjectIdentifier(CommonMixin, TestCase): offset + obj_decoded.expl_tlen + obj_decoded.expl_llen, ) self.assertEqual(obj_decoded.expl_offset, offset) + assert_exceeding_data( + self, + lambda: obj_expled.decod(obj_expled_encoded + tail_junk), + tail_junk, + ) @given( oid_strategy().map(ObjectIdentifier), @@ -2700,6 +2803,77 @@ class TestObjectIdentifier(CommonMixin, TestCase): ObjectIdentifier((2, 999, 3)), ) + def test_nonnormalized_first_arc(self): + tampered = ( + ObjectIdentifier.tag_default + + len_encode(2) + + b'\x80' + + ObjectIdentifier((1, 0)).encode()[-1:] + ) + obj, _ = ObjectIdentifier().decode(tampered, ctx={"bered": True}) + self.assertTrue(obj.ber_encoded) + self.assertTrue(obj.bered) + obj = copy(obj) + self.assertTrue(obj.ber_encoded) + self.assertTrue(obj.bered) + with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"): + ObjectIdentifier().decode(tampered) + + @given(data_strategy()) + def test_negative_arcs(self, d): + oid = list(d.draw(oid_strategy())) + if len(oid) == 2: + return + idx = d.draw(integers(min_value=3, max_value=len(oid))) + oid[idx - 1] *= -1 + if oid[idx - 1] == 0: + oid[idx - 1] = -1 + with self.assertRaises(InvalidOID): + ObjectIdentifier(tuple(oid)) + with self.assertRaises(InvalidOID): + ObjectIdentifier(".".join(str(i) for i in oid)) + + @given(data_strategy()) + def test_plused_arcs(self, d): + oid = [str(arc) for arc in d.draw(oid_strategy())] + idx = d.draw(integers(min_value=0, max_value=len(oid))) + oid[idx - 1] = "+" + oid[idx - 1] + with self.assertRaises(InvalidOID): + ObjectIdentifier(".".join(str(i) for i in oid)) + + @given(data_strategy()) + def test_nonnormalized_arcs(self, d): + arcs = d.draw(lists( + integers(min_value=0, max_value=100), + min_size=1, + max_size=5, + )) + dered = ObjectIdentifier((1, 0) + tuple(arcs)).encode() + _, _, lv = tag_strip(dered) + _, _, v = len_decode(lv) + v_no_first_arc = v[1:] + idx_for_tamper = d.draw(integers( + min_value=0, + max_value=len(v_no_first_arc) - 1, + )) + tampered = list(bytearray(v_no_first_arc)) + for _ in range(d.draw(integers(min_value=1, max_value=3))): + tampered.insert(idx_for_tamper, 0x80) + tampered = bytes(bytearray(tampered)) + tampered = ( + ObjectIdentifier.tag_default + + len_encode(len(tampered)) + + tampered + ) + obj, _ = ObjectIdentifier().decode(tampered, ctx={"bered": True}) + self.assertTrue(obj.ber_encoded) + self.assertTrue(obj.bered) + obj = copy(obj) + self.assertTrue(obj.ber_encoded) + self.assertTrue(obj.bered) + with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"): + ObjectIdentifier().decode(tampered) + @composite def enumerated_values_strategy(draw, schema=None, do_expl=False): @@ -2880,6 +3054,7 @@ class TestEnumerated(CommonMixin, TestCase): class E(Enumerated): schema = schema_input + register_class(E) obj = E( value=value, impl=impl, @@ -2888,9 +3063,10 @@ class TestEnumerated(CommonMixin, TestCase): optional=optional, _decoded=_decoded, ) - obj_copied = obj.copy() - self.assert_copied_basic_fields(obj, obj_copied) - self.assertEqual(obj.specs, obj_copied.specs) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assert_copied_basic_fields(obj, obj_copied) + self.assertEqual(obj.specs, obj_copied.specs) @settings(max_examples=LONG_TEST_MAX_EXAMPLES) @given(data_strategy()) @@ -2922,10 +3098,13 @@ class TestEnumerated(CommonMixin, TestCase): list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) obj_expled_encoded = obj_expled.encode() + ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, offset=offset, + ctx=ctx_copied, ) + self.assertDictEqual(ctx_copied, ctx_dummy) repr(obj_decoded) list(obj_decoded.pps()) pprint(obj_decoded, big_blobs=True, with_decode_path=True) @@ -2948,6 +3127,11 @@ class TestEnumerated(CommonMixin, TestCase): offset + obj_decoded.expl_tlen + obj_decoded.expl_llen, ) self.assertEqual(obj_decoded.expl_offset, offset) + assert_exceeding_data( + self, + lambda: obj_expled.decod(obj_expled_encoded + tail_junk), + tail_junk, + ) @composite @@ -3146,11 +3330,12 @@ class StringMixin(object): def test_copy(self, d): values = d.draw(string_values_strategy(self.text_alphabet())) obj = self.base_klass(*values) - obj_copied = obj.copy() - self.assert_copied_basic_fields(obj, obj_copied) - self.assertEqual(obj._bound_min, obj_copied._bound_min) - self.assertEqual(obj._bound_max, obj_copied._bound_max) - self.assertEqual(obj._value, obj_copied._value) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assert_copied_basic_fields(obj, obj_copied) + self.assertEqual(obj._bound_min, obj_copied._bound_min) + self.assertEqual(obj._bound_max, obj_copied._bound_max) + self.assertEqual(obj._value, obj_copied._value) @given(data_strategy()) def test_stripped(self, d): @@ -3247,10 +3432,13 @@ class StringMixin(object): list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) obj_expled_encoded = obj_expled.encode() + ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, offset=offset, + ctx=ctx_copied, ) + self.assertDictEqual(ctx_copied, ctx_dummy) repr(obj_decoded) list(obj_decoded.pps()) pprint(obj_decoded, big_blobs=True, with_decode_path=True) @@ -3275,18 +3463,26 @@ class StringMixin(object): offset + obj_decoded.expl_tlen + obj_decoded.expl_llen, ) self.assertEqual(obj_decoded.expl_offset, offset) + assert_exceeding_data( + self, + lambda: obj_expled.decod(obj_expled_encoded + tail_junk), + tail_junk, + ) class TestUTF8String(StringMixin, CommonMixin, TestCase): base_klass = UTF8String +cyrillic_letters = text( + alphabet="".join(six_unichr(i) for i in list(range(0x0410, 0x044f + 1))), + min_size=1, + max_size=5, +) + + class UnicodeDecodeErrorMixin(object): - @given(text( - alphabet="".join(six_unichr(i) for i in list(range(0x0410, 0x044f + 1))), - min_size=1, - max_size=5, - )) + @given(cyrillic_letters) def test_unicode_decode_error(self, cyrillic_text): with self.assertRaises(DecodeError): self.base_klass(cyrillic_text) @@ -3296,12 +3492,12 @@ class TestNumericString(StringMixin, CommonMixin, TestCase): base_klass = NumericString def text_alphabet(self): - return digits + return digits + " " @given(text(alphabet=ascii_letters, min_size=1, max_size=5)) - def test_non_numeric(self, cyrillic_text): + def test_non_numeric(self, non_numeric_text): with assertRaisesRegex(self, DecodeError, "non-numeric"): - self.base_klass(cyrillic_text) + self.base_klass(non_numeric_text) @given( sets(integers(min_value=0, max_value=10), min_size=2, max_size=2), @@ -3332,6 +3528,57 @@ class TestPrintableString( ): base_klass = PrintableString + def text_alphabet(self): + return ascii_letters + digits + " '()+,-./:=?" + + @given(text(alphabet=sorted(set(whitespace) - set(" ")), min_size=1, max_size=5)) + def test_non_printable(self, non_printable_text): + with assertRaisesRegex(self, DecodeError, "non-printable"): + self.base_klass(non_printable_text) + + @given( + sets(integers(min_value=0, max_value=10), min_size=2, max_size=2), + integers(min_value=0), + decode_path_strat, + ) + def test_invalid_bounds_while_decoding(self, ints, offset, 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) + + def test_allowable_invalid_chars(self): + for c, kwargs in ( + ("*", {"allow_asterisk": True}), + ("&", {"allow_ampersand": True}), + ("&*", {"allow_asterisk": True, "allow_ampersand": True}), + ): + s = "hello invalid" + obj = self.base_klass(s) + for prop in kwargs.keys(): + self.assertFalse(getattr(obj, prop)) + s += c + with assertRaisesRegex(self, DecodeError, "non-printable"): + self.base_klass(s) + self.base_klass(s, **kwargs) + klass = self.base_klass(**kwargs) + obj = klass(s) + for prop in kwargs.keys(): + self.assertTrue(getattr(obj, prop)) + obj = copy(obj) + obj(s) + for prop in kwargs.keys(): + self.assertTrue(getattr(obj, prop)) + class TestTeletexString( UnicodeDecodeErrorMixin, @@ -3394,6 +3641,10 @@ class TestVisibleString( self.assertTrue(obj.ber_encoded) self.assertFalse(obj.lenindef) self.assertTrue(obj.bered) + obj = copy(obj) + self.assertTrue(obj.ber_encoded) + self.assertFalse(obj.lenindef) + self.assertTrue(obj.bered) obj, tail = VisibleString().decode( hexdec("3A8004034A6F6E040265730000"), @@ -3404,6 +3655,10 @@ class TestVisibleString( self.assertTrue(obj.ber_encoded) self.assertTrue(obj.lenindef) self.assertTrue(obj.bered) + obj = copy(obj) + self.assertTrue(obj.ber_encoded) + self.assertTrue(obj.lenindef) + self.assertTrue(obj.bered) class TestGeneralString( @@ -3482,7 +3737,10 @@ class TimeMixin(object): with self.assertRaises(ObjNotReady) as err: obj.encode() repr(err.exception) - value = d.draw(datetimes(min_value=self.min_datetime)) + value = d.draw(datetimes( + min_value=self.min_datetime, + max_value=self.max_datetime, + )) obj = self.base_klass(value) self.assertTrue(obj.ready) repr(obj) @@ -3584,9 +3842,10 @@ class TimeMixin(object): max_datetime=self.max_datetime, )) obj = self.base_klass(*values) - obj_copied = obj.copy() - self.assert_copied_basic_fields(obj, obj_copied) - self.assertEqual(obj._value, obj_copied._value) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assert_copied_basic_fields(obj, obj_copied) + self.assertEqual(obj._value, obj_copied._value) @given(data_strategy()) def test_stripped(self, d): @@ -3636,16 +3895,20 @@ class TimeMixin(object): pprint(obj, big_blobs=True, with_decode_path=True) self.assertFalse(obj.expled) obj_encoded = obj.encode() + self.additional_symmetric_check(value, obj_encoded) obj_expled = obj(value, expl=tag_expl) self.assertTrue(obj_expled.expled) repr(obj_expled) list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) obj_expled_encoded = obj_expled.encode() + ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, offset=offset, + ctx=ctx_copied, ) + self.assertDictEqual(ctx_copied, ctx_dummy) repr(obj_decoded) list(obj_decoded.pps()) pprint(obj_decoded, big_blobs=True, with_decode_path=True) @@ -3667,6 +3930,11 @@ class TimeMixin(object): offset + obj_decoded.expl_tlen + obj_decoded.expl_llen, ) self.assertEqual(obj_decoded.expl_offset, offset) + assert_exceeding_data( + self, + lambda: obj_expled.decod(obj_expled_encoded + tail_junk), + tail_junk, + ) class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase): @@ -3675,6 +3943,28 @@ class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase): min_datetime = datetime(1900, 1, 1) max_datetime = datetime(9999, 12, 31) + def additional_symmetric_check(self, value, obj_encoded): + if value.microsecond > 0: + self.assertFalse(obj_encoded.endswith(b"0Z")) + + def test_x690_vector_valid(self): + for data in (( + b"19920521000000Z", + b"19920622123421Z", + b"19920722132100.3Z", + )): + GeneralizedTime(data) + + def test_x690_vector_invalid(self): + for data in (( + b"19920520240000Z", + b"19920622123421.0Z", + b"19920722132100.30Z", + )): + with self.assertRaises(DecodeError) as err: + GeneralizedTime(data) + repr(err.exception) + def test_go_vectors_invalid(self): for data in (( b"20100102030405", @@ -3751,6 +4041,11 @@ class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase): junk ) + def test_ns_fractions(self): + GeneralizedTime(b"20010101000000.000001Z") + with assertRaisesRegex(self, DecodeError, "only microsecond fractions"): + GeneralizedTime(b"20010101000000.0000001Z") + class TestUTCTime(TimeMixin, CommonMixin, TestCase): base_klass = UTCTime @@ -3758,6 +4053,26 @@ class TestUTCTime(TimeMixin, CommonMixin, TestCase): min_datetime = datetime(2000, 1, 1) max_datetime = datetime(2049, 12, 31) + def additional_symmetric_check(self, value, obj_encoded): + pass + + def test_x690_vector_valid(self): + for data in (( + b"920521000000Z", + b"920622123421Z", + b"920722132100Z", + )): + UTCTime(data) + + def test_x690_vector_invalid(self): + for data in (( + b"920520240000Z", + b"9207221321Z", + )): + with self.assertRaises(DecodeError) as err: + UTCTime(data) + repr(err.exception) + def test_go_vectors_invalid(self): for data in (( b"a10506234540Z", @@ -3955,9 +4270,10 @@ class TestAny(CommonMixin, TestCase): def test_copy(self, values): for klass in (Any, AnyInherited): obj = klass(*values) - obj_copied = obj.copy() - self.assert_copied_basic_fields(obj, obj_copied) - self.assertEqual(obj._value, obj_copied._value) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assert_copied_basic_fields(obj, obj_copied) + self.assertEqual(obj._value, obj_copied._value) @given(binary().map(OctetString)) def test_stripped(self, value): @@ -4029,10 +4345,13 @@ class TestAny(CommonMixin, TestCase): list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) obj_expled_encoded = obj_expled.encode() + ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, offset=offset, + ctx=ctx_copied, ) + self.assertDictEqual(ctx_copied, ctx_dummy) repr(obj_decoded) list(obj_decoded.pps()) pprint(obj_decoded, big_blobs=True, with_decode_path=True) @@ -4057,6 +4376,11 @@ class TestAny(CommonMixin, TestCase): self.assertEqual(obj_decoded.tlen, 0) self.assertEqual(obj_decoded.llen, 0) self.assertEqual(obj_decoded.vlen, len(value)) + assert_exceeding_data( + self, + lambda: obj_expled.decod(obj_expled_encoded + tail_junk), + tail_junk, + ) @given( integers(min_value=1).map(tag_ctxc), @@ -4091,6 +4415,10 @@ class TestAny(CommonMixin, TestCase): self.assertTrue(obj.lenindef) self.assertFalse(obj.ber_encoded) self.assertTrue(obj.bered) + obj = copy(obj) + self.assertTrue(obj.lenindef) + self.assertFalse(obj.ber_encoded) + self.assertTrue(obj.bered) repr(obj) list(obj.pps()) pprint(obj, big_blobs=True, with_decode_path=True) @@ -4319,6 +4647,7 @@ class TestChoice(CommonMixin, TestCase): class Wahl(self.base_klass): schema = _schema + register_class(Wahl) obj = Wahl( value=value, expl=expl, @@ -4326,15 +4655,17 @@ class TestChoice(CommonMixin, TestCase): optional=optional or False, _decoded=_decoded, ) - obj_copied = obj.copy() - self.assertIsNone(obj.tag) - self.assertIsNone(obj_copied.tag) - # hack for assert_copied_basic_fields - obj.tag = "whatever" - obj_copied.tag = "whatever" - self.assert_copied_basic_fields(obj, obj_copied) - self.assertEqual(obj._value, obj_copied._value) - self.assertEqual(obj.specs, obj_copied.specs) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assertIsNone(obj.tag) + self.assertIsNone(obj_copied.tag) + # hack for assert_copied_basic_fields + obj.tag = "whatever" + obj_copied.tag = "whatever" + self.assert_copied_basic_fields(obj, obj_copied) + obj.tag = None + self.assertEqual(obj._value, obj_copied._value) + self.assertEqual(obj.specs, obj_copied.specs) @given(booleans()) def test_stripped(self, value): @@ -4380,10 +4711,13 @@ class TestChoice(CommonMixin, TestCase): list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) obj_expled_encoded = obj_expled.encode() + ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, offset=offset, + ctx=ctx_copied, ) + self.assertDictEqual(ctx_copied, ctx_dummy) repr(obj_decoded) list(obj_decoded.pps()) pprint(obj_decoded, big_blobs=True, with_decode_path=True) @@ -4414,6 +4748,11 @@ class TestChoice(CommonMixin, TestCase): ], obj_encoded, ) + assert_exceeding_data( + self, + lambda: obj_expled.decod(obj_expled_encoded + tail_junk), + tail_junk, + ) @given(integers()) def test_set_get(self, value): @@ -4473,15 +4812,13 @@ def seq_values_strategy(draw, seq_klass, do_expl=False): value = None if draw(booleans()): value = seq_klass() - value._value = { - k: v for k, v in draw(dictionaries( - integers(), - one_of( - booleans().map(Boolean), - integers().map(Integer), - ), - )).items() - } + value._value = draw(dictionaries( + integers(), + one_of( + booleans().map(Boolean), + integers().map(Integer), + ), + )) schema = None if draw(booleans()): schema = list(draw(dictionaries( @@ -4500,15 +4837,13 @@ def seq_values_strategy(draw, seq_klass, do_expl=False): default = None if draw(booleans()): default = seq_klass() - default._value = { - k: v for k, v in draw(dictionaries( - integers(), - one_of( - booleans().map(Boolean), - integers().map(Integer), - ), - )).items() - } + default._value = draw(dictionaries( + integers(), + one_of( + booleans().map(Boolean), + integers().map(Integer), + ), + )) optional = draw(one_of(none(), booleans())) _decoded = ( draw(integers(min_value=0)), @@ -4766,13 +5101,15 @@ class SeqMixing(object): def test_copy(self, d): class SeqInherited(self.base_klass): pass + register_class(SeqInherited) for klass in (self.base_klass, SeqInherited): values = d.draw(seq_values_strategy(seq_klass=klass)) obj = klass(*values) - obj_copied = obj.copy() - self.assert_copied_basic_fields(obj, obj_copied) - self.assertEqual(obj.specs, obj_copied.specs) - self.assertEqual(obj._value, obj_copied._value) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assert_copied_basic_fields(obj, obj_copied) + self.assertEqual(obj.specs, obj_copied.specs) + self.assertEqual(obj._value, obj_copied._value) @given(data_strategy()) def test_stripped(self, d): @@ -4891,10 +5228,17 @@ class SeqMixing(object): t, _, lv = tag_strip(seq_encoded) _, _, v = len_decode(lv) seq_encoded_lenindef = t + LENINDEF + v + EOC + ctx_copied = deepcopy(ctx_dummy) + ctx_copied["bered"] = True seq_decoded_lenindef, tail_lenindef = seq.decode( seq_encoded_lenindef + tail_junk, - ctx={"bered": True}, + ctx=ctx_copied, ) + del ctx_copied["bered"] + self.assertDictEqual(ctx_copied, ctx_dummy) + self.assertTrue(seq_decoded_lenindef.lenindef) + self.assertTrue(seq_decoded_lenindef.bered) + seq_decoded_lenindef = copy(seq_decoded_lenindef) self.assertTrue(seq_decoded_lenindef.lenindef) self.assertTrue(seq_decoded_lenindef.bered) with self.assertRaises(DecodeError): @@ -4929,6 +5273,12 @@ class SeqMixing(object): obj.encode(), ) + assert_exceeding_data( + self, + lambda: seq.decod(seq_encoded_lenindef + tail_junk, ctx={"bered": True}), + tail_junk, + ) + @settings(max_examples=LONG_TEST_MAX_EXAMPLES) @given(data_strategy()) def test_symmetric_with_seq(self, d): @@ -5014,6 +5364,9 @@ class SeqMixing(object): seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx) self.assertTrue(seq_decoded.ber_encoded) self.assertTrue(seq_decoded.bered) + seq_decoded = copy(seq_decoded) + self.assertTrue(seq_decoded.ber_encoded) + self.assertTrue(seq_decoded.bered) for name, value in _schema: self.assertEqual(seq_decoded[name], seq_with_default[name]) self.assertEqual(seq_decoded[name], value) @@ -5042,8 +5395,7 @@ class SeqMixing(object): with self.assertRaises(TagMismatch): seq_missing.decode(seq_encoded) - @given(data_strategy()) - def test_bered(self, d): + def test_bered(self): class Seq(self.base_klass): schema = (("underlying", Boolean()),) encoded = Boolean.tag_default + len_encode(1) + b"\x01" @@ -5052,6 +5404,10 @@ class SeqMixing(object): self.assertFalse(decoded.ber_encoded) self.assertFalse(decoded.lenindef) self.assertTrue(decoded.bered) + decoded = copy(decoded) + self.assertFalse(decoded.ber_encoded) + self.assertFalse(decoded.lenindef) + self.assertTrue(decoded.bered) class Seq(self.base_klass): schema = (("underlying", OctetString()),) @@ -5068,6 +5424,10 @@ class SeqMixing(object): self.assertFalse(decoded.ber_encoded) self.assertFalse(decoded.lenindef) self.assertTrue(decoded.bered) + decoded = copy(decoded) + self.assertFalse(decoded.ber_encoded) + self.assertFalse(decoded.lenindef) + self.assertTrue(decoded.bered) class TestSequence(SeqMixing, CommonMixin, TestCase): @@ -5164,9 +5524,12 @@ class TestSet(SeqMixing, CommonMixin, TestCase): seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx) self.assertTrue(seq_decoded.ber_encoded) self.assertTrue(seq_decoded.bered) + seq_decoded = copy(seq_decoded) + self.assertTrue(seq_decoded.ber_encoded) + self.assertTrue(seq_decoded.bered) self.assertSequenceEqual( [bytes(seq_decoded[str(i)]) for i, t in enumerate(tags)], - [t for t in tags], + tags, ) @@ -5439,6 +5802,7 @@ class SeqOfMixing(object): class SeqOf(self.base_klass): schema = _schema + register_class(SeqOf) obj = SeqOf( value=value, bounds=bounds, @@ -5448,11 +5812,12 @@ class SeqOfMixing(object): optional=optional or False, _decoded=_decoded, ) - obj_copied = obj.copy() - self.assert_copied_basic_fields(obj, obj_copied) - self.assertEqual(obj._bound_min, obj_copied._bound_min) - self.assertEqual(obj._bound_max, obj_copied._bound_max) - self.assertEqual(obj._value, obj_copied._value) + for copy_func in copy_funcs: + obj_copied = copy_func(obj) + self.assert_copied_basic_fields(obj, obj_copied) + self.assertEqual(obj._bound_min, obj_copied._bound_min) + self.assertEqual(obj._bound_max, obj_copied._bound_max) + self.assertEqual(obj._value, obj_copied._value) @given( lists(binary()), @@ -5544,10 +5909,13 @@ class SeqOfMixing(object): list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) obj_expled_encoded = obj_expled.encode() + ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, offset=offset, + ctx=ctx_copied, ) + self.assertDictEqual(ctx_copied, ctx_dummy) repr(obj_decoded) list(obj_decoded.pps()) pprint(obj_decoded, big_blobs=True, with_decode_path=True) @@ -5586,17 +5954,26 @@ class SeqOfMixing(object): ) self.assertTrue(obj_decoded_lenindef.lenindef) self.assertTrue(obj_decoded_lenindef.bered) + obj_decoded_lenindef = copy(obj_decoded_lenindef) + self.assertTrue(obj_decoded_lenindef.lenindef) + self.assertTrue(obj_decoded_lenindef.bered) repr(obj_decoded_lenindef) list(obj_decoded_lenindef.pps()) pprint(obj_decoded_lenindef, big_blobs=True, with_decode_path=True) + self.assertEqual(tail_lenindef, tail_junk) self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef)) with self.assertRaises(DecodeError): obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True}) with self.assertRaises(DecodeError): obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True}) - @given(data_strategy()) - def test_bered(self, d): + assert_exceeding_data( + self, + lambda: obj_expled.decod(obj_expled_encoded + tail_junk), + tail_junk, + ) + + def test_bered(self): class SeqOf(self.base_klass): schema = Boolean() encoded = Boolean(False).encode() @@ -5608,6 +5985,10 @@ class SeqOfMixing(object): self.assertFalse(decoded.ber_encoded) self.assertFalse(decoded.lenindef) self.assertTrue(decoded.bered) + decoded = copy(decoded) + self.assertFalse(decoded.ber_encoded) + self.assertFalse(decoded.lenindef) + self.assertTrue(decoded.bered) class SeqOf(self.base_klass): schema = OctetString() @@ -5625,6 +6006,10 @@ class SeqOfMixing(object): self.assertFalse(decoded.ber_encoded) self.assertFalse(decoded.lenindef) self.assertTrue(decoded.bered) + decoded = copy(decoded) + self.assertFalse(decoded.ber_encoded) + self.assertFalse(decoded.lenindef) + self.assertTrue(decoded.bered) class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase): @@ -5690,6 +6075,9 @@ class TestSetOf(SeqOfMixing, CommonMixin, TestCase): seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx) self.assertTrue(seq_decoded.ber_encoded) self.assertTrue(seq_decoded.bered) + seq_decoded = copy(seq_decoded) + self.assertTrue(seq_decoded.ber_encoded) + self.assertTrue(seq_decoded.bered) self.assertSequenceEqual( [obj.encode() for obj in seq_decoded], values, @@ -5837,8 +6225,11 @@ class TestGoMarshalVectors(TestCase): seq = Seq() seq["erste"] = PrintableString("test") self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374")) + # Asterisk is actually not allowable + PrintableString._allowable_chars |= set(b"*") seq["erste"] = PrintableString("test*") self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a")) + PrintableString._allowable_chars -= set(b"*") class Seq(Sequence): schema = ( @@ -5890,7 +6281,10 @@ class TestPP(TestCase): chosen_id = oids[chosen] pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen) self.assertNotIn(chosen_id, pp_console_row(pp)) - self.assertIn(chosen_id, pp_console_row(pp, oids=oids)) + self.assertIn( + chosen_id, + pp_console_row(pp, oid_maps=[{'whatever': 'whenever'}, oids]), + ) class TestAutoAddSlots(TestCase): @@ -6010,7 +6404,12 @@ class TestDefinesByPath(TestCase): pprint(seq_sequenced, big_blobs=True, with_decode_path=True) defines_by_path = [] - seq_integered, _ = Seq().decode(seq_integered_raw) + ctx_copied = deepcopy(ctx_dummy) + seq_integered, _ = Seq().decode( + seq_integered_raw, + ctx=ctx_copied, + ) + self.assertDictEqual(ctx_copied, ctx_dummy) self.assertIsNone(seq_integered["value"].defined) defines_by_path.append( (("type",), ((("value",), { @@ -6018,10 +6417,13 @@ class TestDefinesByPath(TestCase): type_sequenced: SeqInner(), }),)) ) + ctx_copied["defines_by_path"] = defines_by_path seq_integered, _ = Seq().decode( seq_integered_raw, - ctx={"defines_by_path": defines_by_path}, + ctx=ctx_copied, ) + del ctx_copied["defines_by_path"] + self.assertDictEqual(ctx_copied, ctx_dummy) self.assertIsNotNone(seq_integered["value"].defined) self.assertEqual(seq_integered["value"].defined[0], type_integered) self.assertEqual(seq_integered["value"].defined[1], Integer(123)) @@ -6032,10 +6434,13 @@ class TestDefinesByPath(TestCase): list(seq_integered.pps()) pprint(seq_integered, big_blobs=True, with_decode_path=True) + ctx_copied["defines_by_path"] = defines_by_path seq_sequenced, _ = Seq().decode( seq_sequenced_raw, - ctx={"defines_by_path": defines_by_path}, + ctx=ctx_copied, ) + del ctx_copied["defines_by_path"] + self.assertDictEqual(ctx_copied, ctx_dummy) self.assertIsNotNone(seq_sequenced["value"].defined) self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced) seq_inner = seq_sequenced["value"].defined[1] @@ -6048,10 +6453,13 @@ class TestDefinesByPath(TestCase): ("value", DecodePathDefBy(type_sequenced), "typeInner"), ((("valueInner",), {type_innered: Pairs()}),), )) + ctx_copied["defines_by_path"] = defines_by_path seq_sequenced, _ = Seq().decode( seq_sequenced_raw, - ctx={"defines_by_path": defines_by_path}, + ctx=ctx_copied, ) + del ctx_copied["defines_by_path"] + self.assertDictEqual(ctx_copied, ctx_dummy) self.assertIsNotNone(seq_sequenced["value"].defined) self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced) seq_inner = seq_sequenced["value"].defined[1] @@ -6078,10 +6486,13 @@ class TestDefinesByPath(TestCase): type_octet_stringed: OctetString(), }),), )) + ctx_copied["defines_by_path"] = defines_by_path seq_sequenced, _ = Seq().decode( seq_sequenced_raw, - ctx={"defines_by_path": defines_by_path}, + ctx=ctx_copied, ) + del ctx_copied["defines_by_path"] + self.assertDictEqual(ctx_copied, ctx_dummy) self.assertIsNotNone(seq_sequenced["value"].defined) self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced) seq_inner = seq_sequenced["value"].defined[1] @@ -6175,9 +6586,15 @@ class TestStrictDefaultExistence(TestCase): decoded, _ = seq.decode(raw, ctx={"allow_default_values": True}) self.assertTrue(decoded.ber_encoded) self.assertTrue(decoded.bered) + decoded = copy(decoded) + self.assertTrue(decoded.ber_encoded) + self.assertTrue(decoded.bered) decoded, _ = seq.decode(raw, ctx={"bered": True}) self.assertTrue(decoded.ber_encoded) self.assertTrue(decoded.bered) + decoded = copy(decoded) + self.assertTrue(decoded.ber_encoded) + self.assertTrue(decoded.bered) class TestX690PrefixedType(TestCase): @@ -6227,3 +6644,15 @@ class TestExplOOB(TestCase): with assertRaisesRegex(self, DecodeError, "explicit tag out-of-bound"): Integer(expl=expl).decode(raw) Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True}) + + +class TestPickleDifferentVersion(TestCase): + def runTest(self): + pickled = pickle_dumps(Integer(123), pickle_proto) + import pyderasn + version_orig = pyderasn.__version__ + pyderasn.__version__ += "different" + with assertRaisesRegex(self, ValueError, "different PyDERASN version"): + pickle_loads(pickled) + pyderasn.__version__ = version_orig + pickle_loads(pickled)