# coding: utf-8
-# PyDERASN -- Python ASN.1 DER codec with abstract structures
-# Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
+# PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures
+# Copyright (C) 2017-2020 Sergey Matveev <stargrave@stargrave.org>
#
# 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
# License along with this program. If not, see
# <http://www.gnu.org/licenses/>.
+from copy import copy
+from copy import deepcopy
from datetime import datetime
+from datetime import timedelta
+from importlib import import_module
+from io import BytesIO
+from operator import attrgetter
+from os import environ
+from os import urandom
+from random import random
from string import ascii_letters
+from string import digits
from string import printable
from string import whitespace
+from time import mktime
+from time import time
from unittest import TestCase
from hypothesis import assume
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
from six import iterbytes
from six import PY2
from six import text_type
+from six import unichr as six_unichr
+from six.moves import xrange as six_xrange
+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
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 DecodePathDefBy
+from pyderasn import encode2pass
+from pyderasn import encode_cer
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
from pyderasn import InvalidValueType
from pyderasn import len_decode
from pyderasn import len_encode
+from pyderasn import LEN_YYMMDDHHMMSSZ
+from pyderasn import LEN_YYYYMMDDHHMMSSDMZ
+from pyderasn import LEN_YYYYMMDDHHMMSSZ
+from pyderasn import LENINDEF
+from pyderasn import LenIndefForm
from pyderasn import NotEnoughData
from pyderasn import Null
from pyderasn import NumericString
from pyderasn import VisibleString
-settings.register_profile('local', settings(
+max_examples = environ.get("MAX_EXAMPLES")
+settings.register_profile("local", settings(
deadline=5000,
- perform_health_check=False,
+ **({"max_examples": int(max_examples)} if max_examples else {})
))
-settings.load_profile('local')
+settings.load_profile("local")
LONG_TEST_MAX_EXAMPLES = settings().max_examples * 4
tag_classes = sampled_from((
TagClassUniversal,
))
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:
+ return
+ with assertRaisesRegex(self, ExceedingData, "%d trailing bytes" % len(junk)) as err:
+ call()
+ repr(err)
class TestHex(TestCase):
with self.assertRaises(ValueError):
self.base_klass(impl=b"whatever", expl=b"whenever")
- @given(binary(), integers(), integers(), integers())
+ @given(binary(min_size=1), integers(), integers(), integers())
def test_decoded(self, impl, offset, llen, vlen):
obj = self.base_klass(impl=impl, _decoded=(offset, llen, vlen))
self.assertEqual(obj.offset, offset)
self.assertEqual(obj.tlen, len(impl))
self.assertEqual(obj.tlvlen, obj.tlen + obj.llen + obj.vlen)
- @given(binary())
+ @given(binary(min_size=1))
def test_impl_inherited(self, impl_tag):
class Inherited(self.base_klass):
impl = impl_tag
obj = Inherited()
self.assertSequenceEqual(obj.impl, impl_tag)
self.assertFalse(obj.expled)
+ if obj.ready:
+ tag_class, _, tag_num = tag_decode(impl_tag)
+ self.assertEqual(obj.tag_order, (tag_class, tag_num))
- @given(binary())
+ @given(binary(min_size=1))
def test_expl_inherited(self, expl_tag):
class Inherited(self.base_klass):
expl = expl_tag
obj = Inherited()
self.assertSequenceEqual(obj.expl, expl_tag)
self.assertTrue(obj.expled)
+ if obj.ready:
+ tag_class, _, tag_num = tag_decode(expl_tag)
+ self.assertEqual(obj.tag_order, (tag_class, tag_num))
def assert_copied_basic_fields(self, obj, obj_copied):
self.assertEqual(obj, obj_copied)
self.assertEqual(obj.offset, obj_copied.offset)
self.assertEqual(obj.llen, obj_copied.llen)
self.assertEqual(obj.vlen, obj_copied.vlen)
+ if obj.ready:
+ self.assertEqual(obj.tag_order, obj_copied.tag_order)
@composite
obj = Boolean()
self.assertFalse(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
with self.assertRaises(ObjNotReady) as err:
obj.encode()
+ with self.assertRaises(ObjNotReady) as err:
+ encode2pass(obj)
repr(err.exception)
obj = Boolean(value)
self.assertTrue(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
- @given(booleans(), booleans(), binary(), binary())
+ @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
def test_comparison(self, value1, value2, tag1, tag2):
for klass in (Boolean, BooleanInherited):
obj1 = klass(value1)
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(),
obj = Boolean(value, impl=tag_impl)
with self.assertRaises(NotEnoughData):
obj.decode(obj.encode()[:-1])
+ with self.assertRaises(NotEnoughData):
+ obj.decode(encode2pass(obj)[:-1])
@given(
booleans(),
obj = Boolean(value, expl=tag_expl)
with self.assertRaises(NotEnoughData):
obj.decode(obj.encode()[:-1])
+ with self.assertRaises(NotEnoughData):
+ obj.decode(encode2pass(obj)[:-1])
@given(
integers(min_value=31),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_tag(self, tag, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
Boolean().decode(
tag_encode(tag)[:-1],
@given(
integers(min_value=31),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_expl_tag(self, tag, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
Boolean(expl=Boolean.tag_default).decode(
tag_encode(tag)[:-1],
@given(
integers(min_value=128),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_len(self, l, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
Boolean().decode(
Boolean.tag_default + len_encode(l)[:-1],
@given(
integers(min_value=128),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_expl_len(self, l, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
Boolean(expl=Boolean.tag_default).decode(
Boolean.tag_default + len_encode(l)[:-1],
booleans(),
integers(min_value=1).map(tag_ctxc),
integers(min_value=0),
+ binary(max_size=5),
+ decode_path_strat,
)
- def test_symmetric(self, values, value, tag_expl, offset):
+ def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
for klass in (Boolean, BooleanInherited):
_, _, _, default, optional, _decoded = values
obj = klass(
_decoded=_decoded,
)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertFalse(obj.expled)
obj_encoded = obj.encode()
+ self.assertEqual(encode2pass(obj), obj_encoded)
+ self.assertSequenceEqual(encode_cer(obj), obj_encoded)
obj_expled = obj(value, expl=tag_expl)
self.assertTrue(obj_expled.expled)
repr(obj_expled)
- pprint(obj_expled)
- obj_expled_encoded = obj_expled.encode()
- obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
+ list(obj_expled.pps())
+ pprint(obj_expled, big_blobs=True, with_decode_path=True)
+ obj_expled_cer = encode_cer(obj_expled)
+ self.assertNotEqual(obj_expled_cer, obj_encoded)
+ self.assertSequenceEqual(
+ obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
+ obj_expled.encode(),
+ )
+ 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)
- pprint(obj_decoded)
- self.assertEqual(tail, b"")
+ list(obj_decoded.pps())
+ pprint(obj_decoded, big_blobs=True, with_decode_path=True)
+ 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(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(
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,
+ )
+
+ evgens = list(obj_expled.decode_evgen(
+ hexdec(obj_expled_hex_encoded) + tail_junk,
+ offset=offset,
+ decode_path=decode_path,
+ ctx=ctx_copied,
+ ))
+ self.assertEqual(len(evgens), 1)
+ _decode_path, obj, tail = evgens[0]
+ self.assertSequenceEqual(tail, tail_junk)
+ self.assertEqual(_decode_path, decode_path)
+ self.assertEqual(obj, obj_decoded)
+ self.assertEqual(obj.expl_offset, offset)
+ repr(obj)
+ list(obj.pps())
@given(integers(min_value=2))
def test_invalid_len(self, l):
)))
@given(integers(min_value=0 + 1, max_value=255 - 1))
- def test_invalid_value(self, value):
+ def test_ber_value(self, value):
with assertRaisesRegex(self, DecodeError, "unacceptable Boolean value"):
Boolean().decode(b"".join((
Boolean.tag_default,
len_encode(1),
int2byte(value),
)))
+ encoded = b"".join((
+ Boolean.tag_default,
+ len_encode(1),
+ int2byte(value),
+ ))
+ obj, _ = Boolean().decode(encoded, ctx={"bered": True})
+ list(Boolean().decode_evgen(encoded, ctx={"bered": True}))
+ self.assertTrue(bool(obj))
+ 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),
+ binary().filter(lambda x: not x.startswith(EOC)),
+ )
+ def test_ber_expl_no_eoc(self, expl, junk):
+ encoded = expl + LENINDEF + Boolean(False).encode()
+ with self.assertRaises(LenIndefForm):
+ Boolean(expl=expl).decode(encoded + junk)
+ with assertRaisesRegex(self, DecodeError, "no EOC"):
+ Boolean(expl=expl).decode(encoded + junk, ctx={"bered": True})
+ obj, tail = Boolean(expl=expl).decode(
+ encoded + EOC + junk,
+ ctx={"bered": True},
+ )
+ self.assertTrue(obj.expl_lenindef)
+ 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())
+ pprint(obj, big_blobs=True, with_decode_path=True)
+
+ @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 +
+ LENINDEF +
+ Boolean(value).encode() +
+ EOC
+ )
+ encoded = SequenceOf.tag_default + len_encode(len(encoded)) + encoded
+
+ class SeqOf(SequenceOf):
+ schema = Boolean(expl=expl)
+ with self.assertRaises(LenIndefForm):
+ SeqOf().decode(encoded)
+ seqof, tail = SeqOf().decode(encoded, ctx={"bered": True})
+ list(SeqOf().decode_evgen(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.ber_encoded,
+ v.lenindef,
+ v.expl_lenindef,
+ v.bered,
+ ) for v in seqof
+ ),
+ set(((
+ 3 + EOC_LEN,
+ len(expl) + 1 + 3 + EOC_LEN,
+ len(expl),
+ 1,
+ False,
+ False,
+ True,
+ True,
+ ),)),
+ )
+ repr(seqof)
+ list(seqof.pps())
+ pprint(seqof, big_blobs=True, with_decode_path=True)
@composite
obj = Integer()
self.assertFalse(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
with self.assertRaises(ObjNotReady) as err:
obj.encode()
+ with self.assertRaises(ObjNotReady) as err:
+ encode2pass(obj)
repr(err.exception)
obj = Integer(value)
self.assertTrue(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
hash(obj)
- @given(integers(), integers(), binary(), binary())
+ @given(integers(), integers(), binary(min_size=1), binary(min_size=1))
def test_comparison(self, value1, value2, tag1, tag2):
for klass in (Integer, IntegerInherited):
obj1 = klass(value1)
with self.assertRaises(BoundsError) as err:
Integer(value=values[0], bounds=(values[1], values[2]))
repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ Integer(bounds=(values[1], values[2])).decode(
+ Integer(values[0]).encode()
+ )
+ repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ Integer(bounds=(values[1], values[2])).decode(
+ encode2pass(Integer(values[0]))
+ )
with self.assertRaises(BoundsError) as err:
Integer(value=values[2], bounds=(values[0], values[1]))
repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ Integer(bounds=(values[0], values[1])).decode(
+ Integer(values[2]).encode()
+ )
+ repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ Integer(bounds=(values[0], values[1])).decode(
+ encode2pass(Integer(values[2]))
+ )
@given(data_strategy())
def test_call(self, d):
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(),
@given(
integers(min_value=31),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_tag(self, tag, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
Integer().decode(
tag_encode(tag)[:-1],
@given(
integers(min_value=128),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_len(self, l, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
Integer().decode(
Integer.tag_default + len_encode(l)[:-1],
@given(
sets(integers(), min_size=2, max_size=2),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
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 Int(Integer):
integers(),
integers(min_value=1).map(tag_ctxc),
integers(min_value=0),
+ binary(max_size=5),
+ decode_path_strat,
)
- def test_symmetric(self, values, value, tag_expl, offset):
+ def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
for klass in (Integer, IntegerInherited):
_, _, _, _, default, optional, _, _decoded = values
obj = klass(
_decoded=_decoded,
)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertFalse(obj.expled)
obj_encoded = obj.encode()
+ self.assertEqual(encode2pass(obj), obj_encoded)
+ self.assertSequenceEqual(encode_cer(obj), obj_encoded)
obj_expled = obj(value, expl=tag_expl)
self.assertTrue(obj_expled.expled)
repr(obj_expled)
- pprint(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, offset=offset)
+ obj_expled_cer = encode_cer(obj_expled)
+ self.assertNotEqual(obj_expled_cer, obj_encoded)
+ self.assertSequenceEqual(
+ obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
+ obj_expled_encoded,
+ )
+ 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)
- pprint(obj_decoded)
- self.assertEqual(tail, b"")
+ list(obj_decoded.pps())
+ pprint(obj_decoded, big_blobs=True, with_decode_path=True)
+ self.assertEqual(tail, tail_junk)
self.assertEqual(obj_decoded, obj_expled)
self.assertNotEqual(obj_decoded, obj)
self.assertEqual(int(obj_decoded), int(obj_expled))
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,
+ )
+
+ evgens = list(obj_expled.decode_evgen(
+ obj_expled_encoded + tail_junk,
+ offset=offset,
+ decode_path=decode_path,
+ ctx=ctx_copied,
+ ))
+ self.assertEqual(len(evgens), 1)
+ _decode_path, obj, tail = evgens[0]
+ self.assertSequenceEqual(tail, tail_junk)
+ self.assertEqual(_decode_path, decode_path)
+ self.assertEqual(obj, obj_decoded)
+ self.assertEqual(obj.expl_offset, offset)
+ repr(obj)
+ list(obj.pps())
def test_go_vectors_valid(self):
for data, expect in ((
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)))
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)
obj = BitString()
self.assertFalse(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
with self.assertRaises(ObjNotReady) as err:
obj.encode()
repr(err.exception)
+ with self.assertRaises(ObjNotReady) as err:
+ encode2pass(obj)
obj = BitString(value)
self.assertTrue(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
@given(
tuples(integers(min_value=0), binary()),
tuples(integers(min_value=0), binary()),
- binary(),
- binary(),
+ binary(min_size=1),
+ binary(min_size=1),
)
def test_comparison(self, value1, value2, tag1, tag2):
for klass in (BitString, BitStringInherited):
class BS(klass):
schema = _schema
+ register_class(BS)
obj = BS(
value=value,
impl=impl,
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(),
@given(
integers(min_value=31),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_tag(self, tag, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
BitString().decode(
tag_encode(tag)[:-1],
@given(
integers(min_value=128),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_len(self, l, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
BitString().decode(
BitString.tag_default + len_encode(l)[:-1],
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))
+ decode_path = d.draw(decode_path_strat)
for klass in (BitString, BitStringInherited):
class BS(klass):
schema = _schema
_decoded=_decoded,
)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertFalse(obj.expled)
obj_encoded = obj.encode()
+ self.assertEqual(encode2pass(obj), obj_encoded)
+ self.assertSequenceEqual(encode_cer(obj), obj_encoded)
obj_expled = obj(value, expl=tag_expl)
self.assertTrue(obj_expled.expled)
repr(obj_expled)
- pprint(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, offset=offset)
+ obj_expled_cer = encode_cer(obj_expled)
+ self.assertNotEqual(obj_expled_cer, obj_encoded)
+ self.assertSequenceEqual(
+ obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
+ obj_expled_encoded,
+ )
+ 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)
- pprint(obj_decoded)
- self.assertEqual(tail, b"")
+ list(obj_decoded.pps())
+ pprint(obj_decoded, big_blobs=True, with_decode_path=True)
+ 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.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,
+ )
+
+ evgens = list(obj_expled.decode_evgen(
+ obj_expled_encoded + tail_junk,
+ offset=offset,
+ decode_path=decode_path,
+ ctx=ctx_copied,
+ ))
+ self.assertEqual(len(evgens), 1)
+ _decode_path, obj, tail = evgens[0]
+ self.assertSequenceEqual(tail, tail_junk)
+ self.assertEqual(_decode_path, decode_path)
+ self.assertEqual(obj.expl_offset, offset)
+ repr(obj)
+ list(obj.pps())
@given(integers(min_value=1, max_value=255))
def test_bad_zero_value(self, pad_size):
self.assertTrue(obj[9])
self.assertFalse(obj[17])
+ @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
+ @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),
+ binary(),
+ decode_path_strat,
+ )
+ def test_constructed(self, impl, chunk_inputs, chunk_last_bits, junk, decode_path):
+ def chunk_constructed(contents):
+ return (
+ tag_encode(form=TagFormConstructed, num=3) +
+ LENINDEF +
+ b"".join(BitString(content).encode() for content in contents) +
+ EOC
+ )
+ chunks = []
+ chunks_len_expected = []
+ 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
+ chunks_len_expected.append(len(chunk_input) + 1)
+ else:
+ chunks.append(chunk_constructed(chunk_input))
+ payload = b"".join(chunk_input)
+ payload_expected += payload
+ bit_len_expected += len(payload) * 8
+ for c in chunk_input:
+ chunks_len_expected.append(len(c) + 1)
+ chunks_len_expected.append(len(chunks[-1]) - 1 - 1)
+ chunk_last = BitString("'%s'B" % "".join(
+ "1" if bit else "0" for bit in chunk_last_bits
+ ))
+ chunks_len_expected.append(BitString().decod(chunk_last.encode()).vlen)
+ payload_expected += bytes(chunk_last)
+ bit_len_expected += chunk_last.bit_len
+ encoded_indefinite = (
+ tag_encode(form=TagFormConstructed, num=impl) +
+ LENINDEF +
+ 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 + junk,
+ ctx={"bered": True},
+ )
+ self.assertSequenceEqual(tail, junk)
+ self.assertEqual(obj.bit_len, bit_len_expected)
+ self.assertSequenceEqual(bytes(obj), payload_expected)
+ 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)
+ repr(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
+
+ evgens = list(BitString(impl=tag_encode(impl)).decode_evgen(
+ encoded,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ ))
+ self.assertEqual(len(evgens), len(chunks_len_expected) + 1)
+ for chunk_len_expected, (dp, obj, _) in zip(chunks_len_expected, evgens):
+ self.assertGreater(len(dp), len(decode_path))
+ self.assertEqual(obj.vlen, chunk_len_expected)
+
+ @given(
+ integers(min_value=0),
+ decode_path_strat,
+ )
+ def test_ber_definite_too_short(self, offset, decode_path):
+ with assertRaisesRegex(self, DecodeError, "longer than data") as err:
+ BitString().decode(
+ tag_encode(3, form=TagFormConstructed) + len_encode(1),
+ offset=offset,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ )
+ self.assertEqual(err.exception.decode_path, decode_path)
+ self.assertEqual(err.exception.offset, offset)
+
+ @given(
+ integers(min_value=0),
+ decode_path_strat,
+ )
+ def test_ber_definite_no_data(self, offset, decode_path):
+ with assertRaisesRegex(self, DecodeError, "zero length") as err:
+ BitString().decode(
+ tag_encode(3, form=TagFormConstructed) + len_encode(0),
+ offset=offset,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ )
+ self.assertEqual(err.exception.decode_path, decode_path)
+ self.assertEqual(err.exception.offset, offset)
+
+ @given(
+ integers(min_value=0),
+ decode_path_strat,
+ integers(min_value=1, max_value=3),
+ )
+ def test_ber_indefinite_no_eoc(self, offset, decode_path, chunks):
+ bs = BitString(b"data").encode()
+ with self.assertRaises(NotEnoughData) as err:
+ BitString().decode(
+ tag_encode(3, form=TagFormConstructed) + LENINDEF + chunks * bs,
+ offset=offset,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ )
+ self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
+ self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
+
+ @given(
+ integers(min_value=0),
+ decode_path_strat,
+ integers(min_value=1, max_value=3),
+ )
+ def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
+ bs = BitString(b"data").encode()
+ bs_longer = BitString(b"data-longer").encode()
+ with assertRaisesRegex(self, DecodeError, "chunk out of bounds") as err:
+ BitString().decode(
+ (
+ tag_encode(3, form=TagFormConstructed) +
+ len_encode((chunks + 1) * len(bs)) +
+ chunks * bs +
+ bs_longer
+ ),
+ offset=offset,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ )
+ self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
+ self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
+
+ @given(
+ integers(min_value=0),
+ decode_path_strat,
+ )
+ def test_ber_indefinite_no_chunks(self, offset, decode_path):
+ with assertRaisesRegex(self, DecodeError, "no chunks") as err:
+ BitString().decode(
+ tag_encode(3, form=TagFormConstructed) + LENINDEF + EOC,
+ offset=offset,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ )
+ self.assertEqual(err.exception.decode_path, decode_path)
+ self.assertEqual(err.exception.offset, offset)
+
+ @given(data_strategy())
+ def test_ber_indefinite_not_multiple(self, d):
+ bs_short = BitString("'A'H").encode()
+ bs_full = BitString("'AA'H").encode()
+ chunks = [bs_full for _ in range(d.draw(integers(min_value=0, max_value=3)))]
+ chunks.append(bs_short)
+ d.draw(permutations(chunks))
+ chunks.append(bs_short)
+ offset = d.draw(integers(min_value=0))
+ decode_path = d.draw(decode_path_strat)
+ with assertRaisesRegex(self, DecodeError, "multiple of 8 bits") as err:
+ BitString().decode(
+ (
+ tag_encode(3, form=TagFormConstructed) +
+ LENINDEF +
+ b"".join(chunks) +
+ EOC
+ ),
+ offset=offset,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ )
+ self.assertEqual(
+ err.exception.decode_path,
+ decode_path + (str(chunks.index(bs_short)),),
+ )
+ self.assertEqual(
+ err.exception.offset,
+ offset + 1 + 1 + chunks.index(bs_short) * len(bs_full),
+ )
+
+ 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.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)
+
+ @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
+ @given(integers(min_value=1000, max_value=3000))
+ def test_cer(self, data_len):
+ data = urandom(data_len)
+ encoded = encode_cer(BitString(data))
+ ctx = {"bered": True}
+ self.assertSequenceEqual(bytes(BitString().decod(encoded, ctx=ctx)), data)
+ evgens = list(BitString().decode_evgen(encoded, ctx=ctx))
+ evgens_expected = data_len // 999
+ if evgens_expected * 999 != data_len:
+ evgens_expected += 1
+ evgens_expected += 1
+ self.assertEqual(len(evgens), evgens_expected)
+ for (_, obj, _) in evgens[:-2]:
+ self.assertEqual(obj.vlen, 1000)
+ _, obj, _ = evgens[-2]
+ self.assertEqual(obj.vlen, 1 + data_len - len(evgens[:-2]) * 999)
+
@composite
def octet_string_values_strategy(draw, do_expl=False):
obj = OctetString()
self.assertFalse(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
with self.assertRaises(ObjNotReady) as err:
obj.encode()
repr(err.exception)
+ with self.assertRaises(ObjNotReady) as err:
+ encode2pass(obj)
obj = OctetString(value)
self.assertTrue(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
- @given(binary(), binary(), binary(), binary())
+ @given(binary(), binary(), binary(min_size=1), binary(min_size=1))
def test_comparison(self, value1, value2, tag1, tag2):
for klass in (OctetString, OctetStringInherited):
obj1 = klass(value1)
with self.assertRaises(BoundsError) as err:
OctetString(value=value, bounds=(bound_min, bound_max))
repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ OctetString(bounds=(bound_min, bound_max)).decode(
+ OctetString(value).encode()
+ )
+ repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ OctetString(bounds=(bound_min, bound_max)).decode(
+ encode2pass(OctetString(value))
+ )
value = d.draw(binary(min_size=bound_max + 1))
with self.assertRaises(BoundsError) as err:
OctetString(value=value, bounds=(bound_min, bound_max))
repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ OctetString(bounds=(bound_min, bound_max)).decode(
+ OctetString(value).encode()
+ )
+ repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ OctetString(bounds=(bound_min, bound_max)).decode(
+ encode2pass(OctetString(value))
+ )
@given(data_strategy())
def test_call(self, d):
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(),
@given(
integers(min_value=31),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_tag(self, tag, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
OctetString().decode(
tag_encode(tag)[:-1],
@given(
integers(min_value=128),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_len(self, l, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
OctetString().decode(
OctetString.tag_default + len_encode(l)[:-1],
@given(
sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
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(OctetString):
binary(),
integers(min_value=1).map(tag_ctxc),
integers(min_value=0),
+ binary(max_size=5),
+ decode_path_strat,
)
- def test_symmetric(self, values, value, tag_expl, offset):
+ def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
for klass in (OctetString, OctetStringInherited):
_, _, _, _, default, optional, _decoded = values
obj = klass(
_decoded=_decoded,
)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertFalse(obj.expled)
obj_encoded = obj.encode()
+ self.assertEqual(encode2pass(obj), obj_encoded)
+ self.assertSequenceEqual(encode_cer(obj), obj_encoded)
obj_expled = obj(value, expl=tag_expl)
self.assertTrue(obj_expled.expled)
repr(obj_expled)
- pprint(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, offset=offset)
+ obj_expled_cer = encode_cer(obj_expled)
+ self.assertNotEqual(obj_expled_cer, obj_encoded)
+ self.assertSequenceEqual(
+ obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
+ obj_expled_encoded,
+ )
+ 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)
- pprint(obj_decoded)
- self.assertEqual(tail, b"")
+ list(obj_decoded.pps())
+ pprint(obj_decoded, big_blobs=True, with_decode_path=True)
+ self.assertEqual(tail, tail_junk)
self.assertEqual(obj_decoded, obj_expled)
self.assertNotEqual(obj_decoded, obj)
self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
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,
+ )
+
+ evgens = list(obj_expled.decode_evgen(
+ obj_expled_encoded + tail_junk,
+ offset=offset,
+ decode_path=decode_path,
+ ctx=ctx_copied,
+ ))
+ self.assertEqual(len(evgens), 1)
+ _decode_path, obj, tail = evgens[0]
+ self.assertSequenceEqual(tail, tail_junk)
+ self.assertEqual(_decode_path, decode_path)
+ self.assertEqual(obj.expl_offset, offset)
+ repr(obj)
+ list(obj.pps())
+
+ @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
+ @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,
+ ),
+ binary(),
+ decode_path_strat,
+ )
+ def test_constructed(self, impl, chunk_inputs, junk, decode_path):
+ def chunk_constructed(contents):
+ return (
+ tag_encode(form=TagFormConstructed, num=4) +
+ LENINDEF +
+ b"".join(OctetString(content).encode() for content in contents) +
+ EOC
+ )
+ chunks = []
+ chunks_len_expected = []
+ 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
+ chunks_len_expected.append(len(chunk_input))
+ else:
+ chunks.append(chunk_constructed(chunk_input))
+ payload = b"".join(chunk_input)
+ payload_expected += payload
+ for c in chunk_input:
+ chunks_len_expected.append(len(c))
+ chunks_len_expected.append(len(chunks[-1]) - 1 - 1)
+ encoded_indefinite = (
+ tag_encode(form=TagFormConstructed, num=impl) +
+ LENINDEF +
+ 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 + junk,
+ ctx={"bered": True},
+ )
+ self.assertSequenceEqual(tail, junk)
+ self.assertSequenceEqual(bytes(obj), payload_expected)
+ 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)
+ repr(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
+
+ evgens = list(OctetString(impl=tag_encode(impl)).decode_evgen(
+ encoded,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ ))
+ self.assertEqual(len(evgens), len(chunks_len_expected) + 1)
+ for chunk_len_expected, (dp, obj, _) in zip(chunks_len_expected, evgens):
+ self.assertGreater(len(dp), len(decode_path))
+ self.assertEqual(obj.vlen, chunk_len_expected)
+
+ @given(
+ integers(min_value=0),
+ decode_path_strat,
+ )
+ def test_ber_definite_too_short(self, offset, decode_path):
+ with assertRaisesRegex(self, DecodeError, "longer than data") as err:
+ OctetString().decode(
+ tag_encode(4, form=TagFormConstructed) + len_encode(1),
+ offset=offset,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ )
+ self.assertEqual(err.exception.decode_path, decode_path)
+ self.assertEqual(err.exception.offset, offset)
+
+ @given(
+ integers(min_value=0),
+ decode_path_strat,
+ integers(min_value=1, max_value=3),
+ )
+ def test_ber_indefinite_no_eoc(self, offset, decode_path, chunks):
+ bs = OctetString(b"data").encode()
+ with self.assertRaises(NotEnoughData) as err:
+ OctetString().decode(
+ tag_encode(4, form=TagFormConstructed) + LENINDEF + chunks * bs,
+ offset=offset,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ )
+ self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
+ self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
+
+ @given(
+ integers(min_value=0),
+ decode_path_strat,
+ integers(min_value=1, max_value=3),
+ )
+ def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
+ bs = OctetString(b"data").encode()
+ bs_longer = OctetString(b"data-longer").encode()
+ with assertRaisesRegex(self, DecodeError, "chunk out of bounds") as err:
+ OctetString().decode(
+ (
+ tag_encode(4, form=TagFormConstructed) +
+ len_encode((chunks + 1) * len(bs)) +
+ chunks * bs +
+ bs_longer
+ ),
+ offset=offset,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ )
+ self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
+ self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
+
+ @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
+ @given(integers(min_value=1001, max_value=3000))
+ def test_cer(self, data_len):
+ data = urandom(data_len)
+ encoded = encode_cer(OctetString(data))
+ ctx = {"bered": True}
+ self.assertSequenceEqual(bytes(OctetString().decod(encoded, ctx=ctx)), data)
+ evgens = list(OctetString().decode_evgen(encoded, ctx=ctx))
+ evgens_expected = data_len // 1000
+ if evgens_expected * 1000 != data_len:
+ evgens_expected += 1
+ evgens_expected += 1
+ self.assertEqual(len(evgens), evgens_expected)
+ for (_, obj, _) in evgens[:-2]:
+ self.assertEqual(obj.vlen, 1000)
+ _, obj, _ = evgens[-2]
+ self.assertEqual(obj.vlen, data_len - len(evgens[:-2]) * 1000)
@composite
obj = Null()
self.assertTrue(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
- @given(binary(), binary())
+ @given(binary(min_size=1), binary(min_size=1))
def test_comparison(self, tag1, tag2):
for klass in (Null, NullInherited):
obj1 = klass(impl=tag1)
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):
@given(
integers(min_value=31),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_tag(self, tag, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
Null().decode(
tag_encode(tag)[:-1],
@given(
integers(min_value=128),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_len(self, l, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
Null().decode(
Null.tag_default + len_encode(l)[:-1],
null_values_strategy(),
integers(min_value=1).map(tag_ctxc),
integers(min_value=0),
+ binary(max_size=5),
+ decode_path_strat,
)
- def test_symmetric(self, values, tag_expl, offset):
+ def test_symmetric(self, values, tag_expl, offset, tail_junk, decode_path):
for klass in (Null, NullInherited):
_, _, optional, _decoded = values
obj = klass(optional=optional, _decoded=_decoded)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertFalse(obj.expled)
obj_encoded = obj.encode()
+ self.assertEqual(encode2pass(obj), obj_encoded)
+ self.assertSequenceEqual(encode_cer(obj), obj_encoded)
obj_expled = obj(expl=tag_expl)
self.assertTrue(obj_expled.expled)
repr(obj_expled)
- pprint(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, offset=offset)
+ obj_expled_cer = encode_cer(obj_expled)
+ self.assertNotEqual(obj_expled_cer, obj_encoded)
+ self.assertSequenceEqual(
+ obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
+ obj_expled_encoded,
+ )
+ 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)
- pprint(obj_decoded)
- self.assertEqual(tail, b"")
+ list(obj_decoded.pps())
+ pprint(obj_decoded, big_blobs=True, with_decode_path=True)
+ self.assertEqual(tail, tail_junk)
self.assertEqual(obj_decoded, obj_expled)
self.assertNotEqual(obj_decoded, obj)
self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
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,
+ )
+
+ evgens = list(obj_expled.decode_evgen(
+ obj_expled_encoded + tail_junk,
+ offset=offset,
+ decode_path=decode_path,
+ ctx=ctx_copied,
+ ))
+ self.assertEqual(len(evgens), 1)
+ _decode_path, obj, tail = evgens[0]
+ self.assertSequenceEqual(tail, tail_junk)
+ self.assertEqual(_decode_path, decode_path)
+ self.assertEqual(obj, obj_decoded)
+ self.assertEqual(obj.expl_offset, offset)
+ repr(obj)
+ list(obj.pps())
+
@given(integers(min_value=1))
def test_invalid_len(self, l):
obj = ObjectIdentifier()
self.assertFalse(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
with self.assertRaises(ObjNotReady) as err:
obj.encode()
repr(err.exception)
+ with self.assertRaises(ObjNotReady) as err:
+ encode2pass(obj)
obj = ObjectIdentifier(value)
self.assertTrue(obj.ready)
+ self.assertFalse(obj.ber_encoded)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
hash(obj)
- @given(oid_strategy(), oid_strategy(), binary(), binary())
+ @given(oid_strategy(), oid_strategy(), binary(min_size=1), binary(min_size=1))
def test_comparison(self, value1, value2, tag1, tag2):
for klass in (ObjectIdentifier, ObjectIdentifierInherited):
obj1 = klass(value1)
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(
@given(
integers(min_value=31),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_tag(self, tag, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
ObjectIdentifier().decode(
tag_encode(tag)[:-1],
@given(
integers(min_value=128),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_len(self, l, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
ObjectIdentifier().decode(
ObjectIdentifier.tag_default + len_encode(l)[:-1],
self.assertEqual(obj, ObjectIdentifier(".".join(str(arc) for arc in oid)))
str(obj)
repr(obj)
- pprint(obj)
+ pprint(obj, big_blobs=True, with_decode_path=True)
@settings(max_examples=LONG_TEST_MAX_EXAMPLES)
@given(
oid_strategy(),
integers(min_value=1).map(tag_ctxc),
integers(min_value=0),
+ binary(max_size=5),
+ decode_path_strat,
)
- def test_symmetric(self, values, value, tag_expl, offset):
+ def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
for klass in (ObjectIdentifier, ObjectIdentifierInherited):
_, _, _, default, optional, _decoded = values
obj = klass(
_decoded=_decoded,
)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertFalse(obj.expled)
obj_encoded = obj.encode()
+ self.assertEqual(encode2pass(obj), obj_encoded)
+ self.assertSequenceEqual(encode_cer(obj), obj_encoded)
obj_expled = obj(value, expl=tag_expl)
self.assertTrue(obj_expled.expled)
repr(obj_expled)
- pprint(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, offset=offset)
+ obj_expled_cer = encode_cer(obj_expled)
+ self.assertNotEqual(obj_expled_cer, obj_encoded)
+ self.assertSequenceEqual(
+ obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
+ obj_expled_encoded,
+ )
+ 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)
- pprint(obj_decoded)
- self.assertEqual(tail, b"")
+ list(obj_decoded.pps())
+ pprint(obj_decoded, big_blobs=True, with_decode_path=True)
+ self.assertEqual(tail, tail_junk)
self.assertEqual(obj_decoded, obj_expled)
self.assertNotEqual(obj_decoded, obj)
self.assertEqual(tuple(obj_decoded), tuple(obj_expled))
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,
+ )
+
+ evgens = list(obj_expled.decode_evgen(
+ obj_expled_encoded + tail_junk,
+ offset=offset,
+ decode_path=decode_path,
+ ctx=ctx_copied,
+ ))
+ self.assertEqual(len(evgens), 1)
+ _decode_path, obj, tail = evgens[0]
+ self.assertSequenceEqual(tail, tail_junk)
+ self.assertEqual(_decode_path, decode_path)
+ self.assertEqual(obj, obj_decoded)
+ self.assertEqual(obj.expl_offset, offset)
+ repr(obj)
+ list(obj.pps())
@given(
oid_strategy().map(ObjectIdentifier),
data,
)))
+ def test_x690_vector(self):
+ self.assertEqual(
+ ObjectIdentifier().decode(hexdec("0603883703"))[0],
+ 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):
obj = self.base_klass()
self.assertFalse(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
with self.assertRaises(ObjNotReady) as err:
obj.encode()
repr(err.exception)
obj = self.base_klass("whatever")
self.assertTrue(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
- @given(integers(), integers(), binary(), binary())
+ @given(integers(), integers(), binary(min_size=1), binary(min_size=1))
def test_comparison(self, value1, value2, tag1, tag2):
class E(Enumerated):
schema = (
class E(Enumerated):
schema = schema_input
+ register_class(E)
obj = E(
value=value,
impl=impl,
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())
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))
+ decode_path = d.draw(decode_path_strat)
class E(Enumerated):
schema = schema_input
_decoded=_decoded,
)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertFalse(obj.expled)
obj_encoded = obj.encode()
+ self.assertEqual(encode2pass(obj), obj_encoded)
obj_expled = obj(value, expl=tag_expl)
self.assertTrue(obj_expled.expled)
repr(obj_expled)
- pprint(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, offset=offset)
+ 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)
- pprint(obj_decoded)
- self.assertEqual(tail, b"")
+ list(obj_decoded.pps())
+ pprint(obj_decoded, big_blobs=True, with_decode_path=True)
+ self.assertEqual(tail, tail_junk)
self.assertEqual(obj_decoded, obj_expled)
self.assertNotEqual(obj_decoded, obj)
self.assertEqual(int(obj_decoded), int(obj_expled))
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,
+ )
+
+ evgens = list(obj_expled.decode_evgen(
+ obj_expled_encoded + tail_junk,
+ offset=offset,
+ decode_path=decode_path,
+ ctx=ctx_copied,
+ ))
+ self.assertEqual(len(evgens), 1)
+ _decode_path, obj, tail = evgens[0]
+ self.assertSequenceEqual(tail, tail_junk)
+ self.assertEqual(_decode_path, decode_path)
+ self.assertEqual(obj, obj_decoded)
+ self.assertEqual(obj.expl_offset, offset)
+ repr(obj)
+ list(obj.pps())
@composite
obj = self.base_klass()
self.assertFalse(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
text_type(obj)
with self.assertRaises(ObjNotReady) as err:
obj.encode()
repr(err.exception)
+ with self.assertRaises(ObjNotReady) as err:
+ encode2pass(obj)
value = d.draw(text(alphabet=self.text_alphabet()))
obj = self.base_klass(value)
self.assertTrue(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
text_type(obj)
@given(data_strategy())
def test_comparison(self, d):
value1 = d.draw(text(alphabet=self.text_alphabet()))
value2 = d.draw(text(alphabet=self.text_alphabet()))
- tag1 = d.draw(binary())
- tag2 = d.draw(binary())
+ tag1 = d.draw(binary(min_size=1))
+ tag2 = d.draw(binary(min_size=1))
obj1 = self.base_klass(value1)
obj2 = self.base_klass(value2)
self.assertEqual(obj1 == obj2, value1 == value2)
with self.assertRaises(BoundsError) as err:
self.base_klass(value=value, bounds=(bound_min, bound_max))
repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ self.base_klass(bounds=(bound_min, bound_max)).decode(
+ self.base_klass(value).encode()
+ )
+ repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ self.base_klass(bounds=(bound_min, bound_max)).decode(
+ encode2pass(self.base_klass(value))
+ )
value = d.draw(text(alphabet=self.text_alphabet(), min_size=bound_max + 1))
with self.assertRaises(BoundsError) as err:
self.base_klass(value=value, bounds=(bound_min, bound_max))
repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ self.base_klass(bounds=(bound_min, bound_max)).decode(
+ self.base_klass(value).encode()
+ )
+ repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ self.base_klass(bounds=(bound_min, bound_max)).decode(
+ encode2pass(self.base_klass(value))
+ )
@given(data_strategy())
def test_call(self, d):
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):
@given(
integers(min_value=31),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_tag(self, tag, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
self.base_klass().decode(
tag_encode(tag)[:-1],
@given(
integers(min_value=128),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_len(self, l, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
self.base_klass().decode(
self.base_klass.tag_default + len_encode(l)[:-1],
@given(
sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
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):
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))
+ decode_path = d.draw(decode_path_strat)
_, _, _, _, default, optional, _decoded = values
obj = self.base_klass(
value=value,
_decoded=_decoded,
)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertFalse(obj.expled)
obj_encoded = obj.encode()
+ self.assertEqual(encode2pass(obj), obj_encoded)
obj_expled = obj(value, expl=tag_expl)
self.assertTrue(obj_expled.expled)
repr(obj_expled)
- pprint(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, offset=offset)
+ 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)
- pprint(obj_decoded)
- self.assertEqual(tail, b"")
+ list(obj_decoded.pps())
+ pprint(obj_decoded, big_blobs=True, with_decode_path=True)
+ self.assertEqual(tail, tail_junk)
self.assertEqual(obj_decoded, obj_expled)
self.assertNotEqual(obj_decoded, obj)
self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
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,
+ )
+
+ evgens = list(obj_expled.decode_evgen(
+ obj_expled_encoded + tail_junk,
+ offset=offset,
+ decode_path=decode_path,
+ ctx=ctx_copied,
+ ))
+ self.assertEqual(len(evgens), 1)
+ _decode_path, obj, tail = evgens[0]
+ self.assertSequenceEqual(tail, tail_junk)
+ self.assertEqual(_decode_path, decode_path)
+ if not getattr(self, "evgen_mode_skip_value", True):
+ self.assertEqual(obj, obj_decoded)
+ self.assertEqual(obj.expl_offset, offset)
+ repr(obj)
+ list(obj.pps())
+
+
+cyrillic_letters = text(
+ alphabet="".join(six_unichr(i) for i in list(range(0x0410, 0x044f + 1))),
+ min_size=1,
+ max_size=5,
+)
class TestUTF8String(StringMixin, CommonMixin, TestCase):
base_klass = UTF8String
+ @given(cyrillic_letters)
+ def test_byte_per_primitive(self, chars):
+ char = chars[0]
+ char_raw = char.encode("utf-8")
+ encoded = b"".join((
+ self.base_klass().tag_constructed,
+ LENINDEF,
+ OctetString(char_raw[:1]).encode(),
+ OctetString(char_raw[1:2]).encode(),
+ EOC,
+ ))
+ self.assertEqual(
+ self.base_klass().decod(encoded, ctx={"bered": True}),
+ char,
+ )
+
+
+class UnicodeDecodeErrorMixin(object):
+ @given(cyrillic_letters)
+ 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
+ def text_alphabet(self):
+ return digits + " "
+
+ @given(text(alphabet=ascii_letters, min_size=1, max_size=5))
+ def test_non_numeric(self, non_numeric_text):
+ with assertRaisesRegex(self, DecodeError, "non-numeric"):
+ self.base_klass(non_numeric_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_byte_per_primitive(self):
+ encoded = b"".join((
+ self.base_klass().tag_constructed,
+ LENINDEF,
+ OctetString(b"1").encode(),
+ OctetString(b"2").encode(),
+ EOC,
+ ))
+ self.assertEqual(
+ self.base_klass().decod(encoded, ctx={"bered": True}),
+ "12",
+ )
+
-class TestPrintableString(StringMixin, CommonMixin, TestCase):
+class TestPrintableString(
+ UnicodeDecodeErrorMixin,
+ StringMixin,
+ CommonMixin,
+ TestCase,
+):
base_klass = PrintableString
+ def text_alphabet(self):
+ return ascii_letters + digits + " '()+,-./:=?"
-class TestTeletexString(StringMixin, CommonMixin, TestCase):
+ @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,
+ StringMixin,
+ CommonMixin,
+ TestCase,
+):
base_klass = TeletexString
-class TestVideotexString(StringMixin, CommonMixin, TestCase):
+class TestVideotexString(
+ UnicodeDecodeErrorMixin,
+ StringMixin,
+ CommonMixin,
+ TestCase,
+):
base_klass = VideotexString
-class TestIA5String(StringMixin, CommonMixin, TestCase):
+class TestIA5String(
+ UnicodeDecodeErrorMixin,
+ StringMixin,
+ CommonMixin,
+ TestCase,
+):
base_klass = IA5String
-class TestGraphicString(StringMixin, CommonMixin, TestCase):
+class TestGraphicString(
+ UnicodeDecodeErrorMixin,
+ StringMixin,
+ CommonMixin,
+ TestCase,
+):
base_klass = GraphicString
-class TestVisibleString(StringMixin, CommonMixin, TestCase):
+class TestVisibleString(
+ UnicodeDecodeErrorMixin,
+ StringMixin,
+ CommonMixin,
+ TestCase,
+):
base_klass = VisibleString
-
-class TestGeneralString(StringMixin, CommonMixin, TestCase):
+ def test_x690_vector(self):
+ obj, tail = VisibleString().decode(hexdec("1A054A6F6E6573"))
+ self.assertSequenceEqual(tail, b"")
+ self.assertEqual(str(obj), "Jones")
+ self.assertFalse(obj.ber_encoded)
+ self.assertFalse(obj.lenindef)
+ self.assertFalse(obj.bered)
+
+ obj, tail = VisibleString().decode(
+ hexdec("3A0904034A6F6E04026573"),
+ ctx={"bered": True},
+ )
+ self.assertSequenceEqual(tail, b"")
+ self.assertEqual(str(obj), "Jones")
+ 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"),
+ ctx={"bered": True},
+ )
+ self.assertSequenceEqual(tail, b"")
+ self.assertEqual(str(obj), "Jones")
+ 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(
+ UnicodeDecodeErrorMixin,
+ StringMixin,
+ CommonMixin,
+ TestCase,
+):
base_klass = GeneralString
obj = self.base_klass()
self.assertFalse(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
with self.assertRaises(ObjNotReady) as err:
obj.encode()
repr(err.exception)
- value = d.draw(datetimes(min_value=self.min_datetime))
+ with self.assertRaises(ObjNotReady) as err:
+ encode2pass(obj)
+ 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)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
@given(data_strategy())
def test_comparison(self, d):
min_value=self.min_datetime,
max_value=self.max_datetime,
))
- tag1 = d.draw(binary())
- tag2 = d.draw(binary())
+ tag1 = d.draw(binary(min_size=1))
+ tag2 = d.draw(binary(min_size=1))
if self.omit_ms:
value1 = value1.replace(microsecond=0)
value2 = value2.replace(microsecond=0)
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):
))
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,
_decoded=_decoded,
)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertFalse(obj.expled)
obj_encoded = obj.encode()
+ self.assertEqual(encode2pass(obj), obj_encoded)
+ self.additional_symmetric_check(value, obj_encoded)
obj_expled = obj(value, expl=tag_expl)
self.assertTrue(obj_expled.expled)
repr(obj_expled)
- pprint(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, offset=offset)
+ 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)
- pprint(obj_decoded)
- self.assertEqual(tail, b"")
+ list(obj_decoded.pps())
+ pprint(obj_decoded, big_blobs=True, with_decode_path=True)
+ 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())
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):
omit_ms = False
min_datetime = datetime(1900, 1, 1)
max_datetime = datetime(9999, 12, 31)
+ evgen_mode_skip_value = False
+
+ def additional_symmetric_check(self, value, obj_encoded):
+ if value.microsecond > 0:
+ self.assertFalse(obj_encoded.endswith(b"0Z"))
+
+ def test_repr_not_ready(self):
+ unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
+ repr(GeneralizedTime())
+
+ 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 ((
datetime(2010, 1, 2, 3, 4, 5, 0),
)
+ def test_go_vectors_valid_ber(self):
+ for data in ((
+ b"20100102030405+0607",
+ b"20100102030405-0607",
+ )):
+ GeneralizedTime(data, ctx={"bered": True})
+
+ def test_utc_offsets(self):
+ """Some know equal UTC offsets
+ """
+ dts = [
+ GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
+ for data in (
+ "200101011830Z",
+ "200101012230+04",
+ "200101011130-0700",
+ "200101011500-03:30",
+ )
+ ]
+ self.assertEqual(dts[0], dts[1])
+ self.assertEqual(dts[0], dts[2])
+ self.assertEqual(dts[0], dts[3])
+
+ @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
+ @given(data_strategy())
+ def test_valid_ber(self, d):
+ min_year = 1901 if PY2 else 2
+ year = d.draw(integers(min_value=min_year, max_value=9999))
+ month = d.draw(integers(min_value=1, max_value=12))
+ day = d.draw(integers(min_value=1, max_value=28))
+ hours = d.draw(integers(min_value=0, max_value=23))
+ data = "%04d%02d%02d%02d" % (year, month, day, hours)
+ dt = datetime(year, month, day, hours)
+ fractions_sign = d.draw(sampled_from(" ,."))
+ fractions = None
+ if fractions_sign != " ":
+ fractions = random()
+ if d.draw(booleans()):
+ minutes = d.draw(integers(min_value=0, max_value=59))
+ data += "%02d" % minutes
+ dt += timedelta(seconds=60 * minutes)
+ if d.draw(booleans()):
+ seconds = d.draw(integers(min_value=0, max_value=59))
+ data += "%02d" % seconds
+ dt += timedelta(seconds=seconds)
+ if fractions is not None:
+ dt += timedelta(microseconds=10**6 * fractions)
+ elif fractions is not None:
+ dt += timedelta(seconds=60 * fractions)
+ elif fractions is not None:
+ dt += timedelta(seconds=3600 * fractions)
+ if fractions is not None:
+ data += fractions_sign + str(fractions)[2:]
+ if d.draw(booleans()):
+ data += "Z"
+ elif d.draw(booleans()):
+ offset_hour = d.draw(integers(min_value=0, max_value=13))
+ sign = 1
+ if d.draw(booleans()):
+ data += "-"
+ sign = -1
+ else:
+ data += "+"
+ dt -= timedelta(seconds=sign * 3600 * offset_hour)
+ data += "%02d" % offset_hour
+ minutes_separator = d.draw(sampled_from((None, "", ":")))
+ if minutes_separator is not None:
+ offset_minute = d.draw(integers(min_value=0, max_value=59))
+ dt -= timedelta(seconds=sign * 60 * offset_minute)
+ data += "%s%02d" % (minutes_separator, offset_minute)
+ data = data.encode("ascii")
+ data_der = GeneralizedTime.tag_default + len_encode(len(data)) + data
+ try:
+ GeneralizedTime().decod(data_der)
+ except DecodeError:
+ dered = False
+ else:
+ dered = True
+ obj = GeneralizedTime().decod(data_der, ctx={"bered": True})
+ if dt.year > 1970:
+ self.assertEqual(
+ mktime(obj.todatetime().timetuple()),
+ mktime(dt.timetuple()),
+ )
+ elif not PY2:
+ self.assertEqual(obj.todatetime().timestamp(), dt.timestamp())
+ self.assertEqual(obj.ber_encoded, not dered)
+ self.assertEqual(obj.bered, not dered)
+ self.assertEqual(obj.ber_raw, None if dered else data)
+ self.assertEqual(obj.encode() == data_der, dered)
+ repr(obj)
+ bytes(obj)
+ str(obj)
+
+ def test_invalid_ber(self):
+ for data in ((
+ # "00010203040506.07",
+ "-0010203040506.07",
+ "0001-203040506.07",
+ "000102-3040506.07",
+ "00010203-40506.07",
+ "0001020304-506.07",
+ "000102030405-6.07",
+ "00010203040506.-7",
+ "+0010203040506.07",
+ "0001+203040506.07",
+ "000102+3040506.07",
+ "00010203+40506.07",
+ "0001020304+506.07",
+ "000102030405+6.07",
+ "00010203040506.+7",
+ " 0010203040506.07",
+ "0001 203040506.07",
+ "000102 3040506.07",
+ "00010203 40506.07",
+ "0001020304 506.07",
+ "000102030405 6.07",
+ "00010203040506. 7",
+ "001 0203040506.07",
+ "00012 03040506.07",
+ "0001023 040506.07",
+ "000102034 0506.07",
+ "00010203045 06.07",
+ "0001020304056 .07",
+ "00010203040506.7 ",
+ "00010203040506.",
+ "0001020304050607",
+
+ "-0010203040506",
+ "0001-203040506",
+ "000102-3040506",
+ "00010203-40506",
+ "0001020304-506",
+ "000102030405-6",
+ "0001+203040506",
+ "000102+3040506",
+ "00010203+40506",
+ "0001020304+506",
+ "000102030405+6",
+ " 0010203040506",
+ "0001 203040506",
+ "000102 3040506",
+ "00010203 40506",
+ "0001020304 506",
+ "000102030405 6",
+ "001 0203040506",
+ "00012 03040506",
+ "0001023 040506",
+ "000102034 0506",
+ "00010203045 06",
+ "0001020304056 ",
+
+ "-00102030405.07",
+ "0001-2030405.07",
+ "000102-30405.07",
+ "00010203-405.07",
+ "0001020304-5.07",
+ "000102030405.-7",
+ "+00102030405.07",
+ "0001+2030405.07",
+ "00010203+405.07",
+ "0001020304+5.07",
+ "000102030405.+7",
+ " 00102030405.07",
+ "0001 2030405.07",
+ "000102 30405.07",
+ "00010203 405.07",
+ "0001020304 5.07",
+ "000102030405. 7",
+ "001 02030405.07",
+ "00012 030405.07",
+ "0001023 0405.07",
+ "000102034 05.07",
+ "00010203045 .07",
+ "000102030405.7 ",
+ "000102030405.",
+
+ "-001020304.07",
+ "0001-20304.07",
+ "000102-304.07",
+ "00010203-4.07",
+ "0001020304.-7",
+ "+001020304.07",
+ "0001+20304.07",
+ "00010203+4.07",
+ "0001020304.+7",
+ " 001020304.07",
+ "0001 20304.07",
+ "000102 304.07",
+ "00010203 4.07",
+ "0001020304. 7",
+ "001 020304.07",
+ "00012 0304.07",
+ "0001023 04.07",
+ "000102034 .07",
+ "0001020304.7 ",
+ "0001020304.",
+
+ "00010203",
+ "00010203040506Y",
+ "0001010100+0001",
+ "0001010100+00:01",
+ "0001010100+01",
+
+ "00010203040506.07+15",
+ "00010203040506.07-15",
+ "00010203040506.07+14:60",
+ "00010203040506.07+1460",
+ "00010203040506.07-1460",
+ "00010203040506.07+00:60",
+ "00010203040506.07-00:60",
+
+ "00010203040506+15",
+ "00010203040506-15",
+ "00010203040506+14:60",
+ "00010203040506+1460",
+ "00010203040506-1460",
+ "00010203040506+00:60",
+ "00010203040506-00:60",
+
+ "0001020304050.07",
+ "00010203040.07",
+ "000102030.07",
+ "0001020304050",
+ "00010203040",
+ "000102030",
+ )):
+ with self.assertRaises(DecodeError):
+ GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
+ data = data.replace(".", ",")
+ with self.assertRaises(DecodeError):
+ GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
+
+ @given(
+ binary(
+ min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
+ max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
+ ),
+ binary(min_size=1, max_size=1),
+ binary(
+ min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
+ max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
+ ),
+ )
+ def test_junk(self, part0, part1, part2):
+ junk = part0 + part1 + part2
+ assume(not (set(junk) <= set(digits.encode("ascii"))))
+ with self.assertRaises(DecodeError):
+ GeneralizedTime().decode(
+ GeneralizedTime.tag_default +
+ len_encode(len(junk)) +
+ junk
+ )
+
+ @given(
+ binary(
+ min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
+ max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
+ ),
+ binary(min_size=1, max_size=1),
+ binary(
+ min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
+ max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
+ ),
+ )
+ def test_junk_dm(self, part0, part1, part2):
+ junk = part0 + part1 + part2
+ assume(not (set(junk) <= set(digits.encode("ascii"))))
+ with self.assertRaises(DecodeError):
+ GeneralizedTime().decode(
+ GeneralizedTime.tag_default +
+ len_encode(len(junk)) +
+ junk
+ )
+
+ def test_ns_fractions(self):
+ GeneralizedTime(b"20010101000000.000001Z")
+ with assertRaisesRegex(self, DecodeError, "only microsecond fractions"):
+ GeneralizedTime(b"20010101000000.0000001Z")
+
+ def test_non_pure_integers(self):
+ for data in ((
+ # b"20000102030405Z,
+ b"+2000102030405Z",
+ b"2000+102030405Z",
+ b"200001+2030405Z",
+ b"20000102+30405Z",
+ b"2000010203+405Z",
+ b"200001020304+5Z",
+ b"20000102030405.+6Z",
+ b"20000102030405.-6Z",
+ b"_2000102030405Z",
+ b"2000_102030405Z",
+ b"200001_2030405Z",
+ b"20000102_30405Z",
+ b"2000010203_405Z",
+ b"200001020304_5Z",
+ b"20000102030405._6Z",
+ b"20000102030405.6_Z",
+ b" 2000102030405Z",
+ b"2000 102030405Z",
+ b"200001 2030405Z",
+ b"20000102 30405Z",
+ b"2000010203 405Z",
+ b"200001020304 5Z",
+ b"20000102030405. 6Z",
+ b"200 0102030405Z",
+ b"20001 02030405Z",
+ b"2000012 030405Z",
+ b"200001023 0405Z",
+ b"20000102034 05Z",
+ b"2000010203045 Z",
+ b"20000102030405.6 Z",
+ )):
+ with self.assertRaises(DecodeError):
+ GeneralizedTime(data)
+
class TestUTCTime(TimeMixin, CommonMixin, TestCase):
base_klass = UTCTime
omit_ms = True
min_datetime = datetime(2000, 1, 1)
max_datetime = datetime(2049, 12, 31)
+ evgen_mode_skip_value = False
+
+ def additional_symmetric_check(self, value, obj_encoded):
+ pass
+
+ def test_repr_not_ready(self):
+ unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
+ repr(UTCTime())
+
+ 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 ((
datetime(1991, 5, 6, 23, 45, 40, 0),
)
+ def test_non_pure_integers(self):
+ for data in ((
+ # b"000102030405Z",
+ b"+10102030405Z",
+ b"00+102030405Z",
+ b"0001+2030405Z",
+ b"000102+30405Z",
+ b"00010203+405Z",
+ b"0001020304+5Z",
+ b"_10102030405Z",
+ b"00_102030405Z",
+ b"0001_2030405Z",
+ b"000102_30405Z",
+ b"00010203_405Z",
+ b"0001020304_5Z",
+ b"00010203045_Z",
+ b" 10102030405Z",
+ b"00 102030405Z",
+ b"0001 2030405Z",
+ b"000102 30405Z",
+ b"00010203 405Z",
+ b"0001020304 5Z",
+ b"1 0102030405Z",
+ b"001 02030405Z",
+ b"00012 030405Z",
+ b"0001023 0405Z",
+ b"000102034 05Z",
+ b"00010203045 Z",
+ )):
+ with self.assertRaises(DecodeError):
+ UTCTime(data)
+
+ def test_x680_vector_valid_ber(self):
+ for data, dt in ((
+ (b"8201021200Z", datetime(1982, 1, 2, 12)),
+ (b"8201020700-0500", datetime(1982, 1, 2, 12)),
+ (b"0101021200Z", datetime(2001, 1, 2, 12)),
+ (b"0101020700-0500", datetime(2001, 1, 2, 12)),
+ )):
+ data_der = UTCTime.tag_default + len_encode(len(data)) + data
+ obj = UTCTime().decod(data_der, ctx={"bered": True})
+ self.assertEqual(obj, dt)
+ self.assertEqual(obj.todatetime(), dt)
+ self.assertTrue(obj.ber_encoded)
+ self.assertTrue(obj.bered)
+ self.assertEqual(obj.ber_raw, data)
+ self.assertNotEqual(obj.encode(), data_der)
+ repr(obj)
+
+ def test_go_vectors_valid_ber(self):
+ for data in ((
+ b"910506164540-0700",
+ b"910506164540+0730",
+ b"9105062345Z",
+ b"5105062345Z",
+ )):
+ data = UTCTime.tag_default + len_encode(len(data)) + data
+ obj = UTCTime().decod(data, ctx={"bered": True})
+ self.assertTrue(obj.ber_encoded)
+ self.assertTrue(obj.bered)
+ self.assertNotEqual(obj.encode(), data)
+ repr(obj)
+
+ @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
+ @given(data_strategy())
+ def test_valid_ber(self, d):
+ year = d.draw(integers(min_value=0, max_value=99))
+ month = d.draw(integers(min_value=1, max_value=12))
+ day = d.draw(integers(min_value=1, max_value=28))
+ hours = d.draw(integers(min_value=0, max_value=23))
+ minute = d.draw(integers(min_value=0, max_value=59))
+ data = "%02d%02d%02d%02d%02d" % (year, month, day, hours, minute)
+ dt = datetime(
+ year + (2000 if year < 50 else 1900),
+ month,
+ day,
+ hours,
+ minute,
+ )
+ dered = False
+ if d.draw(booleans()):
+ dered = True
+ seconds = d.draw(integers(min_value=0, max_value=59))
+ data += "%02d" % seconds
+ dt += timedelta(seconds=seconds)
+ if d.draw(booleans()):
+ data += "Z"
+ else:
+ dered = False
+ offset_hour = d.draw(integers(min_value=0, max_value=13))
+ offset_minute = d.draw(integers(min_value=0, max_value=59))
+ offset = timedelta(seconds=offset_hour * 3600 + offset_minute * 60)
+ if d.draw(booleans()):
+ dt += offset
+ data += "-"
+ else:
+ dt -= offset
+ data += "+"
+ data += "%02d%02d" % (offset_hour, offset_minute)
+ data = data.encode("ascii")
+ data_der = UTCTime.tag_default + len_encode(len(data)) + data
+ obj = UTCTime().decod(data_der, ctx={"bered": True})
+ self.assertEqual(obj, dt)
+ self.assertEqual(obj.todatetime(), dt)
+ self.assertEqual(obj.ber_encoded, not dered)
+ self.assertEqual(obj.bered, not dered)
+ self.assertEqual(obj.ber_raw, None if dered else data)
+ self.assertEqual(obj.encode() == data_der, dered)
+ repr(obj)
+ bytes(obj)
+ str(obj)
+
+ def test_invalid_ber(self):
+ for data in ((
+ # b"0001020304Z",
+ b"-101020304Z",
+ b"00-1020304Z",
+ b"0001-20304Z",
+ b"000102-304Z",
+ b"000102-104Z",
+ b"00000203-4Z",
+ b"+101020304Z",
+ b"00+1020304Z",
+ b"0001+20304Z",
+ b"000102+304Z",
+ b"000102+104Z",
+ b"00000203+4Z",
+ b" 101020304Z",
+ b"00 1020304Z",
+ b"0001 20304Z",
+ b"000102 304Z",
+ b"000102 104Z",
+ b"00000203 4Z",
+ b"1 01020304Z",
+ b"001 020304Z",
+ b"00012 0304Z",
+ b"0001023 04Z",
+ b"0001021 04Z",
+ b"000002034 Z",
+ b"0013020304Z",
+ b"0001000304Z",
+ b"0001320304Z",
+ b"0001022404Z",
+ b"0001020360Z",
+ b"0002300304Z",
+ b"0001020304",
+ b"0001020304T",
+ b"0001020304+",
+ b"0001020304-",
+ b"0001020304+0",
+ b"0001020304+00",
+ b"0001020304+000",
+ b"0001020304+000Z",
+ b"0001020304+0000Z",
+ b"0001020304+-101",
+ b"0001020304+01-1",
+ b"0001020304+0060",
+ b"0001020304+1401",
+ b"5001010000+0001",
+ b"000102030Z",
+ b"0001020Z",
+ )):
+ with self.assertRaises(DecodeError):
+ UTCTime(data, ctx={"bered": True})
+ data = data[:8] + data[8+2:]
+ with self.assertRaises(DecodeError):
+ UTCTime(data, ctx={"bered": True})
+
+ for data in ((
+ # b"000102030405Z",
+ b"-10102030405Z",
+ b"00-102030405Z",
+ b"0001-2030405Z",
+ b"000102-30405Z",
+ b"000102-10405Z",
+ b"00000203-405Z",
+ b"0000020304-5Z",
+ b"+10102030405Z",
+ b"00+102030405Z",
+ b"0001+2030405Z",
+ b"000102+30405Z",
+ b"000102+10405Z",
+ b"00000203+405Z",
+ b"0000020304+5Z",
+ b" 10102030405Z",
+ b"00 102030405Z",
+ b"0001 2030405Z",
+ b"000102 30405Z",
+ b"000102 10405Z",
+ b"00000203 405Z",
+ b"0000020304 5Z",
+ b"1 0102030405Z",
+ b"001 02030405Z",
+ b"00012 030405Z",
+ b"0001023 0405Z",
+ b"0001021 0405Z",
+ b"000002034 05Z",
+ b"00000203045 Z",
+ b"001302030405Z",
+ b"000100030405Z",
+ b"000132030405Z",
+ b"000102240405Z",
+ b"000102036005Z",
+ b"000230030405Z",
+ b"000102030460Z",
+ b"000102030405",
+ b"000102030405T",
+ b"000102030405+",
+ b"000102030405-",
+ b"000102030405+0",
+ b"000102030405+00",
+ b"000102030405+000",
+ b"000102030405+000Z",
+ b"000102030405+0000Z",
+ b"000102030405+-101",
+ b"000102030405+01-1",
+ b"000102030405+0060",
+ b"000102030405+1401",
+ b"500101000002+0003",
+ )):
+ with self.assertRaises(DecodeError):
+ UTCTime(data, ctx={"bered": True})
+
@given(integers(min_value=0, max_value=49))
def test_pre50(self, year):
self.assertEqual(
1900 + year,
)
+ @given(
+ binary(
+ min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
+ max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
+ ),
+ binary(min_size=1, max_size=1),
+ binary(
+ min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
+ max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
+ ),
+ )
+ def test_junk(self, part0, part1, part2):
+ junk = part0 + part1 + part2
+ assume(not (set(junk) <= set(digits.encode("ascii"))))
+ with self.assertRaises(DecodeError):
+ UTCTime().decode(
+ UTCTime.tag_default +
+ len_encode(len(junk)) +
+ junk
+ )
+
+
+@composite
+def tlv_value_strategy(draw):
+ tag_num = draw(integers(min_value=1))
+ data = draw(binary())
+ return b"".join((tag_encode(tag_num), len_encode(len(data)), data))
+
@composite
def any_values_strategy(draw, do_expl=False):
- value = draw(one_of(none(), binary()))
+ value = draw(one_of(none(), tlv_value_strategy()))
expl = None
if do_expl:
expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
obj = Any(optional=optional)
self.assertEqual(obj.optional, optional)
- @given(binary())
+ @given(tlv_value_strategy())
def test_ready(self, value):
obj = Any()
self.assertFalse(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
with self.assertRaises(ObjNotReady) as err:
obj.encode()
repr(err.exception)
+ with self.assertRaises(ObjNotReady) as err:
+ encode2pass(obj)
obj = Any(value)
self.assertTrue(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
@given(integers())
def test_basic(self, value):
len(integer_encoded),
)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertSequenceEqual(obj.encode(), integer_encoded)
- @given(binary(), binary())
+ @given(tlv_value_strategy(), tlv_value_strategy())
def test_comparison(self, value1, value2):
for klass in (Any, AnyInherited):
obj1 = klass(value1)
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):
obj.decode(obj.encode()[:-1])
@given(
- binary(),
+ tlv_value_strategy(),
integers(min_value=1).map(tag_ctxc),
)
def test_stripped_expl(self, value, tag_expl):
@given(
integers(min_value=31),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_tag(self, tag, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
Any().decode(
tag_encode(tag)[:-1],
@given(
integers(min_value=128),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_len(self, l, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
Any().decode(
Any.tag_default + len_encode(l)[:-1],
integers().map(lambda x: Integer(x).encode()),
integers(min_value=1).map(tag_ctxc),
integers(min_value=0),
+ binary(max_size=5),
+ decode_path_strat,
)
- def test_symmetric(self, values, value, tag_expl, offset):
+ def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
for klass in (Any, AnyInherited):
_, _, optional, _decoded = values
obj = klass(value=value, optional=optional, _decoded=_decoded)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertFalse(obj.expled)
+ tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
+ self.assertEqual(obj.tag_order, (tag_class, tag_num))
obj_encoded = obj.encode()
+ self.assertEqual(encode2pass(obj), obj_encoded)
obj_expled = obj(value, expl=tag_expl)
self.assertTrue(obj_expled.expled)
+ tag_class, _, tag_num = tag_decode(tag_expl)
+ self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
repr(obj_expled)
- pprint(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, offset=offset)
+ 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)
- pprint(obj_decoded)
- self.assertEqual(tail, b"")
+ list(obj_decoded.pps())
+ pprint(obj_decoded, big_blobs=True, with_decode_path=True)
+ 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.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,
+ )
+
+ evgens = list(obj_expled.decode_evgen(
+ obj_expled_encoded + tail_junk,
+ offset=offset,
+ decode_path=decode_path,
+ ctx=ctx_copied,
+ ))
+ self.assertEqual(len(evgens), 1)
+ _decode_path, obj, tail = evgens[0]
+ self.assertSequenceEqual(tail, tail_junk)
+ self.assertEqual(_decode_path, decode_path)
+ self.assertEqual(obj.expl_offset, offset)
+ repr(obj)
+ list(obj.pps())
+
+ @given(
+ integers(min_value=1).map(tag_ctxc),
+ integers(min_value=0, max_value=3),
+ integers(min_value=0),
+ decode_path_strat,
+ binary(),
+ )
+ def test_indefinite(self, expl, chunks, offset, decode_path, junk):
+ chunk = Boolean(False, expl=expl).encode()
+ encoded = (
+ OctetString.tag_default +
+ LENINDEF +
+ b"".join([chunk] * chunks) +
+ EOC
+ )
+ with self.assertRaises(LenIndefForm):
+ Any().decode(
+ encoded + junk,
+ offset=offset,
+ decode_path=decode_path,
+ )
+ obj, tail = Any().decode(
+ encoded + junk,
+ offset=offset,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ )
+ self.assertSequenceEqual(tail, junk)
+ self.assertEqual(obj.offset, offset)
+ self.assertEqual(obj.tlvlen, len(encoded))
+ 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)
+ with self.assertRaises(NotEnoughData) as err:
+ Any().decode(
+ encoded[:-1],
+ offset=offset,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ )
+ self.assertEqual(err.exception.offset, offset + 1 + 1 + len(chunk) * chunks)
+ self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
+
+ class SeqOf(SequenceOf):
+ schema = Boolean(expl=expl)
+
+ class Seq(Sequence):
+ schema = (
+ ("type", ObjectIdentifier(defines=((("value",), {
+ ObjectIdentifier("1.2.3"): SeqOf(impl=OctetString.tag_default),
+ }),))),
+ ("value", Any()),
+ )
+ seq = Seq((
+ ("type", ObjectIdentifier("1.2.3")),
+ ("value", Any(encoded)),
+ ))
+ seq_encoded = seq.encode()
+ seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
+ self.assertIsNotNone(seq_decoded["value"].defined)
+ repr(seq_decoded)
+ list(seq_decoded.pps())
+ pprint(seq_decoded, big_blobs=True, with_decode_path=True)
+ self.assertTrue(seq_decoded.bered)
+ self.assertFalse(seq_decoded["type"].bered)
+ self.assertTrue(seq_decoded["value"].bered)
+
+ chunk = chunk[:-1] + b"\x01"
+ chunks = b"".join([chunk] * (chunks + 1))
+ encoded = OctetString.tag_default + len_encode(len(chunks)) + chunks
+ seq = Seq((
+ ("type", ObjectIdentifier("1.2.3")),
+ ("value", Any(encoded)),
+ ))
+ seq_encoded = seq.encode()
+ seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
+ self.assertIsNotNone(seq_decoded["value"].defined)
+ repr(seq_decoded)
+ list(seq_decoded.pps())
+ pprint(seq_decoded, big_blobs=True, with_decode_path=True)
+ self.assertTrue(seq_decoded.bered)
+ self.assertFalse(seq_decoded["type"].bered)
+ self.assertTrue(seq_decoded["value"].bered)
@composite
def choice_values_strategy(draw, value_required=False, schema=None, do_expl=False):
if schema is None:
names = list(draw(sets(text_letters(), min_size=1, max_size=5)))
- tags = [tag_encode(tag) for tag in draw(sets(
- integers(min_value=0),
+ tags = [{tag_type: tag_value} for tag_type, tag_value in draw(sets(
+ one_of(
+ tuples(just("impl"), integers(min_value=0).map(tag_encode)),
+ tuples(just("expl"), integers(min_value=0).map(tag_ctxp)),
+ ),
min_size=len(names),
max_size=len(names),
))]
- schema = [(name, Integer(impl=tag)) for name, tag in zip(names, tags)]
+ schema = [
+ (name, Integer(**tag_kwargs))
+ for name, tag_kwargs in zip(names, tags)
+ ]
value = None
if value_required or draw(booleans()):
value = draw(tuples(
obj = self.base_klass()
self.assertFalse(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertIsNone(obj["whatever"])
with self.assertRaises(ObjNotReady) as err:
obj.encode()
repr(err.exception)
+ with self.assertRaises(ObjNotReady) as err:
+ encode2pass(obj)
obj["whatever"] = Boolean()
self.assertFalse(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
obj["whatever"] = Boolean(value)
self.assertTrue(obj.ready)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
@given(booleans(), booleans())
def test_comparison(self, value1, value2):
class Wahl(self.base_klass):
schema = _schema
+ register_class(Wahl)
obj = Wahl(
value=value,
expl=expl,
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):
)
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))
+ decode_path = d.draw(decode_path_strat)
class Wahl(self.base_klass):
schema = _schema
_decoded=_decoded,
)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertFalse(obj.expled)
+ self.assertEqual(obj.tag_order, obj.value.tag_order)
obj_encoded = obj.encode()
+ self.assertEqual(encode2pass(obj), obj_encoded)
obj_expled = obj(value, expl=tag_expl)
self.assertTrue(obj_expled.expled)
+ tag_class, _, tag_num = tag_decode(tag_expl)
+ self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
repr(obj_expled)
- pprint(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, offset=offset)
+ 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)
- pprint(obj_decoded)
- self.assertEqual(tail, b"")
+ list(obj_decoded.pps())
+ pprint(obj_decoded, big_blobs=True, with_decode_path=True)
+ 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.expl_offset, offset)
self.assertSequenceEqual(
obj_expled_encoded[
- obj_decoded.value.offset - offset:
- obj_decoded.value.offset + obj_decoded.value.tlvlen - offset
+ obj_decoded.value.fulloffset - offset:
+ obj_decoded.value.fulloffset + obj_decoded.value.fulllen - offset
],
obj_encoded,
)
+ assert_exceeding_data(
+ self,
+ lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+ tail_junk,
+ )
+
+ evgens = list(obj_expled.decode_evgen(
+ obj_expled_encoded + tail_junk,
+ offset=offset,
+ decode_path=decode_path,
+ ctx=ctx_copied,
+ ))
+ self.assertEqual(len(evgens), 2)
+ _decode_path, obj, tail = evgens[0]
+ self.assertEqual(_decode_path, decode_path + (obj_decoded.choice,))
+ _decode_path, obj, tail = evgens[1]
+ self.assertSequenceEqual(tail, tail_junk)
+ self.assertEqual(_decode_path, decode_path)
+ self.assertEqual(obj.expl_offset, offset)
+ repr(obj)
+ list(obj.pps())
@given(integers())
def test_set_get(self, value):
with self.assertRaises(TagMismatch):
obj.decode(int_encoded)
+ def test_tag_mismatch_underlying(self):
+ class SeqOfBoolean(SequenceOf):
+ schema = Boolean()
+
+ class SeqOfInteger(SequenceOf):
+ schema = Integer()
+
+ class Wahl(Choice):
+ schema = (
+ ("erste", SeqOfBoolean()),
+ )
+
+ int_encoded = SeqOfInteger((Integer(123),)).encode()
+ bool_encoded = SeqOfBoolean((Boolean(False),)).encode()
+ obj = Wahl()
+ obj.decode(bool_encoded)
+ with self.assertRaises(TagMismatch) as err:
+ obj.decode(int_encoded)
+ self.assertEqual(err.exception.decode_path, ("erste", "0"))
+
@composite
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(
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)),
class SeqMixing(object):
def test_invalid_value_type(self):
with self.assertRaises(InvalidValueType) as err:
- self.base_klass((1, 2, 3))
+ self.base_klass(123)
repr(err.exception)
def test_invalid_value_type_set(self):
seq[name] = Boolean()
self.assertFalse(seq.ready)
repr(seq)
- pprint(seq)
+ list(seq.pps())
+ pprint(seq, big_blobs=True, with_decode_path=True)
for name, value in ready.items():
seq[name] = Boolean(value)
self.assertFalse(seq.ready)
repr(seq)
- pprint(seq)
+ list(seq.pps())
+ pprint(seq, big_blobs=True, with_decode_path=True)
with self.assertRaises(ObjNotReady) as err:
seq.encode()
repr(err.exception)
+ with self.assertRaises(ObjNotReady) as err:
+ encode2pass(seq)
for name, value in non_ready.items():
seq[name] = Boolean(value)
self.assertTrue(seq.ready)
repr(seq)
- pprint(seq)
+ list(seq.pps())
+ pprint(seq, big_blobs=True, with_decode_path=True)
@given(data_strategy())
def test_call(self, d):
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):
with self.assertRaises(NotEnoughData):
seq.decode(seq.encode()[:-1])
- @given(binary(min_size=2))
- def test_non_tag_mismatch_raised(self, junk):
+ @given(integers(min_value=3), binary(min_size=2))
+ def test_non_tag_mismatch_raised(self, junk_tag_num, junk):
+ junk = tag_encode(junk_tag_num) + junk
try:
_, _, len_encoded = tag_strip(memoryview(junk))
len_decode(len_encoded)
@given(
integers(min_value=31),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_tag(self, tag, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
self.base_klass().decode(
tag_encode(tag)[:-1],
@given(
integers(min_value=128),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_len(self, l, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
self.base_klass().decode(
self.base_klass.tag_default + len_encode(l)[:-1],
@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))
+ decode_path = d.draw(decode_path_strat)
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"")
+ list(seq.pps())
+ pprint(seq, big_blobs=True, with_decode_path=True)
self.assertTrue(seq.ready)
- self._assert_expects(seq_decoded, expects)
- self.assertEqual(seq, seq_decoded)
- self.assertEqual(seq_decoded.encode(), seq_encoded)
- for expect in expects:
- if not expect["presented"]:
- self.assertNotIn(expect["name"], seq_decoded)
- continue
- self.assertIn(expect["name"], seq_decoded)
- obj = seq_decoded[expect["name"]]
- self.assertTrue(obj.decoded)
- offset = obj.expl_offset if obj.expled else obj.offset
- tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
- self.assertSequenceEqual(
- seq_encoded[offset:offset + tlvlen],
- obj.encode(),
- )
+ seq_encoded = seq.encode()
+ self.assertEqual(encode2pass(seq), seq_encoded)
+ seq_encoded_cer = encode_cer(seq)
+ self.assertNotEqual(seq_encoded_cer, seq_encoded)
+ self.assertSequenceEqual(
+ seq.decod(seq_encoded_cer, ctx={"bered": True}).encode(),
+ seq_encoded,
+ )
+ seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
+ self.assertFalse(seq_decoded.lenindef)
+ self.assertFalse(seq_decoded.ber_encoded)
+ self.assertFalse(seq_decoded.bered)
+
+ t, _, lv = tag_strip(seq_encoded)
+ _, _, v = len_decode(lv)
+ seq_encoded_lenindef = t + LENINDEF + v + EOC
+ with self.assertRaises(DecodeError):
+ seq.decode(seq_encoded_lenindef)
+ ctx_copied = deepcopy(ctx_dummy)
+ ctx_copied["bered"] = True
+ seq_decoded_lenindef, tail_lenindef = seq.decode(
+ seq_encoded_lenindef + tail_junk,
+ 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):
+ seq.decode(seq_encoded_lenindef[:-1], ctx={"bered": True})
+ with self.assertRaises(DecodeError):
+ seq.decode(seq_encoded_lenindef[:-2], ctx={"bered": True})
+ repr(seq_decoded_lenindef)
+ list(seq_decoded_lenindef.pps())
+ pprint(seq_decoded_lenindef, big_blobs=True, with_decode_path=True)
+ self.assertTrue(seq_decoded_lenindef.ready)
+
+ for decoded, decoded_tail, encoded in (
+ (seq_decoded, tail, seq_encoded),
+ (seq_decoded_lenindef, tail_lenindef, seq_encoded_lenindef),
+ ):
+ self.assertEqual(decoded_tail, tail_junk)
+ self._assert_expects(decoded, expects)
+ self.assertEqual(seq, decoded)
+ self.assertEqual(decoded.encode(), seq_encoded)
+ self.assertEqual(decoded.tlvlen, len(encoded))
+ for expect in expects:
+ if not expect["presented"]:
+ self.assertNotIn(expect["name"], decoded)
+ continue
+ self.assertIn(expect["name"], decoded)
+ obj = decoded[expect["name"]]
+ self.assertTrue(obj.decoded)
+ offset = obj.expl_offset if obj.expled else obj.offset
+ tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
+ self.assertSequenceEqual(
+ seq_encoded[offset:offset + tlvlen],
+ obj.encode(),
+ )
+
+ evgens = list(seq.decode_evgen(
+ encoded + decoded_tail,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ ))
+ self.assertEqual(len(evgens), len(list(decoded._values_for_encoding())) + 1)
+ for _decode_path, obj, _ in evgens[:-1]:
+ self.assertEqual(_decode_path[:-1], decode_path)
+ repr(obj)
+ list(obj.pps())
+ _decode_path, obj, tail = evgens[-1]
+ self.assertEqual(_decode_path, decode_path)
+ repr(obj)
+ list(obj.pps())
+
+ 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())
seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
self.assertTrue(seq.ready)
seq_encoded = seq.encode()
+ self.assertEqual(encode2pass(seq), seq_encoded)
seq_decoded, tail = seq.decode(seq_encoded)
self.assertEqual(tail, b"")
self.assertTrue(seq.ready)
self.assertSequenceEqual(seq.encode(), empty_seq)
@given(data_strategy())
- def test_encoded_default_accepted(self, d):
+ def test_encoded_default_not_accepted(self, d):
_schema = list(d.draw(dictionaries(
text_letters(),
integers(),
min_size=1,
)).items())
tags = [tag_encode(tag) for tag in d.draw(sets(
- integers(min_value=0),
+ integers(min_value=1),
min_size=len(_schema),
max_size=len(_schema),
))]
+ class Wahl(Choice):
+ schema = (("int", Integer()),)
class SeqWithoutDefault(self.base_klass):
schema = [
- (n, Integer(impl=t))
+ (n, Wahl(expl=t))
for (n, _), t in zip(_schema, tags)
]
seq_without_default = SeqWithoutDefault()
for name, value in _schema:
- seq_without_default[name] = Integer(value)
+ seq_without_default[name] = Wahl(("int", Integer(value)))
seq_encoded = seq_without_default.encode()
+ seq_without_default.decode(seq_encoded)
+ self.assertEqual(
+ len(list(seq_without_default.decode_evgen(seq_encoded))),
+ len(_schema) * 2 + 1,
+ )
class SeqWithDefault(self.base_klass):
schema = [
- (n, Integer(default=v, impl=t))
+ (n, Wahl(default=Wahl(("int", Integer(v))), expl=t))
for (n, v), t in zip(_schema, tags)
]
seq_with_default = SeqWithDefault()
- seq_decoded, _ = seq_with_default.decode(seq_encoded)
+ with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+ seq_with_default.decode(seq_encoded)
+ with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+ list(seq_with_default.decode_evgen(seq_encoded))
+ for ctx in ({"bered": True}, {"allow_default_values": True}):
+ 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, value)
+ self.assertEqual(
+ len(list(seq_with_default.decode_evgen(seq_encoded, ctx=ctx))),
+ len(_schema) + 1,
+ )
+
+ seq_without_default = SeqWithoutDefault()
for name, value in _schema:
- self.assertEqual(seq_decoded[name], seq_with_default[name])
- self.assertEqual(seq_decoded[name], value)
+ seq_without_default[name] = Wahl(("int", Integer(value + 1)))
+ seq_encoded = seq_without_default.encode()
+ seq_with_default.decode(seq_encoded)
+ self.assertEqual(
+ len(list(seq_with_default.decode_evgen(seq_encoded))),
+ len(_schema) + 1,
+ )
@given(data_strategy())
def test_missing_from_spec(self, d):
names = list(d.draw(sets(text_letters(), min_size=2)))
tags = [tag_encode(tag) for tag in d.draw(sets(
- integers(min_value=0),
+ integers(min_value=1),
min_size=len(names),
max_size=len(names),
))]
seq_missing = SeqMissing()
with self.assertRaises(TagMismatch):
seq_missing.decode(seq_encoded)
+ with self.assertRaises(TagMismatch):
+ list(seq_missing.decode_evgen(seq_encoded))
+
+ def test_bered(self):
+ class Seq(self.base_klass):
+ schema = (("underlying", Boolean()),)
+ encoded = Boolean.tag_default + len_encode(1) + b"\x01"
+ encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
+ decoded, _ = Seq().decode(encoded, ctx={"bered": True})
+ 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()),)
+ encoded = (
+ tag_encode(form=TagFormConstructed, num=4) +
+ LENINDEF +
+ OctetString(b"whatever").encode() +
+ EOC
+ )
+ encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
+ with self.assertRaises(DecodeError):
+ Seq().decode(encoded)
+ with self.assertRaises(DecodeError):
+ list(Seq().decode_evgen(encoded))
+ list(Seq().decode_evgen(encoded, ctx={"bered": True}))
+ decoded, _ = Seq().decode(encoded, ctx={"bered": True})
+ 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):
seq[missing] = Boolean()
repr(err.exception)
+ def test_x690_vector(self):
+ class Seq(Sequence):
+ schema = (
+ ("name", IA5String()),
+ ("ok", Boolean()),
+ )
+ seq = Seq().decode(hexdec("300A1605536d6974680101FF"))[0]
+ self.assertEqual(seq["name"], "Smith")
+ self.assertEqual(seq["ok"], True)
+
class TestSet(SeqMixing, CommonMixin, TestCase):
base_klass = Set
@settings(max_examples=LONG_TEST_MAX_EXAMPLES)
@given(data_strategy())
def test_sorted(self, d):
- tags = [
- tag_encode(tag) for tag in
- d.draw(sets(integers(min_value=1), min_size=1, max_size=10))
- ]
+ class DummySeq(Sequence):
+ schema = (("null", Null()),)
+
+ tag_nums = d.draw(sets(integers(min_value=1), min_size=1, max_size=50))
+ _, _, dummy_seq_tag_num = tag_decode(DummySeq.tag_default)
+ assume(any(i > dummy_seq_tag_num for i in tag_nums))
+ tag_nums -= set([dummy_seq_tag_num])
+ _schema = [(str(i), OctetString(impl=tag_encode(i))) for i in tag_nums]
+ _schema.append(("seq", DummySeq()))
class Seq(Set):
- schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
+ schema = d.draw(permutations(_schema))
seq = Seq()
- for name, _ in Seq.schema:
- seq[name] = OctetString(b"")
+ for name, _ in _schema:
+ if name != "seq":
+ seq[name] = OctetString(name.encode("ascii"))
+ seq["seq"] = DummySeq((("null", Null()),))
+
seq_encoded = seq.encode()
seq_decoded, _ = seq.decode(seq_encoded)
+ seq_encoded_expected = []
+ for tag_num in sorted(tag_nums | set([dummy_seq_tag_num])):
+ if tag_num == dummy_seq_tag_num:
+ seq_encoded_expected.append(seq["seq"].encode())
+ else:
+ seq_encoded_expected.append(seq[str(tag_num)].encode())
self.assertSequenceEqual(
seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
- b"".join(sorted([seq[name].encode() for name, _ in Seq.schema])),
+ b"".join(seq_encoded_expected),
)
+ encoded = b"".join(seq[str(i)].encode() for i in tag_nums)
+ encoded += seq["seq"].encode()
+ seq_encoded = b"".join((
+ Set.tag_default,
+ len_encode(len(encoded)),
+ encoded,
+ ))
+ with assertRaisesRegex(self, DecodeError, "unordered SET"):
+ seq.decode(seq_encoded)
+ for ctx in ({"bered": True}, {"allow_unordered_set": True}):
+ 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)
+
+ def test_same_value_twice(self):
+ class Seq(Set):
+ schema = (
+ ("bool", Boolean()),
+ ("int", Integer()),
+ )
+
+ encoded = b"".join((
+ Integer(123).encode(),
+ Integer(234).encode(),
+ Boolean(True).encode(),
+ ))
+ encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
+ with self.assertRaises(TagMismatch):
+ Seq().decod(encoded, ctx={"allow_unordered_set": True})
+
@composite
def seqof_values_strategy(draw, schema=None, do_expl=False):
with assertRaisesRegex(self, ValueError, "schema must be specified"):
self.base_klass.__mro__[1]()
- @given(booleans(), booleans(), binary(), binary())
+ @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
def test_comparison(self, value1, value2, tag1, tag2):
class SeqOf(self.base_klass):
schema = Boolean()
seqof.append(value)
self.assertFalse(seqof.ready)
repr(seqof)
- pprint(seqof)
+ list(seqof.pps())
+ pprint(seqof, big_blobs=True, with_decode_path=True)
with self.assertRaises(ObjNotReady) as err:
seqof.encode()
repr(err.exception)
+ with self.assertRaises(ObjNotReady) as err:
+ encode2pass(seqof)
for i, value in enumerate(values):
self.assertEqual(seqof[i], value)
if not seqof[i].ready:
seqof[i] = Integer(i)
self.assertTrue(seqof.ready)
repr(seqof)
- pprint(seqof)
+ list(seqof.pps())
+ pprint(seqof, big_blobs=True, with_decode_path=True)
def test_spec_mismatch(self):
class SeqOf(self.base_klass):
schema = Boolean()
bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
- value = [Boolean()] * d.draw(integers(max_value=bound_min - 1))
+ value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1))
with self.assertRaises(BoundsError) as err:
SeqOf(value=value, bounds=(bound_min, bound_max))
repr(err.exception)
- value = [Boolean()] * d.draw(integers(
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ SeqOf(bounds=(bound_min, bound_max)).decode(
+ SeqOf(value).encode()
+ )
+ repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ SeqOf(bounds=(bound_min, bound_max)).decode(
+ encode2pass(SeqOf(value))
+ )
+ value = [Boolean(True)] * d.draw(integers(
min_value=bound_max + 1,
max_value=bound_max + 10,
))
with self.assertRaises(BoundsError) as err:
SeqOf(value=value, bounds=(bound_min, bound_max))
repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ SeqOf(bounds=(bound_min, bound_max)).decode(
+ SeqOf(value).encode()
+ )
+ repr(err.exception)
+ with assertRaisesRegex(self, DecodeError, "bounds") as err:
+ SeqOf(bounds=(bound_min, bound_max)).decode(
+ encode2pass(SeqOf(value))
+ )
@given(integers(min_value=1, max_value=10))
def test_out_of_bounds(self, bound_max):
class SeqOf(self.base_klass):
schema = _schema
+ register_class(SeqOf)
obj = SeqOf(
value=value,
bounds=bounds,
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()),
@given(
integers(min_value=31),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_tag(self, tag, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
self.base_klass().decode(
tag_encode(tag)[:-1],
@given(
integers(min_value=128),
integers(min_value=0),
- lists(integers()),
+ decode_path_strat,
)
def test_bad_len(self, l, offset, decode_path):
- decode_path = tuple(str(i) for i in decode_path)
with self.assertRaises(DecodeError) as err:
self.base_klass().decode(
self.base_klass.tag_default + len_encode(l)[:-1],
lists(integers().map(Integer)),
integers(min_value=1).map(tag_ctxc),
integers(min_value=0),
+ binary(max_size=5),
+ decode_path_strat,
)
- def test_symmetric(self, values, value, tag_expl, offset):
+ def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
_, _, _, _, _, default, optional, _decoded = values
class SeqOf(self.base_klass):
_decoded=_decoded,
)
repr(obj)
- pprint(obj)
+ list(obj.pps())
+ pprint(obj, big_blobs=True, with_decode_path=True)
self.assertFalse(obj.expled)
obj_encoded = obj.encode()
+ self.assertEqual(encode2pass(obj), obj_encoded)
+ obj_encoded_cer = encode_cer(obj)
+ self.assertNotEqual(obj_encoded_cer, obj_encoded)
+ self.assertSequenceEqual(
+ obj.decod(obj_encoded_cer, ctx={"bered": True}).encode(),
+ obj_encoded,
+ )
obj_expled = obj(value, expl=tag_expl)
self.assertTrue(obj_expled.expled)
repr(obj_expled)
- pprint(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, offset=offset)
+ 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)
- pprint(obj_decoded)
- self.assertEqual(tail, b"")
+ list(obj_decoded.pps())
+ pprint(obj_decoded, big_blobs=True, with_decode_path=True)
+ 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)
],
)
+ t, _, lv = tag_strip(obj_encoded)
+ _, _, v = len_decode(lv)
+ obj_encoded_lenindef = t + LENINDEF + v + EOC
+ with self.assertRaises(DecodeError):
+ obj.decode(obj_encoded_lenindef)
+ obj_decoded_lenindef, tail_lenindef = obj.decode(
+ obj_encoded_lenindef + tail_junk,
+ ctx={"bered": True},
+ )
+ 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})
+
+ evgens = list(obj.decode_evgen(
+ obj_encoded_lenindef + tail_junk,
+ decode_path=decode_path,
+ ctx={"bered": True},
+ ))
+ self.assertEqual(len(evgens), len(obj_decoded_lenindef) + 1)
+ for i, (_decode_path, obj, _) in enumerate(evgens[:-1]):
+ self.assertEqual(_decode_path, decode_path + (str(i),))
+ repr(obj)
+ list(obj.pps())
+ _decode_path, obj, tail = evgens[-1]
+ self.assertEqual(_decode_path, decode_path)
+ repr(obj)
+ list(obj.pps())
+
+ 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()
+ encoded += Boolean.tag_default + len_encode(1) + b"\x01"
+ encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
+ with self.assertRaises(DecodeError):
+ SeqOf().decode(encoded)
+ decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
+ 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()
+ encoded = OctetString(b"whatever").encode()
+ encoded += (
+ tag_encode(form=TagFormConstructed, num=4) +
+ LENINDEF +
+ OctetString(b"whatever").encode() +
+ EOC
+ )
+ encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
+ with self.assertRaises(DecodeError):
+ SeqOf().decode(encoded)
+ decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
+ 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):
class SeqOf(SequenceOf):
self.assertEqual(obj1, obj2)
self.assertSequenceEqual(list(obj1), list(obj2))
+ def test_iterator_pickling(self):
+ class SeqOf(SequenceOf):
+ schema = Integer()
+ register_class(SeqOf)
+ seqof = SeqOf()
+ pickle_dumps(seqof)
+ seqof = seqof(iter(six_xrange(10)))
+ with assertRaisesRegex(self, ValueError, "iterator"):
+ pickle_dumps(seqof)
+
+ def test_iterator_bounds(self):
+ class SeqOf(SequenceOf):
+ schema = Integer()
+ bounds = (10, 20)
+ seqof = None
+ def gen(n):
+ for i in six_xrange(n):
+ yield Integer(i)
+ for n in (9, 21):
+ seqof = SeqOf(gen(n))
+ self.assertTrue(seqof.ready)
+ with self.assertRaises(BoundsError):
+ seqof.encode()
+ self.assertFalse(seqof.ready)
+ seqof = seqof(gen(n))
+ self.assertTrue(seqof.ready)
+ with self.assertRaises(BoundsError):
+ encode_cer(seqof)
+ self.assertFalse(seqof.ready)
+
+ def test_iterator_twice(self):
+ class SeqOf(SequenceOf):
+ schema = Integer()
+ bounds = (1, float("+inf"))
+ def gen():
+ for i in six_xrange(10):
+ yield Integer(i)
+ seqof = SeqOf(gen())
+ self.assertTrue(seqof.ready)
+ seqof.encode()
+ self.assertFalse(seqof.ready)
+ register_class(SeqOf)
+ pickle_dumps(seqof)
+
+ def test_iterator_2pass(self):
+ class SeqOf(SequenceOf):
+ schema = Integer()
+ bounds = (1, float("+inf"))
+ def gen():
+ for i in six_xrange(10):
+ yield Integer(i)
+ seqof = SeqOf(gen())
+ self.assertTrue(seqof.ready)
+ _, state = seqof.encode1st()
+ self.assertFalse(seqof.ready)
+ seqof = seqof(gen())
+ self.assertTrue(seqof.ready)
+ buf = BytesIO()
+ seqof.encode2nd(buf.write, iter(state))
+ self.assertSequenceEqual(
+ [int(i) for i in seqof.decod(buf.getvalue())],
+ list(gen()),
+ )
+
+ def test_non_ready_bound_min(self):
+ class SeqOf(SequenceOf):
+ schema = Integer()
+ bounds = (1, float("+inf"))
+ seqof = SeqOf()
+ self.assertFalse(seqof.ready)
+
class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
class SeqOf(SetOf):
b"".join(sorted([v.encode() for v in values])),
)
+ @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
+ @given(data_strategy())
+ def test_unsorted(self, d):
+ values = [OctetString(v).encode() for v in d.draw(sets(
+ binary(min_size=1, max_size=5),
+ min_size=2,
+ max_size=5,
+ ))]
+ values = d.draw(permutations(values))
+ assume(values != sorted(values))
+ encoded = b"".join(values)
+ seq_encoded = b"".join((
+ SetOf.tag_default,
+ len_encode(len(encoded)),
+ encoded,
+ ))
+
+ class Seq(SetOf):
+ schema = OctetString()
+ seq = Seq()
+ with assertRaisesRegex(self, DecodeError, "unordered SET OF"):
+ seq.decode(seq_encoded)
+
+ for ctx in ({"bered": True}, {"allow_unordered_set": True}):
+ 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,
+ )
+
class TestGoMarshalVectors(TestCase):
def runTest(self):
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 = (
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):
min_size=len(value_names),
max_size=len(value_names),
))
- _schema = [
- ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
- 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 definable_class in (Any, OctetString, BitString):
+ _schema = [
+ ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
+ oid: Integer() for oid in oids[:-1]
+ }),))),
+ ]
+ for i, value_name in enumerate(value_names):
+ _schema.append((value_name, definable_class(expl=tag_ctxp(i))))
- class Seq(Sequence):
- schema = _schema
- seq = Seq()
- for value_name, value in zip(value_names, values):
- seq[value_name] = Any(Integer(value).encode())
- seq["type"] = oid_chosen
- seq, _ = Seq().decode(seq.encode())
- for value_name in value_names:
- if value_name == value_name_chosen:
- continue
- self.assertIsNone(seq[value_name].defined)
- if value_name_chosen in oids[:-1]:
- self.assertIsNotNone(seq[value_name_chosen].defined)
- self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
- self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
+ class Seq(Sequence):
+ schema = _schema
+ seq = Seq()
+ for value_name, value in zip(value_names, values):
+ seq[value_name] = definable_class(Integer(value).encode())
+ seq["type"] = oid_chosen
+ seq, _ = Seq().decode(seq.encode())
+ for value_name in value_names:
+ if value_name == value_name_chosen:
+ continue
+ self.assertIsNone(seq[value_name].defined)
+ if value_name_chosen in oids[:-1]:
+ self.assertIsNotNone(seq[value_name_chosen].defined)
+ self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
+ self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
+ repr(seq)
+ list(seq.pps())
+ pprint(seq, big_blobs=True, with_decode_path=True)
class TestDefinesByPath(TestCase):
(type_integered, Integer(234)),
)
for t, v in pairs_input:
- pair = Pair()
- pair["type"] = t
- pair["value"] = PairValue((Any(v),))
- pairs.append(pair)
+ pairs.append(Pair((
+ ("type", t),
+ ("value", PairValue((Any(v),))),
+ )))
seq_inner = SeqInner()
seq_inner["typeInner"] = type_innered
seq_inner["valueInner"] = Any(pairs)
seq_sequenced["type"] = type_sequenced
seq_sequenced["value"] = OctetString(seq_inner.encode())
seq_sequenced_raw = seq_sequenced.encode()
+ repr(seq_sequenced)
+ list(seq_sequenced.pps())
+ 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",), {
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))
-
+ self.assertTrue(seq_integered_raw[
+ seq_integered["value"].defined[1].offset:
+ ].startswith(Integer(123).encode()))
+ repr(seq_integered)
+ 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]
self.assertIsNone(seq_inner["valueInner"].defined)
+ repr(seq_sequenced)
+ list(seq_sequenced.pps())
+ pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
defines_by_path.append((
- ("value", decode_path_defby(type_sequenced), "typeInner"),
+ ("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]
pairs = seq_inner["valueInner"].defined[1]
for pair in pairs:
self.assertIsNone(pair["value"][0].defined)
+ repr(seq_sequenced)
+ list(seq_sequenced.pps())
+ pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
defines_by_path.append((
(
"value",
- decode_path_defby(type_sequenced),
+ DecodePathDefBy(type_sequenced),
"valueInner",
- decode_path_defby(type_innered),
+ DecodePathDefBy(type_innered),
any,
"type",
),
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]
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])
+ repr(seq_sequenced)
+ list(seq_sequenced.pps())
+ pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
@given(oid_strategy(), integers())
def test_simple(self, oid, tgt):
decoded, _ = Outer().decode(outer.encode())
self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
+ def test_remaining_data(self):
+ oid = ObjectIdentifier("1.2.3")
+ class Seq(Sequence):
+ schema = (
+ ("oid", ObjectIdentifier(defines=((("tgt",), {
+ oid: Integer(),
+ }),))),
+ ("tgt", OctetString()),
+ )
+
+ seq = Seq((
+ ("oid", oid),
+ ("tgt", OctetString(Integer(123).encode() + b"junk")),
+ ))
+ with assertRaisesRegex(self, DecodeError, "remaining data"):
+ Seq().decode(seq.encode())
+
+ def test_remaining_data_seqof(self):
+ oid = ObjectIdentifier("1.2.3")
+ class SeqOf(SetOf):
+ schema = OctetString()
+
+ class Seq(Sequence):
+ schema = (
+ ("oid", ObjectIdentifier(defines=((("tgt",), {
+ oid: Integer(),
+ }),))),
+ ("tgt", SeqOf()),
+ )
+
+ seq = Seq((
+ ("oid", oid),
+ ("tgt", SeqOf([OctetString(Integer(123).encode() + b"junk")])),
+ ))
+ with assertRaisesRegex(self, DecodeError, "remaining data"):
+ Seq().decode(seq.encode())
+
+
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,
- )
+ dp = abs_decode_path(decode_path, rel_path)
+ self.assertSequenceEqual(dp, decode_path + rel_path)
+ repr(dp)
@given(
lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
for i in range(count)
]
+ for klass in (Sequence, Set):
+ class Seq(klass):
+ schema = _schema
+ seq = Seq()
+ for i in range(count):
+ seq["int%d" % i] = Integer(123)
+ raw = seq.encode()
+ chosen_choice = "int%d" % chosen
+ seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
+ with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+ seq.decode(raw)
+ 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):
+ def test_1(self):
+ self.assertSequenceEqual(
+ VisibleString("Jones").encode(),
+ hexdec("1A054A6F6E6573"),
+ )
- 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})
+ def test_2(self):
+ self.assertSequenceEqual(
+ VisibleString(
+ "Jones",
+ impl=tag_encode(3, klass=TagClassApplication),
+ ).encode(),
+ hexdec("43054A6F6E6573"),
+ )
+
+ def test_3(self):
+ self.assertSequenceEqual(
+ Any(
+ VisibleString(
+ "Jones",
+ impl=tag_encode(3, klass=TagClassApplication),
+ ),
+ expl=tag_ctxc(2),
+ ).encode(),
+ hexdec("A20743054A6F6E6573"),
+ )
+
+ def test_4(self):
+ self.assertSequenceEqual(
+ OctetString(
+ VisibleString(
+ "Jones",
+ impl=tag_encode(3, klass=TagClassApplication),
+ ).encode(),
+ impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
+ ).encode(),
+ hexdec("670743054A6F6E6573"),
+ )
+
+ def test_5(self):
+ self.assertSequenceEqual(
+ VisibleString("Jones", impl=tag_ctxp(2)).encode(),
+ hexdec("82054A6F6E6573"),
+ )
+
+
+class TestExplOOB(TestCase):
+ def runTest(self):
+ expl = tag_ctxc(123)
+ raw = Integer(123).encode() + Integer(234).encode()
+ raw = b"".join((expl, len_encode(len(raw)), raw))
+ 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)
+
+
+class TestCERSetOrdering(TestCase):
+ def test_vectors(self):
+ """Taken from X.690-201508
+ """
+ class B(Choice):
+ schema = (
+ ("c", Integer(impl=tag_ctxp(2))),
+ ("d", Integer(impl=tag_ctxp(4))),
+ )
+
+ class F(Choice):
+ schema = (
+ ("g", Integer(impl=tag_ctxp(5))),
+ ("h", Integer(impl=tag_ctxp(6))),
+ )
+
+ class I(Choice):
+ schema = (
+ ("j", Integer(impl=tag_ctxp(0))),
+ )
+
+ class E(Choice):
+ schema = (
+ ("f", F()),
+ ("i", I()),
+ )
+
+ class A(Set):
+ schema = (
+ ("a", Integer(impl=tag_ctxp(3))),
+ ("b", B(expl=tag_ctxc(1))),
+ ("e", E()),
+ )
+
+ a = A((
+ ("a", Integer(123)),
+ ("b", B(("d", Integer(234)))),
+ ("e", E(("f", F(("g", Integer(345)))))),
+ ))
+ order = sorted(a._values_for_encoding(), key=attrgetter("tag_order_cer"))
+ self.assertSequenceEqual(
+ [i.__class__.__name__ for i in order],
+ ("E", "B", "Integer"),
+ )