Currently available context options:
+* :ref:`allow_default_values <allow_default_values_ctx>`
* :ref:`allow_expl_oob <allow_expl_oob_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
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):
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)
specs_items = self.specs.items
while len(v) > 0:
if lenindef and v[:EOC_LEN].tobytes() == EOC:
sub_offset += value_len
vlen += value_len
v = v_tail
- if spec.default is None:
- values[name] = value
+ if spec.default is None or value != spec.default:
+ pass
+ elif ctx_bered or ctx_allow_default_values:
+ bered = True
else:
- if value != spec.default:
- values[name] = value
- if ctx.get("strict_default_existence", False):
- raise DecodeError(
- "DEFAULT value met",
- klass=self.__class__,
- decode_path=sub_decode_path,
- offset=sub_offset,
- )
+ raise DecodeError(
+ "DEFAULT value met",
+ klass=self.__class__,
+ decode_path=sub_decode_path,
+ offset=sub_offset,
+ )
+ values[name] = value
obj = self.__class__(
schema=self.specs,
impl=self.tag,
_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(
seq_encoded = seq.encode()
seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
self.assertFalse(seq_decoded.lenindef)
+ self.assertFalse(seq_decoded.bered)
t, _, lv = tag_strip(seq_encoded)
_, _, v = len_decode(lv)
self.assertSequenceEqual(seq.encode(), empty_seq)
@given(data_strategy())
- def test_encoded_default_accepted(self, d):
+ def test_encoded_default_not_accepted(self, d):
_schema = list(d.draw(dictionaries(
text_letters(),
integers(),
for (n, v), t in zip(_schema, tags)
]
seq_with_default = SeqWithDefault()
- seq_decoded, _ = seq_with_default.decode(seq_encoded)
- for name, value in _schema:
- self.assertEqual(seq_decoded[name], seq_with_default[name])
- self.assertEqual(seq_decoded[name], value)
+ with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+ seq_with_default.decode(seq_encoded)
+ for ctx in ({"bered": True}, {"allow_default_values": True}):
+ seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
+ self.assertTrue(seq_decoded.bered)
+ for name, value in _schema:
+ self.assertEqual(seq_decoded[name], seq_with_default[name])
+ self.assertEqual(seq_decoded[name], value)
@given(data_strategy())
def test_missing_from_spec(self, d):
raw = seq.encode()
chosen_choice = "int%d" % chosen
seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
- seq.decode(raw)
with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
- seq.decode(raw, ctx={"strict_default_existence": True})
+ seq.decode(raw)
+ decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
+ self.assertTrue(decoded.bered)
+ decoded, _ = seq.decode(raw, ctx={"bered": True})
+ self.assertTrue(decoded.bered)
class TestX690PrefixedType(TestCase):