X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=pyderasn.py;h=f1be6473130142969088c58038efc402a5995a10;hb=2e6117387cfb10eca87e9846498a9a045f05dba3;hp=58583b18d0083bb56df58116e8f588533e9c3186;hpb=370a9698a2b53a681a0f94d371b3f889df55225f;p=pyderasn.git diff --git a/pyderasn.py b/pyderasn.py index 58583b1..f1be647 100755 --- a/pyderasn.py +++ b/pyderasn.py @@ -352,7 +352,6 @@ Let's parse that output, human:: (and its derivatives), ``SET``, ``SET OF``, ``UTCTime``, ``GeneralizedTime`` could be BERed. - .. _definedby: DEFINED BY @@ -550,12 +549,12 @@ _______ Integer _______ .. autoclass:: pyderasn.Integer - :members: __init__ + :members: __init__, named BitString _________ .. autoclass:: pyderasn.BitString - :members: __init__ + :members: __init__, bit_len, named OctetString ___________ @@ -587,7 +586,7 @@ _____________ PrintableString _______________ .. autoclass:: pyderasn.PrintableString - :members: __init__ + :members: __init__, allow_asterisk, allow_ampersand UTCTime _______ @@ -597,6 +596,7 @@ _______ GeneralizedTime _______________ .. autoclass:: pyderasn.GeneralizedTime + :members: __init__, todatetime Special types ------------- @@ -604,7 +604,7 @@ Special types Choice ______ .. autoclass:: pyderasn.Choice - :members: __init__ + :members: __init__, choice, value PrimitiveTypes ______________ @@ -780,6 +780,7 @@ from copy import copy from datetime import datetime from datetime import timedelta from math import ceil +from operator import attrgetter from string import ascii_letters from string import digits from sys import version_info @@ -807,7 +808,7 @@ except ImportError: # pragma: no cover def colored(what, *args, **kwargs): return what -__version__ = "6.3" +__version__ = "7.0" __all__ = ( "Any", @@ -1189,6 +1190,7 @@ class AutoAddSlots(type): BasicState = namedtuple("BasicState", ( "version", "tag", + "tag_order", "expl", "default", "optional", @@ -1210,6 +1212,7 @@ class Obj(object): """ __slots__ = ( "tag", + "_tag_order", "_value", "_expl", "default", @@ -1234,6 +1237,13 @@ class Obj(object): 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") + if self.tag is None: + self._tag_order = None + else: + tag_class, _, tag_num = tag_decode( + self.tag if self._expl is None else self._expl + ) + self._tag_order = (tag_class, tag_num) if default is not None: optional = True self.optional = optional @@ -1274,6 +1284,7 @@ class Obj(object): if state.version != __version__: raise ValueError("data is pickled by different PyDERASN version") self.tag = state.tag + self._tag_order = state.tag_order self._expl = state.expl self.default = state.default self.optional = state.optional @@ -1284,6 +1295,12 @@ class Obj(object): self.lenindef = state.lenindef self.ber_encoded = state.ber_encoded + @property + def tag_order(self): + """Tag's (class, number) used for DER/CER sorting + """ + return self._tag_order + @property def tlen(self): """See :ref:`decoding` @@ -1937,6 +1954,7 @@ class Boolean(Obj): return BooleanState( __version__, self.tag, + self._tag_order, self._expl, self.default, self.optional, @@ -2206,6 +2224,7 @@ class Integer(Obj): return IntegerState( __version__, self.tag, + self._tag_order, self._expl, self.default, self.optional, @@ -2256,6 +2275,8 @@ class Integer(Obj): @property def named(self): + """Return named representation (if exists) of the value + """ for name, value in iteritems(self.specs): if value == self._value: return name @@ -2604,6 +2625,7 @@ class BitString(Obj): return BitStringState( __version__, self.tag, + self._tag_order, self._expl, self.default, self.optional, @@ -2633,6 +2655,8 @@ class BitString(Obj): @property def bit_len(self): + """Returns number of bits in the string + """ self._assert_ready() return self._value[0] @@ -2653,6 +2677,10 @@ class BitString(Obj): @property def named(self): + """Named representation (if exists) of the bits + + :returns: [str(name), ...] + """ return [name for name, bit in iteritems(self.specs) if self[bit]] def __call__( @@ -3033,6 +3061,7 @@ class OctetString(Obj): return OctetStringState( __version__, self.tag, + self._tag_order, self._expl, self.default, self.optional, @@ -3338,6 +3367,7 @@ class Null(Obj): return NullState( __version__, self.tag, + self._tag_order, self._expl, self.default, self.optional, @@ -3553,6 +3583,7 @@ class ObjectIdentifier(Obj): return ObjectIdentifierState( __version__, self.tag, + self._tag_order, self._expl, self.default, self.optional, @@ -4654,6 +4685,9 @@ class Choice(Obj): self.default = default_obj if value is None: self._value = copy(default_obj._value) + if self._expl is not None: + tag_class, _, tag_num = tag_decode(self._expl) + self._tag_order = (tag_class, tag_num) def _value_sanitize(self, value): if (value.__class__ == tuple) and len(value) == 2: @@ -4683,6 +4717,7 @@ class Choice(Obj): return ChoiceState( __version__, self.tag, + self._tag_order, self._expl, self.default, self.optional, @@ -4728,14 +4763,23 @@ class Choice(Obj): @property def choice(self): + """Name of the choice + """ self._assert_ready() return self._value[0] @property def value(self): + """Value of underlying choice + """ self._assert_ready() return self._value[1] + @property + def tag_order(self): + self._assert_ready() + return self._value[1].tag_order if self._tag_order is None else self._tag_order + def __getitem__(self, key): if key not in self.specs: raise ObjUnknown(key) @@ -4904,17 +4948,31 @@ class Any(Obj): """ :param value: set the value. Either any kind of pyderasn's **ready** object, or bytes. Pay attention that - **no** validation is performed is raw binary value - is valid TLV + **no** validation is performed if raw binary value + is valid TLV, except just tag decoding :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) - self._value = None if value is None else self._value_sanitize(value) + 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: + tag_class, _, tag_num = tag_decode(tag_strip(value)[0]) + else: + tag_class, tag_num = value.tag_order + else: + tag_class, _, tag_num = tag_decode(self._expl) + self._tag_order = (tag_class, tag_num) self.defined = None def _value_sanitize(self, value): if value.__class__ == binary_type: + if len(value) == 0: + raise ValueError("Any value can not be empty") return value if isinstance(value, self.__class__): return value._value @@ -4926,6 +4984,11 @@ class Any(Obj): def ready(self): return self._value is not None + @property + def tag_order(self): + self._assert_ready() + return self._tag_order + @property def bered(self): if self.expl_lenindef or self.lenindef: @@ -4938,6 +5001,7 @@ class Any(Obj): return AnyState( __version__, self.tag, + self._tag_order, self._expl, None, self.optional, @@ -5294,6 +5358,7 @@ class Sequence(Obj): return SequenceState( __version__, self.tag, + self._tag_order, self._expl, self.default, self.optional, @@ -5623,14 +5688,12 @@ class Set(Sequence): asn1_type_name = "SET" def _encode(self): - raws = [v.encode() for v in self._values_for_encoding()] - raws.sort() - v = b"".join(raws) + v = b"".join(value.encode() for value in sorted( + self._values_for_encoding(), + key=attrgetter("tag_order"), + )) 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) @@ -5684,12 +5747,13 @@ class Set(Sequence): ber_encoded = False 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]) + tag_order_prev = (0, 0) + _specs_items = copy(self.specs) while len(v) > 0: if lenindef and v[:EOC_LEN].tobytes() == EOC: break - for name, spec in self._specs_items(): + for name, spec in iteritems(_specs_items): sub_decode_path = decode_path + (name,) try: spec.decode( @@ -5718,8 +5782,9 @@ class Set(Sequence): ctx=ctx, _ctx_immutable=False, ) + value_tag_order = value.tag_order value_len = value.fulllen - if value_prev.tobytes() > v[:value_len].tobytes(): + if tag_order_prev >= value_tag_order: if ctx_bered or ctx_allow_unordered_set: ber_encoded = True else: @@ -5741,10 +5806,12 @@ class Set(Sequence): offset=sub_offset, ) values[name] = value - value_prev = v[:value_len] + del _specs_items[name] + tag_order_prev = value_tag_order sub_offset += value_len vlen += value_len v = v_tail + obj = self.__class__( schema=self.specs, impl=self.tag, @@ -5882,6 +5949,7 @@ class SequenceOf(Obj): return SequenceOfState( __version__, self.tag, + self._tag_order, self._expl, self.default, self.optional, @@ -6135,9 +6203,7 @@ class SetOf(SequenceOf): asn1_type_name = "SET OF" def _encode(self): - raws = [v.encode() for v in self._values_for_encoding()] - raws.sort() - v = b"".join(raws) + v = b"".join(sorted(v.encode() for v in self._values_for_encoding())) return b"".join((self.tag, len_encode(len(v)), v)) def _decode(self, tlv, offset, decode_path, ctx, tag_only):