summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Matveev <stargrave@stargrave.org>2018-09-10 22:55:09 +0300
committerSergey Matveev <stargrave@stargrave.org>2018-09-10 22:55:09 +0300
commit24bd1587aa3d3ce2723043db8e81c1f56974dc09 (patch)
tree3bbf9cf61358889fad29c6e8e3cc44806442d484
parentf928d1e65eeff56c2b5d511567e6b52417195f3b (diff)
downloadpyderasn-24bd1587aa3d3ce2723043db8e81c1f56974dc09.tar.xz
Strict SET OF values ordering check
-rw-r--r--doc/news.rst2
-rwxr-xr-xpyderasn.py36
-rw-r--r--tests/test_pyderasn.py31
3 files changed, 64 insertions, 5 deletions
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):