summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Matveev <stargrave@stargrave.org>2018-09-10 22:26:48 +0300
committerSergey Matveev <stargrave@stargrave.org>2018-09-10 22:54:28 +0300
commitf928d1e65eeff56c2b5d511567e6b52417195f3b (patch)
treeb3f36e3311c0c0cde281262378be4ac69241d562
parent2dc06a21fd4bd1e52bbea125ef744d58fc0d17bf (diff)
downloadpyderasn-f928d1e65eeff56c2b5d511567e6b52417195f3b.tar.xz
Strict SET values ordering check
-rw-r--r--doc/news.rst1
-rwxr-xr-xpyderasn.py32
-rw-r--r--tests/test_pyderasn.py29
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 <allow_default_values_ctx>`
* :ref:`allow_expl_oob <allow_expl_oob_ctx>`
+* :ref:`allow_unordered_set <allow_unordered_set_ctx>`
* :ref:`bered <bered_ctx>`
* :ref:`defines_by_path <defines_by_path_ctx>`
@@ -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 <bered_ctx>` 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 <ctx>` 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):