#!/usr/bin/env python
# coding: utf-8
-# PyDERASN -- Python ASN.1 DER codec with abstract structures
+# PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
# Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
#
# This program is free software: you can redistribute it and/or modify
# You should have received a copy of the GNU Lesser General Public
# License along with this program. If not, see
# <http://www.gnu.org/licenses/>.
-"""Python ASN.1 DER codec with abstract structures
+"""Python ASN.1 DER/BER codec with abstract structures
-This library allows you to marshal and unmarshal various structures in
-ASN.1 DER format, like this:
+This library allows you to marshal various structures in ASN.1 DER
+format, unmarshal them in BER/CER/DER ones.
>>> i = Integer(123)
>>> raw = i.encode()
Pay attention that those values do **not** include anything related to
explicit tag. If you want to know information about it, then use:
-``expled`` (to know if explicit tag is set), ``expl_offset`` (it is
-lesser than ``offset``), ``expl_tlen``, ``expl_llen``, ``expl_vlen``
-(that actually equals to ordinary ``tlvlen``).
-When error occurs, then :py:exc:`pyderasn.DecodeError` is raised.
+* ``expled`` -- to know if explicit tag is set
+* ``expl_offset`` (it is lesser than ``offset``)
+* ``expl_tlen``,
+* ``expl_llen``
+* ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
+* ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
+ ``offset`` otherwise
+* ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
+ ``tlvlen`` otherwise
+
+When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
.. _ctx:
Currently available context options:
+* :ref:`bered <bered_ctx>`
* :ref:`defines_by_path <defines_by_path_ctx>`
* :ref:`strict_default_existence <strict_default_existence_ctx>`
decoding is already done. ``any`` means literally any value it meet --
useful for SEQUENCE/SET OF-s.
+.. _bered_ctx:
+
+BER encoding
+------------
+
+By default PyDERASN accepts only DER encoded data. It always encodes to
+DER. But you can optionally enable BER decoding with setting ``bered``
+:ref:`context <ctx>` argument to True. Indefinite lengths and
+constructed primitive types should be parsed successfully.
+
+* 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.
+* 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
+ contain it.
+* If object has an indefinite length encoded explicit tag, then
+ ``expl_lenindef`` is set to True.
+
+EOC (end-of-contents) token's length is taken in advance in object's
+value length.
+
Primitive types
---------------
____________
.. autoclass:: pyderasn.CommonString
+NumericString
+_____________
+.. autoclass:: pyderasn.NumericString
+
UTCTime
_______
.. autoclass:: pyderasn.UTCTime
.. autofunction:: pyderasn.tag_ctxp
.. autofunction:: pyderasn.tag_ctxc
.. autoclass:: pyderasn.Obj
+.. autoclass:: pyderasn.DecodeError
+ :members: __init__
+.. autoclass:: pyderasn.NotEnoughData
+.. autoclass:: pyderasn.LenIndefForm
+.. autoclass:: pyderasn.TagMismatch
+.. autoclass:: pyderasn.InvalidLength
+.. autoclass:: pyderasn.InvalidOID
+.. autoclass:: pyderasn.ObjUnknown
+.. autoclass:: pyderasn.ObjNotReady
+.. autoclass:: pyderasn.InvalidValueType
+.. autoclass:: pyderasn.BoundsError
"""
from codecs import getdecoder
"InvalidOID",
"InvalidValueType",
"ISO646String",
+ "LenIndefForm",
"NotEnoughData",
"Null",
"NumericString",
}
EOC = b"\x00\x00"
EOC_LEN = len(EOC)
+LENINDEF = b"\x80" # length indefinite mark
+LENINDEF_PP_CHAR = "I" if PY2 else "∞"
########################################################################
pass
-class LenIndefiniteForm(DecodeError):
+class LenIndefForm(DecodeError):
pass
def len_decode(data):
+ """Decode length
+
+ :returns: (decoded length, length's length, remaining data)
+ :raises LenIndefForm: if indefinite form encoding is met
+ """
if len(data) == 0:
raise NotEnoughData("no data at all")
first_octet = byte2int(data)
if octets_num + 1 > len(data):
raise NotEnoughData("encoded length is longer than data")
if octets_num == 0:
- raise LenIndefiniteForm()
+ raise LenIndefForm()
if byte2int(data[1:]) == 0:
raise DecodeError("leading zeros")
l = 0
"offset",
"llen",
"vlen",
- "lenindef",
"expl_lenindef",
+ "lenindef",
"bered",
)
self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
self._expl = getattr(self, "expl", None) if expl is None else expl
if self.tag != self.tag_default and self._expl is not None:
- raise ValueError(
- "implicit and explicit tags can not be set simultaneously"
- )
+ raise ValueError("implicit and explicit tags can not be set simultaneously")
if default is not None:
optional = True
self.optional = optional
self.offset, self.llen, self.vlen = _decoded
self.default = None
- self.lenindef = False
self.expl_lenindef = False
+ self.lenindef = False
self.bered = False
@property
)
try:
l, llen, v = len_decode(lv)
- except LenIndefiniteForm as err:
+ except LenIndefForm as err:
if not ctx.get("bered", False):
raise err.__class__(
msg=err.msg,
eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
if eoc_expected.tobytes() != EOC:
raise DecodeError(
- msg="no EOC",
+ "no EOC",
+ klass=self.__class__,
decode_path=decode_path,
offset=offset,
)
def expl_tlvlen(self):
return self.expl_tlen + self.expl_llen + self.expl_vlen
+ @property
+ def fulloffset(self):
+ return self.expl_offset if self.expled else self.offset
+
+ @property
+ def fulllen(self):
+ return self.expl_tlvlen if self.expled else self.tlvlen
+
+ def pps_lenindef(self, decode_path):
+ if self.lenindef:
+ yield _pp(
+ asn1_type_name="EOC",
+ obj_name="",
+ decode_path=decode_path,
+ offset=(
+ self.offset + self.tlvlen -
+ (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
+ ),
+ tlen=1,
+ llen=1,
+ vlen=0,
+ bered=True,
+ )
+ if self.expl_lenindef:
+ yield _pp(
+ asn1_type_name="EOC",
+ obj_name="EXPLICIT",
+ decode_path=decode_path,
+ offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
+ tlen=1,
+ llen=1,
+ vlen=0,
+ bered=True,
+ )
+
class DecodePathDefBy(object):
"""DEFINED BY representation inside decode path
"expl_tlen",
"expl_llen",
"expl_vlen",
+ "expl_lenindef",
+ "lenindef",
+ "bered",
))
expl_tlen=None,
expl_llen=None,
expl_vlen=None,
+ expl_lenindef=False,
+ lenindef=False,
+ bered=False,
):
return PP(
asn1_type_name,
expl_tlen,
expl_llen,
expl_vlen,
+ expl_lenindef,
+ lenindef,
+ bered,
)
):
cols = []
if with_offsets:
- col = "%5d%s" % (
+ col = "%5d%s%s" % (
pp.offset,
(
" " if pp.expl_offset is None else
("-%d" % (pp.offset - pp.expl_offset))
),
+ LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
)
cols.append(_colorize(col, "red", with_colours, ()))
- col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
- cols.append(_colorize(col, "green", with_colours, ()))
+ col = "[%d,%d,%4d]%s" % (
+ pp.tlen,
+ pp.llen,
+ pp.vlen,
+ LENINDEF_PP_CHAR if pp.lenindef else " "
+ )
+ col = _colorize(col, "green", with_colours, ())
+ cols.append(col)
if len(pp.decode_path) > 0:
cols.append(" ." * (len(pp.decode_path)))
ent = pp.decode_path[-1]
cols.append(_colorize(col, "blue", with_colours))
if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
cols.append(_colorize(pp.obj_name, "magenta", with_colours))
+ if pp.bered:
+ cols.append(_colorize("BER", "red", with_colours))
cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
if pp.value is not None:
value = pp.value
def pp_console_blob(pp):
- cols = [" " * len("XXXXXYY [X,X,XXXX]")]
+ cols = [" " * len("XXXXXYYZ [X,X,XXXX]Z")]
if len(pp.decode_path) > 0:
cols.append(" ." * (len(pp.decode_path) + 1))
if isinstance(pp.blob, binary_type):
expl_tlen=self.expl_tlen if self.expled else None,
expl_llen=self.expl_llen if self.expled else None,
expl_vlen=self.expl_vlen if self.expled else None,
+ expl_lenindef=self.expl_lenindef,
+ bered=self.bered,
)
+ for pp in self.pps_lenindef(decode_path):
+ yield pp
class Integer(Obj):
expl_tlen=self.expl_tlen if self.expled else None,
expl_llen=self.expl_llen if self.expled else None,
expl_vlen=self.expl_vlen if self.expled else None,
+ expl_lenindef=self.expl_lenindef,
)
+ for pp in self.pps_lenindef(decode_path):
+ yield pp
class BitString(Obj):
>>> b.bit_len
88
+ >>> BitString("'0A3B5F291CD'H")
+ BIT STRING 44 bits 0a3b5f291cd0
>>> b = BitString("'010110000000'B")
BIT STRING 12 bits 5800
>>> b.bit_len
['nonRepudiation', 'keyEncipherment']
>>> b.specs
{'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
+
+ .. note::
+
+ Pay attention that BIT STRING can be encoded both in primitive
+ and constructed forms. Decoder always checks constructed form tag
+ additionally to specified primitive one. If BER decoding is
+ :ref:`not enabled <bered_ctx>`, then decoder will fail, because
+ of DER restrictions.
"""
__slots__ = ("tag_constructed", "specs", "defined")
tag_default = tag_encode(3)
if isinstance(value, (string_types, binary_type)):
if (
isinstance(value, string_types) and
- value.startswith("'") and
- value.endswith("'B")
+ value.startswith("'")
):
- value = value[1:-2]
- if not set(value) <= set(("0", "1")):
- raise ValueError("B's coding contains unacceptable chars")
- return self._bits2octets(value)
- elif isinstance(value, binary_type):
+ if value.endswith("'B"):
+ value = value[1:-2]
+ if not set(value) <= set(("0", "1")):
+ raise ValueError("B's coding contains unacceptable chars")
+ return self._bits2octets(value)
+ elif value.endswith("'H"):
+ value = value[1:-2]
+ return (
+ len(value) * 4,
+ hexdec(value + ("" if len(value) % 2 == 0 else "0")),
+ )
+ if isinstance(value, binary_type):
return (len(value) * 8, value)
else:
- raise InvalidValueType((
- self.__class__,
- string_types,
- binary_type,
- ))
+ raise InvalidValueType((self.__class__, string_types, binary_type))
if isinstance(value, tuple):
if (
len(value) == 2 and
if t == self.tag_constructed:
if not ctx.get("bered", False):
raise DecodeError(
- msg="unallowed BER constructed encoding",
+ "unallowed BER constructed encoding",
+ klass=self.__class__,
decode_path=decode_path,
offset=offset,
)
lenindef = False
try:
l, llen, v = len_decode(lv)
- except LenIndefiniteForm:
+ except LenIndefForm:
llen, l, v = 1, 0, lv[1:]
lenindef = True
except DecodeError as err:
decode_path=decode_path,
offset=offset,
)
- if l > 0 and l > len(v):
+ if l > len(v):
raise NotEnoughData(
"encoded length is longer than data",
klass=self.__class__,
break
if vlen > l:
raise DecodeError(
- msg="chunk out of bounds",
- decode_path=len(chunks) - 1,
+ "chunk out of bounds",
+ klass=self.__class__,
+ decode_path=decode_path + (str(len(chunks) - 1),),
offset=chunks[-1].offset,
)
sub_decode_path = decode_path + (str(len(chunks)),)
)
except TagMismatch:
raise DecodeError(
- msg="expected BitString encoded chunk",
+ "expected BitString encoded chunk",
+ klass=self.__class__,
decode_path=sub_decode_path,
offset=sub_offset,
)
v = v_tail
if len(chunks) == 0:
raise DecodeError(
- msg="no chunks",
+ "no chunks",
+ klass=self.__class__,
decode_path=decode_path,
offset=offset,
)
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",
+ "BitString chunk is not multiple of 8 bits",
+ klass=self.__class__,
decode_path=decode_path + (str(chunk_i),),
offset=chunk.offset,
)
expl_tlen=self.expl_tlen if self.expled else None,
expl_llen=self.expl_llen if self.expled else None,
expl_vlen=self.expl_vlen if self.expled else None,
+ expl_lenindef=self.expl_lenindef,
+ lenindef=self.lenindef,
+ bered=self.bered,
)
defined_by, defined = self.defined or (None, None)
if defined_by is not None:
yield defined.pps(
decode_path=decode_path + (DecodePathDefBy(defined_by),)
)
+ for pp in self.pps_lenindef(decode_path):
+ yield pp
class OctetString(Obj):
pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
>>> OctetString(b"hell", bounds=(4, 4))
OCTET STRING 4 bytes 68656c6c
+
+ .. note::
+
+ Pay attention that OCTET STRING can be encoded both in primitive
+ and constructed forms. Decoder always checks constructed form tag
+ additionally to specified primitive one. If BER decoding is
+ :ref:`not enabled <bered_ctx>`, then decoder will fail, because
+ of DER restrictions.
"""
__slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
tag_default = tag_encode(4)
if t == self.tag_constructed:
if not ctx.get("bered", False):
raise DecodeError(
- msg="unallowed BER constructed encoding",
+ "unallowed BER constructed encoding",
+ klass=self.__class__,
decode_path=decode_path,
offset=offset,
)
lenindef = False
try:
l, llen, v = len_decode(lv)
- except LenIndefiniteForm:
+ except LenIndefForm:
llen, l, v = 1, 0, lv[1:]
lenindef = True
except DecodeError as err:
decode_path=decode_path,
offset=offset,
)
- if l > 0 and l > len(v):
+ if 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
break
if vlen > l:
raise DecodeError(
- msg="chunk out of bounds",
- decode_path=len(chunks) - 1,
+ "chunk out of bounds",
+ klass=self.__class__,
+ decode_path=decode_path + (str(len(chunks) - 1),),
offset=chunks[-1].offset,
)
sub_decode_path = decode_path + (str(len(chunks)),)
)
except TagMismatch:
raise DecodeError(
- msg="expected OctetString encoded chunk",
+ "expected OctetString encoded chunk",
+ klass=self.__class__,
decode_path=sub_decode_path,
offset=sub_offset,
)
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),
expl_tlen=self.expl_tlen if self.expled else None,
expl_llen=self.expl_llen if self.expled else None,
expl_vlen=self.expl_vlen if self.expled else None,
+ expl_lenindef=self.expl_lenindef,
+ lenindef=self.lenindef,
+ bered=self.bered,
)
defined_by, defined = self.defined or (None, None)
if defined_by is not None:
yield defined.pps(
decode_path=decode_path + (DecodePathDefBy(defined_by),)
)
+ for pp in self.pps_lenindef(decode_path):
+ yield pp
class Null(Obj):
expl_tlen=self.expl_tlen if self.expled else None,
expl_llen=self.expl_llen if self.expled else None,
expl_vlen=self.expl_vlen if self.expled else None,
+ expl_lenindef=self.expl_lenindef,
)
+ for pp in self.pps_lenindef(decode_path):
+ yield pp
class ObjectIdentifier(Obj):
expl_tlen=self.expl_tlen if self.expled else None,
expl_llen=self.expl_llen if self.expled else None,
expl_vlen=self.expl_vlen if self.expled else None,
+ expl_lenindef=self.expl_lenindef,
)
+ for pp in self.pps_lenindef(decode_path):
+ yield pp
class Enumerated(Integer):
tlen=self.tlen,
llen=self.llen,
vlen=self.vlen,
+ expl_offset=self.expl_offset if self.expled else None,
+ expl_tlen=self.expl_tlen if self.expled else None,
+ expl_llen=self.expl_llen if self.expled else None,
+ expl_vlen=self.expl_vlen if self.expled else None,
+ expl_lenindef=self.expl_lenindef,
)
+ for pp in self.pps_lenindef(decode_path):
+ yield pp
class UTF8String(CommonString):
class NumericString(CommonString):
+ """Numeric string
+
+ Its value is properly sanitized: only ASCII digits can be stored.
+ """
__slots__ = ()
tag_default = tag_encode(18)
encoding = "ascii"
if isinstance(value, datetime):
return value.strftime(self.fmt).encode("ascii")
if isinstance(value, binary_type):
- value_decoded = value.decode("ascii")
+ try:
+ value_decoded = value.decode("ascii")
+ except (UnicodeEncodeError, UnicodeDecodeError) as err:
+ raise DecodeError("invalid UTCTime encoding")
if len(value_decoded) == LEN_YYMMDDHHMMSSZ:
try:
datetime.strptime(value_decoded, self.fmt)
- except ValueError:
+ except (TypeError, ValueError):
raise DecodeError("invalid UTCTime format")
return value
else:
tlen=self.tlen,
llen=self.llen,
vlen=self.vlen,
+ expl_offset=self.expl_offset if self.expled else None,
+ expl_tlen=self.expl_tlen if self.expled else None,
+ expl_llen=self.expl_llen if self.expled else None,
+ expl_vlen=self.expl_vlen if self.expled else None,
+ expl_lenindef=self.expl_lenindef,
)
+ for pp in self.pps_lenindef(decode_path):
+ yield pp
class GeneralizedTime(UTCTime):
self.fmt_ms if value.microsecond > 0 else self.fmt
).encode("ascii")
if isinstance(value, binary_type):
- value_decoded = value.decode("ascii")
+ try:
+ value_decoded = value.decode("ascii")
+ except (UnicodeEncodeError, UnicodeDecodeError) as err:
+ raise DecodeError("invalid GeneralizedTime encoding")
if len(value_decoded) == LEN_YYYYMMDDHHMMSSZ:
try:
datetime.strptime(value_decoded, self.fmt)
- except ValueError:
+ except (TypeError, ValueError):
raise DecodeError(
"invalid GeneralizedTime (without ms) format",
)
elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
try:
datetime.strptime(value_decoded, self.fmt_ms)
- except ValueError:
+ except (TypeError, ValueError):
raise DecodeError(
"invalid GeneralizedTime (with ms) format",
)
expl=self._expl,
default=self.default,
optional=self.optional,
- _decoded=(offset, 0, value.tlvlen),
+ _decoded=(offset, 0, value.fulllen),
)
obj._value = (choice, value)
return obj, tail
tlen=self.tlen,
llen=self.llen,
vlen=self.vlen,
+ expl_lenindef=self.expl_lenindef,
)
if self.ready:
yield self.value.pps(decode_path=decode_path + (self.choice,))
+ for pp in self.pps_lenindef(decode_path):
+ yield pp
class PrimitiveTypes(Choice):
)
try:
l, llen, v = len_decode(lv)
- except LenIndefiniteForm as err:
+ except LenIndefForm as err:
if not ctx.get("bered", False):
raise err.__class__(
msg=err.msg,
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
+ while v[:EOC_LEN].tobytes() != EOC:
+ 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
+ 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:]
except DecodeError as err:
raise err.__class__(
msg=err.msg,
expl_tlen=self.expl_tlen if self.expled else None,
expl_llen=self.expl_llen if self.expled else None,
expl_vlen=self.expl_vlen if self.expled else None,
+ expl_lenindef=self.expl_lenindef,
+ lenindef=self.lenindef,
)
defined_by, defined = self.defined or (None, None)
if defined_by is not None:
yield defined.pps(
decode_path=decode_path + (DecodePathDefBy(defined_by),)
)
+ for pp in self.pps_lenindef(decode_path):
+ yield pp
########################################################################
("algorithm", ObjectIdentifier("1.2.3")),
("parameters", Any(Null()))
))
- AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
+ AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
You can determine if value exists/set in the sequence and take its value:
lenindef = False
try:
l, llen, v = len_decode(lv)
- except LenIndefiniteForm as err:
+ except LenIndefForm as err:
if not ctx.get("bered", False):
raise err.__class__(
msg=err.msg,
values = {}
for name, spec in self.specs.items():
if spec.optional and (
- (lenindef and v[:EOC_LEN].tobytes() == EOC) or
- len(v) == 0
+ (lenindef and v[:EOC_LEN].tobytes() == EOC) or
+ len(v) == 0
):
continue
sub_decode_path = decode_path + (name,)
continue
raise
- defined = get_def_by_path(ctx.get("defines", ()), sub_decode_path)
+ defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
if defined is not None:
defined_by, defined_spec = defined
if issubclass(value.__class__, SequenceOf):
)
value.defined = (defined_by, defined_value)
- value_len = value.expl_tlvlen if value.expled else value.tlvlen
+ value_len = value.fulllen
vlen += value_len
sub_offset += value_len
v = v_tail
for rel_path, schema in spec_defines:
defined = schema.get(value, None)
if defined is not None:
- ctx.setdefault("defines", []).append((
+ ctx.setdefault("_defines", []).append((
abs_decode_path(sub_decode_path[:-1], rel_path),
(value, defined),
))
_value = self._value.get(name)
if _value is None:
continue
- cols.append(repr(_value))
- return "%s[%s]" % (value, ", ".join(cols))
+ cols.append("%s: %s" % (name, repr(_value)))
+ return "%s[%s]" % (value, "; ".join(cols))
def pps(self, decode_path=()):
yield _pp(
expl_tlen=self.expl_tlen if self.expled else None,
expl_llen=self.expl_llen if self.expled else None,
expl_vlen=self.expl_vlen if self.expled else None,
+ expl_lenindef=self.expl_lenindef,
+ lenindef=self.lenindef,
)
for name in self.specs:
value = self._value.get(name)
if value is None:
continue
yield value.pps(decode_path=decode_path + (name,))
+ for pp in self.pps_lenindef(decode_path):
+ yield pp
class Set(Sequence):
lenindef = False
try:
l, llen, v = len_decode(lv)
- except LenIndefiniteForm as err:
+ except LenIndefForm as err:
if not ctx.get("bered", False):
raise err.__class__(
msg=err.msg,
decode_path=sub_decode_path,
ctx=ctx,
)
- value_len = value.expl_tlvlen if value.expled else value.tlvlen
+ value_len = value.fulllen
sub_offset += value_len
vlen += value_len
v = v_tail
_decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
)
obj._value = values
+ 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:]
+ obj.lenindef = True
if not obj.ready:
raise DecodeError(
- msg="not all values are ready",
+ "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)
+ return obj, tail
class SequenceOf(Obj):
lenindef = False
try:
l, llen, v = len_decode(lv)
- except LenIndefiniteForm as err:
+ except LenIndefForm as err:
if not ctx.get("bered", False):
raise err.__class__(
msg=err.msg,
decode_path=decode_path + (str(len(_value)),),
ctx=ctx,
)
- value_len = value.expl_tlvlen if value.expled else value.tlvlen
+ value_len = value.fulllen
sub_offset += value_len
vlen += value_len
v = v_tail
expl=self._expl,
default=self.default,
optional=self.optional,
- _decoded=(offset, llen, vlen),
+ _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
)
- obj.lenindef = lenindef
- return obj, (v[EOC_LEN:] if lenindef else tail)
+ if lenindef:
+ if v[:EOC_LEN].tobytes() != EOC:
+ raise DecodeError(
+ "no EOC",
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
+ obj.lenindef = True
+ tail = v[EOC_LEN:]
+ return obj, tail
def __repr__(self):
return "%s[%s]" % (
expl_tlen=self.expl_tlen if self.expled else None,
expl_llen=self.expl_llen if self.expled else None,
expl_vlen=self.expl_vlen if self.expled else None,
+ expl_lenindef=self.expl_lenindef,
+ lenindef=self.lenindef,
)
for i, value in enumerate(self._value):
yield value.pps(decode_path=decode_path + (str(i),))
+ for pp in self.pps_lenindef(decode_path):
+ yield pp
class SetOf(SequenceOf):
def main(): # pragma: no cover
import argparse
- parser = argparse.ArgumentParser(description="PyDERASN ASN.1 DER decoder")
+ parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
parser.add_argument(
"--skip",
type=int,
"--defines-by-path",
help="Python path to decoder's defines_by_path",
)
+ parser.add_argument(
+ "--nobered",
+ action='store_true',
+ help="Disallow BER encoding",
+ )
parser.add_argument(
"DERFile",
type=argparse.FileType("rb"),
pprinter = partial(pprint, big_blobs=True)
else:
schema, pprinter = generic_decoder()
- ctx = {"bered": True}
+ ctx = {"bered": not args.nobered}
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)