_____________
.. autoclass:: pyderasn.NumericString
+PrintableString
+_______________
+.. autoclass:: pyderasn.PrintableString
+
UTCTime
_______
.. autoclass:: pyderasn.UTCTime
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
def colonize_hex(hexed):
"""Separate hexadecimal string with colons
"""
- return ":".join(hexed[i:i + 2] for i in range(0, len(hexed), 2))
+ return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
def pp_console_row(
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):
+ for i in six_xrange(0, len(blob), 32):
chunk = blob[i:i + 32]
yield " ".join(cols + [colonize_hex(chunk)])
elif isinstance(pp.blob, tuple):
self._value = default
def _value_sanitize(self, value):
- if issubclass(value.__class__, Boolean):
- return value._value
if isinstance(value, bool):
return value
+ if issubclass(value.__class__, Boolean):
+ return value._value
raise InvalidValueType((self.__class__, bool))
@property
self._value = default
def _value_sanitize(self, value):
- if issubclass(value.__class__, Integer):
- value = value._value
- elif isinstance(value, integer_types):
+ if isinstance(value, integer_types):
pass
+ elif issubclass(value.__class__, Integer):
+ value = value._value
elif isinstance(value, str):
value = self.specs.get(value)
if value is None:
@property
def named(self):
- for name, value in self.specs.items():
+ for name, value in iteritems(self.specs):
if value == self._value:
return name
yield pp
+SET01 = frozenset(("0", "1"))
+
+
class BitString(Obj):
"""``BIT STRING`` bit string type
return bit_len, bytes(octets)
def _value_sanitize(self, value):
- if issubclass(value.__class__, BitString):
- return value._value
if isinstance(value, (string_types, binary_type)):
if (
isinstance(value, string_types) and
):
if value.endswith("'B"):
value = value[1:-2]
- if not set(value) <= set(("0", "1")):
+ if not frozenset(value) <= SET01:
raise ValueError("B's coding contains unacceptable chars")
return self._bits2octets(value)
elif value.endswith("'H"):
bits.append(bit)
if len(bits) == 0:
return self._bits2octets("")
- bits = set(bits)
+ bits = frozenset(bits)
return self._bits2octets("".join(
("1" if bit in bits else "0")
for bit in six_xrange(max(bits) + 1)
))
+ if issubclass(value.__class__, BitString):
+ return value._value
raise InvalidValueType((self.__class__, binary_type, string_types))
@property
@property
def named(self):
- return [name for name, bit in self.specs.items() if self[bit]]
+ return [name for name, bit in iteritems(self.specs) if self[bit]]
def __call__(
self,
)
def _value_sanitize(self, value):
- if issubclass(value.__class__, OctetString):
- value = value._value
- elif isinstance(value, binary_type):
+ if isinstance(value, binary_type):
pass
+ elif issubclass(value.__class__, OctetString):
+ value = value._value
else:
raise InvalidValueType((self.__class__, bytes))
if not self._bound_min <= len(value) <= self._bound_max:
if isinstance(value, self.__class__):
value = value._value
elif isinstance(value, integer_types):
- if value not in list(self.specs.values()):
+ for _value in itervalues(self.specs):
+ if _value == value:
+ break
+ else:
raise DecodeError(
"unknown integer value: %s" % value,
klass=self.__class__,
def allowable_chars(self):
if PY2:
return self._allowable_chars
- return set(six_unichr(c) for c in self._allowable_chars)
+ return frozenset(six_unichr(c) for c in self._allowable_chars)
class NumericString(AllowableCharsMixin, CommonString):
be stored.
>>> NumericString().allowable_chars
- set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2'])
+ frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
"""
__slots__ = ()
tag_default = tag_encode(18)
encoding = "ascii"
asn1_type_name = "NumericString"
- _allowable_chars = set(digits.encode("ascii") + b" ")
+ _allowable_chars = frozenset(digits.encode("ascii") + b" ")
def _value_sanitize(self, value):
value = super(NumericString, self)._value_sanitize(value)
- if not set(value) <= self._allowable_chars:
+ if not frozenset(value) <= self._allowable_chars:
raise DecodeError("non-numeric value")
return value
Its value is properly sanitized: see X.680 41.4 table 10.
>>> PrintableString().allowable_chars
- >>> set([' ', "'", ..., 'z'])
+ frozenset([' ', "'", ..., 'z'])
"""
__slots__ = ()
tag_default = tag_encode(19)
encoding = "ascii"
asn1_type_name = "PrintableString"
- _allowable_chars = set(
+ _allowable_chars = frozenset(
(ascii_letters + digits + " '()+,-./:=?").encode("ascii")
)
def _value_sanitize(self, value):
value = super(PrintableString, self)._value_sanitize(value)
- if not set(value) <= self._allowable_chars:
+ if not frozenset(value) <= self._allowable_chars:
raise DecodeError("non-printable value")
return value
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)
+
+ .. warning::
+
+ BER encoding is unsupported.
"""
__slots__ = ()
tag_default = tag_encode(23)
encoding = "ascii"
asn1_type_name = "UTCTime"
- fmt = "%y%m%d%H%M%SZ"
-
def __init__(
self,
value=None,
if self._value is None:
self._value = default
+ def _strptime(self, value):
+ # datetime.strptime's format: %y%m%d%H%M%SZ
+ if len(value) != LEN_YYMMDDHHMMSSZ:
+ raise ValueError("invalid UTCTime length")
+ if value[-1] != "Z":
+ raise ValueError("non UTC timezone")
+ return datetime(
+ 2000 + int(value[:2]), # %y
+ int(value[2:4]), # %m
+ int(value[4:6]), # %d
+ int(value[6:8]), # %H
+ int(value[8:10]), # %M
+ int(value[10:12]), # %S
+ )
+
def _value_sanitize(self, value):
- if isinstance(value, self.__class__):
- return value._value
- if isinstance(value, datetime):
- return value.strftime(self.fmt).encode("ascii")
if isinstance(value, binary_type):
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 (TypeError, ValueError):
- raise DecodeError("invalid UTCTime format")
- return value
- else:
- raise DecodeError("invalid UTCTime length")
+ try:
+ self._strptime(value_decoded)
+ except (TypeError, ValueError) as err:
+ raise DecodeError("invalid UTCTime format: %r" % err)
+ return value
+ if isinstance(value, self.__class__):
+ return value._value
+ if isinstance(value, datetime):
+ return value.strftime("%y%m%d%H%M%SZ").encode("ascii")
raise InvalidValueType((self.__class__, datetime))
def __eq__(self, their):
having < 50 years are treated as 20xx, 19xx otherwise, according
to X.509 recomendation.
"""
- value = datetime.strptime(self._value.decode("ascii"), self.fmt)
+ value = self._strptime(self._value.decode("ascii"))
year = value.year % 100
return datetime(
year=(2000 + year) if year < 50 else (1900 + year),
'20170930220750.000123Z'
>>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
+
+ .. warning::
+
+ BER encoding is unsupported.
+
+ .. warning::
+
+ Only microsecond fractions are supported.
+ :py:exc:`pyderasn.DecodeError` will be raised during decoding of
+ higher precision values.
"""
__slots__ = ()
tag_default = tag_encode(24)
asn1_type_name = "GeneralizedTime"
- fmt = "%Y%m%d%H%M%SZ"
- fmt_ms = "%Y%m%d%H%M%S.%fZ"
+ def _strptime(self, value):
+ l = len(value)
+ if l == LEN_YYYYMMDDHHMMSSZ:
+ # datetime.strptime's format: %y%m%d%H%M%SZ
+ if value[-1] != "Z":
+ raise ValueError("non UTC timezone")
+ return datetime(
+ int(value[:4]), # %Y
+ int(value[4:6]), # %m
+ int(value[6:8]), # %d
+ int(value[8:10]), # %H
+ int(value[10:12]), # %M
+ int(value[12:14]), # %S
+ )
+ if l >= LEN_YYYYMMDDHHMMSSDMZ:
+ # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
+ if value[-1] != "Z":
+ raise ValueError("non UTC timezone")
+ if value[14] != ".":
+ raise ValueError("no fractions separator")
+ us = value[15:-1]
+ if us[-1] == "0":
+ raise ValueError("trailing zero")
+ us_len = len(us)
+ if us_len > 6:
+ raise ValueError("only microsecond fractions are supported")
+ us = int(us + ("0" * (6 - us_len)))
+ decoded = datetime(
+ int(value[:4]), # %Y
+ int(value[4:6]), # %m
+ int(value[6:8]), # %d
+ int(value[8:10]), # %H
+ int(value[10:12]), # %M
+ int(value[12:14]), # %S
+ us, # %f
+ )
+ return decoded
+ raise ValueError("invalid GeneralizedTime length")
def _value_sanitize(self, value):
- if isinstance(value, self.__class__):
- return value._value
- if isinstance(value, datetime):
- return value.strftime(
- self.fmt_ms if value.microsecond > 0 else self.fmt
- ).encode("ascii")
if isinstance(value, binary_type):
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 (TypeError, ValueError):
- raise DecodeError(
- "invalid GeneralizedTime (without ms) format",
- )
- return value
- elif len(value_decoded) >= LEN_YYYYMMDDHHMMSSDMZ:
- try:
- datetime.strptime(value_decoded, self.fmt_ms)
- except (TypeError, ValueError):
- raise DecodeError(
- "invalid GeneralizedTime (with ms) format",
- )
- return value
- else:
+ try:
+ self._strptime(value_decoded)
+ except (TypeError, ValueError) as err:
raise DecodeError(
- "invalid GeneralizedTime length",
+ "invalid GeneralizedTime format: %r" % err,
klass=self.__class__,
)
+ return value
+ if isinstance(value, self.__class__):
+ return value._value
+ if isinstance(value, datetime):
+ encoded = value.strftime("%Y%m%d%H%M%S")
+ if value.microsecond > 0:
+ encoded = encoded + (".%06d" % value.microsecond).rstrip("0")
+ return (encoded + "Z").encode("ascii")
raise InvalidValueType((self.__class__, datetime))
def todatetime(self):
- value = self._value.decode("ascii")
- if len(value) == LEN_YYYYMMDDHHMMSSZ:
- return datetime.strptime(value, self.fmt)
- return datetime.strptime(value, self.fmt_ms)
+ return self._strptime(self._value.decode("ascii"))
class GraphicString(CommonString):
self._value = default_obj.copy()._value
def _value_sanitize(self, value):
- if isinstance(value, self.__class__):
- return value._value
if isinstance(value, tuple) and len(value) == 2:
choice, obj = value
spec = self.specs.get(choice)
if not isinstance(obj, spec.__class__):
raise InvalidValueType((spec,))
return (choice, spec(obj))
+ if isinstance(value, self.__class__):
+ return value._value
raise InvalidValueType((self.__class__, tuple))
@property
return self._value[1].encode()
def _decode(self, tlv, offset, decode_path, ctx, tag_only):
- for choice, spec in self.specs.items():
+ for choice, spec in iteritems(self.specs):
sub_decode_path = decode_path + (choice,)
try:
spec.decode(
self.defined = None
def _value_sanitize(self, value):
+ if isinstance(value, binary_type):
+ return value
if isinstance(value, self.__class__):
return value._value
if isinstance(value, Obj):
return value.encode()
- if isinstance(value, binary_type):
- return value
raise InvalidValueType((self.__class__, Obj, binary_type))
@property
@property
def ready(self):
- for name, spec in self.specs.items():
+ for name, spec in iteritems(self.specs):
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 self._value.values())
+ return any(value.bered for value in itervalues(self._value))
def copy(self):
obj = self.__class__(schema=self.specs)
obj.expl_lenindef = self.expl_lenindef
obj.lenindef = self.lenindef
obj.ber_encoded = self.ber_encoded
- obj._value = {k: v.copy() for k, v in self._value.items()}
+ obj._value = {k: v.copy() for k, v in iteritems(self._value)}
return obj
def __eq__(self, their):
def _encoded_values(self):
raws = []
- for name, spec in self.specs.items():
+ for name, spec in iteritems(self.specs):
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 self.specs.items():
+ for name, spec in iteritems(self.specs):
if spec.optional and (
(lenindef and v[:EOC_LEN].tobytes() == EOC) or
len(v) == 0
v = b"".join(raws)
return b"".join((self.tag, len_encode(len(v)), v))
+ def _specs_items(self):
+ return iteritems(self.specs)
+
def _decode(self, tlv, offset, decode_path, ctx, tag_only):
try:
t, tlen, lv = tag_strip(tlv)
ctx_allow_default_values = ctx.get("allow_default_values", False)
ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
value_prev = memoryview(v[:0])
- specs_items = self.specs.items
+
while len(v) > 0:
if lenindef and v[:EOC_LEN].tobytes() == EOC:
break
- for name, spec in specs_items():
+ for name, spec in self._specs_items():
sub_decode_path = decode_path + (name,)
try:
spec.decode(
choice = PrimitiveTypes()
choice.specs["SequenceOf"] = SequenceOf(schema=choice)
choice.specs["SetOf"] = SetOf(schema=choice)
- for i in range(31):
+ for i in six_xrange(31):
choice.specs["SequenceOf%d" % i] = SequenceOf(
schema=choice,
expl=tag_ctxc(i),