]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - pyderasn.py
Valid DER SET ordering
[pyderasn.git] / pyderasn.py
index ed5b763ccef4123c790d52569d9b376ef2acf168..f1be6473130142969088c58038efc402a5995a10 100755 (executable)
@@ -75,9 +75,17 @@ tags simultaneously.
 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
 functions, allowing you to easily create ``CONTEXT``
 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
-number. Pay attention that explicit tags always have *constructed* tag
-(``tag_ctxc``), but implicit tags for primitive types are primitive
-(``tag_ctxp``).
+number.
+
+.. note::
+
+   EXPLICIT tags always have **constructed** tag. PyDERASN does not
+   explicitly check correctness of schema input here.
+
+.. note::
+
+   Implicit tags have **primitive** (``tag_ctxp``) encoding for
+   primitive values.
 
 ::
 
@@ -344,7 +352,6 @@ Let's parse that output, human::
  (and its derivatives), ``SET``, ``SET OF``, ``UTCTime``, ``GeneralizedTime``
  could be BERed.
 
-
 .. _definedby:
 
 DEFINED BY
@@ -420,7 +427,7 @@ defines_by_path context option
 ______________________________
 
 Sometimes you either can not or do not want to explicitly set *defines*
-in the scheme. You can dynamically apply those definitions when calling
+in the schema. You can dynamically apply those definitions when calling
 ``.decode()`` method.
 
 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
@@ -542,12 +549,12 @@ _______
 Integer
 _______
 .. autoclass:: pyderasn.Integer
-   :members: __init__
+   :members: __init__, named
 
 BitString
 _________
 .. autoclass:: pyderasn.BitString
-   :members: __init__
+   :members: __init__, bit_len, named
 
 OctetString
 ___________
@@ -579,7 +586,7 @@ _____________
 PrintableString
 _______________
 .. autoclass:: pyderasn.PrintableString
-   :members: __init__
+   :members: __init__, allow_asterisk, allow_ampersand
 
 UTCTime
 _______
@@ -589,6 +596,7 @@ _______
 GeneralizedTime
 _______________
 .. autoclass:: pyderasn.GeneralizedTime
+   :members: __init__, todatetime
 
 Special types
 -------------
@@ -596,7 +604,7 @@ Special types
 Choice
 ______
 .. autoclass:: pyderasn.Choice
-   :members: __init__
+   :members: __init__, choice, value
 
 PrimitiveTypes
 ______________
@@ -653,6 +661,115 @@ Various
 .. autoclass:: pyderasn.ObjNotReady
 .. autoclass:: pyderasn.InvalidValueType
 .. autoclass:: pyderasn.BoundsError
