From f928d1e65eeff56c2b5d511567e6b52417195f3b Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Mon, 10 Sep 2018 22:26:48 +0300 Subject: [PATCH] Strict SET values ordering check --- doc/news.rst | 1 + pyderasn.py | 32 +++++++++++++++++++++++++++----- tests/test_pyderasn.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/doc/news.rst b/doc/news.rst index d0f156f..6712fe3 100644 --- a/doc/news.rst +++ b/doc/news.rst @@ -9,6 +9,7 @@ News * **Incompatible** change: defaulted values in Sequence/Set are always strictly checked, unless ``allow_default_values`` context option is set. ``strict_default_existence`` option disappeared +* Strict Set's values ordering check .. _release3.14: diff --git a/pyderasn.py b/pyderasn.py index a773296..3dd3ea9 100755 --- a/pyderasn.py +++ b/pyderasn.py @@ -215,6 +215,7 @@ Currently available context options: * :ref:`allow_default_values ` * :ref:`allow_expl_oob ` +* :ref:`allow_unordered_set ` * :ref:`bered ` * :ref:`defines_by_path ` @@ -4708,6 +4709,14 @@ class Set(Sequence): """``SET`` structure type Its usage is identical to :py:class:`pyderasn.Sequence`. + + .. _allow_unordered_set_ctx: + + DER prohibits unordered values encoding and will raise an error + during decode. If If :ref:`bered ` context option is set, + then no error will occure. Also you can disable strict values + ordering check by setting ``"allow_unordered_set": True`` + :ref:`context ` option. """ __slots__ = () tag_default = tag_encode(form=TagFormConstructed, num=17) @@ -4771,6 +4780,8 @@ class Set(Sequence): values = {} bered = False ctx_allow_default_values = ctx.get("allow_default_values", False) + ctx_allow_unordered_set = ctx.get("allow_unordered_set", False) + value_prev = memoryview(v[:0]) specs_items = self.specs.items while len(v) > 0: if lenindef and v[:EOC_LEN].tobytes() == EOC: @@ -4803,9 +4814,16 @@ class Set(Sequence): ctx=ctx, ) value_len = value.fulllen - sub_offset += value_len - vlen += value_len - v = v_tail + if value_prev.tobytes() > v[:value_len].tobytes(): + if ctx_bered or ctx_allow_unordered_set: + bered = True + else: + raise DecodeError( + "unordered " + self.asn1_type_name, + klass=self.__class__, + decode_path=sub_decode_path, + offset=sub_offset, + ) if spec.default is None or value != spec.default: pass elif ctx_bered or ctx_allow_default_values: @@ -4818,6 +4836,10 @@ class Set(Sequence): offset=sub_offset, ) values[name] = value + value_prev = v[:value_len] + sub_offset += value_len + vlen += value_len + v = v_tail obj = self.__class__( schema=self.specs, impl=self.tag, @@ -4826,8 +4848,6 @@ class Set(Sequence): optional=self.optional, _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)), ) - obj._value = values - obj.bered = bered if lenindef: if v[:EOC_LEN].tobytes() != EOC: raise DecodeError( @@ -4838,6 +4858,7 @@ class Set(Sequence): ) tail = v[EOC_LEN:] obj.lenindef = True + obj._value = values if not obj.ready: raise DecodeError( "not all values are ready", @@ -4845,6 +4866,7 @@ class Set(Sequence): decode_path=decode_path, offset=offset, ) + obj.bered = bered return obj, tail diff --git a/tests/test_pyderasn.py b/tests/test_pyderasn.py index c8321bb..797d9b0 100644 --- a/tests/test_pyderasn.py +++ b/tests/test_pyderasn.py @@ -4972,6 +4972,35 @@ class TestSet(SeqMixing, CommonMixin, TestCase): b"".join(sorted([seq[name].encode() for name, _ in Seq.schema])), ) + @settings(max_examples=LONG_TEST_MAX_EXAMPLES) + @given(data_strategy()) + def test_unsorted(self, d): + tags = [ + tag_encode(tag) for tag in + d.draw(sets(integers(min_value=1), min_size=2, max_size=5)) + ] + tags = d.draw(permutations(tags)) + assume(tags != sorted(tags)) + encoded = b"".join(OctetString(t, impl=t).encode() for t in tags) + seq_encoded = b"".join(( + Set.tag_default, + len_encode(len(encoded)), + encoded, + )) + + class Seq(Set): + schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)] + seq = Seq() + 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.bered) + self.assertSequenceEqual( + [bytes(seq_decoded[str(i)]) for i, t in enumerate(tags)], + [t for t in tags], + ) + @composite def seqof_values_strategy(draw, schema=None, do_expl=False): -- 2.44.0