From: Sergey Matveev Date: Mon, 10 Sep 2018 19:55:09 +0000 (+0300) Subject: Strict SET OF values ordering check X-Git-Tag: 4.0~1 X-Git-Url: http://www.git.cypherpunks.ru/?p=pyderasn.git;a=commitdiff_plain;h=24bd1587aa3d3ce2723043db8e81c1f56974dc09 Strict SET OF values ordering check --- diff --git a/doc/news.rst b/doc/news.rst index 6712fe3..f9b9f8e 100644 --- a/doc/news.rst +++ b/doc/news.rst @@ -9,7 +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 +* Strict Set/Set Of's values ordering check .. _release3.14: diff --git a/pyderasn.py b/pyderasn.py index 3dd3ea9..df639eb 100755 --- a/pyderasn.py +++ b/pyderasn.py @@ -5050,7 +5050,7 @@ class SequenceOf(Obj): v = b"".join(self._encoded_values()) return b"".join((self.tag, len_encode(len(v)), v)) - def _decode(self, tlv, offset, decode_path, ctx, tag_only): + def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False): try: t, tlen, lv = tag_strip(tlv) except DecodeError as err: @@ -5069,10 +5069,11 @@ class SequenceOf(Obj): if tag_only: return lenindef = False + ctx_bered = ctx.get("bered", False) try: l, llen, v = len_decode(lv) except LenIndefForm as err: - if not ctx.get("bered", False): + if not ctx_bered: raise err.__class__( msg=err.msg, klass=self.__class__, @@ -5100,22 +5101,38 @@ class SequenceOf(Obj): vlen = 0 sub_offset = offset + tlen + llen _value = [] + ctx_allow_unordered_set = ctx.get("allow_unordered_set", False) + value_prev = memoryview(v[:0]) + bered = False spec = self.spec while len(v) > 0: if lenindef and v[:EOC_LEN].tobytes() == EOC: break + sub_decode_path = decode_path + (str(len(_value)),) value, v_tail = spec.decode( v, sub_offset, leavemm=True, - decode_path=decode_path + (str(len(_value)),), + decode_path=sub_decode_path, ctx=ctx, ) value_len = value.fulllen + if ordering_check: + 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, + ) + value_prev = v[:value_len] + _value.append(value) sub_offset += value_len vlen += value_len v = v_tail - _value.append(value) try: obj = self.__class__( value=_value, @@ -5144,6 +5161,7 @@ class SequenceOf(Obj): ) obj.lenindef = True tail = v[EOC_LEN:] + obj.bered = bered return obj, tail def __repr__(self): @@ -5193,6 +5211,16 @@ class SetOf(SequenceOf): v = b"".join(raws) return b"".join((self.tag, len_encode(len(v)), v)) + def _decode(self, tlv, offset, decode_path, ctx, tag_only): + return super(SetOf, self)._decode( + tlv, + offset, + decode_path, + ctx, + tag_only, + ordering_check=True, + ) + def obj_by_path(pypath): # pragma: no cover """Import object specified as string Python path diff --git a/tests/test_pyderasn.py b/tests/test_pyderasn.py index 797d9b0..0ce2b31 100644 --- a/tests/test_pyderasn.py +++ b/tests/test_pyderasn.py @@ -5457,6 +5457,37 @@ class TestSetOf(SeqOfMixing, CommonMixin, TestCase): 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.bered) + self.assertSequenceEqual( + [obj.encode() for obj in seq_decoded], + values, + ) + class TestGoMarshalVectors(TestCase): def runTest(self):