]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - pyderasn.py
Grammar typos
[pyderasn.git] / pyderasn.py
index 92c18240c923115b6bed263ccdd2a9a10d6d6193..5adc5b068d17915f0c543144b66557b9fc355f30 100755 (executable)
@@ -21,7 +21,7 @@ format, unmarshal them in BER/CER/DER ones.
 
     >>> i = Integer(123)
     >>> raw = i.encode()
-    >>> Integer().decode(raw) == i
+    >>> Integer().decod(raw) == i
     True
 
 There are primitive types, holding single values
@@ -162,21 +162,27 @@ All objects have ``ready`` boolean property, that tells if object is
 ready to be encoded. If that kind of action is performed on unready
 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
 
-All objects have ``copy()`` method, that returns their copy, that can be
+All objects are friendly to ``copy.copy()`` and copied objects can be
 safely mutated.
 
+Also all objects can be safely ``pickle``-d, but pay attention that
+pickling among different PyDERASN versions is prohibited.
+
 .. _decoding:
 
 Decoding
 --------
 
-Decoding is performed using ``decode()`` method. ``offset`` optional
-argument could be used to set initial object's offset in the binary
-data, for convenience. It returns decoded object and remaining
-unmarshalled data (tail). Internally all work is done on
+Decoding is performed using :py:meth:`pyderasn.Obj.decode` method.
+``offset`` optional argument could be used to set initial object's
+offset in the binary data, for convenience. It returns decoded object
+and remaining unmarshalled data (tail). Internally all work is done on
 ``memoryview(data)``, and you can leave returning tail as a memoryview,
 by specifying ``leavemm=True`` argument.
 
+Also note convenient :py:meth:`pyderasn.Obj.decod` method, that
+immediately checks and raises if there is non-empty tail.
+
 When object is decoded, ``decoded`` property is true and you can safely
 use following properties:
 
@@ -206,9 +212,9 @@ When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
 Context
 _______
 
-You can specify so called context keyword argument during ``decode()``
-invocation. It is dictionary containing various options governing
-decoding process.
+You can specify so called context keyword argument during
+:py:meth:`pyderasn.Obj.decode` invocation. It is dictionary containing
+various options governing decoding process.
 
 Currently available context options:
 
@@ -430,7 +436,7 @@ For example, again for CMS, you want to automatically decode
 structures it may hold. Also, automatically decode ``controlSequence``
 of ``PKIResponse``::
 
