summaryrefslogtreecommitdiff
path: root/python/pyasn1
diff options
context:
space:
mode:
Diffstat (limited to 'python/pyasn1')
-rw-r--r--python/pyasn1/CHANGES278
-rw-r--r--python/pyasn1/LICENSE24
-rw-r--r--python/pyasn1/MANIFEST.in3
-rw-r--r--python/pyasn1/PKG-INFO26
-rw-r--r--python/pyasn1/README68
-rw-r--r--python/pyasn1/THANKS4
-rw-r--r--python/pyasn1/TODO36
-rw-r--r--python/pyasn1/doc/codecs.html503
-rw-r--r--python/pyasn1/doc/constraints.html436
-rw-r--r--python/pyasn1/doc/constructed.html377
-rw-r--r--python/pyasn1/doc/intro.html156
-rw-r--r--python/pyasn1/doc/pyasn1-tutorial.html2405
-rw-r--r--python/pyasn1/doc/scalar.html794
-rw-r--r--python/pyasn1/doc/tagging.html233
-rw-r--r--python/pyasn1/pyasn1/__init__.py8
-rw-r--r--python/pyasn1/pyasn1/codec/__init__.py1
-rw-r--r--python/pyasn1/pyasn1/codec/ber/__init__.py1
-rw-r--r--python/pyasn1/pyasn1/codec/ber/decoder.py808
-rw-r--r--python/pyasn1/pyasn1/codec/ber/encoder.py353
-rw-r--r--python/pyasn1/pyasn1/codec/ber/eoo.py8
-rw-r--r--python/pyasn1/pyasn1/codec/cer/__init__.py1
-rw-r--r--python/pyasn1/pyasn1/codec/cer/decoder.py35
-rw-r--r--python/pyasn1/pyasn1/codec/cer/encoder.py87
-rw-r--r--python/pyasn1/pyasn1/codec/der/__init__.py1
-rw-r--r--python/pyasn1/pyasn1/codec/der/decoder.py9
-rw-r--r--python/pyasn1/pyasn1/codec/der/encoder.py28
-rw-r--r--python/pyasn1/pyasn1/compat/__init__.py1
-rw-r--r--python/pyasn1/pyasn1/compat/octets.py20
-rw-r--r--python/pyasn1/pyasn1/debug.py65
-rw-r--r--python/pyasn1/pyasn1/error.py3
-rw-r--r--python/pyasn1/pyasn1/type/__init__.py1
-rw-r--r--python/pyasn1/pyasn1/type/base.py249
-rw-r--r--python/pyasn1/pyasn1/type/char.py61
-rw-r--r--python/pyasn1/pyasn1/type/constraint.py200
-rw-r--r--python/pyasn1/pyasn1/type/error.py3
-rw-r--r--python/pyasn1/pyasn1/type/namedtype.py132
-rw-r--r--python/pyasn1/pyasn1/type/namedval.py46
-rw-r--r--python/pyasn1/pyasn1/type/tag.py122
-rw-r--r--python/pyasn1/pyasn1/type/tagmap.py52
-rw-r--r--python/pyasn1/pyasn1/type/univ.py1042
-rw-r--r--python/pyasn1/pyasn1/type/useful.py12
-rw-r--r--python/pyasn1/setup.cfg5
-rw-r--r--python/pyasn1/setup.py115
-rw-r--r--python/pyasn1/test/__init__.py1
-rw-r--r--python/pyasn1/test/codec/__init__.py1
-rw-r--r--python/pyasn1/test/codec/ber/__init__.py1
-rw-r--r--python/pyasn1/test/codec/ber/suite.py22
-rw-r--r--python/pyasn1/test/codec/ber/test_decoder.py535
-rw-r--r--python/pyasn1/test/codec/ber/test_encoder.py338
-rw-r--r--python/pyasn1/test/codec/cer/__init__.py1
-rw-r--r--python/pyasn1/test/codec/cer/suite.py22
-rw-r--r--python/pyasn1/test/codec/cer/test_decoder.py31
-rw-r--r--python/pyasn1/test/codec/cer/test_encoder.py107
-rw-r--r--python/pyasn1/test/codec/der/__init__.py1
-rw-r--r--python/pyasn1/test/codec/der/suite.py22
-rw-r--r--python/pyasn1/test/codec/der/test_decoder.py20
-rw-r--r--python/pyasn1/test/codec/der/test_encoder.py44
-rw-r--r--python/pyasn1/test/codec/suite.py29
-rw-r--r--python/pyasn1/test/suite.py26
-rw-r--r--python/pyasn1/test/type/__init__.py1
-rw-r--r--python/pyasn1/test/type/suite.py20
-rw-r--r--python/pyasn1/test/type/test_constraint.py280
-rw-r--r--python/pyasn1/test/type/test_namedtype.py87
-rw-r--r--python/pyasn1/test/type/test_tag.py107
-rw-r--r--python/pyasn1/test/type/test_univ.py479
65 files changed, 10987 insertions, 0 deletions
diff --git a/python/pyasn1/CHANGES b/python/pyasn1/CHANGES
new file mode 100644
index 0000000000..561dedd882
--- /dev/null
+++ b/python/pyasn1/CHANGES
@@ -0,0 +1,278 @@
+Revision 0.1.7
+--------------
+
+- License updated to vanilla BSD 2-Clause to ease package use
+ (http://opensource.org/licenses/BSD-2-Clause).
+- Test suite made discoverable by unittest/unittest2 discovery feature.
+- Fix to decoder working on indefinite length substrate -- end-of-octets
+ marker is now detected by both tag and value. Otherwise zero values may
+ interfere with end-of-octets marker.
+- Fix to decoder to fail in cases where tagFormat indicates inappropriate
+ format for the type (e.g. BOOLEAN is always PRIMITIVE, SET is always
+ CONSTRUCTED and OCTET STRING is either of the two)
+- Fix to REAL type encoder to force primitive encoding form encoding.
+- Fix to CHOICE decoder to handle explicitly tagged, indefinite length
+ mode encoding
+- Fix to REAL type decoder to handle negative REAL values correctly. Test
+ case added.
+
+Revision 0.1.6
+--------------
+
+- The compact (valueless) way of encoding zero INTEGERs introduced in
+ 0.1.5 seems to fail miserably as the world is filled with broken
+ BER decoders. So we had to back off the *encoder* for a while.
+ There's still the IntegerEncoder.supportCompactZero flag which
+ enables compact encoding form whenever it evaluates to True.
+- Report package version on debugging code initialization.
+
+Revision 0.1.5
+--------------
+
+- Documentation updated and split into chapters to better match
+ web-site contents.
+- Make prettyPrint() working for non-initialized pyasn1 data objects. It
+ used to throw an exception.
+- Fix to encoder to produce empty-payload INTEGER values for zeros
+- Fix to decoder to support empty-payload INTEGER and REAL values
+- Fix to unit test suites imports to be able to run each from
+ their current directory
+
+Revision 0.1.4
+--------------
+
+- Built-in codec debugging facility added
+- Added some more checks to ObjectIdentifier BER encoder catching
+ posible 2^8 overflow condition by two leading sub-OIDs
+- Implementations overriding the AbstractDecoder.valueDecoder method
+ changed to return the rest of substrate behind the item being processed
+ rather than the unprocessed substrate within the item (which is usually
+ empty).
+- Decoder's recursiveFlag feature generalized as a user callback function
+ which is passed an uninitialized object recovered from substrate and
+ its uninterpreted payload.
+- Catch inappropriate substrate type passed to decoder.
+- Expose tagMap/typeMap/Decoder objects at DER decoder to uniform API.
+- Obsolete __init__.MajorVersionId replaced with __init__.__version__
+ which is now in-sync with distutils.
+- Package classifiers updated.
+- The __init__.py's made non-empty (rumors are that they may be optimized
+ out by package managers).
+- Bail out gracefully whenever Python version is older than 2.4.
+- Fix to Real codec exponent encoding (should be in 2's complement form),
+ some more test cases added.
+- Fix in Boolean truth testing built-in methods
+- Fix to substrate underrun error handling at ObjectIdentifier BER decoder
+- Fix to BER Boolean decoder that allows other pre-computed
+ values besides 0 and 1
+- Fix to leading 0x80 octet handling in DER/CER/DER ObjectIdentifier decoder.
+ See http://www.cosic.esat.kuleuven.be/publications/article-1432.pdf
+
+Revision 0.1.3
+--------------
+
+- Include class name into asn1 value constraint violation exception.
+- Fix to OctetString.prettyOut() method that looses leading zero when
+ building hex string.
+
+Revision 0.1.2
+--------------
+
+- Fix to __long__() to actually return longs on py2k
+- Fix to OctetString.__str__() workings of a non-initialized object.
+- Fix to quote initializer of OctetString.__repr__()
+- Minor fix towards ObjectIdentifier.prettyIn() reliability
+- ObjectIdentifier.__str__() is aliased to prettyPrint()
+- Exlicit repr() calls replaced with '%r'
+
+Revision 0.1.1
+--------------
+
+- Hex/bin string initializer to OctetString object reworked
+ (in a backward-incompatible manner)
+- Fixed float() infinity compatibility issue (affects 2.5 and earlier)
+- Fixed a bug/typo at Boolean CER encoder.
+- Major overhawl for Python 2.4 -- 3.2 compatibility:
+ + get rid of old-style types
+ + drop string module usage
+ + switch to rich comparation
+ + drop explicit long integer type use
+ + map()/filter() replaced with list comprehension
+ + apply() replaced with */**args
+ + switched to use 'key' sort() callback function
+ + support both __nonzero__() and __bool__() methods
+ + modified not to use py3k-incompatible exception syntax
+ + getslice() operator fully replaced with getitem()
+ + dictionary operations made 2K/3K compatible
+ + base type for encoding substrate and OctetString-based types
+ is now 'bytes' when running py3k and 'str' otherwise
+ + OctetString and derivatives now unicode compliant.
+ + OctetString now supports two python-neutral getters: asOcts() & asInts()
+ + print OctetString content in hex whenever it is not printable otherwise
+ + in test suite, implicit relative import replaced with the absolute one
+ + in test suite, string constants replaced with numerics
+
+Revision 0.0.13
+---------------
+
+- Fix to base10 normalization function that loops on univ.Real(0)
+
+Revision 0.0.13b
+----------------
+
+- ASN.1 Real type is now supported properly.
+- Objects of Constructed types now support __setitem__()
+- Set/Sequence objects can now be addressed by their field names (string index)
+ and position (integer index).
+- Typo fix to ber.SetDecoder code that prevented guided decoding operation.
+- Fix to explicitly tagged items decoding support.
+- Fix to OctetString.prettyPrint() to better handle non-printable content.
+- Fix to repr() workings of Choice objects.
+
+Revision 0.0.13a
+----------------
+
+- Major codec re-design.
+- Documentation significantly improved.
+- ASN.1 Any type is now supported.
+- All example ASN.1 modules moved to separate pyasn1-modules package.
+- Fix to initial sub-OID overflow condition detection an encoder.
+- BitString initialization value verification improved.
+- The Set/Sequence.getNameByPosition() method implemented.
+- Fix to proper behaviour of PermittedAlphabetConstraint object.
+- Fix to improper Boolean substrate handling at CER/DER decoders.
+- Changes towards performance improvement:
+ + all dict.has_key() & dict.get() invocations replaced with modern syntax
+ (this breaks compatibility with Python 2.1 and older).
+ + tag and tagset caches introduced to decoder
+ + decoder code improved to prevent unnecessary pyasn1 objects creation
+ + allow disabling components verification when setting components to
+ structured types, this is used by decoder whilst running in guided mode.
+ + BER decoder for integer values now looks up a small set of pre-computed
+ substrate values to save on decoding.
+ + a few pre-computed values configured to ObjectIdentifier BER encoder.
+ + ChoiceDecoder split-off SequenceOf one to save on unnecessary checks.
+ + replace slow hasattr()/getattr() calls with isinstance() introspection.
+ + track the number of initialized components of Constructed types to save
+ on default/optional components initialization.
+ + added a shortcut ObjectIdentifier.asTuple() to be used instead of
+ __getitem__() in hotspots.
+ + use Tag.asTuple() and pure integers at tag encoder.
+ + introduce and use in decoder the baseTagSet attribute of the built-in
+ ASN.1 types.
+
+Revision 0.0.12a
+----------------
+
+- The individual tag/length/value processing methods of
+ encoder.AbstractItemEncoder renamed (leading underscore stripped)
+ to promote overloading in cases where partial substrate processing
+ is required.
+- The ocsp.py, ldap.py example scripts added.
+- Fix to univ.ObjectIdentifier input value handler to disallow negative
+ sub-IDs.
+
+Revision 0.0.11a
+----------------
+
+- Decoder can now treat values of unknown types as opaque OctetString.
+- Fix to Set/SetOf type decoder to handle uninitialized scalar SetOf
+ components correctly.
+
+Revision 0.0.10a
+----------------
+
+- API versioning mechanics retired (pyasn1.v1 -> pyasn1) what makes
+ it possible to zip-import pyasn1 sources (used by egg and py2exe).
+
+Revision 0.0.9a
+---------------
+
+- Allow any non-zero values in Boolean type BER decoder, as it's in
+ accordnance with the standard.
+
+Revision 0.0.8a
+---------------
+
+- Integer.__index__() now supported (for Python 2.5+).
+- Fix to empty value encoding in BitString encoder, test case added.
+- Fix to SequenceOf decoder that prevents it skipping possible Choice
+ typed inner component.
+- Choice.getName() method added for getting currently set component
+ name.
+- OctetsString.prettyPrint() does a single str() against its value
+ eliminating an extra quotes.
+
+Revision 0.0.7a
+---------------
+
+- Large tags (>31) now supported by codecs.
+- Fix to encoder to properly handle explicitly tagged untagged items.
+- All possible value lengths (up to 256^126) now supported by encoders.
+- Fix to Tag class constructor to prevent negative IDs.
+
+Revision 0.0.6a
+---------------
+
+- Make use of setuptools.
+- Constraints derivation verification (isSuperTypeOf()/isSubTypeOf()) fixed.
+- Fix to constraints comparation logic -- can't cmp() hash values as it
+ may cause false positives due to hash conflicts.
+
+Revision 0.0.5a
+---------------
+
+- Integer BER codec reworked fixing negative values encoding bug.
+- clone() and subtype() methods of Constructed ASN.1 classes now
+ accept optional cloneValueFlag flag which controls original value
+ inheritance. The default is *not* to inherit original value for
+ performance reasons (this may affect backward compatibility).
+ Performance penalty may be huge on deeply nested Constructed objects
+ re-creation.
+- Base ASN.1 types (pyasn1.type.univ.*) do not have default values
+ anymore. They remain uninitialized acting as ASN.1 types. In
+ this model, initialized ASN.1 types represent either types with
+ default value installed or a type instance.
+- Decoders' prototypes are now class instances rather than classes.
+ This is to simplify initial value installation to decoder's
+ prototype value.
+- Bugfix to BitString BER decoder (trailing bits not regarded).
+- Bugfix to Constraints use as mapping keys.
+- Bugfix to Integer & BitString clone() methods
+- Bugix to the way to distinguish Set from SetOf at CER/DER SetOfEncoder
+- Adjustments to make it running on Python 1.5.
+- In tests, substrate constants converted from hex escaped literals into
+ octals to overcome indefinite hex width issue occuring in young Python.
+- Minor performance optimization of TagSet.isSuperTagSetOf() method
+- examples/sshkey.py added
+
+Revision 0.0.4a
+---------------
+
+* Asn1ItemBase.prettyPrinter() -> *.prettyPrint()
+
+Revision 0.0.3a
+---------------
+
+* Simple ASN1 objects now hash to their Python value and don't
+ depend upon tag/constraints/etc.
+* prettyIn & prettyOut methods of SimplleAsn1Object become public
+* many syntax fixes
+
+Revision 0.0.2a
+---------------
+
+* ConstraintsIntersection.isSuperTypeOf() and
+ ConstraintsIntersection.hasConstraint() implemented
+* Bugfix to NamedValues initialization code
+* +/- operators added to NamedValues objects
+* Integer.__abs__() & Integer.subtype() added
+* ObjectIdentifier.prettyOut() fixes
+* Allow subclass components at SequenceAndSetBase
+* AbstractConstraint.__cmp__() dropped
+* error.Asn1Error replaced with error.PyAsn1Error
+
+Revision 0.0.1a
+---------------
+
+* Initial public alpha release
diff --git a/python/pyasn1/LICENSE b/python/pyasn1/LICENSE
new file mode 100644
index 0000000000..fac589b8cd
--- /dev/null
+++ b/python/pyasn1/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2005-2013, Ilya Etingof <ilya@glas.net>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/python/pyasn1/MANIFEST.in b/python/pyasn1/MANIFEST.in
new file mode 100644
index 0000000000..e8b3d36ce0
--- /dev/null
+++ b/python/pyasn1/MANIFEST.in
@@ -0,0 +1,3 @@
+include CHANGES README LICENSE THANKS TODO
+recursive-include test *.py
+recursive-include doc *.html
diff --git a/python/pyasn1/PKG-INFO b/python/pyasn1/PKG-INFO
new file mode 100644
index 0000000000..5de78eceb0
--- /dev/null
+++ b/python/pyasn1/PKG-INFO
@@ -0,0 +1,26 @@
+Metadata-Version: 1.0
+Name: pyasn1
+Version: 0.1.7
+Summary: ASN.1 types and codecs
+Home-page: http://sourceforge.net/projects/pyasn1/
+Author: Ilya Etingof <ilya@glas.net>
+Author-email: ilya@glas.net
+License: BSD
+Description: A pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208).
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Education
+Classifier: Intended Audience :: Information Technology
+Classifier: Intended Audience :: Science/Research
+Classifier: Intended Audience :: System Administrators
+Classifier: Intended Audience :: Telecommunications Industry
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Natural Language :: English
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Communications
+Classifier: Topic :: Security :: Cryptography
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/python/pyasn1/README b/python/pyasn1/README
new file mode 100644
index 0000000000..ffa3b57e5a
--- /dev/null
+++ b/python/pyasn1/README
@@ -0,0 +1,68 @@
+
+ASN.1 library for Python
+------------------------
+
+This is an implementation of ASN.1 types and codecs in Python programming
+language. It has been first written to support particular protocol (SNMP)
+but then generalized to be suitable for a wide range of protocols
+based on ASN.1 specification.
+
+FEATURES
+--------
+
+* Generic implementation of ASN.1 types (X.208)
+* Fully standard compliant BER/CER/DER codecs
+* 100% Python, works with Python 2.4 up to Python 3.3 (beta 1)
+* MT-safe
+
+MISFEATURES
+-----------
+
+* No ASN.1 compiler (by-hand ASN.1 spec compilation into Python code required)
+* Codecs are not restartable
+
+INSTALLATION
+------------
+
+The pyasn1 package uses setuptools/distutils for installation. Thus do
+either:
+
+$ easy_install pyasn1
+
+or
+
+$ tar zxf pyasn1-0.1.3.tar.gz
+$ cd pyasn1-0.1.3
+$ python setup.py install
+$ cd test
+$ python suite.py # run unit tests
+
+OPERATION
+---------
+
+Perhaps a typical use would involve [by-hand] compilation of your ASN.1
+specification into pyasn1-backed Python code at your application.
+
+For more information on pyasn1 APIs, please, refer to the
+doc/pyasn1-tutorial.html file in the distribution.
+
+Also refer to example modules. Take a look at pyasn1-modules package -- maybe
+it already holds something useful to you.
+
+AVAILABILITY
+------------
+
+The pyasn1 package is distributed under terms and conditions of BSD-style
+license. See LICENSE file in the distribution. Source code is freely
+available from:
+
+http://pyasn1.sf.net
+
+
+FEEDBACK
+--------
+
+Please, send your comments and fixes to mailing lists at project web site.
+
+=-=-=
+mailto: ilya@glas.net
diff --git a/python/pyasn1/THANKS b/python/pyasn1/THANKS
new file mode 100644
index 0000000000..4de1713c03
--- /dev/null
+++ b/python/pyasn1/THANKS
@@ -0,0 +1,4 @@
+Denis S. Otkidach
+Gregory Golberg
+Bud P. Bruegger
+Jacek Konieczny
diff --git a/python/pyasn1/TODO b/python/pyasn1/TODO
new file mode 100644
index 0000000000..0ee211c2a4
--- /dev/null
+++ b/python/pyasn1/TODO
@@ -0,0 +1,36 @@
+* Specialize ASN.1 character and useful types
+* Come up with simpler API for deeply nested constructed objects
+ addressing
+
+ber.decoder:
+* suspend codec on underrun error ?
+* class-static components map (in simple type classes)
+* present subtypes ?
+* component presence check wont work at innertypeconst
+* add the rest of ASN1 types/codecs
+* type vs value, defaultValue
+
+ber.encoder:
+* Asn1Item.clone() / shallowcopy issue
+* large length encoder?
+* codec restart
+* preserve compatible API whenever stateful codec gets implemented
+* restartable vs incremental
+* plan: make a stateless univeral decoder, then convert it to restartable
+ then to incremental
+
+type.useful:
+* may need to implement prettyIn/Out
+
+type.char:
+* may need to implement constraints
+
+type.univ:
+* simpler API to constructed objects: value init, recursive
+
+type.namedtypes
+* type vs tagset name convention
+
+general:
+
+* how untagged TagSet should be initialized?
diff --git a/python/pyasn1/doc/codecs.html b/python/pyasn1/doc/codecs.html
new file mode 100644
index 0000000000..9c2c36ed6f
--- /dev/null
+++ b/python/pyasn1/doc/codecs.html
@@ -0,0 +1,503 @@
+<html>
+<title>
+PyASN1 codecs
+</title>
+<head>
+</head>
+<body>
+<center>
+<table width=60%>
+<tr>
+<td>
+<h3>
+2. PyASN1 Codecs
+</h3>
+
+<p>
+In ASN.1 context,
+<a href=http://en.wikipedia.org/wiki/Codec>codec</a>
+is a program that transforms between concrete data structures and a stream
+of octets, suitable for transmission over the wire. This serialized form of
+data is sometimes called <i>substrate</i> or <i>essence</i>.
+</p>
+
+<p>
+In pyasn1 implementation, substrate takes shape of Python 3 bytes or
+Python 2 string objects.
+</p>
+
+<p>
+One of the properties of a codec is its ability to cope with incomplete
+data and/or substrate what implies codec to be stateful. In other words,
+when decoder runs out of substrate and data item being recovered is still
+incomplete, stateful codec would suspend and complete data item recovery
+whenever the rest of substrate becomes available. Similarly, stateful encoder
+would encode data items in multiple steps waiting for source data to
+arrive. Codec restartability is especially important when application deals
+with large volumes of data and/or runs on low RAM. For an interesting
+discussion on codecs options and design choices, refer to
+<a href=http://directory.apache.org/subprojects/asn1/>Apache ASN.1 project</a>
+.
+</p>
+
+<p>
+As of this writing, codecs implemented in pyasn1 are all stateless, mostly
+to keep the code simple.
+</p>
+
+<p>
+The pyasn1 package currently supports
+<a href=http://en.wikipedia.org/wiki/Basic_encoding_rules>BER</a> codec and
+its variations --
+<a href=http://en.wikipedia.org/wiki/Canonical_encoding_rules>CER</a> and
+<a href=http://en.wikipedia.org/wiki/Distinguished_encoding_rules>DER</a>.
+More ASN.1 codecs are planned for implementation in the future.
+</p>
+
+<a name="2.1"></a>
+<h4>
+2.1 Encoders
+</h4>
+
+<p>
+Encoder is used for transforming pyasn1 value objects into substrate. Only
+pyasn1 value objects could be serialized, attempts to process pyasn1 type
+objects will cause encoder failure.
+</p>
+
+<p>
+The following code will create a pyasn1 Integer object and serialize it with
+BER encoder:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder
+>>> encoder.encode(univ.Integer(123456))
+b'\x02\x03\x01\xe2@'
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+BER standard also defines a so-called <i>indefinite length</i> encoding form
+which makes large data items processing more memory efficient. It is mostly
+useful when encoder does not have the whole value all at once and the
+length of the value can not be determined at the beginning of encoding.
+</p>
+
+<p>
+<i>Constructed encoding</i> is another feature of BER closely related to the
+indefinite length form. In essence, a large scalar value (such as ASN.1
+character BitString type) could be chopped into smaller chunks by encoder
+and transmitted incrementally to limit memory consumption. Unlike indefinite
+length case, the length of the whole value must be known in advance when
+using constructed, definite length encoding form.
+</p>
+
+<p>
+Since pyasn1 codecs are not restartable, pyasn1 encoder may only encode data
+item all at once. However, even in this case, generating indefinite length
+encoding may help a low-memory receiver, running a restartable decoder,
+to process a large data item.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder
+>>> encoder.encode(
+... univ.OctetString('The quick brown fox jumps over the lazy dog'),
+... defMode=False,
+... maxChunkSize=8
+... )
+b'$\x80\x04\x08The quic\x04\x08k brown \x04\x08fox jump\x04\x08s over \
+t\x04\x08he lazy \x04\x03dog\x00\x00'
+>>>
+>>> encoder.encode(
+... univ.OctetString('The quick brown fox jumps over the lazy dog'),
+... maxChunkSize=8
+... )
+b'$7\x04\x08The quic\x04\x08k brown \x04\x08fox jump\x04\x08s over \
+t\x04\x08he lazy \x04\x03dog'
+</pre>
+</td></tr></table>
+
+<p>
+The <b>defMode</b> encoder parameter disables definite length encoding mode,
+while the optional <b>maxChunkSize</b> parameter specifies desired
+substrate chunk size that influences memory requirements at the decoder's end.
+</p>
+
+<p>
+To use CER or DER encoders one needs to explicitly import and call them - the
+APIs are all compatible.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder as ber_encoder
+>>> from pyasn1.codec.cer import encoder as cer_encoder
+>>> from pyasn1.codec.der import encoder as der_encoder
+>>> ber_encoder.encode(univ.Boolean(True))
+b'\x01\x01\x01'
+>>> cer_encoder.encode(univ.Boolean(True))
+b'\x01\x01\xff'
+>>> der_encoder.encode(univ.Boolean(True))
+b'\x01\x01\xff'
+>>>
+</pre>
+</td></tr></table>
+
+<a name="2.2"></a>
+<h4>
+2.2 Decoders
+</h4>
+
+<p>
+In the process of decoding, pyasn1 value objects are created and linked to
+each other, based on the information containted in the substrate. Thus,
+the original pyasn1 value object(s) are recovered.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> substrate = encoder.encode(univ.Boolean(True))
+>>> decoder.decode(substrate)
+(Boolean('True(1)'), b'')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Commenting on the code snippet above, pyasn1 decoder accepts substrate
+as an argument and returns a tuple of pyasn1 value object (possibly
+a top-level one in case of constructed object) and unprocessed part
+of input substrate.
+</p>
+
+<p>
+All pyasn1 decoders can handle both definite and indefinite length
+encoding modes automatically, explicit switching into one mode
+to another is not required.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> substrate = encoder.encode(
+... univ.OctetString('The quick brown fox jumps over the lazy dog'),
+... defMode=False,
+... maxChunkSize=8
+... )
+>>> decoder.decode(substrate)
+(OctetString(b'The quick brown fox jumps over the lazy dog'), b'')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Speaking of BER/CER/DER encoding, in many situations substrate may not contain
+all necessary information needed for complete and accurate ASN.1 values
+recovery. The most obvious cases include implicitly tagged ASN.1 types
+and constrained types.
+</p>
+
+<p>
+As discussed earlier in this handbook, when an ASN.1 type is implicitly
+tagged, previous outermost tag is lost and never appears in substrate.
+If it is the base tag that gets lost, decoder is unable to pick type-specific
+value decoder at its table of built-in types, and therefore recover
+the value part, based only on the information contained in substrate. The
+approach taken by pyasn1 decoder is to use a prototype pyasn1 type object (or
+a set of them) to <i>guide</i> the decoding process by matching [possibly
+incomplete] tags recovered from substrate with those found in prototype pyasn1
+type objects (also called pyasn1 specification object further in this paper).
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.codec.ber import decoder
+>>> decoder.decode(b'\x02\x01\x0c', asn1Spec=univ.Integer())
+Integer(12), b''
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Decoder would neither modify pyasn1 specification object nor use
+its current values (if it's a pyasn1 value object), but rather use it as
+a hint for choosing proper decoder and as a pattern for creating new objects:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, tag
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> i = univ.Integer(12345).subtype(
+... implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 40)
+... )
+>>> substrate = encoder.encode(i)
+>>> substrate
+b'\x9f(\x0209'
+>>> decoder.decode(substrate)
+Traceback (most recent call last):
+...
+pyasn1.error.PyAsn1Error:
+ TagSet(Tag(tagClass=128, tagFormat=0, tagId=40)) not in asn1Spec
+>>> decoder.decode(substrate, asn1Spec=i)
+(Integer(12345), b'')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Notice in the example above, that an attempt to run decoder without passing
+pyasn1 specification object fails because recovered tag does not belong
+to any of the built-in types.
+</p>
+
+<p>
+Another important feature of guided decoder operation is the use of
+values constraints possibly present in pyasn1 specification object.
+To explain this, we will decode a random integer object into generic Integer
+and the constrained one.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, constraint
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> class DialDigit(univ.Integer):
+... subtypeSpec = constraint.ValueRangeConstraint(0,9)
+>>> substrate = encoder.encode(univ.Integer(13))
+>>> decoder.decode(substrate)
+(Integer(13), b'')
+>>> decoder.decode(substrate, asn1Spec=DialDigit())
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ValueRangeConstraint(0, 9) failed at: 13
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Similarily to encoders, to use CER or DER decoders application has to
+explicitly import and call them - all APIs are compatible.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder as ber_encoder
+>>> substrate = ber_encoder.encode(univ.OctetString('http://pyasn1.sf.net'))
+>>>
+>>> from pyasn1.codec.ber import decoder as ber_decoder
+>>> from pyasn1.codec.cer import decoder as cer_decoder
+>>> from pyasn1.codec.der import decoder as der_decoder
+>>>
+>>> ber_decoder.decode(substrate)
+(OctetString(b'http://pyasn1.sf.net'), b'')
+>>> cer_decoder.decode(substrate)
+(OctetString(b'http://pyasn1.sf.net'), b'')
+>>> der_decoder.decode(substrate)
+(OctetString(b'http://pyasn1.sf.net'), b'')
+>>>
+</pre>
+</td></tr></table>
+
+<a name="2.2.1"></a>
+<h4>
+2.2.1 Decoding untagged types
+</h4>
+
+<p>
+It has already been mentioned, that ASN.1 has two "special case" types:
+CHOICE and ANY. They are different from other types in part of
+tagging - unless these two are additionally tagged, neither of them will
+have their own tag. Therefore these types become invisible in substrate
+and can not be recovered without passing pyasn1 specification object to
+decoder.
+</p>
+
+<p>
+To explain the issue, we will first prepare a Choice object to deal with:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedtype
+>>> class CodeOrMessage(univ.Choice):
+... componentType = namedtype.NamedTypes(
+... namedtype.NamedType('code', univ.Integer()),
+... namedtype.NamedType('message', univ.OctetString())
+... )
+>>>
+>>> codeOrMessage = CodeOrMessage()
+>>> codeOrMessage.setComponentByName('message', 'my string value')
+>>> print(codeOrMessage.prettyPrint())
+CodeOrMessage:
+ message=b'my string value'
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Let's now encode this Choice object and then decode its substrate
+with and without pyasn1 specification object:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> substrate = encoder.encode(codeOrMessage)
+>>> substrate
+b'\x04\x0fmy string value'
+>>> encoder.encode(univ.OctetString('my string value'))
+b'\x04\x0fmy string value'
+>>>
+>>> decoder.decode(substrate)
+(OctetString(b'my string value'), b'')
+>>> codeOrMessage, substrate = decoder.decode(substrate, asn1Spec=CodeOrMessage())
+>>> print(codeOrMessage.prettyPrint())
+CodeOrMessage:
+ message=b'my string value'
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+First thing to notice in the listing above is that the substrate produced
+for our Choice value object is equivalent to the substrate for an OctetString
+object initialized to the same value. In other words, any information about
+the Choice component is absent in encoding.
+</p>
+
+<p>
+Sure enough, that kind of substrate will decode into an OctetString object,
+unless original Choice type object is passed to decoder to guide the decoding
+process.
+</p>
+
+<p>
+Similarily untagged ANY type behaves differently on decoding phase - when
+decoder bumps into an Any object in pyasn1 specification, it stops decoding
+and puts all the substrate into a new Any value object in form of an octet
+string. Concerned application could then re-run decoder with an additional,
+more exact pyasn1 specification object to recover the contents of Any
+object.
+</p>
+
+<p>
+As it was mentioned elsewhere in this paper, Any type allows for incomplete
+or changing ASN.1 specification to be handled gracefully by decoder and
+applications.
+</p>
+
+<p>
+To illustrate the working of Any type, we'll have to make the stage
+by encoding a pyasn1 object and then putting its substrate into an any
+object.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> innerSubstrate = encoder.encode(univ.Integer(1234))
+>>> innerSubstrate
+b'\x02\x02\x04\xd2'
+>>> any = univ.Any(innerSubstrate)
+>>> any
+Any(b'\x02\x02\x04\xd2')
+>>> substrate = encoder.encode(any)
+>>> substrate
+b'\x02\x02\x04\xd2'
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+As with Choice type encoding, there is no traces of Any type in substrate.
+Obviously, the substrate we are dealing with, will decode into the inner
+[Integer] component, unless pyasn1 specification is given to guide the
+decoder. Continuing previous code:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder, decoder
+
+>>> decoder.decode(substrate)
+(Integer(1234), b'')
+>>> any, substrate = decoder.decode(substrate, asn1Spec=univ.Any())
+>>> any
+Any(b'\x02\x02\x04\xd2')
+>>> decoder.decode(str(any))
+(Integer(1234), b'')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Both CHOICE and ANY types are widely used in practice. Reader is welcome to
+take a look at
+<a href=http://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt>
+ASN.1 specifications of X.509 applications</a> for more information.
+</p>
+
+<a name="2.2.2"></a>
+<h4>
+2.2.2 Ignoring unknown types
+</h4>
+
+<p>
+When dealing with a loosely specified ASN.1 structure, the receiving
+end may not be aware of some types present in the substrate. It may be
+convenient then to turn decoder into a recovery mode. Whilst there, decoder
+will not bail out when hit an unknown tag but rather treat it as an Any
+type.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, tag
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> taggedInt = univ.Integer(12345).subtype(
+... implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 40)
+... )
+>>> substrate = encoder.encode(taggedInt)
+>>> decoder.decode(substrate)
+Traceback (most recent call last):
+...
+pyasn1.error.PyAsn1Error: TagSet(Tag(tagClass=128, tagFormat=0, tagId=40)) not in asn1Spec
+>>>
+>>> decoder.decode.defaultErrorState = decoder.stDumpRawValue
+>>> decoder.decode(substrate)
+(Any(b'\x9f(\x0209'), '')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+It's also possible to configure a custom decoder, to handle unknown tags
+found in substrate. This can be done by means of <b>defaultRawDecoder</b>
+attribute holding a reference to type decoder object. Refer to the source
+for API details.
+</p>
+
+<hr>
+
+</td>
+</tr>
+</table>
+</center>
+</body>
+</html>
diff --git a/python/pyasn1/doc/constraints.html b/python/pyasn1/doc/constraints.html
new file mode 100644
index 0000000000..53da1addff
--- /dev/null
+++ b/python/pyasn1/doc/constraints.html
@@ -0,0 +1,436 @@
+<html>
+<title>
+PyASN1 subtype constraints
+</title>
+<head>
+</head>
+<body>
+<center>
+<table width=60%>
+<tr>
+<td>
+
+<h4>
+1.4 PyASN1 subtype constraints
+</h4>
+
+<p>
+Most ASN.1 types can correspond to an infinite set of values. To adapt to
+particular application's data model and needs, ASN.1 provides a mechanism
+for limiting the infinite set to values, that make sense in particular case.
+</p>
+
+<p>
+Imposing value constraints on an ASN.1 type can also be seen as creating
+a subtype from its base type.
+</p>
+
+<p>
+In pyasn1, constraints take shape of immutable objects capable
+of evaluating given value against constraint-specific requirements.
+Constraint object is a property of pyasn1 type. Like TagSet property,
+associated with every pyasn1 type, constraints can never be modified
+in place. The only way to modify pyasn1 type constraint is to associate
+new constraint object to a new pyasn1 type object.
+</p>
+
+<p>
+A handful of different flavors of <i>constraints</i> are defined in ASN.1.
+We will discuss them one by one in the following chapters and also explain
+how to combine and apply them to types.
+</p>
+
+<a name="1.4.1"></a>
+<h4>
+1.4.1 Single value constraint
+</h4>
+
+<p>
+This kind of constraint allows for limiting type to a finite, specified set
+of values.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+DialButton ::= OCTET STRING (
+ "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
+)
+</pre>
+</td></tr></table>
+
+<p>
+Its pyasn1 implementation would look like:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import constraint
+>>> c = constraint.SingleValueConstraint(
+ '0','1','2','3','4','5','6','7','8','9'
+)
+>>> c
+SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
+>>> c('0')
+>>> c('A')
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) failed at: A
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+As can be seen in the snippet above, if a value violates the constraint, an
+exception will be thrown. A constrainted pyasn1 type object holds a
+reference to a constraint object (or their combination, as will be explained
+later) and calls it for value verification.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, constraint
+>>> class DialButton(univ.OctetString):
+... subtypeSpec = constraint.SingleValueConstraint(
+... '0','1','2','3','4','5','6','7','8','9'
+... )
+>>> DialButton('0')
+DialButton(b'0')
+>>> DialButton('A')
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) failed at: A
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Constrained pyasn1 value object can never hold a violating value.
+</p>
+
+<a name="1.4.2"></a>
+<h4>
+1.4.2 Value range constraint
+</h4>
+
+<p>
+A pair of values, compliant to a type to be constrained, denote low and upper
+bounds of allowed range of values of a type.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+Teenagers ::= INTEGER (13..19)
+</pre>
+</td></tr></table>
+
+<p>
+And in pyasn1 terms:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, constraint
+>>> class Teenagers(univ.Integer):
+... subtypeSpec = constraint.ValueRangeConstraint(13, 19)
+>>> Teenagers(14)
+Teenagers(14)
+>>> Teenagers(20)
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ValueRangeConstraint(13, 19) failed at: 20
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Value range constraint usually applies numeric types.
+</p>
+
+<a name="1.4.3"></a>
+<h4>
+1.4.3 Size constraint
+</h4>
+
+<p>
+It is sometimes convenient to set or limit the allowed size of a data item
+to be sent from one application to another to manage bandwidth and memory
+consumption issues. Size constraint specifies the lower and upper bounds
+of the size of a valid value.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+TwoBits ::= BIT STRING (SIZE (2))
+</pre>
+</td></tr></table>
+
+<p>
+Express the same grammar in pyasn1:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, constraint
+>>> class TwoBits(univ.BitString):
+... subtypeSpec = constraint.ValueSizeConstraint(2, 2)
+>>> TwoBits((1,1))
+TwoBits("'11'B")
+>>> TwoBits((1,1,0))
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ValueSizeConstraint(2, 2) failed at: (1, 1, 0)
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Size constraint can be applied to potentially massive values - bit or octet
+strings, SEQUENCE OF/SET OF values.
+</p>
+
+<a name="1.4.4"></a>
+<h4>
+1.4.4 Alphabet constraint
+</h4>
+
+<p>
+The permitted alphabet constraint is similar to Single value constraint
+but constraint applies to individual characters of a value.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+MorseCode ::= PrintableString (FROM ("."|"-"|" "))
+</pre>
+</td></tr></table>
+
+<p>
+And in pyasn1:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import char, constraint
+>>> class MorseCode(char.PrintableString):
+... subtypeSpec = constraint.PermittedAlphabetConstraint(".", "-", " ")
+>>> MorseCode("...---...")
+MorseCode('...---...')
+>>> MorseCode("?")
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ PermittedAlphabetConstraint(".", "-", " ") failed at: "?"
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Current implementation does not handle ranges of characters in constraint
+(FROM "A".."Z" syntax), one has to list the whole set in a range.
+</p>
+
+<a name="1.4.5"></a>
+<h4>
+1.4.5 Constraint combinations
+</h4>
+
+<p>
+Up to this moment, we used a single constraint per ASN.1 type. The standard,
+however, allows for combining multiple individual constraints into
+intersections, unions and exclusions.
+</p>
+
+<p>
+In pyasn1 data model, all of these methods of constraint combinations are
+implemented as constraint-like objects holding individual constraint (or
+combination) objects. Like terminal constraint objects, combination objects
+are capable to perform value verification at its set of enclosed constraints
+according to the logic of particular combination.
+</p>
+
+<p>
+Constraints intersection verification succeeds only if a value is
+compliant to each constraint in a set. To begin with, the following
+specification will constitute a valid telephone number:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+PhoneNumber ::= NumericString (FROM ("0".."9")) (SIZE 11)
+</pre>
+</td></tr></table>
+
+<p>
+Constraint intersection object serves the logic above:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import char, constraint
+>>> class PhoneNumber(char.NumericString):
+... subtypeSpec = constraint.ConstraintsIntersection(
+... constraint.PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'),
+... constraint.ValueSizeConstraint(11, 11)
+... )
+>>> PhoneNumber('79039343212')
+PhoneNumber('79039343212')
+>>> PhoneNumber('?9039343212')
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ConstraintsIntersection(
+ PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'),
+ ValueSizeConstraint(11, 11)) failed at:
+ PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9') failed at: "?039343212"
+>>> PhoneNumber('9343212')
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ConstraintsIntersection(
+ PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'),
+ ValueSizeConstraint(11, 11)) failed at:
+ ValueSizeConstraint(10, 10) failed at: "9343212"
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Union of constraints works by making sure that a value is compliant
+to any of the constraint in a set. For instance:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+CapitalOrSmall ::= IA5String (FROM ('A','B','C') | FROM ('a','b','c'))
+</pre>
+</td></tr></table>
+
+<p>
+It's important to note, that a value must fully comply to any single
+constraint in a set. In the specification above, a value of all small or
+all capital letters is compliant, but a mix of small&capitals is not.
+Here's its pyasn1 analogue:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import char, constraint
+>>> class CapitalOrSmall(char.IA5String):
+... subtypeSpec = constraint.ConstraintsUnion(
+... constraint.PermittedAlphabetConstraint('A','B','C'),
+... constraint.PermittedAlphabetConstraint('a','b','c')
+... )
+>>> CapitalOrSmall('ABBA')
+CapitalOrSmall('ABBA')
+>>> CapitalOrSmall('abba')
+CapitalOrSmall('abba')
+>>> CapitalOrSmall('Abba')
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ConstraintsUnion(PermittedAlphabetConstraint('A', 'B', 'C'),
+ PermittedAlphabetConstraint('a', 'b', 'c')) failed at: failed for "Abba"
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Finally, the exclusion constraint simply negates the logic of value
+verification at a constraint. In the following example, any integer value
+is allowed in a type but not zero.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+NoZero ::= INTEGER (ALL EXCEPT 0)
+</pre>
+</td></tr></table>
+
+<p>
+In pyasn1 the above definition would read:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, constraint
+>>> class NoZero(univ.Integer):
+... subtypeSpec = constraint.ConstraintsExclusion(
+... constraint.SingleValueConstraint(0)
+... )
+>>> NoZero(1)
+NoZero(1)
+>>> NoZero(0)
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ConstraintsExclusion(SingleValueConstraint(0)) failed at: 0
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+The depth of such a constraints tree, built with constraint combination objects
+at its nodes, has not explicit limit. Value verification is performed in a
+recursive manner till a definite solution is found.
+</p>
+
+<a name="1.5"></a>
+<h4>
+1.5 Types relationships
+</h4>
+
+<p>
+In the course of data processing in an application, it is sometimes
+convenient to figure out the type relationships between pyasn1 type or
+value objects. Formally, two things influence pyasn1 types relationship:
+<i>tag set</i> and <i>subtype constraints</i>. One pyasn1 type is considered
+to be a derivative of another if their TagSet and Constraint objects are
+a derivation of one another.
+</p>
+
+<p>
+The following example illustrates the concept (we use the same tagset but
+different constraints for simplicity):
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, constraint
+>>> i1 = univ.Integer(subtypeSpec=constraint.ValueRangeConstraint(3,8))
+>>> i2 = univ.Integer(subtypeSpec=constraint.ConstraintsIntersection(
+... constraint.ValueRangeConstraint(3,8),
+... constraint.ValueRangeConstraint(4,7)
+... ) )
+>>> i1.isSameTypeWith(i2)
+False
+>>> i1.isSuperTypeOf(i2)
+True
+>>> i1.isSuperTypeOf(i1)
+True
+>>> i2.isSuperTypeOf(i1)
+False
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+As can be seen in the above code snippet, there are two methods of any pyasn1
+type/value object that test types for their relationship:
+<b>isSameTypeWith</b>() and <b>isSuperTypeOf</b>(). The former is
+self-descriptive while the latter yields true if the argument appears
+to be a pyasn1 object which has tagset and constraints derived from those
+of the object being called.
+</p>
+
+<hr>
+
+</td>
+</tr>
+</table>
+</center>
+</body>
+</html>
diff --git a/python/pyasn1/doc/constructed.html b/python/pyasn1/doc/constructed.html
new file mode 100644
index 0000000000..88de750758
--- /dev/null
+++ b/python/pyasn1/doc/constructed.html
@@ -0,0 +1,377 @@
+<html>
+<title>
+PyASN1 Constructed types
+</title>
+<head>
+</head>
+<body>
+<center>
+<table width=60%>
+<tr>
+<td>
+
+<h4>
+1.3 PyASN1 Constructed types
+</h4>
+
+<p>
+Besides scalar types, ASN.1 specifies so-called constructed ones - these
+are capable of holding one or more values of other types, both scalar
+and constructed.
+</p>
+
+<p>
+In pyasn1 implementation, constructed ASN.1 types behave like
+Python sequences, and also support additional component addressing methods,
+specific to particular constructed type.
+</p>
+
+<a name="1.3.1"></a>
+<h4>
+1.3.1 Sequence and Set types
+</h4>
+
+<p>
+The Sequence and Set types have many similar properties:
+</p>
+<ul>
+<li>they can hold any number of inner components of different types
+<li>every component has a human-friendly identifier
+<li>any component can have a default value
+<li>some components can be absent.
+</ul>
+
+<p>
+However, Sequence type guarantees the ordering of Sequence value components
+to match their declaration order. By contrast, components of the
+Set type can be ordered to best suite application's needs.
+<p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+Record ::= SEQUENCE {
+ id INTEGER,
+ room [0] INTEGER OPTIONAL,
+ house [1] INTEGER DEFAULT 0
+}
+</pre>
+</td></tr></table>
+
+<p>
+Up to this moment, the only method we used for creating new pyasn1 types
+is Python sub-classing. With this method, a new, named Python class is created
+what mimics type derivation in ASN.1 grammar. However, ASN.1 also allows for
+defining anonymous subtypes (room and house components in the example above).
+To support anonymous subtyping in pyasn1, a cloning operation on an existing
+pyasn1 type object can be invoked what creates a new instance of original
+object with possibly modified properties.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedtype, tag
+>>> class Record(univ.Sequence):
+... componentType = namedtype.NamedTypes(
+... namedtype.NamedType('id', univ.Integer()),
+... namedtype.OptionalNamedType(
+... 'room',
+... univ.Integer().subtype(
+... implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)
+... )
+... ),
+... namedtype.DefaultedNamedType(
+... 'house',
+... univ.Integer(0).subtype(
+... implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)
+... )
+... )
+... )
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+All pyasn1 constructed type classes have a class attribute <b>componentType</b>
+that represent default type specification. Its value is a NamedTypes object.
+</p>
+
+<p>
+The NamedTypes class instance holds a sequence of NameType, OptionalNamedType
+or DefaultedNamedType objects which, in turn, refer to pyasn1 type objects that
+represent inner SEQUENCE components specification.
+</p>
+
+<p>
+Finally, invocation of a subtype() method of pyasn1 type objects in the code
+above returns an implicitly tagged copy of original object.
+</p>
+
+<p>
+Once a SEQUENCE or SET type is decleared with pyasn1, it can be instantiated
+and initialized (continuing the above code):
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> record = Record()
+>>> record.setComponentByName('id', 123)
+>>> print(record.prettyPrint())
+Record:
+ id=123
+>>>
+>>> record.setComponentByPosition(1, 321)
+>>> print(record.prettyPrint())
+Record:
+ id=123
+ room=321
+>>>
+>>> record.setDefaultComponents()
+>>> print(record.prettyPrint())
+Record:
+ id=123
+ room=321
+ house=0
+</pre>
+</td></tr></table>
+
+<p>
+Inner components of pyasn1 Sequence/Set objects could be accessed using the
+following methods:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> record.getComponentByName('id')
+Integer(123)
+>>> record.getComponentByPosition(1)
+Integer(321)
+>>> record[2]
+Integer(0)
+>>> for idx in range(len(record)):
+... print(record.getNameByPosition(idx), record.getComponentByPosition(idx))
+id 123
+room 321
+house 0
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+The Set type share all the properties of Sequence type, and additionally
+support by-tag component addressing (as all Set components have distinct
+types).
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedtype, tag
+>>> class Gamer(univ.Set):
+... componentType = namedtype.NamedTypes(
+... namedtype.NamedType('score', univ.Integer()),
+... namedtype.NamedType('player', univ.OctetString()),
+... namedtype.NamedType('id', univ.ObjectIdentifier())
+... )
+>>> gamer = Gamer()
+>>> gamer.setComponentByType(univ.Integer().getTagSet(), 121343)
+>>> gamer.setComponentByType(univ.OctetString().getTagSet(), 'Pascal')
+>>> gamer.setComponentByType(univ.ObjectIdentifier().getTagSet(), (1,3,7,2))
+>>> print(gamer.prettyPrint())
+Gamer:
+ score=121343
+ player=b'Pascal'
+ id=1.3.7.2
+>>>
+</pre>
+</td></tr></table>
+
+<a name="1.3.2"></a>
+<h4>
+1.3.2 SequenceOf and SetOf types
+</h4>
+
+<p>
+Both, SequenceOf and SetOf types resemble an unlimited size list of components.
+All the components must be of the same type.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+Progression ::= SEQUENCE OF INTEGER
+
+arithmeticProgression Progression ::= { 1, 3, 5, 7 }
+</pre>
+</td></tr></table>
+
+<p>
+SequenceOf and SetOf types are expressed by the very similar pyasn1 type
+objects. Their components can only be addressed by position and they
+both have a property of automatic resize.
+</p>
+
+<p>
+To specify inner component type, the <b>componentType</b> class attribute
+should refer to another pyasn1 type object.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> class Progression(univ.SequenceOf):
+... componentType = univ.Integer()
+>>> arithmeticProgression = Progression()
+>>> arithmeticProgression.setComponentByPosition(1, 111)
+>>> print(arithmeticProgression.prettyPrint())
+Progression:
+-empty- 111
+>>> arithmeticProgression.setComponentByPosition(0, 100)
+>>> print(arithmeticProgression.prettyPrint())
+Progression:
+100 111
+>>>
+>>> for idx in range(len(arithmeticProgression)):
+... arithmeticProgression.getComponentByPosition(idx)
+Integer(100)
+Integer(111)
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Any scalar or constructed pyasn1 type object can serve as an inner component.
+Missing components are prohibited in SequenceOf/SetOf value objects.
+</p>
+
+<a name="1.3.3"></a>
+<h4>
+1.3.3 Choice type
+</h4>
+
+<p>
+Values of ASN.1 CHOICE type can contain only a single value of a type from a
+list of possible alternatives. Alternatives must be ASN.1 types with
+distinct tags for the whole structure to remain unambiguous. Unlike most
+other types, CHOICE is an untagged one, e.g. it has no base tag of its own.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+CodeOrMessage ::= CHOICE {
+ code INTEGER,
+ message OCTET STRING
+}
+</pre>
+</td></tr></table>
+
+<p>
+In pyasn1 implementation, Choice object behaves like Set but accepts only
+a single inner component at a time. It also offers a few additional methods
+specific to its behaviour.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedtype
+>>> class CodeOrMessage(univ.Choice):
+... componentType = namedtype.NamedTypes(
+... namedtype.NamedType('code', univ.Integer()),
+... namedtype.NamedType('message', univ.OctetString())
+... )
+>>>
+>>> codeOrMessage = CodeOrMessage()
+>>> print(codeOrMessage.prettyPrint())
+CodeOrMessage:
+>>> codeOrMessage.setComponentByName('code', 123)
+>>> print(codeOrMessage.prettyPrint())
+CodeOrMessage:
+ code=123
+>>> codeOrMessage.setComponentByName('message', 'my string value')
+>>> print(codeOrMessage.prettyPrint())
+CodeOrMessage:
+ message=b'my string value'
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Since there could be only a single inner component value in the pyasn1 Choice
+value object, either of the following methods could be used for fetching it
+(continuing previous code):
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> codeOrMessage.getName()
+'message'
+>>> codeOrMessage.getComponent()
+OctetString(b'my string value')
+>>>
+</pre>
+</td></tr></table>
+
+<a name="1.3.4"></a>
+<h4>
+1.3.4 Any type
+</h4>
+
+<p>
+The ASN.1 ANY type is a kind of wildcard or placeholder that matches
+any other type without knowing it in advance. Like CHOICE type, ANY
+has no base tag.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+Error ::= SEQUENCE {
+ code INTEGER,
+ parameter ANY DEFINED BY code
+}
+</pre>
+</td></tr></table>
+
+<p>
+The ANY type is frequently used in specifications, where exact type is not
+yet agreed upon between communicating parties or the number of possible
+alternatives of a type is infinite.
+Sometimes an auxiliary selector is kept around to help parties indicate
+the kind of ANY payload in effect ("code" in the example above).
+</p>
+
+<p>
+Values of the ANY type contain serialized ASN.1 value(s) in form of
+an octet string. Therefore pyasn1 Any value object share the properties of
+pyasn1 OctetString object.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> someValue = univ.Any(b'\x02\x01\x01')
+>>> someValue
+Any(b'\x02\x01\x01')
+>>> str(someValue)
+'\x02\x01\x01'
+>>> bytes(someValue)
+b'\x02\x01\x01'
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Receiving application is supposed to explicitly deserialize the content of Any
+value object, possibly using auxiliary selector for figuring out its ASN.1
+type to pick appropriate decoder.
+</p>
+
+<p>
+There will be some more talk and code snippets covering Any type in the codecs
+chapters that follow.
+</p>
+
+<hr>
+
+</td>
+</tr>
+</table>
+</center>
+</body>
+</html>
diff --git a/python/pyasn1/doc/intro.html b/python/pyasn1/doc/intro.html
new file mode 100644
index 0000000000..3ff18b6ae5
--- /dev/null
+++ b/python/pyasn1/doc/intro.html
@@ -0,0 +1,156 @@
+<html>
+<title>
+PyASN1 reference manual
+</title>
+<head>
+</head>
+<body>
+<center>
+<table width=60%>
+<tr>
+<td>
+
+<h3>
+PyASN1 reference manual
+</h3>
+
+<p align=right>
+<i>written by <a href=mailto:ilya@glas.net>Ilya Etingof</a>, 2011-2012</i>
+</p>
+
+<p>
+Free and open-source pyasn1 library makes it easier for programmers and
+network engineers to develop, debug and experiment with ASN.1-based protocols
+using Python programming language as a tool.
+</p>
+
+<p>
+Abstract Syntax Notation One
+(<a href=http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_1x>ASN.1</a>)
+is a set of
+<a href=http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-X.693-0207w.zip>
+ITU standards</a> concered with provisioning instrumentation for developing
+data exchange protocols in a robust, clear and interoperabable way for
+various IT systems and applications. Most of the efforts are targeting the
+following areas:
+<ul>
+<li>Data structures: the standard introduces a collection of basic data types
+(similar to integers, bits, strings, arrays and records in a programming
+language) that can be used for defining complex, possibly nested data
+structures representing domain-specific data units.
+<li>Serialization protocols: domain-specific data units expressed in ASN.1
+types could be converted into a series of octets for storage or transmission
+over the wire and then recovered back into their structured form on the
+receiving end. This process is immune to various hardware and software
+related dependencies.
+<li>Data description language: could be used to describe particular set of
+domain-specific data structures and their relationships. Such a description
+could be passed to an ASN.1 compiler for automated generation of program
+code that represents ASN.1 data structures in language-native environment
+and handles data serialization issues.
+</ul>
+</p>
+
+<p>
+This tutorial and algorithms, implemented by pyasn1 library, are
+largely based on the information read in the book
+<a href="http://www.oss.com/asn1/dubuisson.html">
+ASN.1 - Communication between heterogeneous systems</a>
+by Olivier Dubuisson. Another relevant resource is
+<a href=ftp://ftp.rsasecurity.com/pub/pkcs/ascii/layman.asc>
+A Layman's Guide to a Subset of ASN.1, BER, and DER</a> by Burton S. Kaliski.
+It's advised to refer to these books for more in-depth knowledge on the
+subject of ASN.1.
+</p>
+
+<p>
+As of this writing, pyasn1 library implements most of standard ASN.1 data
+structures in a rather detailed and feature-rich manner. Another highly
+important capability of the library is its data serialization facilities.
+The last component of the standard - ASN.1 compiler is planned for
+implementation in the future.
+</p>
+
+</p>
+The pyasn1 library was designed to follow the pre-1995 ASN.1 specification
+(also known as X.208). Later, post 1995, revision (X.680) introduced
+significant changes most of which have not yet been supported by pyasn1.
+</p>
+
+<h3>
+Table of contents
+</h3>
+
+<p>
+<ul>
+<li><a href="scalar.html">1. Data model for ASN.1 types</a>
+<li><a href="scalar.html#1.1">1.1 Scalar types</a>
+<li><a href="scalar.html#1.1.1">1.1.1 Boolean type</a>
+<li><a href="scalar.html#1.1.2">1.1.2 Null type</a>
+<li><a href="scalar.html#1.1.3">1.1.3 Integer type</a>
+<li><a href="scalar.html#1.1.4">1.1.4 Enumerated type</a>
+<li><a href="scalar.html#1.1.5">1.1.5 Real type</a>
+<li><a href="scalar.html#1.1.6">1.1.6 Bit string type</a>
+<li><a href="scalar.html#1.1.7">1.1.7 OctetString type</a>
+<li><a href="scalar.html#1.1.8">1.1.8 ObjectIdentifier type</a>
+<li><a href="scalar.html#1.1.9">1.1.9 Character string types</a>
+<li><a href="scalar.html#1.1.10">1.1.10 Useful types</a>
+<li><a href="tagging.html">1.2 Tagging</a>
+<li><a href="constructed.html">1.3 Constructed types</a>
+<li><a href="constructed.html#1.3.1">1.3.1 Sequence and Set types</a>
+<li><a href="constructed.html#1.3.2">1.3.2 SequenceOf and SetOf types</a>
+<li><a href="constructed.html#1.3.3">1.3.3 Choice type</a>
+<li><a href="constructed.html#1.3.4">1.3.4 Any type</a>
+<li><a href="constraints.html">1.4 Subtype constraints</a>
+<li><a href="constraints.html#1.4.1">1.4.1 Single value constraint</a>
+<li><a href="constraints.html#1.4.2">1.4.2 Value range constraint</a>
+<li><a href="constraints.html#1.4.3">1.4.3 Size constraint</a>
+<li><a href="constraints.html#1.4.4">1.4.4 Alphabet constraint</a>
+<li><a href="constraints.html#1.4.5">1.4.5 Constraint combinations</a>
+<li><a href="constraints.html#1.5">1.5 Types relationships</a>
+<li><a href="codecs.html">2. Codecs</a>
+<li><a href="codecs.html#2.1">2.1 Encoders</a>
+<li><a href="codecs.html#2.2">2.2 Decoders</a>
+<li><a href="codecs.html#2.2.1">2.2.1 Decoding untagged types</a>
+<li><a href="codecs.html#2.2.2">2.2.2 Ignoring unknown types</a>
+</ul>
+
+<p>
+Although pyasn1 software is almost a decade old and used in many production
+environments, it still may have bugs and non-implemented pieces. Anyone
+who happens to run into such defect is welcome to complain to
+<a href=mailto:pyasn1-users@lists.sourceforge.net>pyasn1 mailing list</a>
+or better yet fix the issue and send
+<a href=mailto:ilya@glas.net>me</a> the patch.
+</p>
+
+<p>
+Typically, pyasn1 is used for building arbitrary protocol support into
+various applications. This involves manual translation of ASN.1 data
+structures into their pyasn1 implementations. To save time and effort,
+data structures for some of the popular protocols are pre-programmed
+and kept for further re-use in form of the
+<a href=http://sourceforge.net/projects/pyasn1/files/pyasn1-modules/>
+pyasn1-modules package</a>. For instance, many structures for PKI (X.509,
+PKCS#*, CRMF, OCSP), LDAP and SNMP are present.
+Applications authors are advised to import and use relevant modules
+from that package whenever needed protocol structures are already
+there. New protocol modules contributions are welcome.
+</p>
+
+<p>
+And finally, the latest pyasn1 package revision is available for free
+download from
+<a href=http://sourceforge.net/projects/pyasn1/>project home</a> and
+also from the
+<a href=http://pypi.python.org/pypi>Python package repository</a>.
+</p>
+
+<hr>
+
+</td>
+</tr>
+</table>
+</center>
+</body>
+</html>
diff --git a/python/pyasn1/doc/pyasn1-tutorial.html b/python/pyasn1/doc/pyasn1-tutorial.html
new file mode 100644
index 0000000000..2eb82f1e93
--- /dev/null
+++ b/python/pyasn1/doc/pyasn1-tutorial.html
@@ -0,0 +1,2405 @@
+<html>
+<title>
+PyASN1 programmer's manual
+</title>
+<head>
+</head>
+<body>
+<center>
+<table width=60%>
+<tr>
+<td>
+
+<h3>
+PyASN1 programmer's manual
+</h3>
+
+<p align=right>
+<i>written by <a href=mailto:ilya@glas.net>Ilya Etingof</a>, 2011-2012</i>
+</p>
+
+<p>
+Free and open-source pyasn1 library makes it easier for programmers and
+network engineers to develop, debug and experiment with ASN.1-based protocols
+using Python programming language as a tool.
+</p>
+
+<p>
+Abstract Syntax Notation One
+(<a href=http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_1x>ASN.1</a>)
+is a set of
+<a href=http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-X.693-0207w.zip>
+ITU standards</a> concered with provisioning instrumentation for developing
+data exchange protocols in a robust, clear and interoperabable way for
+various IT systems and applications. Most of the efforts are targeting the
+following areas:
+<ul>
+<li>Data structures: the standard introduces a collection of basic data types
+(similar to integers, bits, strings, arrays and records in a programming
+language) that can be used for defining complex, possibly nested data
+structures representing domain-specific data units.
+<li>Serialization protocols: domain-specific data units expressed in ASN.1
+types could be converted into a series of octets for storage or transmission
+over the wire and then recovered back into their structured form on the
+receiving end. This process is immune to various hardware and software
+related dependencies.
+<li>Data description language: could be used to describe particular set of
+domain-specific data structures and their relationships. Such a description
+could be passed to an ASN.1 compiler for automated generation of program
+code that represents ASN.1 data structures in language-native environment
+and handles data serialization issues.
+</ul>
+</p>
+
+<p>
+This tutorial and algorithms, implemented by pyasn1 library, are
+largely based on the information read in the book
+<a href="http://www.oss.com/asn1/dubuisson.html">
+ASN.1 - Communication between heterogeneous systems</a>
+by Olivier Dubuisson. Another relevant resource is
+<a href=ftp://ftp.rsasecurity.com/pub/pkcs/ascii/layman.asc>
+A Layman's Guide to a Subset of ASN.1, BER, and DER</a> by Burton S. Kaliski.
+It's advised to refer to these books for more in-depth knowledge on the
+subject of ASN.1.
+</p>
+
+<p>
+As of this writing, pyasn1 library implements most of standard ASN.1 data
+structures in a rather detailed and feature-rich manner. Another highly
+important capability of the library is its data serialization facilities.
+The last component of the standard - ASN.1 compiler is planned for
+implementation in the future.
+</p>
+
+</p>
+The pyasn1 library was designed to follow the pre-1995 ASN.1 specification
+(also known as X.208). Later, post 1995, revision (X.680) introduced
+significant changes most of which have not yet been supported by pyasn1.
+</p>
+
+<h3>
+Table of contents
+</h3>
+
+<p>
+<ul>
+<li><a href="#1">1. Data model for ASN.1 types</a>
+<li><a href="#1.1">1.1 Scalar types</a>
+<li><a href="#1.1.1">1.1.1 Boolean type</a>
+<li><a href="#1.1.2">1.1.2 Null type</a>
+<li><a href="#1.1.3">1.1.3 Integer type</a>
+<li><a href="#1.1.4">1.1.4 Enumerated type</a>
+<li><a href="#1.1.5">1.1.5 Real type</a>
+<li><a href="#1.1.6">1.1.6 Bit string type</a>
+<li><a href="#1.1.7">1.1.7 OctetString type</a>
+<li><a href="#1.1.8">1.1.8 ObjectIdentifier type</a>
+<li><a href="#1.1.9">1.1.9 Character string types</a>
+<li><a href="#1.1.10">1.1.10 Useful types</a>
+<li><a href="#1.2">1.2 Tagging</a>
+<li><a href="#1.3">1.3 Constructed types</a>
+<li><a href="#1.3.1">1.3.1 Sequence and Set types</a>
+<li><a href="#1.3.2">1.3.2 SequenceOf and SetOf types</a>
+<li><a href="#1.3.3">1.3.3 Choice type</a>
+<li><a href="#1.3.4">1.3.4 Any type</a>
+<li><a href="#1.4">1.4 Subtype constraints</a>
+<li><a href="#1.4.1">1.4.1 Single value constraint</a>
+<li><a href="#1.4.2">1.4.2 Value range constraint</a>
+<li><a href="#1.4.3">1.4.3 Size constraint</a>
+<li><a href="#1.4.4">1.4.4 Alphabet constraint</a>
+<li><a href="#1.4.5">1.4.5 Constraint combinations</a>
+<li><a href="#1.5">1.5 Types relationships</a>
+<li><a href="#2">2. Codecs</a>
+<li><a href="#2.1">2.1 Encoders</a>
+<li><a href="#2.2">2.2 Decoders</a>
+<li><a href="#2.2.1">2.2.1 Decoding untagged types</a>
+<li><a href="#2.2.2">2.2.2 Ignoring unknown types</a>
+<li><a href="#3">3. Feedback and getting help</a>
+</ul>
+
+
+<a name="1"></a>
+<h3>
+1. Data model for ASN.1 types
+</h3>
+
+<p>
+All ASN.1 types could be categorized into two groups: scalar (also called
+simple or primitive) and constructed. The first group is populated by
+well-known types like Integer or String. Members of constructed group
+hold other types (simple or constructed) as their inner components, thus
+they are semantically close to a programming language records or lists.
+</p>
+
+<p>
+In pyasn1, all ASN.1 types and values are implemented as Python objects.
+The same pyasn1 object can represent either ASN.1 type and/or value
+depending of the presense of value initializer on object instantiation.
+We will further refer to these as <i>pyasn1 type object</i> versus <i>pyasn1
+value object</i>.
+</p>
+
+<p>
+Primitive ASN.1 types are implemented as immutable scalar objects. There values
+could be used just like corresponding native Python values (integers,
+strings/bytes etc) and freely mixed with them in expressions.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> asn1IntegerValue = univ.Integer(12)
+>>> asn1IntegerValue - 2
+10
+>>> univ.OctetString('abc') == 'abc'
+True # Python 2
+>>> univ.OctetString(b'abc') == b'abc'
+True # Python 3
+</pre>
+</td></tr></table>
+
+<p>
+It would be an error to perform an operation on a pyasn1 type object
+as it holds no value to deal with:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> asn1IntegerType = univ.Integer()
+>>> asn1IntegerType - 2
+...
+pyasn1.error.PyAsn1Error: No value for __coerce__()
+</pre>
+</td></tr></table>
+
+<a name="1.1"></a>
+<h4>
+1.1 Scalar types
+</h4>
+
+<p>
+In the sub-sections that follow we will explain pyasn1 mapping to those
+primitive ASN.1 types. Both, ASN.1 notation and corresponding pyasn1
+syntax will be given in each case.
+</p>
+
+<a name="1.1.1"></a>
+<h4>
+1.1.1 Boolean type
+</h4>
+
+<p>
+This is the simplest type those values could be either True or False.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+;; type specification
+FunFactorPresent ::= BOOLEAN
+
+;; values declaration and assignment
+pythonFunFactor FunFactorPresent ::= TRUE
+cobolFunFactor FunFactorPresent :: FALSE
+</pre>
+</td></tr></table>
+
+<p>
+And here's pyasn1 version of it:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> class FunFactorPresent(univ.Boolean): pass
+...
+>>> pythonFunFactor = FunFactorPresent(True)
+>>> cobolFunFactor = FunFactorPresent(False)
+>>> pythonFunFactor
+FunFactorPresent('True(1)')
+>>> cobolFunFactor
+FunFactorPresent('False(0)')
+>>> pythonFunFactor == cobolFunFactor
+False
+>>>
+</pre>
+</td></tr></table>
+
+<a name="1.1.2"></a>
+<h4>
+1.1.2 Null type
+</h4>
+
+<p>
+The NULL type is sometimes used to express the absense of any information.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+;; type specification
+Vote ::= CHOICE {
+ agreed BOOLEAN,
+ skip NULL
+}
+</td></tr></table>
+
+;; value declaration and assignment
+myVote Vote ::= skip:NULL
+</pre>
+
+<p>
+We will explain the CHOICE type later in this paper, meanwhile the NULL
+type:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> skip = univ.Null()
+>>> skip
+Null('')
+>>>
+</pre>
+</td></tr></table>
+
+<a name="1.1.3"></a>
+<h4>
+1.1.3 Integer type
+</h4>
+
+<p>
+ASN.1 defines the values of Integer type as negative or positive of whatever
+length. This definition plays nicely with Python as the latter places no
+limit on Integers. However, some ASN.1 implementations may impose certain
+limits of integer value ranges. Keep that in mind when designing new
+data structures.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+;; values specification
+age-of-universe INTEGER ::= 13750000000
+mean-martian-surface-temperature INTEGER ::= -63
+</pre>
+</td></tr></table>
+
+<p>
+A rather strigntforward mapping into pyasn1:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> ageOfUniverse = univ.Integer(13750000000)
+>>> ageOfUniverse
+Integer(13750000000)
+>>>
+>>> meanMartianSurfaceTemperature = univ.Integer(-63)
+>>> meanMartianSurfaceTemperature
+Integer(-63)
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+ASN.1 allows to assign human-friendly names to particular values of
+an INTEGER type.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+Temperature ::= INTEGER {
+ freezing(0),
+ boiling(100)
+}
+</pre>
+</td></tr></table>
+
+<p>
+The Temperature type expressed in pyasn1:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedval
+>>> class Temperature(univ.Integer):
+... namedValues = namedval.NamedValues(('freezing', 0), ('boiling', 100))
+...
+>>> t = Temperature(0)
+>>> t
+Temperature('freezing(0)')
+>>> t + 1
+Temperature(1)
+>>> t + 100
+Temperature('boiling(100)')
+>>> t = Temperature('boiling')
+>>> t
+Temperature('boiling(100)')
+>>> Temperature('boiling') / 2
+Temperature(50)
+>>> -1 < Temperature('freezing')
+True
+>>> 47 > Temperature('boiling')
+False
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+These values labels have no effect on Integer type operations, any value
+still could be assigned to a type (information on value constraints will
+follow further in this paper).
+</p>
+
+<a name="1.1.4"></a>
+<h4>
+1.1.4 Enumerated type
+</h4>
+
+<p>
+ASN.1 Enumerated type differs from an Integer type in a number of ways.
+Most important is that its instance can only hold a value that belongs
+to a set of values specified on type declaration.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+error-status ::= ENUMERATED {
+ no-error(0),
+ authentication-error(10),
+ authorization-error(20),
+ general-failure(51)
+}
+</pre>
+</td></tr></table>
+
+<p>
+When constructing Enumerated type we will use two pyasn1 features: values
+labels (as mentioned above) and value constraint (will be described in
+more details later on).
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedval, constraint
+>>> class ErrorStatus(univ.Enumerated):
+... namedValues = namedval.NamedValues(
+... ('no-error', 0),
+... ('authentication-error', 10),
+... ('authorization-error', 20),
+... ('general-failure', 51)
+... )
+... subtypeSpec = univ.Enumerated.subtypeSpec + \
+... constraint.SingleValueConstraint(0, 10, 20, 51)
+...
+>>> errorStatus = univ.ErrorStatus('no-error')
+>>> errorStatus
+ErrorStatus('no-error(0)')
+>>> errorStatus == univ.ErrorStatus('general-failure')
+False
+>>> univ.ErrorStatus('non-existing-state')
+Traceback (most recent call last):
+...
+pyasn1.error.PyAsn1Error: Can't coerce non-existing-state into integer
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Particular integer values associated with Enumerated value states
+have no meaning. They should not be used as such or in any kind of
+math operation. Those integer values are only used by codecs to
+transfer state from one entity to another.
+</p>
+
+<a name="1.1.5"></a>
+<h4>
+1.1.5 Real type
+</h4>
+
+<p>
+Values of the Real type are a three-component tuple of mantissa, base and
+exponent. All three are integers.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+pi ::= REAL { mantissa 314159, base 10, exponent -5 }
+</pre>
+</td></tr></table>
+
+<p>
+Corresponding pyasn1 objects can be initialized with either a three-component
+tuple or a Python float. Infinite values could be expressed in a way,
+compatible with Python float type.
+
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> pi = univ.Real((314159, 10, -5))
+>>> pi
+Real((314159, 10,-5))
+>>> float(pi)
+3.14159
+>>> pi == univ.Real(3.14159)
+True
+>>> univ.Real('inf')
+Real('inf')
+>>> univ.Real('-inf') == float('-inf')
+True
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+If a Real object is initialized from a Python float or yielded by a math
+operation, the base is set to decimal 10 (what affects encoding).
+</p>
+
+<a name="1.1.6"></a>
+<h4>
+1.1.6 Bit string type
+</h4>
+
+<p>
+ASN.1 BIT STRING type holds opaque binary data of an arbitrarily length.
+A BIT STRING value could be initialized by either a binary (base 2) or
+hex (base 16) value.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+public-key BIT STRING ::= '1010111011110001010110101101101
+ 1011000101010000010110101100010
+ 0110101010000111101010111111110'B
+
+signature BIT STRING ::= 'AF01330CD932093392100B39FF00DE0'H
+</pre>
+</td></tr></table>
+
+<p>
+The pyasn1 BitString objects can initialize from native ASN.1 notation
+(base 2 or base 16 strings) or from a Python tuple of binary components.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> publicKey = univ.BitString(
+... "'1010111011110001010110101101101"
+... "1011000101010000010110101100010"
+... "0110101010000111101010111111110'B"
+)
+>>> publicKey
+BitString("'10101110111100010101101011011011011000101010000010110101100010\
+0110101010000111101010111111110'B")
+>>> signature = univ.BitString(
+... "'AF01330CD932093392100B39FF00DE0'H"
+... )
+>>> signature
+BitString("'101011110000000100110011000011001101100100110010000010010011001\
+1100100100001000000001011001110011111111100000000110111100000'B")
+>>> fingerprint = univ.BitString(
+... (1, 0, 1, 1 ,0, 1, 1, 1, 0, 1, 0, 1)
+... )
+>>> fingerprint
+BitString("'101101110101'B")
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Another BIT STRING initialization method supported by ASN.1 notation
+is to specify only 1-th bits along with their human-friendly label
+and bit offset relative to the beginning of the bit string. With this
+method, all not explicitly mentioned bits are doomed to be zeros.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+bit-mask BIT STRING ::= {
+ read-flag(0),
+ write-flag(2),
+ run-flag(4)
+}
+</pre>
+</td></tr></table>
+
+<p>
+To express this in pyasn1, we will employ the named values feature (as with
+Enumeration type).
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedval
+>>> class BitMask(univ.BitString):
+... namedValues = namedval.NamedValues(
+... ('read-flag', 0),
+... ('write-flag', 2),
+... ('run-flag', 4)
+... )
+>>> bitMask = BitMask('read-flag,run-flag')
+>>> bitMask
+BitMask("'10001'B")
+>>> tuple(bitMask)
+(1, 0, 0, 0, 1)
+>>> bitMask[4]
+1
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+The BitString objects mimic the properties of Python tuple type in part
+of immutable sequence object protocol support.
+</p>
+
+<a name="1.1.7"></a>
+<h4>
+1.1.7 OctetString type
+</h4>
+
+<p>
+The OCTET STRING type is a confusing subject. According to ASN.1
+specification, this type is similar to BIT STRING, the major difference
+is that the former operates in 8-bit chunks of data. What is important
+to note, is that OCTET STRING was NOT designed to handle text strings - the
+standard provides many other types specialized for text content. For that
+reason, ASN.1 forbids to initialize OCTET STRING values with "quoted text
+strings", only binary or hex initializers, similar to BIT STRING ones,
+are allowed.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+thumbnail OCTET STRING ::= '1000010111101110101111000000111011'B
+thumbnail OCTET STRING ::= 'FA9823C43E43510DE3422'H
+</pre>
+</td></tr></table>
+
+<p>
+However, ASN.1 users (e.g. protocols designers) seem to ignore the original
+purpose of the OCTET STRING type - they used it for handling all kinds of
+data, including text strings.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+welcome-message OCTET STRING ::= "Welcome to ASN.1 wilderness!"
+</pre>
+</td></tr></table>
+
+<p>
+In pyasn1, we have taken a liberal approach and allowed both BIT STRING
+style and quoted text initializers for the OctetString objects. To avoid
+possible collisions, quoted text is the default initialization syntax.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> thumbnail = univ.OctetString(
+... binValue='1000010111101110101111000000111011'
+... )
+>>> thumbnail
+OctetString(hexValue='85eebcec0')
+>>> thumbnail = univ.OctetString(
+... hexValue='FA9823C43E43510DE3422'
+... )
+>>> thumbnail
+OctetString(hexValue='fa9823c43e4351de34220')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Most frequent usage of the OctetString class is to instantiate it with
+a text string.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> welcomeMessage = univ.OctetString('Welcome to ASN.1 wilderness!')
+>>> welcomeMessage
+OctetString(b'Welcome to ASN.1 wilderness!')
+>>> print('%s' % welcomeMessage)
+Welcome to ASN.1 wilderness!
+>>> welcomeMessage[11:16]
+OctetString(b'ASN.1')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+OctetString objects support the immutable sequence object protocol.
+In other words, they behave like Python 3 bytes (or Python 2 strings).
+</p>
+
+<p>
+When running pyasn1 on Python 3, it's better to use the bytes objects for
+OctetString instantiation, as it's more reliable and efficient.
+</p>
+
+<p>
+Additionally, OctetString's can also be instantiated with a sequence of
+8-bit integers (ASCII codes).
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> univ.OctetString((77, 101, 101, 103, 111))
+OctetString(b'Meego')
+</pre>
+</td></tr></table>
+
+<p>
+It is sometimes convenient to express OctetString instances as 8-bit
+characters (Python 3 bytes or Python 2 strings) or 8-bit integers.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> octetString = univ.OctetString('ABCDEF')
+>>> octetString.asNumbers()
+(65, 66, 67, 68, 69, 70)
+>>> octetString.asOctets()
+b'ABCDEF'
+</pre>
+</td></tr></table>
+
+<a name="1.1.8"></a>
+<h4>
+1.1.8 ObjectIdentifier type
+</h4>
+
+<p>
+Values of the OBJECT IDENTIFIER type are sequences of integers that could
+be used to identify virtually anything in the world. Various ASN.1-based
+protocols employ OBJECT IDENTIFIERs for their own identification needs.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+internet-id OBJECT IDENTIFIER ::= {
+ iso(1) identified-organization(3) dod(6) internet(1)
+}
+</pre>
+</td></tr></table>
+
+<p>
+One of the natural ways to map OBJECT IDENTIFIER type into a Python
+one is to use Python tuples of integers. So this approach is taken by
+pyasn1.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> internetId = univ.ObjectIdentifier((1, 3, 6, 1))
+>>> internetId
+ObjectIdentifier('1.3.6.1')
+>>> internetId[2]
+6
+>>> internetId[1:3]
+ObjectIdentifier('3.6')
+</pre>
+</td></tr></table>
+
+<p>
+A more human-friendly "dotted" notation is also supported.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> univ.ObjectIdentifier('1.3.6.1')
+ObjectIdentifier('1.3.6.1')
+</pre>
+</td></tr></table>
+
+<p>
+Symbolic names of the arcs of object identifier, sometimes present in
+ASN.1 specifications, are not preserved and used in pyasn1 objects.
+</p>
+
+<p>
+The ObjectIdentifier objects mimic the properties of Python tuple type in
+part of immutable sequence object protocol support.
+</p>
+
+<a name="1.1.9"></a>
+<h4>
+1.1.9 Character string types
+</h4>
+
+<p>
+ASN.1 standard introduces a diverse set of text-specific types. All of them
+were designed to handle various types of characters. Some of these types seem
+be obsolete nowdays, as their target technologies are gone. Another issue
+to be aware of is that raw OCTET STRING type is sometimes used in practice
+by ASN.1 users instead of specialized character string types, despite
+explicit prohibition imposed by ASN.1 specification.
+</p>
+
+<p>
+The two types are specific to ASN.1 are NumericString and PrintableString.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+welcome-message ::= PrintableString {
+ "Welcome to ASN.1 text types"
+}
+
+dial-pad-numbers ::= NumericString {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
+}
+</pre>
+</td></tr></table>
+
+<p>
+Their pyasn1 implementations are:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import char
+>>> '%s' % char.PrintableString("Welcome to ASN.1 text types")
+'Welcome to ASN.1 text types'
+>>> dialPadNumbers = char.NumericString(
+ "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"
+)
+>>> dialPadNumbers
+NumericString(b'0123456789')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+The following types came to ASN.1 from ISO standards on character sets.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import char
+>>> char.VisibleString("abc")
+VisibleString(b'abc')
+>>> char.IA5String('abc')
+IA5String(b'abc')
+>>> char.TeletexString('abc')
+TeletexString(b'abc')
+>>> char.VideotexString('abc')
+VideotexString(b'abc')
+>>> char.GraphicString('abc')
+GraphicString(b'abc')
+>>> char.GeneralString('abc')
+GeneralString(b'abc')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+The last three types are relatively recent addition to the family of
+character string types: UniversalString, BMPString, UTF8String.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import char
+>>> char.UniversalString("abc")
+UniversalString(b'abc')
+>>> char.BMPString('abc')
+BMPString(b'abc')
+>>> char.UTF8String('abc')
+UTF8String(b'abc')
+>>> utf8String = char.UTF8String('У попа была собака')
+>>> utf8String
+UTF8String(b'\xd0\xa3 \xd0\xbf\xd0\xbe\xd0\xbf\xd0\xb0 \xd0\xb1\xd1\x8b\xd0\xbb\xd0\xb0 \
+\xd1\x81\xd0\xbe\xd0\xb1\xd0\xb0\xd0\xba\xd0\xb0')
+>>> print(utf8String)
+У попа была собака
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+In pyasn1, all character type objects behave like Python strings. None of
+them is currently constrained in terms of valid alphabet so it's up to
+the data source to keep an eye on data validation for these types.
+</p>
+
+<a name="1.1.10"></a>
+<h4>
+1.1.10 Useful types
+</h4>
+
+<p>
+There are three so-called useful types defined in the standard:
+ObjectDescriptor, GeneralizedTime, UTCTime. They all are subtypes
+of GraphicString or VisibleString types therefore useful types are
+character string types.
+</p>
+
+<p>
+It's advised by the ASN.1 standard to have an instance of ObjectDescriptor
+type holding a human-readable description of corresponding instance of
+OBJECT IDENTIFIER type. There are no formal linkage between these instances
+and provision for ObjectDescriptor uniqueness in the standard.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import useful
+>>> descrBER = useful.ObjectDescriptor(
+ "Basic encoding of a single ASN.1 type"
+)
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+GeneralizedTime and UTCTime types are designed to hold a human-readable
+timestamp in a universal and unambiguous form. The former provides
+more flexibility in notation while the latter is more strict but has
+Y2K issues.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+;; Mar 8 2010 12:00:00 MSK
+moscow-time GeneralizedTime ::= "20110308120000.0"
+;; Mar 8 2010 12:00:00 UTC
+utc-time GeneralizedTime ::= "201103081200Z"
+;; Mar 8 1999 12:00:00 UTC
+utc-time UTCTime ::= "9803081200Z"
+</pre>
+</td></tr></table>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import useful
+>>> moscowTime = useful.GeneralizedTime("20110308120000.0")
+>>> utcTime = useful.UTCTime("9803081200Z")
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Despite their intended use, these types possess no special, time-related,
+handling in pyasn1. They are just printable strings.
+</p>
+
+<a name="1.2"></a>
+<h4>
+1.2 Tagging
+</h4>
+
+<p>
+In order to continue with the Constructed ASN.1 types, we will first have
+to introduce the concept of tagging (and its pyasn1 implementation), as
+some of the Constructed types rely upon the tagging feature.
+</p>
+
+<p>
+When a value is coming into an ASN.1-based system (received from a network
+or read from some storage), the receiving entity has to determine the
+type of the value to interpret and verify it accordingly.
+</p>
+
+<p>
+Historically, the first data serialization protocol introduced in
+ASN.1 was BER (Basic Encoding Rules). According to BER, any serialized
+value is packed into a triplet of (Type, Length, Value) where Type is a
+code that identifies the value (which is called <i>tag</i> in ASN.1),
+length is the number of bytes occupied by the value in its serialized form
+and value is ASN.1 value in a form suitable for serial transmission or storage.
+</p>
+
+<p>
+For that reason almost every ASN.1 type has a tag (which is actually a
+BER type) associated with it by default.
+</p>
+
+<p>
+An ASN.1 tag could be viewed as a tuple of three numbers:
+(Class, Format, Number). While Number identifies a tag, Class component
+is used to create scopes for Numbers. Four scopes are currently defined:
+UNIVERSAL, context-specific, APPLICATION and PRIVATE. The Format component
+is actually a one-bit flag - zero for tags associated with scalar types,
+and one for constructed types (will be discussed later on).
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+MyIntegerType ::= [12] INTEGER
+MyOctetString ::= [APPLICATION 0] OCTET STRING
+</pre>
+</td></tr></table>
+
+<p>
+In pyasn1, tags are implemented as immutable, tuple-like objects:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import tag
+>>> myTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10)
+>>> myTag
+Tag(tagClass=128, tagFormat=0, tagId=10)
+>>> tuple(myTag)
+(128, 0, 10)
+>>> myTag[2]
+10
+>>> myTag == tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 10)
+False
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Default tag, associated with any ASN.1 type, could be extended or replaced
+to make new type distinguishable from its ancestor. The standard provides
+two modes of tag mangling - IMPLICIT and EXPLICIT.
+</p>
+
+<p>
+EXPLICIT mode works by appending new tag to the existing ones thus creating
+an ordered set of tags. This set will be considered as a whole for type
+identification and encoding purposes. Important property of EXPLICIT tagging
+mode is that it preserves base type information in encoding what makes it
+possible to completely recover type information from encoding.
+</p>
+
+<p>
+When tagging in IMPLICIT mode, the outermost existing tag is dropped and
+replaced with a new one.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+MyIntegerType ::= [12] IMPLICIT INTEGER
+MyOctetString ::= [APPLICATION 0] EXPLICIT OCTET STRING
+</pre>
+</td></tr></table>
+
+<p>
+To model both modes of tagging, a specialized container TagSet object (holding
+zero, one or more Tag objects) is used in pyasn1.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import tag
+>>> tagSet = tag.TagSet(
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10), # base tag
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10) # effective tag
+... )
+>>> tagSet
+TagSet(Tag(tagClass=128, tagFormat=0, tagId=10))
+>>> tagSet.getBaseTag()
+Tag(tagClass=128, tagFormat=0, tagId=10)
+>>> tagSet = tagSet.tagExplicitly(
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 20)
+... )
+>>> tagSet
+TagSet(Tag(tagClass=128, tagFormat=0, tagId=10),
+ Tag(tagClass=128, tagFormat=32, tagId=20))
+>>> tagSet = tagSet.tagExplicitly(
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 30)
+... )
+>>> tagSet
+TagSet(Tag(tagClass=128, tagFormat=0, tagId=10),
+ Tag(tagClass=128, tagFormat=32, tagId=20),
+ Tag(tagClass=128, tagFormat=32, tagId=30))
+>>> tagSet = tagSet.tagImplicitly(
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 40)
+... )
+>>> tagSet
+TagSet(Tag(tagClass=128, tagFormat=0, tagId=10),
+ Tag(tagClass=128, tagFormat=32, tagId=20),
+ Tag(tagClass=128, tagFormat=32, tagId=40))
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+As a side note: the "base tag" concept (accessible through the getBaseTag()
+method) is specific to pyasn1 -- the base tag is used to identify the original
+ASN.1 type of an object in question. Base tag is never occurs in encoding
+and is mostly used internally by pyasn1 for choosing type-specific data
+processing algorithms. The "effective tag" is the one that always appears in
+encoding and is used on tagSets comparation.
+</p>
+
+<p>
+Any two TagSet objects could be compared to see if one is a derivative
+of the other. Figuring this out is also useful in cases when a type-specific
+data processing algorithms are to be chosen.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import tag
+>>> tagSet1 = tag.TagSet(
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10) # base tag
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10) # effective tag
+... )
+>>> tagSet2 = tagSet1.tagExplicitly(
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 20)
+... )
+>>> tagSet1.isSuperTagSetOf(tagSet2)
+True
+>>> tagSet2.isSuperTagSetOf(tagSet1)
+False
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+We will complete this discussion on tagging with a real-world example. The
+following ASN.1 tagged type:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+MyIntegerType ::= [12] EXPLICIT INTEGER
+</pre>
+</td></tr></table>
+
+<p>
+could be expressed in pyasn1 like this:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, tag
+>>> class MyIntegerType(univ.Integer):
+... tagSet = univ.Integer.tagSet.tagExplicitly(
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 12)
+... )
+>>> myInteger = MyIntegerType(12345)
+>>> myInteger.getTagSet()
+TagSet(Tag(tagClass=0, tagFormat=0, tagId=2),
+ Tag(tagClass=128, tagFormat=32, tagId=12))
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Referring to the above code, the tagSet class attribute is a property of any
+pyasn1 type object that assigns default tagSet to a pyasn1 value object. This
+default tagSet specification can be ignored and effectively replaced by some
+other tagSet value passed on object instantiation.
+</p>
+
+<p>
+It's important to understand that the tag set property of pyasn1 type/value
+object can never be modifed in place. In other words, a pyasn1 type/value
+object can never change its tags. The only way is to create a new pyasn1
+type/value object and associate different tag set with it.
+</p>
+
+
+<a name="1.3"></a>
+<h4>
+1.3 Constructed types
+</h4>
+
+<p>
+Besides scalar types, ASN.1 specifies so-called constructed ones - these
+are capable of holding one or more values of other types, both scalar
+and constructed.
+</p>
+
+<p>
+In pyasn1 implementation, constructed ASN.1 types behave like
+Python sequences, and also support additional component addressing methods,
+specific to particular constructed type.
+</p>
+
+<a name="1.3.1"></a>
+<h4>
+1.3.1 Sequence and Set types
+</h4>
+
+<p>
+The Sequence and Set types have many similar properties:
+</p>
+<ul>
+<li>they can hold any number of inner components of different types
+<li>every component has a human-friendly identifier
+<li>any component can have a default value
+<li>some components can be absent.
+</ul>
+
+<p>
+However, Sequence type guarantees the ordering of Sequence value components
+to match their declaration order. By contrast, components of the
+Set type can be ordered to best suite application's needs.
+<p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+Record ::= SEQUENCE {
+ id INTEGER,
+ room [0] INTEGER OPTIONAL,
+ house [1] INTEGER DEFAULT 0
+}
+</pre>
+</td></tr></table>
+
+<p>
+Up to this moment, the only method we used for creating new pyasn1 types
+is Python sub-classing. With this method, a new, named Python class is created
+what mimics type derivation in ASN.1 grammar. However, ASN.1 also allows for
+defining anonymous subtypes (room and house components in the example above).
+To support anonymous subtyping in pyasn1, a cloning operation on an existing
+pyasn1 type object can be invoked what creates a new instance of original
+object with possibly modified properties.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedtype, tag
+>>> class Record(univ.Sequence):
+... componentType = namedtype.NamedTypes(
+... namedtype.NamedType('id', univ.Integer()),
+... namedtype.OptionalNamedType(
+... 'room',
+... univ.Integer().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
+... ),
+... namedtype.DefaultedNamedType(
+... 'house',
+... univ.Integer(0).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
+... )
+... )
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+All pyasn1 constructed type classes have a class attribute <b>componentType</b>
+that represent default type specification. Its value is a NamedTypes object.
+</p>
+
+<p>
+The NamedTypes class instance holds a sequence of NameType, OptionalNamedType
+or DefaultedNamedType objects which, in turn, refer to pyasn1 type objects that
+represent inner SEQUENCE components specification.
+</p>
+
+<p>
+Finally, invocation of a subtype() method of pyasn1 type objects in the code
+above returns an implicitly tagged copy of original object.
+</p>
+
+<p>
+Once a SEQUENCE or SET type is decleared with pyasn1, it can be instantiated
+and initialized (continuing the above code):
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> record = Record()
+>>> record.setComponentByName('id', 123)
+>>> print(record.prettyPrint())
+Record:
+ id=123
+>>>
+>>> record.setComponentByPosition(1, 321)
+>>> print(record.prettyPrint())
+Record:
+ id=123
+ room=321
+>>>
+>>> record.setDefaultComponents()
+>>> print(record.prettyPrint())
+Record:
+ id=123
+ room=321
+ house=0
+</pre>
+</td></tr></table>
+
+<p>
+Inner components of pyasn1 Sequence/Set objects could be accessed using the
+following methods:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> record.getComponentByName('id')
+Integer(123)
+>>> record.getComponentByPosition(1)
+Integer(321)
+>>> record[2]
+Integer(0)
+>>> for idx in range(len(record)):
+... print(record.getNameByPosition(idx), record.getComponentByPosition(idx))
+id 123
+room 321
+house 0
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+The Set type share all the properties of Sequence type, and additionally
+support by-tag component addressing (as all Set components have distinct
+types).
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedtype, tag
+>>> class Gamer(univ.Set):
+... componentType = namedtype.NamedTypes(
+... namedtype.NamedType('score', univ.Integer()),
+... namedtype.NamedType('player', univ.OctetString()),
+... namedtype.NamedType('id', univ.ObjectIdentifier())
+... )
+>>> gamer = Gamer()
+>>> gamer.setComponentByType(univ.Integer().getTagSet(), 121343)
+>>> gamer.setComponentByType(univ.OctetString().getTagSet(), 'Pascal')
+>>> gamer.setComponentByType(univ.ObjectIdentifier().getTagSet(), (1,3,7,2))
+>>> print(gamer.prettyPrint())
+Gamer:
+ score=121343
+ player=b'Pascal'
+ id=1.3.7.2
+>>>
+</pre>
+</td></tr></table>
+
+<a name="1.3.2"></a>
+<h4>
+1.3.2 SequenceOf and SetOf types
+</h4>
+
+<p>
+Both, SequenceOf and SetOf types resemble an unlimited size list of components.
+All the components must be of the same type.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+Progression ::= SEQUENCE OF INTEGER
+
+arithmeticProgression Progression ::= { 1, 3, 5, 7 }
+</pre>
+</td></tr></table>
+
+<p>
+SequenceOf and SetOf types are expressed by the very similar pyasn1 type
+objects. Their components can only be addressed by position and they
+both have a property of automatic resize.
+</p>
+
+<p>
+To specify inner component type, the <b>componentType</b> class attribute
+should refer to another pyasn1 type object.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> class Progression(univ.SequenceOf):
+... componentType = univ.Integer()
+>>> arithmeticProgression = Progression()
+>>> arithmeticProgression.setComponentByPosition(1, 111)
+>>> print(arithmeticProgression.prettyPrint())
+Progression:
+-empty- 111
+>>> arithmeticProgression.setComponentByPosition(0, 100)
+>>> print(arithmeticProgression.prettyPrint())
+Progression:
+100 111
+>>>
+>>> for idx in range(len(arithmeticProgression)):
+... arithmeticProgression.getComponentByPosition(idx)
+Integer(100)
+Integer(111)
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Any scalar or constructed pyasn1 type object can serve as an inner component.
+Missing components are prohibited in SequenceOf/SetOf value objects.
+</p>
+
+<a name="1.3.3"></a>
+<h4>
+1.3.3 Choice type
+</h4>
+
+<p>
+Values of ASN.1 CHOICE type can contain only a single value of a type from a
+list of possible alternatives. Alternatives must be ASN.1 types with
+distinct tags for the whole structure to remain unambiguous. Unlike most
+other types, CHOICE is an untagged one, e.g. it has no base tag of its own.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+CodeOrMessage ::= CHOICE {
+ code INTEGER,
+ message OCTET STRING
+}
+</pre>
+</td></tr></table>
+
+<p>
+In pyasn1 implementation, Choice object behaves like Set but accepts only
+a single inner component at a time. It also offers a few additional methods
+specific to its behaviour.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedtype
+>>> class CodeOrMessage(univ.Choice):
+... componentType = namedtype.NamedTypes(
+... namedtype.NamedType('code', univ.Integer()),
+... namedtype.NamedType('message', univ.OctetString())
+... )
+>>>
+>>> codeOrMessage = CodeOrMessage()
+>>> print(codeOrMessage.prettyPrint())
+CodeOrMessage:
+>>> codeOrMessage.setComponentByName('code', 123)
+>>> print(codeOrMessage.prettyPrint())
+CodeOrMessage:
+ code=123
+>>> codeOrMessage.setComponentByName('message', 'my string value')
+>>> print(codeOrMessage.prettyPrint())
+CodeOrMessage:
+ message=b'my string value'
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Since there could be only a single inner component value in the pyasn1 Choice
+value object, either of the following methods could be used for fetching it
+(continuing previous code):
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> codeOrMessage.getName()
+'message'
+>>> codeOrMessage.getComponent()
+OctetString(b'my string value')
+>>>
+</pre>
+</td></tr></table>
+
+<a name="1.3.4"></a>
+<h4>
+1.3.4 Any type
+</h4>
+
+<p>
+The ASN.1 ANY type is a kind of wildcard or placeholder that matches
+any other type without knowing it in advance. Like CHOICE type, ANY
+has no base tag.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+Error ::= SEQUENCE {
+ code INTEGER,
+ parameter ANY DEFINED BY code
+}
+</pre>
+</td></tr></table>
+
+<p>
+The ANY type is frequently used in specifications, where exact type is not
+yet agreed upon between communicating parties or the number of possible
+alternatives of a type is infinite.
+Sometimes an auxiliary selector is kept around to help parties indicate
+the kind of ANY payload in effect ("code" in the example above).
+</p>
+
+<p>
+Values of the ANY type contain serialized ASN.1 value(s) in form of
+an octet string. Therefore pyasn1 Any value object share the properties of
+pyasn1 OctetString object.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> someValue = univ.Any(b'\x02\x01\x01')
+>>> someValue
+Any(b'\x02\x01\x01')
+>>> str(someValue)
+'\x02\x01\x01'
+>>> bytes(someValue)
+b'\x02\x01\x01'
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Receiving application is supposed to explicitly deserialize the content of Any
+value object, possibly using auxiliary selector for figuring out its ASN.1
+type to pick appropriate decoder.
+</p>
+
+<p>
+There will be some more talk and code snippets covering Any type in the codecs
+chapters that follow.
+</p>
+
+<a name="1.4"></a>
+<h4>
+1.4 Subtype constraints
+</h4>
+
+<p>
+Most ASN.1 types can correspond to an infinite set of values. To adapt to
+particular application's data model and needs, ASN.1 provides a mechanism
+for limiting the infinite set to values, that make sense in particular case.
+</p>
+
+<p>
+Imposing value constraints on an ASN.1 type can also be seen as creating
+a subtype from its base type.
+</p>
+
+<p>
+In pyasn1, constraints take shape of immutable objects capable
+of evaluating given value against constraint-specific requirements.
+Constraint object is a property of pyasn1 type. Like TagSet property,
+associated with every pyasn1 type, constraints can never be modified
+in place. The only way to modify pyasn1 type constraint is to associate
+new constraint object to a new pyasn1 type object.
+</p>
+
+<p>
+A handful of different flavors of <i>constraints</i> are defined in ASN.1.
+We will discuss them one by one in the following chapters and also explain
+how to combine and apply them to types.
+</p>
+
+<a name="1.4.1"></a>
+<h4>
+1.4.1 Single value constraint
+</h4>
+
+<p>
+This kind of constraint allows for limiting type to a finite, specified set
+of values.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+DialButton ::= OCTET STRING (
+ "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
+)
+</pre>
+</td></tr></table>
+
+<p>
+Its pyasn1 implementation would look like:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import constraint
+>>> c = constraint.SingleValueConstraint(
+ '0','1','2','3','4','5','6','7','8','9'
+)
+>>> c
+SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
+>>> c('0')
+>>> c('A')
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) failed at: A
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+As can be seen in the snippet above, if a value violates the constraint, an
+exception will be thrown. A constrainted pyasn1 type object holds a
+reference to a constraint object (or their combination, as will be explained
+later) and calls it for value verification.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, constraint
+>>> class DialButton(univ.OctetString):
+... subtypeSpec = constraint.SingleValueConstraint(
+... '0','1','2','3','4','5','6','7','8','9'
+... )
+>>> DialButton('0')
+DialButton(b'0')
+>>> DialButton('A')
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) failed at: A
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Constrained pyasn1 value object can never hold a violating value.
+</p>
+
+<a name="1.4.2"></a>
+<h4>
+1.4.2 Value range constraint
+</h4>
+
+<p>
+A pair of values, compliant to a type to be constrained, denote low and upper
+bounds of allowed range of values of a type.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+Teenagers ::= INTEGER (13..19)
+</pre>
+</td></tr></table>
+
+<p>
+And in pyasn1 terms:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, constraint
+>>> class Teenagers(univ.Integer):
+... subtypeSpec = constraint.ValueRangeConstraint(13, 19)
+>>> Teenagers(14)
+Teenagers(14)
+>>> Teenagers(20)
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ValueRangeConstraint(13, 19) failed at: 20
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Value range constraint usually applies numeric types.
+</p>
+
+<a name="1.4.3"></a>
+<h4>
+1.4.3 Size constraint
+</h4>
+
+<p>
+It is sometimes convenient to set or limit the allowed size of a data item
+to be sent from one application to another to manage bandwidth and memory
+consumption issues. Size constraint specifies the lower and upper bounds
+of the size of a valid value.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+TwoBits ::= BIT STRING (SIZE (2))
+</pre>
+</td></tr></table>
+
+<p>
+Express the same grammar in pyasn1:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, constraint
+>>> class TwoBits(univ.BitString):
+... subtypeSpec = constraint.ValueSizeConstraint(2, 2)
+>>> TwoBits((1,1))
+TwoBits("'11'B")
+>>> TwoBits((1,1,0))
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ValueSizeConstraint(2, 2) failed at: (1, 1, 0)
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Size constraint can be applied to potentially massive values - bit or octet
+strings, SEQUENCE OF/SET OF values.
+</p>
+
+<a name="1.4.4"></a>
+<h4>
+1.4.4 Alphabet constraint
+</h4>
+
+<p>
+The permitted alphabet constraint is similar to Single value constraint
+but constraint applies to individual characters of a value.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+MorseCode ::= PrintableString (FROM ("."|"-"|" "))
+</pre>
+</td></tr></table>
+
+<p>
+And in pyasn1:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import char, constraint
+>>> class MorseCode(char.PrintableString):
+... subtypeSpec = constraint.PermittedAlphabetConstraint(".", "-", " ")
+>>> MorseCode("...---...")
+MorseCode('...---...')
+>>> MorseCode("?")
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ PermittedAlphabetConstraint(".", "-", " ") failed at: "?"
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Current implementation does not handle ranges of characters in constraint
+(FROM "A".."Z" syntax), one has to list the whole set in a range.
+</p>
+
+<a name="1.4.5"></a>
+<h4>
+1.4.5 Constraint combinations
+</h4>
+
+<p>
+Up to this moment, we used a single constraint per ASN.1 type. The standard,
+however, allows for combining multiple individual constraints into
+intersections, unions and exclusions.
+</p>
+
+<p>
+In pyasn1 data model, all of these methods of constraint combinations are
+implemented as constraint-like objects holding individual constraint (or
+combination) objects. Like terminal constraint objects, combination objects
+are capable to perform value verification at its set of enclosed constraints
+according to the logic of particular combination.
+</p>
+
+<p>
+Constraints intersection verification succeeds only if a value is
+compliant to each constraint in a set. To begin with, the following
+specification will constitute a valid telephone number:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+PhoneNumber ::= NumericString (FROM ("0".."9")) (SIZE 11)
+</pre>
+</td></tr></table>
+
+<p>
+Constraint intersection object serves the logic above:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import char, constraint
+>>> class PhoneNumber(char.NumericString):
+... subtypeSpec = constraint.ConstraintsIntersection(
+... constraint.PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'),
+... constraint.ValueSizeConstraint(11, 11)
+... )
+>>> PhoneNumber('79039343212')
+PhoneNumber('79039343212')
+>>> PhoneNumber('?9039343212')
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ConstraintsIntersection(
+ PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'),
+ ValueSizeConstraint(11, 11)) failed at:
+ PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9') failed at: "?039343212"
+>>> PhoneNumber('9343212')
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ConstraintsIntersection(
+ PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'),
+ ValueSizeConstraint(11, 11)) failed at:
+ ValueSizeConstraint(10, 10) failed at: "9343212"
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Union of constraints works by making sure that a value is compliant
+to any of the constraint in a set. For instance:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+CapitalOrSmall ::= IA5String (FROM ('A','B','C') | FROM ('a','b','c'))
+</pre>
+</td></tr></table>
+
+<p>
+It's important to note, that a value must fully comply to any single
+constraint in a set. In the specification above, a value of all small or
+all capital letters is compliant, but a mix of small&capitals is not.
+Here's its pyasn1 analogue:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import char, constraint
+>>> class CapitalOrSmall(char.IA5String):
+... subtypeSpec = constraint.ConstraintsUnion(
+... constraint.PermittedAlphabetConstraint('A','B','C'),
+... constraint.PermittedAlphabetConstraint('a','b','c')
+... )
+>>> CapitalOrSmall('ABBA')
+CapitalOrSmall('ABBA')
+>>> CapitalOrSmall('abba')
+CapitalOrSmall('abba')
+>>> CapitalOrSmall('Abba')
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ConstraintsUnion(PermittedAlphabetConstraint('A', 'B', 'C'),
+ PermittedAlphabetConstraint('a', 'b', 'c')) failed at: failed for "Abba"
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Finally, the exclusion constraint simply negates the logic of value
+verification at a constraint. In the following example, any integer value
+is allowed in a type but not zero.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+NoZero ::= INTEGER (ALL EXCEPT 0)
+</pre>
+</td></tr></table>
+
+<p>
+In pyasn1 the above definition would read:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, constraint
+>>> class NoZero(univ.Integer):
+... subtypeSpec = constraint.ConstraintsExclusion(
+... constraint.SingleValueConstraint(0)
+... )
+>>> NoZero(1)
+NoZero(1)
+>>> NoZero(0)
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ConstraintsExclusion(SingleValueConstraint(0)) failed at: 0
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+The depth of such a constraints tree, built with constraint combination objects
+at its nodes, has not explicit limit. Value verification is performed in a
+recursive manner till a definite solution is found.
+</p>
+
+<a name="1.5"></a>
+<h4>
+1.5 Types relationships
+</h4>
+
+<p>
+In the course of data processing in an application, it is sometimes
+convenient to figure out the type relationships between pyasn1 type or
+value objects. Formally, two things influence pyasn1 types relationship:
+<i>tag set</i> and <i>subtype constraints</i>. One pyasn1 type is considered
+to be a derivative of another if their TagSet and Constraint objects are
+a derivation of one another.
+</p>
+
+<p>
+The following example illustrates the concept (we use the same tagset but
+different constraints for simplicity):
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, constraint
+>>> i1 = univ.Integer(subtypeSpec=constraint.ValueRangeConstraint(3,8))
+>>> i2 = univ.Integer(subtypeSpec=constraint.ConstraintsIntersection(
+... constraint.ValueRangeConstraint(3,8),
+... constraint.ValueRangeConstraint(4,7)
+... ) )
+>>> i1.isSameTypeWith(i2)
+False
+>>> i1.isSuperTypeOf(i2)
+True
+>>> i1.isSuperTypeOf(i1)
+True
+>>> i2.isSuperTypeOf(i1)
+False
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+As can be seen in the above code snippet, there are two methods of any pyasn1
+type/value object that test types for their relationship:
+<b>isSameTypeWith</b>() and <b>isSuperTypeOf</b>(). The former is
+self-descriptive while the latter yields true if the argument appears
+to be a pyasn1 object which has tagset and constraints derived from those
+of the object being called.
+</p>
+
+<a name="2"></a>
+<h3>
+2. Codecs
+</h3>
+
+<p>
+In ASN.1 context,
+<a href=http://en.wikipedia.org/wiki/Codec>codec</a>
+is a program that transforms between concrete data structures and a stream
+of octets, suitable for transmission over the wire. This serialized form of
+data is sometimes called <i>substrate</i> or <i>essence</i>.
+</p>
+
+<p>
+In pyasn1 implementation, substrate takes shape of Python 3 bytes or
+Python 2 string objects.
+</p>
+
+<p>
+One of the properties of a codec is its ability to cope with incomplete
+data and/or substrate what implies codec to be stateful. In other words,
+when decoder runs out of substrate and data item being recovered is still
+incomplete, stateful codec would suspend and complete data item recovery
+whenever the rest of substrate becomes available. Similarly, stateful encoder
+would encode data items in multiple steps waiting for source data to
+arrive. Codec restartability is especially important when application deals
+with large volumes of data and/or runs on low RAM. For an interesting
+discussion on codecs options and design choices, refer to
+<a href=http://directory.apache.org/subprojects/asn1/>Apache ASN.1 project</a>
+.
+</p>
+
+<p>
+As of this writing, codecs implemented in pyasn1 are all stateless, mostly
+to keep the code simple.
+</p>
+
+<p>
+The pyasn1 package currently supports
+<a href=http://en.wikipedia.org/wiki/Basic_encoding_rules>BER</a> codec and
+its variations --
+<a href=http://en.wikipedia.org/wiki/Canonical_encoding_rules>CER</a> and
+<a href=http://en.wikipedia.org/wiki/Distinguished_encoding_rules>DER</a>.
+More ASN.1 codecs are planned for implementation in the future.
+</p>
+
+<a name="2.1"></a>
+<h4>
+2.1 Encoders
+</h4>
+
+<p>
+Encoder is used for transforming pyasn1 value objects into substrate. Only
+pyasn1 value objects could be serialized, attempts to process pyasn1 type
+objects will cause encoder failure.
+</p>
+
+<p>
+The following code will create a pyasn1 Integer object and serialize it with
+BER encoder:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder
+>>> encoder.encode(univ.Integer(123456))
+b'\x02\x03\x01\xe2@'
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+BER standard also defines a so-called <i>indefinite length</i> encoding form
+which makes large data items processing more memory efficient. It is mostly
+useful when encoder does not have the whole value all at once and the
+length of the value can not be determined at the beginning of encoding.
+</p>
+
+<p>
+<i>Constructed encoding</i> is another feature of BER closely related to the
+indefinite length form. In essence, a large scalar value (such as ASN.1
+character BitString type) could be chopped into smaller chunks by encoder
+and transmitted incrementally to limit memory consumption. Unlike indefinite
+length case, the length of the whole value must be known in advance when
+using constructed, definite length encoding form.
+</p>
+
+<p>
+Since pyasn1 codecs are not restartable, pyasn1 encoder may only encode data
+item all at once. However, even in this case, generating indefinite length
+encoding may help a low-memory receiver, running a restartable decoder,
+to process a large data item.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder
+>>> encoder.encode(
+... univ.OctetString('The quick brown fox jumps over the lazy dog'),
+... defMode=False,
+... maxChunkSize=8
+... )
+b'$\x80\x04\x08The quic\x04\x08k brown \x04\x08fox jump\x04\x08s over \
+t\x04\x08he lazy \x04\x03dog\x00\x00'
+>>>
+>>> encoder.encode(
+... univ.OctetString('The quick brown fox jumps over the lazy dog'),
+... maxChunkSize=8
+... )
+b'$7\x04\x08The quic\x04\x08k brown \x04\x08fox jump\x04\x08s over \
+t\x04\x08he lazy \x04\x03dog'
+</pre>
+</td></tr></table>
+
+<p>
+The <b>defMode</b> encoder parameter disables definite length encoding mode,
+while the optional <b>maxChunkSize</b> parameter specifies desired
+substrate chunk size that influences memory requirements at the decoder's end.
+</p>
+
+<p>
+To use CER or DER encoders one needs to explicitly import and call them - the
+APIs are all compatible.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder as ber_encoder
+>>> from pyasn1.codec.cer import encoder as cer_encoder
+>>> from pyasn1.codec.der import encoder as der_encoder
+>>> ber_encoder.encode(univ.Boolean(True))
+b'\x01\x01\x01'
+>>> cer_encoder.encode(univ.Boolean(True))
+b'\x01\x01\xff'
+>>> der_encoder.encode(univ.Boolean(True))
+b'\x01\x01\xff'
+>>>
+</pre>
+</td></tr></table>
+
+<a name="2.2"></a>
+<h4>
+2.2 Decoders
+</h4>
+
+<p>
+In the process of decoding, pyasn1 value objects are created and linked to
+each other, based on the information containted in the substrate. Thus,
+the original pyasn1 value object(s) are recovered.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> substrate = encoder.encode(univ.Boolean(True))
+>>> decoder.decode(substrate)
+(Boolean('True(1)'), b'')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Commenting on the code snippet above, pyasn1 decoder accepts substrate
+as an argument and returns a tuple of pyasn1 value object (possibly
+a top-level one in case of constructed object) and unprocessed part
+of input substrate.
+</p>
+
+<p>
+All pyasn1 decoders can handle both definite and indefinite length
+encoding modes automatically, explicit switching into one mode
+to another is not required.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> substrate = encoder.encode(
+... univ.OctetString('The quick brown fox jumps over the lazy dog'),
+... defMode=False,
+... maxChunkSize=8
+... )
+>>> decoder.decode(substrate)
+(OctetString(b'The quick brown fox jumps over the lazy dog'), b'')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Speaking of BER/CER/DER encoding, in many situations substrate may not contain
+all necessary information needed for complete and accurate ASN.1 values
+recovery. The most obvious cases include implicitly tagged ASN.1 types
+and constrained types.
+</p>
+
+<p>
+As discussed earlier in this handbook, when an ASN.1 type is implicitly
+tagged, previous outermost tag is lost and never appears in substrate.
+If it is the base tag that gets lost, decoder is unable to pick type-specific
+value decoder at its table of built-in types, and therefore recover
+the value part, based only on the information contained in substrate. The
+approach taken by pyasn1 decoder is to use a prototype pyasn1 type object (or
+a set of them) to <i>guide</i> the decoding process by matching [possibly
+incomplete] tags recovered from substrate with those found in prototype pyasn1
+type objects (also called pyasn1 specification object further in this paper).
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.codec.ber import decoder
+>>> decoder.decode(b'\x02\x01\x0c', asn1Spec=univ.Integer())
+Integer(12), b''
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Decoder would neither modify pyasn1 specification object nor use
+its current values (if it's a pyasn1 value object), but rather use it as
+a hint for choosing proper decoder and as a pattern for creating new objects:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, tag
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> i = univ.Integer(12345).subtype(
+... implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 40)
+... )
+>>> substrate = encoder.encode(i)
+>>> substrate
+b'\x9f(\x0209'
+>>> decoder.decode(substrate)
+Traceback (most recent call last):
+...
+pyasn1.error.PyAsn1Error:
+ TagSet(Tag(tagClass=128, tagFormat=0, tagId=40)) not in asn1Spec
+>>> decoder.decode(substrate, asn1Spec=i)
+(Integer(12345), b'')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Notice in the example above, that an attempt to run decoder without passing
+pyasn1 specification object fails because recovered tag does not belong
+to any of the built-in types.
+</p>
+
+<p>
+Another important feature of guided decoder operation is the use of
+values constraints possibly present in pyasn1 specification object.
+To explain this, we will decode a random integer object into generic Integer
+and the constrained one.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, constraint
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> class DialDigit(univ.Integer):
+... subtypeSpec = constraint.ValueRangeConstraint(0,9)
+>>> substrate = encoder.encode(univ.Integer(13))
+>>> decoder.decode(substrate)
+(Integer(13), b'')
+>>> decoder.decode(substrate, asn1Spec=DialDigit())
+Traceback (most recent call last):
+...
+pyasn1.type.error.ValueConstraintError:
+ ValueRangeConstraint(0, 9) failed at: 13
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Similarily to encoders, to use CER or DER decoders application has to
+explicitly import and call them - all APIs are compatible.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder as ber_encoder
+>>> substrate = ber_encoder.encode(univ.OctetString('http://pyasn1.sf.net'))
+>>>
+>>> from pyasn1.codec.ber import decoder as ber_decoder
+>>> from pyasn1.codec.cer import decoder as cer_decoder
+>>> from pyasn1.codec.der import decoder as der_decoder
+>>>
+>>> ber_decoder.decode(substrate)
+(OctetString(b'http://pyasn1.sf.net'), b'')
+>>> cer_decoder.decode(substrate)
+(OctetString(b'http://pyasn1.sf.net'), b'')
+>>> der_decoder.decode(substrate)
+(OctetString(b'http://pyasn1.sf.net'), b'')
+>>>
+</pre>
+</td></tr></table>
+
+<a name="2.2.1"></a>
+<h4>
+2.2.1 Decoding untagged types
+</h4>
+
+<p>
+It has already been mentioned, that ASN.1 has two "special case" types:
+CHOICE and ANY. They are different from other types in part of
+tagging - unless these two are additionally tagged, neither of them will
+have their own tag. Therefore these types become invisible in substrate
+and can not be recovered without passing pyasn1 specification object to
+decoder.
+</p>
+
+<p>
+To explain the issue, we will first prepare a Choice object to deal with:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedtype
+>>> class CodeOrMessage(univ.Choice):
+... componentType = namedtype.NamedTypes(
+... namedtype.NamedType('code', univ.Integer()),
+... namedtype.NamedType('message', univ.OctetString())
+... )
+>>>
+>>> codeOrMessage = CodeOrMessage()
+>>> codeOrMessage.setComponentByName('message', 'my string value')
+>>> print(codeOrMessage.prettyPrint())
+CodeOrMessage:
+ message=b'my string value'
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Let's now encode this Choice object and then decode its substrate
+with and without pyasn1 specification object:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> substrate = encoder.encode(codeOrMessage)
+>>> substrate
+b'\x04\x0fmy string value'
+>>> encoder.encode(univ.OctetString('my string value'))
+b'\x04\x0fmy string value'
+>>>
+>>> decoder.decode(substrate)
+(OctetString(b'my string value'), b'')
+>>> codeOrMessage, substrate = decoder.decode(substrate, asn1Spec=CodeOrMessage())
+>>> print(codeOrMessage.prettyPrint())
+CodeOrMessage:
+ message=b'my string value'
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+First thing to notice in the listing above is that the substrate produced
+for our Choice value object is equivalent to the substrate for an OctetString
+object initialized to the same value. In other words, any information about
+the Choice component is absent in encoding.
+</p>
+
+<p>
+Sure enough, that kind of substrate will decode into an OctetString object,
+unless original Choice type object is passed to decoder to guide the decoding
+process.
+</p>
+
+<p>
+Similarily untagged ANY type behaves differently on decoding phase - when
+decoder bumps into an Any object in pyasn1 specification, it stops decoding
+and puts all the substrate into a new Any value object in form of an octet
+string. Concerned application could then re-run decoder with an additional,
+more exact pyasn1 specification object to recover the contents of Any
+object.
+</p>
+
+<p>
+As it was mentioned elsewhere in this paper, Any type allows for incomplete
+or changing ASN.1 specification to be handled gracefully by decoder and
+applications.
+</p>
+
+<p>
+To illustrate the working of Any type, we'll have to make the stage
+by encoding a pyasn1 object and then putting its substrate into an any
+object.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> innerSubstrate = encoder.encode(univ.Integer(1234))
+>>> innerSubstrate
+b'\x02\x02\x04\xd2'
+>>> any = univ.Any(innerSubstrate)
+>>> any
+Any(b'\x02\x02\x04\xd2')
+>>> substrate = encoder.encode(any)
+>>> substrate
+b'\x02\x02\x04\xd2'
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+As with Choice type encoding, there is no traces of Any type in substrate.
+Obviously, the substrate we are dealing with, will decode into the inner
+[Integer] component, unless pyasn1 specification is given to guide the
+decoder. Continuing previous code:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> from pyasn1.codec.ber import encoder, decoder
+
+>>> decoder.decode(substrate)
+(Integer(1234), b'')
+>>> any, substrate = decoder.decode(substrate, asn1Spec=univ.Any())
+>>> any
+Any(b'\x02\x02\x04\xd2')
+>>> decoder.decode(str(any))
+(Integer(1234), b'')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Both CHOICE and ANY types are widely used in practice. Reader is welcome to
+take a look at
+<a href=http://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt>
+ASN.1 specifications of X.509 applications</a> for more information.
+</p>
+
+<a name="2.2.2"></a>
+<h4>
+2.2.2 Ignoring unknown types
+</h4>
+
+<p>
+When dealing with a loosely specified ASN.1 structure, the receiving
+end may not be aware of some types present in the substrate. It may be
+convenient then to turn decoder into a recovery mode. Whilst there, decoder
+will not bail out when hit an unknown tag but rather treat it as an Any
+type.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, tag
+>>> from pyasn1.codec.ber import encoder, decoder
+>>> taggedInt = univ.Integer(12345).subtype(
+... implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 40)
+... )
+>>> substrate = encoder.encode(taggedInt)
+>>> decoder.decode(substrate)
+Traceback (most recent call last):
+...
+pyasn1.error.PyAsn1Error: TagSet(Tag(tagClass=128, tagFormat=0, tagId=40)) not in asn1Spec
+>>>
+>>> decoder.decode.defaultErrorState = decoder.stDumpRawValue
+>>> decoder.decode(substrate)
+(Any(b'\x9f(\x0209'), '')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+It's also possible to configure a custom decoder, to handle unknown tags
+found in substrate. This can be done by means of <b>defaultRawDecoder</b>
+attribute holding a reference to type decoder object. Refer to the source
+for API details.
+</p>
+
+<a name="3"></a>
+<h3>
+3. Feedback and getting help
+</h3>
+
+<p>
+Although pyasn1 software is almost a decade old and used in many production
+environments, it still may have bugs and non-implemented pieces. Anyone
+who happens to run into such defect is welcome to complain to
+<a href=mailto:pyasn1-users@lists.sourceforge.net>pyasn1 mailing list</a>
+or better yet fix the issue and send
+<a href=mailto:ilya@glas.net>me</a> the patch.
+</p>
+
+<p>
+Typically, pyasn1 is used for building arbitrary protocol support into
+various applications. This involves manual translation of ASN.1 data
+structures into their pyasn1 implementations. To save time and effort,
+data structures for some of the popular protocols are pre-programmed
+and kept for further re-use in form of the
+<a href=http://sourceforge.net/projects/pyasn1/files/pyasn1-modules/>
+pyasn1-modules package</a>. For instance, many structures for PKI (X.509,
+PKCS#*, CRMF, OCSP), LDAP and SNMP are present.
+Applications authors are advised to import and use relevant modules
+from that package whenever needed protocol structures are already
+there. New protocol modules contributions are welcome.
+</p>
+
+<p>
+And finally, the latest pyasn1 package revision is available for free
+download from
+<a href=http://sourceforge.net/projects/pyasn1/>project home</a> and
+also from the
+<a href=http://pypi.python.org/pypi>Python package repository</a>.
+</p>
+
+<hr>
+
+</td>
+</tr>
+</table>
+</center>
+</body>
+</html>
diff --git a/python/pyasn1/doc/scalar.html b/python/pyasn1/doc/scalar.html
new file mode 100644
index 0000000000..e5ccefe60e
--- /dev/null
+++ b/python/pyasn1/doc/scalar.html
@@ -0,0 +1,794 @@
+<html>
+<title>
+PyASN1 data model and scalar types
+</title>
+<head>
+</head>
+<body>
+<center>
+<table width=60%>
+<tr>
+<td>
+
+<h3>
+1. Data model for ASN.1 types
+</h3>
+
+<p>
+All ASN.1 types could be categorized into two groups: scalar (also called
+simple or primitive) and constructed. The first group is populated by
+well-known types like Integer or String. Members of constructed group
+hold other types (simple or constructed) as their inner components, thus
+they are semantically close to a programming language records or lists.
+</p>
+
+<p>
+In pyasn1, all ASN.1 types and values are implemented as Python objects.
+The same pyasn1 object can represent either ASN.1 type and/or value
+depending of the presense of value initializer on object instantiation.
+We will further refer to these as <i>pyasn1 type object</i> versus <i>pyasn1
+value object</i>.
+</p>
+
+<p>
+Primitive ASN.1 types are implemented as immutable scalar objects. There values
+could be used just like corresponding native Python values (integers,
+strings/bytes etc) and freely mixed with them in expressions.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> asn1IntegerValue = univ.Integer(12)
+>>> asn1IntegerValue - 2
+10
+>>> univ.OctetString('abc') == 'abc'
+True # Python 2
+>>> univ.OctetString(b'abc') == b'abc'
+True # Python 3
+</pre>
+</td></tr></table>
+
+<p>
+It would be an error to perform an operation on a pyasn1 type object
+as it holds no value to deal with:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> asn1IntegerType = univ.Integer()
+>>> asn1IntegerType - 2
+...
+pyasn1.error.PyAsn1Error: No value for __coerce__()
+</pre>
+</td></tr></table>
+
+<a name="1.1"></a>
+<h4>
+1.1 Scalar types
+</h4>
+
+<p>
+In the sub-sections that follow we will explain pyasn1 mapping to those
+primitive ASN.1 types. Both, ASN.1 notation and corresponding pyasn1
+syntax will be given in each case.
+</p>
+
+<a name="1.1.1"></a>
+<h4>
+1.1.1 Boolean type
+</h4>
+
+<p>
+This is the simplest type those values could be either True or False.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+;; type specification
+FunFactorPresent ::= BOOLEAN
+
+;; values declaration and assignment
+pythonFunFactor FunFactorPresent ::= TRUE
+cobolFunFactor FunFactorPresent :: FALSE
+</pre>
+</td></tr></table>
+
+<p>
+And here's pyasn1 version of it:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> class FunFactorPresent(univ.Boolean): pass
+...
+>>> pythonFunFactor = FunFactorPresent(True)
+>>> cobolFunFactor = FunFactorPresent(False)
+>>> pythonFunFactor
+FunFactorPresent('True(1)')
+>>> cobolFunFactor
+FunFactorPresent('False(0)')
+>>> pythonFunFactor == cobolFunFactor
+False
+>>>
+</pre>
+</td></tr></table>
+
+<a name="1.1.2"></a>
+<h4>
+1.1.2 Null type
+</h4>
+
+<p>
+The NULL type is sometimes used to express the absense of any information.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+;; type specification
+Vote ::= CHOICE {
+ agreed BOOLEAN,
+ skip NULL
+}
+</td></tr></table>
+
+;; value declaration and assignment
+myVote Vote ::= skip:NULL
+</pre>
+
+<p>
+We will explain the CHOICE type later in this paper, meanwhile the NULL
+type:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> skip = univ.Null()
+>>> skip
+Null('')
+>>>
+</pre>
+</td></tr></table>
+
+<a name="1.1.3"></a>
+<h4>
+1.1.3 Integer type
+</h4>
+
+<p>
+ASN.1 defines the values of Integer type as negative or positive of whatever
+length. This definition plays nicely with Python as the latter places no
+limit on Integers. However, some ASN.1 implementations may impose certain
+limits of integer value ranges. Keep that in mind when designing new
+data structures.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+;; values specification
+age-of-universe INTEGER ::= 13750000000
+mean-martian-surface-temperature INTEGER ::= -63
+</pre>
+</td></tr></table>
+
+<p>
+A rather strigntforward mapping into pyasn1:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> ageOfUniverse = univ.Integer(13750000000)
+>>> ageOfUniverse
+Integer(13750000000)
+>>>
+>>> meanMartianSurfaceTemperature = univ.Integer(-63)
+>>> meanMartianSurfaceTemperature
+Integer(-63)
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+ASN.1 allows to assign human-friendly names to particular values of
+an INTEGER type.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+Temperature ::= INTEGER {
+ freezing(0),
+ boiling(100)
+}
+</pre>
+</td></tr></table>
+
+<p>
+The Temperature type expressed in pyasn1:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedval
+>>> class Temperature(univ.Integer):
+... namedValues = namedval.NamedValues(('freezing', 0), ('boiling', 100))
+...
+>>> t = Temperature(0)
+>>> t
+Temperature('freezing(0)')
+>>> t + 1
+Temperature(1)
+>>> t + 100
+Temperature('boiling(100)')
+>>> t = Temperature('boiling')
+>>> t
+Temperature('boiling(100)')
+>>> Temperature('boiling') / 2
+Temperature(50)
+>>> -1 < Temperature('freezing')
+True
+>>> 47 > Temperature('boiling')
+False
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+These values labels have no effect on Integer type operations, any value
+still could be assigned to a type (information on value constraints will
+follow further in this paper).
+</p>
+
+<a name="1.1.4"></a>
+<h4>
+1.1.4 Enumerated type
+</h4>
+
+<p>
+ASN.1 Enumerated type differs from an Integer type in a number of ways.
+Most important is that its instance can only hold a value that belongs
+to a set of values specified on type declaration.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+error-status ::= ENUMERATED {
+ no-error(0),
+ authentication-error(10),
+ authorization-error(20),
+ general-failure(51)
+}
+</pre>
+</td></tr></table>
+
+<p>
+When constructing Enumerated type we will use two pyasn1 features: values
+labels (as mentioned above) and value constraint (will be described in
+more details later on).
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedval, constraint
+>>> class ErrorStatus(univ.Enumerated):
+... namedValues = namedval.NamedValues(
+... ('no-error', 0),
+... ('authentication-error', 10),
+... ('authorization-error', 20),
+... ('general-failure', 51)
+... )
+... subtypeSpec = univ.Enumerated.subtypeSpec + \
+... constraint.SingleValueConstraint(0, 10, 20, 51)
+...
+>>> errorStatus = univ.ErrorStatus('no-error')
+>>> errorStatus
+ErrorStatus('no-error(0)')
+>>> errorStatus == univ.ErrorStatus('general-failure')
+False
+>>> univ.ErrorStatus('non-existing-state')
+Traceback (most recent call last):
+...
+pyasn1.error.PyAsn1Error: Can't coerce non-existing-state into integer
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Particular integer values associated with Enumerated value states
+have no meaning. They should not be used as such or in any kind of
+math operation. Those integer values are only used by codecs to
+transfer state from one entity to another.
+</p>
+
+<a name="1.1.5"></a>
+<h4>
+1.1.5 Real type
+</h4>
+
+<p>
+Values of the Real type are a three-component tuple of mantissa, base and
+exponent. All three are integers.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+pi ::= REAL { mantissa 314159, base 10, exponent -5 }
+</pre>
+</td></tr></table>
+
+<p>
+Corresponding pyasn1 objects can be initialized with either a three-component
+tuple or a Python float. Infinite values could be expressed in a way,
+compatible with Python float type.
+
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> pi = univ.Real((314159, 10, -5))
+>>> pi
+Real((314159, 10,-5))
+>>> float(pi)
+3.14159
+>>> pi == univ.Real(3.14159)
+True
+>>> univ.Real('inf')
+Real('inf')
+>>> univ.Real('-inf') == float('-inf')
+True
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+If a Real object is initialized from a Python float or yielded by a math
+operation, the base is set to decimal 10 (what affects encoding).
+</p>
+
+<a name="1.1.6"></a>
+<h4>
+1.1.6 Bit string type
+</h4>
+
+<p>
+ASN.1 BIT STRING type holds opaque binary data of an arbitrarily length.
+A BIT STRING value could be initialized by either a binary (base 2) or
+hex (base 16) value.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+public-key BIT STRING ::= '1010111011110001010110101101101
+ 1011000101010000010110101100010
+ 0110101010000111101010111111110'B
+
+signature BIT STRING ::= 'AF01330CD932093392100B39FF00DE0'H
+</pre>
+</td></tr></table>
+
+<p>
+The pyasn1 BitString objects can initialize from native ASN.1 notation
+(base 2 or base 16 strings) or from a Python tuple of binary components.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> publicKey = univ.BitString(
+... "'1010111011110001010110101101101"
+... "1011000101010000010110101100010"
+... "0110101010000111101010111111110'B"
+)
+>>> publicKey
+BitString("'10101110111100010101101011011011011000101010000010110101100010\
+0110101010000111101010111111110'B")
+>>> signature = univ.BitString(
+... "'AF01330CD932093392100B39FF00DE0'H"
+... )
+>>> signature
+BitString("'101011110000000100110011000011001101100100110010000010010011001\
+1100100100001000000001011001110011111111100000000110111100000'B")
+>>> fingerprint = univ.BitString(
+... (1, 0, 1, 1 ,0, 1, 1, 1, 0, 1, 0, 1)
+... )
+>>> fingerprint
+BitString("'101101110101'B")
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Another BIT STRING initialization method supported by ASN.1 notation
+is to specify only 1-th bits along with their human-friendly label
+and bit offset relative to the beginning of the bit string. With this
+method, all not explicitly mentioned bits are doomed to be zeros.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+bit-mask BIT STRING ::= {
+ read-flag(0),
+ write-flag(2),
+ run-flag(4)
+}
+</pre>
+</td></tr></table>
+
+<p>
+To express this in pyasn1, we will employ the named values feature (as with
+Enumeration type).
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, namedval
+>>> class BitMask(univ.BitString):
+... namedValues = namedval.NamedValues(
+... ('read-flag', 0),
+... ('write-flag', 2),
+... ('run-flag', 4)
+... )
+>>> bitMask = BitMask('read-flag,run-flag')
+>>> bitMask
+BitMask("'10001'B")
+>>> tuple(bitMask)
+(1, 0, 0, 0, 1)
+>>> bitMask[4]
+1
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+The BitString objects mimic the properties of Python tuple type in part
+of immutable sequence object protocol support.
+</p>
+
+<a name="1.1.7"></a>
+<h4>
+1.1.7 OctetString type
+</h4>
+
+<p>
+The OCTET STRING type is a confusing subject. According to ASN.1
+specification, this type is similar to BIT STRING, the major difference
+is that the former operates in 8-bit chunks of data. What is important
+to note, is that OCTET STRING was NOT designed to handle text strings - the
+standard provides many other types specialized for text content. For that
+reason, ASN.1 forbids to initialize OCTET STRING values with "quoted text
+strings", only binary or hex initializers, similar to BIT STRING ones,
+are allowed.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+thumbnail OCTET STRING ::= '1000010111101110101111000000111011'B
+thumbnail OCTET STRING ::= 'FA9823C43E43510DE3422'H
+</pre>
+</td></tr></table>
+
+<p>
+However, ASN.1 users (e.g. protocols designers) seem to ignore the original
+purpose of the OCTET STRING type - they used it for handling all kinds of
+data, including text strings.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+welcome-message OCTET STRING ::= "Welcome to ASN.1 wilderness!"
+</pre>
+</td></tr></table>
+
+<p>
+In pyasn1, we have taken a liberal approach and allowed both BIT STRING
+style and quoted text initializers for the OctetString objects. To avoid
+possible collisions, quoted text is the default initialization syntax.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> thumbnail = univ.OctetString(
+... binValue='1000010111101110101111000000111011'
+... )
+>>> thumbnail
+OctetString(hexValue='85eebcec0')
+>>> thumbnail = univ.OctetString(
+... hexValue='FA9823C43E43510DE3422'
+... )
+>>> thumbnail
+OctetString(hexValue='fa9823c43e4351de34220')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Most frequent usage of the OctetString class is to instantiate it with
+a text string.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> welcomeMessage = univ.OctetString('Welcome to ASN.1 wilderness!')
+>>> welcomeMessage
+OctetString(b'Welcome to ASN.1 wilderness!')
+>>> print('%s' % welcomeMessage)
+Welcome to ASN.1 wilderness!
+>>> welcomeMessage[11:16]
+OctetString(b'ASN.1')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+OctetString objects support the immutable sequence object protocol.
+In other words, they behave like Python 3 bytes (or Python 2 strings).
+</p>
+
+<p>
+When running pyasn1 on Python 3, it's better to use the bytes objects for
+OctetString instantiation, as it's more reliable and efficient.
+</p>
+
+<p>
+Additionally, OctetString's can also be instantiated with a sequence of
+8-bit integers (ASCII codes).
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> univ.OctetString((77, 101, 101, 103, 111))
+OctetString(b'Meego')
+</pre>
+</td></tr></table>
+
+<p>
+It is sometimes convenient to express OctetString instances as 8-bit
+characters (Python 3 bytes or Python 2 strings) or 8-bit integers.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> octetString = univ.OctetString('ABCDEF')
+>>> octetString.asNumbers()
+(65, 66, 67, 68, 69, 70)
+>>> octetString.asOctets()
+b'ABCDEF'
+</pre>
+</td></tr></table>
+
+<a name="1.1.8"></a>
+<h4>
+1.1.8 ObjectIdentifier type
+</h4>
+
+<p>
+Values of the OBJECT IDENTIFIER type are sequences of integers that could
+be used to identify virtually anything in the world. Various ASN.1-based
+protocols employ OBJECT IDENTIFIERs for their own identification needs.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+internet-id OBJECT IDENTIFIER ::= {
+ iso(1) identified-organization(3) dod(6) internet(1)
+}
+</pre>
+</td></tr></table>
+
+<p>
+One of the natural ways to map OBJECT IDENTIFIER type into a Python
+one is to use Python tuples of integers. So this approach is taken by
+pyasn1.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> internetId = univ.ObjectIdentifier((1, 3, 6, 1))
+>>> internetId
+ObjectIdentifier('1.3.6.1')
+>>> internetId[2]
+6
+>>> internetId[1:3]
+ObjectIdentifier('3.6')
+</pre>
+</td></tr></table>
+
+<p>
+A more human-friendly "dotted" notation is also supported.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ
+>>> univ.ObjectIdentifier('1.3.6.1')
+ObjectIdentifier('1.3.6.1')
+</pre>
+</td></tr></table>
+
+<p>
+Symbolic names of the arcs of object identifier, sometimes present in
+ASN.1 specifications, are not preserved and used in pyasn1 objects.
+</p>
+
+<p>
+The ObjectIdentifier objects mimic the properties of Python tuple type in
+part of immutable sequence object protocol support.
+</p>
+
+<a name="1.1.9"></a>
+<h4>
+1.1.9 Character string types
+</h4>
+
+<p>
+ASN.1 standard introduces a diverse set of text-specific types. All of them
+were designed to handle various types of characters. Some of these types seem
+be obsolete nowdays, as their target technologies are gone. Another issue
+to be aware of is that raw OCTET STRING type is sometimes used in practice
+by ASN.1 users instead of specialized character string types, despite
+explicit prohibition imposed by ASN.1 specification.
+</p>
+
+<p>
+The two types are specific to ASN.1 are NumericString and PrintableString.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+welcome-message ::= PrintableString {
+ "Welcome to ASN.1 text types"
+}
+
+dial-pad-numbers ::= NumericString {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
+}
+</pre>
+</td></tr></table>
+
+<p>
+Their pyasn1 implementations are:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import char
+>>> '%s' % char.PrintableString("Welcome to ASN.1 text types")
+'Welcome to ASN.1 text types'
+>>> dialPadNumbers = char.NumericString(
+ "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"
+)
+>>> dialPadNumbers
+NumericString(b'0123456789')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+The following types came to ASN.1 from ISO standards on character sets.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import char
+>>> char.VisibleString("abc")
+VisibleString(b'abc')
+>>> char.IA5String('abc')
+IA5String(b'abc')
+>>> char.TeletexString('abc')
+TeletexString(b'abc')
+>>> char.VideotexString('abc')
+VideotexString(b'abc')
+>>> char.GraphicString('abc')
+GraphicString(b'abc')
+>>> char.GeneralString('abc')
+GeneralString(b'abc')
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+The last three types are relatively recent addition to the family of
+character string types: UniversalString, BMPString, UTF8String.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import char
+>>> char.UniversalString("abc")
+UniversalString(b'abc')
+>>> char.BMPString('abc')
+BMPString(b'abc')
+>>> char.UTF8String('abc')
+UTF8String(b'abc')
+>>> utf8String = char.UTF8String('У попа была собака')
+>>> utf8String
+UTF8String(b'\xd0\xa3 \xd0\xbf\xd0\xbe\xd0\xbf\xd0\xb0 \xd0\xb1\xd1\x8b\xd0\xbb\xd0\xb0 \
+\xd1\x81\xd0\xbe\xd0\xb1\xd0\xb0\xd0\xba\xd0\xb0')
+>>> print(utf8String)
+У попа была собака
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+In pyasn1, all character type objects behave like Python strings. None of
+them is currently constrained in terms of valid alphabet so it's up to
+the data source to keep an eye on data validation for these types.
+</p>
+
+<a name="1.1.10"></a>
+<h4>
+1.1.10 Useful types
+</h4>
+
+<p>
+There are three so-called useful types defined in the standard:
+ObjectDescriptor, GeneralizedTime, UTCTime. They all are subtypes
+of GraphicString or VisibleString types therefore useful types are
+character string types.
+</p>
+
+<p>
+It's advised by the ASN.1 standard to have an instance of ObjectDescriptor
+type holding a human-readable description of corresponding instance of
+OBJECT IDENTIFIER type. There are no formal linkage between these instances
+and provision for ObjectDescriptor uniqueness in the standard.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import useful
+>>> descrBER = useful.ObjectDescriptor(
+ "Basic encoding of a single ASN.1 type"
+)
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+GeneralizedTime and UTCTime types are designed to hold a human-readable
+timestamp in a universal and unambiguous form. The former provides
+more flexibility in notation while the latter is more strict but has
+Y2K issues.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+;; Mar 8 2010 12:00:00 MSK
+moscow-time GeneralizedTime ::= "20110308120000.0"
+;; Mar 8 2010 12:00:00 UTC
+utc-time GeneralizedTime ::= "201103081200Z"
+;; Mar 8 1999 12:00:00 UTC
+utc-time UTCTime ::= "9803081200Z"
+</pre>
+</td></tr></table>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import useful
+>>> moscowTime = useful.GeneralizedTime("20110308120000.0")
+>>> utcTime = useful.UTCTime("9803081200Z")
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Despite their intended use, these types possess no special, time-related,
+handling in pyasn1. They are just printable strings.
+</p>
+
+<hr>
+
+</td>
+</tr>
+</table>
+</center>
+</body>
+</html>
diff --git a/python/pyasn1/doc/tagging.html b/python/pyasn1/doc/tagging.html
new file mode 100644
index 0000000000..187f1180d2
--- /dev/null
+++ b/python/pyasn1/doc/tagging.html
@@ -0,0 +1,233 @@
+<html>
+<title>
+Tagging in PyASN1
+</title>
+<head>
+</head>
+<body>
+<center>
+<table width=60%>
+<tr>
+<td>
+<a name="1.2"></a>
+<h4>
+1.2 Tagging in PyASN1
+</h4>
+
+<p>
+In order to continue with the Constructed ASN.1 types, we will first have
+to introduce the concept of tagging (and its pyasn1 implementation), as
+some of the Constructed types rely upon the tagging feature.
+</p>
+
+<p>
+When a value is coming into an ASN.1-based system (received from a network
+or read from some storage), the receiving entity has to determine the
+type of the value to interpret and verify it accordingly.
+</p>
+
+<p>
+Historically, the first data serialization protocol introduced in
+ASN.1 was BER (Basic Encoding Rules). According to BER, any serialized
+value is packed into a triplet of (Type, Length, Value) where Type is a
+code that identifies the value (which is called <i>tag</i> in ASN.1),
+length is the number of bytes occupied by the value in its serialized form
+and value is ASN.1 value in a form suitable for serial transmission or storage.
+</p>
+
+<p>
+For that reason almost every ASN.1 type has a tag (which is actually a
+BER type) associated with it by default.
+</p>
+
+<p>
+An ASN.1 tag could be viewed as a tuple of three numbers:
+(Class, Format, Number). While Number identifies a tag, Class component
+is used to create scopes for Numbers. Four scopes are currently defined:
+UNIVERSAL, context-specific, APPLICATION and PRIVATE. The Format component
+is actually a one-bit flag - zero for tags associated with scalar types,
+and one for constructed types (will be discussed later on).
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+MyIntegerType ::= [12] INTEGER
+MyOctetString ::= [APPLICATION 0] OCTET STRING
+</pre>
+</td></tr></table>
+
+<p>
+In pyasn1, tags are implemented as immutable, tuple-like objects:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import tag
+>>> myTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10)
+>>> myTag
+Tag(tagClass=128, tagFormat=0, tagId=10)
+>>> tuple(myTag)
+(128, 0, 10)
+>>> myTag[2]
+10
+>>> myTag == tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 10)
+False
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Default tag, associated with any ASN.1 type, could be extended or replaced
+to make new type distinguishable from its ancestor. The standard provides
+two modes of tag mangling - IMPLICIT and EXPLICIT.
+</p>
+
+<p>
+EXPLICIT mode works by appending new tag to the existing ones thus creating
+an ordered set of tags. This set will be considered as a whole for type
+identification and encoding purposes. Important property of EXPLICIT tagging
+mode is that it preserves base type information in encoding what makes it
+possible to completely recover type information from encoding.
+</p>
+
+<p>
+When tagging in IMPLICIT mode, the outermost existing tag is dropped and
+replaced with a new one.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+MyIntegerType ::= [12] IMPLICIT INTEGER
+MyOctetString ::= [APPLICATION 0] EXPLICIT OCTET STRING
+</pre>
+</td></tr></table>
+
+<p>
+To model both modes of tagging, a specialized container TagSet object (holding
+zero, one or more Tag objects) is used in pyasn1.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import tag
+>>> tagSet = tag.TagSet(
+... # base tag
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10),
+... # effective tag
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10)
+... )
+>>> tagSet
+TagSet(Tag(tagClass=128, tagFormat=0, tagId=10))
+>>> tagSet.getBaseTag()
+Tag(tagClass=128, tagFormat=0, tagId=10)
+>>> tagSet = tagSet.tagExplicitly(
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 20)
+... )
+>>> tagSet
+TagSet(Tag(tagClass=128, tagFormat=0, tagId=10),
+ Tag(tagClass=128, tagFormat=32, tagId=20))
+>>> tagSet = tagSet.tagExplicitly(
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 30)
+... )
+>>> tagSet
+TagSet(Tag(tagClass=128, tagFormat=0, tagId=10),
+ Tag(tagClass=128, tagFormat=32, tagId=20),
+ Tag(tagClass=128, tagFormat=32, tagId=30))
+>>> tagSet = tagSet.tagImplicitly(
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 40)
+... )
+>>> tagSet
+TagSet(Tag(tagClass=128, tagFormat=0, tagId=10),
+ Tag(tagClass=128, tagFormat=32, tagId=20),
+ Tag(tagClass=128, tagFormat=32, tagId=40))
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+As a side note: the "base tag" concept (accessible through the getBaseTag()
+method) is specific to pyasn1 -- the base tag is used to identify the original
+ASN.1 type of an object in question. Base tag is never occurs in encoding
+and is mostly used internally by pyasn1 for choosing type-specific data
+processing algorithms. The "effective tag" is the one that always appears in
+encoding and is used on tagSets comparation.
+</p>
+
+<p>
+Any two TagSet objects could be compared to see if one is a derivative
+of the other. Figuring this out is also useful in cases when a type-specific
+data processing algorithms are to be chosen.
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import tag
+>>> tagSet1 = tag.TagSet(
+... # base tag
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10)
+... # effective tag
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10)
+... )
+>>> tagSet2 = tagSet1.tagExplicitly(
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 20)
+... )
+>>> tagSet1.isSuperTagSetOf(tagSet2)
+True
+>>> tagSet2.isSuperTagSetOf(tagSet1)
+False
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+We will complete this discussion on tagging with a real-world example. The
+following ASN.1 tagged type:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+MyIntegerType ::= [12] EXPLICIT INTEGER
+</pre>
+</td></tr></table>
+
+<p>
+could be expressed in pyasn1 like this:
+</p>
+
+<table bgcolor="lightgray" border=0 width=100%><TR><TD>
+<pre>
+>>> from pyasn1.type import univ, tag
+>>> class MyIntegerType(univ.Integer):
+... tagSet = univ.Integer.tagSet.tagExplicitly(
+... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 12)
+... )
+>>> myInteger = MyIntegerType(12345)
+>>> myInteger.getTagSet()
+TagSet(Tag(tagClass=0, tagFormat=0, tagId=2),
+ Tag(tagClass=128, tagFormat=32, tagId=12))
+>>>
+</pre>
+</td></tr></table>
+
+<p>
+Referring to the above code, the tagSet class attribute is a property of any
+pyasn1 type object that assigns default tagSet to a pyasn1 value object. This
+default tagSet specification can be ignored and effectively replaced by some
+other tagSet value passed on object instantiation.
+</p>
+
+<p>
+It's important to understand that the tag set property of pyasn1 type/value
+object can never be modifed in place. In other words, a pyasn1 type/value
+object can never change its tags. The only way is to create a new pyasn1
+type/value object and associate different tag set with it.
+</p>
+
+<hr>
+
+</td>
+</tr>
+</table>
+</center>
+</body>
+</html>
diff --git a/python/pyasn1/pyasn1/__init__.py b/python/pyasn1/pyasn1/__init__.py
new file mode 100644
index 0000000000..88aff79c84
--- /dev/null
+++ b/python/pyasn1/pyasn1/__init__.py
@@ -0,0 +1,8 @@
+import sys
+
+# http://www.python.org/dev/peps/pep-0396/
+__version__ = '0.1.7'
+
+if sys.version_info[:2] < (2, 4):
+ raise RuntimeError('PyASN1 requires Python 2.4 or later')
+
diff --git a/python/pyasn1/pyasn1/codec/__init__.py b/python/pyasn1/pyasn1/codec/__init__.py
new file mode 100644
index 0000000000..8c3066b2e6
--- /dev/null
+++ b/python/pyasn1/pyasn1/codec/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/python/pyasn1/pyasn1/codec/ber/__init__.py b/python/pyasn1/pyasn1/codec/ber/__init__.py
new file mode 100644
index 0000000000..8c3066b2e6
--- /dev/null
+++ b/python/pyasn1/pyasn1/codec/ber/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/python/pyasn1/pyasn1/codec/ber/decoder.py b/python/pyasn1/pyasn1/codec/ber/decoder.py
new file mode 100644
index 0000000000..be0cf49074
--- /dev/null
+++ b/python/pyasn1/pyasn1/codec/ber/decoder.py
@@ -0,0 +1,808 @@
+# BER decoder
+from pyasn1.type import tag, base, univ, char, useful, tagmap
+from pyasn1.codec.ber import eoo
+from pyasn1.compat.octets import oct2int, octs2ints, isOctetsType
+from pyasn1 import debug, error
+
+class AbstractDecoder:
+ protoComponent = None
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ raise error.PyAsn1Error('Decoder not implemented for %s' % (tagSet,))
+
+ def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,))
+
+class AbstractSimpleDecoder(AbstractDecoder):
+ tagFormats = (tag.tagFormatSimple,)
+ def _createComponent(self, asn1Spec, tagSet, value=None):
+ if tagSet[0][1] not in self.tagFormats:
+ raise error.PyAsn1Error('Invalid tag format %r for %r' % (tagSet[0], self.protoComponent,))
+ if asn1Spec is None:
+ return self.protoComponent.clone(value, tagSet)
+ elif value is None:
+ return asn1Spec
+ else:
+ return asn1Spec.clone(value)
+
+class AbstractConstructedDecoder(AbstractDecoder):
+ tagFormats = (tag.tagFormatConstructed,)
+ def _createComponent(self, asn1Spec, tagSet, value=None):
+ if tagSet[0][1] not in self.tagFormats:
+ raise error.PyAsn1Error('Invalid tag format %r for %r' % (tagSet[0], self.protoComponent,))
+ if asn1Spec is None:
+ return self.protoComponent.clone(tagSet)
+ else:
+ return asn1Spec.clone()
+
+class EndOfOctetsDecoder(AbstractSimpleDecoder):
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ return eoo.endOfOctets, substrate[length:]
+
+class ExplicitTagDecoder(AbstractSimpleDecoder):
+ protoComponent = univ.Any('')
+ tagFormats = (tag.tagFormatConstructed,)
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ if substrateFun:
+ return substrateFun(
+ self._createComponent(asn1Spec, tagSet, ''),
+ substrate, length
+ )
+ head, tail = substrate[:length], substrate[length:]
+ value, _ = decodeFun(head, asn1Spec, tagSet, length)
+ return value, tail
+
+ def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ if substrateFun:
+ return substrateFun(
+ self._createComponent(asn1Spec, tagSet, ''),
+ substrate, length
+ )
+ value, substrate = decodeFun(substrate, asn1Spec, tagSet, length)
+ terminator, substrate = decodeFun(substrate)
+ if eoo.endOfOctets.isSameTypeWith(terminator) and \
+ terminator == eoo.endOfOctets:
+ return value, substrate
+ else:
+ raise error.PyAsn1Error('Missing end-of-octets terminator')
+
+explicitTagDecoder = ExplicitTagDecoder()
+
+class IntegerDecoder(AbstractSimpleDecoder):
+ protoComponent = univ.Integer(0)
+ precomputedValues = {
+ '\x00': 0,
+ '\x01': 1,
+ '\x02': 2,
+ '\x03': 3,
+ '\x04': 4,
+ '\x05': 5,
+ '\x06': 6,
+ '\x07': 7,
+ '\x08': 8,
+ '\x09': 9,
+ '\xff': -1,
+ '\xfe': -2,
+ '\xfd': -3,
+ '\xfc': -4,
+ '\xfb': -5
+ }
+
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
+ state, decodeFun, substrateFun):
+ head, tail = substrate[:length], substrate[length:]
+ if not head:
+ return self._createComponent(asn1Spec, tagSet, 0), tail
+ if head in self.precomputedValues:
+ value = self.precomputedValues[head]
+ else:
+ firstOctet = oct2int(head[0])
+ if firstOctet & 0x80:
+ value = -1
+ else:
+ value = 0
+ for octet in head:
+ value = value << 8 | oct2int(octet)
+ return self._createComponent(asn1Spec, tagSet, value), tail
+
+class BooleanDecoder(IntegerDecoder):
+ protoComponent = univ.Boolean(0)
+ def _createComponent(self, asn1Spec, tagSet, value=None):
+ return IntegerDecoder._createComponent(self, asn1Spec, tagSet, value and 1 or 0)
+
+class BitStringDecoder(AbstractSimpleDecoder):
+ protoComponent = univ.BitString(())
+ tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed)
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
+ state, decodeFun, substrateFun):
+ head, tail = substrate[:length], substrate[length:]
+ if tagSet[0][1] == tag.tagFormatSimple: # XXX what tag to check?
+ if not head:
+ raise error.PyAsn1Error('Empty substrate')
+ trailingBits = oct2int(head[0])
+ if trailingBits > 7:
+ raise error.PyAsn1Error(
+ 'Trailing bits overflow %s' % trailingBits
+ )
+ head = head[1:]
+ lsb = p = 0; l = len(head)-1; b = ()
+ while p <= l:
+ if p == l:
+ lsb = trailingBits
+ j = 7
+ o = oct2int(head[p])
+ while j >= lsb:
+ b = b + ((o>>j)&0x01,)
+ j = j - 1
+ p = p + 1
+ return self._createComponent(asn1Spec, tagSet, b), tail
+ r = self._createComponent(asn1Spec, tagSet, ())
+ if substrateFun:
+ return substrateFun(r, substrate, length)
+ while head:
+ component, head = decodeFun(head)
+ r = r + component
+ return r, tail
+
+ def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ r = self._createComponent(asn1Spec, tagSet, '')
+ if substrateFun:
+ return substrateFun(r, substrate, length)
+ while substrate:
+ component, substrate = decodeFun(substrate)
+ if eoo.endOfOctets.isSameTypeWith(component) and \
+ component == eoo.endOfOctets:
+ break
+ r = r + component
+ else:
+ raise error.SubstrateUnderrunError(
+ 'No EOO seen before substrate ends'
+ )
+ return r, substrate
+
+class OctetStringDecoder(AbstractSimpleDecoder):
+ protoComponent = univ.OctetString('')
+ tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed)
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
+ state, decodeFun, substrateFun):
+ head, tail = substrate[:length], substrate[length:]
+ if tagSet[0][1] == tag.tagFormatSimple: # XXX what tag to check?
+ return self._createComponent(asn1Spec, tagSet, head), tail
+ r = self._createComponent(asn1Spec, tagSet, '')
+ if substrateFun:
+ return substrateFun(r, substrate, length)
+ while head:
+ component, head = decodeFun(head)
+ r = r + component
+ return r, tail
+
+ def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ r = self._createComponent(asn1Spec, tagSet, '')
+ if substrateFun:
+ return substrateFun(r, substrate, length)
+ while substrate:
+ component, substrate = decodeFun(substrate)
+ if eoo.endOfOctets.isSameTypeWith(component) and \
+ component == eoo.endOfOctets:
+ break
+ r = r + component
+ else:
+ raise error.SubstrateUnderrunError(
+ 'No EOO seen before substrate ends'
+ )
+ return r, substrate
+
+class NullDecoder(AbstractSimpleDecoder):
+ protoComponent = univ.Null('')
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ head, tail = substrate[:length], substrate[length:]
+ r = self._createComponent(asn1Spec, tagSet)
+ if head:
+ raise error.PyAsn1Error('Unexpected %d-octet substrate for Null' % length)
+ return r, tail
+
+class ObjectIdentifierDecoder(AbstractSimpleDecoder):
+ protoComponent = univ.ObjectIdentifier(())
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
+ state, decodeFun, substrateFun):
+ head, tail = substrate[:length], substrate[length:]
+ if not head:
+ raise error.PyAsn1Error('Empty substrate')
+
+ # Get the first subid
+ subId = oct2int(head[0])
+ oid = divmod(subId, 40)
+
+ index = 1
+ substrateLen = len(head)
+ while index < substrateLen:
+ subId = oct2int(head[index])
+ index = index + 1
+ if subId == 128:
+ # ASN.1 spec forbids leading zeros (0x80) in sub-ID OID
+ # encoding, tolerating it opens a vulnerability.
+ # See http://www.cosic.esat.kuleuven.be/publications/article-1432.pdf page 7
+ raise error.PyAsn1Error('Invalid leading 0x80 in sub-OID')
+ elif subId > 128:
+ # Construct subid from a number of octets
+ nextSubId = subId
+ subId = 0
+ while nextSubId >= 128:
+ subId = (subId << 7) + (nextSubId & 0x7F)
+ if index >= substrateLen:
+ raise error.SubstrateUnderrunError(
+ 'Short substrate for sub-OID past %s' % (oid,)
+ )
+ nextSubId = oct2int(head[index])
+ index = index + 1
+ subId = (subId << 7) + nextSubId
+ oid = oid + (subId,)
+ return self._createComponent(asn1Spec, tagSet, oid), tail
+
+class RealDecoder(AbstractSimpleDecoder):
+ protoComponent = univ.Real()
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ head, tail = substrate[:length], substrate[length:]
+ if not head:
+ return self._createComponent(asn1Spec, tagSet, 0.0), tail
+ fo = oct2int(head[0]); head = head[1:]
+ if fo & 0x80: # binary enoding
+ n = (fo & 0x03) + 1
+ if n == 4:
+ n = oct2int(head[0])
+ eo, head = head[:n], head[n:]
+ if not eo or not head:
+ raise error.PyAsn1Error('Real exponent screwed')
+ e = oct2int(eo[0]) & 0x80 and -1 or 0
+ while eo: # exponent
+ e <<= 8
+ e |= oct2int(eo[0])
+ eo = eo[1:]
+ p = 0
+ while head: # value
+ p <<= 8
+ p |= oct2int(head[0])
+ head = head[1:]
+ if fo & 0x40: # sign bit
+ p = -p
+ value = (p, 2, e)
+ elif fo & 0x40: # infinite value
+ value = fo & 0x01 and '-inf' or 'inf'
+ elif fo & 0xc0 == 0: # character encoding
+ try:
+ if fo & 0x3 == 0x1: # NR1
+ value = (int(head), 10, 0)
+ elif fo & 0x3 == 0x2: # NR2
+ value = float(head)
+ elif fo & 0x3 == 0x3: # NR3
+ value = float(head)
+ else:
+ raise error.SubstrateUnderrunError(
+ 'Unknown NR (tag %s)' % fo
+ )
+ except ValueError:
+ raise error.SubstrateUnderrunError(
+ 'Bad character Real syntax'
+ )
+ else:
+ raise error.SubstrateUnderrunError(
+ 'Unknown encoding (tag %s)' % fo
+ )
+ return self._createComponent(asn1Spec, tagSet, value), tail
+
+class SequenceDecoder(AbstractConstructedDecoder):
+ protoComponent = univ.Sequence()
+ def _getComponentTagMap(self, r, idx):
+ try:
+ return r.getComponentTagMapNearPosition(idx)
+ except error.PyAsn1Error:
+ return
+
+ def _getComponentPositionByType(self, r, t, idx):
+ return r.getComponentPositionNearType(t, idx)
+
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ head, tail = substrate[:length], substrate[length:]
+ r = self._createComponent(asn1Spec, tagSet)
+ idx = 0
+ if substrateFun:
+ return substrateFun(r, substrate, length)
+ while head:
+ asn1Spec = self._getComponentTagMap(r, idx)
+ component, head = decodeFun(head, asn1Spec)
+ idx = self._getComponentPositionByType(
+ r, component.getEffectiveTagSet(), idx
+ )
+ r.setComponentByPosition(idx, component, asn1Spec is None)
+ idx = idx + 1
+ r.setDefaultComponents()
+ r.verifySizeSpec()
+ return r, tail
+
+ def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ r = self._createComponent(asn1Spec, tagSet)
+ if substrateFun:
+ return substrateFun(r, substrate, length)
+ idx = 0
+ while substrate:
+ asn1Spec = self._getComponentTagMap(r, idx)
+ component, substrate = decodeFun(substrate, asn1Spec)
+ if eoo.endOfOctets.isSameTypeWith(component) and \
+ component == eoo.endOfOctets:
+ break
+ idx = self._getComponentPositionByType(
+ r, component.getEffectiveTagSet(), idx
+ )
+ r.setComponentByPosition(idx, component, asn1Spec is None)
+ idx = idx + 1
+ else:
+ raise error.SubstrateUnderrunError(
+ 'No EOO seen before substrate ends'
+ )
+ r.setDefaultComponents()
+ r.verifySizeSpec()
+ return r, substrate
+
+class SequenceOfDecoder(AbstractConstructedDecoder):
+ protoComponent = univ.SequenceOf()
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ head, tail = substrate[:length], substrate[length:]
+ r = self._createComponent(asn1Spec, tagSet)
+ if substrateFun:
+ return substrateFun(r, substrate, length)
+ asn1Spec = r.getComponentType()
+ idx = 0
+ while head:
+ component, head = decodeFun(head, asn1Spec)
+ r.setComponentByPosition(idx, component, asn1Spec is None)
+ idx = idx + 1
+ r.verifySizeSpec()
+ return r, tail
+
+ def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ r = self._createComponent(asn1Spec, tagSet)
+ if substrateFun:
+ return substrateFun(r, substrate, length)
+ asn1Spec = r.getComponentType()
+ idx = 0
+ while substrate:
+ component, substrate = decodeFun(substrate, asn1Spec)
+ if eoo.endOfOctets.isSameTypeWith(component) and \
+ component == eoo.endOfOctets:
+ break
+ r.setComponentByPosition(idx, component, asn1Spec is None)
+ idx = idx + 1
+ else:
+ raise error.SubstrateUnderrunError(
+ 'No EOO seen before substrate ends'
+ )
+ r.verifySizeSpec()
+ return r, substrate
+
+class SetDecoder(SequenceDecoder):
+ protoComponent = univ.Set()
+ def _getComponentTagMap(self, r, idx):
+ return r.getComponentTagMap()
+
+ def _getComponentPositionByType(self, r, t, idx):
+ nextIdx = r.getComponentPositionByType(t)
+ if nextIdx is None:
+ return idx
+ else:
+ return nextIdx
+
+class SetOfDecoder(SequenceOfDecoder):
+ protoComponent = univ.SetOf()
+
+class ChoiceDecoder(AbstractConstructedDecoder):
+ protoComponent = univ.Choice()
+ tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed)
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ head, tail = substrate[:length], substrate[length:]
+ r = self._createComponent(asn1Spec, tagSet)
+ if substrateFun:
+ return substrateFun(r, substrate, length)
+ if r.getTagSet() == tagSet: # explicitly tagged Choice
+ component, head = decodeFun(
+ head, r.getComponentTagMap()
+ )
+ else:
+ component, head = decodeFun(
+ head, r.getComponentTagMap(), tagSet, length, state
+ )
+ if isinstance(component, univ.Choice):
+ effectiveTagSet = component.getEffectiveTagSet()
+ else:
+ effectiveTagSet = component.getTagSet()
+ r.setComponentByType(effectiveTagSet, component, 0, asn1Spec is None)
+ return r, tail
+
+ def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ r = self._createComponent(asn1Spec, tagSet)
+ if substrateFun:
+ return substrateFun(r, substrate, length)
+ if r.getTagSet() == tagSet: # explicitly tagged Choice
+ component, substrate = decodeFun(substrate, r.getComponentTagMap())
+ eooMarker, substrate = decodeFun(substrate) # eat up EOO marker
+ if not eoo.endOfOctets.isSameTypeWith(eooMarker) or \
+ eooMarker != eoo.endOfOctets:
+ raise error.PyAsn1Error('No EOO seen before substrate ends')
+ else:
+ component, substrate= decodeFun(
+ substrate, r.getComponentTagMap(), tagSet, length, state
+ )
+ if isinstance(component, univ.Choice):
+ effectiveTagSet = component.getEffectiveTagSet()
+ else:
+ effectiveTagSet = component.getTagSet()
+ r.setComponentByType(effectiveTagSet, component, 0, asn1Spec is None)
+ return r, substrate
+
+class AnyDecoder(AbstractSimpleDecoder):
+ protoComponent = univ.Any()
+ tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed)
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ if asn1Spec is None or \
+ asn1Spec is not None and tagSet != asn1Spec.getTagSet():
+ # untagged Any container, recover inner header substrate
+ length = length + len(fullSubstrate) - len(substrate)
+ substrate = fullSubstrate
+ if substrateFun:
+ return substrateFun(self._createComponent(asn1Spec, tagSet),
+ substrate, length)
+ head, tail = substrate[:length], substrate[length:]
+ return self._createComponent(asn1Spec, tagSet, value=head), tail
+
+ def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet,
+ length, state, decodeFun, substrateFun):
+ if asn1Spec is not None and tagSet == asn1Spec.getTagSet():
+ # tagged Any type -- consume header substrate
+ header = ''
+ else:
+ # untagged Any, recover header substrate
+ header = fullSubstrate[:-len(substrate)]
+
+ r = self._createComponent(asn1Spec, tagSet, header)
+
+ # Any components do not inherit initial tag
+ asn1Spec = self.protoComponent
+
+ if substrateFun:
+ return substrateFun(r, substrate, length)
+ while substrate:
+ component, substrate = decodeFun(substrate, asn1Spec)
+ if eoo.endOfOctets.isSameTypeWith(component) and \
+ component == eoo.endOfOctets:
+ break
+ r = r + component
+ else:
+ raise error.SubstrateUnderrunError(
+ 'No EOO seen before substrate ends'
+ )
+ return r, substrate
+
+# character string types
+class UTF8StringDecoder(OctetStringDecoder):
+ protoComponent = char.UTF8String()
+class NumericStringDecoder(OctetStringDecoder):
+ protoComponent = char.NumericString()
+class PrintableStringDecoder(OctetStringDecoder):
+ protoComponent = char.PrintableString()
+class TeletexStringDecoder(OctetStringDecoder):
+ protoComponent = char.TeletexString()
+class VideotexStringDecoder(OctetStringDecoder):
+ protoComponent = char.VideotexString()
+class IA5StringDecoder(OctetStringDecoder):
+ protoComponent = char.IA5String()
+class GraphicStringDecoder(OctetStringDecoder):
+ protoComponent = char.GraphicString()
+class VisibleStringDecoder(OctetStringDecoder):
+ protoComponent = char.VisibleString()
+class GeneralStringDecoder(OctetStringDecoder):
+ protoComponent = char.GeneralString()
+class UniversalStringDecoder(OctetStringDecoder):
+ protoComponent = char.UniversalString()
+class BMPStringDecoder(OctetStringDecoder):
+ protoComponent = char.BMPString()
+
+# "useful" types
+class GeneralizedTimeDecoder(OctetStringDecoder):
+ protoComponent = useful.GeneralizedTime()
+class UTCTimeDecoder(OctetStringDecoder):
+ protoComponent = useful.UTCTime()
+
+tagMap = {
+ eoo.endOfOctets.tagSet: EndOfOctetsDecoder(),
+ univ.Integer.tagSet: IntegerDecoder(),
+ univ.Boolean.tagSet: BooleanDecoder(),
+ univ.BitString.tagSet: BitStringDecoder(),
+ univ.OctetString.tagSet: OctetStringDecoder(),
+ univ.Null.tagSet: NullDecoder(),
+ univ.ObjectIdentifier.tagSet: ObjectIdentifierDecoder(),
+ univ.Enumerated.tagSet: IntegerDecoder(),
+ univ.Real.tagSet: RealDecoder(),
+ univ.Sequence.tagSet: SequenceDecoder(), # conflicts with SequenceOf
+ univ.Set.tagSet: SetDecoder(), # conflicts with SetOf
+ univ.Choice.tagSet: ChoiceDecoder(), # conflicts with Any
+ # character string types
+ char.UTF8String.tagSet: UTF8StringDecoder(),
+ char.NumericString.tagSet: NumericStringDecoder(),
+ char.PrintableString.tagSet: PrintableStringDecoder(),
+ char.TeletexString.tagSet: TeletexStringDecoder(),
+ char.VideotexString.tagSet: VideotexStringDecoder(),
+ char.IA5String.tagSet: IA5StringDecoder(),
+ char.GraphicString.tagSet: GraphicStringDecoder(),
+ char.VisibleString.tagSet: VisibleStringDecoder(),
+ char.GeneralString.tagSet: GeneralStringDecoder(),
+ char.UniversalString.tagSet: UniversalStringDecoder(),
+ char.BMPString.tagSet: BMPStringDecoder(),
+ # useful types
+ useful.GeneralizedTime.tagSet: GeneralizedTimeDecoder(),
+ useful.UTCTime.tagSet: UTCTimeDecoder()
+ }
+
+# Type-to-codec map for ambiguous ASN.1 types
+typeMap = {
+ univ.Set.typeId: SetDecoder(),
+ univ.SetOf.typeId: SetOfDecoder(),
+ univ.Sequence.typeId: SequenceDecoder(),
+ univ.SequenceOf.typeId: SequenceOfDecoder(),
+ univ.Choice.typeId: ChoiceDecoder(),
+ univ.Any.typeId: AnyDecoder()
+ }
+
+( stDecodeTag, stDecodeLength, stGetValueDecoder, stGetValueDecoderByAsn1Spec,
+ stGetValueDecoderByTag, stTryAsExplicitTag, stDecodeValue,
+ stDumpRawValue, stErrorCondition, stStop ) = [x for x in range(10)]
+
+class Decoder:
+ defaultErrorState = stErrorCondition
+# defaultErrorState = stDumpRawValue
+ defaultRawDecoder = AnyDecoder()
+ def __init__(self, tagMap, typeMap={}):
+ self.__tagMap = tagMap
+ self.__typeMap = typeMap
+ self.__endOfOctetsTagSet = eoo.endOfOctets.getTagSet()
+ # Tag & TagSet objects caches
+ self.__tagCache = {}
+ self.__tagSetCache = {}
+
+ def __call__(self, substrate, asn1Spec=None, tagSet=None,
+ length=None, state=stDecodeTag, recursiveFlag=1,
+ substrateFun=None):
+ if debug.logger & debug.flagDecoder:
+ debug.logger('decoder called at scope %s with state %d, working with up to %d octets of substrate: %s' % (debug.scope, state, len(substrate), debug.hexdump(substrate)))
+ fullSubstrate = substrate
+ while state != stStop:
+ if state == stDecodeTag:
+ # Decode tag
+ if not substrate:
+ raise error.SubstrateUnderrunError(
+ 'Short octet stream on tag decoding'
+ )
+ if not isOctetsType(substrate) and \
+ not isinstance(substrate, univ.OctetString):
+ raise error.PyAsn1Error('Bad octet stream type')
+
+ firstOctet = substrate[0]
+ substrate = substrate[1:]
+ if firstOctet in self.__tagCache:
+ lastTag = self.__tagCache[firstOctet]
+ else:
+ t = oct2int(firstOctet)
+ tagClass = t&0xC0
+ tagFormat = t&0x20
+ tagId = t&0x1F
+ if tagId == 0x1F:
+ tagId = 0
+ while 1:
+ if not substrate:
+ raise error.SubstrateUnderrunError(
+ 'Short octet stream on long tag decoding'
+ )
+ t = oct2int(substrate[0])
+ tagId = tagId << 7 | (t&0x7F)
+ substrate = substrate[1:]
+ if not t&0x80:
+ break
+ lastTag = tag.Tag(
+ tagClass=tagClass, tagFormat=tagFormat, tagId=tagId
+ )
+ if tagId < 31:
+ # cache short tags
+ self.__tagCache[firstOctet] = lastTag
+ if tagSet is None:
+ if firstOctet in self.__tagSetCache:
+ tagSet = self.__tagSetCache[firstOctet]
+ else:
+ # base tag not recovered
+ tagSet = tag.TagSet((), lastTag)
+ if firstOctet in self.__tagCache:
+ self.__tagSetCache[firstOctet] = tagSet
+ else:
+ tagSet = lastTag + tagSet
+ state = stDecodeLength
+ debug.logger and debug.logger & debug.flagDecoder and debug.logger('tag decoded into %r, decoding length' % tagSet)
+ if state == stDecodeLength:
+ # Decode length
+ if not substrate:
+ raise error.SubstrateUnderrunError(
+ 'Short octet stream on length decoding'
+ )
+ firstOctet = oct2int(substrate[0])
+ if firstOctet == 128:
+ size = 1
+ length = -1
+ elif firstOctet < 128:
+ length, size = firstOctet, 1
+ else:
+ size = firstOctet & 0x7F
+ # encoded in size bytes
+ length = 0
+ lengthString = substrate[1:size+1]
+ # missing check on maximum size, which shouldn't be a
+ # problem, we can handle more than is possible
+ if len(lengthString) != size:
+ raise error.SubstrateUnderrunError(
+ '%s<%s at %s' %
+ (size, len(lengthString), tagSet)
+ )
+ for char in lengthString:
+ length = (length << 8) | oct2int(char)
+ size = size + 1
+ substrate = substrate[size:]
+ if length != -1 and len(substrate) < length:
+ raise error.SubstrateUnderrunError(
+ '%d-octet short' % (length - len(substrate))
+ )
+ state = stGetValueDecoder
+ debug.logger and debug.logger & debug.flagDecoder and debug.logger('value length decoded into %d, payload substrate is: %s' % (length, debug.hexdump(length == -1 and substrate or substrate[:length])))
+ if state == stGetValueDecoder:
+ if asn1Spec is None:
+ state = stGetValueDecoderByTag
+ else:
+ state = stGetValueDecoderByAsn1Spec
+ #
+ # There're two ways of creating subtypes in ASN.1 what influences
+ # decoder operation. These methods are:
+ # 1) Either base types used in or no IMPLICIT tagging has been
+ # applied on subtyping.
+ # 2) Subtype syntax drops base type information (by means of
+ # IMPLICIT tagging.
+ # The first case allows for complete tag recovery from substrate
+ # while the second one requires original ASN.1 type spec for
+ # decoding.
+ #
+ # In either case a set of tags (tagSet) is coming from substrate
+ # in an incremental, tag-by-tag fashion (this is the case of
+ # EXPLICIT tag which is most basic). Outermost tag comes first
+ # from the wire.
+ #
+ if state == stGetValueDecoderByTag:
+ if tagSet in self.__tagMap:
+ concreteDecoder = self.__tagMap[tagSet]
+ else:
+ concreteDecoder = None
+ if concreteDecoder:
+ state = stDecodeValue
+ else:
+ _k = tagSet[:1]
+ if _k in self.__tagMap:
+ concreteDecoder = self.__tagMap[_k]
+ else:
+ concreteDecoder = None
+ if concreteDecoder:
+ state = stDecodeValue
+ else:
+ state = stTryAsExplicitTag
+ if debug.logger and debug.logger & debug.flagDecoder:
+ debug.logger('codec %s chosen by a built-in type, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state == stDecodeValue and 'value' or 'as explicit tag'))
+ debug.scope.push(concreteDecoder is None and '?' or concreteDecoder.protoComponent.__class__.__name__)
+ if state == stGetValueDecoderByAsn1Spec:
+ if isinstance(asn1Spec, (dict, tagmap.TagMap)):
+ if tagSet in asn1Spec:
+ __chosenSpec = asn1Spec[tagSet]
+ else:
+ __chosenSpec = None
+ if debug.logger and debug.logger & debug.flagDecoder:
+ debug.logger('candidate ASN.1 spec is a map of:')
+ for t, v in asn1Spec.getPosMap().items():
+ debug.logger(' %r -> %s' % (t, v.__class__.__name__))
+ if asn1Spec.getNegMap():
+ debug.logger('but neither of: ')
+ for i in asn1Spec.getNegMap().items():
+ debug.logger(' %r -> %s' % (t, v.__class__.__name__))
+ debug.logger('new candidate ASN.1 spec is %s, chosen by %r' % (__chosenSpec is None and '<none>' or __chosenSpec.__class__.__name__, tagSet))
+ else:
+ __chosenSpec = asn1Spec
+ debug.logger and debug.logger & debug.flagDecoder and debug.logger('candidate ASN.1 spec is %s' % asn1Spec.__class__.__name__)
+ if __chosenSpec is not None and (
+ tagSet == __chosenSpec.getTagSet() or \
+ tagSet in __chosenSpec.getTagMap()
+ ):
+ # use base type for codec lookup to recover untagged types
+ baseTagSet = __chosenSpec.baseTagSet
+ if __chosenSpec.typeId is not None and \
+ __chosenSpec.typeId in self.__typeMap:
+ # ambiguous type
+ concreteDecoder = self.__typeMap[__chosenSpec.typeId]
+ debug.logger and debug.logger & debug.flagDecoder and debug.logger('value decoder chosen for an ambiguous type by type ID %s' % (__chosenSpec.typeId,))
+ elif baseTagSet in self.__tagMap:
+ # base type or tagged subtype
+ concreteDecoder = self.__tagMap[baseTagSet]
+ debug.logger and debug.logger & debug.flagDecoder and debug.logger('value decoder chosen by base %r' % (baseTagSet,))
+ else:
+ concreteDecoder = None
+ if concreteDecoder:
+ asn1Spec = __chosenSpec
+ state = stDecodeValue
+ else:
+ state = stTryAsExplicitTag
+ elif tagSet == self.__endOfOctetsTagSet:
+ concreteDecoder = self.__tagMap[tagSet]
+ state = stDecodeValue
+ debug.logger and debug.logger & debug.flagDecoder and debug.logger('end-of-octets found')
+ else:
+ concreteDecoder = None
+ state = stTryAsExplicitTag
+ if debug.logger and debug.logger & debug.flagDecoder:
+ debug.logger('codec %s chosen by ASN.1 spec, decoding %s' % (state == stDecodeValue and concreteDecoder.__class__.__name__ or "<none>", state == stDecodeValue and 'value' or 'as explicit tag'))
+ debug.scope.push(__chosenSpec is None and '?' or __chosenSpec.__class__.__name__)
+ if state == stTryAsExplicitTag:
+ if tagSet and \
+ tagSet[0][1] == tag.tagFormatConstructed and \
+ tagSet[0][0] != tag.tagClassUniversal:
+ # Assume explicit tagging
+ concreteDecoder = explicitTagDecoder
+ state = stDecodeValue
+ else:
+ concreteDecoder = None
+ state = self.defaultErrorState
+ debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s chosen, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state == stDecodeValue and 'value' or 'as failure'))
+ if state == stDumpRawValue:
+ concreteDecoder = self.defaultRawDecoder
+ debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s chosen, decoding value' % concreteDecoder.__class__.__name__)
+ state = stDecodeValue
+ if state == stDecodeValue:
+ if recursiveFlag == 0 and not substrateFun: # legacy
+ substrateFun = lambda a,b,c: (a,b[:c])
+ if length == -1: # indef length
+ value, substrate = concreteDecoder.indefLenValueDecoder(
+ fullSubstrate, substrate, asn1Spec, tagSet, length,
+ stGetValueDecoder, self, substrateFun
+ )
+ else:
+ value, substrate = concreteDecoder.valueDecoder(
+ fullSubstrate, substrate, asn1Spec, tagSet, length,
+ stGetValueDecoder, self, substrateFun
+ )
+ state = stStop
+ debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s yields type %s, value:\n%s\n...remaining substrate is: %s' % (concreteDecoder.__class__.__name__, value.__class__.__name__, value.prettyPrint(), substrate and debug.hexdump(substrate) or '<none>'))
+ if state == stErrorCondition:
+ raise error.PyAsn1Error(
+ '%r not in asn1Spec: %r' % (tagSet, asn1Spec)
+ )
+ if debug.logger and debug.logger & debug.flagDecoder:
+ debug.scope.pop()
+ debug.logger('decoder left scope %s, call completed' % debug.scope)
+ return value, substrate
+
+decode = Decoder(tagMap, typeMap)
+
+# XXX
+# non-recursive decoding; return position rather than substrate
diff --git a/python/pyasn1/pyasn1/codec/ber/encoder.py b/python/pyasn1/pyasn1/codec/ber/encoder.py
new file mode 100644
index 0000000000..173949d0b6
--- /dev/null
+++ b/python/pyasn1/pyasn1/codec/ber/encoder.py
@@ -0,0 +1,353 @@
+# BER encoder
+from pyasn1.type import base, tag, univ, char, useful
+from pyasn1.codec.ber import eoo
+from pyasn1.compat.octets import int2oct, oct2int, ints2octs, null, str2octs
+from pyasn1 import debug, error
+
+class Error(Exception): pass
+
+class AbstractItemEncoder:
+ supportIndefLenMode = 1
+ def encodeTag(self, t, isConstructed):
+ tagClass, tagFormat, tagId = t.asTuple() # this is a hotspot
+ v = tagClass | tagFormat
+ if isConstructed:
+ v = v|tag.tagFormatConstructed
+ if tagId < 31:
+ return int2oct(v|tagId)
+ else:
+ s = int2oct(tagId&0x7f)
+ tagId = tagId >> 7
+ while tagId:
+ s = int2oct(0x80|(tagId&0x7f)) + s
+ tagId = tagId >> 7
+ return int2oct(v|0x1F) + s
+
+ def encodeLength(self, length, defMode):
+ if not defMode and self.supportIndefLenMode:
+ return int2oct(0x80)
+ if length < 0x80:
+ return int2oct(length)
+ else:
+ substrate = null
+ while length:
+ substrate = int2oct(length&0xff) + substrate
+ length = length >> 8
+ substrateLen = len(substrate)
+ if substrateLen > 126:
+ raise Error('Length octets overflow (%d)' % substrateLen)
+ return int2oct(0x80 | substrateLen) + substrate
+
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ raise Error('Not implemented')
+
+ def _encodeEndOfOctets(self, encodeFun, defMode):
+ if defMode or not self.supportIndefLenMode:
+ return null
+ else:
+ return encodeFun(eoo.endOfOctets, defMode)
+
+ def encode(self, encodeFun, value, defMode, maxChunkSize):
+ substrate, isConstructed = self.encodeValue(
+ encodeFun, value, defMode, maxChunkSize
+ )
+ tagSet = value.getTagSet()
+ if tagSet:
+ if not isConstructed: # primitive form implies definite mode
+ defMode = 1
+ return self.encodeTag(
+ tagSet[-1], isConstructed
+ ) + self.encodeLength(
+ len(substrate), defMode
+ ) + substrate + self._encodeEndOfOctets(encodeFun, defMode)
+ else:
+ return substrate # untagged value
+
+class EndOfOctetsEncoder(AbstractItemEncoder):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ return null, 0
+
+class ExplicitlyTaggedItemEncoder(AbstractItemEncoder):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ if isinstance(value, base.AbstractConstructedAsn1Item):
+ value = value.clone(tagSet=value.getTagSet()[:-1],
+ cloneValueFlag=1)
+ else:
+ value = value.clone(tagSet=value.getTagSet()[:-1])
+ return encodeFun(value, defMode, maxChunkSize), 1
+
+explicitlyTaggedItemEncoder = ExplicitlyTaggedItemEncoder()
+
+class BooleanEncoder(AbstractItemEncoder):
+ supportIndefLenMode = 0
+ _true = ints2octs((1,))
+ _false = ints2octs((0,))
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ return value and self._true or self._false, 0
+
+class IntegerEncoder(AbstractItemEncoder):
+ supportIndefLenMode = 0
+ supportCompactZero = False
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ if value == 0: # shortcut for zero value
+ if self.supportCompactZero:
+ # this seems to be a correct way for encoding zeros
+ return null, 0
+ else:
+ # this seems to be a widespread way for encoding zeros
+ return ints2octs((0,)), 0
+ octets = []
+ value = int(value) # to save on ops on asn1 type
+ while 1:
+ octets.insert(0, value & 0xff)
+ if value == 0 or value == -1:
+ break
+ value = value >> 8
+ if value == 0 and octets[0] & 0x80:
+ octets.insert(0, 0)
+ while len(octets) > 1 and \
+ (octets[0] == 0 and octets[1] & 0x80 == 0 or \
+ octets[0] == 0xff and octets[1] & 0x80 != 0):
+ del octets[0]
+ return ints2octs(octets), 0
+
+class BitStringEncoder(AbstractItemEncoder):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ if not maxChunkSize or len(value) <= maxChunkSize*8:
+ r = {}; l = len(value); p = 0; j = 7
+ while p < l:
+ i, j = divmod(p, 8)
+ r[i] = r.get(i,0) | value[p]<<(7-j)
+ p = p + 1
+ keys = list(r); keys.sort()
+ return int2oct(7-j) + ints2octs([r[k] for k in keys]), 0
+ else:
+ pos = 0; substrate = null
+ while 1:
+ # count in octets
+ v = value.clone(value[pos*8:pos*8+maxChunkSize*8])
+ if not v:
+ break
+ substrate = substrate + encodeFun(v, defMode, maxChunkSize)
+ pos = pos + maxChunkSize
+ return substrate, 1
+
+class OctetStringEncoder(AbstractItemEncoder):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ if not maxChunkSize or len(value) <= maxChunkSize:
+ return value.asOctets(), 0
+ else:
+ pos = 0; substrate = null
+ while 1:
+ v = value.clone(value[pos:pos+maxChunkSize])
+ if not v:
+ break
+ substrate = substrate + encodeFun(v, defMode, maxChunkSize)
+ pos = pos + maxChunkSize
+ return substrate, 1
+
+class NullEncoder(AbstractItemEncoder):
+ supportIndefLenMode = 0
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ return null, 0
+
+class ObjectIdentifierEncoder(AbstractItemEncoder):
+ supportIndefLenMode = 0
+ precomputedValues = {
+ (1, 3, 6, 1, 2): (43, 6, 1, 2),
+ (1, 3, 6, 1, 4): (43, 6, 1, 4)
+ }
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ oid = value.asTuple()
+ if oid[:5] in self.precomputedValues:
+ octets = self.precomputedValues[oid[:5]]
+ index = 5
+ else:
+ if len(oid) < 2:
+ raise error.PyAsn1Error('Short OID %s' % (value,))
+
+ # Build the first twos
+ if oid[0] > 6 or oid[1] > 39 or oid[0] == 6 and oid[1] > 15:
+ raise error.PyAsn1Error(
+ 'Initial sub-ID overflow %s in OID %s' % (oid[:2], value)
+ )
+ octets = (oid[0] * 40 + oid[1],)
+ index = 2
+
+ # Cycle through subids
+ for subid in oid[index:]:
+ if subid > -1 and subid < 128:
+ # Optimize for the common case
+ octets = octets + (subid & 0x7f,)
+ elif subid < 0 or subid > 0xFFFFFFFF:
+ raise error.PyAsn1Error(
+ 'SubId overflow %s in %s' % (subid, value)
+ )
+ else:
+ # Pack large Sub-Object IDs
+ res = (subid & 0x7f,)
+ subid = subid >> 7
+ while subid > 0:
+ res = (0x80 | (subid & 0x7f),) + res
+ subid = subid >> 7
+ # Add packed Sub-Object ID to resulted Object ID
+ octets += res
+
+ return ints2octs(octets), 0
+
+class RealEncoder(AbstractItemEncoder):
+ supportIndefLenMode = 0
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ if value.isPlusInfinity():
+ return int2oct(0x40), 0
+ if value.isMinusInfinity():
+ return int2oct(0x41), 0
+ m, b, e = value
+ if not m:
+ return null, 0
+ if b == 10:
+ return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), 0
+ elif b == 2:
+ fo = 0x80 # binary enoding
+ if m < 0:
+ fo = fo | 0x40 # sign bit
+ m = -m
+ while int(m) != m: # drop floating point
+ m *= 2
+ e -= 1
+ while m & 0x1 == 0: # mantissa normalization
+ m >>= 1
+ e += 1
+ eo = null
+ while e not in (0, -1):
+ eo = int2oct(e&0xff) + eo
+ e >>= 8
+ if e == 0 and eo and oct2int(eo[0]) & 0x80:
+ eo = int2oct(0) + eo
+ n = len(eo)
+ if n > 0xff:
+ raise error.PyAsn1Error('Real exponent overflow')
+ if n == 1:
+ pass
+ elif n == 2:
+ fo |= 1
+ elif n == 3:
+ fo |= 2
+ else:
+ fo |= 3
+ eo = int2oct(n//0xff+1) + eo
+ po = null
+ while m:
+ po = int2oct(m&0xff) + po
+ m >>= 8
+ substrate = int2oct(fo) + eo + po
+ return substrate, 0
+ else:
+ raise error.PyAsn1Error('Prohibited Real base %s' % b)
+
+class SequenceEncoder(AbstractItemEncoder):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ value.setDefaultComponents()
+ value.verifySizeSpec()
+ substrate = null; idx = len(value)
+ while idx > 0:
+ idx = idx - 1
+ if value[idx] is None: # Optional component
+ continue
+ component = value.getDefaultComponentByPosition(idx)
+ if component is not None and component == value[idx]:
+ continue
+ substrate = encodeFun(
+ value[idx], defMode, maxChunkSize
+ ) + substrate
+ return substrate, 1
+
+class SequenceOfEncoder(AbstractItemEncoder):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ value.verifySizeSpec()
+ substrate = null; idx = len(value)
+ while idx > 0:
+ idx = idx - 1
+ substrate = encodeFun(
+ value[idx], defMode, maxChunkSize
+ ) + substrate
+ return substrate, 1
+
+class ChoiceEncoder(AbstractItemEncoder):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ return encodeFun(value.getComponent(), defMode, maxChunkSize), 1
+
+class AnyEncoder(OctetStringEncoder):
+ def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
+ return value.asOctets(), defMode == 0
+
+tagMap = {
+ eoo.endOfOctets.tagSet: EndOfOctetsEncoder(),
+ univ.Boolean.tagSet: BooleanEncoder(),
+ univ.Integer.tagSet: IntegerEncoder(),
+ univ.BitString.tagSet: BitStringEncoder(),
+ univ.OctetString.tagSet: OctetStringEncoder(),
+ univ.Null.tagSet: NullEncoder(),
+ univ.ObjectIdentifier.tagSet: ObjectIdentifierEncoder(),
+ univ.Enumerated.tagSet: IntegerEncoder(),
+ univ.Real.tagSet: RealEncoder(),
+ # Sequence & Set have same tags as SequenceOf & SetOf
+ univ.SequenceOf.tagSet: SequenceOfEncoder(),
+ univ.SetOf.tagSet: SequenceOfEncoder(),
+ univ.Choice.tagSet: ChoiceEncoder(),
+ # character string types
+ char.UTF8String.tagSet: OctetStringEncoder(),
+ char.NumericString.tagSet: OctetStringEncoder(),
+ char.PrintableString.tagSet: OctetStringEncoder(),
+ char.TeletexString.tagSet: OctetStringEncoder(),
+ char.VideotexString.tagSet: OctetStringEncoder(),
+ char.IA5String.tagSet: OctetStringEncoder(),
+ char.GraphicString.tagSet: OctetStringEncoder(),
+ char.VisibleString.tagSet: OctetStringEncoder(),
+ char.GeneralString.tagSet: OctetStringEncoder(),
+ char.UniversalString.tagSet: OctetStringEncoder(),
+ char.BMPString.tagSet: OctetStringEncoder(),
+ # useful types
+ useful.GeneralizedTime.tagSet: OctetStringEncoder(),
+ useful.UTCTime.tagSet: OctetStringEncoder()
+ }
+
+# Type-to-codec map for ambiguous ASN.1 types
+typeMap = {
+ univ.Set.typeId: SequenceEncoder(),
+ univ.SetOf.typeId: SequenceOfEncoder(),
+ univ.Sequence.typeId: SequenceEncoder(),
+ univ.SequenceOf.typeId: SequenceOfEncoder(),
+ univ.Choice.typeId: ChoiceEncoder(),
+ univ.Any.typeId: AnyEncoder()
+ }
+
+class Encoder:
+ def __init__(self, tagMap, typeMap={}):
+ self.__tagMap = tagMap
+ self.__typeMap = typeMap
+
+ def __call__(self, value, defMode=1, maxChunkSize=0):
+ debug.logger & debug.flagEncoder and debug.logger('encoder called in %sdef mode, chunk size %s for type %s, value:\n%s' % (not defMode and 'in' or '', maxChunkSize, value.__class__.__name__, value.prettyPrint()))
+ tagSet = value.getTagSet()
+ if len(tagSet) > 1:
+ concreteEncoder = explicitlyTaggedItemEncoder
+ else:
+ if value.typeId is not None and value.typeId in self.__typeMap:
+ concreteEncoder = self.__typeMap[value.typeId]
+ elif tagSet in self.__tagMap:
+ concreteEncoder = self.__tagMap[tagSet]
+ else:
+ tagSet = value.baseTagSet
+ if tagSet in self.__tagMap:
+ concreteEncoder = self.__tagMap[tagSet]
+ else:
+ raise Error('No encoder for %s' % (value,))
+ debug.logger & debug.flagEncoder and debug.logger('using value codec %s chosen by %r' % (concreteEncoder.__class__.__name__, tagSet))
+ substrate = concreteEncoder.encode(
+ self, value, defMode, maxChunkSize
+ )
+ debug.logger & debug.flagEncoder and debug.logger('built %s octets of substrate: %s\nencoder completed' % (len(substrate), debug.hexdump(substrate)))
+ return substrate
+
+encode = Encoder(tagMap, typeMap)
diff --git a/python/pyasn1/pyasn1/codec/ber/eoo.py b/python/pyasn1/pyasn1/codec/ber/eoo.py
new file mode 100644
index 0000000000..379be19965
--- /dev/null
+++ b/python/pyasn1/pyasn1/codec/ber/eoo.py
@@ -0,0 +1,8 @@
+from pyasn1.type import base, tag
+
+class EndOfOctets(base.AbstractSimpleAsn1Item):
+ defaultValue = 0
+ tagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00)
+ )
+endOfOctets = EndOfOctets()
diff --git a/python/pyasn1/pyasn1/codec/cer/__init__.py b/python/pyasn1/pyasn1/codec/cer/__init__.py
new file mode 100644
index 0000000000..8c3066b2e6
--- /dev/null
+++ b/python/pyasn1/pyasn1/codec/cer/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/python/pyasn1/pyasn1/codec/cer/decoder.py b/python/pyasn1/pyasn1/codec/cer/decoder.py
new file mode 100644
index 0000000000..9fd37c1347
--- /dev/null
+++ b/python/pyasn1/pyasn1/codec/cer/decoder.py
@@ -0,0 +1,35 @@
+# CER decoder
+from pyasn1.type import univ
+from pyasn1.codec.ber import decoder
+from pyasn1.compat.octets import oct2int
+from pyasn1 import error
+
+class BooleanDecoder(decoder.AbstractSimpleDecoder):
+ protoComponent = univ.Boolean(0)
+ def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
+ state, decodeFun, substrateFun):
+ head, tail = substrate[:length], substrate[length:]
+ if not head:
+ raise error.PyAsn1Error('Empty substrate')
+ byte = oct2int(head[0])
+ # CER/DER specifies encoding of TRUE as 0xFF and FALSE as 0x0, while
+ # BER allows any non-zero value as TRUE; cf. sections 8.2.2. and 11.1
+ # in http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
+ if byte == 0xff:
+ value = 1
+ elif byte == 0x00:
+ value = 0
+ else:
+ raise error.PyAsn1Error('Boolean CER violation: %s' % byte)
+ return self._createComponent(asn1Spec, tagSet, value), tail
+
+tagMap = decoder.tagMap.copy()
+tagMap.update({
+ univ.Boolean.tagSet: BooleanDecoder()
+ })
+
+typeMap = decoder.typeMap
+
+class Decoder(decoder.Decoder): pass
+
+decode = Decoder(tagMap, decoder.typeMap)
diff --git a/python/pyasn1/pyasn1/codec/cer/encoder.py b/python/pyasn1/pyasn1/codec/cer/encoder.py
new file mode 100644
index 0000000000..4c05130af9
--- /dev/null
+++ b/python/pyasn1/pyasn1/codec/cer/encoder.py
@@ -0,0 +1,87 @@
+# CER encoder
+from pyasn1.type import univ
+from pyasn1.codec.ber import encoder
+from pyasn1.compat.octets import int2oct, null
+
+class BooleanEncoder(encoder.IntegerEncoder):
+ def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
+ if client == 0:
+ substrate = int2oct(0)
+ else:
+ substrate = int2oct(255)
+ return substrate, 0
+
+class BitStringEncoder(encoder.BitStringEncoder):
+ def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
+ return encoder.BitStringEncoder.encodeValue(
+ self, encodeFun, client, defMode, 1000
+ )
+
+class OctetStringEncoder(encoder.OctetStringEncoder):
+ def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
+ return encoder.OctetStringEncoder.encodeValue(
+ self, encodeFun, client, defMode, 1000
+ )
+
+# specialized RealEncoder here
+# specialized GeneralStringEncoder here
+# specialized GeneralizedTimeEncoder here
+# specialized UTCTimeEncoder here
+
+class SetOfEncoder(encoder.SequenceOfEncoder):
+ def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
+ if isinstance(client, univ.SequenceAndSetBase):
+ client.setDefaultComponents()
+ client.verifySizeSpec()
+ substrate = null; idx = len(client)
+ # This is certainly a hack but how else do I distinguish SetOf
+ # from Set if they have the same tags&constraints?
+ if isinstance(client, univ.SequenceAndSetBase):
+ # Set
+ comps = []
+ while idx > 0:
+ idx = idx - 1
+ if client[idx] is None: # Optional component
+ continue
+ if client.getDefaultComponentByPosition(idx) == client[idx]:
+ continue
+ comps.append(client[idx])
+ comps.sort(key=lambda x: isinstance(x, univ.Choice) and \
+ x.getMinTagSet() or x.getTagSet())
+ for c in comps:
+ substrate += encodeFun(c, defMode, maxChunkSize)
+ else:
+ # SetOf
+ compSubs = []
+ while idx > 0:
+ idx = idx - 1
+ compSubs.append(
+ encodeFun(client[idx], defMode, maxChunkSize)
+ )
+ compSubs.sort() # perhaps padding's not needed
+ substrate = null
+ for compSub in compSubs:
+ substrate += compSub
+ return substrate, 1
+
+tagMap = encoder.tagMap.copy()
+tagMap.update({
+ univ.Boolean.tagSet: BooleanEncoder(),
+ univ.BitString.tagSet: BitStringEncoder(),
+ univ.OctetString.tagSet: OctetStringEncoder(),
+ univ.SetOf().tagSet: SetOfEncoder() # conflcts with Set
+ })
+
+typeMap = encoder.typeMap.copy()
+typeMap.update({
+ univ.Set.typeId: SetOfEncoder(),
+ univ.SetOf.typeId: SetOfEncoder()
+ })
+
+class Encoder(encoder.Encoder):
+ def __call__(self, client, defMode=0, maxChunkSize=0):
+ return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
+
+encode = Encoder(tagMap, typeMap)
+
+# EncoderFactory queries class instance and builds a map of tags -> encoders
diff --git a/python/pyasn1/pyasn1/codec/der/__init__.py b/python/pyasn1/pyasn1/codec/der/__init__.py
new file mode 100644
index 0000000000..8c3066b2e6
--- /dev/null
+++ b/python/pyasn1/pyasn1/codec/der/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/python/pyasn1/pyasn1/codec/der/decoder.py b/python/pyasn1/pyasn1/codec/der/decoder.py
new file mode 100644
index 0000000000..604abec2bc
--- /dev/null
+++ b/python/pyasn1/pyasn1/codec/der/decoder.py
@@ -0,0 +1,9 @@
+# DER decoder
+from pyasn1.type import univ
+from pyasn1.codec.cer import decoder
+
+tagMap = decoder.tagMap
+typeMap = decoder.typeMap
+Decoder = decoder.Decoder
+
+decode = Decoder(tagMap, typeMap)
diff --git a/python/pyasn1/pyasn1/codec/der/encoder.py b/python/pyasn1/pyasn1/codec/der/encoder.py
new file mode 100644
index 0000000000..4e5faefad4
--- /dev/null
+++ b/python/pyasn1/pyasn1/codec/der/encoder.py
@@ -0,0 +1,28 @@
+# DER encoder
+from pyasn1.type import univ
+from pyasn1.codec.cer import encoder
+
+class SetOfEncoder(encoder.SetOfEncoder):
+ def _cmpSetComponents(self, c1, c2):
+ tagSet1 = isinstance(c1, univ.Choice) and \
+ c1.getEffectiveTagSet() or c1.getTagSet()
+ tagSet2 = isinstance(c2, univ.Choice) and \
+ c2.getEffectiveTagSet() or c2.getTagSet()
+ return cmp(tagSet1, tagSet2)
+
+tagMap = encoder.tagMap.copy()
+tagMap.update({
+ # Overload CER encodrs with BER ones (a bit hackerish XXX)
+ univ.BitString.tagSet: encoder.encoder.BitStringEncoder(),
+ univ.OctetString.tagSet: encoder.encoder.OctetStringEncoder(),
+ # Set & SetOf have same tags
+ univ.SetOf().tagSet: SetOfEncoder()
+ })
+
+typeMap = encoder.typeMap
+
+class Encoder(encoder.Encoder):
+ def __call__(self, client, defMode=1, maxChunkSize=0):
+ return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
+
+encode = Encoder(tagMap, typeMap)
diff --git a/python/pyasn1/pyasn1/compat/__init__.py b/python/pyasn1/pyasn1/compat/__init__.py
new file mode 100644
index 0000000000..8c3066b2e6
--- /dev/null
+++ b/python/pyasn1/pyasn1/compat/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/python/pyasn1/pyasn1/compat/octets.py b/python/pyasn1/pyasn1/compat/octets.py
new file mode 100644
index 0000000000..f7f2a29bf5
--- /dev/null
+++ b/python/pyasn1/pyasn1/compat/octets.py
@@ -0,0 +1,20 @@
+from sys import version_info
+
+if version_info[0] <= 2:
+ int2oct = chr
+ ints2octs = lambda s: ''.join([ int2oct(x) for x in s ])
+ null = ''
+ oct2int = ord
+ octs2ints = lambda s: [ oct2int(x) for x in s ]
+ str2octs = lambda x: x
+ octs2str = lambda x: x
+ isOctetsType = lambda s: isinstance(s, str)
+else:
+ ints2octs = bytes
+ int2oct = lambda x: ints2octs((x,))
+ null = ints2octs()
+ oct2int = lambda x: x
+ octs2ints = lambda s: [ x for x in s ]
+ str2octs = lambda x: x.encode()
+ octs2str = lambda x: x.decode()
+ isOctetsType = lambda s: isinstance(s, bytes)
diff --git a/python/pyasn1/pyasn1/debug.py b/python/pyasn1/pyasn1/debug.py
new file mode 100644
index 0000000000..c27cb1d446
--- /dev/null
+++ b/python/pyasn1/pyasn1/debug.py
@@ -0,0 +1,65 @@
+import sys
+from pyasn1.compat.octets import octs2ints
+from pyasn1 import error
+from pyasn1 import __version__
+
+flagNone = 0x0000
+flagEncoder = 0x0001
+flagDecoder = 0x0002
+flagAll = 0xffff
+
+flagMap = {
+ 'encoder': flagEncoder,
+ 'decoder': flagDecoder,
+ 'all': flagAll
+ }
+
+class Debug:
+ defaultPrinter = sys.stderr.write
+ def __init__(self, *flags):
+ self._flags = flagNone
+ self._printer = self.defaultPrinter
+ self('running pyasn1 version %s' % __version__)
+ for f in flags:
+ if f not in flagMap:
+ raise error.PyAsn1Error('bad debug flag %s' % (f,))
+ self._flags = self._flags | flagMap[f]
+ self('debug category \'%s\' enabled' % f)
+
+ def __str__(self):
+ return 'logger %s, flags %x' % (self._printer, self._flags)
+
+ def __call__(self, msg):
+ self._printer('DBG: %s\n' % msg)
+
+ def __and__(self, flag):
+ return self._flags & flag
+
+ def __rand__(self, flag):
+ return flag & self._flags
+
+logger = 0
+
+def setLogger(l):
+ global logger
+ logger = l
+
+def hexdump(octets):
+ return ' '.join(
+ [ '%s%.2X' % (n%16 == 0 and ('\n%.5d: ' % n) or '', x)
+ for n,x in zip(range(len(octets)), octs2ints(octets)) ]
+ )
+
+class Scope:
+ def __init__(self):
+ self._list = []
+
+ def __str__(self): return '.'.join(self._list)
+
+ def push(self, token):
+ self._list.append(token)
+
+ def pop(self):
+ return self._list.pop()
+
+scope = Scope()
diff --git a/python/pyasn1/pyasn1/error.py b/python/pyasn1/pyasn1/error.py
new file mode 100644
index 0000000000..716406ff63
--- /dev/null
+++ b/python/pyasn1/pyasn1/error.py
@@ -0,0 +1,3 @@
+class PyAsn1Error(Exception): pass
+class ValueConstraintError(PyAsn1Error): pass
+class SubstrateUnderrunError(PyAsn1Error): pass
diff --git a/python/pyasn1/pyasn1/type/__init__.py b/python/pyasn1/pyasn1/type/__init__.py
new file mode 100644
index 0000000000..8c3066b2e6
--- /dev/null
+++ b/python/pyasn1/pyasn1/type/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/python/pyasn1/pyasn1/type/base.py b/python/pyasn1/pyasn1/type/base.py
new file mode 100644
index 0000000000..40873719ca
--- /dev/null
+++ b/python/pyasn1/pyasn1/type/base.py
@@ -0,0 +1,249 @@
+# Base classes for ASN.1 types
+import sys
+from pyasn1.type import constraint, tagmap
+from pyasn1 import error
+
+class Asn1Item: pass
+
+class Asn1ItemBase(Asn1Item):
+ # Set of tags for this ASN.1 type
+ tagSet = ()
+
+ # A list of constraint.Constraint instances for checking values
+ subtypeSpec = constraint.ConstraintsIntersection()
+
+ # Used for ambiguous ASN.1 types identification
+ typeId = None
+
+ def __init__(self, tagSet=None, subtypeSpec=None):
+ if tagSet is None:
+ self._tagSet = self.tagSet
+ else:
+ self._tagSet = tagSet
+ if subtypeSpec is None:
+ self._subtypeSpec = self.subtypeSpec
+ else:
+ self._subtypeSpec = subtypeSpec
+
+ def _verifySubtypeSpec(self, value, idx=None):
+ try:
+ self._subtypeSpec(value, idx)
+ except error.PyAsn1Error:
+ c, i, t = sys.exc_info()
+ raise c('%s at %s' % (i, self.__class__.__name__))
+
+ def getSubtypeSpec(self): return self._subtypeSpec
+
+ def getTagSet(self): return self._tagSet
+ def getEffectiveTagSet(self): return self._tagSet # used by untagged types
+ def getTagMap(self): return tagmap.TagMap({self._tagSet: self})
+
+ def isSameTypeWith(self, other):
+ return self is other or \
+ self._tagSet == other.getTagSet() and \
+ self._subtypeSpec == other.getSubtypeSpec()
+ def isSuperTypeOf(self, other):
+ """Returns true if argument is a ASN1 subtype of ourselves"""
+ return self._tagSet.isSuperTagSetOf(other.getTagSet()) and \
+ self._subtypeSpec.isSuperTypeOf(other.getSubtypeSpec())
+
+class __NoValue:
+ def __getattr__(self, attr):
+ raise error.PyAsn1Error('No value for %s()' % attr)
+ def __getitem__(self, i):
+ raise error.PyAsn1Error('No value')
+
+noValue = __NoValue()
+
+# Base class for "simple" ASN.1 objects. These are immutable.
+class AbstractSimpleAsn1Item(Asn1ItemBase):
+ defaultValue = noValue
+ def __init__(self, value=None, tagSet=None, subtypeSpec=None):
+ Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
+ if value is None or value is noValue:
+ value = self.defaultValue
+ if value is None or value is noValue:
+ self.__hashedValue = value = noValue
+ else:
+ value = self.prettyIn(value)
+ self._verifySubtypeSpec(value)
+ self.__hashedValue = hash(value)
+ self._value = value
+ self._len = None
+
+ def __repr__(self):
+ if self._value is noValue:
+ return self.__class__.__name__ + '()'
+ else:
+ return self.__class__.__name__ + '(%s)' % (self.prettyOut(self._value),)
+ def __str__(self): return str(self._value)
+ def __eq__(self, other):
+ return self is other and True or self._value == other
+ def __ne__(self, other): return self._value != other
+ def __lt__(self, other): return self._value < other
+ def __le__(self, other): return self._value <= other
+ def __gt__(self, other): return self._value > other
+ def __ge__(self, other): return self._value >= other
+ if sys.version_info[0] <= 2:
+ def __nonzero__(self): return bool(self._value)
+ else:
+ def __bool__(self): return bool(self._value)
+ def __hash__(self): return self.__hashedValue
+
+ def clone(self, value=None, tagSet=None, subtypeSpec=None):
+ if value is None and tagSet is None and subtypeSpec is None:
+ return self
+ if value is None:
+ value = self._value
+ if tagSet is None:
+ tagSet = self._tagSet
+ if subtypeSpec is None:
+ subtypeSpec = self._subtypeSpec
+ return self.__class__(value, tagSet, subtypeSpec)
+
+ def subtype(self, value=None, implicitTag=None, explicitTag=None,
+ subtypeSpec=None):
+ if value is None:
+ value = self._value
+ if implicitTag is not None:
+ tagSet = self._tagSet.tagImplicitly(implicitTag)
+ elif explicitTag is not None:
+ tagSet = self._tagSet.tagExplicitly(explicitTag)
+ else:
+ tagSet = self._tagSet
+ if subtypeSpec is None:
+ subtypeSpec = self._subtypeSpec
+ else:
+ subtypeSpec = subtypeSpec + self._subtypeSpec
+ return self.__class__(value, tagSet, subtypeSpec)
+
+ def prettyIn(self, value): return value
+ def prettyOut(self, value): return str(value)
+
+ def prettyPrint(self, scope=0):
+ if self._value is noValue:
+ return '<no value>'
+ else:
+ return self.prettyOut(self._value)
+
+ # XXX Compatibility stub
+ def prettyPrinter(self, scope=0): return self.prettyPrint(scope)
+
+#
+# Constructed types:
+# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice
+# * ASN1 types and values are represened by Python class instances
+# * Value initialization is made for defaulted components only
+# * Primary method of component addressing is by-position. Data model for base
+# type is Python sequence. Additional type-specific addressing methods
+# may be implemented for particular types.
+# * SequenceOf and SetOf types do not implement any additional methods
+# * Sequence, Set and Choice types also implement by-identifier addressing
+# * Sequence, Set and Choice types also implement by-asn1-type (tag) addressing
+# * Sequence and Set types may include optional and defaulted
+# components
+# * Constructed types hold a reference to component types used for value
+# verification and ordering.
+# * Component type is a scalar type for SequenceOf/SetOf types and a list
+# of types for Sequence/Set/Choice.
+#
+
+class AbstractConstructedAsn1Item(Asn1ItemBase):
+ componentType = None
+ sizeSpec = constraint.ConstraintsIntersection()
+ def __init__(self, componentType=None, tagSet=None,
+ subtypeSpec=None, sizeSpec=None):
+ Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
+ if componentType is None:
+ self._componentType = self.componentType
+ else:
+ self._componentType = componentType
+ if sizeSpec is None:
+ self._sizeSpec = self.sizeSpec
+ else:
+ self._sizeSpec = sizeSpec
+ self._componentValues = []
+ self._componentValuesSet = 0
+
+ def __repr__(self):
+ r = self.__class__.__name__ + '()'
+ for idx in range(len(self._componentValues)):
+ if self._componentValues[idx] is None:
+ continue
+ r = r + '.setComponentByPosition(%s, %r)' % (
+ idx, self._componentValues[idx]
+ )
+ return r
+
+ def __eq__(self, other):
+ return self is other and True or self._componentValues == other
+ def __ne__(self, other): return self._componentValues != other
+ def __lt__(self, other): return self._componentValues < other
+ def __le__(self, other): return self._componentValues <= other
+ def __gt__(self, other): return self._componentValues > other
+ def __ge__(self, other): return self._componentValues >= other
+ if sys.version_info[0] <= 2:
+ def __nonzero__(self): return bool(self._componentValues)
+ else:
+ def __bool__(self): return bool(self._componentValues)
+
+ def getComponentTagMap(self):
+ raise error.PyAsn1Error('Method not implemented')
+
+ def _cloneComponentValues(self, myClone, cloneValueFlag): pass
+
+ def clone(self, tagSet=None, subtypeSpec=None, sizeSpec=None,
+ cloneValueFlag=None):
+ if tagSet is None:
+ tagSet = self._tagSet
+ if subtypeSpec is None:
+ subtypeSpec = self._subtypeSpec
+ if sizeSpec is None:
+ sizeSpec = self._sizeSpec
+ r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
+ if cloneValueFlag:
+ self._cloneComponentValues(r, cloneValueFlag)
+ return r
+
+ def subtype(self, implicitTag=None, explicitTag=None, subtypeSpec=None,
+ sizeSpec=None, cloneValueFlag=None):
+ if implicitTag is not None:
+ tagSet = self._tagSet.tagImplicitly(implicitTag)
+ elif explicitTag is not None:
+ tagSet = self._tagSet.tagExplicitly(explicitTag)
+ else:
+ tagSet = self._tagSet
+ if subtypeSpec is None:
+ subtypeSpec = self._subtypeSpec
+ else:
+ subtypeSpec = subtypeSpec + self._subtypeSpec
+ if sizeSpec is None:
+ sizeSpec = self._sizeSpec
+ else:
+ sizeSpec = sizeSpec + self._sizeSpec
+ r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
+ if cloneValueFlag:
+ self._cloneComponentValues(r, cloneValueFlag)
+ return r
+
+ def _verifyComponent(self, idx, value): pass
+
+ def verifySizeSpec(self): self._sizeSpec(self)
+
+ def getComponentByPosition(self, idx):
+ raise error.PyAsn1Error('Method not implemented')
+ def setComponentByPosition(self, idx, value, verifyConstraints=True):
+ raise error.PyAsn1Error('Method not implemented')
+
+ def getComponentType(self): return self._componentType
+
+ def __getitem__(self, idx): return self.getComponentByPosition(idx)
+ def __setitem__(self, idx, value): self.setComponentByPosition(idx, value)
+
+ def __len__(self): return len(self._componentValues)
+
+ def clear(self):
+ self._componentValues = []
+ self._componentValuesSet = 0
+
+ def setDefaultComponents(self): pass
diff --git a/python/pyasn1/pyasn1/type/char.py b/python/pyasn1/pyasn1/type/char.py
new file mode 100644
index 0000000000..ae112f8bd3
--- /dev/null
+++ b/python/pyasn1/pyasn1/type/char.py
@@ -0,0 +1,61 @@
+# ASN.1 "character string" types
+from pyasn1.type import univ, tag
+
+class UTF8String(univ.OctetString):
+ tagSet = univ.OctetString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
+ )
+ encoding = "utf-8"
+
+class NumericString(univ.OctetString):
+ tagSet = univ.OctetString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18)
+ )
+
+class PrintableString(univ.OctetString):
+ tagSet = univ.OctetString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19)
+ )
+
+class TeletexString(univ.OctetString):
+ tagSet = univ.OctetString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20)
+ )
+
+
+class VideotexString(univ.OctetString):
+ tagSet = univ.OctetString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21)
+ )
+
+class IA5String(univ.OctetString):
+ tagSet = univ.OctetString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22)
+ )
+
+class GraphicString(univ.OctetString):
+ tagSet = univ.OctetString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25)
+ )
+
+class VisibleString(univ.OctetString):
+ tagSet = univ.OctetString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26)
+ )
+
+class GeneralString(univ.OctetString):
+ tagSet = univ.OctetString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27)
+ )
+
+class UniversalString(univ.OctetString):
+ tagSet = univ.OctetString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28)
+ )
+ encoding = "utf-32-be"
+
+class BMPString(univ.OctetString):
+ tagSet = univ.OctetString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30)
+ )
+ encoding = "utf-16-be"
diff --git a/python/pyasn1/pyasn1/type/constraint.py b/python/pyasn1/pyasn1/type/constraint.py
new file mode 100644
index 0000000000..66873937d8
--- /dev/null
+++ b/python/pyasn1/pyasn1/type/constraint.py
@@ -0,0 +1,200 @@
+#
+# ASN.1 subtype constraints classes.
+#
+# Constraints are relatively rare, but every ASN1 object
+# is doing checks all the time for whether they have any
+# constraints and whether they are applicable to the object.
+#
+# What we're going to do is define objects/functions that
+# can be called unconditionally if they are present, and that
+# are simply not present if there are no constraints.
+#
+# Original concept and code by Mike C. Fletcher.
+#
+import sys
+from pyasn1.type import error
+
+class AbstractConstraint:
+ """Abstract base-class for constraint objects
+
+ Constraints should be stored in a simple sequence in the
+ namespace of their client Asn1Item sub-classes.
+ """
+ def __init__(self, *values):
+ self._valueMap = {}
+ self._setValues(values)
+ self.__hashedValues = None
+ def __call__(self, value, idx=None):
+ try:
+ self._testValue(value, idx)
+ except error.ValueConstraintError:
+ raise error.ValueConstraintError(
+ '%s failed at: \"%s\"' % (self, sys.exc_info()[1])
+ )
+ def __repr__(self):
+ return '%s(%s)' % (
+ self.__class__.__name__,
+ ', '.join([repr(x) for x in self._values])
+ )
+ def __eq__(self, other):
+ return self is other and True or self._values == other
+ def __ne__(self, other): return self._values != other
+ def __lt__(self, other): return self._values < other
+ def __le__(self, other): return self._values <= other
+ def __gt__(self, other): return self._values > other
+ def __ge__(self, other): return self._values >= other
+ if sys.version_info[0] <= 2:
+ def __nonzero__(self): return bool(self._values)
+ else:
+ def __bool__(self): return bool(self._values)
+
+ def __hash__(self):
+ if self.__hashedValues is None:
+ self.__hashedValues = hash((self.__class__.__name__, self._values))
+ return self.__hashedValues
+
+ def _setValues(self, values): self._values = values
+ def _testValue(self, value, idx):
+ raise error.ValueConstraintError(value)
+
+ # Constraints derivation logic
+ def getValueMap(self): return self._valueMap
+ def isSuperTypeOf(self, otherConstraint):
+ return self in otherConstraint.getValueMap() or \
+ otherConstraint is self or otherConstraint == self
+ def isSubTypeOf(self, otherConstraint):
+ return otherConstraint in self._valueMap or \
+ otherConstraint is self or otherConstraint == self
+
+class SingleValueConstraint(AbstractConstraint):
+ """Value must be part of defined values constraint"""
+ def _testValue(self, value, idx):
+ # XXX index vals for performance?
+ if value not in self._values:
+ raise error.ValueConstraintError(value)
+
+class ContainedSubtypeConstraint(AbstractConstraint):
+ """Value must satisfy all of defined set of constraints"""
+ def _testValue(self, value, idx):
+ for c in self._values:
+ c(value, idx)
+
+class ValueRangeConstraint(AbstractConstraint):
+ """Value must be within start and stop values (inclusive)"""
+ def _testValue(self, value, idx):
+ if value < self.start or value > self.stop:
+ raise error.ValueConstraintError(value)
+
+ def _setValues(self, values):
+ if len(values) != 2:
+ raise error.PyAsn1Error(
+ '%s: bad constraint values' % (self.__class__.__name__,)
+ )
+ self.start, self.stop = values
+ if self.start > self.stop:
+ raise error.PyAsn1Error(
+ '%s: screwed constraint values (start > stop): %s > %s' % (
+ self.__class__.__name__,
+ self.start, self.stop
+ )
+ )
+ AbstractConstraint._setValues(self, values)
+
+class ValueSizeConstraint(ValueRangeConstraint):
+ """len(value) must be within start and stop values (inclusive)"""
+ def _testValue(self, value, idx):
+ l = len(value)
+ if l < self.start or l > self.stop:
+ raise error.ValueConstraintError(value)
+
+class PermittedAlphabetConstraint(SingleValueConstraint):
+ def _setValues(self, values):
+ self._values = ()
+ for v in values:
+ self._values = self._values + tuple(v)
+
+ def _testValue(self, value, idx):
+ for v in value:
+ if v not in self._values:
+ raise error.ValueConstraintError(value)
+
+# This is a bit kludgy, meaning two op modes within a single constraing
+class InnerTypeConstraint(AbstractConstraint):
+ """Value must satisfy type and presense constraints"""
+ def _testValue(self, value, idx):
+ if self.__singleTypeConstraint:
+ self.__singleTypeConstraint(value)
+ elif self.__multipleTypeConstraint:
+ if idx not in self.__multipleTypeConstraint:
+ raise error.ValueConstraintError(value)
+ constraint, status = self.__multipleTypeConstraint[idx]
+ if status == 'ABSENT': # XXX presense is not checked!
+ raise error.ValueConstraintError(value)
+ constraint(value)
+
+ def _setValues(self, values):
+ self.__multipleTypeConstraint = {}
+ self.__singleTypeConstraint = None
+ for v in values:
+ if isinstance(v, tuple):
+ self.__multipleTypeConstraint[v[0]] = v[1], v[2]
+ else:
+ self.__singleTypeConstraint = v
+ AbstractConstraint._setValues(self, values)
+
+# Boolean ops on constraints
+
+class ConstraintsExclusion(AbstractConstraint):
+ """Value must not fit the single constraint"""
+ def _testValue(self, value, idx):
+ try:
+ self._values[0](value, idx)
+ except error.ValueConstraintError:
+ return
+ else:
+ raise error.ValueConstraintError(value)
+
+ def _setValues(self, values):
+ if len(values) != 1:
+ raise error.PyAsn1Error('Single constraint expected')
+ AbstractConstraint._setValues(self, values)
+
+class AbstractConstraintSet(AbstractConstraint):
+ """Value must not satisfy the single constraint"""
+ def __getitem__(self, idx): return self._values[idx]
+
+ def __add__(self, value): return self.__class__(self, value)
+ def __radd__(self, value): return self.__class__(self, value)
+
+ def __len__(self): return len(self._values)
+
+ # Constraints inclusion in sets
+
+ def _setValues(self, values):
+ self._values = values
+ for v in values:
+ self._valueMap[v] = 1
+ self._valueMap.update(v.getValueMap())
+
+class ConstraintsIntersection(AbstractConstraintSet):
+ """Value must satisfy all constraints"""
+ def _testValue(self, value, idx):
+ for v in self._values:
+ v(value, idx)
+
+class ConstraintsUnion(AbstractConstraintSet):
+ """Value must satisfy at least one constraint"""
+ def _testValue(self, value, idx):
+ for v in self._values:
+ try:
+ v(value, idx)
+ except error.ValueConstraintError:
+ pass
+ else:
+ return
+ raise error.ValueConstraintError(
+ 'all of %s failed for \"%s\"' % (self._values, value)
+ )
+
+# XXX
+# add tests for type check
diff --git a/python/pyasn1/pyasn1/type/error.py b/python/pyasn1/pyasn1/type/error.py
new file mode 100644
index 0000000000..3e68484472
--- /dev/null
+++ b/python/pyasn1/pyasn1/type/error.py
@@ -0,0 +1,3 @@
+from pyasn1.error import PyAsn1Error
+
+class ValueConstraintError(PyAsn1Error): pass
diff --git a/python/pyasn1/pyasn1/type/namedtype.py b/python/pyasn1/pyasn1/type/namedtype.py
new file mode 100644
index 0000000000..48967a5fe2
--- /dev/null
+++ b/python/pyasn1/pyasn1/type/namedtype.py
@@ -0,0 +1,132 @@
+# NamedType specification for constructed types
+import sys
+from pyasn1.type import tagmap
+from pyasn1 import error
+
+class NamedType:
+ isOptional = 0
+ isDefaulted = 0
+ def __init__(self, name, t):
+ self.__name = name; self.__type = t
+ def __repr__(self): return '%s(%s, %s)' % (
+ self.__class__.__name__, self.__name, self.__type
+ )
+ def getType(self): return self.__type
+ def getName(self): return self.__name
+ def __getitem__(self, idx):
+ if idx == 0: return self.__name
+ if idx == 1: return self.__type
+ raise IndexError()
+
+class OptionalNamedType(NamedType):
+ isOptional = 1
+class DefaultedNamedType(NamedType):
+ isDefaulted = 1
+
+class NamedTypes:
+ def __init__(self, *namedTypes):
+ self.__namedTypes = namedTypes
+ self.__namedTypesLen = len(self.__namedTypes)
+ self.__minTagSet = None
+ self.__tagToPosIdx = {}; self.__nameToPosIdx = {}
+ self.__tagMap = { False: None, True: None }
+ self.__ambigiousTypes = {}
+
+ def __repr__(self):
+ r = '%s(' % self.__class__.__name__
+ for n in self.__namedTypes:
+ r = r + '%r, ' % (n,)
+ return r + ')'
+
+ def __getitem__(self, idx): return self.__namedTypes[idx]
+
+ if sys.version_info[0] <= 2:
+ def __nonzero__(self): return bool(self.__namedTypesLen)
+ else:
+ def __bool__(self): return bool(self.__namedTypesLen)
+ def __len__(self): return self.__namedTypesLen
+
+ def getTypeByPosition(self, idx):
+ if idx < 0 or idx >= self.__namedTypesLen:
+ raise error.PyAsn1Error('Type position out of range')
+ else:
+ return self.__namedTypes[idx].getType()
+
+ def getPositionByType(self, tagSet):
+ if not self.__tagToPosIdx:
+ idx = self.__namedTypesLen
+ while idx > 0:
+ idx = idx - 1
+ tagMap = self.__namedTypes[idx].getType().getTagMap()
+ for t in tagMap.getPosMap():
+ if t in self.__tagToPosIdx:
+ raise error.PyAsn1Error('Duplicate type %s' % (t,))
+ self.__tagToPosIdx[t] = idx
+ try:
+ return self.__tagToPosIdx[tagSet]
+ except KeyError:
+ raise error.PyAsn1Error('Type %s not found' % (tagSet,))
+
+ def getNameByPosition(self, idx):
+ try:
+ return self.__namedTypes[idx].getName()
+ except IndexError:
+ raise error.PyAsn1Error('Type position out of range')
+ def getPositionByName(self, name):
+ if not self.__nameToPosIdx:
+ idx = self.__namedTypesLen
+ while idx > 0:
+ idx = idx - 1
+ n = self.__namedTypes[idx].getName()
+ if n in self.__nameToPosIdx:
+ raise error.PyAsn1Error('Duplicate name %s' % (n,))
+ self.__nameToPosIdx[n] = idx
+ try:
+ return self.__nameToPosIdx[name]
+ except KeyError:
+ raise error.PyAsn1Error('Name %s not found' % (name,))
+
+ def __buildAmbigiousTagMap(self):
+ ambigiousTypes = ()
+ idx = self.__namedTypesLen
+ while idx > 0:
+ idx = idx - 1
+ t = self.__namedTypes[idx]
+ if t.isOptional or t.isDefaulted:
+ ambigiousTypes = (t, ) + ambigiousTypes
+ else:
+ ambigiousTypes = (t, )
+ self.__ambigiousTypes[idx] = NamedTypes(*ambigiousTypes)
+
+ def getTagMapNearPosition(self, idx):
+ if not self.__ambigiousTypes: self.__buildAmbigiousTagMap()
+ try:
+ return self.__ambigiousTypes[idx].getTagMap()
+ except KeyError:
+ raise error.PyAsn1Error('Type position out of range')
+
+ def getPositionNearType(self, tagSet, idx):
+ if not self.__ambigiousTypes: self.__buildAmbigiousTagMap()
+ try:
+ return idx+self.__ambigiousTypes[idx].getPositionByType(tagSet)
+ except KeyError:
+ raise error.PyAsn1Error('Type position out of range')
+
+ def genMinTagSet(self):
+ if self.__minTagSet is None:
+ for t in self.__namedTypes:
+ __type = t.getType()
+ tagSet = getattr(__type,'getMinTagSet',__type.getTagSet)()
+ if self.__minTagSet is None or tagSet < self.__minTagSet:
+ self.__minTagSet = tagSet
+ return self.__minTagSet
+
+ def getTagMap(self, uniq=False):
+ if self.__tagMap[uniq] is None:
+ tagMap = tagmap.TagMap()
+ for nt in self.__namedTypes:
+ tagMap = tagMap.clone(
+ nt.getType(), nt.getType().getTagMap(), uniq
+ )
+ self.__tagMap[uniq] = tagMap
+ return self.__tagMap[uniq]
diff --git a/python/pyasn1/pyasn1/type/namedval.py b/python/pyasn1/pyasn1/type/namedval.py
new file mode 100644
index 0000000000..d0fea7cc7c
--- /dev/null
+++ b/python/pyasn1/pyasn1/type/namedval.py
@@ -0,0 +1,46 @@
+# ASN.1 named integers
+from pyasn1 import error
+
+__all__ = [ 'NamedValues' ]
+
+class NamedValues:
+ def __init__(self, *namedValues):
+ self.nameToValIdx = {}; self.valToNameIdx = {}
+ self.namedValues = ()
+ automaticVal = 1
+ for namedValue in namedValues:
+ if isinstance(namedValue, tuple):
+ name, val = namedValue
+ else:
+ name = namedValue
+ val = automaticVal
+ if name in self.nameToValIdx:
+ raise error.PyAsn1Error('Duplicate name %s' % (name,))
+ self.nameToValIdx[name] = val
+ if val in self.valToNameIdx:
+ raise error.PyAsn1Error('Duplicate value %s=%s' % (name, val))
+ self.valToNameIdx[val] = name
+ self.namedValues = self.namedValues + ((name, val),)
+ automaticVal = automaticVal + 1
+ def __str__(self): return str(self.namedValues)
+
+ def getName(self, value):
+ if value in self.valToNameIdx:
+ return self.valToNameIdx[value]
+
+ def getValue(self, name):
+ if name in self.nameToValIdx:
+ return self.nameToValIdx[name]
+
+ def __getitem__(self, i): return self.namedValues[i]
+ def __len__(self): return len(self.namedValues)
+
+ def __add__(self, namedValues):
+ return self.__class__(*self.namedValues + namedValues)
+ def __radd__(self, namedValues):
+ return self.__class__(*namedValues + tuple(self))
+
+ def clone(self, *namedValues):
+ return self.__class__(*tuple(self) + namedValues)
+
+# XXX clone/subtype?
diff --git a/python/pyasn1/pyasn1/type/tag.py b/python/pyasn1/pyasn1/type/tag.py
new file mode 100644
index 0000000000..1144907fa1
--- /dev/null
+++ b/python/pyasn1/pyasn1/type/tag.py
@@ -0,0 +1,122 @@
+# ASN.1 types tags
+from operator import getitem
+from pyasn1 import error
+
+tagClassUniversal = 0x00
+tagClassApplication = 0x40
+tagClassContext = 0x80
+tagClassPrivate = 0xC0
+
+tagFormatSimple = 0x00
+tagFormatConstructed = 0x20
+
+tagCategoryImplicit = 0x01
+tagCategoryExplicit = 0x02
+tagCategoryUntagged = 0x04
+
+class Tag:
+ def __init__(self, tagClass, tagFormat, tagId):
+ if tagId < 0:
+ raise error.PyAsn1Error(
+ 'Negative tag ID (%s) not allowed' % (tagId,)
+ )
+ self.__tag = (tagClass, tagFormat, tagId)
+ self.uniq = (tagClass, tagId)
+ self.__hashedUniqTag = hash(self.uniq)
+
+ def __repr__(self):
+ return '%s(tagClass=%s, tagFormat=%s, tagId=%s)' % (
+ (self.__class__.__name__,) + self.__tag
+ )
+ # These is really a hotspot -- expose public "uniq" attribute to save on
+ # function calls
+ def __eq__(self, other): return self.uniq == other.uniq
+ def __ne__(self, other): return self.uniq != other.uniq
+ def __lt__(self, other): return self.uniq < other.uniq
+ def __le__(self, other): return self.uniq <= other.uniq
+ def __gt__(self, other): return self.uniq > other.uniq
+ def __ge__(self, other): return self.uniq >= other.uniq
+ def __hash__(self): return self.__hashedUniqTag
+ def __getitem__(self, idx): return self.__tag[idx]
+ def __and__(self, otherTag):
+ (tagClass, tagFormat, tagId) = otherTag
+ return self.__class__(
+ self.__tag&tagClass, self.__tag&tagFormat, self.__tag&tagId
+ )
+ def __or__(self, otherTag):
+ (tagClass, tagFormat, tagId) = otherTag
+ return self.__class__(
+ self.__tag[0]|tagClass,
+ self.__tag[1]|tagFormat,
+ self.__tag[2]|tagId
+ )
+ def asTuple(self): return self.__tag # __getitem__() is slow
+
+class TagSet:
+ def __init__(self, baseTag=(), *superTags):
+ self.__baseTag = baseTag
+ self.__superTags = superTags
+ self.__hashedSuperTags = hash(superTags)
+ _uniq = ()
+ for t in superTags:
+ _uniq = _uniq + t.uniq
+ self.uniq = _uniq
+ self.__lenOfSuperTags = len(superTags)
+
+ def __repr__(self):
+ return '%s(%s)' % (
+ self.__class__.__name__,
+ ', '.join([repr(x) for x in self.__superTags])
+ )
+
+ def __add__(self, superTag):
+ return self.__class__(
+ self.__baseTag, *self.__superTags + (superTag,)
+ )
+ def __radd__(self, superTag):
+ return self.__class__(
+ self.__baseTag, *(superTag,) + self.__superTags
+ )
+
+ def tagExplicitly(self, superTag):
+ tagClass, tagFormat, tagId = superTag
+ if tagClass == tagClassUniversal:
+ raise error.PyAsn1Error(
+ 'Can\'t tag with UNIVERSAL-class tag'
+ )
+ if tagFormat != tagFormatConstructed:
+ superTag = Tag(tagClass, tagFormatConstructed, tagId)
+ return self + superTag
+
+ def tagImplicitly(self, superTag):
+ tagClass, tagFormat, tagId = superTag
+ if self.__superTags:
+ superTag = Tag(tagClass, self.__superTags[-1][1], tagId)
+ return self[:-1] + superTag
+
+ def getBaseTag(self): return self.__baseTag
+ def __getitem__(self, idx):
+ if isinstance(idx, slice):
+ return self.__class__(
+ self.__baseTag, *getitem(self.__superTags, idx)
+ )
+ return self.__superTags[idx]
+ def __eq__(self, other): return self.uniq == other.uniq
+ def __ne__(self, other): return self.uniq != other.uniq
+ def __lt__(self, other): return self.uniq < other.uniq
+ def __le__(self, other): return self.uniq <= other.uniq
+ def __gt__(self, other): return self.uniq > other.uniq
+ def __ge__(self, other): return self.uniq >= other.uniq
+ def __hash__(self): return self.__hashedSuperTags
+ def __len__(self): return self.__lenOfSuperTags
+ def isSuperTagSetOf(self, tagSet):
+ if len(tagSet) < self.__lenOfSuperTags:
+ return
+ idx = self.__lenOfSuperTags - 1
+ while idx >= 0:
+ if self.__superTags[idx] != tagSet[idx]:
+ return
+ idx = idx - 1
+ return 1
+
+def initTagSet(tag): return TagSet(tag, tag)
diff --git a/python/pyasn1/pyasn1/type/tagmap.py b/python/pyasn1/pyasn1/type/tagmap.py
new file mode 100644
index 0000000000..7cec3a10e4
--- /dev/null
+++ b/python/pyasn1/pyasn1/type/tagmap.py
@@ -0,0 +1,52 @@
+from pyasn1 import error
+
+class TagMap:
+ def __init__(self, posMap={}, negMap={}, defType=None):
+ self.__posMap = posMap.copy()
+ self.__negMap = negMap.copy()
+ self.__defType = defType
+
+ def __contains__(self, tagSet):
+ return tagSet in self.__posMap or \
+ self.__defType is not None and tagSet not in self.__negMap
+
+ def __getitem__(self, tagSet):
+ if tagSet in self.__posMap:
+ return self.__posMap[tagSet]
+ elif tagSet in self.__negMap:
+ raise error.PyAsn1Error('Key in negative map')
+ elif self.__defType is not None:
+ return self.__defType
+ else:
+ raise KeyError()
+
+ def __repr__(self):
+ s = '%r/%r' % (self.__posMap, self.__negMap)
+ if self.__defType is not None:
+ s = s + '/%r' % (self.__defType,)
+ return s
+
+ def clone(self, parentType, tagMap, uniq=False):
+ if self.__defType is not None and tagMap.getDef() is not None:
+ raise error.PyAsn1Error('Duplicate default value at %s' % (self,))
+ if tagMap.getDef() is not None:
+ defType = tagMap.getDef()
+ else:
+ defType = self.__defType
+
+ posMap = self.__posMap.copy()
+ for k in tagMap.getPosMap():
+ if uniq and k in posMap:
+ raise error.PyAsn1Error('Duplicate positive key %s' % (k,))
+ posMap[k] = parentType
+
+ negMap = self.__negMap.copy()
+ negMap.update(tagMap.getNegMap())
+
+ return self.__class__(
+ posMap, negMap, defType,
+ )
+
+ def getPosMap(self): return self.__posMap.copy()
+ def getNegMap(self): return self.__negMap.copy()
+ def getDef(self): return self.__defType
diff --git a/python/pyasn1/pyasn1/type/univ.py b/python/pyasn1/pyasn1/type/univ.py
new file mode 100644
index 0000000000..9cd16f8a2a
--- /dev/null
+++ b/python/pyasn1/pyasn1/type/univ.py
@@ -0,0 +1,1042 @@
+# ASN.1 "universal" data types
+import operator, sys
+from pyasn1.type import base, tag, constraint, namedtype, namedval, tagmap
+from pyasn1.codec.ber import eoo
+from pyasn1.compat import octets
+from pyasn1 import error
+
+# "Simple" ASN.1 types (yet incomplete)
+
+class Integer(base.AbstractSimpleAsn1Item):
+ tagSet = baseTagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02)
+ )
+ namedValues = namedval.NamedValues()
+ def __init__(self, value=None, tagSet=None, subtypeSpec=None,
+ namedValues=None):
+ if namedValues is None:
+ self.__namedValues = self.namedValues
+ else:
+ self.__namedValues = namedValues
+ base.AbstractSimpleAsn1Item.__init__(
+ self, value, tagSet, subtypeSpec
+ )
+
+ def __and__(self, value): return self.clone(self._value & value)
+ def __rand__(self, value): return self.clone(value & self._value)
+ def __or__(self, value): return self.clone(self._value | value)
+ def __ror__(self, value): return self.clone(value | self._value)
+ def __xor__(self, value): return self.clone(self._value ^ value)
+ def __rxor__(self, value): return self.clone(value ^ self._value)
+ def __lshift__(self, value): return self.clone(self._value << value)
+ def __rshift__(self, value): return self.clone(self._value >> value)
+
+ def __add__(self, value): return self.clone(self._value + value)
+ def __radd__(self, value): return self.clone(value + self._value)
+ def __sub__(self, value): return self.clone(self._value - value)
+ def __rsub__(self, value): return self.clone(value - self._value)
+ def __mul__(self, value): return self.clone(self._value * value)
+ def __rmul__(self, value): return self.clone(value * self._value)
+ def __mod__(self, value): return self.clone(self._value % value)
+ def __rmod__(self, value): return self.clone(value % self._value)
+ def __pow__(self, value, modulo=None): return self.clone(pow(self._value, value, modulo))
+ def __rpow__(self, value): return self.clone(pow(value, self._value))
+
+ if sys.version_info[0] <= 2:
+ def __div__(self, value): return self.clone(self._value // value)
+ def __rdiv__(self, value): return self.clone(value // self._value)
+ else:
+ def __truediv__(self, value): return self.clone(self._value / value)
+ def __rtruediv__(self, value): return self.clone(value / self._value)
+ def __divmod__(self, value): return self.clone(self._value // value)
+ def __rdivmod__(self, value): return self.clone(value // self._value)
+
+ __hash__ = base.AbstractSimpleAsn1Item.__hash__
+
+ def __int__(self): return int(self._value)
+ if sys.version_info[0] <= 2:
+ def __long__(self): return long(self._value)
+ def __float__(self): return float(self._value)
+ def __abs__(self): return abs(self._value)
+ def __index__(self): return int(self._value)
+
+ def __lt__(self, value): return self._value < value
+ def __le__(self, value): return self._value <= value
+ def __eq__(self, value): return self._value == value
+ def __ne__(self, value): return self._value != value
+ def __gt__(self, value): return self._value > value
+ def __ge__(self, value): return self._value >= value
+
+ def prettyIn(self, value):
+ if not isinstance(value, str):
+ try:
+ return int(value)
+ except:
+ raise error.PyAsn1Error(
+ 'Can\'t coerce %s into integer: %s' % (value, sys.exc_info()[1])
+ )
+ r = self.__namedValues.getValue(value)
+ if r is not None:
+ return r
+ try:
+ return int(value)
+ except:
+ raise error.PyAsn1Error(
+ 'Can\'t coerce %s into integer: %s' % (value, sys.exc_info()[1])
+ )
+
+ def prettyOut(self, value):
+ r = self.__namedValues.getName(value)
+ return r is None and str(value) or repr(r)
+
+ def getNamedValues(self): return self.__namedValues
+
+ def clone(self, value=None, tagSet=None, subtypeSpec=None,
+ namedValues=None):
+ if value is None and tagSet is None and subtypeSpec is None \
+ and namedValues is None:
+ return self
+ if value is None:
+ value = self._value
+ if tagSet is None:
+ tagSet = self._tagSet
+ if subtypeSpec is None:
+ subtypeSpec = self._subtypeSpec
+ if namedValues is None:
+ namedValues = self.__namedValues
+ return self.__class__(value, tagSet, subtypeSpec, namedValues)
+
+ def subtype(self, value=None, implicitTag=None, explicitTag=None,
+ subtypeSpec=None, namedValues=None):
+ if value is None:
+ value = self._value
+ if implicitTag is not None:
+ tagSet = self._tagSet.tagImplicitly(implicitTag)
+ elif explicitTag is not None:
+ tagSet = self._tagSet.tagExplicitly(explicitTag)
+ else:
+ tagSet = self._tagSet
+ if subtypeSpec is None:
+ subtypeSpec = self._subtypeSpec
+ else:
+ subtypeSpec = subtypeSpec + self._subtypeSpec
+ if namedValues is None:
+ namedValues = self.__namedValues
+ else:
+ namedValues = namedValues + self.__namedValues
+ return self.__class__(value, tagSet, subtypeSpec, namedValues)
+
+class Boolean(Integer):
+ tagSet = baseTagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01),
+ )
+ subtypeSpec = Integer.subtypeSpec+constraint.SingleValueConstraint(0,1)
+ namedValues = Integer.namedValues.clone(('False', 0), ('True', 1))
+
+class BitString(base.AbstractSimpleAsn1Item):
+ tagSet = baseTagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03)
+ )
+ namedValues = namedval.NamedValues()
+ def __init__(self, value=None, tagSet=None, subtypeSpec=None,
+ namedValues=None):
+ if namedValues is None:
+ self.__namedValues = self.namedValues
+ else:
+ self.__namedValues = namedValues
+ base.AbstractSimpleAsn1Item.__init__(
+ self, value, tagSet, subtypeSpec
+ )
+
+ def clone(self, value=None, tagSet=None, subtypeSpec=None,
+ namedValues=None):
+ if value is None and tagSet is None and subtypeSpec is None \
+ and namedValues is None:
+ return self
+ if value is None:
+ value = self._value
+ if tagSet is None:
+ tagSet = self._tagSet
+ if subtypeSpec is None:
+ subtypeSpec = self._subtypeSpec
+ if namedValues is None:
+ namedValues = self.__namedValues
+ return self.__class__(value, tagSet, subtypeSpec, namedValues)
+
+ def subtype(self, value=None, implicitTag=None, explicitTag=None,
+ subtypeSpec=None, namedValues=None):
+ if value is None:
+ value = self._value
+ if implicitTag is not None:
+ tagSet = self._tagSet.tagImplicitly(implicitTag)
+ elif explicitTag is not None:
+ tagSet = self._tagSet.tagExplicitly(explicitTag)
+ else:
+ tagSet = self._tagSet
+ if subtypeSpec is None:
+ subtypeSpec = self._subtypeSpec
+ else:
+ subtypeSpec = subtypeSpec + self._subtypeSpec
+ if namedValues is None:
+ namedValues = self.__namedValues
+ else:
+ namedValues = namedValues + self.__namedValues
+ return self.__class__(value, tagSet, subtypeSpec, namedValues)
+
+ def __str__(self): return str(tuple(self))
+
+ # Immutable sequence object protocol
+
+ def __len__(self):
+ if self._len is None:
+ self._len = len(self._value)
+ return self._len
+ def __getitem__(self, i):
+ if isinstance(i, slice):
+ return self.clone(operator.getitem(self._value, i))
+ else:
+ return self._value[i]
+
+ def __add__(self, value): return self.clone(self._value + value)
+ def __radd__(self, value): return self.clone(value + self._value)
+ def __mul__(self, value): return self.clone(self._value * value)
+ def __rmul__(self, value): return self * value
+
+ def prettyIn(self, value):
+ r = []
+ if not value:
+ return ()
+ elif isinstance(value, str):
+ if value[0] == '\'':
+ if value[-2:] == '\'B':
+ for v in value[1:-2]:
+ if v == '0':
+ r.append(0)
+ elif v == '1':
+ r.append(1)
+ else:
+ raise error.PyAsn1Error(
+ 'Non-binary BIT STRING initializer %s' % (v,)
+ )
+ return tuple(r)
+ elif value[-2:] == '\'H':
+ for v in value[1:-2]:
+ i = 4
+ v = int(v, 16)
+ while i:
+ i = i - 1
+ r.append((v>>i)&0x01)
+ return tuple(r)
+ else:
+ raise error.PyAsn1Error(
+ 'Bad BIT STRING value notation %s' % (value,)
+ )
+ else:
+ for i in value.split(','):
+ j = self.__namedValues.getValue(i)
+ if j is None:
+ raise error.PyAsn1Error(
+ 'Unknown bit identifier \'%s\'' % (i,)
+ )
+ if j >= len(r):
+ r.extend([0]*(j-len(r)+1))
+ r[j] = 1
+ return tuple(r)
+ elif isinstance(value, (tuple, list)):
+ r = tuple(value)
+ for b in r:
+ if b and b != 1:
+ raise error.PyAsn1Error(
+ 'Non-binary BitString initializer \'%s\'' % (r,)
+ )
+ return r
+ elif isinstance(value, BitString):
+ return tuple(value)
+ else:
+ raise error.PyAsn1Error(
+ 'Bad BitString initializer type \'%s\'' % (value,)
+ )
+
+ def prettyOut(self, value):
+ return '\"\'%s\'B\"' % ''.join([str(x) for x in value])
+
+class OctetString(base.AbstractSimpleAsn1Item):
+ tagSet = baseTagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04)
+ )
+ defaultBinValue = defaultHexValue = base.noValue
+ encoding = 'us-ascii'
+ def __init__(self, value=None, tagSet=None, subtypeSpec=None,
+ encoding=None, binValue=None, hexValue=None):
+ if encoding is None:
+ self._encoding = self.encoding
+ else:
+ self._encoding = encoding
+ if binValue is not None:
+ value = self.fromBinaryString(binValue)
+ if hexValue is not None:
+ value = self.fromHexString(hexValue)
+ if value is None or value is base.noValue:
+ value = self.defaultHexValue
+ if value is None or value is base.noValue:
+ value = self.defaultBinValue
+ self.__intValue = None
+ base.AbstractSimpleAsn1Item.__init__(self, value, tagSet, subtypeSpec)
+
+ def clone(self, value=None, tagSet=None, subtypeSpec=None,
+ encoding=None, binValue=None, hexValue=None):
+ if value is None and tagSet is None and subtypeSpec is None and \
+ encoding is None and binValue is None and hexValue is None:
+ return self
+ if value is None and binValue is None and hexValue is None:
+ value = self._value
+ if tagSet is None:
+ tagSet = self._tagSet
+ if subtypeSpec is None:
+ subtypeSpec = self._subtypeSpec
+ if encoding is None:
+ encoding = self._encoding
+ return self.__class__(
+ value, tagSet, subtypeSpec, encoding, binValue, hexValue
+ )
+
+ if sys.version_info[0] <= 2:
+ def prettyIn(self, value):
+ if isinstance(value, str):
+ return value
+ elif isinstance(value, (tuple, list)):
+ try:
+ return ''.join([ chr(x) for x in value ])
+ except ValueError:
+ raise error.PyAsn1Error(
+ 'Bad OctetString initializer \'%s\'' % (value,)
+ )
+ else:
+ return str(value)
+ else:
+ def prettyIn(self, value):
+ if isinstance(value, bytes):
+ return value
+ elif isinstance(value, OctetString):
+ return value.asOctets()
+ elif isinstance(value, (tuple, list, map)):
+ try:
+ return bytes(value)
+ except ValueError:
+ raise error.PyAsn1Error(
+ 'Bad OctetString initializer \'%s\'' % (value,)
+ )
+ else:
+ try:
+ return str(value).encode(self._encoding)
+ except UnicodeEncodeError:
+ raise error.PyAsn1Error(
+ 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding)
+ )
+
+
+ def fromBinaryString(self, value):
+ bitNo = 8; byte = 0; r = ()
+ for v in value:
+ if bitNo:
+ bitNo = bitNo - 1
+ else:
+ bitNo = 7
+ r = r + (byte,)
+ byte = 0
+ if v == '0':
+ v = 0
+ elif v == '1':
+ v = 1
+ else:
+ raise error.PyAsn1Error(
+ 'Non-binary OCTET STRING initializer %s' % (v,)
+ )
+ byte = byte | (v << bitNo)
+ return octets.ints2octs(r + (byte,))
+
+ def fromHexString(self, value):
+ r = p = ()
+ for v in value:
+ if p:
+ r = r + (int(p+v, 16),)
+ p = ()
+ else:
+ p = v
+ if p:
+ r = r + (int(p+'0', 16),)
+ return octets.ints2octs(r)
+
+ def prettyOut(self, value):
+ if sys.version_info[0] <= 2:
+ numbers = tuple([ ord(x) for x in value ])
+ else:
+ numbers = tuple(value)
+ if [ x for x in numbers if x < 32 or x > 126 ]:
+ return '0x' + ''.join([ '%.2x' % x for x in numbers ])
+ else:
+ return str(value)
+
+ def __repr__(self):
+ if self._value is base.noValue:
+ return self.__class__.__name__ + '()'
+ if [ x for x in self.asNumbers() if x < 32 or x > 126 ]:
+ return self.__class__.__name__ + '(hexValue=\'' + ''.join([ '%.2x' % x for x in self.asNumbers() ])+'\')'
+ else:
+ return self.__class__.__name__ + '(\'' + self.prettyOut(self._value) + '\')'
+
+ if sys.version_info[0] <= 2:
+ def __str__(self): return str(self._value)
+ def __unicode__(self):
+ return self._value.decode(self._encoding, 'ignore')
+ def asOctets(self): return self._value
+ def asNumbers(self):
+ if self.__intValue is None:
+ self.__intValue = tuple([ ord(x) for x in self._value ])
+ return self.__intValue
+ else:
+ def __str__(self): return self._value.decode(self._encoding, 'ignore')
+ def __bytes__(self): return self._value
+ def asOctets(self): return self._value
+ def asNumbers(self):
+ if self.__intValue is None:
+ self.__intValue = tuple(self._value)
+ return self.__intValue
+
+ # Immutable sequence object protocol
+
+ def __len__(self):
+ if self._len is None:
+ self._len = len(self._value)
+ return self._len
+ def __getitem__(self, i):
+ if isinstance(i, slice):
+ return self.clone(operator.getitem(self._value, i))
+ else:
+ return self._value[i]
+
+ def __add__(self, value): return self.clone(self._value + self.prettyIn(value))
+ def __radd__(self, value): return self.clone(self.prettyIn(value) + self._value)
+ def __mul__(self, value): return self.clone(self._value * value)
+ def __rmul__(self, value): return self * value
+
+class Null(OctetString):
+ defaultValue = ''.encode() # This is tightly constrained
+ tagSet = baseTagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05)
+ )
+ subtypeSpec = OctetString.subtypeSpec+constraint.SingleValueConstraint(''.encode())
+
+if sys.version_info[0] <= 2:
+ intTypes = (int, long)
+else:
+ intTypes = int
+
+class ObjectIdentifier(base.AbstractSimpleAsn1Item):
+ tagSet = baseTagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06)
+ )
+ def __add__(self, other): return self.clone(self._value + other)
+ def __radd__(self, other): return self.clone(other + self._value)
+
+ def asTuple(self): return self._value
+
+ # Sequence object protocol
+
+ def __len__(self):
+ if self._len is None:
+ self._len = len(self._value)
+ return self._len
+ def __getitem__(self, i):
+ if isinstance(i, slice):
+ return self.clone(
+ operator.getitem(self._value, i)
+ )
+ else:
+ return self._value[i]
+
+ def __str__(self): return self.prettyPrint()
+
+ def index(self, suboid): return self._value.index(suboid)
+
+ def isPrefixOf(self, value):
+ """Returns true if argument OID resides deeper in the OID tree"""
+ l = len(self)
+ if l <= len(value):
+ if self._value[:l] == value[:l]:
+ return 1
+ return 0
+
+ def prettyIn(self, value):
+ """Dotted -> tuple of numerics OID converter"""
+ if isinstance(value, tuple):
+ pass
+ elif isinstance(value, ObjectIdentifier):
+ return tuple(value)
+ elif isinstance(value, str):
+ r = []
+ for element in [ x for x in value.split('.') if x != '' ]:
+ try:
+ r.append(int(element, 0))
+ except ValueError:
+ raise error.PyAsn1Error(
+ 'Malformed Object ID %s at %s: %s' %
+ (str(value), self.__class__.__name__, sys.exc_info()[1])
+ )
+ value = tuple(r)
+ else:
+ try:
+ value = tuple(value)
+ except TypeError:
+ raise error.PyAsn1Error(
+ 'Malformed Object ID %s at %s: %s' %
+ (str(value), self.__class__.__name__,sys.exc_info()[1])
+ )
+
+ for x in value:
+ if not isinstance(x, intTypes) or x < 0:
+ raise error.PyAsn1Error(
+ 'Invalid sub-ID in %s at %s' % (value, self.__class__.__name__)
+ )
+
+ return value
+
+ def prettyOut(self, value): return '.'.join([ str(x) for x in value ])
+
+class Real(base.AbstractSimpleAsn1Item):
+ try:
+ _plusInf = float('inf')
+ _minusInf = float('-inf')
+ _inf = (_plusInf, _minusInf)
+ except ValueError:
+ # Infinity support is platform and Python dependent
+ _plusInf = _minusInf = None
+ _inf = ()
+
+ tagSet = baseTagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09)
+ )
+
+ def __normalizeBase10(self, value):
+ m, b, e = value
+ while m and m % 10 == 0:
+ m = m / 10
+ e = e + 1
+ return m, b, e
+
+ def prettyIn(self, value):
+ if isinstance(value, tuple) and len(value) == 3:
+ for d in value:
+ if not isinstance(d, intTypes):
+ raise error.PyAsn1Error(
+ 'Lame Real value syntax: %s' % (value,)
+ )
+ if value[1] not in (2, 10):
+ raise error.PyAsn1Error(
+ 'Prohibited base for Real value: %s' % (value[1],)
+ )
+ if value[1] == 10:
+ value = self.__normalizeBase10(value)
+ return value
+ elif isinstance(value, intTypes):
+ return self.__normalizeBase10((value, 10, 0))
+ elif isinstance(value, float):
+ if self._inf and value in self._inf:
+ return value
+ else:
+ e = 0
+ while int(value) != value:
+ value = value * 10
+ e = e - 1
+ return self.__normalizeBase10((int(value), 10, e))
+ elif isinstance(value, Real):
+ return tuple(value)
+ elif isinstance(value, str): # handle infinite literal
+ try:
+ return float(value)
+ except ValueError:
+ pass
+ raise error.PyAsn1Error(
+ 'Bad real value syntax: %s' % (value,)
+ )
+
+ def prettyOut(self, value):
+ if value in self._inf:
+ return '\'%s\'' % value
+ else:
+ return str(value)
+
+ def isPlusInfinity(self): return self._value == self._plusInf
+ def isMinusInfinity(self): return self._value == self._minusInf
+ def isInfinity(self): return self._value in self._inf
+
+ def __str__(self): return str(float(self))
+
+ def __add__(self, value): return self.clone(float(self) + value)
+ def __radd__(self, value): return self + value
+ def __mul__(self, value): return self.clone(float(self) * value)
+ def __rmul__(self, value): return self * value
+ def __sub__(self, value): return self.clone(float(self) - value)
+ def __rsub__(self, value): return self.clone(value - float(self))
+ def __mod__(self, value): return self.clone(float(self) % value)
+ def __rmod__(self, value): return self.clone(value % float(self))
+ def __pow__(self, value, modulo=None): return self.clone(pow(float(self), value, modulo))
+ def __rpow__(self, value): return self.clone(pow(value, float(self)))
+
+ if sys.version_info[0] <= 2:
+ def __div__(self, value): return self.clone(float(self) / value)
+ def __rdiv__(self, value): return self.clone(value / float(self))
+ else:
+ def __truediv__(self, value): return self.clone(float(self) / value)
+ def __rtruediv__(self, value): return self.clone(value / float(self))
+ def __divmod__(self, value): return self.clone(float(self) // value)
+ def __rdivmod__(self, value): return self.clone(value // float(self))
+
+ def __int__(self): return int(float(self))
+ if sys.version_info[0] <= 2:
+ def __long__(self): return long(float(self))
+ def __float__(self):
+ if self._value in self._inf:
+ return self._value
+ else:
+ return float(
+ self._value[0] * pow(self._value[1], self._value[2])
+ )
+ def __abs__(self): return abs(float(self))
+
+ def __lt__(self, value): return float(self) < value
+ def __le__(self, value): return float(self) <= value
+ def __eq__(self, value): return float(self) == value
+ def __ne__(self, value): return float(self) != value
+ def __gt__(self, value): return float(self) > value
+ def __ge__(self, value): return float(self) >= value
+
+ if sys.version_info[0] <= 2:
+ def __nonzero__(self): return bool(float(self))
+ else:
+ def __bool__(self): return bool(float(self))
+ __hash__ = base.AbstractSimpleAsn1Item.__hash__
+
+ def __getitem__(self, idx):
+ if self._value in self._inf:
+ raise error.PyAsn1Error('Invalid infinite value operation')
+ else:
+ return self._value[idx]
+
+class Enumerated(Integer):
+ tagSet = baseTagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x0A)
+ )
+
+# "Structured" ASN.1 types
+
+class SetOf(base.AbstractConstructedAsn1Item):
+ componentType = None
+ tagSet = baseTagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+ )
+ typeId = 1
+
+ def _cloneComponentValues(self, myClone, cloneValueFlag):
+ idx = 0; l = len(self._componentValues)
+ while idx < l:
+ c = self._componentValues[idx]
+ if c is not None:
+ if isinstance(c, base.AbstractConstructedAsn1Item):
+ myClone.setComponentByPosition(
+ idx, c.clone(cloneValueFlag=cloneValueFlag)
+ )
+ else:
+ myClone.setComponentByPosition(idx, c.clone())
+ idx = idx + 1
+
+ def _verifyComponent(self, idx, value):
+ if self._componentType is not None and \
+ not self._componentType.isSuperTypeOf(value):
+ raise error.PyAsn1Error('Component type error %s' % (value,))
+
+ def getComponentByPosition(self, idx): return self._componentValues[idx]
+ def setComponentByPosition(self, idx, value=None, verifyConstraints=True):
+ l = len(self._componentValues)
+ if idx >= l:
+ self._componentValues = self._componentValues + (idx-l+1)*[None]
+ if value is None:
+ if self._componentValues[idx] is None:
+ if self._componentType is None:
+ raise error.PyAsn1Error('Component type not defined')
+ self._componentValues[idx] = self._componentType.clone()
+ self._componentValuesSet = self._componentValuesSet + 1
+ return self
+ elif not isinstance(value, base.Asn1Item):
+ if self._componentType is None:
+ raise error.PyAsn1Error('Component type not defined')
+ if isinstance(self._componentType, base.AbstractSimpleAsn1Item):
+ value = self._componentType.clone(value=value)
+ else:
+ raise error.PyAsn1Error('Instance value required')
+ if verifyConstraints:
+ if self._componentType is not None:
+ self._verifyComponent(idx, value)
+ self._verifySubtypeSpec(value, idx)
+ if self._componentValues[idx] is None:
+ self._componentValuesSet = self._componentValuesSet + 1
+ self._componentValues[idx] = value
+ return self
+
+ def getComponentTagMap(self):
+ if self._componentType is not None:
+ return self._componentType.getTagMap()
+
+ def prettyPrint(self, scope=0):
+ scope = scope + 1
+ r = self.__class__.__name__ + ':\n'
+ for idx in range(len(self._componentValues)):
+ r = r + ' '*scope
+ if self._componentValues[idx] is None:
+ r = r + '<empty>'
+ else:
+ r = r + self._componentValues[idx].prettyPrint(scope)
+ return r
+
+class SequenceOf(SetOf):
+ tagSet = baseTagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
+ )
+ typeId = 2
+
+class SequenceAndSetBase(base.AbstractConstructedAsn1Item):
+ componentType = namedtype.NamedTypes()
+ def __init__(self, componentType=None, tagSet=None,
+ subtypeSpec=None, sizeSpec=None):
+ base.AbstractConstructedAsn1Item.__init__(
+ self, componentType, tagSet, subtypeSpec, sizeSpec
+ )
+ if self._componentType is None:
+ self._componentTypeLen = 0
+ else:
+ self._componentTypeLen = len(self._componentType)
+
+ def __getitem__(self, idx):
+ if isinstance(idx, str):
+ return self.getComponentByName(idx)
+ else:
+ return base.AbstractConstructedAsn1Item.__getitem__(self, idx)
+
+ def __setitem__(self, idx, value):
+ if isinstance(idx, str):
+ self.setComponentByName(idx, value)
+ else:
+ base.AbstractConstructedAsn1Item.__setitem__(self, idx, value)
+
+ def _cloneComponentValues(self, myClone, cloneValueFlag):
+ idx = 0; l = len(self._componentValues)
+ while idx < l:
+ c = self._componentValues[idx]
+ if c is not None:
+ if isinstance(c, base.AbstractConstructedAsn1Item):
+ myClone.setComponentByPosition(
+ idx, c.clone(cloneValueFlag=cloneValueFlag)
+ )
+ else:
+ myClone.setComponentByPosition(idx, c.clone())
+ idx = idx + 1
+
+ def _verifyComponent(self, idx, value):
+ if idx >= self._componentTypeLen:
+ raise error.PyAsn1Error(
+ 'Component type error out of range'
+ )
+ t = self._componentType[idx].getType()
+ if not t.isSuperTypeOf(value):
+ raise error.PyAsn1Error('Component type error %r vs %r' % (t, value))
+
+ def getComponentByName(self, name):
+ return self.getComponentByPosition(
+ self._componentType.getPositionByName(name)
+ )
+ def setComponentByName(self, name, value=None, verifyConstraints=True):
+ return self.setComponentByPosition(
+ self._componentType.getPositionByName(name), value,
+ verifyConstraints
+ )
+
+ def getComponentByPosition(self, idx):
+ try:
+ return self._componentValues[idx]
+ except IndexError:
+ if idx < self._componentTypeLen:
+ return
+ raise
+ def setComponentByPosition(self, idx, value=None, verifyConstraints=True):
+ l = len(self._componentValues)
+ if idx >= l:
+ self._componentValues = self._componentValues + (idx-l+1)*[None]
+ if value is None:
+ if self._componentValues[idx] is None:
+ self._componentValues[idx] = self._componentType.getTypeByPosition(idx).clone()
+ self._componentValuesSet = self._componentValuesSet + 1
+ return self
+ elif not isinstance(value, base.Asn1Item):
+ t = self._componentType.getTypeByPosition(idx)
+ if isinstance(t, base.AbstractSimpleAsn1Item):
+ value = t.clone(value=value)
+ else:
+ raise error.PyAsn1Error('Instance value required')
+ if verifyConstraints:
+ if self._componentTypeLen:
+ self._verifyComponent(idx, value)
+ self._verifySubtypeSpec(value, idx)
+ if self._componentValues[idx] is None:
+ self._componentValuesSet = self._componentValuesSet + 1
+ self._componentValues[idx] = value
+ return self
+
+ def getNameByPosition(self, idx):
+ if self._componentTypeLen:
+ return self._componentType.getNameByPosition(idx)
+
+ def getDefaultComponentByPosition(self, idx):
+ if self._componentTypeLen and self._componentType[idx].isDefaulted:
+ return self._componentType[idx].getType()
+
+ def getComponentType(self):
+ if self._componentTypeLen:
+ return self._componentType
+
+ def setDefaultComponents(self):
+ if self._componentTypeLen == self._componentValuesSet:
+ return
+ idx = self._componentTypeLen
+ while idx:
+ idx = idx - 1
+ if self._componentType[idx].isDefaulted:
+ if self.getComponentByPosition(idx) is None:
+ self.setComponentByPosition(idx)
+ elif not self._componentType[idx].isOptional:
+ if self.getComponentByPosition(idx) is None:
+ raise error.PyAsn1Error(
+ 'Uninitialized component #%s at %r' % (idx, self)
+ )
+
+ def prettyPrint(self, scope=0):
+ scope = scope + 1
+ r = self.__class__.__name__ + ':\n'
+ for idx in range(len(self._componentValues)):
+ if self._componentValues[idx] is not None:
+ r = r + ' '*scope
+ componentType = self.getComponentType()
+ if componentType is None:
+ r = r + '<no-name>'
+ else:
+ r = r + componentType.getNameByPosition(idx)
+ r = '%s=%s\n' % (
+ r, self._componentValues[idx].prettyPrint(scope)
+ )
+ return r
+
+class Sequence(SequenceAndSetBase):
+ tagSet = baseTagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
+ )
+ typeId = 3
+
+ def getComponentTagMapNearPosition(self, idx):
+ if self._componentType:
+ return self._componentType.getTagMapNearPosition(idx)
+
+ def getComponentPositionNearType(self, tagSet, idx):
+ if self._componentType:
+ return self._componentType.getPositionNearType(tagSet, idx)
+ else:
+ return idx
+
+class Set(SequenceAndSetBase):
+ tagSet = baseTagSet = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+ )
+ typeId = 4
+
+ def getComponent(self, innerFlag=0): return self
+
+ def getComponentByType(self, tagSet, innerFlag=0):
+ c = self.getComponentByPosition(
+ self._componentType.getPositionByType(tagSet)
+ )
+ if innerFlag and isinstance(c, Set):
+ # get inner component by inner tagSet
+ return c.getComponent(1)
+ else:
+ # get outer component by inner tagSet
+ return c
+
+ def setComponentByType(self, tagSet, value=None, innerFlag=0,
+ verifyConstraints=True):
+ idx = self._componentType.getPositionByType(tagSet)
+ t = self._componentType.getTypeByPosition(idx)
+ if innerFlag: # set inner component by inner tagSet
+ if t.getTagSet():
+ return self.setComponentByPosition(
+ idx, value, verifyConstraints
+ )
+ else:
+ t = self.setComponentByPosition(idx).getComponentByPosition(idx)
+ return t.setComponentByType(
+ tagSet, value, innerFlag, verifyConstraints
+ )
+ else: # set outer component by inner tagSet
+ return self.setComponentByPosition(
+ idx, value, verifyConstraints
+ )
+
+ def getComponentTagMap(self):
+ if self._componentType:
+ return self._componentType.getTagMap(True)
+
+ def getComponentPositionByType(self, tagSet):
+ if self._componentType:
+ return self._componentType.getPositionByType(tagSet)
+
+class Choice(Set):
+ tagSet = baseTagSet = tag.TagSet() # untagged
+ sizeSpec = constraint.ConstraintsIntersection(
+ constraint.ValueSizeConstraint(1, 1)
+ )
+ typeId = 5
+ _currentIdx = None
+
+ def __eq__(self, other):
+ if self._componentValues:
+ return self._componentValues[self._currentIdx] == other
+ return NotImplemented
+ def __ne__(self, other):
+ if self._componentValues:
+ return self._componentValues[self._currentIdx] != other
+ return NotImplemented
+ def __lt__(self, other):
+ if self._componentValues:
+ return self._componentValues[self._currentIdx] < other
+ return NotImplemented
+ def __le__(self, other):
+ if self._componentValues:
+ return self._componentValues[self._currentIdx] <= other
+ return NotImplemented
+ def __gt__(self, other):
+ if self._componentValues:
+ return self._componentValues[self._currentIdx] > other
+ return NotImplemented
+ def __ge__(self, other):
+ if self._componentValues:
+ return self._componentValues[self._currentIdx] >= other
+ return NotImplemented
+ if sys.version_info[0] <= 2:
+ def __nonzero__(self): return bool(self._componentValues)
+ else:
+ def __bool__(self): return bool(self._componentValues)
+
+ def __len__(self): return self._currentIdx is not None and 1 or 0
+
+ def verifySizeSpec(self):
+ if self._currentIdx is None:
+ raise error.PyAsn1Error('Component not chosen')
+ else:
+ self._sizeSpec(' ')
+
+ def _cloneComponentValues(self, myClone, cloneValueFlag):
+ try:
+ c = self.getComponent()
+ except error.PyAsn1Error:
+ pass
+ else:
+ if isinstance(c, Choice):
+ tagSet = c.getEffectiveTagSet()
+ else:
+ tagSet = c.getTagSet()
+ if isinstance(c, base.AbstractConstructedAsn1Item):
+ myClone.setComponentByType(
+ tagSet, c.clone(cloneValueFlag=cloneValueFlag)
+ )
+ else:
+ myClone.setComponentByType(tagSet, c.clone())
+
+ def setComponentByPosition(self, idx, value=None, verifyConstraints=True):
+ l = len(self._componentValues)
+ if idx >= l:
+ self._componentValues = self._componentValues + (idx-l+1)*[None]
+ if self._currentIdx is not None:
+ self._componentValues[self._currentIdx] = None
+ if value is None:
+ if self._componentValues[idx] is None:
+ self._componentValues[idx] = self._componentType.getTypeByPosition(idx).clone()
+ self._componentValuesSet = 1
+ self._currentIdx = idx
+ return self
+ elif not isinstance(value, base.Asn1Item):
+ value = self._componentType.getTypeByPosition(idx).clone(
+ value=value
+ )
+ if verifyConstraints:
+ if self._componentTypeLen:
+ self._verifyComponent(idx, value)
+ self._verifySubtypeSpec(value, idx)
+ self._componentValues[idx] = value
+ self._currentIdx = idx
+ self._componentValuesSet = 1
+ return self
+
+ def getMinTagSet(self):
+ if self._tagSet:
+ return self._tagSet
+ else:
+ return self._componentType.genMinTagSet()
+
+ def getEffectiveTagSet(self):
+ if self._tagSet:
+ return self._tagSet
+ else:
+ c = self.getComponent()
+ if isinstance(c, Choice):
+ return c.getEffectiveTagSet()
+ else:
+ return c.getTagSet()
+
+ def getTagMap(self):
+ if self._tagSet:
+ return Set.getTagMap(self)
+ else:
+ return Set.getComponentTagMap(self)
+
+ def getComponent(self, innerFlag=0):
+ if self._currentIdx is None:
+ raise error.PyAsn1Error('Component not chosen')
+ else:
+ c = self._componentValues[self._currentIdx]
+ if innerFlag and isinstance(c, Choice):
+ return c.getComponent(innerFlag)
+ else:
+ return c
+
+ def getName(self, innerFlag=0):
+ if self._currentIdx is None:
+ raise error.PyAsn1Error('Component not chosen')
+ else:
+ if innerFlag:
+ c = self._componentValues[self._currentIdx]
+ if isinstance(c, Choice):
+ return c.getName(innerFlag)
+ return self._componentType.getNameByPosition(self._currentIdx)
+
+ def setDefaultComponents(self): pass
+
+class Any(OctetString):
+ tagSet = baseTagSet = tag.TagSet() # untagged
+ typeId = 6
+
+ def getTagMap(self):
+ return tagmap.TagMap(
+ { self.getTagSet(): self },
+ { eoo.endOfOctets.getTagSet(): eoo.endOfOctets },
+ self
+ )
+
+# XXX
+# coercion rules?
diff --git a/python/pyasn1/pyasn1/type/useful.py b/python/pyasn1/pyasn1/type/useful.py
new file mode 100644
index 0000000000..a7139c22ce
--- /dev/null
+++ b/python/pyasn1/pyasn1/type/useful.py
@@ -0,0 +1,12 @@
+# ASN.1 "useful" types
+from pyasn1.type import char, tag
+
+class GeneralizedTime(char.VisibleString):
+ tagSet = char.VisibleString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24)
+ )
+
+class UTCTime(char.VisibleString):
+ tagSet = char.VisibleString.tagSet.tagImplicitly(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23)
+ )
diff --git a/python/pyasn1/setup.cfg b/python/pyasn1/setup.cfg
new file mode 100644
index 0000000000..861a9f5542
--- /dev/null
+++ b/python/pyasn1/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build =
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/python/pyasn1/setup.py b/python/pyasn1/setup.py
new file mode 100644
index 0000000000..194f0c8ca8
--- /dev/null
+++ b/python/pyasn1/setup.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+"""ASN.1 types and codecs
+
+ A pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208).
+"""
+
+import os
+import sys
+
+classifiers = """\
+Development Status :: 5 - Production/Stable
+Environment :: Console
+Intended Audience :: Developers
+Intended Audience :: Education
+Intended Audience :: Information Technology
+Intended Audience :: Science/Research
+Intended Audience :: System Administrators
+Intended Audience :: Telecommunications Industry
+License :: OSI Approved :: BSD License
+Natural Language :: English
+Operating System :: OS Independent
+Programming Language :: Python :: 2
+Programming Language :: Python :: 3
+Topic :: Communications
+Topic :: Security :: Cryptography
+Topic :: Software Development :: Libraries :: Python Modules
+"""
+
+def howto_install_distribute():
+ print("""
+ Error: You need the distribute Python package!
+
+ It's very easy to install it, just type (as root on Linux):
+
+ wget http://python-distribute.org/distribute_setup.py
+ python distribute_setup.py
+
+ Then you could make eggs from this package.
+""")
+
+def howto_install_setuptools():
+ print("""
+ Error: You need setuptools Python package!
+
+ It's very easy to install it, just type (as root on Linux):
+
+ wget http://peak.telecommunity.com/dist/ez_setup.py
+ python ez_setup.py
+
+ Then you could make eggs from this package.
+""")
+
+try:
+ from setuptools import setup, Command
+ params = {
+ 'zip_safe': True
+ }
+except ImportError:
+ for arg in sys.argv:
+ if arg.find('egg') != -1:
+ if sys.version_info[0] > 2:
+ howto_install_distribute()
+ else:
+ howto_install_setuptools()
+ sys.exit(1)
+ from distutils.core import setup, Command
+ params = {}
+
+doclines = [ x.strip() for x in __doc__.split('\n') if x ]
+
+params.update( {
+ 'name': 'pyasn1',
+ 'version': open(os.path.join('pyasn1','__init__.py')).read().split('\'')[1],
+ 'description': doclines[0],
+ 'long_description': ' '.join(doclines[1:]),
+ 'maintainer': 'Ilya Etingof <ilya@glas.net>',
+ 'author': 'Ilya Etingof',
+ 'author_email': 'ilya@glas.net',
+ 'url': 'http://sourceforge.net/projects/pyasn1/',
+ 'platforms': ['any'],
+ 'classifiers': [ x for x in classifiers.split('\n') if x ],
+ 'license': 'BSD',
+ 'packages': [ 'pyasn1',
+ 'pyasn1.type',
+ 'pyasn1.compat',
+ 'pyasn1.codec',
+ 'pyasn1.codec.ber',
+ 'pyasn1.codec.cer',
+ 'pyasn1.codec.der' ]
+} )
+
+# handle unittest discovery feature
+if sys.version_info[0:2] < (2, 7) or \
+ sys.version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ unittest = None
+else:
+ import unittest
+
+if unittest:
+ class PyTest(Command):
+ user_options = []
+
+ def initialize_options(self): pass
+ def finalize_options(self): pass
+
+ def run(self):
+ suite = unittest.defaultTestLoader.discover('.')
+ unittest.TextTestRunner(verbosity=2).run(suite)
+
+ params['cmdclass'] = { 'test': PyTest }
+
+setup(**params)
diff --git a/python/pyasn1/test/__init__.py b/python/pyasn1/test/__init__.py
new file mode 100644
index 0000000000..8c3066b2e6
--- /dev/null
+++ b/python/pyasn1/test/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/python/pyasn1/test/codec/__init__.py b/python/pyasn1/test/codec/__init__.py
new file mode 100644
index 0000000000..8c3066b2e6
--- /dev/null
+++ b/python/pyasn1/test/codec/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/python/pyasn1/test/codec/ber/__init__.py b/python/pyasn1/test/codec/ber/__init__.py
new file mode 100644
index 0000000000..8c3066b2e6
--- /dev/null
+++ b/python/pyasn1/test/codec/ber/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/python/pyasn1/test/codec/ber/suite.py b/python/pyasn1/test/codec/ber/suite.py
new file mode 100644
index 0000000000..796c526b4b
--- /dev/null
+++ b/python/pyasn1/test/codec/ber/suite.py
@@ -0,0 +1,22 @@
+from sys import path, version_info
+from os.path import sep
+path.insert(1, path[0]+sep+'ber')
+import test_encoder, test_decoder
+from pyasn1.error import PyAsn1Error
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+suite = unittest.TestSuite()
+loader = unittest.TestLoader()
+for m in (test_encoder, test_decoder):
+ suite.addTest(loader.loadTestsFromModule(m))
+
+def runTests(): unittest.TextTestRunner(verbosity=2).run(suite)
+
+if __name__ == '__main__': runTests()
diff --git a/python/pyasn1/test/codec/ber/test_decoder.py b/python/pyasn1/test/codec/ber/test_decoder.py
new file mode 100644
index 0000000000..36999e84d4
--- /dev/null
+++ b/python/pyasn1/test/codec/ber/test_decoder.py
@@ -0,0 +1,535 @@
+from pyasn1.type import tag, namedtype, univ
+from pyasn1.codec.ber import decoder
+from pyasn1.compat.octets import ints2octs, str2octs, null
+from pyasn1.error import PyAsn1Error
+from sys import version_info
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+class LargeTagDecoderTestCase(unittest.TestCase):
+ def testLargeTag(self):
+ assert decoder.decode(ints2octs((127, 141, 245, 182, 253, 47, 3, 2, 1, 1))) == (1, null)
+
+class IntegerDecoderTestCase(unittest.TestCase):
+ def testPosInt(self):
+ assert decoder.decode(ints2octs((2, 1, 12))) == (12, null)
+ def testNegInt(self):
+ assert decoder.decode(ints2octs((2, 1, 244))) == (-12, null)
+ def testZero(self):
+ assert decoder.decode(ints2octs((2, 0))) == (0, null)
+ def testZeroLong(self):
+ assert decoder.decode(ints2octs((2, 1, 0))) == (0, null)
+ def testMinusOne(self):
+ assert decoder.decode(ints2octs((2, 1, 255))) == (-1, null)
+ def testPosLong(self):
+ assert decoder.decode(
+ ints2octs((2, 9, 0, 255, 255, 255, 255, 255, 255, 255, 255))
+ ) == (0xffffffffffffffff, null)
+ def testNegLong(self):
+ assert decoder.decode(
+ ints2octs((2, 9, 255, 0, 0, 0, 0, 0, 0, 0, 1))
+ ) == (-0xffffffffffffffff, null)
+ def testSpec(self):
+ try:
+ decoder.decode(
+ ints2octs((2, 1, 12)), asn1Spec=univ.Null()
+ ) == (12, null)
+ except PyAsn1Error:
+ pass
+ else:
+ assert 0, 'wrong asn1Spec worked out'
+ assert decoder.decode(
+ ints2octs((2, 1, 12)), asn1Spec=univ.Integer()
+ ) == (12, null)
+ def testTagFormat(self):
+ try:
+ decoder.decode(ints2octs((34, 1, 12)))
+ except PyAsn1Error:
+ pass
+ else:
+ assert 0, 'wrong tagFormat worked out'
+
+class BooleanDecoderTestCase(unittest.TestCase):
+ def testTrue(self):
+ assert decoder.decode(ints2octs((1, 1, 1))) == (1, null)
+ def testTrueNeg(self):
+ assert decoder.decode(ints2octs((1, 1, 255))) == (1, null)
+ def testExtraTrue(self):
+ assert decoder.decode(ints2octs((1, 1, 1, 0, 120, 50, 50))) == (1, ints2octs((0, 120, 50, 50)))
+ def testFalse(self):
+ assert decoder.decode(ints2octs((1, 1, 0))) == (0, null)
+ def testTagFormat(self):
+ try:
+ decoder.decode(ints2octs((33, 1, 1)))
+ except PyAsn1Error:
+ pass
+ else:
+ assert 0, 'wrong tagFormat worked out'
+
+class BitStringDecoderTestCase(unittest.TestCase):
+ def testDefMode(self):
+ assert decoder.decode(
+ ints2octs((3, 3, 1, 169, 138))
+ ) == ((1,0,1,0,1,0,0,1,1,0,0,0,1,0,1), null)
+ def testIndefMode(self):
+ assert decoder.decode(
+ ints2octs((3, 3, 1, 169, 138))
+ ) == ((1,0,1,0,1,0,0,1,1,0,0,0,1,0,1), null)
+ def testDefModeChunked(self):
+ assert decoder.decode(
+ ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138))
+ ) == ((1,0,1,0,1,0,0,1,1,0,0,0,1,0,1), null)
+ def testIndefModeChunked(self):
+ assert decoder.decode(
+ ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0))
+ ) == ((1,0,1,0,1,0,0,1,1,0,0,0,1,0,1), null)
+ def testDefModeChunkedSubst(self):
+ assert decoder.decode(
+ ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138)),
+ substrateFun=lambda a,b,c: (b,c)
+ ) == (ints2octs((3, 2, 0, 169, 3, 2, 1, 138)), 8)
+ def testIndefModeChunkedSubst(self):
+ assert decoder.decode(
+ ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0)),
+ substrateFun=lambda a,b,c: (b,c)
+ ) == (ints2octs((3, 2, 0, 169, 3, 2, 1, 138, 0, 0)), -1)
+
+class OctetStringDecoderTestCase(unittest.TestCase):
+ def testDefMode(self):
+ assert decoder.decode(
+ ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+ ) == (str2octs('Quick brown fox'), null)
+ def testIndefMode(self):
+ assert decoder.decode(
+ ints2octs((36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0))
+ ) == (str2octs('Quick brown fox'), null)
+ def testDefModeChunked(self):
+ assert decoder.decode(
+ ints2octs((36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120))
+ ) == (str2octs('Quick brown fox'), null)
+ def testIndefModeChunked(self):
+ assert decoder.decode(
+ ints2octs((36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0))
+ ) == (str2octs('Quick brown fox'), null)
+ def testDefModeChunkedSubst(self):
+ assert decoder.decode(
+ ints2octs((36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120)),
+ substrateFun=lambda a,b,c: (b,c)
+ ) == (ints2octs((4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120)), 23)
+ def testIndefModeChunkedSubst(self):
+ assert decoder.decode(
+ ints2octs((36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0)),
+ substrateFun=lambda a,b,c: (b,c)
+ ) == (ints2octs((4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0)), -1)
+
+class ExpTaggedOctetStringDecoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.o = univ.OctetString(
+ 'Quick brown fox',
+ tagSet=univ.OctetString.tagSet.tagExplicitly(
+ tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 5)
+ ))
+
+ def testDefMode(self):
+ assert self.o.isSameTypeWith(decoder.decode(
+ ints2octs((101, 17, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+ )[0])
+
+ def testIndefMode(self):
+ v, s = decoder.decode(ints2octs((101, 128, 36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0, 0, 0)))
+ assert self.o.isSameTypeWith(v)
+ assert not s
+
+ def testDefModeChunked(self):
+ v, s = decoder.decode(ints2octs((101, 25, 36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120)))
+ assert self.o.isSameTypeWith(v)
+ assert not s
+
+ def testIndefModeChunked(self):
+ v, s = decoder.decode(ints2octs((101, 128, 36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0, 0, 0)))
+ assert self.o.isSameTypeWith(v)
+ assert not s
+
+ def testDefModeSubst(self):
+ assert decoder.decode(
+ ints2octs((101, 17, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)),
+ substrateFun=lambda a,b,c: (b,c)
+ ) == (ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)), 17)
+
+ def testIndefModeSubst(self):
+ assert decoder.decode(
+ ints2octs((101, 128, 36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0, 0, 0)),
+ substrateFun=lambda a,b,c: (b,c)
+ ) == (ints2octs((36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0, 0, 0)), -1)
+
+class NullDecoderTestCase(unittest.TestCase):
+ def testNull(self):
+ assert decoder.decode(ints2octs((5, 0))) == (null, null)
+ def testTagFormat(self):
+ try:
+ decoder.decode(ints2octs((37, 0)))
+ except PyAsn1Error:
+ pass
+ else:
+ assert 0, 'wrong tagFormat worked out'
+
+class ObjectIdentifierDecoderTestCase(unittest.TestCase):
+ def testOID(self):
+ assert decoder.decode(
+ ints2octs((6, 6, 43, 6, 0, 191, 255, 126))
+ ) == ((1,3,6,0,0xffffe), null)
+
+ def testEdges1(self):
+ assert decoder.decode(
+ ints2octs((6, 1, 255))
+ ) == ((6,15), null)
+
+ def testEdges2(self):
+ assert decoder.decode(
+ ints2octs((6, 1, 239))
+ ) == ((5,39), null)
+
+ def testEdges3(self):
+ assert decoder.decode(
+ ints2octs((6, 7, 43, 6, 143, 255, 255, 255, 127))
+ ) == ((1, 3, 6, 4294967295), null)
+
+ def testNonLeading0x80(self):
+ assert decoder.decode(
+ ints2octs((6, 5, 85, 4, 129, 128, 0)),
+ ) == ((2, 5, 4, 16384), null)
+
+ def testLeading0x80(self):
+ try:
+ decoder.decode(
+ ints2octs((6, 5, 85, 4, 128, 129, 0))
+ )
+ except PyAsn1Error:
+ pass
+ else:
+ assert 1, 'Leading 0x80 tolarated'
+
+ def testTagFormat(self):
+ try:
+ decoder.decode(ints2octs((38, 1, 239)))
+ except PyAsn1Error:
+ pass
+ else:
+ assert 0, 'wrong tagFormat worked out'
+
+class RealDecoderTestCase(unittest.TestCase):
+ def testChar(self):
+ assert decoder.decode(
+ ints2octs((9, 7, 3, 49, 50, 51, 69, 49, 49))
+ ) == (univ.Real((123, 10, 11)), null)
+
+ def testBin1(self):
+ assert decoder.decode(
+ ints2octs((9, 4, 128, 245, 4, 77))
+ ) == (univ.Real((1101, 2, -11)), null)
+
+ def testBin2(self):
+ assert decoder.decode(
+ ints2octs((9, 4, 128, 11, 4, 77))
+ ) == (univ.Real((1101, 2, 11)), null)
+
+ def testBin3(self):
+ assert decoder.decode(
+ ints2octs((9, 3, 192, 10, 123))
+ ) == (univ.Real((-123, 2, 10)), null)
+
+
+ def testPlusInf(self):
+ assert decoder.decode(
+ ints2octs((9, 1, 64))
+ ) == (univ.Real('inf'), null)
+
+ def testMinusInf(self):
+ assert decoder.decode(
+ ints2octs((9, 1, 65))
+ ) == (univ.Real('-inf'), null)
+
+ def testEmpty(self):
+ assert decoder.decode(
+ ints2octs((9, 0))
+ ) == (univ.Real(0.0), null)
+
+ def testTagFormat(self):
+ try:
+ decoder.decode(ints2octs((41, 0)))
+ except PyAsn1Error:
+ pass
+ else:
+ assert 0, 'wrong tagFormat worked out'
+
+class SequenceDecoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.s = univ.Sequence(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null(null)),
+ namedtype.NamedType('first-name', univ.OctetString(null)),
+ namedtype.NamedType('age', univ.Integer(33)),
+ ))
+ self.s.setComponentByPosition(0, univ.Null(null))
+ self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+ self.s.setComponentByPosition(2, univ.Integer(1))
+ self.s.setDefaultComponents()
+
+ def testWithOptionalAndDefaultedDefMode(self):
+ assert decoder.decode(
+ ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+ ) == (self.s, null)
+
+ def testWithOptionalAndDefaultedIndefMode(self):
+ assert decoder.decode(
+ ints2octs((48, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0))
+ ) == (self.s, null)
+
+ def testWithOptionalAndDefaultedDefModeChunked(self):
+ assert decoder.decode(
+ ints2octs((48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1))
+ ) == (self.s, null)
+
+ def testWithOptionalAndDefaultedIndefModeChunked(self):
+ assert decoder.decode(
+ ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0))
+ ) == (self.s, null)
+
+ def testWithOptionalAndDefaultedDefModeSubst(self):
+ assert decoder.decode(
+ ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)),
+ substrateFun=lambda a,b,c: (b,c)
+ ) == (ints2octs((5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)), 18)
+
+ def testWithOptionalAndDefaultedIndefModeSubst(self):
+ assert decoder.decode(
+ ints2octs((48, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)),
+ substrateFun=lambda a,b,c: (b,c)
+ ) == (ints2octs((5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), -1)
+
+ def testTagFormat(self):
+ try:
+ decoder.decode(
+ ints2octs((16, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+ )
+ except PyAsn1Error:
+ pass
+ else:
+ assert 0, 'wrong tagFormat worked out'
+
+class GuidedSequenceDecoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.s = univ.Sequence(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null(null)),
+ namedtype.OptionalNamedType('first-name', univ.OctetString(null)),
+ namedtype.DefaultedNamedType('age', univ.Integer(33)),
+ ))
+
+ def __init(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0, univ.Null(null))
+ self.s.setDefaultComponents()
+
+ def __initWithOptional(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0, univ.Null(null))
+ self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+ self.s.setDefaultComponents()
+
+ def __initWithDefaulted(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0, univ.Null(null))
+ self.s.setComponentByPosition(2, univ.Integer(1))
+ self.s.setDefaultComponents()
+
+ def __initWithOptionalAndDefaulted(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0, univ.Null(null))
+ self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+ self.s.setComponentByPosition(2, univ.Integer(1))
+ self.s.setDefaultComponents()
+
+ def testDefMode(self):
+ self.__init()
+ assert decoder.decode(
+ ints2octs((48, 128, 5, 0, 0, 0)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testIndefMode(self):
+ self.__init()
+ assert decoder.decode(
+ ints2octs((48, 128, 5, 0, 0, 0)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testDefModeChunked(self):
+ self.__init()
+ assert decoder.decode(
+ ints2octs((48, 2, 5, 0)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testIndefModeChunked(self):
+ self.__init()
+ assert decoder.decode(
+ ints2octs((48, 128, 5, 0, 0, 0)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithOptionalDefMode(self):
+ self.__initWithOptional()
+ assert decoder.decode(
+ ints2octs((48, 15, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithOptionaIndefMode(self):
+ self.__initWithOptional()
+ assert decoder.decode(
+ ints2octs((48, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 0, 0)),
+ asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithOptionalDefModeChunked(self):
+ self.__initWithOptional()
+ assert decoder.decode(
+ ints2octs((48, 21, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)),
+ asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithOptionalIndefModeChunked(self):
+ self.__initWithOptional()
+ assert decoder.decode(
+ ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)),
+ asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithDefaultedDefMode(self):
+ self.__initWithDefaulted()
+ assert decoder.decode(
+ ints2octs((48, 5, 5, 0, 2, 1, 1)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithDefaultedIndefMode(self):
+ self.__initWithDefaulted()
+ assert decoder.decode(
+ ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithDefaultedDefModeChunked(self):
+ self.__initWithDefaulted()
+ assert decoder.decode(
+ ints2octs((48, 5, 5, 0, 2, 1, 1)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithDefaultedIndefModeChunked(self):
+ self.__initWithDefaulted()
+ assert decoder.decode(
+ ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithOptionalAndDefaultedDefMode(self):
+ self.__initWithOptionalAndDefaulted()
+ assert decoder.decode(
+ ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithOptionalAndDefaultedIndefMode(self):
+ self.__initWithOptionalAndDefaulted()
+ assert decoder.decode(
+ ints2octs((48, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithOptionalAndDefaultedDefModeChunked(self):
+ self.__initWithOptionalAndDefaulted()
+ assert decoder.decode(
+ ints2octs((48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithOptionalAndDefaultedIndefModeChunked(self):
+ self.__initWithOptionalAndDefaulted()
+ assert decoder.decode(
+ ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s
+ ) == (self.s, null)
+
+class ChoiceDecoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.s = univ.Choice(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null(null)),
+ namedtype.NamedType('number', univ.Integer(0)),
+ namedtype.NamedType('string', univ.OctetString())
+ ))
+
+ def testBySpec(self):
+ self.s.setComponentByPosition(0, univ.Null(null))
+ assert decoder.decode(
+ ints2octs((5, 0)), asn1Spec=self.s
+ ) == (self.s, null)
+
+ def testWithoutSpec(self):
+ self.s.setComponentByPosition(0, univ.Null(null))
+ assert decoder.decode(ints2octs((5, 0))) == (self.s, null)
+ assert decoder.decode(ints2octs((5, 0))) == (univ.Null(null), null)
+
+ def testUndefLength(self):
+ self.s.setComponentByPosition(2, univ.OctetString('abcdefgh'))
+ assert decoder.decode(ints2octs((36, 128, 4, 3, 97, 98, 99, 4, 3, 100, 101, 102, 4, 2, 103, 104, 0, 0)), asn1Spec=self.s) == (self.s, null)
+
+ def testExplicitTag(self):
+ s = self.s.subtype(explicitTag=tag.Tag(tag.tagClassContext,
+ tag.tagFormatConstructed, 4))
+ s.setComponentByPosition(0, univ.Null(null))
+ assert decoder.decode(ints2octs((164, 2, 5, 0)), asn1Spec=s) == (s, null)
+
+ def testExplicitTagUndefLength(self):
+ s = self.s.subtype(explicitTag=tag.Tag(tag.tagClassContext,
+ tag.tagFormatConstructed, 4))
+ s.setComponentByPosition(0, univ.Null(null))
+ assert decoder.decode(ints2octs((164, 128, 5, 0, 0, 0)), asn1Spec=s) == (s, null)
+
+class AnyDecoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.s = univ.Any()
+
+ def testByUntagged(self):
+ assert decoder.decode(
+ ints2octs((4, 3, 102, 111, 120)), asn1Spec=self.s
+ ) == (univ.Any('\004\003fox'), null)
+
+ def testTaggedEx(self):
+ s = univ.Any('\004\003fox').subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
+ assert decoder.decode(ints2octs((164, 5, 4, 3, 102, 111, 120)), asn1Spec=s) == (s, null)
+
+ def testTaggedIm(self):
+ s = univ.Any('\004\003fox').subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
+ assert decoder.decode(ints2octs((132, 5, 4, 3, 102, 111, 120)), asn1Spec=s) == (s, null)
+
+ def testByUntaggedIndefMode(self):
+ assert decoder.decode(
+ ints2octs((4, 3, 102, 111, 120)), asn1Spec=self.s
+ ) == (univ.Any('\004\003fox'), null)
+
+ def testTaggedExIndefMode(self):
+ s = univ.Any('\004\003fox').subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
+ assert decoder.decode(ints2octs((164, 128, 4, 3, 102, 111, 120, 0, 0)), asn1Spec=s) == (s, null)
+
+ def testTaggedImIndefMode(self):
+ s = univ.Any('\004\003fox').subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
+ assert decoder.decode(ints2octs((164, 128, 4, 3, 102, 111, 120, 0, 0)), asn1Spec=s) == (s, null)
+
+ def testByUntaggedSubst(self):
+ assert decoder.decode(
+ ints2octs((4, 3, 102, 111, 120)),
+ asn1Spec=self.s,
+ substrateFun=lambda a,b,c: (b,c)
+ ) == (ints2octs((4, 3, 102, 111, 120)), 5)
+
+ def testTaggedExSubst(self):
+ assert decoder.decode(
+ ints2octs((164, 5, 4, 3, 102, 111, 120)),
+ asn1Spec=self.s,
+ substrateFun=lambda a,b,c: (b,c)
+ ) == (ints2octs((164, 5, 4, 3, 102, 111, 120)), 7)
+
+if __name__ == '__main__': unittest.main()
diff --git a/python/pyasn1/test/codec/ber/test_encoder.py b/python/pyasn1/test/codec/ber/test_encoder.py
new file mode 100644
index 0000000000..bfb3f618c7
--- /dev/null
+++ b/python/pyasn1/test/codec/ber/test_encoder.py
@@ -0,0 +1,338 @@
+from pyasn1.type import tag, namedtype, univ
+from pyasn1.codec.ber import encoder
+from pyasn1.compat.octets import ints2octs
+from pyasn1.error import PyAsn1Error
+from sys import version_info
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+class LargeTagEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.o = univ.Integer().subtype(
+ value=1, explicitTag=tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 0xdeadbeaf)
+ )
+ def testEncoder(self):
+ assert encoder.encode(self.o) == ints2octs((127, 141, 245, 182, 253, 47, 3, 2, 1, 1))
+
+class IntegerEncoderTestCase(unittest.TestCase):
+ def testPosInt(self):
+ assert encoder.encode(univ.Integer(12)) == ints2octs((2, 1, 12))
+
+ def testNegInt(self):
+ assert encoder.encode(univ.Integer(-12)) == ints2octs((2, 1, 244))
+
+ def testZero(self):
+ assert encoder.encode(univ.Integer(0)) == ints2octs((2, 1, 0))
+
+ def testCompactZero(self):
+ encoder.IntegerEncoder.supportCompactZero = True
+ substrate = encoder.encode(univ.Integer(0))
+ encoder.IntegerEncoder.supportCompactZero = False
+ assert substrate == ints2octs((2, 0))
+
+ def testMinusOne(self):
+ assert encoder.encode(univ.Integer(-1)) == ints2octs((2, 1, 255))
+
+ def testPosLong(self):
+ assert encoder.encode(
+ univ.Integer(0xffffffffffffffff)
+ ) == ints2octs((2, 9, 0, 255, 255, 255, 255, 255, 255, 255, 255))
+
+ def testNegLong(self):
+ assert encoder.encode(
+ univ.Integer(-0xffffffffffffffff)
+ ) == ints2octs((2, 9, 255, 0, 0, 0, 0, 0, 0, 0, 1))
+
+class BooleanEncoderTestCase(unittest.TestCase):
+ def testTrue(self):
+ assert encoder.encode(univ.Boolean(1)) == ints2octs((1, 1, 1))
+
+ def testFalse(self):
+ assert encoder.encode(univ.Boolean(0)) == ints2octs((1, 1, 0))
+
+class BitStringEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.b = univ.BitString((1,0,1,0,1,0,0,1,1,0,0,0,1,0,1))
+
+ def testDefMode(self):
+ assert encoder.encode(self.b) == ints2octs((3, 3, 1, 169, 138))
+
+ def testIndefMode(self):
+ assert encoder.encode(
+ self.b, defMode=0
+ ) == ints2octs((3, 3, 1, 169, 138))
+
+ def testDefModeChunked(self):
+ assert encoder.encode(
+ self.b, maxChunkSize=1
+ ) == ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138))
+
+ def testIndefModeChunked(self):
+ assert encoder.encode(
+ self.b, defMode=0, maxChunkSize=1
+ ) == ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0))
+
+ def testEmptyValue(self):
+ assert encoder.encode(univ.BitString(())) == ints2octs((3, 1, 0))
+
+class OctetStringEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.o = univ.OctetString('Quick brown fox')
+
+ def testDefMode(self):
+ assert encoder.encode(self.o) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+
+ def testIndefMode(self):
+ assert encoder.encode(
+ self.o, defMode=0
+ ) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+
+ def testDefModeChunked(self):
+ assert encoder.encode(
+ self.o, maxChunkSize=4
+ ) == ints2octs((36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120))
+
+ def testIndefModeChunked(self):
+ assert encoder.encode(
+ self.o, defMode=0, maxChunkSize=4
+ ) == ints2octs((36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0))
+
+class ExpTaggedOctetStringEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.o = univ.OctetString().subtype(
+ value='Quick brown fox',
+ explicitTag=tag.Tag(tag.tagClassApplication,tag.tagFormatSimple,5)
+ )
+ def testDefMode(self):
+ assert encoder.encode(self.o) == ints2octs((101, 17, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+
+ def testIndefMode(self):
+ assert encoder.encode(
+ self.o, defMode=0
+ ) == ints2octs((101, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0))
+
+ def testDefModeChunked(self):
+ assert encoder.encode(
+ self.o, defMode=1, maxChunkSize=4
+ ) == ints2octs((101, 25, 36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120))
+
+ def testIndefModeChunked(self):
+ assert encoder.encode(
+ self.o, defMode=0, maxChunkSize=4
+ ) == ints2octs((101, 128, 36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0, 0, 0))
+
+class NullEncoderTestCase(unittest.TestCase):
+ def testNull(self):
+ assert encoder.encode(univ.Null('')) == ints2octs((5, 0))
+
+class ObjectIdentifierEncoderTestCase(unittest.TestCase):
+ def testNull(self):
+ assert encoder.encode(
+ univ.ObjectIdentifier((1,3,6,0,0xffffe))
+ ) == ints2octs((6, 6, 43, 6, 0, 191, 255, 126))
+
+class RealEncoderTestCase(unittest.TestCase):
+ def testChar(self):
+ assert encoder.encode(
+ univ.Real((123, 10, 11))
+ ) == ints2octs((9, 7, 3, 49, 50, 51, 69, 49, 49))
+
+ def testBin1(self):
+ assert encoder.encode(
+ univ.Real((1101, 2, 11))
+ ) == ints2octs((9, 4, 128, 11, 4, 77))
+
+ def testBin2(self):
+ assert encoder.encode(
+ univ.Real((1101, 2, -11))
+ ) == ints2octs((9, 4, 128, 245, 4, 77))
+
+ def testPlusInf(self):
+ assert encoder.encode(univ.Real('inf')) == ints2octs((9, 1, 64))
+
+ def testMinusInf(self):
+ assert encoder.encode(univ.Real('-inf')) == ints2octs((9, 1, 65))
+
+ def testZero(self):
+ assert encoder.encode(univ.Real(0)) == ints2octs((9, 0))
+
+class SequenceEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.s = univ.Sequence(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null('')),
+ namedtype.OptionalNamedType('first-name', univ.OctetString('')),
+ namedtype.DefaultedNamedType('age', univ.Integer(33)),
+ ))
+
+ def __init(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0)
+
+ def __initWithOptional(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0)
+ self.s.setComponentByPosition(1, 'quick brown')
+
+ def __initWithDefaulted(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0)
+ self.s.setComponentByPosition(2, 1)
+
+ def __initWithOptionalAndDefaulted(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0, univ.Null(''))
+ self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+ self.s.setComponentByPosition(2, univ.Integer(1))
+
+ def testDefMode(self):
+ self.__init()
+ assert encoder.encode(self.s) == ints2octs((48, 2, 5, 0))
+
+ def testIndefMode(self):
+ self.__init()
+ assert encoder.encode(
+ self.s, defMode=0
+ ) == ints2octs((48, 128, 5, 0, 0, 0))
+
+ def testDefModeChunked(self):
+ self.__init()
+ assert encoder.encode(
+ self.s, defMode=1, maxChunkSize=4
+ ) == ints2octs((48, 2, 5, 0))
+
+ def testIndefModeChunked(self):
+ self.__init()
+ assert encoder.encode(
+ self.s, defMode=0, maxChunkSize=4
+ ) == ints2octs((48, 128, 5, 0, 0, 0))
+
+ def testWithOptionalDefMode(self):
+ self.__initWithOptional()
+ assert encoder.encode(self.s) == ints2octs((48, 15, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+ def testWithOptionalIndefMode(self):
+ self.__initWithOptional()
+ assert encoder.encode(
+ self.s, defMode=0
+ ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0))
+
+ def testWithOptionalDefModeChunked(self):
+ self.__initWithOptional()
+ assert encoder.encode(
+ self.s, defMode=1, maxChunkSize=4
+ ) == ints2octs((48, 21, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110))
+
+ def testWithOptionalIndefModeChunked(self):
+ self.__initWithOptional()
+ assert encoder.encode(
+ self.s, defMode=0, maxChunkSize=4
+ ) == ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0))
+
+ def testWithDefaultedDefMode(self):
+ self.__initWithDefaulted()
+ assert encoder.encode(self.s) == ints2octs((48, 5, 5, 0, 2, 1, 1))
+
+ def testWithDefaultedIndefMode(self):
+ self.__initWithDefaulted()
+ assert encoder.encode(
+ self.s, defMode=0
+ ) == ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0))
+
+ def testWithDefaultedDefModeChunked(self):
+ self.__initWithDefaulted()
+ assert encoder.encode(
+ self.s, defMode=1, maxChunkSize=4
+ ) == ints2octs((48, 5, 5, 0, 2, 1, 1))
+
+ def testWithDefaultedIndefModeChunked(self):
+ self.__initWithDefaulted()
+ assert encoder.encode(
+ self.s, defMode=0, maxChunkSize=4
+ ) == ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0))
+
+ def testWithOptionalAndDefaultedDefMode(self):
+ self.__initWithOptionalAndDefaulted()
+ assert encoder.encode(self.s) == ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1))
+
+ def testWithOptionalAndDefaultedIndefMode(self):
+ self.__initWithOptionalAndDefaulted()
+ assert encoder.encode(
+ self.s, defMode=0
+ ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0))
+
+ def testWithOptionalAndDefaultedDefModeChunked(self):
+ self.__initWithOptionalAndDefaulted()
+ assert encoder.encode(
+ self.s, defMode=1, maxChunkSize=4
+ ) == ints2octs((48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1))
+
+ def testWithOptionalAndDefaultedIndefModeChunked(self):
+ self.__initWithOptionalAndDefaulted()
+ assert encoder.encode(
+ self.s, defMode=0, maxChunkSize=4
+ ) == ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0))
+
+class ChoiceEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.s = univ.Choice(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null('')),
+ namedtype.NamedType('number', univ.Integer(0)),
+ namedtype.NamedType('string', univ.OctetString())
+ ))
+
+ def testEmpty(self):
+ try:
+ encoder.encode(self.s)
+ except PyAsn1Error:
+ pass
+ else:
+ assert 0, 'encoded unset choice'
+
+ def testFilled(self):
+ self.s.setComponentByPosition(0, univ.Null(''))
+ assert encoder.encode(self.s) == ints2octs((5, 0))
+
+ def testTagged(self):
+ s = self.s.subtype(
+ explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,4)
+ )
+ s.setComponentByPosition(0, univ.Null(''))
+ assert encoder.encode(s) == ints2octs((164, 2, 5, 0))
+
+ def testUndefLength(self):
+ self.s.setComponentByPosition(2, univ.OctetString('abcdefgh'))
+ assert encoder.encode(self.s, defMode=False, maxChunkSize=3) == ints2octs((36, 128, 4, 3, 97, 98, 99, 4, 3, 100, 101, 102, 4, 2, 103, 104, 0, 0))
+
+ def testTaggedUndefLength(self):
+ s = self.s.subtype(
+ explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,4)
+ )
+ s.setComponentByPosition(2, univ.OctetString('abcdefgh'))
+ assert encoder.encode(s, defMode=False, maxChunkSize=3) == ints2octs((164, 128, 36, 128, 4, 3, 97, 98, 99, 4, 3, 100, 101, 102, 4, 2, 103, 104, 0, 0, 0, 0))
+
+class AnyEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.s = univ.Any(encoder.encode(univ.OctetString('fox')))
+
+ def testUntagged(self):
+ assert encoder.encode(self.s) == ints2octs((4, 3, 102, 111, 120))
+
+ def testTaggedEx(self):
+ s = self.s.subtype(
+ explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)
+ )
+ assert encoder.encode(s) == ints2octs((164, 5, 4, 3, 102, 111, 120))
+
+ def testTaggedIm(self):
+ s = self.s.subtype(
+ implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)
+ )
+ assert encoder.encode(s) == ints2octs((132, 5, 4, 3, 102, 111, 120))
+
+if __name__ == '__main__': unittest.main()
diff --git a/python/pyasn1/test/codec/cer/__init__.py b/python/pyasn1/test/codec/cer/__init__.py
new file mode 100644
index 0000000000..8c3066b2e6
--- /dev/null
+++ b/python/pyasn1/test/codec/cer/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/python/pyasn1/test/codec/cer/suite.py b/python/pyasn1/test/codec/cer/suite.py
new file mode 100644
index 0000000000..49d682918b
--- /dev/null
+++ b/python/pyasn1/test/codec/cer/suite.py
@@ -0,0 +1,22 @@
+from sys import path, version_info
+from os.path import sep
+path.insert(1, path[0]+sep+'cer')
+import test_encoder, test_decoder
+from pyasn1.error import PyAsn1Error
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+suite = unittest.TestSuite()
+loader = unittest.TestLoader()
+for m in (test_encoder, test_decoder):
+ suite.addTest(loader.loadTestsFromModule(m))
+
+def runTests(): unittest.TextTestRunner(verbosity=2).run(suite)
+
+if __name__ == '__main__': runTests()
diff --git a/python/pyasn1/test/codec/cer/test_decoder.py b/python/pyasn1/test/codec/cer/test_decoder.py
new file mode 100644
index 0000000000..7195b72e09
--- /dev/null
+++ b/python/pyasn1/test/codec/cer/test_decoder.py
@@ -0,0 +1,31 @@
+from pyasn1.type import univ
+from pyasn1.codec.cer import decoder
+from pyasn1.compat.octets import ints2octs, str2octs, null
+from pyasn1.error import PyAsn1Error
+from sys import version_info
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+class BooleanDecoderTestCase(unittest.TestCase):
+ def testTrue(self):
+ assert decoder.decode(ints2octs((1, 1, 255))) == (1, null)
+ def testFalse(self):
+ assert decoder.decode(ints2octs((1, 1, 0))) == (0, null)
+
+class OctetStringDecoderTestCase(unittest.TestCase):
+ def testShortMode(self):
+ assert decoder.decode(
+ ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)),
+ ) == (str2octs('Quick brown fox'), null)
+ def testLongMode(self):
+ assert decoder.decode(
+ ints2octs((36, 128, 4, 130, 3, 232) + (81,)*1000 + (4, 1, 81, 0, 0))
+ ) == (str2octs('Q'*1001), null)
+
+if __name__ == '__main__': unittest.main()
diff --git a/python/pyasn1/test/codec/cer/test_encoder.py b/python/pyasn1/test/codec/cer/test_encoder.py
new file mode 100644
index 0000000000..a4f80aa20e
--- /dev/null
+++ b/python/pyasn1/test/codec/cer/test_encoder.py
@@ -0,0 +1,107 @@
+from pyasn1.type import namedtype, univ
+from pyasn1.codec.cer import encoder
+from pyasn1.compat.octets import ints2octs
+from pyasn1.error import PyAsn1Error
+from sys import version_info
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+class BooleanEncoderTestCase(unittest.TestCase):
+ def testTrue(self):
+ assert encoder.encode(univ.Boolean(1)) == ints2octs((1, 1, 255))
+ def testFalse(self):
+ assert encoder.encode(univ.Boolean(0)) == ints2octs((1, 1, 0))
+
+class BitStringEncoderTestCase(unittest.TestCase):
+ def testShortMode(self):
+ assert encoder.encode(
+ univ.BitString((1,0)*501)
+ ) == ints2octs((3, 127, 6) + (170,) * 125 + (128,))
+
+ def testLongMode(self):
+ assert encoder.encode(
+ univ.BitString((1,0)*501)
+ ) == ints2octs((3, 127, 6) + (170,) * 125 + (128,))
+
+class OctetStringEncoderTestCase(unittest.TestCase):
+ def testShortMode(self):
+ assert encoder.encode(
+ univ.OctetString('Quick brown fox')
+ ) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+ def testLongMode(self):
+ assert encoder.encode(
+ univ.OctetString('Q'*1001)
+ ) == ints2octs((36, 128, 4, 130, 3, 232) + (81,)*1000 + (4, 1, 81, 0, 0))
+
+class SetEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ self.s = univ.Set(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null('')),
+ namedtype.OptionalNamedType('first-name', univ.OctetString('')),
+ namedtype.DefaultedNamedType('age', univ.Integer(33))
+ ))
+
+ def __init(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0)
+ def __initWithOptional(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0)
+ self.s.setComponentByPosition(1, 'quick brown')
+
+ def __initWithDefaulted(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0)
+ self.s.setComponentByPosition(2, 1)
+
+ def __initWithOptionalAndDefaulted(self):
+ self.s.clear()
+ self.s.setComponentByPosition(0, univ.Null(''))
+ self.s.setComponentByPosition(1, univ.OctetString('quick brown'))
+ self.s.setComponentByPosition(2, univ.Integer(1))
+
+ def testIndefMode(self):
+ self.__init()
+ assert encoder.encode(self.s) == ints2octs((49, 128, 5, 0, 0, 0))
+
+ def testWithOptionalIndefMode(self):
+ self.__initWithOptional()
+ assert encoder.encode(
+ self.s
+ ) == ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0))
+
+ def testWithDefaultedIndefMode(self):
+ self.__initWithDefaulted()
+ assert encoder.encode(
+ self.s
+ ) == ints2octs((49, 128, 2, 1, 1, 5, 0, 0, 0))
+
+ def testWithOptionalAndDefaultedIndefMode(self):
+ self.__initWithOptionalAndDefaulted()
+ assert encoder.encode(
+ self.s
+ ) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0))
+
+class SetWithChoiceEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ c = univ.Choice(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('actual', univ.Boolean(0))
+ ))
+ self.s = univ.Set(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null('')),
+ namedtype.NamedType('status', c)
+ ))
+
+ def testIndefMode(self):
+ self.s.setComponentByPosition(0)
+ self.s.setComponentByName('status')
+ self.s.getComponentByName('status').setComponentByPosition(0, 1)
+ assert encoder.encode(self.s) == ints2octs((49, 128, 1, 1, 255, 5, 0, 0, 0))
+
+if __name__ == '__main__': unittest.main()
diff --git a/python/pyasn1/test/codec/der/__init__.py b/python/pyasn1/test/codec/der/__init__.py
new file mode 100644
index 0000000000..8c3066b2e6
--- /dev/null
+++ b/python/pyasn1/test/codec/der/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/python/pyasn1/test/codec/der/suite.py b/python/pyasn1/test/codec/der/suite.py
new file mode 100644
index 0000000000..7af83bf94f
--- /dev/null
+++ b/python/pyasn1/test/codec/der/suite.py
@@ -0,0 +1,22 @@
+from sys import path, version_info
+from os.path import sep
+path.insert(1, path[0]+sep+'der')
+import test_encoder, test_decoder
+from pyasn1.error import PyAsn1Error
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+suite = unittest.TestSuite()
+loader = unittest.TestLoader()
+for m in (test_encoder, test_decoder):
+ suite.addTest(loader.loadTestsFromModule(m))
+
+def runTests(): unittest.TextTestRunner(verbosity=2).run(suite)
+
+if __name__ == '__main__': runTests()
diff --git a/python/pyasn1/test/codec/der/test_decoder.py b/python/pyasn1/test/codec/der/test_decoder.py
new file mode 100644
index 0000000000..5c9a1948b9
--- /dev/null
+++ b/python/pyasn1/test/codec/der/test_decoder.py
@@ -0,0 +1,20 @@
+from pyasn1.type import univ
+from pyasn1.codec.der import decoder
+from pyasn1.error import PyAsn1Error
+from sys import version_info
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+class OctetStringDecoderTestCase(unittest.TestCase):
+ def testShortMode(self):
+ assert decoder.decode(
+ '\004\017Quick brown fox'.encode()
+ ) == ('Quick brown fox'.encode(), ''.encode())
+
+if __name__ == '__main__': unittest.main()
diff --git a/python/pyasn1/test/codec/der/test_encoder.py b/python/pyasn1/test/codec/der/test_encoder.py
new file mode 100644
index 0000000000..787da7bec3
--- /dev/null
+++ b/python/pyasn1/test/codec/der/test_encoder.py
@@ -0,0 +1,44 @@
+from pyasn1.type import namedtype, univ
+from pyasn1.codec.der import encoder
+from pyasn1.compat.octets import ints2octs
+from pyasn1.error import PyAsn1Error
+from sys import version_info
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+class OctetStringEncoderTestCase(unittest.TestCase):
+ def testShortMode(self):
+ assert encoder.encode(
+ univ.OctetString('Quick brown fox')
+ ) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120))
+
+class BitStringEncoderTestCase(unittest.TestCase):
+ def testShortMode(self):
+ assert encoder.encode(
+ univ.BitString((1,))
+ ) == ints2octs((3, 2, 7, 128))
+
+class SetWithChoiceEncoderTestCase(unittest.TestCase):
+ def setUp(self):
+ c = univ.Choice(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('name', univ.OctetString('')),
+ namedtype.NamedType('amount', univ.Integer(0))
+ ))
+ self.s = univ.Set(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('place-holder', univ.Null('')),
+ namedtype.NamedType('status', c)
+ ))
+
+ def testDefMode(self):
+ self.s.setComponentByPosition(0)
+ self.s.setComponentByName('status')
+ self.s.getComponentByName('status').setComponentByPosition(0, 'ann')
+ assert encoder.encode(self.s) == ints2octs((49, 7, 4, 3, 97, 110, 110, 5, 0))
+
+if __name__ == '__main__': unittest.main()
diff --git a/python/pyasn1/test/codec/suite.py b/python/pyasn1/test/codec/suite.py
new file mode 100644
index 0000000000..93ff063818
--- /dev/null
+++ b/python/pyasn1/test/codec/suite.py
@@ -0,0 +1,29 @@
+from sys import path, version_info
+from os.path import sep
+path.insert(1, path[0]+sep+'codec'+sep+'ber')
+import ber.suite
+path.insert(1, path[0]+sep+'codec'+sep+'cer')
+import cer.suite
+path.insert(1, path[0]+sep+'codec'+sep+'der')
+import der.suite
+from pyasn1.error import PyAsn1Error
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+suite = unittest.TestSuite()
+for m in (
+ ber.suite,
+ cer.suite,
+ der.suite
+ ):
+ suite.addTest(getattr(m, 'suite'))
+
+def runTests(): unittest.TextTestRunner(verbosity=2).run(suite)
+
+if __name__ == '__main__': runTests()
diff --git a/python/pyasn1/test/suite.py b/python/pyasn1/test/suite.py
new file mode 100644
index 0000000000..b4d80e864a
--- /dev/null
+++ b/python/pyasn1/test/suite.py
@@ -0,0 +1,26 @@
+from sys import path, version_info
+from os.path import sep
+path.insert(1, path[0]+sep+'type')
+import type.suite
+path.insert(1, path[0]+sep+'codec')
+import codec.suite
+from pyasn1.error import PyAsn1Error
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+suite = unittest.TestSuite()
+for m in (
+ type.suite,
+ codec.suite
+ ):
+ suite.addTest(getattr(m, 'suite'))
+
+def runTests(): unittest.TextTestRunner(verbosity=2).run(suite)
+
+if __name__ == '__main__': runTests()
diff --git a/python/pyasn1/test/type/__init__.py b/python/pyasn1/test/type/__init__.py
new file mode 100644
index 0000000000..8c3066b2e6
--- /dev/null
+++ b/python/pyasn1/test/type/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/python/pyasn1/test/type/suite.py b/python/pyasn1/test/type/suite.py
new file mode 100644
index 0000000000..bc4b48685f
--- /dev/null
+++ b/python/pyasn1/test/type/suite.py
@@ -0,0 +1,20 @@
+import test_tag, test_constraint, test_namedtype, test_univ
+from pyasn1.error import PyAsn1Error
+from sys import version_info
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+suite = unittest.TestSuite()
+loader = unittest.TestLoader()
+for m in (test_tag, test_constraint, test_namedtype, test_univ):
+ suite.addTest(loader.loadTestsFromModule(m))
+
+def runTests(): unittest.TextTestRunner(verbosity=2).run(suite)
+
+if __name__ == '__main__': runTests()
diff --git a/python/pyasn1/test/type/test_constraint.py b/python/pyasn1/test/type/test_constraint.py
new file mode 100644
index 0000000000..3457c0fc37
--- /dev/null
+++ b/python/pyasn1/test/type/test_constraint.py
@@ -0,0 +1,280 @@
+from pyasn1.type import constraint, error
+from pyasn1.error import PyAsn1Error
+from sys import version_info
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+class SingleValueConstraintTestCase(unittest.TestCase):
+ def setUp(self):
+ self.c1 = constraint.SingleValueConstraint(1,2)
+ self.c2 = constraint.SingleValueConstraint(3,4)
+
+ def testCmp(self): assert self.c1 == self.c1, 'comparation fails'
+ def testHash(self): assert hash(self.c1) != hash(self.c2), 'hash() fails'
+ def testGoodVal(self):
+ try:
+ self.c1(1)
+ except error.ValueConstraintError:
+ assert 0, 'constraint check fails'
+ def testBadVal(self):
+ try:
+ self.c1(4)
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint check fails'
+
+class ContainedSubtypeConstraintTestCase(unittest.TestCase):
+ def setUp(self):
+ self.c1 = constraint.ContainedSubtypeConstraint(
+ constraint.SingleValueConstraint(12)
+ )
+
+ def testGoodVal(self):
+ try:
+ self.c1(12)
+ except error.ValueConstraintError:
+ assert 0, 'constraint check fails'
+ def testBadVal(self):
+ try:
+ self.c1(4)
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint check fails'
+
+class ValueRangeConstraintTestCase(unittest.TestCase):
+ def setUp(self):
+ self.c1 = constraint.ValueRangeConstraint(1,4)
+
+ def testGoodVal(self):
+ try:
+ self.c1(1)
+ except error.ValueConstraintError:
+ assert 0, 'constraint check fails'
+ def testBadVal(self):
+ try:
+ self.c1(-5)
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint check fails'
+
+class ValueSizeConstraintTestCase(unittest.TestCase):
+ def setUp(self):
+ self.c1 = constraint.ValueSizeConstraint(1,2)
+
+ def testGoodVal(self):
+ try:
+ self.c1('a')
+ except error.ValueConstraintError:
+ assert 0, 'constraint check fails'
+ def testBadVal(self):
+ try:
+ self.c1('abc')
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint check fails'
+
+class PermittedAlphabetConstraintTestCase(SingleValueConstraintTestCase):
+ def setUp(self):
+ self.c1 = constraint.PermittedAlphabetConstraint('A', 'B', 'C')
+ self.c2 = constraint.PermittedAlphabetConstraint('DEF')
+
+ def testGoodVal(self):
+ try:
+ self.c1('A')
+ except error.ValueConstraintError:
+ assert 0, 'constraint check fails'
+ def testBadVal(self):
+ try:
+ self.c1('E')
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint check fails'
+
+class ConstraintsIntersectionTestCase(unittest.TestCase):
+ def setUp(self):
+ self.c1 = constraint.ConstraintsIntersection(
+ constraint.SingleValueConstraint(4),
+ constraint.ValueRangeConstraint(2, 4)
+ )
+
+ def testCmp1(self):
+ assert constraint.SingleValueConstraint(4) in self.c1, '__cmp__() fails'
+
+ def testCmp2(self):
+ assert constraint.SingleValueConstraint(5) not in self.c1, \
+ '__cmp__() fails'
+
+ def testCmp3(self):
+ c = constraint.ConstraintsUnion(constraint.ConstraintsIntersection(
+ constraint.SingleValueConstraint(4),
+ constraint.ValueRangeConstraint(2, 4)
+ ))
+ assert self.c1 in c, '__cmp__() fails'
+ def testCmp4(self):
+ c = constraint.ConstraintsUnion(
+ constraint.ConstraintsIntersection(constraint.SingleValueConstraint(5))
+ )
+ assert self.c1 not in c, '__cmp__() fails'
+
+ def testGoodVal(self):
+ try:
+ self.c1(4)
+ except error.ValueConstraintError:
+ assert 0, 'constraint check fails'
+ def testBadVal(self):
+ try:
+ self.c1(-5)
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint check fails'
+
+class InnerTypeConstraintTestCase(unittest.TestCase):
+ def testConst1(self):
+ c = constraint.InnerTypeConstraint(
+ constraint.SingleValueConstraint(4)
+ )
+ try:
+ c(4, 32)
+ except error.ValueConstraintError:
+ assert 0, 'constraint check fails'
+ try:
+ c(5, 32)
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint check fails'
+ def testConst2(self):
+ c = constraint.InnerTypeConstraint(
+ (0, constraint.SingleValueConstraint(4), 'PRESENT'),
+ (1, constraint.SingleValueConstraint(4), 'ABSENT')
+ )
+ try:
+ c(4, 0)
+ except error.ValueConstraintError:
+ raise
+ assert 0, 'constraint check fails'
+ try:
+ c(4, 1)
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint check fails'
+ try:
+ c(3, 0)
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint check fails'
+
+# Constraints compositions
+
+class ConstraintsIntersectionTestCase(unittest.TestCase):
+ def setUp(self):
+ self.c1 = constraint.ConstraintsIntersection(
+ constraint.ValueRangeConstraint(1, 9),
+ constraint.ValueRangeConstraint(2, 5)
+ )
+
+ def testGoodVal(self):
+ try:
+ self.c1(3)
+ except error.ValueConstraintError:
+ assert 0, 'constraint check fails'
+ def testBadVal(self):
+ try:
+ self.c1(0)
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint check fails'
+
+class ConstraintsUnionTestCase(unittest.TestCase):
+ def setUp(self):
+ self.c1 = constraint.ConstraintsUnion(
+ constraint.SingleValueConstraint(5),
+ constraint.ValueRangeConstraint(1, 3)
+ )
+
+ def testGoodVal(self):
+ try:
+ self.c1(2)
+ self.c1(5)
+ except error.ValueConstraintError:
+ assert 0, 'constraint check fails'
+ def testBadVal(self):
+ try:
+ self.c1(-5)
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint check fails'
+
+class ConstraintsExclusionTestCase(unittest.TestCase):
+ def setUp(self):
+ self.c1 = constraint.ConstraintsExclusion(
+ constraint.ValueRangeConstraint(2, 4)
+ )
+
+ def testGoodVal(self):
+ try:
+ self.c1(6)
+ except error.ValueConstraintError:
+ assert 0, 'constraint check fails'
+ def testBadVal(self):
+ try:
+ self.c1(2)
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint check fails'
+
+# Constraints derivations
+
+class DirectDerivationTestCase(unittest.TestCase):
+ def setUp(self):
+ self.c1 = constraint.SingleValueConstraint(5)
+ self.c2 = constraint.ConstraintsUnion(
+ self.c1, constraint.ValueRangeConstraint(1, 3)
+ )
+
+ def testGoodVal(self):
+ assert self.c1.isSuperTypeOf(self.c2), 'isSuperTypeOf failed'
+ assert not self.c1.isSubTypeOf(self.c2) , 'isSubTypeOf failed'
+ def testBadVal(self):
+ assert not self.c2.isSuperTypeOf(self.c1) , 'isSuperTypeOf failed'
+ assert self.c2.isSubTypeOf(self.c1) , 'isSubTypeOf failed'
+
+class IndirectDerivationTestCase(unittest.TestCase):
+ def setUp(self):
+ self.c1 = constraint.ConstraintsIntersection(
+ constraint.ValueRangeConstraint(1, 30)
+ )
+ self.c2 = constraint.ConstraintsIntersection(
+ self.c1, constraint.ValueRangeConstraint(1, 20)
+ )
+ self.c2 = constraint.ConstraintsIntersection(
+ self.c2, constraint.ValueRangeConstraint(1, 10)
+ )
+
+ def testGoodVal(self):
+ assert self.c1.isSuperTypeOf(self.c2), 'isSuperTypeOf failed'
+ assert not self.c1.isSubTypeOf(self.c2) , 'isSubTypeOf failed'
+ def testBadVal(self):
+ assert not self.c2.isSuperTypeOf(self.c1) , 'isSuperTypeOf failed'
+ assert self.c2.isSubTypeOf(self.c1) , 'isSubTypeOf failed'
+
+if __name__ == '__main__': unittest.main()
+
+# how to apply size constriants to constructed types?
diff --git a/python/pyasn1/test/type/test_namedtype.py b/python/pyasn1/test/type/test_namedtype.py
new file mode 100644
index 0000000000..3a4f305994
--- /dev/null
+++ b/python/pyasn1/test/type/test_namedtype.py
@@ -0,0 +1,87 @@
+from pyasn1.type import namedtype, univ
+from pyasn1.error import PyAsn1Error
+from sys import version_info
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+class NamedTypeCaseBase(unittest.TestCase):
+ def setUp(self):
+ self.e = namedtype.NamedType('age', univ.Integer())
+ def testIter(self):
+ n, t = self.e
+ assert n == 'age' or t == univ.Integer(), 'unpack fails'
+
+class NamedTypesCaseBase(unittest.TestCase):
+ def setUp(self):
+ self.e = namedtype.NamedTypes(
+ namedtype.NamedType('first-name', univ.OctetString('')),
+ namedtype.OptionalNamedType('age', univ.Integer(0)),
+ namedtype.NamedType('family-name', univ.OctetString(''))
+ )
+ def testIter(self):
+ for t in self.e:
+ break
+ else:
+ assert 0, '__getitem__() fails'
+
+ def testGetTypeByPosition(self):
+ assert self.e.getTypeByPosition(0) == univ.OctetString(''), \
+ 'getTypeByPosition() fails'
+
+ def testGetNameByPosition(self):
+ assert self.e.getNameByPosition(0) == 'first-name', \
+ 'getNameByPosition() fails'
+
+ def testGetPositionByName(self):
+ assert self.e.getPositionByName('first-name') == 0, \
+ 'getPositionByName() fails'
+
+ def testGetTypesNearPosition(self):
+ assert self.e.getTagMapNearPosition(0).getPosMap() == {
+ univ.OctetString.tagSet: univ.OctetString('')
+ }
+ assert self.e.getTagMapNearPosition(1).getPosMap() == {
+ univ.Integer.tagSet: univ.Integer(0),
+ univ.OctetString.tagSet: univ.OctetString('')
+ }
+ assert self.e.getTagMapNearPosition(2).getPosMap() == {
+ univ.OctetString.tagSet: univ.OctetString('')
+ }
+
+ def testGetTagMap(self):
+ assert self.e.getTagMap().getPosMap() == {
+ univ.OctetString.tagSet: univ.OctetString(''),
+ univ.Integer.tagSet: univ.Integer(0)
+ }
+
+ def testGetTagMapWithDups(self):
+ try:
+ self.e.getTagMap(1)
+ except PyAsn1Error:
+ pass
+ else:
+ assert 0, 'Duped types not noticed'
+
+ def testGetPositionNearType(self):
+ assert self.e.getPositionNearType(univ.OctetString.tagSet, 0) == 0
+ assert self.e.getPositionNearType(univ.Integer.tagSet, 1) == 1
+ assert self.e.getPositionNearType(univ.OctetString.tagSet, 2) == 2
+
+class OrderedNamedTypesCaseBase(unittest.TestCase):
+ def setUp(self):
+ self.e = namedtype.NamedTypes(
+ namedtype.NamedType('first-name', univ.OctetString('')),
+ namedtype.NamedType('age', univ.Integer(0))
+ )
+
+ def testGetTypeByPosition(self):
+ assert self.e.getTypeByPosition(0) == univ.OctetString(''), \
+ 'getTypeByPosition() fails'
+
+if __name__ == '__main__': unittest.main()
diff --git a/python/pyasn1/test/type/test_tag.py b/python/pyasn1/test/type/test_tag.py
new file mode 100644
index 0000000000..78146dca2f
--- /dev/null
+++ b/python/pyasn1/test/type/test_tag.py
@@ -0,0 +1,107 @@
+from pyasn1.type import tag
+from pyasn1.error import PyAsn1Error
+from sys import version_info
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+class TagTestCaseBase(unittest.TestCase):
+ def setUp(self):
+ self.t1 = tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 3)
+ self.t2 = tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 3)
+
+class TagCmpTestCase(TagTestCaseBase):
+ def testCmp(self):
+ assert self.t1 == self.t2, 'tag comparation fails'
+
+ def testHash(self):
+ assert hash(self.t1) == hash(self.t2), 'tag hash comparation fails'
+
+ def testSequence(self):
+ assert self.t1[0] == self.t2[0] and \
+ self.t1[1] == self.t2[1] and \
+ self.t1[2] == self.t2[2], 'tag sequence protocol fails'
+
+class TagSetTestCaseBase(unittest.TestCase):
+ def setUp(self):
+ self.ts1 = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
+ )
+ self.ts2 = tag.initTagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
+ )
+
+class TagSetCmpTestCase(TagSetTestCaseBase):
+ def testCmp(self):
+ assert self.ts1 == self.ts2, 'tag set comparation fails'
+
+ def testHash(self):
+ assert hash(self.ts1) == hash(self.ts2), 'tag set hash comp. fails'
+
+ def testLen(self):
+ assert len(self.ts1) == len(self.ts2), 'tag length comparation fails'
+
+class TaggingTestSuite(TagSetTestCaseBase):
+ def testImplicitTag(self):
+ t = self.ts1.tagImplicitly(
+ tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 14)
+ )
+ assert t == tag.TagSet(
+ tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 12),
+ tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 14)
+ ), 'implicit tagging went wrong'
+
+ def testExplicitTag(self):
+ t = self.ts1.tagExplicitly(
+ tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 32)
+ )
+ assert t == tag.TagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+ tag.Tag(tag.tagClassPrivate, tag.tagFormatConstructed, 32)
+ ), 'explicit tagging went wrong'
+
+class TagSetAddTestSuite(TagSetTestCaseBase):
+ def testAdd(self):
+ t = self.ts1 + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2)
+ assert t == tag.TagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+ tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2)
+ ), 'TagSet.__add__() fails'
+
+ def testRadd(self):
+ t = tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2) + self.ts1
+ assert t == tag.TagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+ tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
+ ), 'TagSet.__radd__() fails'
+
+class SuperTagSetTestCase(TagSetTestCaseBase):
+ def testSuperTagCheck1(self):
+ assert self.ts1.isSuperTagSetOf(
+ tag.TagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
+ )), 'isSuperTagSetOf() fails'
+
+ def testSuperTagCheck2(self):
+ assert not self.ts1.isSuperTagSetOf(
+ tag.TagSet(
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 13)
+ )), 'isSuperTagSetOf() fails'
+
+ def testSuperTagCheck3(self):
+ assert self.ts1.isSuperTagSetOf(
+ tag.TagSet((), tag.Tag(tag.tagClassUniversal,
+ tag.tagFormatSimple, 12))
+ ), 'isSuperTagSetOf() fails'
+
+if __name__ == '__main__': unittest.main()
diff --git a/python/pyasn1/test/type/test_univ.py b/python/pyasn1/test/type/test_univ.py
new file mode 100644
index 0000000000..3eedcf26a6
--- /dev/null
+++ b/python/pyasn1/test/type/test_univ.py
@@ -0,0 +1,479 @@
+from pyasn1.type import univ, tag, constraint, namedtype, namedval, error
+from pyasn1.compat.octets import str2octs, ints2octs
+from pyasn1.error import PyAsn1Error
+from sys import version_info
+if version_info[0:2] < (2, 7) or \
+ version_info[0:2] in ( (3, 0), (3, 1) ):
+ try:
+ import unittest2 as unittest
+ except ImportError:
+ import unittest
+else:
+ import unittest
+
+class IntegerTestCase(unittest.TestCase):
+ def testStr(self): assert str(univ.Integer(1)) in ('1','1L'),'str() fails'
+ def testAnd(self): assert univ.Integer(1) & 0 == 0, '__and__() fails'
+ def testOr(self): assert univ.Integer(1) | 0 == 1, '__or__() fails'
+ def testXor(self): assert univ.Integer(1) ^ 0 == 1, '__xor__() fails'
+ def testRand(self): assert 0 & univ.Integer(1) == 0, '__rand__() fails'
+ def testRor(self): assert 0 | univ.Integer(1) == 1, '__ror__() fails'
+ def testRxor(self): assert 0 ^ univ.Integer(1) == 1, '__rxor__() fails'
+ def testAdd(self): assert univ.Integer(-4) + 6 == 2, '__add__() fails'
+ def testRadd(self): assert 4 + univ.Integer(5) == 9, '__radd__() fails'
+ def testSub(self): assert univ.Integer(3) - 6 == -3, '__sub__() fails'
+ def testRsub(self): assert 6 - univ.Integer(3) == 3, '__rsub__() fails'
+ def testMul(self): assert univ.Integer(3) * -3 == -9, '__mul__() fails'
+ def testRmul(self): assert 2 * univ.Integer(3) == 6, '__rmul__() fails'
+ def testDiv(self): assert univ.Integer(3) / 2 == 1, '__div__() fails'
+ def testRdiv(self): assert 6 / univ.Integer(3) == 2, '__rdiv__() fails'
+ def testMod(self): assert univ.Integer(3) % 2 == 1, '__mod__() fails'
+ def testRmod(self): assert 4 % univ.Integer(3) == 1, '__rmod__() fails'
+ def testPow(self): assert univ.Integer(3) ** 2 == 9, '__pow__() fails'
+ def testRpow(self): assert 2 ** univ.Integer(2) == 4, '__rpow__() fails'
+ def testLshift(self): assert univ.Integer(1) << 1 == 2, '<< fails'
+ def testRshift(self): assert univ.Integer(2) >> 1 == 1, '>> fails'
+ def testInt(self): assert int(univ.Integer(3)) == 3, '__int__() fails'
+ def testLong(self): assert int(univ.Integer(8)) == 8, '__long__() fails'
+ def testFloat(self): assert float(univ.Integer(4))==4.0,'__float__() fails'
+ def testPrettyIn(self): assert univ.Integer('3') == 3, 'prettyIn() fails'
+ def testTag(self):
+ assert univ.Integer().getTagSet() == tag.TagSet(
+ (),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02)
+ )
+ def testNamedVals(self):
+ i = univ.Integer(
+ 'asn1', namedValues=univ.Integer.namedValues.clone(('asn1', 1))
+ )
+ assert i == 1, 'named val fails'
+ assert str(i) != 'asn1', 'named val __str__() fails'
+
+class BooleanTestCase(unittest.TestCase):
+ def testTruth(self):
+ assert univ.Boolean(True) and univ.Boolean(1), 'Truth initializer fails'
+ def testFalse(self):
+ assert not univ.Boolean(False) and not univ.Boolean(0), 'False initializer fails'
+ def testStr(self):
+ assert str(univ.Boolean(1)) in ('1', '1L'), 'str() fails'
+ def testTag(self):
+ assert univ.Boolean().getTagSet() == tag.TagSet(
+ (),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01)
+ )
+ def testConstraints(self):
+ try:
+ univ.Boolean(2)
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint fail'
+ def testSubtype(self):
+ assert univ.Integer().subtype(
+ value=1,
+ implicitTag=tag.Tag(tag.tagClassPrivate,tag.tagFormatSimple,2),
+ subtypeSpec=constraint.SingleValueConstraint(1,3)
+ ) == univ.Integer(
+ value=1,
+ tagSet=tag.TagSet(tag.Tag(tag.tagClassPrivate,
+ tag.tagFormatSimple,2)),
+ subtypeSpec=constraint.ConstraintsIntersection(constraint.SingleValueConstraint(1,3))
+ )
+
+class BitStringTestCase(unittest.TestCase):
+ def setUp(self):
+ self.b = univ.BitString(
+ namedValues=namedval.NamedValues(('Active', 0), ('Urgent', 1))
+ )
+ def testSet(self):
+ assert self.b.clone('Active') == (1,)
+ assert self.b.clone("'1010100110001010'B") == (1,0,1,0,1,0,0,1,1,0,0,0,1,0,1,0)
+ assert self.b.clone("'A98A'H") == (1,0,1,0,1,0,0,1,1,0,0,0,1,0,1,0)
+ assert self.b.clone((1,0,1)) == (1,0,1)
+ def testStr(self):
+ assert str(self.b.clone('Urgent,Active')) == '(1, 1)'
+ def testRepr(self):
+ assert repr(self.b.clone('Urgent,Active')) == 'BitString("\'11\'B")'
+ def testTag(self):
+ assert univ.BitString().getTagSet() == tag.TagSet(
+ (),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03)
+ )
+ def testLen(self): assert len(self.b.clone("'A98A'H")) == 16
+ def testIter(self):
+ assert self.b.clone("'A98A'H")[0] == 1
+ assert self.b.clone("'A98A'H")[1] == 0
+ assert self.b.clone("'A98A'H")[2] == 1
+
+class OctetStringTestCase(unittest.TestCase):
+ def testInit(self):
+ assert univ.OctetString(str2octs('abcd')) == str2octs('abcd'), '__init__() fails'
+ def testBinStr(self):
+ assert univ.OctetString(binValue="1000010111101110101111000000111011") == ints2octs((133, 238, 188, 14, 192)), 'bin init fails'
+ def testHexStr(self):
+ assert univ.OctetString(hexValue="FA9823C43E43510DE3422") == ints2octs((250, 152, 35, 196, 62, 67, 81, 13, 227, 66, 32)), 'hex init fails'
+ def testTuple(self):
+ assert univ.OctetString((1,2,3,4,5)) == ints2octs((1,2,3,4,5)), 'tuple init failed'
+ def testStr(self):
+ assert str(univ.OctetString('q')) == 'q', '__str__() fails'
+ def testSeq(self):
+ assert univ.OctetString('q')[0] == str2octs('q')[0],'__getitem__() fails'
+ def testAsOctets(self):
+ assert univ.OctetString('abcd').asOctets() == str2octs('abcd'), 'testAsOctets() fails'
+ def testAsInts(self):
+ assert univ.OctetString('abcd').asNumbers() == (97, 98, 99, 100), 'testAsNumbers() fails'
+
+ def testEmpty(self):
+ try:
+ str(univ.OctetString())
+ except PyAsn1Error:
+ pass
+ else:
+ assert 0, 'empty OctetString() not reported'
+
+ def testAdd(self):
+ assert univ.OctetString('') + 'q' == str2octs('q'), '__add__() fails'
+ def testRadd(self):
+ assert 'b' + univ.OctetString('q') == str2octs('bq'), '__radd__() fails'
+ def testMul(self):
+ assert univ.OctetString('a') * 2 == str2octs('aa'), '__mul__() fails'
+ def testRmul(self):
+ assert 2 * univ.OctetString('b') == str2octs('bb'), '__rmul__() fails'
+ def testTag(self):
+ assert univ.OctetString().getTagSet() == tag.TagSet(
+ (),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04)
+ )
+
+class Null(unittest.TestCase):
+ def testStr(self): assert str(univ.Null('')) == '', 'str() fails'
+ def testTag(self):
+ assert univ.Null().getTagSet() == tag.TagSet(
+ (),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05)
+ )
+ def testConstraints(self):
+ try:
+ univ.Null(2)
+ except error.ValueConstraintError:
+ pass
+ else:
+ assert 0, 'constraint fail'
+
+class RealTestCase(unittest.TestCase):
+ def testStr(self): assert str(univ.Real(1.0)) == '1.0','str() fails'
+ def testRepr(self): assert repr(univ.Real(-4.1)) == 'Real((-41, 10, -1))','repr() fails'
+ def testAdd(self): assert univ.Real(-4.1) + 1.4 == -2.7, '__add__() fails'
+ def testRadd(self): assert 4 + univ.Real(0.5) == 4.5, '__radd__() fails'
+ def testSub(self): assert univ.Real(3.9) - 1.7 == 2.2, '__sub__() fails'
+ def testRsub(self): assert 6.1 - univ.Real(0.1) == 6, '__rsub__() fails'
+ def testMul(self): assert univ.Real(3.0) * -3 == -9, '__mul__() fails'
+ def testRmul(self): assert 2 * univ.Real(3.0) == 6, '__rmul__() fails'
+ def testDiv(self): assert univ.Real(3.0) / 2 == 1.5, '__div__() fails'
+ def testRdiv(self): assert 6 / univ.Real(3.0) == 2, '__rdiv__() fails'
+ def testMod(self): assert univ.Real(3.0) % 2 == 1, '__mod__() fails'
+ def testRmod(self): assert 4 % univ.Real(3.0) == 1, '__rmod__() fails'
+ def testPow(self): assert univ.Real(3.0) ** 2 == 9, '__pow__() fails'
+ def testRpow(self): assert 2 ** univ.Real(2.0) == 4, '__rpow__() fails'
+ def testInt(self): assert int(univ.Real(3.0)) == 3, '__int__() fails'
+ def testLong(self): assert int(univ.Real(8.0)) == 8, '__long__() fails'
+ def testFloat(self): assert float(univ.Real(4.0))==4.0,'__float__() fails'
+ def testPrettyIn(self): assert univ.Real((3,10,0)) == 3, 'prettyIn() fails'
+ # infinite float values
+ def testStrInf(self):
+ assert str(univ.Real('inf')) == 'inf','str() fails'
+ def testReprInf(self):
+ assert repr(univ.Real('inf')) == 'Real(\'inf\')','repr() fails'
+ def testAddInf(self):
+ assert univ.Real('inf') + 1 == float('inf'), '__add__() fails'
+ def testRaddInf(self):
+ assert 1 + univ.Real('inf') == float('inf'), '__radd__() fails'
+ def testIntInf(self):
+ try:
+ assert int(univ.Real('inf'))
+ except OverflowError:
+ pass
+ else:
+ assert 0, '__int__() fails'
+ def testLongInf(self):
+ try:
+ assert int(univ.Real('inf'))
+ except OverflowError:
+ pass
+ else:
+ assert 0, '__long__() fails'
+ assert int(univ.Real(8.0)) == 8, '__long__() fails'
+ def testFloatInf(self):
+ assert float(univ.Real('-inf')) == float('-inf'),'__float__() fails'
+ def testPrettyInInf(self):
+ assert univ.Real(float('inf')) == float('inf'), 'prettyIn() fails'
+ def testPlusInf(self):
+ assert univ.Real('inf').isPlusInfinity(), 'isPlusInfinity failed'
+ def testMinusInf(self):
+ assert univ.Real('-inf').isMinusInfinity(), 'isMinusInfinity failed'
+
+ def testTag(self):
+ assert univ.Real().getTagSet() == tag.TagSet(
+ (),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09)
+ )
+
+class ObjectIdentifier(unittest.TestCase):
+ def testStr(self):
+ assert str(univ.ObjectIdentifier((1,3,6))) == '1.3.6'
+ def testEq(self):
+ assert univ.ObjectIdentifier((1,3,6)) == (1,3,6), '__cmp__() fails'
+ def testAdd(self):
+ assert univ.ObjectIdentifier((1,3)) + (6,)==(1,3,6),'__add__() fails'
+ def testRadd(self):
+ assert (1,) + univ.ObjectIdentifier((3,6))==(1,3,6),'__radd__() fails'
+ def testLen(self):
+ assert len(univ.ObjectIdentifier((1,3))) == 2,'__len__() fails'
+ def testPrefix(self):
+ o = univ.ObjectIdentifier('1.3.6')
+ assert o.isPrefixOf((1,3,6)), 'isPrefixOf() fails'
+ assert o.isPrefixOf((1,3,6,1)), 'isPrefixOf() fails'
+ assert not o.isPrefixOf((1,3)), 'isPrefixOf() fails'
+ def testInput(self):
+ assert univ.ObjectIdentifier('1.3.6')==(1,3,6),'prettyIn() fails'
+ def testTag(self):
+ assert univ.ObjectIdentifier().getTagSet() == tag.TagSet(
+ (),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06)
+ )
+
+class SequenceOf(unittest.TestCase):
+ def setUp(self):
+ self.s1 = univ.SequenceOf(
+ componentType=univ.OctetString('')
+ )
+ self.s2 = self.s1.clone()
+ def testTag(self):
+ assert self.s1.getTagSet() == tag.TagSet(
+ (),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
+ ), 'wrong tagSet'
+ def testSeq(self):
+ self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+ assert self.s1[0] == str2octs('abc'), 'set by idx fails'
+ self.s1[0] = 'cba'
+ assert self.s1[0] == str2octs('cba'), 'set by idx fails'
+ def testCmp(self):
+ self.s1.clear()
+ self.s1.setComponentByPosition(0, 'abc')
+ self.s2.clear()
+ self.s2.setComponentByPosition(0, univ.OctetString('abc'))
+ assert self.s1 == self.s2, '__cmp__() fails'
+ def testSubtypeSpec(self):
+ s = self.s1.clone(subtypeSpec=constraint.ConstraintsUnion(
+ constraint.SingleValueConstraint(str2octs('abc'))
+ ))
+ try:
+ s.setComponentByPosition(0, univ.OctetString('abc'))
+ except:
+ assert 0, 'constraint fails'
+ try:
+ s.setComponentByPosition(1, univ.OctetString('Abc'))
+ except:
+ pass
+ else:
+ assert 0, 'constraint fails'
+ def testSizeSpec(self):
+ s = self.s1.clone(sizeSpec=constraint.ConstraintsUnion(
+ constraint.ValueSizeConstraint(1,1)
+ ))
+ s.setComponentByPosition(0, univ.OctetString('abc'))
+ try:
+ s.verifySizeSpec()
+ except:
+ assert 0, 'size spec fails'
+ s.setComponentByPosition(1, univ.OctetString('abc'))
+ try:
+ s.verifySizeSpec()
+ except:
+ pass
+ else:
+ assert 0, 'size spec fails'
+ def testGetComponentTagMap(self):
+ assert self.s1.getComponentTagMap().getPosMap() == {
+ univ.OctetString.tagSet: univ.OctetString('')
+ }
+ def testSubtype(self):
+ self.s1.clear()
+ assert self.s1.subtype(
+ implicitTag=tag.Tag(tag.tagClassPrivate,tag.tagFormatSimple,2),
+ subtypeSpec=constraint.SingleValueConstraint(1,3),
+ sizeSpec=constraint.ValueSizeConstraint(0,1)
+ ) == self.s1.clone(
+ tagSet=tag.TagSet(tag.Tag(tag.tagClassPrivate,
+ tag.tagFormatSimple,2)),
+ subtypeSpec=constraint.ConstraintsIntersection(constraint.SingleValueConstraint(1,3)),
+ sizeSpec=constraint.ValueSizeConstraint(0,1)
+ )
+ def testClone(self):
+ self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+ s = self.s1.clone()
+ assert len(s) == 0
+ s = self.s1.clone(cloneValueFlag=1)
+ assert len(s) == 1
+ assert s.getComponentByPosition(0) == self.s1.getComponentByPosition(0)
+
+class Sequence(unittest.TestCase):
+ def setUp(self):
+ self.s1 = univ.Sequence(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('name', univ.OctetString('')),
+ namedtype.OptionalNamedType('nick', univ.OctetString('')),
+ namedtype.DefaultedNamedType('age', univ.Integer(34))
+ ))
+ def testTag(self):
+ assert self.s1.getTagSet() == tag.TagSet(
+ (),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10)
+ ), 'wrong tagSet'
+ def testById(self):
+ self.s1.setComponentByName('name', univ.OctetString('abc'))
+ assert self.s1.getComponentByName('name') == str2octs('abc'), 'set by name fails'
+ def testByKey(self):
+ self.s1['name'] = 'abc'
+ assert self.s1['name'] == str2octs('abc'), 'set by key fails'
+ def testGetNearPosition(self):
+ assert self.s1.getComponentTagMapNearPosition(1).getPosMap() == {
+ univ.OctetString.tagSet: univ.OctetString(''),
+ univ.Integer.tagSet: univ.Integer(34)
+ }
+ assert self.s1.getComponentPositionNearType(
+ univ.OctetString.tagSet, 1
+ ) == 1
+ def testGetDefaultComponentByPosition(self):
+ self.s1.clear()
+ assert self.s1.getDefaultComponentByPosition(0) == None
+ assert self.s1.getDefaultComponentByPosition(2) == univ.Integer(34)
+ def testSetDefaultComponents(self):
+ self.s1.clear()
+ assert self.s1.getComponentByPosition(2) == None
+ self.s1.setComponentByPosition(0, univ.OctetString('Ping'))
+ self.s1.setComponentByPosition(1, univ.OctetString('Pong'))
+ self.s1.setDefaultComponents()
+ assert self.s1.getComponentByPosition(2) == 34
+ def testClone(self):
+ self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+ self.s1.setComponentByPosition(1, univ.OctetString('def'))
+ self.s1.setComponentByPosition(2, univ.Integer(123))
+ s = self.s1.clone()
+ assert s.getComponentByPosition(0) != self.s1.getComponentByPosition(0)
+ assert s.getComponentByPosition(1) != self.s1.getComponentByPosition(1)
+ assert s.getComponentByPosition(2) != self.s1.getComponentByPosition(2)
+ s = self.s1.clone(cloneValueFlag=1)
+ assert s.getComponentByPosition(0) == self.s1.getComponentByPosition(0)
+ assert s.getComponentByPosition(1) == self.s1.getComponentByPosition(1)
+ assert s.getComponentByPosition(2) == self.s1.getComponentByPosition(2)
+
+class SetOf(unittest.TestCase):
+ def setUp(self):
+ self.s1 = univ.SetOf(componentType=univ.OctetString(''))
+ def testTag(self):
+ assert self.s1.getTagSet() == tag.TagSet(
+ (),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+ ), 'wrong tagSet'
+ def testSeq(self):
+ self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+ assert self.s1[0] == str2octs('abc'), 'set by idx fails'
+ self.s1.setComponentByPosition(0, self.s1[0].clone('cba'))
+ assert self.s1[0] == str2octs('cba'), 'set by idx fails'
+
+class Set(unittest.TestCase):
+ def setUp(self):
+ self.s1 = univ.Set(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('name', univ.OctetString('')),
+ namedtype.OptionalNamedType('null', univ.Null('')),
+ namedtype.DefaultedNamedType('age', univ.Integer(34))
+ ))
+ self.s2 = self.s1.clone()
+ def testTag(self):
+ assert self.s1.getTagSet() == tag.TagSet(
+ (),
+ tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11)
+ ), 'wrong tagSet'
+ def testByTypeWithPythonValue(self):
+ self.s1.setComponentByType(univ.OctetString.tagSet, 'abc')
+ assert self.s1.getComponentByType(
+ univ.OctetString.tagSet
+ ) == str2octs('abc'), 'set by name fails'
+ def testByTypeWithInstance(self):
+ self.s1.setComponentByType(univ.OctetString.tagSet, univ.OctetString('abc'))
+ assert self.s1.getComponentByType(
+ univ.OctetString.tagSet
+ ) == str2octs('abc'), 'set by name fails'
+ def testGetTagMap(self):
+ assert self.s1.getTagMap().getPosMap() == {
+ univ.Set.tagSet: univ.Set()
+ }
+ def testGetComponentTagMap(self):
+ assert self.s1.getComponentTagMap().getPosMap() == {
+ univ.OctetString.tagSet: univ.OctetString(''),
+ univ.Null.tagSet: univ.Null(''),
+ univ.Integer.tagSet: univ.Integer(34)
+ }
+ def testGetPositionByType(self):
+ assert self.s1.getComponentPositionByType(
+ univ.Null().getTagSet()
+ ) == 1
+
+class Choice(unittest.TestCase):
+ def setUp(self):
+ innerComp = univ.Choice(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('count', univ.Integer()),
+ namedtype.NamedType('flag', univ.Boolean())
+ ))
+ self.s1 = univ.Choice(componentType=namedtype.NamedTypes(
+ namedtype.NamedType('name', univ.OctetString()),
+ namedtype.NamedType('sex', innerComp)
+ ))
+ def testTag(self):
+ assert self.s1.getTagSet() == tag.TagSet(), 'wrong tagSet'
+ def testOuterByTypeWithPythonValue(self):
+ self.s1.setComponentByType(univ.OctetString.tagSet, 'abc')
+ assert self.s1.getComponentByType(
+ univ.OctetString.tagSet
+ ) == str2octs('abc')
+ def testOuterByTypeWithInstanceValue(self):
+ self.s1.setComponentByType(
+ univ.OctetString.tagSet, univ.OctetString('abc')
+ )
+ assert self.s1.getComponentByType(
+ univ.OctetString.tagSet
+ ) == str2octs('abc')
+ def testInnerByTypeWithPythonValue(self):
+ self.s1.setComponentByType(univ.Integer.tagSet, 123, 1)
+ assert self.s1.getComponentByType(
+ univ.Integer.tagSet, 1
+ ) == 123
+ def testInnerByTypeWithInstanceValue(self):
+ self.s1.setComponentByType(
+ univ.Integer.tagSet, univ.Integer(123), 1
+ )
+ assert self.s1.getComponentByType(
+ univ.Integer.tagSet, 1
+ ) == 123
+ def testCmp(self):
+ self.s1.setComponentByName('name', univ.OctetString('abc'))
+ assert self.s1 == str2octs('abc'), '__cmp__() fails'
+ def testGetComponent(self):
+ self.s1.setComponentByType(univ.OctetString.tagSet, 'abc')
+ assert self.s1.getComponent() == str2octs('abc'), 'getComponent() fails'
+ def testGetName(self):
+ self.s1.setComponentByType(univ.OctetString.tagSet, 'abc')
+ assert self.s1.getName() == 'name', 'getName() fails'
+ def testSetComponentByPosition(self):
+ self.s1.setComponentByPosition(0, univ.OctetString('Jim'))
+ assert self.s1 == str2octs('Jim')
+ def testClone(self):
+ self.s1.setComponentByPosition(0, univ.OctetString('abc'))
+ s = self.s1.clone()
+ assert len(s) == 0
+ s = self.s1.clone(cloneValueFlag=1)
+ assert len(s) == 1
+ assert s.getComponentByPosition(0) == self.s1.getComponentByPosition(0)
+
+if __name__ == '__main__': unittest.main()