# pylint: disable=line-too-long,superfluous-parens,protected-access,too-many-lines
# pylint: disable=too-many-return-statements,too-many-branches,too-many-statements
# PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures
-# Copyright (C) 2017-2021 Sergey Matveev <stargrave@stargrave.org>
+# Copyright (C) 2017-2022 Sergey Matveev <stargrave@stargrave.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
* :ref:`bered <bered_ctx>`
* :ref:`defines_by_path <defines_by_path_ctx>`
* :ref:`evgen_mode_upto <evgen_mode_upto_ctx>`
+* :ref:`keep_memoryview <keep_memoryview_ctx>`
.. _pprinting:
raw = file_mmaped(fd)
obj = Something.decode(raw)
-.. warning::
-
- mmap-ed files in Python2.7 does not implement buffer protocol, so
- memoryview won't work on them.
-
.. warning::
mmap maps the **whole** file. So it plays no role if you seek-ed it
page cache used for mmaps. It can take twice the necessary size in
the memory: both in page cache and ZFS ARC.
+.. _keep_memoryview_ctx:
+
+That read-only memoryview could be safe to be used as a value inside
+decoded :py:class:`pyderasn.OctetString` and :py:class:`pyderasn.Any`
+objects. You can enable that by setting `"keep_memoryview": True` in
+:ref:`decode context <ctx>`. No OCTET STRING and ANY values will be
+copied to memory. Of course that works only in DER encoding, where the
+value is continuously encoded.
+
CER encoding
____________
:py:meth:`pyderasn.Obj.encode_cer` method, providing the writer where
encoded data will flow::
- opener = io.open if PY2 else open
- with opener("result", "wb") as fd:
+ with open("result", "wb") as fd:
obj.encode_cer(fd.write)
::
::
- opener = io.open if PY2 else open
- with opener("result", "wb") as fd:
+ with open("result", "wb") as fd:
obj.encode2nd(fd.write, iter(state))
.. warning::
UTCTime
_______
.. autoclass:: pyderasn.UTCTime
- :members: __init__, todatetime
+ :members: __init__, todatetime, totzdatetime
GeneralizedTime
_______________
.. autoclass:: pyderasn.GeneralizedTime
- :members: __init__, todatetime
+ :members: __init__, todatetime, totzdatetime
Special types
-------------
"""
from array import array
-from codecs import getdecoder
-from codecs import getencoder
from collections import namedtuple
from collections import OrderedDict
from copy import copy
from operator import attrgetter
from string import ascii_letters
from string import digits
+from struct import Struct as struct_Struct
from sys import maxsize as sys_maxsize
from sys import version_info
from unicodedata import category as unicat
-from six import add_metaclass
-from six import binary_type
-from six import byte2int
-from six import indexbytes
-from six import int2byte
-from six import integer_types
-from six import iterbytes
-from six import iteritems
-from six import itervalues
-from six import PY2
-from six import string_types
-from six import text_type
-from six import unichr as six_unichr
-from six.moves import xrange as six_xrange
-
-
try:
from termcolor import colored
except ImportError: # pragma: no cover
def colored(what, *args, **kwargs):
return what
-__version__ = "8.2"
+try:
+ from dateutil.tz import UTC as tzUTC
+except ImportError: # pragma: no cover
+ tzUTC = "missing"
+
+
+__version__ = "9.3"
__all__ = (
"agg_octet_string",
EOC = b"\x00\x00"
EOC_LEN = len(EOC)
LENINDEF = b"\x80" # length indefinite mark
-LENINDEF_PP_CHAR = "I" if PY2 else "∞"
+LENINDEF_PP_CHAR = "∞"
NAMEDTUPLE_KWARGS = {} if version_info < (3, 6) else {"module": __name__}
SET01 = frozenset("01")
DECIMALS = frozenset(digits)
DECIMAL_SIGNS = ".,"
-NEXT_ATTR_NAME = "next" if PY2 else "__next__"
+NEXT_ATTR_NAME = "__next__"
def file_mmaped(fd):
.. warning::
- It is known to work under neither Python 2.x nor Windows.
+ It does not work under Windows.
"""
import mmap
return memoryview(mmap.mmap(fd.fileno(), length=0, prot=mmap.PROT_READ))
decoding process has passed
:param int offset: binary offset where failure happened
"""
- super(DecodeError, self).__init__()
+ super().__init__()
self.msg = msg
self.klass = klass
self.decode_path = decode_path
class ExceedingData(ASN1Error):
def __init__(self, nbytes):
- super(ExceedingData, self).__init__()
+ super().__init__()
self.nbytes = nbytes
def __str__(self):
class ObjUnknown(ASN1Error):
def __init__(self, name):
- super(ObjUnknown, self).__init__()
+ super().__init__()
self.name = name
def __str__(self):
class ObjNotReady(ASN1Error):
def __init__(self, name):
- super(ObjNotReady, self).__init__()
+ super().__init__()
self.name = name
def __str__(self):
class InvalidValueType(ASN1Error):
def __init__(self, expected_types):
- super(InvalidValueType, self).__init__()
+ super().__init__()
self.expected_types = expected_types
def __str__(self):
class BoundsError(ASN1Error):
def __init__(self, bound_min, value, bound_max):
- super(BoundsError, self).__init__()
+ super().__init__()
self.bound_min = bound_min
self.value = value
self.bound_max = bound_max
# Basic coders
########################################################################
-_hexdecoder = getdecoder("hex")
-_hexencoder = getencoder("hex")
-
-
def hexdec(data):
"""Binary data to hexadecimal string convert
"""
- return _hexdecoder(data)[0]
+ return bytes.fromhex(data)
def hexenc(data):
"""Hexadecimal string to binary data convert
"""
- return _hexencoder(data)[0].decode("ascii")
+ return data.hex()
def int_bytes_len(num, byte_len=8):
return bytes(octets)
+int2byte = struct_Struct(">B").pack
+
+
def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
"""Encode tag to binary form
It returns tuple with three integers, as
:py:func:`pyderasn.tag_encode` accepts.
"""
- first_octet = byte2int(tag)
+ first_octet = tag[0]
klass = first_octet & 0xC0
form = first_octet & 0x20
if first_octet & 0x1F < 0x1F:
return (klass, form, first_octet & 0x1F)
num = 0
- for octet in iterbytes(tag[1:]):
+ for octet in tag[1:]:
num <<= 7
num |= octet & 0x7F
return (klass, form, num)
"""
if len(data) == 0:
raise NotEnoughData("no data at all")
- if byte2int(data) & 0x1F < 31:
+ if data[0] & 0x1F < 31:
return data[:1], 1, data[1:]
i = 0
while True:
i += 1
if i == len(data):
raise DecodeError("unfinished tag")
- if indexbytes(data, i) & 0x80 == 0:
+ if data[i] & 0x80 == 0:
break
- if i == 1 and indexbytes(data, 1) < 0x1F:
+ if i == 1 and data[1] < 0x1F:
raise DecodeError("unexpected long form")
- if i > 1 and indexbytes(data, 1) & 0x7F == 0:
+ if i > 1 and data[1] & 0x7F == 0:
raise DecodeError("leading zero byte in tag value")
i += 1
return data[:i], i, data[i:]
return int2byte(l)
octets = bytearray(int_bytes_len(l) + 1)
octets[0] = 0x80 | (len(octets) - 1)
- for i in six_xrange(len(octets) - 1, 0, -1):
+ for i in range(len(octets) - 1, 0, -1):
octets[i] = l & 0xFF
l >>= 8
return bytes(octets)
"""
if len(data) == 0:
raise NotEnoughData("no data at all")
- first_octet = byte2int(data)
+ first_octet = data[0]
if first_octet & 0x80 == 0:
return first_octet, 1, data[1:]
octets_num = first_octet & 0x7F
raise NotEnoughData("encoded length is longer than data")
if octets_num == 0:
raise LenIndefForm()
- if byte2int(data[1:]) == 0:
+ if data[1] == 0:
raise DecodeError("leading zeros")
l = 0
- for v in iterbytes(data[1:1 + octets_num]):
+ for v in data[1:1 + octets_num]:
l = (l << 8) | v
if l <= 127:
raise DecodeError("long form instead of short one")
class AutoAddSlots(type):
def __new__(cls, name, bases, _dict):
_dict["__slots__"] = _dict.get("__slots__", ())
- return type.__new__(cls, name, bases, _dict)
+ return super().__new__(cls, name, bases, _dict)
BasicState = namedtuple("BasicState", (
), **NAMEDTUPLE_KWARGS)
-@add_metaclass(AutoAddSlots)
-class Obj(object):
+class Obj(metaclass=AutoAddSlots):
"""Common ASN.1 object class
All ASN.1 types are inherited from it. It has metaclass that
return self.tlen + self.llen + self.vlen
def __str__(self): # pragma: no cover
- return self.__bytes__() if PY2 else self.__unicode__()
+ return self.__unicode__()
def __ne__(self, their):
return not(self == their)
return buf.getvalue()
-class DecodePathDefBy(object):
+class DecodePathDefBy:
"""DEFINED BY representation inside decode path
"""
__slots__ = ("defined_by",)
def colonize_hex(hexed):
"""Separate hexadecimal string with colons
"""
- return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
+ return ":".join(hexed[i:i + 2] for i in range(0, len(hexed), 2))
def find_oid_name(asn1_type_name, oid_maps, value):
"(%s)" % colonize_hex(pp.obj.tohex()), "green", with_colours,
))
if with_blob:
- if pp.blob.__class__ == binary_type:
+ if pp.blob.__class__ == bytes:
cols.append(hexenc(pp.blob))
elif pp.blob.__class__ == tuple:
cols.append(", ".join(pp.blob))
decode_path_len = len(pp.decode_path) - decode_path_len_decrease
if decode_path_len > 0:
cols.append(" ." * (decode_path_len + 1))
- if pp.blob.__class__ == binary_type:
+ if pp.blob.__class__ == bytes:
blob = hexenc(pp.blob).upper()
- for i in six_xrange(0, len(blob), 32):
+ for i in range(0, len(blob), 32):
chunk = blob[i:i + 32]
yield " ".join(cols + [colonize_hex(chunk)])
elif pp.blob.__class__ == tuple:
:param default: set default value. Type same as in ``value``
:param bool optional: is object ``OPTIONAL`` in sequence
"""
- super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
+ super().__init__(impl, expl, default, optional, _decoded)
self._value = None if value is None else self._value_sanitize(value)
if default is not None:
default = self._value_sanitize(default)
)
def __setstate__(self, state):
- super(Boolean, self).__setstate__(state)
+ super().__setstate__(state)
self._value = state.value
def __nonzero__(self):
decode_path=decode_path,
offset=offset,
)
- first_octet = byte2int(v)
+ first_octet = v[0]
ber_encoded = False
if first_octet == 0:
value = False
:param default: set default value. Type same as in ``value``
:param bool optional: is object ``OPTIONAL`` in sequence
"""
- super(Integer, self).__init__(impl, expl, default, optional, _decoded)
+ super().__init__(impl, expl, default, optional, _decoded)
self._value = value
specs = getattr(self, "schema", {}) if _specs is None else _specs
self.specs = specs if specs.__class__ == dict else dict(specs)
self._value = default
def _value_sanitize(self, value):
- if isinstance(value, integer_types):
+ if isinstance(value, int):
pass
elif issubclass(value.__class__, Integer):
value = value._value
)
def __setstate__(self, state):
- super(Integer, self).__setstate__(state)
+ super().__setstate__(state)
self.specs = state.specs
self._value = state.value
self._bound_min = state.bound_min
)))
def __eq__(self, their):
- if isinstance(their, integer_types):
+ if isinstance(their, int):
return self._value == their
if not issubclass(their.__class__, Integer):
return False
def named(self):
"""Return named representation (if exists) of the value
"""
- for name, value in iteritems(self.specs):
+ for name, value in self.specs.items():
if value == self._value:
return name
return None
def _encode_payload(self):
self._assert_ready()
value = self._value
- if PY2:
- if value == 0:
- octets = bytearray([0])
- elif value < 0:
- value = -value
- value -= 1
- octets = bytearray()
- while value > 0:
- octets.append((value & 0xFF) ^ 0xFF)
- value >>= 8
- if len(octets) == 0 or octets[-1] & 0x80 == 0:
- octets.append(0xFF)
+ bytes_len = ceil(value.bit_length() / 8) or 1
+ while True:
+ try:
+ octets = value.to_bytes(bytes_len, byteorder="big", signed=True)
+ except OverflowError:
+ bytes_len += 1
else:
- octets = bytearray()
- while value > 0:
- octets.append(value & 0xFF)
- value >>= 8
- if octets[-1] & 0x80 > 0:
- octets.append(0x00)
- octets.reverse()
- octets = bytes(octets)
- else:
- bytes_len = ceil(value.bit_length() / 8) or 1
- while True:
- try:
- octets = value.to_bytes(
- bytes_len,
- byteorder="big",
- signed=True,
- )
- except OverflowError:
- bytes_len += 1
- else:
- break
+ break
return octets
def _encode(self):
offset=offset,
)
v, tail = v[:l], v[l:]
- first_octet = byte2int(v)
+ first_octet = v[0]
if l > 1:
- second_octet = byte2int(v[1:])
+ second_octet = v[1]
if (
((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
((first_octet == 0xFF) and (second_octet & 0x80 != 0))
decode_path=decode_path,
offset=offset,
)
- if PY2:
- value = 0
- if first_octet & 0x80 > 0:
- octets = bytearray()
- for octet in bytearray(v):
- octets.append(octet ^ 0xFF)
- for octet in octets:
- value = (value << 8) | octet
- value += 1
- value = -value
- else:
- for octet in bytearray(v):
- value = (value << 8) | octet
- else:
- value = int.from_bytes(v, byteorder="big", signed=True)
+ value = int.from_bytes(v, byteorder="big", signed=True)
try:
obj = self.__class__(
value=value,
:param default: set default value. Type same as in ``value``
:param bool optional: is object ``OPTIONAL`` in sequence
"""
- super(BitString, self).__init__(impl, expl, default, optional, _decoded)
+ super().__init__(impl, expl, default, optional, _decoded)
specs = getattr(self, "schema", {}) if _specs is None else _specs
self.specs = specs if specs.__class__ == dict else dict(specs)
self._value = None if value is None else self._value_sanitize(value)
bit_len = len(bits)
bits += "0" * ((8 - (bit_len % 8)) % 8)
octets = bytearray(len(bits) // 8)
- for i in six_xrange(len(octets)):
+ for i in range(len(octets)):
octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
return bit_len, bytes(octets)
def _value_sanitize(self, value):
- if isinstance(value, (string_types, binary_type)):
+ if isinstance(value, (str, bytes)):
if (
- isinstance(value, string_types) and
+ isinstance(value, str) and
value.startswith("'")
):
if value.endswith("'B"):
len(value) * 4,
hexdec(value + ("" if len(value) % 2 == 0 else "0")),
)
- if value.__class__ == binary_type:
+ if value.__class__ == bytes:
return (len(value) * 8, value)
- raise InvalidValueType((self.__class__, string_types, binary_type))
+ raise InvalidValueType((self.__class__, str, bytes))
if value.__class__ == tuple:
if (
len(value) == 2 and
- isinstance(value[0], integer_types) and
- value[1].__class__ == binary_type
+ isinstance(value[0], int) and
+ value[1].__class__ == bytes
):
return value
bits = []
bits = frozenset(bits)
return self._bits2octets("".join(
("1" if bit in bits else "0")
- for bit in six_xrange(max(bits) + 1)
+ for bit in range(max(bits) + 1)
))
if issubclass(value.__class__, BitString):
return value._value
- raise InvalidValueType((self.__class__, binary_type, string_types))
+ raise InvalidValueType((self.__class__, bytes, str))
@property
def ready(self):
)
def __setstate__(self, state):
- super(BitString, self).__setstate__(state)
+ super().__setstate__(state)
self.specs = state.specs
self._value = state.value
self.tag_constructed = state.tag_constructed
def __iter__(self):
self._assert_ready()
- for i in six_xrange(self._value[0]):
+ for i in range(self._value[0]):
yield self[i]
@property
:returns: [str(name), ...]
"""
- return [name for name, bit in iteritems(self.specs) if self[bit]]
+ return [name for name, bit in self.specs.items() if self[bit]]
def __call__(
self,
bit_len, octets = self._value
if key >= bit_len:
return False
- return (
- byte2int(memoryview(octets)[key // 8:]) >>
- (7 - (key % 8))
- ) & 1 == 1
- if isinstance(key, string_types):
+ return memoryview(octets)[key // 8] >> (7 - (key % 8)) & 1 == 1
+ if isinstance(key, str):
value = self.specs.get(key)
if value is None:
raise ObjUnknown("BitString value: %s" % key)
return
write_full(writer, self.tag_constructed)
write_full(writer, LENINDEF)
- for offset in six_xrange(0, (len(octets) // 999) * 999, 999):
+ for offset in range(0, (len(octets) // 999) * 999, 999):
write_full(writer, b"".join((
BitString.tag_default,
LEN1K,
- int2byte(0),
+ b"\x00",
octets[offset:offset + 999],
)))
tail = octets[offset + 999:]
decode_path=decode_path,
offset=offset,
)
- pad_size = byte2int(v)
+ pad_size = v[0]
if l == 1 and pad_size != 0:
raise DecodeError(
"invalid empty value",
decode_path=decode_path,
offset=offset,
)
- if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
+ if v[l - 1] & ((1 << pad_size) - 1) != 0:
raise DecodeError(
"invalid pad",
klass=self.__class__,
tag_default = tag_encode(4)
asn1_type_name = "OCTET STRING"
evgen_mode_skip_value = True
+ memoryview_safe = True
def __init__(
self,
:param default: set default value. Type same as in ``value``
:param bool optional: is object ``OPTIONAL`` in sequence
"""
- super(OctetString, self).__init__(impl, expl, default, optional, _decoded)
+ super().__init__(impl, expl, default, optional, _decoded)
self._value = value
self._bound_min, self._bound_max = getattr(
self,
)
def _value_sanitize(self, value):
- if value.__class__ == binary_type or value.__class__ == memoryview:
+ if value.__class__ == bytes or value.__class__ == memoryview:
pass
elif issubclass(value.__class__, OctetString):
value = value._value
)
def __setstate__(self, state):
- super(OctetString, self).__setstate__(state)
+ super().__setstate__(state)
self._value = state.value
self._bound_min = state.bound_min
self._bound_max = state.bound_max
self._assert_ready()
return bytes(self._value)
+ def memoryview(self):
+ self._assert_ready()
+ return memoryview(self._value)
+
def __eq__(self, their):
- if their.__class__ == binary_type:
+ if their.__class__ == bytes:
return self._value == their
if not issubclass(their.__class__, OctetString):
return False
return
write_full(writer, self.tag_constructed)
write_full(writer, LENINDEF)
- for offset in six_xrange(0, (len(octets) // 1000) * 1000, 1000):
+ for offset in range(0, (len(octets) // 1000) * 1000, 1000):
write_full(writer, b"".join((
OctetString.tag_default,
LEN1K,
decode_path=decode_path,
offset=offset,
)
+ if evgen_mode and self.evgen_mode_skip_value:
+ value = None
+ elif self.memoryview_safe and ctx.get("keep_memoryview", False):
+ value = v
+ else:
+ value = v.tobytes()
try:
obj = self.__class__(
- value=(
- None if (evgen_mode and self.evgen_mode_skip_value)
- else v.tobytes()
- ),
+ value=value,
bounds=(self._bound_min, self._bound_max),
impl=self.tag,
expl=self._expl,
:param bytes expl: override default tag with ``EXPLICIT`` one
:param bool optional: is object ``OPTIONAL`` in sequence
"""
- super(Null, self).__init__(impl, expl, None, optional, _decoded)
+ super().__init__(impl, expl, None, optional, _decoded)
self.default = None
@property
:param default: set default value. Type same as in ``value``
:param bool optional: is object ``OPTIONAL`` in sequence
"""
- super(ObjectIdentifier, self).__init__(impl, expl, default, optional, _decoded)
+ super().__init__(impl, expl, default, optional, _decoded)
self._value = value
if value is not None:
self._value = self._value_sanitize(value)
def _value_sanitize(self, value):
if issubclass(value.__class__, ObjectIdentifier):
return value._value
- if isinstance(value, string_types):
+ if isinstance(value, str):
try:
value = array("L", (pureint(arc) for arc in value.split(".")))
except ValueError:
)
def __setstate__(self, state):
- super(ObjectIdentifier, self).__setstate__(state)
+ super().__setstate__(state)
self._value = state.value
self.defines = state.defines
i = 0
arc = 0
while True:
- octet = indexbytes(v, i)
+ octet = v[i]
if i == 0 and octet == 0x80:
if ctx.get("bered", False):
ber_encoded = True
_decoded=(0, 0, 0),
bounds=None, # dummy argument, workability for Integer.decode
):
- super(Enumerated, self).__init__(
+ super().__init__(
value, bounds, impl, expl, default, optional, _specs, _decoded,
)
if len(self.specs) == 0:
def _value_sanitize(self, value):
if isinstance(value, self.__class__):
value = value._value
- elif isinstance(value, integer_types):
- for _value in itervalues(self.specs):
+ elif isinstance(value, int):
+ for _value in self.specs.values():
if _value == value:
break
else:
"unknown integer value: %s" % value,
klass=self.__class__,
)
- elif isinstance(value, string_types):
+ elif isinstance(value, str):
value = self.specs.get(value)
if value is None:
raise ObjUnknown("integer value: %s" % value)
- utf-16-be
"""
__slots__ = ()
+ memoryview_safe = False
def _value_sanitize(self, value):
value_raw = None
value_decoded = None
if isinstance(value, self.__class__):
value_raw = value._value
- elif value.__class__ == text_type:
+ elif value.__class__ == str:
value_decoded = value
- elif value.__class__ == binary_type:
+ elif value.__class__ == bytes:
value_raw = value
else:
- raise InvalidValueType((self.__class__, text_type, binary_type))
+ raise InvalidValueType((self.__class__, str, bytes))
try:
value_raw = (
value_decoded.encode(self.encoding)
return value_raw
def __eq__(self, their):
- if their.__class__ == binary_type:
+ if their.__class__ == bytes:
return self._value == their
- if their.__class__ == text_type:
+ if their.__class__ == str:
return self._value == their.encode(self.encoding)
if not isinstance(their, self.__class__):
return False
def __unicode__(self):
if self.ready:
return self._value.decode(self.encoding)
- return text_type(self._value)
+ return str(self._value)
+
+ def memoryview(self):
+ raise ValueError("CommonString does not support .memoryview()")
def __repr__(self):
- return pp_console_row(next(self.pps(no_unicode=PY2)))
+ return pp_console_row(next(self.pps()))
- def pps(self, decode_path=(), no_unicode=False):
+ def pps(self, decode_path=()):
value = None
if self.ready:
- value = (
- hexenc(bytes(self)) if no_unicode else
- "".join(escape_control_unicode(c) for c in self.__unicode__())
- )
+ value = "".join(escape_control_unicode(c) for c in self.__unicode__())
yield _pp(
obj=self,
asn1_type_name=self.asn1_type_name,
asn1_type_name = "UTF8String"
-class AllowableCharsMixin(object):
+class AllowableCharsMixin:
+ __slots__ = ()
+
@property
def allowable_chars(self):
- if PY2:
- return self._allowable_chars
- return frozenset(six_unichr(c) for c in self._allowable_chars)
+ return frozenset(chr(c) for c in self._allowable_chars)
def _value_sanitize(self, value):
- value = super(AllowableCharsMixin, self)._value_sanitize(value)
+ value = super()._value_sanitize(value)
if not frozenset(value) <= self._allowable_chars:
raise DecodeError("non satisfying alphabet value")
return value
+NUMERIC_ALLOWABLE_CHARS = frozenset(digits.encode("ascii") + b" ")
+
+
class NumericString(AllowableCharsMixin, CommonString):
"""Numeric string
>>> NumericString().allowable_chars
frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
"""
- __slots__ = ()
+ __slots__ = ("_allowable_chars",)
tag_default = tag_encode(18)
encoding = "ascii"
asn1_type_name = "NumericString"
- _allowable_chars = frozenset(digits.encode("ascii") + b" ")
+
+ def __init__(self, *args, **kwargs):
+ self._allowable_chars = NUMERIC_ALLOWABLE_CHARS
+ super().__init__(*args, **kwargs)
PrintableStringState = namedtuple(
)
+PRINTABLE_ALLOWABLE_CHARS = frozenset(
+ (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
+)
+
+
class PrintableString(AllowableCharsMixin, CommonString):
"""Printable string
>>> obj.allow_asterisk, obj.allow_ampersand
(True, False)
"""
- __slots__ = ()
+ __slots__ = ("_allowable_chars",)
tag_default = tag_encode(19)
encoding = "ascii"
asn1_type_name = "PrintableString"
- _allowable_chars = frozenset(
- (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
- )
_asterisk = frozenset("*".encode("ascii"))
_ampersand = frozenset("&".encode("ascii"))
:param allow_asterisk: allow asterisk character
:param allow_ampersand: allow ampersand character
"""
+ allowable_chars = PRINTABLE_ALLOWABLE_CHARS
if allow_asterisk:
- self._allowable_chars |= self._asterisk
+ allowable_chars |= self._asterisk
if allow_ampersand:
- self._allowable_chars |= self._ampersand
- super(PrintableString, self).__init__(
+ allowable_chars |= self._ampersand
+ self._allowable_chars = allowable_chars
+ super().__init__(
value, bounds, impl, expl, default, optional, _decoded, ctx,
)
def __getstate__(self):
return PrintableStringState(
- *super(PrintableString, self).__getstate__(),
+ *super().__getstate__(),
**{"allowable_chars": self._allowable_chars}
)
def __setstate__(self, state):
- super(PrintableString, self).__setstate__(state)
+ super().__setstate__(state)
self._allowable_chars = state.allowable_chars
def __call__(
asn1_type_name = "VideotexString"
+IA5_ALLOWABLE_CHARS = frozenset(b"".join(
+ chr(c).encode("ascii") for c in range(128)
+))
+
+
class IA5String(AllowableCharsMixin, CommonString):
"""IA5 string
>>> IA5String().allowable_chars
frozenset(["NUL", ... "DEL"])
"""
- __slots__ = ()
+ __slots__ = ("_allowable_chars",)
tag_default = tag_encode(22)
encoding = "ascii"
asn1_type_name = "IA5"
- _allowable_chars = frozenset(b"".join(
- six_unichr(c).encode("ascii") for c in six_xrange(128)
- ))
+
+ def __init__(self, *args, **kwargs):
+ self._allowable_chars = IA5_ALLOWABLE_CHARS
+ super().__init__(*args, **kwargs)
LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
LEN_LEN_YYYYMMDDHHMMSSZ = len_encode(LEN_YYYYMMDDHHMMSSZ)
+VISIBLE_ALLOWABLE_CHARS = frozenset(b"".join(
+ chr(c).encode("ascii") for c in range(ord(" "), ord("~") + 1)
+))
+
+
class VisibleString(AllowableCharsMixin, CommonString):
"""Visible string
>>> VisibleString().allowable_chars
frozenset([" ", ... "~"])
"""
- __slots__ = ()
+ __slots__ = ("_allowable_chars",)
tag_default = tag_encode(26)
encoding = "ascii"
asn1_type_name = "VisibleString"
- _allowable_chars = frozenset(b"".join(
- six_unichr(c).encode("ascii") for c in six_xrange(ord(" "), ord("~") + 1)
- ))
+
+ def __init__(self, *args, **kwargs):
+ self._allowable_chars = VISIBLE_ALLOWABLE_CHARS
+ super().__init__(*args, **kwargs)
class ISO646String(VisibleString):
datetime.datetime(2017, 9, 30, 22, 7, 50)
>>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
datetime.datetime(1957, 9, 30, 22, 7, 50)
+ >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).totzdatetime()
+ datetime.datetime(1957, 9, 30, 22, 7, 50, tzinfo=tzutc())
If BER encoded value was met, then ``ber_raw`` attribute will hold
its raw representation.
:param default: set default value. Type same as in ``value``
:param bool optional: is object ``OPTIONAL`` in sequence
"""
- super(UTCTime, self).__init__(
- None, None, impl, expl, None, optional, _decoded, ctx,
- )
+ super().__init__(None, None, impl, expl, None, optional, _decoded, ctx)
self._value = value
self.ber_raw = None
if value is not None:
return value.replace(microsecond=0)
def _value_sanitize(self, value, ctx=None):
- if value.__class__ == binary_type:
+ if value.__class__ == bytes:
try:
value_decoded = value.decode("ascii")
except (UnicodeEncodeError, UnicodeDecodeError) as err:
if self.ber_encoded:
value += " (%s)" % self.ber_raw
return value
- return text_type(self._pp_value())
+ return str(self._pp_value())
def __getstate__(self):
- return UTCTimeState(
- *super(UTCTime, self).__getstate__(),
- **{"ber_raw": self.ber_raw}
- )
+ return UTCTimeState(*super().__getstate__(), **{"ber_raw": self.ber_raw})
def __setstate__(self, state):
- super(UTCTime, self).__setstate__(state)
+ super().__setstate__(state)
self.ber_raw = state.ber_raw
def __bytes__(self):
return self._encode_time()
def __eq__(self, their):
- if their.__class__ == binary_type:
+ if their.__class__ == bytes:
return self._encode_time() == their
if their.__class__ == datetime:
return self.todatetime() == their
def todatetime(self):
return self._value
+ def totzdatetime(self):
+ try:
+ return self._value.replace(tzinfo=tzUTC)
+ except TypeError as err:
+ raise NotImplementedError("Missing dateutil.tz") from err
+
def __repr__(self):
return pp_console_row(next(self.pps()))
"""
if impl is not None:
raise ValueError("no implicit tag allowed for CHOICE")
- super(Choice, self).__init__(None, expl, default, optional, _decoded)
+ super().__init__(None, expl, default, optional, _decoded)
if schema is None:
schema = getattr(self, "schema", ())
if len(schema) == 0:
)
def __setstate__(self, state):
- super(Choice, self).__setstate__(state)
+ super().__setstate__(state)
self.specs = state.specs
self._value = state.value
@property
def tag_order_cer(self):
- return min(v.tag_order_cer for v in itervalues(self.specs))
+ return min(v.tag_order_cer for v in self.specs.values())
def __getitem__(self, key):
if key not in self.specs:
self._value[1].encode_cer(writer)
def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
- for choice, spec in iteritems(self.specs):
+ for choice, spec in self.specs.items():
sub_decode_path = decode_path + (choice,)
try:
spec.decode(
:param bytes expl: override default tag with ``EXPLICIT`` one
:param bool optional: is object ``OPTIONAL`` in sequence
"""
- super(Any, self).__init__(None, expl, None, optional, _decoded)
+ super().__init__(None, expl, None, optional, _decoded)
if value is None:
self._value = None
else:
value = self._value_sanitize(value)
self._value = value
if self._expl is None:
- if value.__class__ == binary_type:
+ if value.__class__ == bytes or value.__class__ == memoryview:
tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
else:
tag_class, tag_num = value.tag_order
self.defined = None
def _value_sanitize(self, value):
- if value.__class__ == binary_type:
+ if value.__class__ == bytes or value.__class__ == memoryview:
if len(value) == 0:
raise ValueError("%s value can not be empty" % self.__class__.__name__)
return value
if isinstance(value, self.__class__):
return value._value
if not isinstance(value, Obj):
- raise InvalidValueType((self.__class__, Obj, binary_type))
+ raise InvalidValueType((self.__class__, Obj, bytes))
return value
@property
)
def __setstate__(self, state):
- super(Any, self).__setstate__(state)
+ super().__setstate__(state)
self._value = state.value
self.defined = state.defined
def __eq__(self, their):
- if their.__class__ == binary_type:
- if self._value.__class__ == binary_type:
+ if their.__class__ == bytes or their.__class__ == memoryview:
+ if self._value.__class__ == bytes or their.__class__ == memoryview:
return self._value == their
return self._value.encode() == their
if issubclass(their.__class__, Any):
if self.ready and their.ready:
- return bytes(self) == bytes(their)
+ return self.memoryview() == their.memoryview()
return self.ready == their.ready
return False
def __bytes__(self):
self._assert_ready()
value = self._value
- if value.__class__ == binary_type:
+ if value.__class__ == bytes:
return value
+ if value.__class__ == memoryview:
+ return bytes(value)
return self._value.encode()
+ def memoryview(self):
+ self._assert_ready()
+ value = self._value
+ if value.__class__ == memoryview:
+ return memoryview(value)
+ return memoryview(bytes(self))
+
@property
def tlen(self):
return 0
def _encode(self):
self._assert_ready()
value = self._value
- if value.__class__ == binary_type:
- return value
+ if value.__class__ == bytes or value.__class__ == memoryview:
+ return bytes(self)
return value.encode()
def _encode1st(self, state):
self._assert_ready()
value = self._value
- if value.__class__ == binary_type:
+ if value.__class__ == bytes or value.__class__ == memoryview:
return len(value), state
return value.encode1st(state)
def _encode2nd(self, writer, state_iter):
value = self._value
- if value.__class__ == binary_type:
+ if value.__class__ == bytes or value.__class__ == memoryview:
write_full(writer, value)
else:
value.encode2nd(writer, state_iter)
def _encode_cer(self, writer):
self._assert_ready()
value = self._value
- if value.__class__ == binary_type:
+ if value.__class__ == bytes or value.__class__ == memoryview:
write_full(writer, value)
else:
value.encode_cer(writer)
)
tlvlen = tlen + llen + l
v, tail = tlv[:tlvlen], v[l:]
+ if evgen_mode:
+ value = None
+ elif ctx.get("keep_memoryview", False):
+ value = v
+ else:
+ value = v.tobytes()
obj = self.__class__(
- value=None if evgen_mode else v.tobytes(),
+ value=value,
expl=self._expl,
optional=self.optional,
_decoded=(offset, 0, tlvlen),
value = self._value
if value is None:
pass
- elif value.__class__ == binary_type:
+ elif value.__class__ == bytes or value.__class__ == memoryview:
value = None
else:
value = repr(value)
obj_name=self.__class__.__name__,
decode_path=decode_path,
value=value,
- blob=self._value if self._value.__class__ == binary_type else None,
+ blob=self._value if (
+ self._value.__class__ == bytes or
+ value.__class__ == memoryview
+ ) else None,
optional=self.optional,
default=self == self.default,
impl=None if self.tag == self.tag_default else tag_decode(self.tag),
)
-class SequenceEncode1stMixing(object):
+class SequenceEncode1stMixin:
+ __slots__ = ()
+
def _encode1st(self, state):
state.append(0)
idx = len(state) - 1
return len(self.tag) + len_size(vlen) + vlen, state
-class Sequence(SequenceEncode1stMixing, Obj):
+class Sequence(SequenceEncode1stMixin, Obj):
"""``SEQUENCE`` structure type
You have to make specification of sequence::
optional=False,
_decoded=(0, 0, 0),
):
- super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
+ super().__init__(impl, expl, default, optional, _decoded)
if schema is None:
schema = getattr(self, "schema", ())
self.specs = (
@property
def ready(self):
- for name, spec in iteritems(self.specs):
+ for name, spec in self.specs.items():
value = self._value.get(name)
if value is None:
if spec.optional:
def bered(self):
if self.expl_lenindef or self.lenindef or self.ber_encoded:
return True
- return any(value.bered for value in itervalues(self._value))
+ return any(value.bered for value in self._value.values())
def __getstate__(self):
return SequenceState(
self.lenindef,
self.ber_encoded,
self.specs,
- {k: copy(v) for k, v in iteritems(self._value)},
+ {k: copy(v) for k, v in self._value.items()},
)
def __setstate__(self, state):
- super(Sequence, self).__setstate__(state)
+ super().__setstate__(state)
self.specs = state.specs
self._value = state.value
return None
def _values_for_encoding(self):
- for name, spec in iteritems(self.specs):
+ for name, spec in self.specs.items():
value = self._value.get(name)
if value is None:
if spec.optional:
values = {}
ber_encoded = False
ctx_allow_default_values = ctx.get("allow_default_values", False)
- for name, spec in iteritems(self.specs):
+ for name, spec in self.specs.items():
if spec.optional and (
(lenindef and v[:EOC_LEN].tobytes() == EOC) or
len(v) == 0
yield pp
-class Set(Sequence, SequenceEncode1stMixing):
+class Set(Sequence, SequenceEncode1stMixin):
"""``SET`` structure type
Its usage is identical to :py:class:`pyderasn.Sequence`.
asn1_type_name = "SET"
def _values_for_encoding(self):
- return sorted(
- super(Set, self)._values_for_encoding(),
- key=attrgetter("tag_order"),
- )
+ return sorted(super()._values_for_encoding(), key=attrgetter("tag_order"))
def _encode_cer(self, writer):
write_full(writer, self.tag + LENINDEF)
for v in sorted(
- super(Set, self)._values_for_encoding(),
+ super()._values_for_encoding(),
key=attrgetter("tag_order_cer"),
):
v.encode_cer(writer)
while len(v) > 0:
if lenindef and v[:EOC_LEN].tobytes() == EOC:
break
- for name, spec in iteritems(_specs_items):
+ for name, spec in _specs_items.items():
sub_decode_path = decode_path + (name,)
try:
spec.decode(
)
tail = v[EOC_LEN:]
obj.lenindef = True
- for name, spec in iteritems(self.specs):
+ for name, spec in self.specs.items():
if name not in values and not spec.optional:
raise DecodeError(
"%s value is not ready" % name,
)
-class SequenceOf(SequenceEncode1stMixing, Obj):
+class SequenceOf(SequenceEncode1stMixin, Obj):
"""``SEQUENCE OF`` sequence type
For that kind of type you must specify the object it will carry on
optional=False,
_decoded=(0, 0, 0),
):
- super(SequenceOf, self).__init__(impl, expl, default, optional, _decoded)
+ super().__init__(impl, expl, default, optional, _decoded)
if schema is None:
schema = getattr(self, "schema", None)
if schema is None:
)
def __setstate__(self, state):
- super(SequenceOf, self).__setstate__(state)
+ super().__setstate__(state)
self.spec = state.spec
self._value = state.value
self._bound_min = state.bound_min
return b"".join((self.tag, len_encode(len(value)), value))
def _encode1st(self, state):
- state = super(SequenceOf, self)._encode1st(state)
+ state = super()._encode1st(state)
if hasattr(self._value, NEXT_ATTR_NAME):
self._value = []
return state
asn1_type_name = "SET OF"
def _value_sanitize(self, value):
- value = super(SetOf, self)._value_sanitize(value)
+ value = super()._value_sanitize(value)
if hasattr(value, NEXT_ATTR_NAME):
raise ValueError(
"SetOf does not support iterator values, as no sense in them"
write_full(writer, EOC)
def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
- return super(SetOf, self)._decode(
+ return super()._decode(
tlv,
offset,
decode_path,
choice = PrimitiveTypes()
choice.specs["SequenceOf"] = SequenceOf(schema=choice)
choice.specs["SetOf"] = SetOf(schema=choice)
- for i in six_xrange(31):
+ for i in range(31):
choice.specs["SequenceOf%d" % i] = SequenceOf(
schema=choice,
expl=tag_ctxc(i),
92 2b 39 20 65 91 e6 8e 95 93 1a 58 df 02 78 ea |.+9 e......X..x.|
^^^^^^^^^^^^^^^^
"""
- return "".join((six_unichr(b) if 0x20 <= b <= 0x7E else ".") for b in ba)
+ return "".join((chr(b) if 0x20 <= b <= 0x7E else ".") for b in ba)
def hexdump(raw):
"""
hexed = hexenc(raw).upper()
addr, cols = 0, ["%08x " % 0]
- for i in six_xrange(0, len(hexed), 2):
+ for i in range(0, len(hexed), 2):
if i != 0 and i // 2 % 8 == 0:
cols[-1] += " "
if i != 0 and i // 2 % 16 == 0:
- cols.append(" |%s|" % ascii_visualize(bytearray(raw[addr:addr + 16])))
+ cols.append(" |%s|" % ascii_visualize(bytes(raw[addr:addr + 16])))
yield cols
addr += 16
cols = ["%08x " % addr]
cols.append(" " + hexed[i:i + 2])
if len(cols) > 0:
- cols.append(" |%s|" % ascii_visualize(bytearray(raw[addr:])))
+ cols.append(" |%s|" % ascii_visualize(bytes(raw[addr:])))
yield cols
def __init__(self, state, *args, **kwargs):
self.state = state
self.scrolled = {"info": False, "hexdump": False}
- super(TW, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
def _get_pp(self):
pp = self.get_node().get_value()
line[idx] = (attr, line[idx])
if pp.expl_offset is not None:
- for i in six_xrange(
+ for i in range(
pp.expl_offset,
pp.expl_offset + pp.expl_tlen + pp.expl_llen,
):
attr_set(i, "select-expl")
- for i in six_xrange(pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen):
+ for i in range(pp.offset, pp.offset + pp.tlen + pp.llen + pp.vlen):
attr_set(i, "select-value")
self.state["hexdump"]._set_body([urwid.Text(line) for line in lines])
self.state["hexdump"].set_focus(pp.offset // 16)
lines.append([
("header", "Hexadecimal: "), colonize_hex(pp.obj.tohex()),
])
- if pp.blob.__class__ == binary_type:
+ if pp.blob.__class__ == bytes:
blob = hexenc(pp.blob).upper()
- for i in six_xrange(0, len(blob), 32):
+ for i in range(0, len(blob), 32):
lines.append([colonize_hex(blob[i:i + 32])])
elif pp.blob.__class__ == tuple:
lines.append([", ".join(pp.blob)])
self.scrolled["info"] = False
self.scrolled["hexdump"] = False
self._state_update()
- return super(TW, self).selectable()
+ return super().selectable()
- def get_display_text(self):
+ def _get_display_text_without_offset(self):
pp, constructed = self._get_pp()
style = "constructed" if constructed else ""
if len(pp.decode_path) == 0:
))
return (style, ent)
+ def get_display_text(self):
+ pp, _ = self._get_pp()
+ style, ent = self._get_display_text_without_offset()
+ return [(style, ent), " [%d]" % pp.offset]
+
def _scroll(self, what, step):
self.state[what]._invalidate()
pos = self.state[what].focus_position
("warning", "Saved to: " + dp)
)
return None
- return super(TW, self).keypress(size, key)
+ return super().keypress(size, key)
class PN(urwid.ParentNode):
def __init__(self, state, value, *args, **kwargs):
self.state = state
if not hasattr(value, "_fields"):
value = list(value)
- super(PN, self).__init__(value, *args, **kwargs)
+ super().__init__(value, *args, **kwargs)
def load_widget(self):
return TW(self.state, self)
class LabeledPG(urwid.ProgressBar):
def __init__(self, label, *args, **kwargs):
self.label = label
- super(LabeledPG, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
def get_text(self):
- return "%s: %s" % (self.label, super(LabeledPG, self).get_text())
+ return "%s: %s" % (self.label, super().get_text())
WinHexdump = urwid.ListBox([urwid.Text("")])
WinInfo = urwid.ListBox([urwid.Text("")])