+
+.. _cmdline:
+
+Command-line usage
+------------------
+
+You can decode DER/BER files using command line abilities::
+
+    $ python -m pyderasn --schema tests.test_crts:Certificate path/to/file
+
+If there is no schema for your file, then you can try parsing it without,
+but of course IMPLICIT tags will often make it impossible. But result is
+good enough for the certificate above::
+
+    $ python -m pyderasn path/to/file
+        0   [1,3,1604]  . >: SEQUENCE OF
+        4   [1,3,1453]  . . >: SEQUENCE OF
+        8   [0,0,   5]  . . . . >: [0] ANY
+                        . . . . . A0:03:02:01:02
+       13   [1,1,   3]  . . . . >: INTEGER 61595
+       18   [1,1,  13]  . . . . >: SEQUENCE OF
+       20   [1,1,   9]  . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
+       31   [1,1,   0]  . . . . . . >: NULL
+       33   [1,3, 274]  . . . . >: SEQUENCE OF
+       37   [1,1,  11]  . . . . . . >: SET OF
+       39   [1,1,   9]  . . . . . . . . >: SEQUENCE OF
+       41   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
+       46   [1,1,   2]  . . . . . . . . . . >: PrintableString PrintableString ES
+    [...]
+     1409   [1,1,  50]  . . . . . . >: SEQUENCE OF
+     1411   [1,1,   8]  . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
+     1421   [1,1,  38]  . . . . . . . . >: OCTET STRING 38 bytes
+                        . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
+                        . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
+                        . . . . . . . . . 61:2E:63:6F:6D:2F
+     1461   [1,1,  13]  . . >: SEQUENCE OF
+     1463   [1,1,   9]  . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
+     1474   [1,1,   0]  . . . . >: NULL
+     1476   [1,2, 129]  . . >: BIT STRING 1024 bits
+                        . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
+                        . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
+    [...]
+
+Human readable OIDs
+___________________
+
+If you have got dictionaries with ObjectIdentifiers, like example one
+from ``tests/test_crts.py``::
+
+    stroid2name = {
+        "1.2.840.113549.1.1.1": "id-rsaEncryption",
+        "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
+        [...]
+        "2.5.4.10": "id-at-organizationName",
+        "2.5.4.11": "id-at-organizationalUnitName",
+    }
+
+then you can pass it to pretty printer to see human readable OIDs::
+
+    $ python -m pyderasn --oids tests.test_crts:stroid2name path/to/file
+    [...]
+       37   [1,1,  11]  . . . . . . >: SET OF
+       39   [1,1,   9]  . . . . . . . . >: SEQUENCE OF
+       41   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
+       46   [1,1,   2]  . . . . . . . . . . >: PrintableString PrintableString ES
+       50   [1,1,  18]  . . . . . . >: SET OF
+       52   [1,1,  16]  . . . . . . . . >: SEQUENCE OF
+       54   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
+       59   [1,1,   9]  . . . . . . . . . . >: PrintableString PrintableString Barcelona
+       70   [1,1,  18]  . . . . . . >: SET OF
+       72   [1,1,  16]  . . . . . . . . >: SEQUENCE OF
+       74   [1,1,   3]  . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
+       79   [1,1,   9]  . . . . . . . . . . >: PrintableString PrintableString Barcelona
+    [...]
+
+Decode paths
+____________
+
+Each decoded element has so-called decode path: sequence of structure
+names it is passing during the decode process. Each element has its own
+unique path inside the whole ASN.1 tree. You can print it out with
+``--print-decode-path`` option::
+
+    $ python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
+       0    [1,3,1604]  Certificate SEQUENCE []
+       4    [1,3,1453]   . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
+      10-2  [1,1,   1]   . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
+      13    [1,1,   3]   . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
+      18    [1,1,  13]   . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
+      20    [1,1,   9]   . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
+      31    [0,0,   2]   . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
+                         . . . . 05:00
+      33    [0,0, 278]   . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
+      33    [1,3, 274]   . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
+      37    [1,1,  11]   . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
+      39    [1,1,   9]   . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
+      41    [1,1,   3]   . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
+      46    [0,0,   4]   . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
+                         . . . . . . . 13:02:45:53
+      46    [1,1,   2]   . . . . . . . DEFINED BY 2.5.4.6: CountryName PrintableString ES [tbsCertificate:issuer:rdnSequence:0:0:value:DEFINED BY 2.5.4.6]
+    [...]
+
+Now you can print only the specified tree, for example signature algorithm::
+
+    $ python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
+      18    [1,1,  13]  AlgorithmIdentifier SEQUENCE
+      20    [1,1,   9]   . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
+      31    [0,0,   2]   . parameters: [UNIV 5] ANY OPTIONAL
+                         . . 05:00
 """
 
 from codecs import getdecoder
@@ -663,9 +780,10 @@ from copy import copy
 from datetime import datetime
 from datetime import timedelta
 from math import ceil
-from os import environ
+from operator import attrgetter
 from string import ascii_letters
 from string import digits
+from sys import version_info
 from unicodedata import category as unicat
 
 from six import add_metaclass
@@ -690,7 +808,7 @@ except ImportError:  # pragma: no cover
     def colored(what, *args, **kwargs):
         return what
 
-__version__ = "6.2"
+__version__ = "7.0"
 
 __all__ = (
     "Any",
@@ -764,7 +882,7 @@ EOC = b"\x00\x00"
 EOC_LEN = len(EOC)
 LENINDEF = b"\x80"  # length indefinite mark
 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
-NAMEDTUPLE_KWARGS = {} if PY2 else {"module": __name__}
+NAMEDTUPLE_KWARGS = {} if version_info < (3, 6) else {"module": __name__}
 SET01 = frozenset("01")
 DECIMALS = frozenset(digits)
 DECIMAL_SIGNS = ".,"
@@ -1069,6 +1187,22 @@ class AutoAddSlots(type):
         return type.__new__(cls, name, bases, _dict)
 
 
+BasicState = namedtuple("BasicState", (
+    "version",
+    "tag",
+    "tag_order",
+    "expl",
+    "default",
+    "optional",
+    "offset",
+    "llen",
+    "vlen",
+    "expl_lenindef",
+    "lenindef",
+    "ber_encoded",
+), **NAMEDTUPLE_KWARGS)
+
+
 @add_metaclass(AutoAddSlots)
 class Obj(object):
     """Common ASN.1 object class
