Bug 1091668 - Import pyasn1-0.1.7; r=ted, r=gerv
This commit is contained in:
278
python/pyasn1/CHANGES
Normal file
278
python/pyasn1/CHANGES
Normal file
@@ -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
|
||||
24
python/pyasn1/LICENSE
Normal file
24
python/pyasn1/LICENSE
Normal file
@@ -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.
|
||||
3
python/pyasn1/MANIFEST.in
Normal file
3
python/pyasn1/MANIFEST.in
Normal file
@@ -0,0 +1,3 @@
|
||||
include CHANGES README LICENSE THANKS TODO
|
||||
recursive-include test *.py
|
||||
recursive-include doc *.html
|
||||
26
python/pyasn1/PKG-INFO
Normal file
26
python/pyasn1/PKG-INFO
Normal file
@@ -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
|
||||
68
python/pyasn1/README
Normal file
68
python/pyasn1/README
Normal file
@@ -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
|
||||
4
python/pyasn1/THANKS
Normal file
4
python/pyasn1/THANKS
Normal file
@@ -0,0 +1,4 @@
|
||||
Denis S. Otkidach
|
||||
Gregory Golberg
|
||||
Bud P. Bruegger
|
||||
Jacek Konieczny
|
||||
36
python/pyasn1/TODO
Normal file
36
python/pyasn1/TODO
Normal file
@@ -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?
|
||||
503
python/pyasn1/doc/codecs.html
Normal file
503
python/pyasn1/doc/codecs.html
Normal file
@@ -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>
|
||||
436
python/pyasn1/doc/constraints.html
Normal file
436
python/pyasn1/doc/constraints.html
Normal file
@@ -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>
|
||||
377
python/pyasn1/doc/constructed.html
Normal file
377
python/pyasn1/doc/constructed.html
Normal file
@@ -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>
|
||||
156
python/pyasn1/doc/intro.html
Normal file
156
python/pyasn1/doc/intro.html
Normal file
@@ -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>
|
||||
2405
python/pyasn1/doc/pyasn1-tutorial.html
Normal file
2405
python/pyasn1/doc/pyasn1-tutorial.html
Normal file
File diff suppressed because it is too large
Load Diff
794
python/pyasn1/doc/scalar.html
Normal file
794
python/pyasn1/doc/scalar.html
Normal file
@@ -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>
|
||||
233
python/pyasn1/doc/tagging.html
Normal file
233
python/pyasn1/doc/tagging.html
Normal file
@@ -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>
|
||||
26
python/pyasn1/pyasn1.egg-info/PKG-INFO
Normal file
26
python/pyasn1/pyasn1.egg-info/PKG-INFO
Normal file
@@ -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
|
||||
68
python/pyasn1/pyasn1.egg-info/SOURCES.txt
Normal file
68
python/pyasn1/pyasn1.egg-info/SOURCES.txt
Normal file
@@ -0,0 +1,68 @@
|
||||
CHANGES
|
||||
LICENSE
|
||||
MANIFEST.in
|
||||
README
|
||||
THANKS
|
||||
TODO
|
||||
setup.py
|
||||
doc/codecs.html
|
||||
doc/constraints.html
|
||||
doc/constructed.html
|
||||
doc/intro.html
|
||||
doc/pyasn1-tutorial.html
|
||||
doc/scalar.html
|
||||
doc/tagging.html
|
||||
pyasn1/__init__.py
|
||||
pyasn1/debug.py
|
||||
pyasn1/error.py
|
||||
pyasn1.egg-info/PKG-INFO
|
||||
pyasn1.egg-info/SOURCES.txt
|
||||
pyasn1.egg-info/dependency_links.txt
|
||||
pyasn1.egg-info/top_level.txt
|
||||
pyasn1.egg-info/zip-safe
|
||||
pyasn1/codec/__init__.py
|
||||
pyasn1/codec/ber/__init__.py
|
||||
pyasn1/codec/ber/decoder.py
|
||||
pyasn1/codec/ber/encoder.py
|
||||
pyasn1/codec/ber/eoo.py
|
||||
pyasn1/codec/cer/__init__.py
|
||||
pyasn1/codec/cer/decoder.py
|
||||
pyasn1/codec/cer/encoder.py
|
||||
pyasn1/codec/der/__init__.py
|
||||
pyasn1/codec/der/decoder.py
|
||||
pyasn1/codec/der/encoder.py
|
||||
pyasn1/compat/__init__.py
|
||||
pyasn1/compat/octets.py
|
||||
pyasn1/type/__init__.py
|
||||
pyasn1/type/base.py
|
||||
pyasn1/type/char.py
|
||||
pyasn1/type/constraint.py
|
||||
pyasn1/type/error.py
|
||||
pyasn1/type/namedtype.py
|
||||
pyasn1/type/namedval.py
|
||||
pyasn1/type/tag.py
|
||||
pyasn1/type/tagmap.py
|
||||
pyasn1/type/univ.py
|
||||
pyasn1/type/useful.py
|
||||
test/__init__.py
|
||||
test/suite.py
|
||||
test/codec/__init__.py
|
||||
test/codec/suite.py
|
||||
test/codec/ber/__init__.py
|
||||
test/codec/ber/suite.py
|
||||
test/codec/ber/test_decoder.py
|
||||
test/codec/ber/test_encoder.py
|
||||
test/codec/cer/__init__.py
|
||||
test/codec/cer/suite.py
|
||||
test/codec/cer/test_decoder.py
|
||||
test/codec/cer/test_encoder.py
|
||||
test/codec/der/__init__.py
|
||||
test/codec/der/suite.py
|
||||
test/codec/der/test_decoder.py
|
||||
test/codec/der/test_encoder.py
|
||||
test/type/__init__.py
|
||||
test/type/suite.py
|
||||
test/type/test_constraint.py
|
||||
test/type/test_namedtype.py
|
||||
test/type/test_tag.py
|
||||
test/type/test_univ.py
|
||||
1
python/pyasn1/pyasn1.egg-info/dependency_links.txt
Normal file
1
python/pyasn1/pyasn1.egg-info/dependency_links.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
1
python/pyasn1/pyasn1.egg-info/top_level.txt
Normal file
1
python/pyasn1/pyasn1.egg-info/top_level.txt
Normal file
@@ -0,0 +1 @@
|
||||
pyasn1
|
||||
1
python/pyasn1/pyasn1.egg-info/zip-safe
Normal file
1
python/pyasn1/pyasn1.egg-info/zip-safe
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
8
python/pyasn1/pyasn1/__init__.py
Normal file
8
python/pyasn1/pyasn1/__init__.py
Normal file
@@ -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')
|
||||
|
||||
1
python/pyasn1/pyasn1/codec/__init__.py
Normal file
1
python/pyasn1/pyasn1/codec/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file is necessary to make this directory a package.
|
||||
1
python/pyasn1/pyasn1/codec/ber/__init__.py
Normal file
1
python/pyasn1/pyasn1/codec/ber/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file is necessary to make this directory a package.
|
||||
808
python/pyasn1/pyasn1/codec/ber/decoder.py
Normal file
808
python/pyasn1/pyasn1/codec/ber/decoder.py
Normal file
@@ -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
|
||||
353
python/pyasn1/pyasn1/codec/ber/encoder.py
Normal file
353
python/pyasn1/pyasn1/codec/ber/encoder.py
Normal file
@@ -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)
|
||||
8
python/pyasn1/pyasn1/codec/ber/eoo.py
Normal file
8
python/pyasn1/pyasn1/codec/ber/eoo.py
Normal file
@@ -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()
|
||||
1
python/pyasn1/pyasn1/codec/cer/__init__.py
Normal file
1
python/pyasn1/pyasn1/codec/cer/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file is necessary to make this directory a package.
|
||||
35
python/pyasn1/pyasn1/codec/cer/decoder.py
Normal file
35
python/pyasn1/pyasn1/codec/cer/decoder.py
Normal file
@@ -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)
|
||||
87
python/pyasn1/pyasn1/codec/cer/encoder.py
Normal file
87
python/pyasn1/pyasn1/codec/cer/encoder.py
Normal file
@@ -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
|
||||
1
python/pyasn1/pyasn1/codec/der/__init__.py
Normal file
1
python/pyasn1/pyasn1/codec/der/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file is necessary to make this directory a package.
|
||||
9
python/pyasn1/pyasn1/codec/der/decoder.py
Normal file
9
python/pyasn1/pyasn1/codec/der/decoder.py
Normal file
@@ -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)
|
||||
28
python/pyasn1/pyasn1/codec/der/encoder.py
Normal file
28
python/pyasn1/pyasn1/codec/der/encoder.py
Normal file
@@ -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)
|
||||
1
python/pyasn1/pyasn1/compat/__init__.py
Normal file
1
python/pyasn1/pyasn1/compat/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file is necessary to make this directory a package.
|
||||
20
python/pyasn1/pyasn1/compat/octets.py
Normal file
20
python/pyasn1/pyasn1/compat/octets.py
Normal file
@@ -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)
|
||||
65
python/pyasn1/pyasn1/debug.py
Normal file
65
python/pyasn1/pyasn1/debug.py
Normal file
@@ -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()
|
||||
3
python/pyasn1/pyasn1/error.py
Normal file
3
python/pyasn1/pyasn1/error.py
Normal file
@@ -0,0 +1,3 @@
|
||||
class PyAsn1Error(Exception): pass
|
||||
class ValueConstraintError(PyAsn1Error): pass
|
||||
class SubstrateUnderrunError(PyAsn1Error): pass
|
||||
1
python/pyasn1/pyasn1/type/__init__.py
Normal file
1
python/pyasn1/pyasn1/type/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file is necessary to make this directory a package.
|
||||
249
python/pyasn1/pyasn1/type/base.py
Normal file
249
python/pyasn1/pyasn1/type/base.py
Normal file
@@ -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
|
||||
61
python/pyasn1/pyasn1/type/char.py
Normal file
61
python/pyasn1/pyasn1/type/char.py
Normal file
@@ -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"
|
||||
200
python/pyasn1/pyasn1/type/constraint.py
Normal file
200
python/pyasn1/pyasn1/type/constraint.py
Normal file
@@ -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
|
||||
3
python/pyasn1/pyasn1/type/error.py
Normal file
3
python/pyasn1/pyasn1/type/error.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from pyasn1.error import PyAsn1Error
|
||||
|
||||
class ValueConstraintError(PyAsn1Error): pass
|
||||
132
python/pyasn1/pyasn1/type/namedtype.py
Normal file
132
python/pyasn1/pyasn1/type/namedtype.py
Normal file
@@ -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]
|
||||
46
python/pyasn1/pyasn1/type/namedval.py
Normal file
46
python/pyasn1/pyasn1/type/namedval.py
Normal file
@@ -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?
|
||||
122
python/pyasn1/pyasn1/type/tag.py
Normal file
122
python/pyasn1/pyasn1/type/tag.py
Normal file
@@ -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)
|
||||
52
python/pyasn1/pyasn1/type/tagmap.py
Normal file
52
python/pyasn1/pyasn1/type/tagmap.py
Normal file
@@ -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
|
||||
1042
python/pyasn1/pyasn1/type/univ.py
Normal file
1042
python/pyasn1/pyasn1/type/univ.py
Normal file
File diff suppressed because it is too large
Load Diff
12
python/pyasn1/pyasn1/type/useful.py
Normal file
12
python/pyasn1/pyasn1/type/useful.py
Normal file
@@ -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)
|
||||
)
|
||||
5
python/pyasn1/setup.cfg
Normal file
5
python/pyasn1/setup.cfg
Normal file
@@ -0,0 +1,5 @@
|
||||
[egg_info]
|
||||
tag_build =
|
||||
tag_date = 0
|
||||
tag_svn_revision = 0
|
||||
|
||||
115
python/pyasn1/setup.py
Normal file
115
python/pyasn1/setup.py
Normal file
@@ -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)
|
||||
1
python/pyasn1/test/__init__.py
Normal file
1
python/pyasn1/test/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file is necessary to make this directory a package.
|
||||
1
python/pyasn1/test/codec/__init__.py
Normal file
1
python/pyasn1/test/codec/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file is necessary to make this directory a package.
|
||||
1
python/pyasn1/test/codec/ber/__init__.py
Normal file
1
python/pyasn1/test/codec/ber/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file is necessary to make this directory a package.
|
||||
22
python/pyasn1/test/codec/ber/suite.py
Normal file
22
python/pyasn1/test/codec/ber/suite.py
Normal file
@@ -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()
|
||||
535
python/pyasn1/test/codec/ber/test_decoder.py
Normal file
535
python/pyasn1/test/codec/ber/test_decoder.py
Normal file
@@ -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()
|
||||
338
python/pyasn1/test/codec/ber/test_encoder.py
Normal file
338
python/pyasn1/test/codec/ber/test_encoder.py
Normal file
@@ -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()
|
||||
1
python/pyasn1/test/codec/cer/__init__.py
Normal file
1
python/pyasn1/test/codec/cer/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file is necessary to make this directory a package.
|
||||
22
python/pyasn1/test/codec/cer/suite.py
Normal file
22
python/pyasn1/test/codec/cer/suite.py
Normal file
@@ -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()
|
||||
31
python/pyasn1/test/codec/cer/test_decoder.py
Normal file
31
python/pyasn1/test/codec/cer/test_decoder.py
Normal file
@@ -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()
|
||||
107
python/pyasn1/test/codec/cer/test_encoder.py
Normal file
107
python/pyasn1/test/codec/cer/test_encoder.py
Normal file
@@ -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()
|
||||
1
python/pyasn1/test/codec/der/__init__.py
Normal file
1
python/pyasn1/test/codec/der/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file is necessary to make this directory a package.
|
||||
22
python/pyasn1/test/codec/der/suite.py
Normal file
22
python/pyasn1/test/codec/der/suite.py
Normal file
@@ -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()
|
||||
20
python/pyasn1/test/codec/der/test_decoder.py
Normal file
20
python/pyasn1/test/codec/der/test_decoder.py
Normal file
@@ -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()
|
||||
44
python/pyasn1/test/codec/der/test_encoder.py
Normal file
44
python/pyasn1/test/codec/der/test_encoder.py
Normal file
@@ -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()
|
||||
29
python/pyasn1/test/codec/suite.py
Normal file
29
python/pyasn1/test/codec/suite.py
Normal file
@@ -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()
|
||||
26
python/pyasn1/test/suite.py
Normal file
26
python/pyasn1/test/suite.py
Normal file
@@ -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()
|
||||
1
python/pyasn1/test/type/__init__.py
Normal file
1
python/pyasn1/test/type/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file is necessary to make this directory a package.
|
||||
20
python/pyasn1/test/type/suite.py
Normal file
20
python/pyasn1/test/type/suite.py
Normal file
@@ -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()
|
||||
280
python/pyasn1/test/type/test_constraint.py
Normal file
280
python/pyasn1/test/type/test_constraint.py
Normal file
@@ -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?
|
||||
87
python/pyasn1/test/type/test_namedtype.py
Normal file
87
python/pyasn1/test/type/test_namedtype.py
Normal file
@@ -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()
|
||||
107
python/pyasn1/test/type/test_tag.py
Normal file
107
python/pyasn1/test/type/test_tag.py
Normal file
@@ -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()
|
||||
479
python/pyasn1/test/type/test_univ.py
Normal file
479
python/pyasn1/test/type/test_univ.py
Normal file
@@ -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()
|
||||
Reference in New Issue
Block a user