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>`
-* :ref:`strict_default_existence <strict_default_existence_ctx>`
.. _pprinting:
* If object is encoded in BER form (not the DER one), then ``bered``
attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
- STRING`` can contain it.
+ STRING``, ``SEQUENCE``, ``SET`` can contain it.
* If object has an indefinite length encoding, then its ``lenindef``
attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
EOC (end-of-contents) token's length is taken in advance in object's
value length.
+.. _allow_expl_oob_ctx:
+
+Allow explicit tag out-of-bound
+-------------------------------
+
+Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
+one value, more than one object. If you set ``allow_expl_oob`` context
+option to True, then no error will be raised and that invalid encoding
+will be silently further processed. But pay attention that offsets and
+lengths will be invalid in that case.
+
+.. warning::
+
+ This option should be used only for skipping some decode errors, just
+ to see the decoded structure somehow.
+
Primitive types
---------------
if tag_only:
return
obj, tail = result
+ if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
+ raise DecodeError(
+ "explicit tag out-of-bound, longer than data",
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
return obj, (tail if leavemm else tail.tobytes())
@property
All defaulted values are always optional.
- .. _strict_default_existence_ctx:
+ .. _allow_default_values_ctx:
- .. warning::
-
- When decoded DER contains defaulted value inside, then
- technically this is not valid DER encoding. But we allow and pass
- it **by default**. Of course reencoding of that kind of DER will
- result in different binary representation (validly without
- defaulted value inside). You can enable strict defaulted values
- existence validation by setting ``"strict_default_existence":
- True`` :ref:`context <ctx>` option -- decoding process will raise
- an exception if defaulted value is met.
+ DER prohibits default value encoding and will raise an error if
+ default value is unexpectedly met during decode.
+ If :ref:`bered <bered_ctx>` context option is set, then no error
+ will be raised, but ``bered`` attribute set. You can disable strict
+ defaulted values existence validation by setting
+ ``"allow_default_values": True`` :ref:`context <ctx>` option.
Two sequences are equal if they have equal specification (schema),
implicit/explicit tagging and the same values.
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__,
vlen = 0
sub_offset = offset + tlen + llen
values = {}
+ bered = False
+ ctx_allow_default_values = ctx.get("allow_default_values", False)
for name, spec in self.specs.items():
if spec.optional and (
(lenindef and v[:EOC_LEN].tobytes() == EOC) or
sub_offset += value_len
v = v_tail
if spec.default is not None and value == spec.default:
- if ctx.get("strict_default_existence", False):
+ if ctx_bered or ctx_allow_default_values:
+ bered = True
+ else:
raise DecodeError(
"DEFAULT value met",
klass=self.__class__,
decode_path=sub_decode_path,
offset=sub_offset,
)
- else:
- continue
values[name] = value
spec_defines = getattr(spec, "defines", ())
)
obj._value = values
obj.lenindef = lenindef
+ obj.bered = bered
return obj, tail
def __repr__(self):
"""``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)
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__,
vlen = 0
sub_offset = offset + tlen + llen
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:
ctx=ctx,
)
value_len = value.fulllen
+ 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:
+ bered = True
+ else:
+ raise DecodeError(
+ "DEFAULT value met",
+ klass=self.__class__,
+ decode_path=sub_decode_path,
+ offset=sub_offset,
+ )
+ values[name] = value
+ value_prev = v[:value_len]
sub_offset += value_len
vlen += value_len
v = v_tail
- if spec.default is None or value != spec.default: # pragma: no cover
- # SeqMixing.test_encoded_default_accepted covers that place
- values[name] = value
obj = self.__class__(
schema=self.specs,
impl=self.tag,
optional=self.optional,
_decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
)
- obj._value = values
if lenindef:
if v[:EOC_LEN].tobytes() != EOC:
raise DecodeError(
)
tail = v[EOC_LEN:]
obj.lenindef = True
+ obj._value = values
if not obj.ready:
raise DecodeError(
"not all values are ready",
decode_path=decode_path,
offset=offset,
)
+ obj.bered = bered
return obj, tail
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:
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__,
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,
)
obj.lenindef = True
tail = v[EOC_LEN:]
+ obj.bered = bered
return obj, tail
def __repr__(self):
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
"--decode-path-only",
help="Print only specified decode path",
)
+ parser.add_argument(
+ "--allow-expl-oob",
+ action="store_true",
+ help="Allow explicit tag out-of-bound",
+ )
parser.add_argument(
"DERFile",
type=argparse.FileType("rb"),
pprinter = partial(pprint, big_blobs=True)
else:
schema, pprinter = generic_decoder()
- ctx = {"bered": not args.nobered}
+ ctx = {
+ "bered": not args.nobered,
+ "allow_expl_oob": args.allow_expl_oob,
+ }
if args.defines_by_path is not None:
ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
obj, tail = schema().decode(der, ctx=ctx)