@@ -1078,6 +1212,7 @@ class Obj(object):
     """
     __slots__ = (
         "tag",
+        "_tag_order",
         "_value",
         "_expl",
         "default",
@@ -1102,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
@@ -1141,17 +1283,23 @@ class Obj(object):
     def __setstate__(self, state):
         if state.version != __version__:
             raise ValueError("data is pickled by different PyDERASN version")
-        self.tag = self.tag_default
-        self._value = None
-        self._expl = None
-        self.default = None
-        self.optional = False
-        self.offset = 0
-        self.llen = 0
-        self.vlen = 0
-        self.expl_lenindef = False
-        self.lenindef = False
-        self.ber_encoded = False
+        self.tag = state.tag
+        self._tag_order = state.tag_order
+        self._expl = state.expl
+        self.default = state.default
+        self.optional = state.optional
+        self.offset = state.offset
+        self.llen = state.llen
+        self.vlen = state.vlen
+        self.expl_lenindef = state.expl_lenindef
+        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):
@@ -1220,7 +1368,7 @@ class Obj(object):
         :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)
+                         determine if tag satisfies the schema)
         :param _ctx_immutable: do we need to ``copy.copy()`` ``ctx``
                                before using it?
         :returns: (Obj, remaining data)
@@ -1741,20 +1889,11 @@ def pprint(
 # ASN.1 primitive types
 ########################################################################
 
-BooleanState = namedtuple("BooleanState", (
-    "version",
-    "value",
-    "tag",
-    "expl",
-    "default",
-    "optional",
-    "offset",
-    "llen",
-    "vlen",
-    "expl_lenindef",
-    "lenindef",
-    "ber_encoded",
-), **NAMEDTUPLE_KWARGS)
+BooleanState = namedtuple(
+    "BooleanState",
+    BasicState._fields + ("value",),
+    **NAMEDTUPLE_KWARGS
+)
 
 
 class Boolean(Obj):
@@ -1814,8 +1953,8 @@ class Boolean(Obj):
     def __getstate__(self):
         return BooleanState(
             __version__,
-            self._value,
             self.tag,
+            self._tag_order,
             self._expl,
             self.default,
             self.optional,
@@ -1825,21 +1964,12 @@ class Boolean(Obj):
             self.expl_lenindef,
             self.lenindef,
             self.ber_encoded,
+            self._value,
         )
 
     def __setstate__(self, state):
         super(Boolean, self).__setstate__(state)
         self._value = state.value
-        self.tag = state.tag
-        self._expl = state.expl
-        self.default = state.default
-        self.optional = state.optional
-        self.offset = state.offset
-        self.llen = state.llen
-        self.vlen = state.vlen
-        self.expl_lenindef = state.expl_lenindef
-        self.lenindef = state.lenindef
-        self.ber_encoded = state.ber_encoded
 
     def __nonzero__(self):
         self._assert_ready()
@@ -1982,23 +2112,11 @@ class Boolean(Obj):
             yield pp
 
 
-IntegerState = namedtuple("IntegerState", (
-    "version",
-    "specs",
-    "value",
-    "bound_min",
-    "bound_max",
-    "tag",
-    "expl",
-    "default",
-    "optional",
-    "offset",
-    "llen",
-    "vlen",
-    "expl_lenindef",
-    "lenindef",
-    "ber_encoded",
-), **NAMEDTUPLE_KWARGS)
+IntegerState = namedtuple(
+    "IntegerState",
+    BasicState._fields + ("specs", "value", "bound_min", "bound_max"),
+    **NAMEDTUPLE_KWARGS
+)
 
 
 class Integer(Obj):
@@ -2105,11 +2223,8 @@ class Integer(Obj):
     def __getstate__(self):
         return IntegerState(
             __version__,
-            self.specs,
-            self._value,
-            self._bound_min,
-            self._bound_max,
             self.tag,
+            self._tag_order,
             self._expl,
             self.default,
             self.optional,
@@ -2119,6 +2234,10 @@ class Integer(Obj):
             self.expl_lenindef,
             self.lenindef,
             self.ber_encoded,
+            self.specs,
+            self._value,
+            self._bound_min,
+            self._bound_max,
         )
 
     def __setstate__(self, state):
@@ -2127,16 +2246,6 @@ class Integer(Obj):
         self._value = state.value
         self._bound_min = state.bound_min
         self._bound_max = state.bound_max
-        self.tag = state.tag
-        self._expl = state.expl
-        self.default = state.default
-        self.optional = state.optional
-        self.offset = state.offset
-        self.llen = state.llen
-        self.vlen = state.vlen
-        self.expl_lenindef = state.expl_lenindef
-        self.lenindef = state.lenindef
-        self.ber_encoded = state.ber_encoded
 
     def __int__(self):
         self._assert_ready()
@@ -2166,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
@@ -2351,23 +2462,11 @@ class Integer(Obj):
             yield pp
 
 
-BitStringState = namedtuple("BitStringState", (
-    "version",
-    "specs",
-    "value",
-    "tag",
-    "expl",
-    "default",
-    "optional",
-    "offset",
-    "llen",
-    "vlen",
-    "expl_lenindef",
-    "lenindef",
-    "ber_encoded",
-    "tag_constructed",
-    "defined",
-), **NAMEDTUPLE_KWARGS)
+BitStringState = namedtuple(
+    "BitStringState",
+    BasicState._fields + ("specs", "value", "tag_constructed", "defined"),
+    **NAMEDTUPLE_KWARGS
+)
 
 
 class BitString(Obj):
@@ -2525,9 +2624,8 @@ class BitString(Obj):
     def __getstate__(self):
         return BitStringState(
             __version__,
-            self.specs,
-            self._value,
             self.tag,
+            self._tag_order,
             self._expl,
             self.default,
             self.optional,
@@ -2537,6 +2635,8 @@ class BitString(Obj):
             self.expl_lenindef,
             self.lenindef,
             self.ber_encoded,
+            self.specs,
+            self._value,
             self.tag_constructed,
             self.defined,
         )
@@ -2545,16 +2645,6 @@ class BitString(Obj):
         super(BitString, self).__setstate__(state)
         self.specs = state.specs
         self._value = state.value
-        self.tag = state.tag
-        self._expl = state.expl
-        self.default = state.default
-        self.optional = state.optional
-        self.offset = state.offset
-        self.llen = state.llen
-        self.vlen = state.vlen
-        self.expl_lenindef = state.expl_lenindef
-        self.lenindef = state.lenindef
-        self.ber_encoded = state.ber_encoded
         self.tag_constructed = state.tag_constructed
         self.defined = state.defined
 
@@ -2565,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]
 
@@ -2585,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__(
@@ -2858,24 +2954,17 @@ class BitString(Obj):
             yield pp
 
 
-OctetStringState = namedtuple("OctetStringState", (
-    "version",
-    "value",
-    "bound_min",
-    "bound_max",
-    "tag",
-    "expl",
-    "default",
-    "optional",
-    "offset",
-    "llen",
-    "vlen",
-    "expl_lenindef",
-    "lenindef",
-    "ber_encoded",
-    "tag_constructed",
-    "defined",
-), **NAMEDTUPLE_KWARGS)
+OctetStringState = namedtuple(
+    "OctetStringState",
+    BasicState._fields + (
+        "value",
+        "bound_min",
+        "bound_max",
+        "tag_constructed",
+        "defined",
+    ),
+    **NAMEDTUPLE_KWARGS
+)
 
 
 class OctetString(Obj):
@@ -2971,10 +3060,8 @@ class OctetString(Obj):
     def __getstate__(self):
         return OctetStringState(
             __version__,
-            self._value,
-            self._bound_min,
-            self._bound_max,
             self.tag,
+            self._tag_order,
             self._expl,
             self.default,
             self.optional,
@@ -2984,6 +3071,9 @@ class OctetString(Obj):
             self.expl_lenindef,
             self.lenindef,
             self.ber_encoded,
+            self._value,
+            self._bound_min,
+            self._bound_max,
             self.tag_constructed,
             self.defined,
         )
@@ -2993,16 +3083,6 @@ class OctetString(Obj):
         self._value = state.value
         self._bound_min = state.bound_min
         self._bound_max = state.bound_max
-        self.tag = state.tag
-        self._expl = state.expl
-        self.default = state.default
-        self.optional = state.optional
-        self.offset = state.offset
-        self.llen = state.llen
-        self.vlen = state.vlen
-        self.expl_lenindef = state.expl_lenindef
-        self.lenindef = state.lenindef
-        self.ber_encoded = state.ber_encoded
         self.tag_constructed = state.tag_constructed
         self.defined = state.defined
 
@@ -3248,19 +3328,7 @@ class OctetString(Obj):
             yield pp
 
 
-NullState = namedtuple("NullState", (
-    "version",
-    "tag",
-    "expl",
-    "default",
-    "optional",
-    "offset",
-    "llen",
-    "vlen",
-    "expl_lenindef",
-    "lenindef",
-    "ber_encoded",
-), **NAMEDTUPLE_KWARGS)
+NullState = namedtuple("NullState", BasicState._fields, **NAMEDTUPLE_KWARGS)
 
 
 class Null(Obj):
@@ -3299,6 +3367,7 @@ class Null(Obj):
         return NullState(
             __version__,
             self.tag,
+            self._tag_order,
             self._expl,
             self.default,
             self.optional,
@@ -3310,19 +3379,6 @@ class Null(Obj):
             self.ber_encoded,
         )
 
-    def __setstate__(self, state):
-        super(Null, self).__setstate__(state)
-        self.tag = state.tag
-        self._expl = state.expl
-        self.default = state.default
-        self.optional = state.optional
-        self.offset = state.offset
-        self.llen = state.llen
-        self.vlen = state.vlen
-        self.expl_lenindef = state.expl_lenindef
-        self.lenindef = state.lenindef
-        self.ber_encoded = state.ber_encoded
-
     def __eq__(self, their):
         if not issubclass(their.__class__, Null):
             return False
@@ -3416,21 +3472,11 @@ class Null(Obj):
             yield pp
 
 
-ObjectIdentifierState = namedtuple("ObjectIdentifierState", (
-    "version",
-    "value",
-    "tag",
-    "expl",
-    "default",
-    "optional",
-    "offset",
-    "llen",
-    "vlen",
-    "expl_lenindef",
-    "lenindef",
-    "ber_encoded",
-    "defines",
-), **NAMEDTUPLE_KWARGS)
+ObjectIdentifierState = namedtuple(
+    "ObjectIdentifierState",
+    BasicState._fields + ("value", "defines"),
+    **NAMEDTUPLE_KWARGS
+)
 
 
 class ObjectIdentifier(Obj):
@@ -3536,8 +3582,8 @@ class ObjectIdentifier(Obj):
     def __getstate__(self):
         return ObjectIdentifierState(
             __version__,
-            self._value,
             self.tag,
+            self._tag_order,
             self._expl,
             self.default,
             self.optional,
@@ -3547,22 +3593,13 @@ class ObjectIdentifier(Obj):
             self.expl_lenindef,
             self.lenindef,
             self.ber_encoded,
+            self._value,
             self.defines,
         )
 
     def __setstate__(self, state):
         super(ObjectIdentifier, self).__setstate__(state)
         self._value = state.value
-        self.tag = state.tag
-        self._expl = state.expl
-        self.default = state.default
-        self.optional = state.optional
-        self.offset = state.offset
-        self.llen = state.llen
-        self.vlen = state.vlen
-        self.expl_lenindef = state.expl_lenindef
-        self.lenindef = state.lenindef
-        self.ber_encoded = state.ber_encoded
         self.defines = state.defines
 
     def __iter__(self):
@@ -4567,21 +4604,11 @@ class BMPString(CommonString):
     asn1_type_name = "BMPString"
 
 
-ChoiceState = namedtuple("ChoiceState", (
-    "version",
-    "specs",
-    "value",
-    "tag",
-    "expl",
-    "default",
-    "optional",
-    "offset",
-    "llen",
-    "vlen",
-    "expl_lenindef",
-    "lenindef",
-    "ber_encoded",
-), **NAMEDTUPLE_KWARGS)
+ChoiceState = namedtuple(
+    "ChoiceState",
+    BasicState._fields + ("specs", "value",),
+    **NAMEDTUPLE_KWARGS
+)
 
 
 class Choice(Obj):
@@ -4658,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:
@@ -4686,9 +4716,8 @@ class Choice(Obj):
     def __getstate__(self):
         return ChoiceState(
             __version__,
-            self.specs,
-            copy(self._value),
             self.tag,
+            self._tag_order,
             self._expl,
             self.default,
             self.optional,
@@ -4698,21 +4727,14 @@ class Choice(Obj):
             self.expl_lenindef,
             self.lenindef,
             self.ber_encoded,
+            self.specs,
+            copy(self._value),
         )
 
     def __setstate__(self, state):
         super(Choice, self).__setstate__(state)
         self.specs = state.specs
         self._value = state.value
-        self._expl = state.expl
-        self.default = state.default
-        self.optional = state.optional
-        self.offset = state.offset
-        self.llen = state.llen
-        self.vlen = state.vlen
-        self.expl_lenindef = state.expl_lenindef
-        self.lenindef = state.lenindef
-        self.ber_encoded = state.ber_encoded
 
     def __eq__(self, their):
         if (their.__class__ == tuple) and len(their) == 2:
@@ -4741,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)
@@ -4886,20 +4917,11 @@ class PrimitiveTypes(Choice):
     ))
 
 
-AnyState = namedtuple("AnyState", (
-    "version",
-    "value",
-    "tag",
-    "expl",
-    "optional",
-    "offset",
-    "llen",
-    "vlen",
-    "expl_lenindef",
-    "lenindef",
-    "ber_encoded",
-    "defined",
-), **NAMEDTUPLE_KWARGS)
+AnyState = namedtuple(
+    "AnyState",
+    BasicState._fields + ("value", "defined"),
+    **NAMEDTUPLE_KWARGS
+)
 
 
 class Any(Obj):
@@ -4926,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
@@ -4948,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:
@@ -4959,9 +5000,10 @@ class Any(Obj):
     def __getstate__(self):
         return AnyState(
             __version__,
-            self._value,
             self.tag,
+            self._tag_order,
             self._expl,
+            None,
             self.optional,
             self.offset,
             self.llen,
@@ -4969,21 +5011,13 @@ class Any(Obj):
             self.expl_lenindef,
             self.lenindef,
             self.ber_encoded,
+            self._value,
             self.defined,
         )
 
     def __setstate__(self, state):
         super(Any, self).__setstate__(state)
         self._value = state.value
-        self.tag = state.tag
-        self._expl = state.expl
-        self.optional = state.optional
-        self.offset = state.offset
-        self.llen = state.llen
-        self.vlen = state.vlen
-        self.expl_lenindef = state.expl_lenindef
-        self.lenindef = state.lenindef
-        self.ber_encoded = state.ber_encoded
         self.defined = state.defined
 
     def __eq__(self, their):
@@ -5133,7 +5167,7 @@ def get_def_by_path(defines_by_path, sub_decode_path):
         if len(path) != len(sub_decode_path):
             continue
         for p1, p2 in zip(path, sub_decode_path):
-            if (p1 != any) and (p1 != p2):
+            if (not p1 is any) and (p1 != p2):
                 break
         else:
             return define
@@ -5164,21 +5198,11 @@ def abs_decode_path(decode_path, rel_path):
     return decode_path + rel_path
 
 
-SequenceState = namedtuple("SequenceState", (
-    "version",
-    "specs",
-    "value",
-    "tag",
-    "expl",
-    "default",
-    "optional",
-    "offset",
-    "llen",
-    "vlen",
-    "expl_lenindef",
-    "lenindef",
-    "ber_encoded",
-), **NAMEDTUPLE_KWARGS)
+SequenceState = namedtuple(
+    "SequenceState",
+    BasicState._fields + ("specs", "value",),
+    **NAMEDTUPLE_KWARGS
+)
 
 
 class Sequence(Obj):
@@ -5333,9 +5357,8 @@ class Sequence(Obj):
     def __getstate__(self):
         return SequenceState(
             __version__,
-            self.specs,
-            {k: copy(v) for k, v in iteritems(self._value)},
             self.tag,
+            self._tag_order,
             self._expl,
             self.default,
             self.optional,
@@ -5345,22 +5368,14 @@ class Sequence(Obj):
             self.expl_lenindef,
             self.lenindef,
             self.ber_encoded,
+            self.specs,
+            {k: copy(v) for k, v in iteritems(self._value)},
         )
 
     def __setstate__(self, state):
         super(Sequence, self).__setstate__(state)
         self.specs = state.specs
         self._value = state.value
-        self.tag = state.tag
-        self._expl = state.expl
-        self.default = state.default
-        self.optional = state.optional
-        self.offset = state.offset
-        self.llen = state.llen
-        self.vlen = state.vlen
-        self.expl_lenindef = state.expl_lenindef
-        self.lenindef = state.lenindef
-        self.ber_encoded = state.ber_encoded
 
     def __eq__(self, their):
         if not isinstance(their, self.__class__):
@@ -5418,19 +5433,17 @@ class Sequence(Obj):
             return spec.default
         return None
 
-    def _encoded_values(self):
-        raws = []
+    def _values_for_encoding(self):
         for name, spec in iteritems(self.specs):
             value = self._value.get(name)
             if value is None:
                 if spec.optional:
                     continue
                 raise ObjNotReady(name)
-            raws.append(value.encode())
-        return raws
+            yield value
 
     def _encode(self):
-        v = b"".join(self._encoded_values())
+        v = b"".join(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):
@@ -5665,8 +5678,8 @@ class Set(Sequence):
     .. _allow_unordered_set_ctx:
 
     DER prohibits unordered values encoding and will raise an error
-    during decode. If If :ref:`bered <bered_ctx>` context option is set,
-    then no error will occure. Also you can disable strict values
+    during decode. If :ref:`bered <bered_ctx>` context option is set,
+    then no error will occur. Also you can disable strict values
     ordering check by setting ``"allow_unordered_set": True``
     :ref:`context <ctx>` option.
     """
