``tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm``::
(
- (('parameters',), {
+ (("parameters",), {
id_ecPublicKey: ECParameters(),
id_GostR3410_2001: GostR34102001PublicKeyParameters(),
}),
- (('..', 'subjectPublicKey'), {
+ (("..", "subjectPublicKey"), {
id_rsaEncryption: RSAPublicKey(),
id_GostR3410_2001: OctetString(),
}),
* :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
* :py:class:`pyderasn.OctetString`
* :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
- ``Any``/``OctetString``-s
+ ``Any``/``BitString``/``OctetString``-s
When any of those fields is automatically decoded, then ``.defined``
attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
(
(
"content",
- decode_path_defby(id_signedData),
+ DecodePathDefBy(id_signedData),
"encapContentInfo",
"eContentType",
),
(
(
"content",
- decode_path_defby(id_signedData),
+ DecodePathDefBy(id_signedData),
"encapContentInfo",
"eContent",
- decode_path_defby(id_cct_PKIResponse),
+ DecodePathDefBy(id_cct_PKIResponse),
"controlSequence",
any,
"attrType",
),
))
-Pay attention for :py:func:`pyderasn.decode_path_defby` and ``any``.
+Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
First function is useful for path construction when some automatic
decoding is already done. ``any`` means literally any value it meet --
useful for SEQUENCE/SET OF-s.
from collections import OrderedDict
from datetime import datetime
from math import ceil
+from os import environ
+from string import digits
from six import add_metaclass
from six import binary_type
from six.moves import xrange as six_xrange
+try:
+ from termcolor import colored
+except ImportError:
+ def colored(what, *args):
+ return what
+
+
__all__ = (
"Any",
"BitString",
"Boolean",
"BoundsError",
"Choice",
- "decode_path_defby",
"DecodeError",
+ "DecodePathDefBy",
"Enumerated",
"GeneralizedTime",
"GeneralString",
c for c in (
"" if self.klass is None else self.klass.__name__,
(
- ("(%s)" % ".".join(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 _encode(self): # pragma: no cover
raise NotImplementedError()
- def _decode(self, tlv, offset, decode_path, ctx): # pragma: no cover
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
raise NotImplementedError()
def encode(self):
return raw
return b"".join((self._expl, len_encode(len(raw)), raw))
- def decode(self, data, offset=0, leavemm=False, decode_path=(), ctx=None):
+ def decode(
+ self,
+ data,
+ offset=0,
+ leavemm=False,
+ decode_path=(),
+ ctx=None,
+ tag_only=False,
+ ):
"""Decode the data
:param data: either binary or memoryview
:param bool leavemm: do we need to leave memoryview of remaining
data as is, or convert it to bytes otherwise
:param ctx: optional :ref:`context <ctx>` governing decoding process.
+ :param tag_only: decode only the tag, without length and contents
+ (used only in Choice and Set structures, trying to
+ determine if tag satisfies the scheme)
:returns: (Obj, remaining data)
"""
if ctx is None:
ctx = {}
tlv = memoryview(data)
if self._expl is None:
- obj, tail = self._decode(
+ result = self._decode(
tlv,
offset,
decode_path=decode_path,
ctx=ctx,
+ tag_only=tag_only,
)
+ if tag_only:
+ return
+ obj, tail = result
else:
try:
t, tlen, lv = tag_strip(tlv)
decode_path=decode_path,
offset=offset,
)
- obj, tail = self._decode(
+ 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
return obj, (tail if leavemm else tail.tobytes())
@property
return self.expl_tlen + self.expl_llen + self.expl_vlen
-def decode_path_defby(defined_by):
+class DecodePathDefBy(object):
"""DEFINED BY representation inside decode path
"""
- return "DEFINED BY (%s)" % defined_by
+ __slots__ = ("defined_by",)
+
+ def __init__(self, defined_by):
+ self.defined_by = defined_by
+
+ def __ne__(self, their):
+ return not(self == their)
+
+ def __eq__(self, their):
+ if not isinstance(their, self.__class__):
+ return False
+ return self.defined_by == their.defined_by
+
+ def __str__(self):
+ return "DEFINED BY " + str(self.defined_by)
+
+ def __repr__(self):
+ return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
########################################################################
)
-def pp_console_row(pp, oids=None, with_offsets=False, with_blob=True):
+def _colorize(what, colour, with_colours, attrs=("bold",)):
+ return colored(what, colour, attrs=attrs) if with_colours else what
+
+
+def pp_console_row(
+ pp,
+ oids=None,
+ with_offsets=False,
+ with_blob=True,
+ with_colours=False,
+):
cols = []
if with_offsets:
- cols.append("%5d%s [%d,%d,%4d]" % (
+ col = "%5d%s" % (
pp.offset,
(
" " if pp.expl_offset is None else
("-%d" % (pp.offset - pp.expl_offset))
),
- pp.tlen,
- pp.llen,
- pp.vlen,
- ))
+ )
+ cols.append(_colorize(col, "red", with_colours, ()))
+ col = "[%d,%d,%4d]" % (pp.tlen, pp.llen, pp.vlen)
+ cols.append(_colorize(col, "green", with_colours, ()))
if len(pp.decode_path) > 0:
cols.append(" ." * (len(pp.decode_path)))
- cols.append("%s:" % pp.decode_path[-1])
+ ent = pp.decode_path[-1]
+ if isinstance(ent, DecodePathDefBy):
+ cols.append(_colorize("DEFINED BY", "red", with_colours, ("reverse",)))
+ value = str(ent.defined_by)
+ if (
+ oids is not None and
+ ent.defined_by.asn1_type_name ==
+ ObjectIdentifier.asn1_type_name and
+ value in oids
+ ):
+ cols.append(_colorize("%s:" % oids[value], "green", with_colours))
+ else:
+ cols.append(_colorize("%s:" % value, "white", with_colours, ("reverse",)))
+ else:
+ cols.append(_colorize("%s:" % ent, "yellow", with_colours, ("reverse",)))
if pp.expl is not None:
klass, _, num = pp.expl
- cols.append("[%s%d] EXPLICIT" % (TagClassReprs[klass], num))
+ col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
+ cols.append(_colorize(col, "blue", with_colours))
if pp.impl is not None:
klass, _, num = pp.impl
- cols.append("[%s%d]" % (TagClassReprs[klass], num))
+ col = "[%s%d]" % (TagClassReprs[klass], num)
+ cols.append(_colorize(col, "blue", with_colours))
if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
- cols.append(pp.obj_name)
- cols.append(pp.asn1_type_name)
+ cols.append(_colorize(pp.obj_name, "magenta", with_colours))
+ cols.append(_colorize(pp.asn1_type_name, "cyan", with_colours))
if pp.value is not None:
value = pp.value
+ cols.append(_colorize(value, "white", with_colours, ("reverse",)))
if (
oids is not None and
pp.asn1_type_name == ObjectIdentifier.asn1_type_name and
value in oids
):
- value = "%s (%s)" % (oids[value], pp.value)
- cols.append(value)
+ cols.append(_colorize("(%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("OPTIONAL")
+ cols.append(_colorize("OPTIONAL", "red", with_colours))
if pp.default:
- cols.append("DEFAULT")
+ cols.append(_colorize("DEFAULT", "red", with_colours))
return " ".join(cols)
yield " ".join(cols + [", ".join(pp.blob)])
-def pprint(obj, oids=None, big_blobs=False):
+def pprint(obj, oids=None, big_blobs=False, with_colours=False):
"""Pretty print object
:param Obj obj: object you want to pretty print
:param big_blobs: if large binary objects are met (like OctetString
values), do we need to print them too, on separate
lines
+ :param with_colours: colourize output, if ``termcolor`` library
+ is available
"""
def _pprint_pps(pps):
for pp in pps:
oids=oids,
with_offsets=True,
with_blob=False,
+ with_colours=with_colours,
)
for row in pp_console_blob(pp):
yield row
else:
- yield pp_console_row(pp, oids=oids, with_offsets=True)
+ yield pp_console_row(
+ pp,
+ oids=oids,
+ with_offsets=True,
+ with_blob=True,
+ with_colours=with_colours,
+ )
else:
for row in _pprint_pps(pp):
yield row
(b"\xFF" if self._value else b"\x00"),
))
- def _decode(self, tlv, offset, decode_path, ctx):
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
try:
t, _, lv = tag_strip(tlv)
except DecodeError as err:
decode_path=decode_path,
offset=offset,
)
+ if tag_only:
+ return
try:
l, _, v = len_decode(lv)
except DecodeError as err:
break
return b"".join((self.tag, len_encode(len(octets)), octets))
- def _decode(self, tlv, offset, decode_path, ctx):
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
try:
t, _, lv = tag_strip(tlv)
except DecodeError as err:
decode_path=decode_path,
offset=offset,
)
+ if tag_only:
+ return
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
class KeyUsage(BitString):
schema = (
- ('digitalSignature', 0),
- ('nonRepudiation', 1),
- ('keyEncipherment', 2),
+ ("digitalSignature", 0),
+ ("nonRepudiation", 1),
+ ("keyEncipherment", 2),
)
- >>> b = KeyUsage(('keyEncipherment', 'nonRepudiation'))
+ >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
>>> b.named
['nonRepudiation', 'keyEncipherment']
octets,
))
- def _decode(self, tlv, offset, decode_path, ctx):
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
try:
t, _, lv = tag_strip(tlv)
except DecodeError as err:
decode_path=decode_path,
offset=offset,
)
+ if tag_only:
+ return
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
decode_path=decode_path,
offset=offset,
)
- if byte2int(v[-1:]) & ((1 << pad_size) - 1) != 0:
+ if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
raise DecodeError(
"invalid pad",
klass=self.__class__,
defined_by, defined = self.defined or (None, None)
if defined_by is not None:
yield defined.pps(
- decode_path=decode_path + (decode_path_defby(defined_by),)
+ decode_path=decode_path + (DecodePathDefBy(defined_by),)
)
self._value,
))
- def _decode(self, tlv, offset, decode_path, ctx):
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
try:
t, _, lv = tag_strip(tlv)
except DecodeError as err:
decode_path=decode_path,
offset=offset,
)
+ if tag_only:
+ return
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
optional=self.optional,
_decoded=(offset, llen, l),
)
+ 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),
defined_by, defined = self.defined or (None, None)
if defined_by is not None:
yield defined.pps(
- decode_path=decode_path + (decode_path_defby(defined_by),)
+ decode_path=decode_path + (DecodePathDefBy(defined_by),)
)
def _encode(self):
return self.tag + len_encode(0)
- def _decode(self, tlv, offset, decode_path, ctx):
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
try:
t, _, lv = tag_strip(tlv)
except DecodeError as err:
decode_path=decode_path,
offset=offset,
)
+ if tag_only:
+ return
try:
l, _, v = len_decode(lv)
except DecodeError as err:
v = b"".join(octets)
return b"".join((self.tag, len_encode(len(v)), v))
- def _decode(self, tlv, offset, decode_path, ctx):
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
try:
t, _, lv = tag_strip(tlv)
except DecodeError as err:
decode_path=decode_path,
offset=offset,
)
+ if tag_only:
+ return
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
>>> PrintableString("привет мир")
Traceback (most recent call last):
- UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
+ pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
>>> BMPString("ада", bounds=(2, 2))
Traceback (most recent call last):
value_raw = value
else:
raise InvalidValueType((self.__class__, text_type, binary_type))
- value_raw = (
- value_decoded.encode(self.encoding)
- if value_raw is None else value_raw
- )
- value_decoded = (
- value_raw.decode(self.encoding)
- if value_decoded is None else value_decoded
- )
+ try:
+ value_raw = (
+ value_decoded.encode(self.encoding)
+ if value_raw is None else value_raw
+ )
+ value_decoded = (
+ value_raw.decode(self.encoding)
+ if value_decoded is None else value_decoded
+ )
+ except (UnicodeEncodeError, UnicodeDecodeError) as err:
+ raise DecodeError(str(err))
if not self._bound_min <= len(value_decoded) <= self._bound_max:
raise BoundsError(
self._bound_min,
tag_default = tag_encode(18)
encoding = "ascii"
asn1_type_name = "NumericString"
+ allowable_chars = set(digits.encode("ascii"))
+
+ def _value_sanitize(self, value):
+ value = super(NumericString, self)._value_sanitize(value)
+ if not set(value) <= self.allowable_chars:
+ raise DecodeError("non-numeric value")
+ return value
class PrintableString(CommonString):
class GeneralName(Choice):
schema = (
- ('rfc822Name', IA5String(impl=tag_ctxp(1))),
- ('dNSName', IA5String(impl=tag_ctxp(2))),
+ ("rfc822Name", IA5String(impl=tag_ctxp(1))),
+ ("dNSName", IA5String(impl=tag_ctxp(2))),
)
>>> gn = GeneralName()
self._assert_ready()
return self._value[1].encode()
- def _decode(self, tlv, offset, decode_path, ctx):
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
for choice, spec in self.specs.items():
+ sub_decode_path = decode_path + (choice,)
try:
- value, tail = spec.decode(
+ spec.decode(
tlv,
offset=offset,
leavemm=True,
- decode_path=decode_path + (choice,),
+ decode_path=sub_decode_path,
ctx=ctx,
+ tag_only=True,
)
except TagMismatch:
continue
- obj = self.__class__(
- schema=self.specs,
- expl=self._expl,
- default=self.default,
- optional=self.optional,
- _decoded=(offset, 0, value.tlvlen),
+ break
+ else:
+ raise TagMismatch(
+ klass=self.__class__,
+ decode_path=decode_path,
+ offset=offset,
)
- obj._value = (choice, value)
- return obj, tail
- raise TagMismatch(
- klass=self.__class__,
- decode_path=decode_path,
+ if tag_only:
+ return
+ value, tail = spec.decode(
+ tlv,
offset=offset,
+ leavemm=True,
+ decode_path=sub_decode_path,
+ ctx=ctx,
+ )
+ obj = self.__class__(
+ schema=self.specs,
+ expl=self._expl,
+ default=self.default,
+ optional=self.optional,
+ _decoded=(offset, 0, value.tlvlen),
)
+ obj._value = (choice, value)
+ return obj, tail
def __repr__(self):
value = pp_console_row(next(self.pps()))
self._assert_ready()
return self._value
- def _decode(self, tlv, offset, decode_path, ctx):
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
try:
t, tlen, lv = tag_strip(tlv)
l, llen, v = len_decode(lv)
defined_by, defined = self.defined or (None, None)
if defined_by is not None:
yield defined.pps(
- decode_path=decode_path + (decode_path_defby(defined_by),)
+ decode_path=decode_path + (DecodePathDefBy(defined_by),)
)
pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
>>> ext["extnID"] = ObjectIdentifier("1.2.3")
- You can know if sequence is ready to be encoded:
+ You can determine if sequence is ready to be encoded:
>>> ext.ready
False
Assign ``None`` to remove value from sequence.
- You can know if value exists/set in the sequence and take its value:
+ You can set values in Sequence during its initialization:
+
+ >>> AlgorithmIdentifier((
+ ("algorithm", ObjectIdentifier("1.2.3")),
+ ("parameters", Any(Null()))
+ ))
+ AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.3, ANY 0500 OPTIONAL]
+
+ You can determine if value exists/set in the sequence and take its value:
>>> "extnID" in ext, "extnValue" in ext, "critical" in ext
(True, True, False)
)
self._value = {}
if value is not None:
- self._value = self._value_sanitize(value)
+ if issubclass(value.__class__, Sequence):
+ self._value = value._value
+ elif hasattr(value, "__iter__"):
+ for seq_key, seq_value in value:
+ self[seq_key] = seq_value
+ else:
+ raise InvalidValueType((Sequence,))
if default is not None:
- default_value = self._value_sanitize(default)
+ if not issubclass(default.__class__, Sequence):
+ raise InvalidValueType((Sequence,))
+ default_value = default._value
default_obj = self.__class__(impl=self.tag, expl=self._expl)
default_obj.specs = self.specs
default_obj._value = default_value
if value is None:
self._value = default_obj.copy()._value
- def _value_sanitize(self, value):
- if not issubclass(value.__class__, Sequence):
- raise InvalidValueType((Sequence,))
- return value._value
-
@property
def ready(self):
for name, spec in self.specs.items():
v = b"".join(self._encoded_values())
return b"".join((self.tag, len_encode(len(v)), v))
- def _decode(self, tlv, offset, decode_path, ctx):
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
try:
t, tlen, lv = tag_strip(tlv)
except DecodeError as err:
decode_path=decode_path,
offset=offset,
)
+ if tag_only:
+ return
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
for i, _value in enumerate(value):
sub_sub_decode_path = sub_decode_path + (
str(i),
- decode_path_defby(defined_by),
+ DecodePathDefBy(defined_by),
)
defined_value, defined_tail = defined_spec.decode(
memoryview(bytes(_value)),
if value.expled else (value.tlen + value.llen)
),
leavemm=True,
- decode_path=sub_decode_path + (decode_path_defby(defined_by),),
+ decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
ctx=ctx,
)
if len(defined_tail) > 0:
raise DecodeError(
"remaining data",
klass=self.__class__,
- decode_path=sub_decode_path + (decode_path_defby(defined_by),),
+ decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
offset=offset,
)
value.defined = (defined_by, defined_value)
v = b"".join(raws)
return b"".join((self.tag, len_encode(len(v)), v))
- def _decode(self, tlv, offset, decode_path, ctx):
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
try:
t, tlen, lv = tag_strip(tlv)
except DecodeError as err:
decode_path=decode_path,
offset=offset,
)
+ if tag_only:
+ return
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
specs_items = self.specs.items
while len(v) > 0:
for name, spec in specs_items():
+ sub_decode_path = decode_path + (name,)
try:
- value, v_tail = spec.decode(
+ spec.decode(
v,
sub_offset,
leavemm=True,
- decode_path=decode_path + (name,),
+ decode_path=sub_decode_path,
ctx=ctx,
+ tag_only=True,
)
except TagMismatch:
continue
- sub_offset += (
- value.expl_tlvlen if value.expled else value.tlvlen
- )
- 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
break
else:
raise TagMismatch(
decode_path=decode_path,
offset=offset,
)
+ value, v_tail = spec.decode(
+ v,
+ sub_offset,
+ leavemm=True,
+ decode_path=sub_decode_path,
+ ctx=ctx,
+ )
+ sub_offset += (
+ value.expl_tlvlen if value.expled else value.tlvlen
+ )
+ 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,
v = b"".join(self._encoded_values())
return b"".join((self.tag, len_encode(len(v)), v))
- def _decode(self, tlv, offset, decode_path, ctx):
+ def _decode(self, tlv, offset, decode_path, ctx, tag_only):
try:
t, tlen, lv = tag_strip(tlv)
except DecodeError as err:
decode_path=decode_path,
offset=offset,
)
+ if tag_only:
+ return
try:
l, llen, v = len_decode(lv)
except DecodeError as err:
__slots__ = ()
schema = choice
- def pprint_any(obj, oids=None):
+ def pprint_any(obj, oids=None, with_colours=False):
def _pprint_pps(pps):
for pp in pps:
if hasattr(pp, "_fields"):
oids=oids,
with_offsets=True,
with_blob=False,
+ with_colours=with_colours,
)
for row in pp_console_blob(pp):
yield row
{"defines_by_path": obj_by_path(args.defines_by_path)}
),
)
- print(pprinter(obj, oids=oids))
+ print(pprinter(
+ obj,
+ oids=oids,
+ with_colours=True if environ.get("NO_COLOR") is None else False,
+ ))
if tail != b"":
print("\nTrailing data: %s" % hexenc(tail))