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``).
+
+* ``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.
``defines`` kwarg is a sequence. You can specify defined field
relatively or absolutely to current decode path. For example ``defines``
for AlgorithmIdentifier of X.509's
-``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
+``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
(
(("parameters",), {
When any of those fields is automatically decoded, then ``.defined``
attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
was defined, ``value`` contains corresponding decoded value. For example
-above, ``content_info["content"].defined == (id_signedData,
-signed_data)``.
+above, ``content_info["content"].defined == (id_signedData, signed_data)``.
.. _defines_by_path_ctx:
EOC = b"\x00\x00"
EOC_LEN = len(EOC)
LENINDEF = b"\x80" # length indefinite mark
-LENINDEF_PP_CHAR = "∞"
+LENINDEF_PP_CHAR = "I" if PY2 else "∞"
########################################################################
c for c in (
"" if self.klass is None else self.klass.__name__,
(
- ("(%s)" % ".".join(str(dp) for dp in self.decode_path))
+ ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
if len(self.decode_path) > 0 else ""
),
("(at %d)" % self.offset) if self.offset > 0 else "",
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(
)
-def _colorize(what, colour, with_colours, attrs=("bold",)):
+def _colourize(what, colour, with_colours, attrs=("bold",)):
return colored(what, colour, attrs=attrs) if with_colours else what
with_offsets=False,
with_blob=True,
with_colours=False,
+ with_decode_path=False,
+ decode_path_len_decrease=0,
):
cols = []
if with_offsets:
),
LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
)
- cols.append(_colorize(col, "red", with_colours, ()))
+ cols.append(_colourize(col, "red", 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, ())
+ col = _colourize(col, "green", with_colours, ())
cols.append(col)
- if len(pp.decode_path) > 0:
- cols.append(" ." * (len(pp.decode_path)))
+ decode_path_len = len(pp.decode_path) - decode_path_len_decrease
+ if decode_path_len > 0:
+ cols.append(" ." * decode_path_len)
ent = pp.decode_path[-1]
if isinstance(ent, DecodePathDefBy):
- cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
+ cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
value = str(ent.defined_by)
if (
oids is not None and
ObjectIdentifier.asn1_type_name and
value in oids
):
- cols.append(_colorize("%s:" % oids[value], "green", with_colours))
+ cols.append(_colourize("%s:" % oids[value], "green", with_colours))
else:
- cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
+ cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
else:
- cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
+ cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
if pp.expl is not None:
klass, _, num = pp.expl
col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
- cols.append(_colorize(col, "blue", with_colours))
+ cols.append(_colourize(col, "blue", with_colours))
if pp.impl is not None:
klass, _, num = pp.impl
col = "[%s%d]" % (TagClassReprs[klass], num)
- cols.append(_colorize(col, "blue", with_colours))
+ cols.append(_colourize(col, "blue", with_colours))
if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
- cols.append(_colorize(pp.obj_name, "magenta", with_colours))
+ cols.append(_colourize(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))
+ cols.append(_colourize("BER", "red", with_colours))
+ cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
if pp.value is not None:
value = pp.value
- cols.append(_colorize(value, "white", with_colours, ("reverse",)))
+ cols.append(_colourize(value, "white", with_colours, ("reverse",)))
if (
oids is not None and
pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
value in oids
):
- cols.append(_colorize("(%s)" % oids[value], "green", with_colours))
+ cols.append(_colourize("(%s)" % oids[value], "green", with_colours))
if with_blob:
if isinstance(pp.blob, binary_type):
cols.append(hexenc(pp.blob))
elif isinstance(pp.blob, tuple):
cols.append(", ".join(pp.blob))
if pp.optional:
- cols.append(_colorize("OPTIONAL", "red", with_colours))
+ cols.append(_colourize("OPTIONAL", "red", with_colours))
if pp.default:
- cols.append(_colorize("DEFAULT", "red", with_colours))
+ cols.append(_colourize("DEFAULT", "red", with_colours))
+ if with_decode_path:
+ cols.append(_colourize(
+ "[%s]" % ":".join(str(p) for p in pp.decode_path),
+ "grey",
+ with_colours,
+ ))
return " ".join(cols)
-def pp_console_blob(pp):
+def pp_console_blob(pp, decode_path_len_decrease=0):
cols = [" " * len("XXXXXYYZ [X,X,XXXX]Z")]
- if len(pp.decode_path) > 0:
- cols.append(" ." * (len(pp.decode_path) + 1))
+ decode_path_len = len(pp.decode_path) - decode_path_len_decrease
+ if decode_path_len > 0:
+ cols.append(" ." * (decode_path_len + 1))
if isinstance(pp.blob, binary_type):
blob = hexenc(pp.blob).upper()
for i in range(0, len(blob), 32):
yield " ".join(cols + [", ".join(pp.blob)])
-def pprint(obj, oids=None, big_blobs=False, with_colours=False):
+def pprint(
+ obj,
+ oids=None,
+ big_blobs=False,
+ with_colours=False,
+ with_decode_path=False,
+ decode_path_only=(),
+):
"""Pretty print object
:param Obj obj: object you want to pretty print
lines
:param with_colours: colourize output, if ``termcolor`` library
is available
+ :param with_decode_path: print decode path
+ :param decode_path_only: print only that specified decode path
"""
def _pprint_pps(pps):
for pp in pps:
if hasattr(pp, "_fields"):
+ if (
+ decode_path_only != () and
+ tuple(
+ str(p) for p in pp.decode_path[:len(decode_path_only)]
+ ) != decode_path_only
+ ):
+ continue
if big_blobs:
yield pp_console_row(
pp,
with_offsets=True,
with_blob=False,
with_colours=with_colours,
+ with_decode_path=with_decode_path,
+ decode_path_len_decrease=len(decode_path_only),
)
- for row in pp_console_blob(pp):
+ for row in pp_console_blob(
+ pp,
+ decode_path_len_decrease=len(decode_path_only),
+ ):
yield row
else:
yield pp_console_row(
with_offsets=True,
with_blob=True,
with_colours=with_colours,
+ with_decode_path=with_decode_path,
+ decode_path_len_decrease=len(decode_path_only),
)
else:
for row in _pprint_pps(pp):
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:
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
("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:
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(
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
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
_value.append(value)
- obj = self.__class__(
- value=_value,
- schema=spec,
- 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)),
- )
+ try:
+ obj = self.__class__(
+ value=_value,
+ schema=spec,
+ 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 BoundsError as err:
+ raise DecodeError(
+ msg=str(err),
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
+ )
if lenindef:
if v[:EOC_LEN].tobytes() != EOC:
raise DecodeError(
__slots__ = ()
schema = choice
- def pprint_any(obj, oids=None, with_colours=False):
+ def pprint_any(
+ obj,
+ oids=None,
+ with_colours=False,
+ with_decode_path=False,
+ decode_path_only=(),
+ ):
def _pprint_pps(pps):
for pp in pps:
if hasattr(pp, "_fields"):
+ if (
+ decode_path_only != () and
+ pp.decode_path[:len(decode_path_only)] != decode_path_only
+ ):
+ continue
if pp.asn1_type_name == Choice.asn1_type_name:
continue
pp_kwargs = pp._asdict()
with_offsets=True,
with_blob=False,
with_colours=with_colours,
+ with_decode_path=with_decode_path,
+ decode_path_len_decrease=len(decode_path_only),
)
- for row in pp_console_blob(pp):
+ for row in pp_console_blob(
+ pp,
+ decode_path_len_decrease=len(decode_path_only),
+ ):
yield row
else:
for row in _pprint_pps(pp):
action='store_true',
help="Disallow BER encoding",
)
+ parser.add_argument(
+ "--print-decode-path",
+ action='store_true',
+ help="Print decode paths",
+ )
+ parser.add_argument(
+ "--decode-path-only",
+ help="Print only specified decode path",
+ )
parser.add_argument(
"DERFile",
type=argparse.FileType("rb"),
obj,
oids=oids,
with_colours=True if environ.get("NO_COLOR") is None else False,
+ with_decode_path=args.print_decode_path,
+ decode_path_only=(
+ () if args.decode_path_only is None else
+ tuple(args.decode_path_only.split(":"))
+ ),
))
if tail != b"":
print("\nTrailing data: %s" % hexenc(tail))