@@ -5675,14 +5688,12 @@ class Set(Sequence):
     asn1_type_name = "SET"
 
     def _encode(self):
-        raws = self._encoded_values()
-        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)
@@ -5736,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(
@@ -5770,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:
@@ -5793,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,
@@ -5828,23 +5843,11 @@ class Set(Sequence):
         return obj, tail
 
 
-SequenceOfState = namedtuple("SequenceOfState", (
-    "version",
-    "spec",
-    "value",
-    "bound_min",
-    "bound_max",
-    "tag",
-    "expl",
-    "default",
-    "optional",
-    "offset",
-    "llen",
-    "vlen",
-    "expl_lenindef",
-    "lenindef",
-    "ber_encoded",
-), **NAMEDTUPLE_KWARGS)
+SequenceOfState = namedtuple(
+    "SequenceOfState",
+    BasicState._fields + ("spec", "value", "bound_min", "bound_max"),
+    **NAMEDTUPLE_KWARGS
+)
 
 
 class SequenceOf(Obj):
@@ -5945,11 +5948,8 @@ class SequenceOf(Obj):
     def __getstate__(self):
         return SequenceOfState(
             __version__,
-            self.spec,
-            [copy(v) for v in self._value],
-            self._bound_min,
-            self._bound_max,
             self.tag,
+            self._tag_order,
             self._expl,
             self.default,
             self.optional,
@@ -5959,6 +5959,10 @@ class SequenceOf(Obj):
             self.expl_lenindef,
             self.lenindef,
             self.ber_encoded,
+            self.spec,
+            [copy(v) for v in self._value],
+            self._bound_min,
+            self._bound_max,
         )
 
     def __setstate__(self, state):
@@ -5967,16 +5971,6 @@ class SequenceOf(Obj):
         self._value = state.value
         self._bound_min = state.bound_min
         self._bound_max = state.bound_max
-        self.tag = state.tag
-        self._expl = state.expl
-        self.default = state.default
-        self.optional = state.optional
-        self.offset = state.offset
-        self.llen = state.llen
-        self.vlen = state.vlen
-        self.expl_lenindef = state.expl_lenindef
-        self.lenindef = state.lenindef
-        self.ber_encoded = state.ber_encoded
 
     def __eq__(self, their):
         if isinstance(their, self.__class__):
@@ -6042,11 +6036,11 @@ class SequenceOf(Obj):
     def __getitem__(self, key):
         return self._value[key]
 
-    def _encoded_values(self):
-        return [v.encode() for v in self._value]
+    def _values_for_encoding(self):
+        return iter(self._value)
 
     def _encode(self):
-        v = b"".join(self._encoded_values())
+        v = b"".join(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, ordering_check=False):
@@ -6209,9 +6203,7 @@ class SetOf(SequenceOf):
     asn1_type_name = "SET OF"
 
     def _encode(self):
-        raws = self._encoded_values()
-        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):
@@ -6367,6 +6359,7 @@ def main():  # pragma: no cover
     if args.defines_by_path is not None:
         ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
     obj, tail = schema().decode(der, ctx=ctx)
+    from os import environ
     print(pprinter(
         obj,
         oid_maps=oid_maps,