-    content_info, tail = ContentInfo().decode(data, ctx={"defines_by_path": (
+    content_info = ContentInfo().decod(data, ctx={"defines_by_path": (
         (
             ("contentType",),
             ((("content",), {id_signedData: SignedData()}),),
@@ -681,7 +687,7 @@ except ImportError:  # pragma: no cover
     def colored(what, *args, **kwargs):
         return what
 
-__version__ = "5.6"
+__version__ = "6.0"
 
 __all__ = (
     "Any",
@@ -1110,11 +1116,26 @@ class Obj(object):
         """
         return (self.llen + self.vlen) > 0
 
-    def copy(self):  # pragma: no cover
-        """Make a copy of object, safe to be mutated
+    def __getstate__(self):  # pragma: no cover
+        """Used for making safe to be mutable pickleable copies
         """
         raise NotImplementedError()
 
+    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
+
     @property
     def tlen(self):
         """See :ref:`decoding`
@@ -1158,6 +1179,11 @@ class Obj(object):
             return raw
         return b"".join((self._expl, len_encode(len(raw)), raw))
 
+    def hexencode(self):
+        """Do hexadecimal encoded :py:meth:`pyderasn.Obj.encode`
+        """
+        return hexenc(self.encode())
+
     def decode(
             self,
             data,
@@ -1178,7 +1204,8 @@ class Obj(object):
         :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)
-        :param _ctx_immutable: do we need to copy ``ctx`` before using it
+        :param _ctx_immutable: do we need to ``copy.copy()`` ``ctx``
+                               before using it?
         :returns: (Obj, remaining data)
 
         .. seealso:: :ref:`decoding`
@@ -1301,6 +1328,16 @@ class Obj(object):
             raise ExceedingData(len(tail))
         return obj
 
+    def hexdecode(self, data, *args, **kwargs):
+        """Do :py:meth:`pyderasn.Obj.decode` with hexadecimal decoded data
+        """
+        return self.decode(hexdec(data), *args, **kwargs)
+
+    def hexdecod(self, data, *args, **kwargs):
+        """Do :py:meth:`pyderasn.Obj.decod` with hexadecimal decoded data
+        """
+        return self.decod(hexdec(data), *args, **kwargs)
+
     @property
     def expled(self):
         """See :ref:`decoding`
@@ -1632,9 +1669,8 @@ def pprint(
     """Pretty print object
 
     :param Obj obj: object you want to pretty print
-    :param oid_maps: list of ``OID <-> humand readable string`` dictionary.
-                     When OID from it is met, then its humand readable form
-                     is printed
+    :param oid_maps: list of ``str(OID) <-> human readable string`` dictionary.
+                     Its human readable form is printed when OID is met
     :param big_blobs: if large binary objects are met (like OctetString
                       values), do we need to print them too, on separate
                       lines
@@ -1688,6 +1724,22 @@ def pprint(
 # ASN.1 primitive types
 ########################################################################
 
+BooleanState = namedtuple("BooleanState", (
+    "version",
+    "value",
+    "tag",
+    "expl",
+    "default",
+    "optional",
+    "offset",
+    "llen",
+    "vlen",
+    "expl_lenindef",
+    "lenindef",
+    "ber_encoded",
+))
+
+
 class Boolean(Obj):
     """``BOOLEAN`` boolean type
 
@@ -1742,20 +1794,35 @@ class Boolean(Obj):
     def ready(self):
         return self._value is not None
 
-    def copy(self):
-        obj = self.__class__()
-        obj._value = self._value
-        obj.tag = self.tag
-        obj._expl = self._expl
-        obj.default = self.default
-        obj.optional = self.optional
-        obj.offset = self.offset
-        obj.llen = self.llen
-        obj.vlen = self.vlen
-        obj.expl_lenindef = self.expl_lenindef
-        obj.lenindef = self.lenindef
-        obj.ber_encoded = self.ber_encoded
-        return obj
+    def __getstate__(self):
+        return BooleanState(
+            __version__,
+            self._value,
+            self.tag,
+            self._expl,
+            self.default,
+            self.optional,
+            self.offset,
+            self.llen,
+            self.vlen,
+            self.expl_lenindef,
+            self.lenindef,
+            self.ber_encoded,
+        )
+
+    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()
@@ -1898,6 +1965,25 @@ 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",
+))
+
+
 class Integer(Obj):
     """``INTEGER`` integer type
 
@@ -1999,22 +2085,41 @@ class Integer(Obj):
     def ready(self):
         return self._value is not None
 
-    def copy(self):
-        obj = self.__class__(_specs=self.specs)
-        obj._value = self._value
-        obj._bound_min = self._bound_min
-        obj._bound_max = self._bound_max
-        obj.tag = self.tag
-        obj._expl = self._expl
-        obj.default = self.default
-        obj.optional = self.optional
-        obj.offset = self.offset
-        obj.llen = self.llen
-        obj.vlen = self.vlen
-        obj.expl_lenindef = self.expl_lenindef
-        obj.lenindef = self.lenindef
-        obj.ber_encoded = self.ber_encoded
-        return obj
+    def __getstate__(self):
+        return IntegerState(
+            __version__,
+            self.specs,
+            self._value,
+            self._bound_min,
+            self._bound_max,
+            self.tag,
+            self._expl,
+            self.default,
+            self.optional,
+            self.offset,
+            self.llen,
+            self.vlen,
+            self.expl_lenindef,
+            self.lenindef,
+            self.ber_encoded,
+        )
+
+    def __setstate__(self, state):
+        super(Integer, self).__setstate__(state)
+        self.specs = state.specs
+        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()
@@ -2230,6 +2335,23 @@ class Integer(Obj):
 
 
 SET01 = frozenset(("0", "1"))
+BitStringState = namedtuple("BitStringState", (
+    "version",
+    "specs",
+    "value",
+    "tag",
+    "expl",
+    "default",
+    "optional",
+    "offset",
+    "llen",
+    "vlen",
+    "expl_lenindef",
+    "lenindef",
+    "ber_encoded",
+    "tag_constructed",
+    "defined",
+))
 
 
 class BitString(Obj):
@@ -2384,23 +2506,41 @@ class BitString(Obj):
     def ready(self):
         return self._value is not None
 
-    def copy(self):
-        obj = self.__class__(_specs=self.specs)
-        value = self._value
-        if value is not None:
-            value = (value[0], value[1])
-        obj._value = value
-        obj.tag = self.tag
-        obj._expl = self._expl
-        obj.default = self.default
-        obj.optional = self.optional
-        obj.offset = self.offset
-        obj.llen = self.llen
-        obj.vlen = self.vlen
-        obj.expl_lenindef = self.expl_lenindef
-        obj.lenindef = self.lenindef
-        obj.ber_encoded = self.ber_encoded
-        return obj
+    def __getstate__(self):
+        return BitStringState(
+            __version__,
+            self.specs,
+            self._value,
+            self.tag,
+            self._expl,
+            self.default,
+            self.optional,
+            self.offset,
+            self.llen,
+            self.vlen,
+            self.expl_lenindef,
+            self.lenindef,
+            self.ber_encoded,
+            self.tag_constructed,
+            self.defined,
+        )
+
+    def __setstate__(self, state):
+        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
 
     def __iter__(self):
         self._assert_ready()
@@ -2705,6 +2845,26 @@ 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",
+))
+
+
 class OctetString(Obj):
     """``OCTET STRING`` binary string type
 
@@ -2794,22 +2954,43 @@ class OctetString(Obj):
     def ready(self):
         return self._value is not None
 
-    def copy(self):
-        obj = self.__class__()
-        obj._value = self._value
-        obj._bound_min = self._bound_min
-        obj._bound_max = self._bound_max
-        obj.tag = self.tag
-        obj._expl = self._expl
-        obj.default = self.default
-        obj.optional = self.optional
-        obj.offset = self.offset
-        obj.llen = self.llen
-        obj.vlen = self.vlen
-        obj.expl_lenindef = self.expl_lenindef
-        obj.lenindef = self.lenindef
-        obj.ber_encoded = self.ber_encoded
-        return obj
+    def __getstate__(self):
+        return OctetStringState(
+            __version__,
+            self._value,
+            self._bound_min,
+            self._bound_max,
+            self.tag,
+            self._expl,
+            self.default,
+            self.optional,
+            self.offset,
+            self.llen,
+            self.vlen,
+            self.expl_lenindef,
+            self.lenindef,
+            self.ber_encoded,
+            self.tag_constructed,
+            self.defined,
+        )
+
+    def __setstate__(self, state):
+        super(OctetString, self).__setstate__(state)
+        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
 
     def __bytes__(self):
         self._assert_ready()
@@ -3054,6 +3235,21 @@ class OctetString(Obj):
             yield pp
 
 
+NullState = namedtuple("NullState", (
+    "version",
+    "tag",
+    "expl",
+    "default",
+    "optional",
+    "offset",
+    "llen",
+    "vlen",
+    "expl_lenindef",
+    "lenindef",
+    "ber_encoded",
+))
+
+
 class Null(Obj):
     """``NULL`` null object
 
@@ -3086,19 +3282,33 @@ class Null(Obj):
     def ready(self):
         return True
 
-    def copy(self):
-        obj = self.__class__()
-        obj.tag = self.tag
-        obj._expl = self._expl
-        obj.default = self.default
-        obj.optional = self.optional
-        obj.offset = self.offset
-        obj.llen = self.llen
-        obj.vlen = self.vlen
-        obj.expl_lenindef = self.expl_lenindef
-        obj.lenindef = self.lenindef
-        obj.ber_encoded = self.ber_encoded
-        return obj
+    def __getstate__(self):
+        return NullState(
+            __version__,
+            self.tag,
+            self._expl,
+            self.default,
+            self.optional,
+            self.offset,
+            self.llen,
+            self.vlen,
+            self.expl_lenindef,
+            self.lenindef,
+            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):
@@ -3193,6 +3403,23 @@ class Null(Obj):
             yield pp
 
 
+ObjectIdentifierState = namedtuple("ObjectIdentifierState", (
+    "version",
+    "value",
+    "tag",
+    "expl",
+    "default",
+    "optional",
+    "offset",
+    "llen",
+    "vlen",
+    "expl_lenindef",
+    "lenindef",
+    "ber_encoded",
+    "defines",
+))
+
+
 class ObjectIdentifier(Obj):
     """``OBJECT IDENTIFIER`` OID type
 
@@ -3291,21 +3518,37 @@ class ObjectIdentifier(Obj):
     def ready(self):
         return self._value is not None
 
-    def copy(self):
-        obj = self.__class__()
-        obj._value = self._value
-        obj.defines = self.defines
-        obj.tag = self.tag
-        obj._expl = self._expl
-        obj.default = self.default
-        obj.optional = self.optional
-        obj.offset = self.offset
-        obj.llen = self.llen
-        obj.vlen = self.vlen
-        obj.expl_lenindef = self.expl_lenindef
-        obj.lenindef = self.lenindef
-        obj.ber_encoded = self.ber_encoded
-        return obj
+    def __getstate__(self):
+        return ObjectIdentifierState(
+            __version__,
+            self._value,
+            self.tag,
+            self._expl,
+            self.default,
+            self.optional,
+            self.offset,
+            self.llen,
+            self.vlen,
+            self.expl_lenindef,
+            self.lenindef,
+            self.ber_encoded,
+            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):
         self._assert_ready()
@@ -3539,23 +3782,6 @@ class Enumerated(Integer):
             raise InvalidValueType((self.__class__, int, str))
         return value
 
-    def copy(self):
-        obj = self.__class__(_specs=self.specs)
-        obj._value = self._value
-        obj._bound_min = self._bound_min
-        obj._bound_max = self._bound_max
-        obj.tag = self.tag
-        obj._expl = self._expl
-        obj.default = self.default
-        obj.optional = self.optional
-        obj.offset = self.offset
-        obj.llen = self.llen
-        obj.vlen = self.vlen
-        obj.expl_lenindef = self.expl_lenindef
-        obj.lenindef = self.lenindef
-        obj.ber_encoded = self.ber_encoded
-        return obj
-
     def __call__(
             self,
             value=None,
@@ -3766,6 +3992,12 @@ class NumericString(AllowableCharsMixin, CommonString):
         return value
 
 
+PrintableStringState = namedtuple(
+    "PrintableStringState",
+    OctetStringState._fields + ("allowable_chars",),
+)
+
+
 class PrintableString(AllowableCharsMixin, CommonString):
     """Printable string
 
@@ -3773,6 +4005,10 @@ class PrintableString(AllowableCharsMixin, CommonString):
 
     >>> PrintableString().allowable_chars
     frozenset([' ', "'", ..., 'z'])
+    >>> obj = PrintableString("foo*bar", allow_asterisk=True)
+    PrintableString PrintableString foo*bar
+    >>> obj.allow_asterisk, obj.allow_ampersand
+    (True, False)
     """
     __slots__ = ()
     tag_default = tag_encode(19)
@@ -3808,16 +4044,33 @@ class PrintableString(AllowableCharsMixin, CommonString):
             value, bounds, impl, expl, default, optional, _decoded,
         )
 
+    @property
+    def allow_asterisk(self):
+        """Is asterisk character allowed?
+        """
+        return self._asterisk <= self._allowable_chars
+
+    @property
+    def allow_ampersand(self):
+        """Is ampersand character allowed?
+        """
+        return self._ampersand <= self._allowable_chars
+
     def _value_sanitize(self, value):
         value = super(PrintableString, self)._value_sanitize(value)
         if not frozenset(value) <= self._allowable_chars:
             raise DecodeError("non-printable value")
         return value
 
-    def copy(self):
-        obj = super(PrintableString, self).copy()
-        obj._allowable_chars = self._allowable_chars
-        return obj
+    def __getstate__(self):
+        return PrintableStringState(
+            *super(PrintableString, self).__getstate__(),
+            **{"allowable_chars": self._allowable_chars}
+        )
+
+    def __setstate__(self, state):
+        super(PrintableString, self).__setstate__(state)
+        self._allowable_chars = state.allowable_chars
 
     def __call__(
             self,
@@ -3838,8 +4091,8 @@ class PrintableString(AllowableCharsMixin, CommonString):
             expl=self._expl if expl is None else expl,
             default=self.default if default is None else default,
             optional=self.optional if optional is None else optional,
-            allow_asterisk=self._asterisk <= self._allowable_chars,
-            allow_ampersand=self._ampersand <= self._allowable_chars,
+            allow_asterisk=self.allow_asterisk,
+            allow_ampersand=self.allow_ampersand,
         )
 
 
@@ -4047,6 +4300,10 @@ class GeneralizedTime(UTCTime):
        Only microsecond fractions are supported.
        :py:exc:`pyderasn.DecodeError` will be raised during decoding of
        higher precision values.
+
+    .. warning::
+
+       Zero year is unsupported.
     """
     __slots__ = ()
     tag_default = tag_encode(24)
@@ -4055,7 +4312,7 @@ class GeneralizedTime(UTCTime):
     def _strptime(self, value):
         l = len(value)
         if l == LEN_YYYYMMDDHHMMSSZ:
-            # datetime.strptime's format: %y%m%d%H%M%SZ
+            # datetime.strptime's format: %Y%m%d%H%M%SZ
             if value[-1] != "Z":
                 raise ValueError("non UTC timezone")
             return datetime(
@@ -4158,6 +4415,23 @@ 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",
+))
+
+
 class Choice(Obj):
     """``CHOICE`` special type
 
@@ -4231,7 +4505,7 @@ class Choice(Obj):
             default_obj._value = default_value
             self.default = default_obj
             if value is None:
-                self._value = default_obj.copy()._value
+                self._value = copy(default_obj._value)
 
     def _value_sanitize(self, value):
         if isinstance(value, tuple) and len(value) == 2:
@@ -4257,21 +4531,36 @@ class Choice(Obj):
             self._value[1].bered
         )
 
-    def copy(self):
-        obj = self.__class__(schema=self.specs)
-        obj._expl = self._expl
-        obj.default = self.default
-        obj.optional = self.optional
-        obj.offset = self.offset
-        obj.llen = self.llen
-        obj.vlen = self.vlen
-        obj.expl_lenindef = self.expl_lenindef
-        obj.lenindef = self.lenindef
-        obj.ber_encoded = self.ber_encoded
-        value = self._value
-        if value is not None:
-            obj._value = (value[0], value[1].copy())
-        return obj
+    def __getstate__(self):
+        return ChoiceState(
+            __version__,
+            self.specs,
+            copy(self._value),
+            self.tag,
+            self._expl,
+            self.default,
+            self.optional,
+            self.offset,
+            self.llen,
+            self.vlen,
+            self.expl_lenindef,
+            self.lenindef,
+            self.ber_encoded,
+        )
+
+    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 isinstance(their, tuple) and len(their) == 2:
@@ -4415,9 +4704,9 @@ class PrimitiveTypes(Choice):
 
     It could be useful for general decoding of some unspecified values:
 
-    >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
+    >>> PrimitiveTypes().decod(hexdec("0403666f6f")).value
     OCTET STRING 3 bytes 666f6f
-    >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
+    >>> PrimitiveTypes().decod(hexdec("0203123456")).value
     INTEGER 1193046
     """
     __slots__ = ()
@@ -4445,6 +4734,22 @@ class PrimitiveTypes(Choice):
     ))
 
 
+AnyState = namedtuple("AnyState", (
+    "version",
+    "value",
+    "tag",
+    "expl",
+    "optional",
+    "offset",
+    "llen",
+    "vlen",
+    "expl_lenindef",
+    "lenindef",
+    "ber_encoded",
+    "defined",
+))
+
+
 class Any(Obj):
     """``ANY`` special type
 
@@ -4499,19 +4804,35 @@ class Any(Obj):
             return False
         return self.defined[1].bered
 
-    def copy(self):
-        obj = self.__class__()
-        obj._value = self._value
-        obj.tag = self.tag
-        obj._expl = self._expl
-        obj.optional = self.optional
-        obj.offset = self.offset
-        obj.llen = self.llen
-        obj.vlen = self.vlen
-        obj.expl_lenindef = self.expl_lenindef
-        obj.lenindef = self.lenindef
-        obj.ber_encoded = self.ber_encoded
-        return obj
+    def __getstate__(self):
+        return AnyState(
+            __version__,
+            self._value,
+            self.tag,
+            self._expl,
+            self.optional,
+            self.offset,
+            self.llen,
+            self.vlen,
+            self.expl_lenindef,
+            self.lenindef,
+            self.ber_encoded,
+            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):
         if isinstance(their, binary_type):
@@ -4691,6 +5012,23 @@ 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",
+))
+
+
 class Sequence(Obj):
     """``SEQUENCE`` structure type
 
@@ -4820,7 +5158,7 @@ class Sequence(Obj):
             default_obj._value = default_value
             self.default = default_obj
             if value is None:
-                self._value = default_obj.copy()._value
+                self._value = copy(default_obj._value)
 
     @property
     def ready(self):
@@ -4840,20 +5178,37 @@ class Sequence(Obj):
             return True
         return any(value.bered for value in itervalues(self._value))
 
-    def copy(self):
-        obj = self.__class__(schema=self.specs)
-        obj.tag = self.tag
-        obj._expl = self._expl
-        obj.default = self.default
-        obj.optional = self.optional
-        obj.offset = self.offset
-        obj.llen = self.llen
-        obj.vlen = self.vlen
-        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 iteritems(self._value)}
-        return obj
+    def __getstate__(self):
+        return SequenceState(
+            __version__,
+            self.specs,
+            {k: copy(v) for k, v in iteritems(self._value)},
+            self.tag,
+            self._expl,
+            self.default,
+            self.optional,
+            self.offset,
+            self.llen,
+            self.vlen,
+            self.expl_lenindef,
+            self.lenindef,
+            self.ber_encoded,
+        )
+
+    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__):
@@ -5320,6 +5675,25 @@ 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",
+))
+
+
 class SequenceOf(Obj):
     """``SEQUENCE OF`` sequence type
 
@@ -5389,7 +5763,7 @@ class SequenceOf(Obj):
             default_obj._value = default_value
             self.default = default_obj
             if value is None:
-                self._value = default_obj.copy()._value
+                self._value = copy(default_obj._value)
 
     def _value_sanitize(self, value):
         if issubclass(value.__class__, SequenceOf):
@@ -5415,22 +5789,41 @@ class SequenceOf(Obj):
             return True
         return any(v.bered for v in self._value)
 
-    def copy(self):
-        obj = self.__class__(schema=self.spec)
-        obj._bound_min = self._bound_min
-        obj._bound_max = self._bound_max
-        obj.tag = self.tag
-        obj._expl = self._expl
-        obj.default = self.default
-        obj.optional = self.optional
-        obj.offset = self.offset
-        obj.llen = self.llen
-        obj.vlen = self.vlen
-        obj.expl_lenindef = self.expl_lenindef
-        obj.lenindef = self.lenindef
-        obj.ber_encoded = self.ber_encoded
-        obj._value = [v.copy() for v in self._value]
-        return 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._expl,
+            self.default,
+            self.optional,
+            self.offset,
+            self.llen,
+            self.vlen,
+            self.expl_lenindef,
+            self.lenindef,
+            self.ber_encoded,
+        )
+
+    def __setstate__(self, state):
+        super(SequenceOf, self).__setstate__(state)
+        self.spec = state.spec
+        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__):