TagClassPrivate: "PRIVATE ",
TagClassUniversal: "UNIV ",
}
+EOC = b"\x00\x00"
+EOC_LEN = len(EOC)
########################################################################
pass
+class LenIndefForm(DecodeError):
+ pass
+
+
class TagMismatch(DecodeError):
pass
if octets_num + 1 > len(data):
raise NotEnoughData("encoded length is longer than data")
if octets_num == 0:
- raise DecodeError("long form instead of short one")
+ raise LenIndefForm()
if byte2int(data[1:]) == 0:
raise DecodeError("leading zeros")
l = 0
"offset",
"llen",
"vlen",
+ "lenindef",
+ "expl_lenindef",
+ "bered",
)
def __init__(
self.optional = optional
self.offset, self.llen, self.vlen = _decoded
self.default = None
+ self.lenindef = False
+ self.expl_lenindef = False
+ self.bered = False
@property
def ready(self): # pragma: no cover
)
try:
l, llen, v = len_decode(lv)
+ except LenIndefForm as err:
+ if not ctx.get("bered", False):
+ raise err.__class__(
+ msg=err.msg,
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ llen, v = 1, lv[1:]
+ offset += tlen + llen
+ result = self._decode(
+ v,
+ offset=offset,
+ decode_path=decode_path,
+ ctx=ctx,
+ tag_only=tag_only,
+ )
+ if tag_only:
+ return
+ obj, tail = result
+ eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
+ if eoc_expected.tobytes() != EOC:
+ raise DecodeError(
+ msg="no EOC",
+ decode_path=decode_path,
+ offset=offset,
+ )
+ obj.vlen += EOC_LEN
+ obj.expl_lenindef = True
except DecodeError as err:
raise err.__class__(
msg=err.msg,
decode_path=decode_path,
offset=offset,
)
- if l > len(v):
- raise NotEnoughData(
- "encoded length is longer than data",
- klass=self.__class__,
+ else:
+ if l > len(v):
+ raise NotEnoughData(
+ "encoded length is longer than data",
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ result = self._decode(
+ v,
+ offset=offset + tlen + llen,
decode_path=decode_path,
- offset=offset,
+ ctx=ctx,
+ tag_only=tag_only,
)
- result = self._decode(
- v,
- offset=offset + tlen + llen,
- decode_path=decode_path,
- ctx=ctx,
- tag_only=tag_only,
- )
- if tag_only:
- return
- obj, tail = result
+ if tag_only:
+ return
+ obj, tail = result
return obj, (tail if leavemm else tail.tobytes())
@property
@property
def expl_llen(self):
+ if self.expl_lenindef:
+ return 1
return len(len_encode(self.tlvlen))
@property
offset=offset,
)
first_octet = byte2int(v)
+ bered = False
if first_octet == 0:
value = False
elif first_octet == 0xFF:
value = True
+ elif ctx.get("bered", False):
+ value = True
+ bered = True
else:
raise DecodeError(
"unacceptable Boolean value",
optional=self.optional,
_decoded=(offset, 1, 1),
)
+ obj.bered = bered
return obj, v[1:]
def __repr__(self):
>>> b.specs
{'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
"""
- __slots__ = ("specs", "defined")
+ __slots__ = ("tag_constructed", "specs", "defined")
tag_default = tag_encode(3)
asn1_type_name = "BIT STRING"
if value is None:
self._value = default
self.defined = None
+ tag_klass, _, tag_num = tag_decode(self.tag)
+ self.tag_constructed = tag_encode(
+ klass=tag_klass,
+ form=TagFormConstructed,
+ num=tag_num,
+ )
def _bits2octets(self, bits):
if len(self.specs) > 0:
octets,
))
- def _decode(self, tlv, offset, decode_path, ctx, tag_only):
- try:
- t, _, lv = tag_strip(tlv)
- except DecodeError as err:
- raise err.__class__(
- msg=err.msg,
- klass=self.__class__,
- decode_path=decode_path,
- offset=offset,
- )
- if t != self.tag:
- raise TagMismatch(
- klass=self.__class__,
- decode_path=decode_path,
- offset=offset,
- )
- if tag_only:
- return
+ def _decode_chunk(self, lv, offset, decode_path, ctx):
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
)
return obj, tail
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+ try:
+ t, tlen, lv = tag_strip(tlv)
+ except DecodeError as err:
+ raise err.__class__(
+ msg=err.msg,
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ if t == self.tag:
+ if tag_only:
+ return
+ return self._decode_chunk(lv, offset, decode_path, ctx)
+ if t == self.tag_constructed:
+ if not ctx.get("bered", False):
+ raise DecodeError(
+ msg="unallowed BER constructed encoding",
+ decode_path=decode_path,
+ offset=offset,
+ )
+ if tag_only:
+ return
+ lenindef = False
+ try:
+ l, llen, v = len_decode(lv)
+ except LenIndefForm:
+ llen, l, v = 1, 0, lv[1:]
+ lenindef = True
+ except DecodeError as err:
+ raise err.__class__(
+ msg=err.msg,
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ if l > 0 and l > len(v):
+ raise NotEnoughData(
+ "encoded length is longer than data",
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ if not lenindef and l == 0:
+ raise NotEnoughData(
+ "zero length",
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ chunks = []
+ sub_offset = offset + tlen + llen
+ vlen = 0
+ while True:
+ if lenindef:
+ if v[:EOC_LEN].tobytes() == EOC:
+ break
+ else:
+ if vlen == l:
+ break
+ if vlen > l:
+ raise DecodeError(
+ msg="chunk out of bounds",
+ decode_path=len(chunks) - 1,
+ offset=chunks[-1].offset,
+ )
+ sub_decode_path = decode_path + (str(len(chunks)),)
+ try:
+ chunk, v_tail = BitString().decode(
+ v,
+ offset=sub_offset,
+ decode_path=sub_decode_path,
+ leavemm=True,
+ ctx=ctx,
+ )
+ except TagMismatch:
+ raise DecodeError(
+ msg="expected BitString encoded chunk",
+ decode_path=sub_decode_path,
+ offset=sub_offset,
+ )
+ chunks.append(chunk)
+ sub_offset += chunk.tlvlen
+ vlen += chunk.tlvlen
+ v = v_tail
+ if len(chunks) == 0:
+ raise DecodeError(
+ msg="no chunks",
+ decode_path=decode_path,
+ offset=offset,
+ )
+ values = []
+ bit_len = 0
+ for chunk_i, chunk in enumerate(chunks[:-1]):
+ if chunk.bit_len % 8 != 0:
+ raise DecodeError(
+ msg="BitString chunk is not multiple of 8 bit",
+ decode_path=decode_path + (str(chunk_i),),
+ offset=chunk.offset,
+ )
+ values.append(bytes(chunk))
+ bit_len += chunk.bit_len
+ chunk_last = chunks[-1]
+ values.append(bytes(chunk_last))
+ bit_len += chunk_last.bit_len
+ obj = self.__class__(
+ value=(bit_len, b"".join(values)),
+ impl=self.tag,
+ expl=self._expl,
+ default=self.default,
+ optional=self.optional,
+ _specs=self.specs,
+ _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
+ )
+ obj.lenindef = lenindef
+ obj.bered = True
+ return obj, (v[EOC_LEN:] if lenindef else v)
+ raise TagMismatch(
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+
def __repr__(self):
return pp_console_row(next(self.pps()))
>>> OctetString(b"hell", bounds=(4, 4))
OCTET STRING 4 bytes 68656c6c
"""
- __slots__ = ("_bound_min", "_bound_max", "defined")
+ __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
tag_default = tag_encode(4)
asn1_type_name = "OCTET STRING"
if self._value is None:
self._value = default
self.defined = None
+ tag_klass, _, tag_num = tag_decode(self.tag)
+ self.tag_constructed = tag_encode(
+ klass=tag_klass,
+ form=TagFormConstructed,
+ num=tag_num,
+ )
def _value_sanitize(self, value):
if issubclass(value.__class__, OctetString):
self._value,
))
- def _decode(self, tlv, offset, decode_path, ctx, tag_only):
- try:
- t, _, lv = tag_strip(tlv)
- except DecodeError as err:
- raise err.__class__(
- msg=err.msg,
- klass=self.__class__,
- decode_path=decode_path,
- offset=offset,
- )
- if t != self.tag:
- raise TagMismatch(
- klass=self.__class__,
- decode_path=decode_path,
- offset=offset,
- )
- if tag_only:
- return
+ def _decode_chunk(self, lv, offset, decode_path, ctx):
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
)
return obj, tail
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+ try:
+ t, tlen, lv = tag_strip(tlv)
+ except DecodeError as err:
+ raise err.__class__(
+ msg=err.msg,
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ if t == self.tag:
+ if tag_only:
+ return
+ return self._decode_chunk(lv, offset, decode_path, ctx)
+ if t == self.tag_constructed:
+ if not ctx.get("bered", False):
+ raise DecodeError(
+ msg="unallowed BER constructed encoding",
+ decode_path=decode_path,
+ offset=offset,
+ )
+ if tag_only:
+ return
+ lenindef = False
+ try:
+ l, llen, v = len_decode(lv)
+ except LenIndefForm:
+ llen, l, v = 1, 0, lv[1:]
+ lenindef = True
+ except DecodeError as err:
+ raise err.__class__(
+ msg=err.msg,
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ if l > 0 and l > len(v):
+ raise NotEnoughData(
+ "encoded length is longer than data",
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ if not lenindef and l == 0:
+ raise NotEnoughData(
+ "zero length",
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ chunks = []
+ sub_offset = offset + tlen + llen
+ vlen = 0
+ while True:
+ if lenindef:
+ if v[:EOC_LEN].tobytes() == EOC:
+ break
+ else:
+ if vlen == l:
+ break
+ if vlen > l:
+ raise DecodeError(
+ msg="chunk out of bounds",
+ decode_path=len(chunks) - 1,
+ offset=chunks[-1].offset,
+ )
+ sub_decode_path = decode_path + (str(len(chunks)),)
+ try:
+ chunk, v_tail = OctetString().decode(
+ v,
+ offset=sub_offset,
+ decode_path=sub_decode_path,
+ leavemm=True,
+ ctx=ctx,
+ )
+ except TagMismatch:
+ raise DecodeError(
+ msg="expected OctetString encoded chunk",
+ decode_path=sub_decode_path,
+ offset=sub_offset,
+ )
+ chunks.append(chunk)
+ sub_offset += chunk.tlvlen
+ vlen += chunk.tlvlen
+ v = v_tail
+ if len(chunks) == 0:
+ raise DecodeError(
+ msg="no chunks",
+ decode_path=decode_path,
+ offset=offset,
+ )
+ try:
+ obj = self.__class__(
+ value=b"".join(bytes(chunk) for chunk in chunks),
+ bounds=(self._bound_min, self._bound_max),
+ impl=self.tag,
+ expl=self._expl,
+ default=self.default,
+ optional=self.optional,
+ _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
+ )
+ except DecodeError as err:
+ raise DecodeError(
+ msg=err.msg,
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ except BoundsError as err:
+ raise DecodeError(
+ msg=str(err),
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ obj.lenindef = lenindef
+ obj.bered = True
+ return obj, (v[EOC_LEN:] if lenindef else v)
+ raise TagMismatch(
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+
def __repr__(self):
return pp_console_row(next(self.pps()))
def _decode(self, tlv, offset, decode_path, ctx, tag_only):
try:
t, tlen, lv = tag_strip(tlv)
+ except DecodeError as err:
+ raise err.__class__(
+ msg=err.msg,
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ try:
l, llen, v = len_decode(lv)
+ except LenIndefForm as err:
+ if not ctx.get("bered", False):
+ raise err.__class__(
+ msg=err.msg,
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ llen, vlen, v = 1, 0, lv[1:]
+ sub_offset = offset + tlen + llen
+ chunk_i = 0
+ while True:
+ if v[:EOC_LEN].tobytes() == EOC:
+ tlvlen = tlen + llen + vlen + EOC_LEN
+ obj = self.__class__(
+ value=tlv[:tlvlen].tobytes(),
+ expl=self._expl,
+ optional=self.optional,
+ _decoded=(offset, 0, tlvlen),
+ )
+ obj.lenindef = True
+ obj.tag = t
+ return obj, v[EOC_LEN:]
+ else:
+ chunk, v = Any().decode(
+ v,
+ offset=sub_offset,
+ decode_path=decode_path + (str(chunk_i),),
+ leavemm=True,
+ ctx=ctx,
+ )
+ vlen += chunk.tlvlen
+ sub_offset += chunk.tlvlen
+ chunk_i += 1
except DecodeError as err:
raise err.__class__(
msg=err.msg,
)
if tag_only:
return
+ lenindef = False
try:
l, llen, v = len_decode(lv)
+ except LenIndefForm as err:
+ if not ctx.get("bered", False):
+ raise err.__class__(
+ msg=err.msg,
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ l, llen, v = 0, 1, lv[1:]
+ lenindef = True
except DecodeError as err:
raise err.__class__(
msg=err.msg,
decode_path=decode_path,
offset=offset,
)
- v, tail = v[:l], v[l:]
+ if not lenindef:
+ v, tail = v[:l], v[l:]
+ vlen = 0
sub_offset = offset + tlen + llen
values = {}
for name, spec in self.specs.items():
- if len(v) == 0 and spec.optional:
+ if spec.optional and (
+ (lenindef and v[:EOC_LEN].tobytes() == EOC) or
+ len(v) == 0
+ ):
continue
sub_decode_path = decode_path + (name,)
try:
)
value.defined = (defined_by, defined_value)
- sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
+ value_len = value.expl_tlvlen if value.expled else value.tlvlen
+ vlen += value_len
+ sub_offset += value_len
v = v_tail
if spec.default is not None and value == spec.default:
if ctx.get("strict_default_existence", False):
abs_decode_path(sub_decode_path[:-1], rel_path),
(value, defined),
))
- if len(v) > 0:
+ if lenindef:
+ if v[:EOC_LEN].tobytes() != EOC:
+ raise DecodeError(
+ "no EOC",
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ tail = v[EOC_LEN:]
+ vlen += EOC_LEN
+ elif len(v) > 0:
raise DecodeError(
"remaining data",
klass=self.__class__,
expl=self._expl,
default=self.default,
optional=self.optional,
- _decoded=(offset, llen, l),
+ _decoded=(offset, llen, vlen),
)
obj._value = values
+ obj.lenindef = lenindef
return obj, tail
def __repr__(self):
)
if tag_only:
return
+ lenindef = False
try:
l, llen, v = len_decode(lv)
+ except LenIndefForm as err:
+ if not ctx.get("bered", False):
+ raise err.__class__(
+ msg=err.msg,
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ l, llen, v = 0, 1, lv[1:]
+ lenindef = True
except DecodeError as err:
raise err.__class__(
msg=err.msg,
klass=self.__class__,
offset=offset,
)
- v, tail = v[:l], v[l:]
+ if not lenindef:
+ v, tail = v[:l], v[l:]
+ vlen = 0
sub_offset = offset + tlen + llen
values = {}
specs_items = self.specs.items
while len(v) > 0:
+ if lenindef and v[:EOC_LEN].tobytes() == EOC:
+ break
for name, spec in specs_items():
sub_decode_path = decode_path + (name,)
try:
decode_path=sub_decode_path,
ctx=ctx,
)
- sub_offset += (
- value.expl_tlvlen if value.expled else value.tlvlen
- )
+ value_len = value.expl_tlvlen if value.expled else value.tlvlen
+ 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
expl=self._expl,
default=self.default,
optional=self.optional,
- _decoded=(offset, llen, l),
+ _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
)
obj._value = values
- return obj, tail
+ if not obj.ready:
+ raise DecodeError(
+ msg="not all values are ready",
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ obj.lenindef = lenindef
+ return obj, (v[EOC_LEN:] if lenindef else tail)
class SequenceOf(Obj):
)
if tag_only:
return
+ lenindef = False
try:
l, llen, v = len_decode(lv)
+ except LenIndefForm as err:
+ if not ctx.get("bered", False):
+ raise err.__class__(
+ msg=err.msg,
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ l, llen, v = 0, 1, lv[1:]
+ lenindef = True
except DecodeError as err:
raise err.__class__(
msg=err.msg,
decode_path=decode_path,
offset=offset,
)
- v, tail = v[:l], v[l:]
+ if not lenindef:
+ v, tail = v[:l], v[l:]
+ vlen = 0
sub_offset = offset + tlen + llen
_value = []
spec = self.spec
while len(v) > 0:
+ if lenindef and v[:EOC_LEN].tobytes() == EOC:
+ break
value, v_tail = spec.decode(
v,
sub_offset,
decode_path=decode_path + (str(len(_value)),),
ctx=ctx,
)
- sub_offset += (value.expl_tlvlen if value.expled else value.tlvlen)
+ value_len = value.expl_tlvlen if value.expled else value.tlvlen
+ sub_offset += value_len
+ vlen += value_len
v = v_tail
_value.append(value)
obj = self.__class__(
expl=self._expl,
default=self.default,
optional=self.optional,
- _decoded=(offset, llen, l),
+ _decoded=(offset, llen, vlen),
)
- return obj, tail
+ obj.lenindef = lenindef
+ return obj, (v[EOC_LEN:] if lenindef else tail)
def __repr__(self):
return "%s[%s]" % (
pprinter = partial(pprint, big_blobs=True)
else:
schema, pprinter = generic_decoder()
- obj, tail = schema().decode(
- der,
- ctx=(
- None if args.defines_by_path is None else
- {"defines_by_path": obj_by_path(args.defines_by_path)}
- ),
- )
+ ctx = {"bered": True}
+ 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)
print(pprinter(
obj,
oids=oids,