bug 1413336 - (1/7) update pyasn1 to 0.3.7 r=ted

MozReview-Commit-ID: L3bvhOZRQyN

--HG--
rename : third_party/python/pyasn1/test/__init__.py => third_party/python/pyasn1/pyasn1/codec/native/__init__.py
rename : third_party/python/pyasn1/test/__init__.py => third_party/python/pyasn1/tests/__init__.py
rename : third_party/python/pyasn1/test/__init__.py => third_party/python/pyasn1/tests/codec/__init__.py
rename : third_party/python/pyasn1/test/__init__.py => third_party/python/pyasn1/tests/codec/ber/__init__.py
rename : third_party/python/pyasn1/test/__init__.py => third_party/python/pyasn1/tests/codec/cer/__init__.py
rename : third_party/python/pyasn1/test/__init__.py => third_party/python/pyasn1/tests/codec/der/__init__.py
rename : third_party/python/pyasn1/test/__init__.py => third_party/python/pyasn1/tests/codec/native/__init__.py
rename : third_party/python/pyasn1/test/__init__.py => third_party/python/pyasn1/tests/compat/__init__.py
rename : third_party/python/pyasn1/test/__init__.py => third_party/python/pyasn1/tests/type/__init__.py
extra : rebase_source : e44063fcc7984b4c64906cf095390c2210404684
This commit is contained in:
David Keeler 2017-11-03 16:53:21 -07:00
parent c8aadb27c8
commit e577789849
156 changed files with 17978 additions and 9372 deletions

View File

@ -1,278 +0,0 @@
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

576
third_party/python/pyasn1/CHANGES.rst vendored Normal file
View File

@ -0,0 +1,576 @@
Revision 0.3.7, released 04-10-2017
-----------------------------------
- Fixed ASN.1 time types pickling/deepcopy'ing
Revision 0.3.6, released 21-09-2017
-----------------------------------
- End-of-octets encoding optimized at ASN.1 encoders
- The __getitem__/__setitem__ behavior of Set/Sequence and SetOf/SequenceOf
objects aligned with the canonical Mapping and Sequence protocols in part
- Fixed crash in ASN.1 encoder when encoding an explicitly tagged
component of a Sequence
Revision 0.3.5, released 16-09-2017
-----------------------------------
- Codecs signatures unified and pass the options kwargs through the
call chain
- Explicit tag encoding optimized to avoid unnecessary copying
- End-of-octets sentinel encoding optimized
- Refactored ASN.1 codecs properties to silently enforce proper
length and chunk size encoding modes
- Fixed DER encoder to always produce primitive encoding
- Fixed crash at SequenceOf native decoder
- Fixed Real.prettyPrint() to fail gracefully on overflow
- Fixed a couple of crashes when debug mode is enabled
Revision 0.3.4, released 07-09-2017
-----------------------------------
- Fixed Native encoder to handle SEQUENCE/SET objects without
the componentType property
- Added missing component-less SEQUENCE/SET objects dict duck-typing support
- Fixed unnecessary duplicate tags detection at NamesType.tagMap
- Fixed crash at SEQUENCE and SEQUENCE OF CER encoder when running
in schemaless mode
- Fixed Character types instantiation from OctetString type -- double
unicode decoding may have scrambled the data
Revision 0.3.3, released 27-08-2017
-----------------------------------
- Improved ASN.1 types instantiation performance
- Improved BER/CER/DER decoder performance by not unconditionally casting
substrate into str/bytes.
- Fixed exponential index size growth bug when building ambiguous
NamedTypes tree
- Fixed constructed types decoding failure at BER codec if running
in schema-less mode
- Fixed crash on prettyPrint'ing a SEQUENCE with no defined components
- Fixed SetOf ordering at CER/DER encoder
- Fixed crash on conditional binascii module import
- Fix to TagSet hash value build
Revision 0.3.2, released 04-08-2017
-----------------------------------
- Fixed SequenceOf/SetOf types initialization syntax to remain
backward compatible with pyasn1 0.2.*
- Rectified thread safety issues by moving lazy, run-time computation
into object initializer.
- Fixed .isValue property to return True for empty SetOf/SequenceOf
objects
- Fixed GeneralizedTime/UTCTime CER/DER codecs to actually get invoked
- Fixed DER/CER encoders handling optional SEQUENCE/SET fields containing
nested SEQUENCE/SET with optional fields.
- Fixed crash in SequenceOf/SetOf pretty printing and decoding (in some
cases)
- Fixed documentation markup issues.
Revision 0.3.1, released 26-07-2017
-----------------------------------
- ASN.1 types __init__(), .clone() and .subtype() signatures
refactored into keyword arguments to simplify their signatures.
- ASN.1 types initialization refactored to minimize the use of
relatively expensive isNoValue() call
- Lazily pre-populate list of values of Sequence/Set/Choice types
- NamedTypes comparison made more efficient
- More efficient constraints computation and code clean up
- The __getitem__() implementation of some ASN.1 types & tag object
refactored for better performance
- BER/CER/DER value encoders refactored to produce either tuple of
bytes or octet-stream depending on what is more optimal
- Reduced the frequency of expensive isinstance() calls
- Tag-related classes optimized, refactored into properties and
documented.
- The NamedValues implementation refactored to mimic Python dict, its use
in ASN.1 types refactored into properties and better documented.
WARNING: this change introduces a deviation from original API.
- NamedType family of classes overhauled, optimized and documented.
- The `componentType` attribute of constructed ASN.1 types turned
read-only on instances.
- Sequence/Set DER/CER/DER decoder optimized to skip the case of
reordered components handling when not necessary.
- Tags and constraints-related getter methods refactored into read-only
instance attributes.
- The .hasValue() method refactored into .isValue property. All ASN.1
objects now support them, not just scalars.
- The Real.{isInfinity, isPlusInfinity, isMinusInfinity} methods
refactored into properties and renamed into IsInf, IsPlusInf and isMinusInf
- The end-of-octets type refactored to ensure it is a singleton. Codecs
changed to rely on that for better performance.
- Codecs lookup made more efficient at BER/CER/DER decoder main loop by
assigning `typeId` to every ASN.1 type, not just ambiguous ones.
- The .getComponent*() methods of constructed ASN.1 types changed
to lazily instantiate underlying type rather than return `None`.
This should simplify its API as initialization like `X[0][1] = 2` becomes
possible.
WARNING: this change introduces a deviation from the original API.
- The .setComponent*() methods of SetOf/SequenceOf types changed not
to allow uninitialized "holes" inside the sequences of their components.
They now behave similarly to Python lists.
WARNING: this change introduces a deviation from the original API.
- Default and optional components en/decoding of Constructed type
refactored towards better efficiency and more control.
- OctetsString and Any decoder optimized to avoid creating ASN.1
objects for chunks of substrate. Instead they now join substrate
chunks together and create ASN.1 object from it just once.
- The GeneralizedTime and UTCTime types now support to/from Python
datetime object conversion.
- Unit tests added for the `compat` sub-package.
- Fixed BitString named bits initialization bug.
- Fixed non-functional tag cache (when running Python 2) at DER decoder.
- Fixed chunked encoding restriction on DER encoder.
- Fixed SET components ordering at DER encoder.
- Fixed BIT STRING & OCTET STRING encoding to be always non-chunked (e.g.
primitive) at DER encoder
- Fixed `compat.integer.from_bytes()` behaviour on empty input.
Revision 0.2.3, released 25-02-2017
-----------------------------------
- Improved SEQUENCE/SET/CHOICE decoding performance by maintaining a single shared
NamedType object for all instances of SEQUENCE/SET object.
- Improved INTEGER encoding/decoding by switching to Python's built-in
integer serialization functions.
- Improved BitString performance by rebasing it onto Python int type and leveraging
fast Integer serialization functions.
- BitString type usability improved in many ways: for example bitshifting and
numeric operation on BitString is now possible.
- Minor ObjectIdentifier type performance optimization.
- ASN.1 character types refactored to keep unicode contents internally
(rather than serialized octet stream) and duck-type it directly.
- ASN.1 OctetString initialized from a Python object performs bytes()
on it when running on Python 3 (used to do str() which is probably
less logical).
- Missing support for NoValue.__sizeof__ added.
- Added checks to make sure SEQUENCE/SET components being assigned
match the prototypes.
- Setter methods for constructed types consistently accept matchTags
and matchConstraints flags to control the strictness of inner
components compatibility verification. Previously, these checks
were tied to verifyConstraints flag, now they are all independent.
- General documentation improvements here and there.
- Fix to __reversed__() magic to make it returning an iterator.
- Test suite simplified and unified.
- The __all__ variable added to most of the Python modules.
- The "test" directory renamed into "tests" not to collide with
the "test" module.
Revision 0.2.2, released 07-02-2017
-----------------------------------
- FIX TO A SECURITY WEAKNESS: define length only decoders could have successfully
processed indefinite length serialization.
- FIX TO A SECURITY WEAKNESS: canonical decoders (CER/DER) may have successfully
consumed non-canonical variations of (otherwise valid) serialization.
- Broken Enumerated subtyping fixed.
Revision 0.2.1, released 05-02-2017
-----------------------------------
- FIX TO A SECURITY WEAKNESS: BER decoder improperly cached long tags.
- New "native" codec implemented to transform pyasn1 types to Python built-in types and back.
- Switched to new-style classes.
- Sphinx documentation added.
- BitString improvements:
* simple string of binary digits is now supported as initializer
* default str() yields string of binary digits (used to yield str(tuple())
* binValue and hexValue initializers added
* .asNumbers(), .asOctets() and asInteger() representation added
- Components of constructed ASN.1 types can now be populated with
uninitialized ASN.1 objects by assigning either noValue sentinel or
setupComponent() function return in addition to now-legacy None sentinel.
This should improve code readability.
- NoValue class improved to become a singleton and catch more kinds
of access to it.
- Compatibility wrappers str2octs() and oct2strs() fixed to run over
iso-8859-1 encoding.
- Integer changed to emit Real instance if division produces a float.
- True division operation now supported by Integer type.
- The __contains__(), __reverse__() methods implemented for container types
- Iterator protocol support implemented for all container types.
Warning, warning, warning: this change may potentially affect backward
compatibility when:
* user class overrides __getitem__() without overriding __iter__()
* when user code iterates over SEQUENCE object to get its components (now keys will be yielded)
- Almost complete Python list and dict protocols added to SequenceOf/SetOf and
Sequence/Set respectively
- Fix to divmod operation implementation in Integer type.
- Fix to IntegerDecoder's precomputed value map on Python 3.
- Fix to base ASN.1 types to run in "unicode_literals" mode.
- Fix to composite constraints "+" operands ordering (AbstractConstraintSet.__radd__)
- Fix to constraints merge in .subtype() -- on merge existing constraints are
expanded to accommodate new constraints, not the other way round. When existing
constraints are wrapped in ConstraintsIntersection composite, additional
constraints being added on subtyping effectively further narrow the set of
allowed values, which aligns well with the notion of subtyping.
- Fix to NamedTypes methods to handle .getTagMap() returning None
- Fix to Set/Sequence.setDefaultComponents() to return self
- Copyright notice added to non-trivial source code files.
- Author's email changed, copyright extended to 2017
Revision 0.1.9, released 28-09-2015
-----------------------------------
- Wheel distribution format now supported.
- Extensions added to text files, CVS attic flushed.
- Fix to make uninitialized pyasn1 objects failing properly on hash().
- Fix to ObjectIdentifier initialization from unicode string.
- Fix to CER/DER Boolean decoder - fail on non single-octet payload.
Revision 0.1.8, released 22-06-2015
-----------------------------------
- ObjectIdentifier codec fixed to work properly with arc 0 and arc 2 values.
- Explicit limit on ObjectIdentifier arc value size removed.
- Unicode initializer support added to OctetString type and derivatives.
- New prettyPrintType() abstract method implemented to base pyasn1 types
to facilitate encoding errors analysis.
- The __str__() method implemented to Tag, TagSet and TagMap classes to
ease encoding errors troubleshooting.
easing encoding errors
- Fix to SEQUENCE and SET types to give them their private componentTypes
collection (which is a NamedTypes object) so that they won't collide in
a MT execution environment.
- Missing T61String,ISO646String character types and ObjectDescriptor useful
type added.
- Distribute is gone, switched to setuptools completely.
- Missing NamedValues.__repr__() added.
- The base.NoValue() class, that indicates uninitialized ASN.1 object,
made public.
- The base.NoValue() class instances now support __repr__() what makes
possible to perform repr() on uninitialized pyasn1 types objects.
- When comparing ASN.1 types, by-tag and/or by-constraints matching
can now be performed with the isSuperTypeOf()/isSameTypeWith() optional
flags.
- Constructed types now verify their consistency by invoking
isSameTypeWith(matchTags=True, matchConstraints=False) and
isSuperTypeOf(matchTags=False, matchConstraints=True) for each of their
components rather than isSuperTypeOf() as it used to be. Constriants check
could be enforced to isSameTypeWith() with the strictConstraints=True
constructed classes attribute.
- Constructed types can now be initialized with new .setComponents() method
which accepts both var-args and keyword-args. Default repr() modified to
reflect this change.
- NamedTypes() and NamedValues() made comparable.
- Test coverage extended to cover pyasn1 types __repr__() function.
- The abs(Integer()) & abs(Real()) operation now returns respective pyasn1
type, not a Python type.
- More Python magic methods implementations added to Integer & Real classes
(e.g. __pos__, __neg__, __round__, __floor__, __ceil__, __trunc__)
- The Integer.__invert__ Python magic method implemented.
- The OctetString.__int__() and .__float__() magic methods implemented.
- Handle the case of null writer at Debug printer.
- BitString encoder/decoder performance improved.
- Built-in debugging is now based on Python logging module.
- Fix to NamedType.__repr__() to work properly.
- Fixes to __repr__() implementation of many built-in ASN.1 types to take into
account all of their initializers such as tagSet, subtypeSpec etc.
- String typed float initializer to REAL type now supported.
- Float typed mantissa initializer to REAL type for base 2 added.
- Encoding bases 8 and 16 support for REAL type binary encoder added.
- More strict CER/DER encoders added for GeneralizedTime and UTCTime types.
- Asn1Item.hasValue() added to easily distinguish initalized ASN.1 objects
from uninitialized ones (e.g. pure types).
- Fix to REAL type binary decoder to handle different bases and scale factor.
- Fix to TagSet.repr() to include [obsolete] baseTag information.
- Fix to broken REAL type decoding handling.
- Fix to BitString and OctetString decoders dealing with constructed
encoding -- it used to be possible to embed other types in substrate.
- DER codec hardened not to tolerate indefinite length encoding/decoding.
- Fix to end-of-octest sentinel handling:
+ require strict two-zeros sentinel encoding
+ recognize EOO sentinel only when explicitly requested by caller
of the decoder via allowEoo=True parameter (warning: API change)
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 with schema 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 with schema
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

View File

@ -1,4 +1,4 @@
Copyright (c) 2005-2013, Ilya Etingof <ilya@glas.net>
Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,3 +1,5 @@
include CHANGES README LICENSE THANKS TODO
recursive-include test *.py
recursive-include doc *.html
include *.rst *.md
recursive-include tests *.py
recursive-include doc Makefile *.rst conf.py
prune doc/build
prune doc/source/.templates

View File

@ -1,26 +1,33 @@
Metadata-Version: 1.0
Metadata-Version: 1.1
Name: pyasn1
Version: 0.1.7
Version: 0.3.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
Home-page: https://github.com/etingof/pyasn1
Author: Ilya Etingof <etingof@gmail.com>
Author-email: etingof@gmail.com
License: BSD
Description: A pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208).
Description: 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 :: 2.4
Classifier: Programming Language :: Python :: 2.5
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Topic :: Communications
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules

View File

@ -1,68 +0,0 @@
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

184
third_party/python/pyasn1/README.md vendored Normal file
View File

@ -0,0 +1,184 @@
ASN.1 library for Python
------------------------
[![PyPI](https://img.shields.io/pypi/v/pyasn1.svg?maxAge=2592000)](https://pypi.python.org/pypi/pyasn1)
[![Python Versions](https://img.shields.io/pypi/pyversions/pyasn1.svg)](https://pypi.python.org/pypi/pyasn1/)
[![Build status](https://travis-ci.org/etingof/pyasn1.svg?branch=master)](https://secure.travis-ci.org/etingof/pyasn1)
[![Coverage Status](https://img.shields.io/codecov/c/github/etingof/pyasn1.svg)](https://codecov.io/github/etingof/pyasn1)
[![GitHub license](https://img.shields.io/badge/license-BSD-blue.svg)](https://raw.githubusercontent.com/etingof/pyasn1/master/LICENSE.txt)
This is a free and open source implementation of ASN.1 types and codecs
as a Python package. 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](https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-X.208-198811-W!!PDF-E&type=items).
Features
--------
* Generic implementation of ASN.1 types (X.208)
* Standards compliant BER/CER/DER codecs
* Dumps/loads ASN.1 structures from Python types
* 100% Python, works with Python 2.4 up to Python 3.6
* MT-safe
* Contributed ASN.1 compiler [Asn1ate](https://github.com/kimgr/asn1ate)
Why using pyasn1
----------------
ASN.1 solves the data serialization problem. This solution was
designed long ago by the wise Ancients. Back then, they did not
have the luxury of wasting bits. That is why ASN.1 is designed
to serialize data structures of unbounded complexity into
something compact and efficient when it comes to processing
the data.
That probably explains why many network protocols and file formats
still rely on the 30+ years old technology. Including a number of
high-profile Internet protocols and file formats.
Quite a number of books cover the topic of ASN.1.
[Communication between heterogeneous systems](http://www.oss.com/asn1/dubuisson.html)
by Olivier Dubuisson is one of those high quality books freely
available on the Internet.
The pyasn1 package is designed to help Python programmers tackling
network protocols and file formats at the comfort of their Python
prompt. The tool struggles to capture all aspects of a rather
complicated ASN.1 system and to represent it on the Python terms.
How to use pyasn1
-----------------
With pyasn1 you can build Python objects from ASN.1 data structures.
For example, the following ASN.1 data structure:
```bash
Record ::= SEQUENCE {
id INTEGER,
room [0] INTEGER OPTIONAL,
house [1] INTEGER DEFAULT 0
}
```
Could be expressed in pyasn1 like this:
```python
class Record(Sequence):
componentType = NamedTypes(
NamedType('id', Integer()),
OptionalNamedType(
'room', Integer().subtype(
implicitTag=Tag(tagClassContext, tagFormatSimple, 0)
)
),
DefaultedNamedType(
'house', Integer(0).subtype(
implicitTag=Tag(tagClassContext, tagFormatSimple, 1)
)
)
)
```
It is in the spirit of ASN.1 to take abstract data description
and turn it into a programming language specific form.
Once you have your ASN.1 data structure expressed in Python, you
can use it along the lines of similar Python type (e.g. ASN.1
`SET` is similar to Python `dict`, `SET OF` to `list`):
```python
>>> record = Record()
>>> record['id'] = 123
>>> record['room'] = 321
>>> print(record.prettyPrint())
Record:
id=123
room=321
>>>
```
Part of the power of ASN.1 comes from its serialization features. You
can serialize your data structure and send it over the network.
```python
>>> from pyasn1.codec.der.encoder import encode
>>> substrate = encode(record)
>>> hexdump(substrate)
00000: 30 07 02 01 7B 80 02 01 41
```
Conversely, you can turn serialized ASN.1 content, as received from
network or read from a file, into a Python object which you can
introspect, modify, encode and send back.
```python
>>> from pyasn1.codec.der.decoder import decode
>>> received_record, rest_of_substrate = decode(substrate, asn1Spec=Record())
>>>
>>> for field in received_record:
>>> print('{} is {}'.format(field, received_record[field]))
id is 123
room is 321
house is 0
>>>
>>> record == received_record
True
>>> received_record.update(room=123)
>>> substrate = encode(received_record)
>>> hexdump(substrate)
00000: 30 06 02 01 7B 80 01 7B
```
The pyasn1 classes struggle to emulate their Python prototypes (e.g. int,
list, dict etc.). But ASN.1 types exhibit more complicated behaviour.
To make life easier for a Pythonista, they can turn their pyasn1
classes into Python built-ins:
```python
>>> from pyasn1.codec.native.encoder import encode
>>> encode(record)
{'id': 123, 'room': 321, 'house': 0}
```
Or vice-versa -- you can initialize an ASN.1 structure from a tree of
Python objects:
```python
>>> from pyasn1.codec.native.decoder import decode
>>> record = decode({'id': 123, 'room': 321, 'house': 0}, asn1Spec=Record())
>>> print(record.prettyPrint())
Record:
id=123
room=321
>>>
```
With ASN.1 design, serialization codecs are decoupled from data objects,
so you could turn every single ASN.1 object into many different
serialized forms. As of this moment, pyasn1 supports BER, DER, CER and
Python built-ins codecs. The extremely compact PER encoding is expected
to be introduced in the upcoming pyasn1 release.
More information on pyasn1 APIs can be found in the
[documentation](http://pyasn1.sourceforge.net),
compiled ASN.1 modules for different protocols and file formats
could be found in the pyasn1-modules
[repo](https://github.com/etingof/pyasn1-modules).
How to get pyasn1
-----------------
The pyasn1 package is distributed under terms and conditions of 2-clause
BSD [license](http://pyasn1.sourceforge.net/license.html). Source code is freely
available as a GitHub [repo](https://github.com/etingof/pyasn1).
You could `pip install pyasn1` or download it from [PyPI](https://pypi.python.org/pypi/pyasn1).
If something does not work as expected,
[open an issue](https://github.com/etingof/pyasn1/issues) at GitHub or
post your question [on Stack Overflow](http://stackoverflow.com/questions/ask)
or try browsing pyasn1
[mailing list archives](https://sourceforge.net/p/pyasn1/mailman/pyasn1-users/).
Copyright (c) 2005-2017, [Ilya Etingof](mailto:etingof@gmail.com).
All rights reserved.

View File

@ -1,4 +0,0 @@
Denis S. Otkidach
Gregory Golberg
Bud P. Bruegger
Jacek Konieczny

View File

@ -1,36 +0,0 @@
* 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?

92
third_party/python/pyasn1/TODO.rst vendored Normal file
View File

@ -0,0 +1,92 @@
Things to be done
=================
Big things to tackle, anyone interested is welcome to fork pyasn1, work on
it and come up with a PR!
New codecs
----------
* PER
* OER
* XER
* LWER
* JSON (alinged with existing experimental schemas)
Lazy codecs
-----------
Implement a thin layer over base types to cache pieces
of substrate being decoded till the very moment of ASN.1
object access in the parse tree.
Codecs generator interface
--------------------------
For indefinite length or chunked encoding mode, make codecs
iterable producing/consuming substrate/objects.
ASN.1 schema compiler
---------------------
Ideally, the compiler should parse modern schema files and be
designed to emit code for arbitrary languages (including SQL).
Base types
----------
Implement X.680 constructs, including information schema.
Examples
--------
Add examples, including advanced/obscure use cases.
Documentation
-------------
Document more API, add notes and example snippets.
More fresh modules
------------------
Compile and ship more Pythonized ASN.1 modules for
various ASN.1-based protocols (e.g. Kerberos etc).
Refresh outdated modules in pyasn1-packages.
Minor, housekeeping things
--------------------------
* more PEP8'ing at places
* consider simplifying repr(), otherwise it tend to be too hard to grasp
* Specialize ASN.1 character and useful types
* ber.decoder:
* suspend codec on underrun error ?
* present subtypes ?
* component presence check wont work at innertypeconst
* type vs value, defaultValue
* ber.encoder:
* Asn1Item.clone() / shallowcopy issue
* large length encoder?
* lookup type by tag first to allow custom codecs for non-base types
* type.useful:
* may need to implement prettyIn/Out
* type.char:
* may need to implement constraints
* type.namedtypes
* type vs tagset name convention
* how untagged TagSet should be initialized?
* type and codecs for Real needs refactoring

192
third_party/python/pyasn1/doc/Makefile vendored Normal file
View File

@ -0,0 +1,192 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " applehelp to make an Apple Help Book"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " coverage to run coverage check of the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyASN1.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyASN1.qhc"
applehelp:
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
@echo
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
@echo "N.B. You won't be able to view it unless you put it in" \
"~/Library/Documentation/Help or install it in your application" \
"bundle."
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/PyASN1"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyASN1"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
coverage:
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
@echo "Testing of coverage in the sources finished, look at the " \
"results in $(BUILDDIR)/coverage/python.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

View File

@ -1,503 +0,0 @@
<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>

View File

@ -1,436 +0,0 @@
<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>

View File

@ -1,377 +0,0 @@
<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>

View File

@ -1,156 +0,0 @@
<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>

File diff suppressed because it is too large Load Diff

View File

@ -1,794 +0,0 @@
<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>

View File

@ -0,0 +1,6 @@
Changelog
=========
.. include:: ../../CHANGES.rst

View File

@ -0,0 +1,323 @@
# -*- coding: utf-8 -*-
#
# PyASN1 documentation build configuration file, created by
# sphinx-quickstart on Sat Jun 27 23:15:54 2015.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
import shlex
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'sphinx.ext.doctest',
'sphinx.ext.intersphinx',
'sphinx.ext.todo'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['.templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'contents'
# General information about the project.
project = u'PyASN1'
# noinspection PyShadowingBuiltins
copyright = u'2005-2017, Ilya Etingof <etingof@gmail.com>'
author = u'Ilya Etingof <etingof@gmail.com>'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.3'
# The full version, including alpha/beta/rc tags.
release = '0.3.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
# The reST default role (used for this markup: `text`) to use for all
# documents.
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
# keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
# html_theme = 'alabaster'
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
# html_title = None
html_title = "PyASN1"
# A shorter title for the navigation bar. Default is the same as html_title.
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
# html_logo = ""
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
if 'PYASN1DEV' in os.environ:
html_static_path = ['.static']
# Custom CSS theme
if 'PYASN1DEV' in os.environ:
html_style = 'css/rtdimproved.css'
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
# html_additional_pages = {}
# If false, no module index is generated.
# html_domain_indices = True
# If false, no index is generated.
# html_use_index = True
# If true, the index is split into individual pages for each letter.
# html_split_index = False
# If true, links to the reST sources are added to the pages.
html_show_sourcelink = False
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
html_show_sphinx = False
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
# html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
# html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
# html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename = 'pyasn1doc'
html_context = {
'include_analytics': 'PYASN1DEV' in os.environ
}
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
# 'preamble': '',
# Latex figure (float) alignment
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'PyASN1.tex', u'PyASN1 Documentation',
u'Ilya Etingof \\textless{}etingof@gmail.com\\textgreater{}', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
# latex_use_parts = False
# If true, show page references after internal links.
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
# latex_appendices = []
# If false, no module index is generated.
# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'pyasn1', u'PyASN1 Documentation',
[author], 1)
]
# If true, show URL addresses after external links.
# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'PyASN1', u'PyASN1 Documentation',
author, 'PyASN1', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
# texinfo_appendices = []
# If false, no module index is generated.
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
# texinfo_no_detailmenu = False
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'python': ('https://docs.python.org/3.4/', None)}
# this merges constructor docstring with class docstring
autoclass_content = 'both'
# Sort members by type
autodoc_member_order = 'bysource'
# autodoc_member_order = 'groupwise'
# Napoleon settings
napoleon_google_docstring = False
napoleon_numpy_docstring = True
napoleon_include_private_with_doc = False
napoleon_include_special_with_doc = True
napoleon_use_admonition_for_examples = False
napoleon_use_admonition_for_notes = False
napoleon_use_admonition_for_references = False
napoleon_use_ivar = False
napoleon_use_param = False
napoleon_use_rtype = False

View File

@ -0,0 +1,194 @@
ASN.1 library for Python
========================
.. toctree::
:maxdepth: 2
Abstract Syntax Notation One (`ASN.1
<http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_1x>`_) is a
technology for exchanging structured data in a universally understood,
hardware agnostic way. Many industrial, security and telephony
applications heavily rely on ASN.1.
The `pyasn1 <https://pypi.python.org/pypi/pyasn1/>`_ library implements
ASN.1 support in pure-Python.
What is ASN.1
-------------
ASN.1 is a large, arguably over-engineered and extremely old data modelling and
serialization tool. It is probably among the first serialization protocols in
the history of computer science and technology.
ASN.1 started its life over 30 years ago as a serialization mechanism for the first
electronic mail (known as X.400). Later on if was split off the e-mail application
and become a stand-alone tech still being actively supported by its designers
and widely used in industry and technology.
Since then ASN.1 is sort of haunted by its relations with the OSI model -- the
first, unsuccessful, version of the Internet. You can read many interesting
`discussions <https://news.ycombinator.com/item?id=8871453>`_ on that topic.
In the following years, generations of software engineers tackled the serialization
problem many times. We can see that in Google's `ProtoBuffers <https://developers.google.com/protocol-buffers/>`_
or `FlatBuffers <https://google.github.io/flatbuffers/>`_, for example.
Interestingly, many new takes on binary protocol design do not depart
far from ASN.1 from technical perspective. It's more of a matter of striking
a balance between processing overhead, wire format overhead and human
readability.
Looking at what ASN.1 has to offer, it has three loosely coupled parts:
* Data types: the standard introduces a collection of basic data types
(integers, bits, strings, arrays and records) that can be used for describing
arbitrarily complex, nested data structures.
* Serialization protocols: the above data structures could be converted into a
series of octets for storage or transmission over the wire as well as
recovered back into their structured form. The system is fully agnostic
to hardware architectures differences.
* Schema language: ASN.1 data structures could be described in terms
of a schema language for ASN.1 compiler to turn it into platform-specific
implementation.
ASN.1 applications
------------------
Being an old and generally successful standard, ASN.1 is widely
adopted for many uses. To give you an example, these technologies
use ASN.1 for their data exchange needs:
* Signaling standards for the public switched telephone network (SS7 family)
* Network management standards (SNMP, CMIP)
* Directory standards (X.500 family, LDAP)
* Public Key Infrastructure standards (X.509, etc.)
* PBX control (CSTA)
* IP-based Videoconferencing (H.323 family)
* Biometrics (BIP, CBEFF, ACBio)
* Intelligent transportation (SAE J2735)
* Cellular telephony (GSM, GPRS/EDGE, UMTS, LTE)
ASN.1 gotchas
-------------
Apparently, ASN.1 is hard to implement properly. Quality open-source
ASN.1 tools are rare, but ad-hoc implementations are numerous. Judging from the
`statistics <http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=ASN.1>`_ on discovered
security vulnerabilities, many people have implemented ASN.1 parsers
and oftentimes fell victim to its edge cases.
On the bright side, ASN.1 has been around for a long time, it is well understood
and security reviewed.
Library capabilities
--------------------
As of this moment, pyasn1 library implements all ASN.1 data
types as Python objects in accordance with X.208 standard. Later,
post-1995, revision (X.680) introduced some changes to the schema
language which may not be fully supported by pyasn1. Aside from data
types a collection of data transformation codecs comes with pyasn1 package.
As for ASN.1 schema language, pyasn1 package does
not ship any compiler for it. However, there's a tool called
`asn1late <https://github.com/kimgr/asn1ate>`_ which is an ASN.1
grammar parser paired to code generator capable of generating pyasn1
code. So this is an alternative (or at least a good start) to manual
implementation of pyasn1 classes from ASN.1 specification.
Both `pyasn1 <https://github.com/etingof/pyasn1>`_ and
`pyasn1-modules <https://github.com/etingof/pyasn1-modules>`_ libraries
can be used out-of-the-box with Python versions 2.4 through 3.6.
No external dependencies required.
Documentation
-------------
.. toctree::
:maxdepth: 2
/docs/tutorial
/docs/api-reference
Use case
--------
.. toctree::
:maxdepth: 2
/example-use-case
Download & Install
------------------
The PyASN1 software is provided under terms and conditions of BSD-style
:ref:`license`, and can be freely downloaded from `Github <https://github.com/etingof/pyasn1>`_
or `PyPI <http://pypi.python.org/pypi/pyasn1/>`_.
It is pure-Python and has no dependencies. Considering how much industrial or finance
software can be stuck with an old platform (think RHEL 5), we struggle to maintain its
compatibility back to the very pre-historic Python (which is 2.4!).
The best way to obtain PyASN1 is by running `pip`:
.. code-block:: bash
$ pip install pyasn1
or
.. code-block:: bash
$ easy_install pyasn1
You may also want to use `pyasn1-modules`:
.. code-block:: bash
$ pip install pyasn1-modules
Changes
-------
All changes and release history is maintained in changelog. There you
could also download the latest unreleased pyasn1 tarball containing
the latest fixes and improvements.
.. toctree::
:maxdepth: 1
/changelog
Getting help
------------
Please, file your `issues <https://github.com/etingof/pyasn1/issues>`_
and `PRs <https://github.com/etingof/pyasn1/pulls>`_ at GitHub.
Alternatively, you could ask for help at
`Stack Overflow <http://stackoverflow.com/questions/tagged/pyasn1>`_
or search
`pyasn1-users <https://lists.sourceforge.net/lists/listinfo/pyasn1-users>`_
mailing list archive.
Books on ASN.1
--------------
The pyasn1 implementation is largely based on reading up the following awesome
books:
* `ASN.1 - Communication between heterogeneous systems <http://www.oss.com/asn1/dubuisson.html>`_ by Olivier Dubuisson
* `ASN.1 Complete <http://www.oss.com/asn1/resources/books-whitepapers-pubs/larmouth-asn1-book.pdf>`_ by Prof John Larmouth
Here you can get the official standards which is hard to read:
* `ITU standards <http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-X.693-0207w.zip>`_
On the other end of the readability spectrum, here is a quick and sweet write up:
* `A Layman's Guide to a Subset of ASN.1, BER, and DER <ftp://ftp.rsasecurity.com/pub/pkcs/ascii/layman.asc>`_ by Burton S. Kaliski
If you are working with ASN.1, we'd highly recommend reading a proper
book on the subject.

View File

@ -0,0 +1,30 @@
Library reference
=================
.. toctree::
:maxdepth: 2
ASN.1 types
-----------
.. toctree::
:maxdepth: 2
/docs/type/univ/contents
/docs/type/char/contents
/docs/type/useful/contents
/docs/type/tag/contents
/docs/type/namedtype/contents
/docs/type/namedval/contents
Transformation codecs
---------------------
.. toctree::
:maxdepth: 2
/docs/codec/ber/contents
/docs/codec/cer/contents
/docs/codec/der/contents
/docs/codec/native/contents

View File

@ -0,0 +1,7 @@
Basic Encoding Rules
--------------------
.. autofunction:: pyasn1.codec.ber.encoder.encode(value, defMode=True, maxChunkSize=0)
.. autofunction:: pyasn1.codec.ber.decoder.decode(substrate, asn1Spec=None)

View File

@ -0,0 +1,7 @@
Canonical Encoding Rules
------------------------
.. autofunction:: pyasn1.codec.cer.encoder.encode(value)
.. autofunction:: pyasn1.codec.cer.decoder.decode(substrate, asn1Spec=None)

View File

@ -0,0 +1,7 @@
Distinguished Encoding Rules
----------------------------
.. autofunction:: pyasn1.codec.der.encoder.encode(value)
.. autofunction:: pyasn1.codec.der.decoder.decode(substrate, asn1Spec=None)

View File

@ -0,0 +1,7 @@
Native Python types
-------------------
.. autofunction:: pyasn1.codec.native.encoder.encode(asn1Value)
.. autofunction:: pyasn1.codec.native.decoder.decode(pyObject, asn1Spec)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: BMPString
.. |encoding| replace:: utf-16-be
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.BMPString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| type models a Unicode (ISO10646-1) character string implicitly serialized into UTF-16 big endian.
.. automethod:: pyasn1.type.char.BMPString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.BMPString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,20 @@
Character types
---------------
.. toctree::
:maxdepth: 2
/docs/type/char/numericstring
/docs/type/char/printablestring
/docs/type/char/teletexstring
/docs/type/char/t61string
/docs/type/char/videotexstring
/docs/type/char/ia5string
/docs/type/char/graphicstring
/docs/type/char/visiblestring
/docs/type/char/iso646string
/docs/type/char/generalstring
/docs/type/char/universalstring
/docs/type/char/bmpstring
/docs/type/char/utf8string

View File

@ -0,0 +1,18 @@
.. |ASN.1| replace:: GeneralString
.. |encoding| replace:: iso-8859-1
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.GeneralString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| type models a character string similar to :py:class:`GraphicString` but additionally
including control characters.
.. automethod:: pyasn1.type.char.GeneralString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.GeneralString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,18 @@
.. |ASN.1| replace:: GraphicString
.. |encoding| replace:: iso-8859-1
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.GraphicString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| type models a character string that can hold any "graphical" characters
mixed with control ones to select particular alphabet.
.. automethod:: pyasn1.type.char.GraphicString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.GraphicString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: IA5String
.. |encoding| replace:: us-ascii
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.IA5String(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| type models a basic character string first published in 1963 as an ISO/ITU standard, then it turned into ASCII.
.. automethod:: pyasn1.type.char.IA5String.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.IA5String.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: ISO646String
.. |encoding| replace:: us-ascii
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.ISO646String(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| type is an alias to the :py:class:`VisibleString` type
.. automethod:: pyasn1.type.char.ISO646String.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.ISO646String.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: NumericString
.. |encoding| replace:: us-ascii
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.NumericString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| models character string that can be entered from a telephone handset.
.. automethod:: pyasn1.type.char.NumericString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.NumericString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,18 @@
.. |ASN.1| replace:: PrintableString
.. |encoding| replace:: us-ascii
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.PrintableString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| models character string that can be entered from a very rudimentary terminals featuring letters,
digits and punctuation marks.
.. automethod:: pyasn1.type.char.PrintableString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.PrintableString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: T61String
.. |encoding| replace:: iso-8859-1
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.T61String(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| type is an alias to :py:class:`TeletexString` type.
.. automethod:: pyasn1.type.char.T61String.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.T61String.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,19 @@
.. |ASN.1| replace:: TeletexString
.. |encoding| replace:: iso-8859-1
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.TeletexString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| type models character string that can be entered from a sophisticated text processing machines
(by 20-th century standards) featuring letters from multiple alphabets (308 characters!), digits,
punctuation marks and escape sequences.
.. automethod:: pyasn1.type.char.TeletexString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.TeletexString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: UniversalString
.. |encoding| replace:: utf-32-be
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.UniversalString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| type models a Unicode (ISO10646-1) character string implicitly serialized into UTF-32 big endian.
.. automethod:: pyasn1.type.char.UniversalString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.UniversalString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: UTF8String
.. |encoding| replace:: utf-8
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.UTF8String(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| type models a Unicode (ISO10646-1) character string implicitly serialized into UTF-8.
.. automethod:: pyasn1.type.char.UTF8String.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.UTF8String.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,18 @@
.. |ASN.1| replace:: VideotexString
.. |encoding| replace:: iso-8859-1
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.VideotexString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| type models character string that can be consumed by sophisticated video
terminals (by 20-th century standards) to render ascii-art style pictures and animations.
.. automethod:: pyasn1.type.char.VideotexString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.VideotexString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,18 @@
.. |ASN.1| replace:: VisibleString
.. |encoding| replace:: us-ascii
|ASN.1| type
------------
.. autoclass:: pyasn1.type.char.VisibleString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap
.. note::
The |ASN.1| type models a character string that can hold any "graphical" characters
mixed with control ones to select particular alphabet.
.. automethod:: pyasn1.type.char.VisibleString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.char.VisibleString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,11 @@
Fields of constructed types
---------------------------
.. toctree::
:maxdepth: 2
/docs/type/namedtype/namedtype
/docs/type/namedtype/optionalnamedtype
/docs/type/namedtype/defaultednamedtype
/docs/type/namedtype/namedtypes

View File

@ -0,0 +1,12 @@
.. |NamedType| replace:: DefaultedNamedType
|NamedType|
------------------
.. autoclass:: pyasn1.type.namedtype.DefaultedNamedType
:members:
.. note::
The |NamedType| class models named field of a constructed ASN.1 type which has a default value.

View File

@ -0,0 +1,12 @@
.. |NamedType| replace:: NamedType
|NamedType|
-----------
.. autoclass:: pyasn1.type.namedtype.NamedType
:members:
.. note::
The |NamedType| class models a mandatory field of a constructed ASN.1 type.

View File

@ -0,0 +1,6 @@
NamedTypes
----------
.. autoclass:: pyasn1.type.namedtype.NamedTypes
:members:

View File

@ -0,0 +1,12 @@
.. |NamedType| replace:: OptionalNamedType
|NamedType|
------------------
.. autoclass:: pyasn1.type.namedtype.OptionalNamedType
:members:
.. note::
The |NamedType| class models an optional field of a constructed ASN.1 type.

View File

@ -0,0 +1,13 @@
Enumerating numbers
-------------------
Some ASN.1 types such as :py:class:`~pyasn1.type.univ.Integer`,
:py:class:`~pyasn1.type.univ.Enumerated` and
:py:class:`~pyasn1.type.univ.BitString` may enumerate their values
with human-friendly labels.
.. toctree::
:maxdepth: 2
/docs/type/namedval/namedval

View File

@ -0,0 +1,11 @@
.. |NamedValues| replace:: NamedValues
|NamedValues|
-------------
The |NamedValues| class associates human-friendly names to a set of numbers
or bits.
.. autoclass:: pyasn1.type.namedval.NamedValues
:members:

View File

@ -0,0 +1,10 @@
Tagging types
-------------
.. toctree::
:maxdepth: 2
/docs/type/tag/tag
/docs/type/tag/tagset
/docs/type/tag/tagmap

View File

@ -0,0 +1,8 @@
Solitary tag
------------
.. automodule:: pyasn1.type.tag
:members: Tag, tagClassUniversal, tagClassApplication, tagClassContext,
tagClassPrivate, tagFormatSimple, tagFormatConstructed

View File

@ -0,0 +1,6 @@
Tag->type map
-------------
.. autoclass:: pyasn1.type.tagmap.TagMap
:members:

View File

@ -0,0 +1,6 @@
Composition of tags
-------------------
.. autoclass:: pyasn1.type.tag.TagSet
:members:

View File

@ -0,0 +1,20 @@
.. |ASN.1| replace:: Any
.. |encoding| replace:: iso-8859-1
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.Any(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='iso-8859-1', binValue=NoValue(),hexValue=NoValue())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec
.. note::
The |ASN.1| type models an arbitrary value of an arbitrary type. Sometimes
type is defined by accompanying object identifier or an integer identifier.
Frequently ANY value holds a serialized representation of some other ASN.1
object.
.. automethod:: pyasn1.type.univ.Any.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='iso-8859-1')
.. automethod:: pyasn1.type.univ.Any.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(),encoding='iso-8859-1')

View File

@ -0,0 +1,15 @@
.. |ASN.1| replace:: BitString
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.BitString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues(),binValue=NoValue(), hexValue=NoValue())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, asInteger, asNumbers, asOctets, asBinary, fromHexString, fromBinaryString, fromOctetString
.. note::
The |ASN.1| type models an arbitrary sequence of bits.
.. automethod:: pyasn1.type.univ.BitString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues(),binValue=NoValue(), hexValue=NoValue())
.. automethod:: pyasn1.type.univ.BitString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), , namedValues=NamedValues(),binValue=NoValue(), hexValue=NoValue())

View File

@ -0,0 +1,15 @@
.. |ASN.1| replace:: Boolean
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.Boolean(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec
.. note::
The |ASN.1| type models a BOOLEAN that can be either TRUE or FALSE.
.. automethod:: pyasn1.type.univ.Boolean.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
.. automethod:: pyasn1.type.univ.Boolean.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(), subtypeSpec=ConstraintsIntersection())

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: Choice
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.Choice(componentType=None, tagSet=tagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
getComponentByPosition, setComponentByPosition, getComponentByName, setComponentByName, setDefaultComponents,
getComponentByType, setComponentByType, getName, getComponent
.. note::
The |ASN.1| type can only hold a single component at a time belonging to the list of allowed types.
.. automethod:: pyasn1.type.univ.Choice.clone(componentType=None, tagSet=tagSet(), subtypeSpec=ConstraintsIntersection())
.. automethod:: pyasn1.type.univ.Choice.subtype(componentType=None, implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection())

View File

@ -0,0 +1,23 @@
Universal types
---------------
.. autoclass:: pyasn1.type.univ.NoValue()
.. toctree::
:maxdepth: 2
/docs/type/univ/integer
/docs/type/univ/boolean
/docs/type/univ/bitstring
/docs/type/univ/octetstring
/docs/type/univ/null
/docs/type/univ/objectidentifier
/docs/type/univ/real
/docs/type/univ/enumerated
/docs/type/univ/any
/docs/type/univ/setof
/docs/type/univ/sequenceof
/docs/type/univ/set
/docs/type/univ/sequence
/docs/type/univ/choice

View File

@ -0,0 +1,16 @@
.. |ASN.1| replace:: Enumerated
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.Enumerated(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, namedValues
.. note::
The |ASN.1| type models bounded set of named integer values. Other than that, it is identical to
the *Integer* class.
.. automethod:: pyasn1.type.univ.Enumerated.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues())
.. automethod:: pyasn1.type.univ.Enumerated.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues())

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: Integer
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.Integer(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap,
subtypeSpec, namedValues
.. note::
The |ASN.1| type models an arbitrary integer. INTEGER values can be positive, negative,
or zero, and can have any magnitude.
.. automethod:: pyasn1.type.univ.Integer.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues())
.. automethod:: pyasn1.type.univ.Integer.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(), namedValues=NamedValues())

View File

@ -0,0 +1,15 @@
.. |ASN.1| replace:: Null
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.Null(value=NoValue(), tagSet=TagSet())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec
.. note::
The |ASN.1| type models ASN.1 NULL.
.. automethod:: pyasn1.type.univ.Null.clone(value=NoValue(), tagSet=TagSet())
.. automethod:: pyasn1.type.univ.Null.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag())

View File

@ -0,0 +1,15 @@
.. |ASN.1| replace:: ObjectIdentifier
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.ObjectIdentifier(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, isPrefixOf
.. note::
The |ASN.1| type models ASN.1 OBJECT IDENTIFIER as a sequence of integer numbers.
.. automethod:: pyasn1.type.univ.ObjectIdentifier.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
.. automethod:: pyasn1.type.univ.ObjectIdentifier.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(), subtypeSpec=ConstraintsIntersection())

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: OctetString
.. |encoding| replace:: iso-8859-1
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.OctetString(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='iso-8859-1', binValue=NoValue(),hexValue=NoValue())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, fromHexString, fromBinaryString
.. note::
The |ASN.1| type models an arbitrary string of octets (eight-bit numbers), not printable text string.
.. automethod:: pyasn1.type.univ.OctetString.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='iso-8859-1')
.. automethod:: pyasn1.type.univ.OctetString.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection(),encoding='iso-8859-1')

View File

@ -0,0 +1,15 @@
.. |ASN.1| replace:: Real
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.Real(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, subtypeSpec, isInf, isPlusInf, isMinusInf
.. note::
The |ASN.1| type models a rational number of arbitrary precision.
.. automethod:: pyasn1.type.univ.Real.clone(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
.. automethod:: pyasn1.type.univ.Real.subtype(value=NoValue(), implicitTag=Tag(), explicitTag=Tag(), subtypeSpec=ConstraintsIntersection())

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: Sequence
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.Sequence(componentType=None, tagSet=tagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec, getComponentByPosition,
setComponentByPosition, getComponentByName, setComponentByName, setDefaultComponents
.. note::
The |ASN.1| type models a collection of named ASN.1 components.
Ordering of the components **is** preserved upon de/serialization.
.. automethod:: pyasn1.type.univ.Sequence.clone(componentType=None, tagSet=tagSet(), subtypeSpec=ConstraintsIntersection())
.. automethod:: pyasn1.type.univ.Sequence.subtype(componentType=None, implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection())

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: SequenceOf
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.SequenceOf(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
getComponentByPosition, setComponentByPosition
.. note::
The |ASN.1| type models a collection of elements of a single ASN.1 type.
Ordering of the components **is** preserved upon de/serialization.
.. automethod:: pyasn1.type.univ.SequenceOf.clone(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
.. automethod:: pyasn1.type.univ.SequenceOf.subtype(componentType=None, implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection())

View File

@ -0,0 +1,18 @@
.. |ASN.1| replace:: Set
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.Set(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
getComponentByPosition, setComponentByPosition, getComponentByName, setComponentByName, setDefaultComponents,
getComponentByType, setComponentByType
.. note::
The |ASN.1| type models a collection of named ASN.1 components.
Ordering of the components **is not** preserved upon de/serialization.
.. automethod:: pyasn1.type.univ.Set.clone(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
.. automethod:: pyasn1.type.univ.Set.subtype(componentType=None, implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection())

View File

@ -0,0 +1,17 @@
.. |ASN.1| replace:: SetOf
|ASN.1| type
------------
.. autoclass:: pyasn1.type.univ.SetOf(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), sizeSpec=ConstraintsIntersection())
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, effectiveTagSet, tagMap, componentType, subtypeSpec, sizeSpec,
 getComponentByPosition, setComponentByPosition
.. note::
The |ASN.1| type models a collection of elements of a single ASN.1 type.
Ordering of the components **is not** preserved upon de/serialization.
.. automethod:: pyasn1.type.univ.SetOf.clone(componentType=None, tagSet=TagSet(), subtypeSpec=ConstraintsIntersection())
.. automethod:: pyasn1.type.univ.SetOf.subtype(componentType=None, implicitTag=Tag(), explicitTag=Tag(),subtypeSpec=ConstraintsIntersection())

View File

@ -0,0 +1,10 @@
Useful types
------------
.. toctree::
:maxdepth: 2
/docs/type/useful/objectdescriptor
/docs/type/useful/generalizedtime
/docs/type/useful/utctime

View File

@ -0,0 +1,35 @@
.. |ASN.1| replace:: GeneralizedTime
.. |encoding| replace:: iso-8859-1
|ASN.1| type
------------
.. autoclass:: pyasn1.type.useful.GeneralizedTime(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, asDateTime, fromDateTime
.. note::
The |ASN.1| type models a character string representing date and time
in many different formats.
Formal syntax for the *GeneralizedTime* value is:
* **YYYYMMDDhh[mm[ss[(.|,)ffff]]]** standing for a local time, four
digits for the year, two for the month, two for the day and two
for the hour, followed by two digits for the minutes and two
for the seconds if required, then a dot (or a comma), and a
number for the fractions of second or
* a string as above followed by the letter “Z” (denoting a UTC
time) or
* a string as above followed by a string **(+|-)hh[mm]** denoting
time zone offset relative to UTC
For example, *20170126120000Z* stands for YYYYMMDDHHMMSSZ.
.. automethod:: pyasn1.type.useful.GeneralizedTime.clone(self, value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.useful.GeneralizedTime.subtype(self, value=NoValue(), implicitTag=TagSet(), explicitTag=TagSet(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,18 @@
.. |ASN.1| replace:: ObjectDescriptor
.. |encoding| replace:: iso-8859-1
|ASN.1| type
------------
.. autoclass:: pyasn1.type.useful.ObjectDescriptor(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet
.. note::
The |ASN.1| type models a character string that can accompany the *ObjectIdentifier* type
to serve as a human-friendly annotation for an OBJECT IDENTIFIER.
.. automethod:: pyasn1.type.useful.ObjectDescriptor.clone(self, value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.useful.ObjectDescriptor.subtype(self, value=NoValue(), implicitTag=TagSet(), explicitTag=TagSet(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,32 @@
.. |ASN.1| replace:: UTCTime
.. |encoding| replace:: iso-8859-1
|ASN.1| type
------------
.. autoclass:: pyasn1.type.useful.UTCTime(value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
:members: isValue, isSameTypeWith, isSuperTypeOf, tagSet, asDateTime, fromDateTime
.. note::
The |ASN.1| type models a character string representing date and time.
Formal syntax for the *UTCTime* value is:
* **YYMMDDhhmm[ss]** standing for UTC time, two
digits for the year, two for the month, two for the day and two
for the hour, followed by two digits for the minutes and two
for the seconds if required or
* a string as above followed by the letter “Z” (denoting a UTC
time) or
* a string as above followed by a string **(+|-)hhmm** denoting
time zone offset relative to UTC
For example, *170126120000Z* which stands for YYMMDDHHMMSSZ.
.. automethod:: pyasn1.type.useful.UTCTime.clone(self, value=NoValue(), tagSet=TagSet(), subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')
.. automethod:: pyasn1.type.useful.UTCTime.subtype(self, value=NoValue(), implicitTag=TagSet(), explicitTag=TagSet(),subtypeSpec=ConstraintsIntersection(), encoding='us-ascii')

View File

@ -0,0 +1,193 @@
Example use case
================
.. toctree::
:maxdepth: 2
To briefly explain how to approach pyasn1, consider a quick workflow example.
Grab ASN.1 schema for SSH keys
------------------------------
ASN.1 is widely used in many Internet protocols. Frequently, whenever ASN.1 is employed,
data structures are described in ASN.1 schema language right in the RFC.
Take `RFC2437 <https://www.ietf.org/rfc/rfc2437.txt>`_ for example -- we can look into
it and weed out data structures specification into a local file:
.. code-block:: python
# pkcs-1.asn
PKCS-1 {iso(1) member(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) modules(0) pkcs-1(1)}
DEFINITIONS EXPLICIT TAGS ::= BEGIN
RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER,
publicExponent INTEGER,
privateExponent INTEGER,
prime1 INTEGER,
prime2 INTEGER,
exponent1 INTEGER,
exponent2 INTEGER,
coefficient INTEGER
}
Version ::= INTEGER
END
Compile ASN.1 schema into Python
--------------------------------
In the best case, you should be able to automatically compile ASN.1 spec into
Python classes. For that purpose we have the `asn1ate <https://github.com/kimgr/asn1ate>`_
tool:
.. code-block:: bash
$ pyasn1gen.py pkcs-1.asn > rsakey.py
Though it may not work out as, as it stands now, asn1ate does not support
all ASN.1 language constructs.
Alternatively, you could check out the `pyasn1-modules <https://github.com/etingof/pyasn1-modules>`_
package to see if it already has the ASN.1 spec you are looking for compiled and shipped
there. Then just install the package, import the data structure you need and use it:
.. code-block:: bash
$ pip install pyasn1-modules
As a last resort, you could express ASN.1 in Python by hand. The end result
should be a declarative Python code resembling original ASN.1 syntax like
this:
.. code-block:: python
# rsakey.py
class Version(Integer):
pass
class RSAPrivateKey(Sequence):
componentType = NamedTypes(
NamedType('version', Version()),
NamedType('modulus', Integer()),
NamedType('publicExponent', Integer()),
NamedType('privateExponent', Integer()),
NamedType('prime1', Integer()),
NamedType('prime2', Integer()),
NamedType('exponent1', Integer()),
NamedType('exponent2', Integer()),
NamedType('coefficient', Integer())
)
Read your ~/.ssh/id_rsa
-----------------------
Given we've put our Python classes into the `rsakey.py` module, we could import
the top-level object for SSH keys container and initialize it from our
`~/.ssh/id_rsa` file (for sake of simplicity here we assume no passphrase is
set on the key file):
.. code-block:: python
from base64 import b64decode
from pyasn1.codec.der.decoder import decode as der_decoder
from rsakey import RSAPrivateKey
# Read SSH key from file (assuming no passphrase)
with open open('.ssh/id_rsa') as key_file:
b64_serialization = ''.join(key_file.readlines()[1:-1])
# Undo BASE64 serialization
der_serialization = b64decode(b64_serialization)
# Undo DER serialization, reconstruct SSH key structure
private_key, rest_of_input = der_decoder(der_serialization, asn1Spec=RSAPrivateKey())
Once we have Python ASN.1 structures initialized, we could inspect them:
.. code-block:: pycon
>>> print(private_key.prettyPrint())
RSAPrivateKey:
version=0
modulus=280789907761334970323210643584308373...
publicExponent=65537
privateExponent=1704567874679144879123080924...
prime1=1780178536719561265324798296279384073...
prime2=1577313184995269616049017780493740138...
exponent1=1193974819720845247396384239609024...
exponent2=9240965721817961178848297404494811...
coefficient=10207364473358910343346707141115...
Play with the keys
------------------
As well as use them nearly as we do with native Python types:
.. code-block:: pycon
>>> pk = private_key
>>>
>>> pk['prime1'] * pk['prime2'] == pk['modulus']
True
>>> pk['prime1'] == pk['modulus'] // pk['prime2']
True
>>> pk['exponent1'] == pk['privateExponent'] % (pk['prime1'] - 1)
True
>>> pk['exponent2'] == pk['privateExponent'] % (pk['prime2'] - 1)
True
Technically, pyasn1 classes `emulate <https://docs.python.org/3/reference/datamodel.html#emulating-container-types>`_
Python built-in types.
Transform to built-ins
----------------------
ASN.1 data structures exhibit a way more complicated behaviour compared to
Python types. You may wish to simplify things by turning the whole tree of
pyasn1 objects into an analogous tree made of base Python types:
.. code-block:: pycon
>>> from pyasn1.codec.native.encoder import encode
>>> ...
>>> py_private_key = encode(private_key)
>>> py_private_key
{'version': 0, 'modulus': 280789907761334970323210643584308373, 'publicExponent': 65537,
'privateExponent': 1704567874679144879123080924, 'prime1': 1780178536719561265324798296279384073,
'prime2': 1577313184995269616049017780493740138, 'exponent1': 1193974819720845247396384239609024,
'exponent2': 9240965721817961178848297404494811, 'coefficient': 10207364473358910343346707141115}
You can do vice-versa: initialize ASN.1 structure from a dict:
.. code-block:: pycon
>>> from pyasn1.codec.native.decoder import decode
>>> py_private_key = {'modulus': 280789907761334970323210643584308373}
>>> private_key = decode(py_private_key, asn1Spec=RSAPrivateKey())
Write it back
-------------
Possibly not that applicable to the SSH key example, but you can of course modify
any part of the ASN.1 data structure and serialize it back into the same or other
wire representation:
.. code-block:: python
from pyasn1.codec.der.encoder import encode as der_encoder
# Serialize SSH key data structure into DER stream
der_serialization = der_encoder(private_key)
# Serialize DER stream into BASE64 stream
b64_serialization = '-----BEGIN RSA PRIVATE KEY-----\n'
b64_serialization += b64encode(der_serialization)
b64_serialization += '-----END RSA PRIVATE KEY-----\n'
with open('.ssh/id_rsa.new', 'w') as key_file:
key_file.write(b64_serialization)

View File

@ -0,0 +1,6 @@
.. _license:
License
=======
.. include:: ../../LICENSE.rst

View File

@ -1,233 +0,0 @@
<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>

View File

@ -1,26 +1,33 @@
Metadata-Version: 1.0
Metadata-Version: 1.1
Name: pyasn1
Version: 0.1.7
Version: 0.3.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
Home-page: https://github.com/etingof/pyasn1
Author: Ilya Etingof <etingof@gmail.com>
Author-email: etingof@gmail.com
License: BSD
Description: A pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208).
Description: 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 :: 2.4
Classifier: Programming Language :: Python :: 2.5
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Topic :: Communications
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules

View File

@ -1,17 +1,66 @@
CHANGES
LICENSE
CHANGES.rst
LICENSE.rst
MANIFEST.in
README
THANKS
TODO
README.md
TODO.rst
setup.cfg
setup.py
doc/codecs.html
doc/constraints.html
doc/constructed.html
doc/intro.html
doc/pyasn1-tutorial.html
doc/scalar.html
doc/tagging.html
doc/Makefile
doc/source/changelog.rst
doc/source/conf.py
doc/source/contents.rst
doc/source/example-use-case.rst
doc/source/license.rst
doc/source/docs/api-reference.rst
doc/source/docs/tutorial.rst
doc/source/docs/codec/ber/contents.rst
doc/source/docs/codec/cer/contents.rst
doc/source/docs/codec/der/contents.rst
doc/source/docs/codec/native/contents.rst
doc/source/docs/type/char/bmpstring.rst
doc/source/docs/type/char/contents.rst
doc/source/docs/type/char/generalstring.rst
doc/source/docs/type/char/graphicstring.rst
doc/source/docs/type/char/ia5string.rst
doc/source/docs/type/char/iso646string.rst
doc/source/docs/type/char/numericstring.rst
doc/source/docs/type/char/printablestring.rst
doc/source/docs/type/char/t61string.rst
doc/source/docs/type/char/teletexstring.rst
doc/source/docs/type/char/universalstring.rst
doc/source/docs/type/char/utf8string.rst
doc/source/docs/type/char/videotexstring.rst
doc/source/docs/type/char/visiblestring.rst
doc/source/docs/type/namedtype/contents.rst
doc/source/docs/type/namedtype/defaultednamedtype.rst
doc/source/docs/type/namedtype/namedtype.rst
doc/source/docs/type/namedtype/namedtypes.rst
doc/source/docs/type/namedtype/optionalnamedtype.rst
doc/source/docs/type/namedval/contents.rst
doc/source/docs/type/namedval/namedval.rst
doc/source/docs/type/tag/contents.rst
doc/source/docs/type/tag/tag.rst
doc/source/docs/type/tag/tagmap.rst
doc/source/docs/type/tag/tagset.rst
doc/source/docs/type/univ/any.rst
doc/source/docs/type/univ/bitstring.rst
doc/source/docs/type/univ/boolean.rst
doc/source/docs/type/univ/choice.rst
doc/source/docs/type/univ/contents.rst
doc/source/docs/type/univ/enumerated.rst
doc/source/docs/type/univ/integer.rst
doc/source/docs/type/univ/null.rst
doc/source/docs/type/univ/objectidentifier.rst
doc/source/docs/type/univ/octetstring.rst
doc/source/docs/type/univ/real.rst
doc/source/docs/type/univ/sequence.rst
doc/source/docs/type/univ/sequenceof.rst
doc/source/docs/type/univ/set.rst
doc/source/docs/type/univ/setof.rst
doc/source/docs/type/useful/contents.rst
doc/source/docs/type/useful/generalizedtime.rst
doc/source/docs/type/useful/objectdescriptor.rst
doc/source/docs/type/useful/utctime.rst
pyasn1/__init__.py
pyasn1/debug.py
pyasn1/error.py
@ -31,8 +80,16 @@ pyasn1/codec/cer/encoder.py
pyasn1/codec/der/__init__.py
pyasn1/codec/der/decoder.py
pyasn1/codec/der/encoder.py
pyasn1/codec/native/__init__.py
pyasn1/codec/native/decoder.py
pyasn1/codec/native/encoder.py
pyasn1/compat/__init__.py
pyasn1/compat/binary.py
pyasn1/compat/calling.py
pyasn1/compat/dateandtime.py
pyasn1/compat/integer.py
pyasn1/compat/octets.py
pyasn1/compat/string.py
pyasn1/type/__init__.py
pyasn1/type/base.py
pyasn1/type/char.py
@ -44,25 +101,39 @@ 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
tests/__init__.py
tests/__main__.py
tests/base.py
tests/test_debug.py
tests/codec/__init__.py
tests/codec/__main__.py
tests/codec/ber/__init__.py
tests/codec/ber/__main__.py
tests/codec/ber/test_decoder.py
tests/codec/ber/test_encoder.py
tests/codec/cer/__init__.py
tests/codec/cer/__main__.py
tests/codec/cer/test_decoder.py
tests/codec/cer/test_encoder.py
tests/codec/der/__init__.py
tests/codec/der/__main__.py
tests/codec/der/test_decoder.py
tests/codec/der/test_encoder.py
tests/codec/native/__init__.py
tests/codec/native/__main__.py
tests/codec/native/test_decoder.py
tests/codec/native/test_encoder.py
tests/compat/__init__.py
tests/compat/__main__.py
tests/compat/test_binary.py
tests/compat/test_integer.py
tests/compat/test_octets.py
tests/type/__init__.py
tests/type/__main__.py
tests/type/test_char.py
tests/type/test_constraint.py
tests/type/test_namedtype.py
tests/type/test_namedval.py
tests/type/test_tag.py
tests/type/test_univ.py
tests/type/test_useful.py

View File

@ -1,8 +1,7 @@
import sys
# http://www.python.org/dev/peps/pep-0396/
__version__ = '0.1.7'
__version__ = '0.3.7'
if sys.version_info[:2] < (2, 4):
raise RuntimeError('PyASN1 requires Python 2.4 or later')
raise RuntimeError('PyASN1 requires Python 2.4 or later')

File diff suppressed because it is too large Load Diff

View File

@ -1,229 +1,344 @@
# BER encoder
from pyasn1.type import base, tag, univ, char, useful
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import tag, univ, char, useful
from pyasn1.codec.ber import eoo
from pyasn1.compat.octets import int2oct, oct2int, ints2octs, null, str2octs
from pyasn1.compat.integer import to_bytes
from pyasn1 import debug, error
class Error(Exception): pass
__all__ = ['encode']
class AbstractItemEncoder:
class AbstractItemEncoder(object):
supportIndefLenMode = 1
def encodeTag(self, t, isConstructed):
tagClass, tagFormat, tagId = t.asTuple() # this is a hotspot
v = tagClass | tagFormat
# An outcome of otherwise legit call `encodeFun(eoo.endOfOctets)`
eooIntegerSubstrate = (0, 0)
eooOctetsSubstrate = ints2octs(eooIntegerSubstrate)
# noinspection PyMethodMayBeStatic
def encodeTag(self, singleTag, isConstructed):
tagClass, tagFormat, tagId = singleTag
encodedTag = tagClass | tagFormat
if isConstructed:
v = v|tag.tagFormatConstructed
encodedTag |= tag.tagFormatConstructed
if tagId < 31:
return int2oct(v|tagId)
return encodedTag | tagId,
else:
s = int2oct(tagId&0x7f)
tagId = tagId >> 7
substrate = tagId & 0x7f,
tagId >>= 7
while tagId:
s = int2oct(0x80|(tagId&0x7f)) + s
tagId = tagId >> 7
return int2oct(v|0x1F) + s
substrate = (0x80 | (tagId & 0x7f),) + substrate
tagId >>= 7
return (encodedTag | 0x1F,) + substrate
def encodeLength(self, length, defMode):
if not defMode and self.supportIndefLenMode:
return int2oct(0x80)
return (0x80,)
if length < 0x80:
return int2oct(length)
return length,
else:
substrate = null
substrate = ()
while length:
substrate = int2oct(length&0xff) + substrate
length = length >> 8
substrate = (length & 0xff,) + substrate
length >>= 8
substrateLen = len(substrate)
if substrateLen > 126:
raise Error('Length octets overflow (%d)' % substrateLen)
return int2oct(0x80 | substrateLen) + substrate
raise error.PyAsn1Error('Length octets overflow (%d)' % substrateLen)
return (0x80 | substrateLen,) + substrate
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
raise Error('Not implemented')
def encodeValue(self, value, encodeFun, **options):
raise error.PyAsn1Error('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
def encode(self, value, encodeFun, **options):
tagSet = value.tagSet
# untagged item?
if not tagSet:
substrate, isConstructed, isOctets = self.encodeValue(
value, encodeFun, **options
)
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
return substrate
defMode = options.get('defMode', True)
for idx, singleTag in enumerate(tagSet.superTags):
defModeOverride = defMode
# base tag?
if not idx:
substrate, isConstructed, isOctets = self.encodeValue(
value, encodeFun, **options
)
if options.get('ifNotEmpty', False) and not substrate:
return substrate
# primitive form implies definite mode
if not isConstructed:
defModeOverride = True
header = self.encodeTag(singleTag, isConstructed)
header += self.encodeLength(len(substrate), defModeOverride)
if isOctets:
substrate = ints2octs(header) + substrate
if not defModeOverride:
substrate += self.eooOctetsSubstrate
else:
substrate = header + substrate
if not defModeOverride:
substrate += self.eooIntegerSubstrate
if not isOctets:
substrate = ints2octs(substrate)
return substrate
class EndOfOctetsEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return null, 0
def encodeValue(self, value, encodeFun, **options):
return null, False, True
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
supportIndefLenMode = False
def encodeValue(self, value, encodeFun, **options):
return value and (1,) or (0,), False, False
class IntegerEncoder(AbstractItemEncoder):
supportIndefLenMode = 0
supportIndefLenMode = False
supportCompactZero = False
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if value == 0: # shortcut for zero value
def encodeValue(self, value, encodeFun, **options):
if value == 0:
# de-facto way to encode zero
if self.supportCompactZero:
# this seems to be a correct way for encoding zeros
return null, 0
return (), False, False
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
return (0,), False, False
return to_bytes(int(value), signed=True), False, True
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
def encodeValue(self, value, encodeFun, **options):
valueLength = len(value)
if valueLength % 8:
alignedValue = value << (8 - valueLength % 8)
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
alignedValue = value
maxChunkSize = options.get('maxChunkSize', 0)
if not maxChunkSize or len(alignedValue) <= maxChunkSize * 8:
substrate = alignedValue.asOctets()
return int2oct(len(substrate) * 8 - valueLength) + substrate, False, True
# strip off explicit tags
alignedValue = alignedValue.clone(
tagSet=tag.TagSet(value.tagSet.baseTag, value.tagSet.baseTag)
)
stop = 0
substrate = null
while stop < valueLength:
start = stop
stop = min(start + maxChunkSize * 8, valueLength)
substrate += encodeFun(alignedValue[start:stop], **options)
return substrate, True, True
class OctetStringEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
def encodeValue(self, value, encodeFun, **options):
maxChunkSize = options.get('maxChunkSize', 0)
if not maxChunkSize or len(value) <= maxChunkSize:
return value.asOctets(), 0
return value.asOctets(), False, True
else:
pos = 0; substrate = null
while 1:
v = value.clone(value[pos:pos+maxChunkSize])
if not v:
# will strip off explicit tags
baseTagSet = tag.TagSet(value.tagSet.baseTag, value.tagSet.baseTag)
pos = 0
substrate = null
while True:
chunk = value.clone(value[pos:pos + maxChunkSize],
tagSet=baseTagSet)
if not chunk:
break
substrate = substrate + encodeFun(v, defMode, maxChunkSize)
pos = pos + maxChunkSize
return substrate, 1
substrate += encodeFun(chunk, **options)
pos += maxChunkSize
return substrate, True, True
class NullEncoder(AbstractItemEncoder):
supportIndefLenMode = 0
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return null, 0
supportIndefLenMode = False
def encodeValue(self, value, encodeFun, **options):
return null, False, True
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):
supportIndefLenMode = False
def encodeValue(self, value, encodeFun, **options):
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
# Build the first pair
try:
first = oid[0]
second = oid[1]
# 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)
)
except IndexError:
raise error.PyAsn1Error('Short OID %s' % (value,))
if 0 <= second <= 39:
if first == 1:
oid = (second + 40,) + oid[2:]
elif first == 0:
oid = (second,) + oid[2:]
elif first == 2:
oid = (second + 80,) + oid[2:]
else:
raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
elif first == 2:
oid = (second + 80,) + oid[2:]
else:
raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,))
octets = ()
# Cycle through subIds
for subOid in oid:
if 0 <= subOid <= 127:
# Optimize for the common case
octets += (subOid,)
elif subOid > 127:
# Pack large Sub-Object IDs
res = (subid & 0x7f,)
subid = subid >> 7
while subid > 0:
res = (0x80 | (subid & 0x7f),) + res
subid = subid >> 7
res = (subOid & 0x7f,)
subOid >>= 7
while subOid:
res = (0x80 | (subOid & 0x7f),) + res
subOid >>= 7
# Add packed Sub-Object ID to resulted Object ID
octets += res
return ints2octs(octets), 0
else:
raise error.PyAsn1Error('Negative OID arc %s at %s' % (subOid, value))
return octets, False, False
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
binEncBase = 2 # set to None to choose encoding base automatically
@staticmethod
def _dropFloatingPoint(m, encbase, e):
ms, es = 1, 1
if m < 0:
ms = -1 # mantissa sign
if e < 0:
es = -1 # exponenta sign
m *= ms
if encbase == 8:
m *= 2 ** (abs(e) % 3 * es)
e = abs(e) // 3 * es
elif encbase == 16:
m *= 2 ** (abs(e) % 4 * es)
e = abs(e) // 4 * es
while True:
if int(m) != m:
m *= encbase
e -= 1
continue
break
return ms, int(m), encbase, e
def _chooseEncBase(self, value):
m, b, e = value
encBase = [2, 8, 16]
if value.binEncBase in encBase:
return self._dropFloatingPoint(m, value.binEncBase, e)
elif self.binEncBase in encBase:
return self._dropFloatingPoint(m, self.binEncBase, e)
# auto choosing base 2/8/16
mantissa = [m, m, m]
exponenta = [e, e, e]
sign = 1
encbase = 2
e = float('inf')
for i in range(3):
(sign,
mantissa[i],
encBase[i],
exponenta[i]) = self._dropFloatingPoint(mantissa[i], encBase[i], exponenta[i])
if abs(exponenta[i]) < abs(e) or (abs(exponenta[i]) == abs(e) and mantissa[i] < m):
e = exponenta[i]
m = int(mantissa[i])
encbase = encBase[i]
return sign, m, encbase, e
def encodeValue(self, value, encodeFun, **options):
if value.isPlusInf:
return (0x40,), False, False
if value.isMinusInf:
return (0x41,), False, False
m, b, e = value
if not m:
return null, 0
return null, False, True
if b == 10:
return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), 0
return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), False, True
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
fo = 0x80 # binary encoding
ms, m, encbase, e = self._chooseEncBase(value)
if ms < 0: # mantissa sign
fo |= 0x40 # sign bit
# exponenta & mantissa normalization
if encbase == 2:
while m & 0x1 == 0:
m >>= 1
e += 1
elif encbase == 8:
while m & 0x7 == 0:
m >>= 3
e += 1
fo |= 0x10
else: # encbase = 16
while m & 0xf == 0:
m >>= 4
e += 1
fo |= 0x20
sf = 0 # scale factor
while m & 0x1 == 0:
m >>= 1
e += 1
sf += 1
if sf > 3:
raise error.PyAsn1Error('Scale factor overflow') # bug if raised
fo |= sf << 2
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
if e == 0 or e == -1:
eo = int2oct(e & 0xff)
else:
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
if e == -1 and eo and not (oct2int(eo[0]) & 0x80):
eo = int2oct(0xff) + eo
n = len(eo)
if n > 0xff:
raise error.PyAsn1Error('Real exponent overflow')
@ -235,51 +350,57 @@ class RealEncoder(AbstractItemEncoder):
fo |= 2
else:
fo |= 3
eo = int2oct(n//0xff+1) + eo
eo = int2oct(n & 0xff) + eo
po = null
while m:
po = int2oct(m&0xff) + po
po = int2oct(m & 0xff) + po
m >>= 8
substrate = int2oct(fo) + eo + po
return substrate, 0
return substrate, False, True
else:
raise error.PyAsn1Error('Prohibited Real base %s' % b)
class SequenceEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
value.setDefaultComponents()
def encodeValue(self, value, encodeFun, **options):
value.verifySizeSpec()
substrate = null; idx = len(value)
namedTypes = value.componentType
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
idx -= 1
if namedTypes:
if namedTypes[idx].isOptional and not value[idx].isValue:
continue
if namedTypes[idx].isDefaulted and value[idx] == namedTypes[idx].asn1Object:
continue
substrate = encodeFun(value[idx], **options) + substrate
return substrate, True, True
class SequenceOfEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
def encodeValue(self, value, encodeFun, **options):
value.verifySizeSpec()
substrate = null; idx = len(value)
substrate = null
idx = len(value)
while idx > 0:
idx = idx - 1
substrate = encodeFun(
value[idx], defMode, maxChunkSize
) + substrate
return substrate, 1
idx -= 1
substrate = encodeFun(value[idx], **options) + substrate
return substrate, True, True
class ChoiceEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return encodeFun(value.getComponent(), defMode, maxChunkSize), 1
def encodeValue(self, value, encodeFun, **options):
return encodeFun(value.getComponent(), **options), True, True
class AnyEncoder(OctetStringEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return value.asOctets(), defMode == 0
def encodeValue(self, value, encodeFun, **options):
return value.asOctets(), not options.get('defMode', True), True
tagMap = {
eoo.endOfOctets.tagSet: EndOfOctetsEncoder(),
@ -308,46 +429,120 @@ tagMap = {
char.UniversalString.tagSet: OctetStringEncoder(),
char.BMPString.tagSet: OctetStringEncoder(),
# useful types
useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
useful.GeneralizedTime.tagSet: OctetStringEncoder(),
useful.UTCTime.tagSet: OctetStringEncoder()
}
useful.UTCTime.tagSet: OctetStringEncoder()
}
# Type-to-codec map for ambiguous ASN.1 types
# Put in ambiguous & non-ambiguous types for faster codec lookup
typeMap = {
univ.Boolean.typeId: BooleanEncoder(),
univ.Integer.typeId: IntegerEncoder(),
univ.BitString.typeId: BitStringEncoder(),
univ.OctetString.typeId: OctetStringEncoder(),
univ.Null.typeId: NullEncoder(),
univ.ObjectIdentifier.typeId: ObjectIdentifierEncoder(),
univ.Enumerated.typeId: IntegerEncoder(),
univ.Real.typeId: RealEncoder(),
# Sequence & Set have same tags as SequenceOf & SetOf
univ.Set.typeId: SequenceEncoder(),
univ.SetOf.typeId: SequenceOfEncoder(),
univ.Sequence.typeId: SequenceEncoder(),
univ.SequenceOf.typeId: SequenceOfEncoder(),
univ.Choice.typeId: ChoiceEncoder(),
univ.Any.typeId: AnyEncoder()
}
univ.Any.typeId: AnyEncoder(),
# character string types
char.UTF8String.typeId: OctetStringEncoder(),
char.NumericString.typeId: OctetStringEncoder(),
char.PrintableString.typeId: OctetStringEncoder(),
char.TeletexString.typeId: OctetStringEncoder(),
char.VideotexString.typeId: OctetStringEncoder(),
char.IA5String.typeId: OctetStringEncoder(),
char.GraphicString.typeId: OctetStringEncoder(),
char.VisibleString.typeId: OctetStringEncoder(),
char.GeneralString.typeId: OctetStringEncoder(),
char.UniversalString.typeId: OctetStringEncoder(),
char.BMPString.typeId: OctetStringEncoder(),
# useful types
useful.ObjectDescriptor.typeId: OctetStringEncoder(),
useful.GeneralizedTime.typeId: OctetStringEncoder(),
useful.UTCTime.typeId: OctetStringEncoder()
}
class Encoder:
class Encoder(object):
fixedDefLengthMode = None
fixedChunkSize = None
# noinspection PyDefaultArgument
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
def __call__(self, value, **options):
if debug.logger & debug.flagEncoder:
logger = debug.logger
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)))
logger = None
if logger:
logger('encoder called in %sdef mode, chunk size %s for type %s, value:\n%s' % (not options.get('defMode', True) and 'in' or '', options.get('maxChunkSize', 0), value.prettyPrintType(), value.prettyPrint()))
if self.fixedDefLengthMode is not None:
options.update(defMode=self.fixedDefLengthMode)
if self.fixedChunkSize is not None:
options.update(maxChunkSize=self.fixedChunkSize)
tagSet = value.tagSet
try:
concreteEncoder = self.__typeMap[value.typeId]
except KeyError:
# use base type for codec lookup to recover untagged types
baseTagSet = tag.TagSet(value.tagSet.baseTag, value.tagSet.baseTag)
try:
concreteEncoder = self.__tagMap[baseTagSet]
except KeyError:
raise error.PyAsn1Error('No encoder for %s' % (value,))
if logger:
logger('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet))
substrate = concreteEncoder.encode(value, self, **options)
if logger:
logger('codec %s built %s octets of substrate: %s\nencoder completed' % (concreteEncoder, len(substrate), debug.hexdump(substrate)))
return substrate
#: Turns ASN.1 object into BER octet stream.
#:
#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: walks all its components recursively and produces a BER octet stream.
#:
#: Parameters
#: ----------
# value: any pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: A pyasn1 object to encode
#:
#: defMode: :py:class:`bool`
#: If `False`, produces indefinite length encoding
#:
#: maxChunkSize: :py:class:`int`
#: Maximum chunk size in chunked encoding mode (0 denotes unlimited chunk size)
#:
#: Returns
#: -------
#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
#: Given ASN.1 object encoded into BER octetstream
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On encoding errors
encode = Encoder(tagMap, typeMap)

View File

@ -1,8 +1,25 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import base, tag
class EndOfOctets(base.AbstractSimpleAsn1Item):
defaultValue = 0
tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00)
)
)
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = object.__new__(cls, *args, **kwargs)
return cls._instance
endOfOctets = EndOfOctets()

View File

@ -1,16 +1,27 @@
# CER decoder
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import univ
from pyasn1.codec.ber import decoder
from pyasn1.compat.octets import oct2int
from pyasn1 import error
__all__ = ['decode']
class BooleanDecoder(decoder.AbstractSimpleDecoder):
protoComponent = univ.Boolean(0)
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
state, decodeFun, substrateFun):
def valueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
head, tail = substrate[:length], substrate[length:]
if not head:
raise error.PyAsn1Error('Empty substrate')
if not head or length != 1:
raise error.PyAsn1Error('Not single-octet Boolean payload')
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
@ -20,16 +31,60 @@ class BooleanDecoder(decoder.AbstractSimpleDecoder):
elif byte == 0x00:
value = 0
else:
raise error.PyAsn1Error('Boolean CER violation: %s' % byte)
raise error.PyAsn1Error('Unexpected Boolean payload: %s' % byte)
return self._createComponent(asn1Spec, tagSet, value), tail
# TODO: prohibit non-canonical encoding
BitStringDecoder = decoder.BitStringDecoder
OctetStringDecoder = decoder.OctetStringDecoder
RealDecoder = decoder.RealDecoder
tagMap = decoder.tagMap.copy()
tagMap.update({
univ.Boolean.tagSet: BooleanDecoder()
})
tagMap.update(
{univ.Boolean.tagSet: BooleanDecoder(),
univ.BitString.tagSet: BitStringDecoder(),
univ.OctetString.tagSet: OctetStringDecoder(),
univ.Real.tagSet: RealDecoder()}
)
typeMap = decoder.typeMap
typeMap = decoder.typeMap.copy()
class Decoder(decoder.Decoder): pass
# Put in non-ambiguous types for faster codec lookup
for typeDecoder in tagMap.values():
if typeDecoder.protoComponent is not None:
typeId = typeDecoder.protoComponent.__class__.typeId
if typeId is not None and typeId not in typeMap:
typeMap[typeId] = typeDecoder
class Decoder(decoder.Decoder):
pass
#: Turns CER octet stream into an ASN.1 object.
#:
#: Takes CER octetstream and decode it into an ASN.1 object
#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
#: may be a scalar or an arbitrary nested structure.
#:
#: Parameters
#: ----------
#: substrate: :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
#: CER octetstream
#:
#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
#: A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
#: being decoded, *asn1Spec* may or may not be required. Most common reason for
#: it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
#:
#: Returns
#: -------
#: : :py:class:`tuple`
#: A tuple of pyasn1 object recovered from CER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: and the unprocessed trailing portion of the *substrate* (may be empty)
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On decoding errors
decode = Decoder(tagMap, decoder.typeMap)

View File

@ -1,87 +1,221 @@
# CER encoder
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import univ
from pyasn1.type import useful
from pyasn1.codec.ber import encoder
from pyasn1.compat.octets import int2oct, null
from pyasn1.compat.octets import str2octs, null
from pyasn1 import error
__all__ = ['encode']
class BooleanEncoder(encoder.IntegerEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
if client == 0:
substrate = int2oct(0)
def encodeValue(self, value, encodeFun, **options):
if value == 0:
substrate = (0,)
else:
substrate = int2oct(255)
return substrate, 0
substrate = (255,)
return substrate, False, False
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
)
class RealEncoder(encoder.RealEncoder):
def _chooseEncBase(self, value):
m, b, e = value
return self._dropFloatingPoint(m, b, e)
# specialized RealEncoder here
# specialized GeneralStringEncoder here
# specialized GeneralizedTimeEncoder here
# specialized UTCTimeEncoder here
class TimeEncoderMixIn(object):
zchar, = str2octs('Z')
pluschar, = str2octs('+')
minuschar, = str2octs('-')
commachar, = str2octs(',')
minLength = 12
maxLength = 19
def encodeValue(self, value, encodeFun, **options):
# Encoding constraints:
# - minutes are mandatory, seconds are optional
# - subseconds must NOT be zero
# - no hanging fraction dot
# - time in UTC (Z)
# - only dot is allowed for fractions
octets = value.asOctets()
if not self.minLength < len(octets) < self.maxLength:
raise error.PyAsn1Error('Length constraint violated: %r' % value)
if self.pluschar in octets or self.minuschar in octets:
raise error.PyAsn1Error('Must be UTC time: %r' % octets)
if octets[-1] != self.zchar:
raise error.PyAsn1Error('Missing "Z" time zone specifier: %r' % octets)
if self.commachar in octets:
raise error.PyAsn1Error('Comma in fractions disallowed: %r' % value)
options.update(maxChunkSize=1000)
return encoder.OctetStringEncoder.encodeValue(
self, value, encodeFun, **options
)
class GeneralizedTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
minLength = 12
maxLength = 19
class UTCTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder):
minLength = 10
maxLength = 14
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
@staticmethod
def _sortComponents(components):
# sort by tags regardless of the Choice value (static sort)
return sorted(components, key=lambda x: isinstance(x, univ.Choice) and x.minTagSet or x.tagSet)
def encodeValue(self, value, encodeFun, **options):
value.verifySizeSpec()
substrate = null
idx = len(value)
if value.typeId == univ.Set.typeId:
namedTypes = value.componentType
comps = []
compsMap = {}
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)
idx -= 1
if namedTypes:
if namedTypes[idx].isOptional and not value[idx].isValue:
continue
if namedTypes[idx].isDefaulted and value[idx] == namedTypes[idx].asn1Object:
continue
comps.append(value[idx])
compsMap[id(value[idx])] = namedTypes and namedTypes[idx].isOptional
for comp in self._sortComponents(comps):
options.update(ifNotEmpty=compsMap[id(comp)])
substrate += encodeFun(comp, **options)
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
components = [encodeFun(x, **options) for x in value]
# sort by serialized and padded components
if len(components) > 1:
zero = str2octs('\x00')
maxLen = max(map(len, components))
paddedComponents = [
(x.ljust(maxLen, zero), x) for x in components
]
paddedComponents.sort(key=lambda x: x[0])
components = [x[1] for x in paddedComponents]
substrate = null.join(components)
return substrate, True, True
class SequenceEncoder(encoder.SequenceEncoder):
def encodeValue(self, value, encodeFun, **options):
value.verifySizeSpec()
namedTypes = value.componentType
substrate = null
idx = len(value)
while idx > 0:
idx -= 1
if namedTypes:
if namedTypes[idx].isOptional and not value[idx].isValue:
continue
if namedTypes[idx].isDefaulted and value[idx] == namedTypes[idx].asn1Object:
continue
options.update(ifNotEmpty=namedTypes and namedTypes[idx].isOptional)
substrate = encodeFun(value[idx], **options) + substrate
return substrate, True, True
class SequenceOfEncoder(encoder.SequenceOfEncoder):
def encodeValue(self, value, encodeFun, **options):
substrate = null
idx = len(value)
if options.get('ifNotEmpty', False) and not idx:
return substrate, True, True
value.verifySizeSpec()
while idx > 0:
idx -= 1
substrate = encodeFun(value[idx], **options) + substrate
return substrate, True, True
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
})
univ.Real.tagSet: RealEncoder(),
useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(),
useful.UTCTime.tagSet: UTCTimeEncoder(),
# Sequence & Set have same tags as SequenceOf & SetOf
univ.SetOf.tagSet: SetOfEncoder(),
univ.Sequence.typeId: SequenceEncoder()
})
typeMap = encoder.typeMap.copy()
typeMap.update({
univ.Boolean.typeId: BooleanEncoder(),
univ.Real.typeId: RealEncoder(),
useful.GeneralizedTime.typeId: GeneralizedTimeEncoder(),
useful.UTCTime.typeId: UTCTimeEncoder(),
# Sequence & Set have same tags as SequenceOf & SetOf
univ.Set.typeId: SetOfEncoder(),
univ.SetOf.typeId: SetOfEncoder()
})
univ.SetOf.typeId: SetOfEncoder(),
univ.Sequence.typeId: SequenceEncoder(),
univ.SequenceOf.typeId: SequenceOfEncoder()
})
class Encoder(encoder.Encoder):
def __call__(self, client, defMode=0, maxChunkSize=0):
return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
fixedDefLengthMode = False
fixedChunkSize = 1000
#: Turns ASN.1 object into CER octet stream.
#:
#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: walks all its components recursively and produces a CER octet stream.
#:
#: Parameters
#: ----------
# value: any pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: A pyasn1 object to encode
#:
#: defMode: :py:class:`bool`
#: If `False`, produces indefinite length encoding
#:
#: maxChunkSize: :py:class:`int`
#: Maximum chunk size in chunked encoding mode (0 denotes unlimited chunk size)
#:
#: Returns
#: -------
#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
#: Given ASN.1 object encoded into BER octetstream
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On encoding errors
encode = Encoder(tagMap, typeMap)
# EncoderFactory queries class instance and builds a map of tags -> encoders

View File

@ -1,9 +1,70 @@
# DER decoder
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import univ
from pyasn1.codec.cer import decoder
tagMap = decoder.tagMap
typeMap = decoder.typeMap
Decoder = decoder.Decoder
__all__ = ['decode']
class BitStringDecoder(decoder.BitStringDecoder):
supportConstructedForm = False
class OctetStringDecoder(decoder.OctetStringDecoder):
supportConstructedForm = False
# TODO: prohibit non-canonical encoding
RealDecoder = decoder.RealDecoder
tagMap = decoder.tagMap.copy()
tagMap.update(
{univ.BitString.tagSet: BitStringDecoder(),
univ.OctetString.tagSet: OctetStringDecoder(),
univ.Real.tagSet: RealDecoder()}
)
typeMap = decoder.typeMap.copy()
# Put in non-ambiguous types for faster codec lookup
for typeDecoder in tagMap.values():
if typeDecoder.protoComponent is not None:
typeId = typeDecoder.protoComponent.__class__.typeId
if typeId is not None and typeId not in typeMap:
typeMap[typeId] = typeDecoder
class Decoder(decoder.Decoder):
supportIndefLength = False
#: Turns DER octet stream into an ASN.1 object.
#:
#: Takes DER octetstream and decode it into an ASN.1 object
#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
#: may be a scalar or an arbitrary nested structure.
#:
#: Parameters
#: ----------
#: substrate: :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
#: DER octetstream
#:
#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
#: A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
#: being decoded, *asn1Spec* may or may not be required. Most common reason for
#: it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
#:
#: Returns
#: -------
#: : :py:class:`tuple`
#: A tuple of pyasn1 object recovered from DER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: and the unprocessed trailing portion of the *substrate* (may be empty)
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On decoding errors
decode = Decoder(tagMap, typeMap)

View File

@ -1,28 +1,62 @@
# DER encoder
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import univ
from pyasn1.codec.cer import encoder
__all__ = ['encode']
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)
@staticmethod
def _sortComponents(components):
# sort by tags depending on the actual Choice value (dynamic sort)
return sorted(components, key=lambda x: isinstance(x, univ.Choice) and x.getComponent().tagSet or x.tagSet)
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()
})
univ.SetOf.tagSet: SetOfEncoder()
})
typeMap = encoder.typeMap.copy()
typeMap.update({
# Set & SetOf have same tags
univ.Set.typeId: SetOfEncoder(),
univ.SetOf.typeId: SetOfEncoder()
})
typeMap = encoder.typeMap
class Encoder(encoder.Encoder):
def __call__(self, client, defMode=1, maxChunkSize=0):
return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
fixedDefLengthMode = True
fixedChunkSize = 0
#: Turns ASN.1 object into DER octet stream.
#:
#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: walks all its components recursively and produces a DER octet stream.
#:
#: Parameters
#: ----------
# value: any pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: A pyasn1 object to encode
#:
#: defMode: :py:class:`bool`
#: If `False`, produces indefinite length encoding
#:
#: maxChunkSize: :py:class:`int`
#: Maximum chunk size in chunked encoding mode (0 denotes unlimited chunk size)
#:
#: Returns
#: -------
#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
#: Given ASN.1 object encoded into BER octetstream
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On encoding errors
encode = Encoder(tagMap, typeMap)

View File

@ -0,0 +1,194 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.type import base, univ, char, useful, tag
from pyasn1 import debug, error
__all__ = ['decode']
class AbstractScalarDecoder(object):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
return asn1Spec.clone(pyObject)
class BitStringDecoder(AbstractScalarDecoder):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
return asn1Spec.clone(univ.BitString.fromBinaryString(pyObject))
class SequenceOrSetDecoder(object):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
asn1Value = asn1Spec.clone()
componentsTypes = asn1Spec.componentType
for field in asn1Value:
if field in pyObject:
asn1Value[field] = decodeFun(pyObject[field], componentsTypes[field].asn1Object, **options)
return asn1Value
class SequenceOfOrSetOfDecoder(object):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
asn1Value = asn1Spec.clone()
for pyValue in pyObject:
asn1Value.append(decodeFun(pyValue, asn1Spec.componentType), **options)
return asn1Value
class ChoiceDecoder(object):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
asn1Value = asn1Spec.clone()
componentsTypes = asn1Spec.componentType
for field in pyObject:
if field in componentsTypes:
asn1Value[field] = decodeFun(pyObject[field], componentsTypes[field].asn1Object, **options)
break
return asn1Value
tagMap = {
univ.Integer.tagSet: AbstractScalarDecoder(),
univ.Boolean.tagSet: AbstractScalarDecoder(),
univ.BitString.tagSet: BitStringDecoder(),
univ.OctetString.tagSet: AbstractScalarDecoder(),
univ.Null.tagSet: AbstractScalarDecoder(),
univ.ObjectIdentifier.tagSet: AbstractScalarDecoder(),
univ.Enumerated.tagSet: AbstractScalarDecoder(),
univ.Real.tagSet: AbstractScalarDecoder(),
univ.Sequence.tagSet: SequenceOrSetDecoder(), # conflicts with SequenceOf
univ.Set.tagSet: SequenceOrSetDecoder(), # conflicts with SetOf
univ.Choice.tagSet: ChoiceDecoder(), # conflicts with Any
# character string types
char.UTF8String.tagSet: AbstractScalarDecoder(),
char.NumericString.tagSet: AbstractScalarDecoder(),
char.PrintableString.tagSet: AbstractScalarDecoder(),
char.TeletexString.tagSet: AbstractScalarDecoder(),
char.VideotexString.tagSet: AbstractScalarDecoder(),
char.IA5String.tagSet: AbstractScalarDecoder(),
char.GraphicString.tagSet: AbstractScalarDecoder(),
char.VisibleString.tagSet: AbstractScalarDecoder(),
char.GeneralString.tagSet: AbstractScalarDecoder(),
char.UniversalString.tagSet: AbstractScalarDecoder(),
char.BMPString.tagSet: AbstractScalarDecoder(),
# useful types
useful.ObjectDescriptor.tagSet: AbstractScalarDecoder(),
useful.GeneralizedTime.tagSet: AbstractScalarDecoder(),
useful.UTCTime.tagSet: AbstractScalarDecoder()
}
# Put in ambiguous & non-ambiguous types for faster codec lookup
typeMap = {
univ.Integer.typeId: AbstractScalarDecoder(),
univ.Boolean.typeId: AbstractScalarDecoder(),
univ.BitString.typeId: BitStringDecoder(),
univ.OctetString.typeId: AbstractScalarDecoder(),
univ.Null.typeId: AbstractScalarDecoder(),
univ.ObjectIdentifier.typeId: AbstractScalarDecoder(),
univ.Enumerated.typeId: AbstractScalarDecoder(),
univ.Real.typeId: AbstractScalarDecoder(),
# ambiguous base types
univ.Set.typeId: SequenceOrSetDecoder(),
univ.SetOf.typeId: SequenceOfOrSetOfDecoder(),
univ.Sequence.typeId: SequenceOrSetDecoder(),
univ.SequenceOf.typeId: SequenceOfOrSetOfDecoder(),
univ.Choice.typeId: ChoiceDecoder(),
univ.Any.typeId: AbstractScalarDecoder(),
# character string types
char.UTF8String.typeId: AbstractScalarDecoder(),
char.NumericString.typeId: AbstractScalarDecoder(),
char.PrintableString.typeId: AbstractScalarDecoder(),
char.TeletexString.typeId: AbstractScalarDecoder(),
char.VideotexString.typeId: AbstractScalarDecoder(),
char.IA5String.typeId: AbstractScalarDecoder(),
char.GraphicString.typeId: AbstractScalarDecoder(),
char.VisibleString.typeId: AbstractScalarDecoder(),
char.GeneralString.typeId: AbstractScalarDecoder(),
char.UniversalString.typeId: AbstractScalarDecoder(),
char.BMPString.typeId: AbstractScalarDecoder(),
# useful types
useful.ObjectDescriptor.typeId: AbstractScalarDecoder(),
useful.GeneralizedTime.typeId: AbstractScalarDecoder(),
useful.UTCTime.typeId: AbstractScalarDecoder()
}
class Decoder(object):
# noinspection PyDefaultArgument
def __init__(self, tagMap, typeMap):
self.__tagMap = tagMap
self.__typeMap = typeMap
def __call__(self, pyObject, asn1Spec, **options):
if debug.logger & debug.flagDecoder:
logger = debug.logger
else:
logger = None
if logger:
debug.scope.push(type(pyObject).__name__)
logger('decoder called at scope %s, working with type %s' % (debug.scope, type(pyObject).__name__))
if asn1Spec is None or not isinstance(asn1Spec, base.Asn1Item):
raise error.PyAsn1Error('asn1Spec is not valid (should be an instance of an ASN.1 Item, not %s)' % asn1Spec.__class__.__name__)
try:
valueDecoder = self.__typeMap[asn1Spec.typeId]
except KeyError:
# use base type for codec lookup to recover untagged types
baseTagSet = tag.TagSet(asn1Spec.tagSet.baseTag, asn1Spec.tagSet.baseTag)
try:
valueDecoder = self.__tagMap[baseTagSet]
except KeyError:
raise error.PyAsn1Error('Unknown ASN.1 tag %s' % asn1Spec.tagSet)
if logger:
logger('calling decoder %s on Python type %s <%s>' % (type(valueDecoder).__name__, type(pyObject).__name__, repr(pyObject)))
value = valueDecoder(pyObject, asn1Spec, self, **options)
if logger:
logger('decoder %s produced ASN.1 type %s <%s>' % (type(valueDecoder).__name__, type(value).__name__, repr(value)))
debug.scope.pop()
return value
#: Turns Python objects of built-in types into ASN.1 objects.
#:
#: Takes Python objects of built-in types and turns them into a tree of
#: ASN.1 objects (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
#: may be a scalar or an arbitrary nested structure.
#:
#: Parameters
#: ----------
#: pyObject: :py:class:`object`
#: A scalar or nested Python objects
#:
#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
#: A pyasn1 type object to act as a template guiding the decoder. It is required
#: for successful interpretation of Python objects mapping into their ASN.1
#: representations.
#:
#: Returns
#: -------
#: : :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
#: A scalar or constructed pyasn1 object
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On decoding errors
decode = Decoder(tagMap, typeMap)

View File

@ -0,0 +1,212 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
try:
from collections import OrderedDict
except ImportError:
OrderedDict = dict
from pyasn1.type import base, univ, tag, char, useful
from pyasn1 import debug, error
__all__ = ['encode']
class AbstractItemEncoder(object):
def encode(self, value, encodeFun, **options):
raise error.PyAsn1Error('Not implemented')
class BooleanEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return bool(value)
class IntegerEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return int(value)
class BitStringEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return str(value)
class OctetStringEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return value.asOctets()
class TextStringEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return value.prettyPrint()
class NullEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return None
class ObjectIdentifierEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return str(value)
class RealEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return float(value)
class SetEncoder(AbstractItemEncoder):
protoDict = dict
def encode(self, value, encodeFun, **options):
value.verifySizeSpec()
namedTypes = value.componentType
substrate = self.protoDict()
for idx, (key, subValue) in enumerate(value.items()):
if namedTypes and namedTypes[idx].isOptional and not value[idx].isValue:
continue
substrate[key] = encodeFun(subValue, **options)
return substrate
class SequenceEncoder(SetEncoder):
protoDict = OrderedDict
class SequenceOfEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
value.verifySizeSpec()
return [encodeFun(x, **options) for x in value]
class ChoiceEncoder(SequenceEncoder):
pass
class AnyEncoder(AbstractItemEncoder):
def encode(self, value, encodeFun, **options):
return value.asOctets()
tagMap = {
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: TextStringEncoder(),
char.NumericString.tagSet: TextStringEncoder(),
char.PrintableString.tagSet: TextStringEncoder(),
char.TeletexString.tagSet: TextStringEncoder(),
char.VideotexString.tagSet: TextStringEncoder(),
char.IA5String.tagSet: TextStringEncoder(),
char.GraphicString.tagSet: TextStringEncoder(),
char.VisibleString.tagSet: TextStringEncoder(),
char.GeneralString.tagSet: TextStringEncoder(),
char.UniversalString.tagSet: TextStringEncoder(),
char.BMPString.tagSet: TextStringEncoder(),
# useful types
useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
useful.GeneralizedTime.tagSet: OctetStringEncoder(),
useful.UTCTime.tagSet: OctetStringEncoder()
}
# Type-to-codec map for ambiguous ASN.1 types
typeMap = {
univ.Set.typeId: SetEncoder(),
univ.SetOf.typeId: SequenceOfEncoder(),
univ.Sequence.typeId: SequenceEncoder(),
univ.SequenceOf.typeId: SequenceOfEncoder(),
univ.Choice.typeId: ChoiceEncoder(),
univ.Any.typeId: AnyEncoder()
}
class Encoder(object):
# noinspection PyDefaultArgument
def __init__(self, tagMap, typeMap={}):
self.__tagMap = tagMap
self.__typeMap = typeMap
def __call__(self, value, **options):
if not isinstance(value, base.Asn1Item):
raise error.PyAsn1Error('value is not valid (should be an instance of an ASN.1 Item)')
if debug.logger & debug.flagEncoder:
logger = debug.logger
else:
logger = None
if logger:
debug.scope.push(type(value).__name__)
logger('encoder called for type %s <%s>' % (type(value).__name__, value.prettyPrint()))
tagSet = value.tagSet
try:
concreteEncoder = self.__typeMap[value.typeId]
except KeyError:
# use base type for codec lookup to recover untagged types
baseTagSet = tag.TagSet(value.tagSet.baseTag, value.tagSet.baseTag)
try:
concreteEncoder = self.__tagMap[baseTagSet]
except KeyError:
raise error.PyAsn1Error('No encoder for %s' % (value,))
if logger:
logger('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet))
pyObject = concreteEncoder.encode(value, self, **options)
if logger:
logger('encoder %s produced: %s' % (type(concreteEncoder).__name__, repr(pyObject)))
debug.scope.pop()
return pyObject
#: Turns ASN.1 object into a Python built-in type object(s).
#:
#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: walks all its components recursively and produces a Python built-in type or a tree
#: of those.
#:
#: One exception is that instead of :py:class:`dict`, the :py:class:`OrderedDict`
#: can be produced (whenever available) to preserve ordering of the components
#: in ASN.1 SEQUENCE.
#:
#: Parameters
#: ----------
# asn1Value: any pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
#: pyasn1 object to encode (or a tree of them)
#:
#: Returns
#: -------
#: : :py:class:`object`
#: Python built-in type instance (or a tree of them)
#:
#: Raises
#: ------
#: : :py:class:`pyasn1.error.PyAsn1Error`
#: On encoding errors
encode = Encoder(tagMap, typeMap)

View File

@ -0,0 +1,33 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from sys import version_info
if version_info[0:2] < (2, 6):
def bin(value):
bitstring = []
if value > 0:
prefix = '0b'
elif value < 0:
prefix = '-0b'
value = abs(value)
else:
prefix = '0b0'
while value:
if value & 1 == 1:
bitstring.append('1')
else:
bitstring.append('0')
value >>= 1
bitstring.reverse()
return prefix + ''.join(bitstring)
else:
bin = bin

View File

@ -0,0 +1,20 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from sys import version_info
__all__ = ['callable']
if (2, 7) < version_info[:2] < (3, 2):
import collections
def callable(x):
return isinstance(x, collections.Callable)
else:
callable = callable

View File

@ -0,0 +1,22 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from sys import version_info
from datetime import datetime
import time
__all__ = ['strptime']
if version_info[:2] <= (2, 4):
def strptime(text, dateFormat):
return datetime(*(time.strptime(text, dateFormat)[0:6]))
else:
def strptime(text, dateFormat):
return datetime.strptime(text, dateFormat)

View File

@ -0,0 +1,108 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
import sys
try:
import platform
implementation = platform.python_implementation()
except (ImportError, AttributeError):
implementation = 'CPython'
from pyasn1.compat.octets import oct2int, null, ensureString
if sys.version_info[0:2] < (3, 2) or implementation != 'CPython':
from binascii import a2b_hex, b2a_hex
if sys.version_info[0] > 2:
long = int
def from_bytes(octets, signed=False):
if not octets:
return 0
value = long(b2a_hex(ensureString(octets)), 16)
if signed and oct2int(octets[0]) & 0x80:
return value - (1 << len(octets) * 8)
return value
def to_bytes(value, signed=False, length=0):
if value < 0:
if signed:
bits = bitLength(value)
# two's complement form
maxValue = 1 << bits
valueToEncode = (value + maxValue) % maxValue
else:
raise OverflowError('can\'t convert negative int to unsigned')
elif value == 0 and length == 0:
return null
else:
bits = 0
valueToEncode = value
hexValue = hex(valueToEncode)[2:]
if hexValue.endswith('L'):
hexValue = hexValue[:-1]
if len(hexValue) & 1:
hexValue = '0' + hexValue
# padding may be needed for two's complement encoding
if value != valueToEncode or length:
hexLength = len(hexValue) * 4
padLength = max(length, bits)
if padLength > hexLength:
hexValue = '00' * ((padLength - hexLength - 1) // 8 + 1) + hexValue
elif length and hexLength - length > 7:
raise OverflowError('int too big to convert')
firstOctet = int(hexValue[:2], 16)
if signed:
if firstOctet & 0x80:
if value >= 0:
hexValue = '00' + hexValue
elif value < 0:
hexValue = 'ff' + hexValue
octets_value = a2b_hex(hexValue)
return octets_value
def bitLength(number):
# bits in unsigned number
hexValue = hex(abs(number))
bits = len(hexValue) - 2
if hexValue.endswith('L'):
bits -= 1
if bits & 1:
bits += 1
bits *= 4
# TODO: strip lhs zeros
return bits
else:
def from_bytes(octets, signed=False):
return int.from_bytes(bytes(octets), 'big', signed=signed)
def to_bytes(value, signed=False, length=0):
length = max(value.bit_length(), length)
if signed and length % 8 == 0:
length += 1
return value.to_bytes(length // 8 + (length % 8 and 1 or 0), 'big', signed=signed)
def bitLength(number):
return int(number).bit_length()

View File

@ -1,20 +1,46 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from sys import version_info
if version_info[0] <= 2:
int2oct = chr
ints2octs = lambda s: ''.join([ int2oct(x) for x in s ])
# noinspection PyPep8
ints2octs = lambda s: ''.join([int2oct(x) for x in s])
null = ''
oct2int = ord
octs2ints = lambda s: [ oct2int(x) for x in s ]
# TODO: refactor to return a sequence of ints
# noinspection PyPep8
octs2ints = lambda s: [oct2int(x) for x in s]
# noinspection PyPep8
str2octs = lambda x: x
# noinspection PyPep8
octs2str = lambda x: x
# noinspection PyPep8
isOctetsType = lambda s: isinstance(s, str)
# noinspection PyPep8
isStringType = lambda s: isinstance(s, (str, unicode))
# noinspection PyPep8
ensureString = str
else:
ints2octs = bytes
# noinspection PyPep8
int2oct = lambda x: ints2octs((x,))
null = ints2octs()
# noinspection PyPep8
oct2int = lambda x: x
octs2ints = lambda s: [ x for x in s ]
str2octs = lambda x: x.encode()
octs2str = lambda x: x.decode()
# noinspection PyPep8
octs2ints = lambda x: x
# noinspection PyPep8
str2octs = lambda x: x.encode('iso-8859-1')
# noinspection PyPep8
octs2str = lambda x: x.decode('iso-8859-1')
# noinspection PyPep8
isOctetsType = lambda s: isinstance(s, bytes)
# noinspection PyPep8
isStringType = lambda s: isinstance(s, str)
# noinspection PyPep8
ensureString = bytes

View File

@ -0,0 +1,26 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from sys import version_info
if version_info[:2] <= (2, 5):
def partition(string, sep):
try:
a, c = string.split(sep, 1)
except ValueError:
a, b, c = string, '', ''
else:
b = sep
return a, b, c
else:
def partition(string, sep):
return string.partition(sep)

View File

@ -1,36 +1,105 @@
import sys
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
import logging
from pyasn1.compat.octets import octs2ints
from pyasn1 import error
from pyasn1 import __version__
flagNone = 0x0000
flagEncoder = 0x0001
flagDecoder = 0x0002
flagAll = 0xffff
__all__ = ['Debug', 'setLogger', 'hexdump']
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):
class Printer(object):
# noinspection PyShadowingNames
def __init__(self, logger=None, handler=None, formatter=None):
if logger is None:
logger = logging.getLogger('pyasn1')
logger.setLevel(logging.DEBUG)
if handler is None:
handler = logging.StreamHandler()
if formatter is None:
formatter = logging.Formatter('%(asctime)s %(name)s: %(message)s')
handler.setFormatter(formatter)
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
self.__logger = logger
def __call__(self, msg):
self.__logger.debug(msg)
def __str__(self):
return '<python logging>'
if hasattr(logging, 'NullHandler'):
NullHandler = logging.NullHandler
else:
# Python 2.6 and older
class NullHandler(logging.Handler):
def emit(self, record):
pass
class Debug(object):
defaultPrinter = Printer()
def __init__(self, *flags, **options):
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)
if 'loggerName' in options:
# route our logs to parent logger
self._printer = Printer(
logger=logging.getLogger(options['loggerName']),
handler=NullHandler()
)
elif 'printer' in options:
self._printer = options.get('printer')
else:
self._printer = self.defaultPrinter
self._printer('running pyasn1 %s, debug flags %s' % (__version__, ', '.join(flags)))
for flag in flags:
inverse = flag and flag[0] in ('!', '~')
if inverse:
flag = flag[1:]
try:
if inverse:
self._flags &= ~flagMap[flag]
else:
self._flags |= flagMap[flag]
except KeyError:
raise error.PyAsn1Error('bad debug flag %s' % flag)
self._printer("debug category '%s' %s" % (flag, inverse and 'disabled' or 'enabled'))
def __str__(self):
return 'logger %s, flags %x' % (self._printer, self._flags)
def __call__(self, msg):
self._printer('DBG: %s\n' % msg)
self._printer(msg)
def __and__(self, flag):
return self._flags & flag
@ -38,19 +107,27 @@ class Debug:
def __rand__(self, flag):
return flag & self._flags
logger = 0
def setLogger(l):
def setLogger(userLogger):
global logger
logger = l
if userLogger:
logger = userLogger
else:
logger = 0
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)) ]
)
['%s%.2X' % (n % 16 == 0 and ('\n%.5d: ' % n) or '', x)
for n, x in zip(range(len(octets)), octs2ints(octets))]
)
class Scope:
class Scope(object):
def __init__(self):
self._list = []
@ -62,4 +139,5 @@ class Scope:
def pop(self):
return self._list.pop()
scope = Scope()

View File

@ -1,3 +1,18 @@
class PyAsn1Error(Exception): pass
class ValueConstraintError(PyAsn1Error): pass
class SubstrateUnderrunError(PyAsn1Error): pass
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
class PyAsn1Error(Exception):
pass
class ValueConstraintError(PyAsn1Error):
pass
class SubstrateUnderrunError(PyAsn1Error):
pass

View File

@ -1,134 +1,394 @@
# Base classes for ASN.1 types
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
import sys
from pyasn1.type import constraint, tagmap
from pyasn1.type import constraint, tagmap, tag
from pyasn1.compat import calling
from pyasn1 import error
class Asn1Item: pass
__all__ = ['Asn1Item', 'Asn1ItemBase', 'AbstractSimpleAsn1Item', 'AbstractConstructedAsn1Item']
class Asn1Item(object):
@classmethod
def getTypeId(cls, increment=1):
try:
Asn1Item._typeCounter += increment
except AttributeError:
Asn1Item._typeCounter = increment
return Asn1Item._typeCounter
class Asn1ItemBase(Asn1Item):
# Set of tags for this ASN.1 type
tagSet = ()
# A list of constraint.Constraint instances for checking values
#: Set or return a :py:class:`~pyasn1.type.tag.TagSet` object representing
#: ASN.1 tag(s) associated with |ASN.1| type.
tagSet = tag.TagSet()
#: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
#: object imposing constraints on initialization values.
subtypeSpec = constraint.ConstraintsIntersection()
# Used for ambiguous ASN.1 types identification
# Disambiguation 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 __init__(self, **kwargs):
readOnly = {
'tagSet': self.tagSet,
'subtypeSpec': self.subtypeSpec
}
readOnly.update(kwargs)
self.__dict__.update(readOnly)
self._readOnly = readOnly
def __setattr__(self, name, value):
if name[0] != '_' and name in self._readOnly:
raise error.PyAsn1Error('read-only instance attribute "%s"' % name)
self.__dict__[name] = value
@property
def readOnly(self):
return self._readOnly
@property
def effectiveTagSet(self):
"""For |ASN.1| type is equivalent to *tagSet*
"""
return self.tagSet # used by untagged types
@property
def tagMap(self):
"""Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects within callee object.
"""
return tagmap.TagMap({self.tagSet: self})
def isSameTypeWith(self, other, matchTags=True, matchConstraints=True):
"""Examine |ASN.1| type for equality with other ASN.1 type.
ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
(:py:mod:`~pyasn1.type.constraint`) are examined when carrying
out ASN.1 types comparison.
No Python inheritance relationship between PyASN1 objects is considered.
Parameters
----------
other: a pyasn1 type object
Class instance representing ASN.1 type.
Returns
-------
: :class:`bool`
:class:`True` if *other* is |ASN.1| type,
:class:`False` otherwise.
"""
return (self is other or
(not matchTags or self.tagSet == other.tagSet) and
(not matchConstraints or self.subtypeSpec == other.subtypeSpec))
def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True):
"""Examine |ASN.1| type for subtype relationship with other ASN.1 type.
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())
ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
(:py:mod:`~pyasn1.type.constraint`) are examined when carrying
out ASN.1 types comparison.
No Python inheritance relationship between PyASN1 objects is considered.
Parameters
----------
other: a pyasn1 type object
Class instance representing ASN.1 type.
Returns
-------
: :class:`bool`
:class:`True` if *other* is a subtype of |ASN.1| type,
:class:`False` otherwise.
"""
return (not matchTags or
(self.tagSet.isSuperTagSetOf(other.tagSet)) and
(not matchConstraints or self.subtypeSpec.isSuperTypeOf(other.subtypeSpec)))
@staticmethod
def isNoValue(*values):
for value in values:
if value is not None and value is not noValue:
return False
return True
# backward compatibility
def getTagSet(self):
return self.tagSet
def getEffectiveTagSet(self):
return self.effectiveTagSet
def getTagMap(self):
return self.tagMap
def getSubtypeSpec(self):
return self.subtypeSpec
def hasValue(self):
return self.isValue
class NoValue(object):
"""Create a singleton instance of NoValue class.
NoValue object can be used as an initializer on PyASN1 type class
instantiation to represent ASN.1 type rather than ASN.1 data value.
No operations other than type comparison can be performed on
a PyASN1 type object.
"""
skipMethods = ('__getattribute__', '__getattr__', '__setattr__', '__delattr__',
'__class__', '__init__', '__del__', '__new__', '__repr__',
'__qualname__', '__objclass__', 'im_class', '__sizeof__')
_instance = None
def __new__(cls):
if cls._instance is None:
def getPlug(name):
def plug(self, *args, **kw):
raise error.PyAsn1Error('Uninitialized ASN.1 value ("%s" attribute looked up)' % name)
return plug
op_names = [name
for typ in (str, int, list, dict)
for name in dir(typ)
if (name not in cls.skipMethods and
name.startswith('__') and
name.endswith('__') and
calling.callable(getattr(typ, name)))]
for name in set(op_names):
setattr(cls, name, getPlug(name))
cls._instance = object.__new__(cls)
return cls._instance
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()
if attr in self.skipMethods:
raise AttributeError('attribute %s not present' % attr)
raise error.PyAsn1Error('No value for "%s"' % attr)
def __repr__(self):
return '%s()' % self.__class__.__name__
noValue = NoValue()
# Base class for "simple" ASN.1 objects. These are immutable.
class AbstractSimpleAsn1Item(Asn1ItemBase):
class AbstractSimpleAsn1Item(Asn1ItemBase):
#: Default payload value
defaultValue = noValue
def __init__(self, value=None, tagSet=None, subtypeSpec=None):
Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
if value is None or value is noValue:
def __init__(self, value=noValue, **kwargs):
Asn1ItemBase.__init__(self, **kwargs)
if value is noValue or value is None:
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)
try:
self.subtypeSpec(value)
except error.PyAsn1Error:
exType, exValue, exTb = sys.exc_info()
raise exType('%s at %s' % (exValue, self.__class__.__name__))
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)
representation = []
if self._value is not self.defaultValue:
representation.append(self.prettyOut(self._value))
if self.tagSet is not self.__class__.tagSet:
representation.append('tagSet=%r' % (self.tagSet,))
if self.subtypeSpec is not self.__class__.subtypeSpec:
representation.append('subtypeSpec=%r' % (self.subtypeSpec,))
return '%s(%s)' % (self.__class__.__name__, ', '.join(representation))
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
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)
def __nonzero__(self):
return self._value and True or False
else:
def __bool__(self): return bool(self._value)
def __hash__(self): return self.__hashedValue
def __bool__(self):
return self._value and True or False
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 __hash__(self):
return hash(self._value)
@property
def isValue(self):
"""Indicate if |ASN.1| object represents ASN.1 type or ASN.1 value.
In other words, if *isValue* is `True`, then the ASN.1 object is
initialized.
Returns
-------
: :class:`bool`
:class:`True` if object represents ASN.1 value and type,
:class:`False` if object represents just ASN.1 type.
Note
----
There is an important distinction between PyASN1 type and value objects.
The PyASN1 type objects can only participate in ASN.1 type
operations (subtyping, comparison etc) and serve as a
blueprint for serialization codecs to resolve ambiguous types.
The PyASN1 value objects can additionally participate in most
of built-in Python operations.
"""
return self._value is not noValue
def clone(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
properties of the |ASN.1| object.
Parameters
----------
value: :class:`tuple`, :class:`str` or |ASN.1| object
Initialization value to pass to new ASN.1 object instead of
inheriting one from the caller.
tagSet: :py:class:`~pyasn1.type.tag.TagSet`
Object representing ASN.1 tag(s) to use in new object instead of inheriting from the caller
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing ASN.1 subtype constraint(s) to use in new object instead of inheriting from the caller
Returns
-------
:
new instance of |ASN.1| type/value
"""
if value is noValue or value is None:
if not kwargs:
return self
def subtype(self, value=None, implicitTag=None, explicitTag=None,
subtypeSpec=None):
if value is None:
value = self._value
initilaizers = self.readOnly.copy()
initilaizers.update(kwargs)
return self.__class__(value, **initilaizers)
def subtype(self, value=noValue, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
properties of the |ASN.1| object.
Parameters
----------
value: :class:`tuple`, :class:`str` or |ASN.1| object
Initialization value to pass to new ASN.1 object instead of
inheriting one from the caller.
implicitTag: :py:class:`~pyasn1.type.tag.Tag`
Implicitly apply given ASN.1 tag object to caller's
:py:class:`~pyasn1.type.tag.TagSet`, then use the result as
new object's ASN.1 tag(s).
explicitTag: :py:class:`~pyasn1.type.tag.Tag`
Explicitly apply given ASN.1 tag object to caller's
:py:class:`~pyasn1.type.tag.TagSet`, then use the result as
new object's ASN.1 tag(s).
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Add ASN.1 constraints object to one of the caller, then
use the result as new object's ASN.1 constraints.
Returns
-------
:
new instance of |ASN.1| type/value
"""
if value is noValue or value is None:
if not kwargs:
return self
value = self._value
initializers = self.readOnly.copy()
implicitTag = kwargs.pop('implicitTag', 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
return self.__class__(value, tagSet, subtypeSpec)
initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
def prettyIn(self, value): return value
def prettyOut(self, value): return str(value)
explicitTag = kwargs.pop('explicitTag', None)
if explicitTag is not None:
initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
for arg, option in kwargs.items():
initializers[arg] += option
return self.__class__(value, **initializers)
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:
"""Provide human-friendly printable object representation.
Returns
-------
: :class:`str`
human-friendly type and/or value representation.
"""
if self.isValue:
return self.prettyOut(self._value)
else:
return '<no value>'
# XXX Compatibility stub
def prettyPrinter(self, scope=0): return self.prettyPrint(scope)
def prettyPrinter(self, scope=0):
return self.prettyPrint(scope)
# noinspection PyUnusedLocal
def prettyPrintType(self, scope=0):
return '%s -> %s' % (self.tagSet, self.__class__.__name__)
#
# Constructed types:
# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice
@ -148,102 +408,194 @@ class AbstractSimpleAsn1Item(Asn1ItemBase):
# of types for Sequence/Set/Choice.
#
def setupComponent():
"""Returns a sentinel value.
Indicates to a constructed type to set up its inner component so that it
can be referred to. This is useful in situation when you want to populate
descendants of a constructed type what requires being able to refer to
their parent types along the way.
Example
-------
>>> constructed['record'] = setupComponent()
>>> constructed['record']['scalar'] = 42
"""
return noValue
class AbstractConstructedAsn1Item(Asn1ItemBase):
#: If `True`, requires exact component type matching,
#: otherwise subtype relation is only enforced
strictConstraints = False
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
sizeSpec = None
def __init__(self, **kwargs):
readOnly = {
'componentType': self.componentType,
'sizeSpec': self.sizeSpec
}
readOnly.update(kwargs)
Asn1ItemBase.__init__(self, **readOnly)
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
representation = []
if self.componentType is not self.__class__.componentType:
representation.append('componentType=%r' % (self.componentType,))
if self.tagSet is not self.__class__.tagSet:
representation.append('tagSet=%r' % (self.tagSet,))
if self.subtypeSpec is not self.__class__.subtypeSpec:
representation.append('subtypeSpec=%r' % (self.subtypeSpec,))
representation = '%s(%s)' % (self.__class__.__name__, ', '.join(representation))
if self._componentValues:
for idx, component in enumerate(self._componentValues):
if component is None or component is noValue:
continue
representation += '.setComponentByPosition(%d, %s)' % (idx, repr(component))
return representation
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
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)
def __nonzero__(self):
return self._componentValues and True or False
else:
def __bool__(self): return bool(self._componentValues)
def __bool__(self):
return self._componentValues and True or False
def getComponentTagMap(self):
raise error.PyAsn1Error('Method not implemented')
def _cloneComponentValues(self, myClone, cloneValueFlag):
pass
def _cloneComponentValues(self, myClone, cloneValueFlag): pass
def clone(self, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
properties of the |ASN.1| object.
Parameters
----------
tagSet: :py:class:`~pyasn1.type.tag.TagSet`
Object representing non-default ASN.1 tag(s)
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 subtype constraint(s)
sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 size constraint(s)
Returns
-------
:
new instance of |ASN.1| type/value
"""
cloneValueFlag = kwargs.pop('cloneValueFlag', False)
initilaizers = self.readOnly.copy()
initilaizers.update(kwargs)
clone = self.__class__(**initilaizers)
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
self._cloneComponentValues(clone, cloneValueFlag)
def subtype(self, implicitTag=None, explicitTag=None, subtypeSpec=None,
sizeSpec=None, cloneValueFlag=None):
return clone
def subtype(self, **kwargs):
"""Create a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
properties of the |ASN.1| object.
Parameters
----------
tagSet: :py:class:`~pyasn1.type.tag.TagSet`
Object representing non-default ASN.1 tag(s)
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 subtype constraint(s)
sizeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 size constraint(s)
Returns
-------
:
new instance of |ASN.1| type/value
"""
initializers = self.readOnly.copy()
cloneValueFlag = kwargs.pop('cloneValueFlag', False)
implicitTag = kwargs.pop('implicitTag', 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)
initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
explicitTag = kwargs.pop('explicitTag', None)
if explicitTag is not None:
initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
for arg, option in kwargs.items():
initializers[arg] += option
clone = self.__class__(**initializers)
if cloneValueFlag:
self._cloneComponentValues(r, cloneValueFlag)
return r
self._cloneComponentValues(clone, cloneValueFlag)
def _verifyComponent(self, idx, value): pass
return clone
def verifySizeSpec(self): self._sizeSpec(self)
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 setComponents(self, *args, **kwargs):
for idx, value in enumerate(args):
self[idx] = value
for k in kwargs:
self[k] = kwargs[k]
return self
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 __len__(self): return len(self._componentValues)
def clear(self):
self._componentValues = []
self._componentValuesSet = 0
def setDefaultComponents(self): pass
# backward compatibility
def setDefaultComponents(self):
pass
def getComponentType(self):
return self.componentType

View File

@ -1,61 +1,374 @@
# ASN.1 "character string" types
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
import sys
from pyasn1.type import univ, tag
from pyasn1 import error
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(
__all__ = ['NumericString', 'PrintableString', 'TeletexString', 'T61String', 'VideotexString',
'IA5String', 'GraphicString', 'VisibleString', 'ISO646String',
'GeneralString', 'UniversalString', 'BMPString', 'UTF8String']
NoValue = univ.NoValue
noValue = univ.noValue
class AbstractCharacterString(univ.OctetString):
"""Creates |ASN.1| type or object.
|ASN.1| objects are immutable and duck-type Python 2 :class:`unicode` or Python 3 :class:`str`.
When used in octet-stream context, |ASN.1| type assumes "|encoding|" encoding.
Parameters
----------
value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object
unicode object (Python 2) or string (Python 3), alternatively string
(Python 2) or bytes (Python 3) representing octet-stream of serialized
unicode string (note `encoding` parameter) or |ASN.1| class instance.
tagSet: :py:class:`~pyasn1.type.tag.TagSet`
Object representing non-default ASN.1 tag(s)
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 subtype constraint(s)
encoding: :py:class:`str`
Unicode codec ID to encode/decode :class:`unicode` (Python 2) or
:class:`str` (Python 3) the payload when |ASN.1| object is used
in octet-stream context.
Raises
------
: :py:class:`pyasn1.error.PyAsn1Error`
On constraint violation or bad initializer.
"""
if sys.version_info[0] <= 2:
def __str__(self):
try:
return self._value.encode(self.encoding)
except UnicodeEncodeError:
raise error.PyAsn1Error(
"Can't encode string '%s' with codec %s" % (self._value, self.encoding)
)
def __unicode__(self):
return unicode(self._value)
def prettyIn(self, value):
try:
if isinstance(value, unicode):
return value
elif isinstance(value, str):
return value.decode(self.encoding)
elif isinstance(value, (tuple, list)):
return self.prettyIn(''.join([chr(x) for x in value]))
elif isinstance(value, univ.OctetString):
return value.asOctets().decode(self.encoding)
else:
return unicode(value)
except (UnicodeDecodeError, LookupError):
raise error.PyAsn1Error(
"Can't decode string '%s' with codec %s" % (value, self.encoding)
)
def asOctets(self, padding=True):
return str(self)
def asNumbers(self, padding=True):
return tuple([ord(x) for x in str(self)])
else:
def __str__(self):
return str(self._value)
def __bytes__(self):
try:
return self._value.encode(self.encoding)
except UnicodeEncodeError:
raise error.PyAsn1Error(
"Can't encode string '%s' with codec %s" % (self._value, self.encoding)
)
def prettyIn(self, value):
try:
if isinstance(value, str):
return value
elif isinstance(value, bytes):
return value.decode(self.encoding)
elif isinstance(value, (tuple, list)):
return self.prettyIn(bytes(value))
elif isinstance(value, univ.OctetString):
return value.asOctets().decode(self.encoding)
else:
return str(value)
except (UnicodeDecodeError, LookupError):
raise error.PyAsn1Error(
"Can't decode string '%s' with codec %s" % (value, self.encoding)
)
def asOctets(self, padding=True):
return bytes(self)
def asNumbers(self, padding=True):
return tuple(bytes(self))
def prettyOut(self, value):
return value
def __reversed__(self):
return reversed(self._value)
def clone(self, value=noValue, **kwargs):
"""Creates a copy of a |ASN.1| type or object.
Any parameters to the *clone()* method will replace corresponding
properties of the |ASN.1| object.
Parameters
----------
value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object
unicode object (Python 2) or string (Python 3), alternatively string
(Python 2) or bytes (Python 3) representing octet-stream of serialized
unicode string (note `encoding` parameter) or |ASN.1| class instance.
tagSet: :py:class:`~pyasn1.type.tag.TagSet`
Object representing non-default ASN.1 tag(s)
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 subtype constraint(s)
encoding: :py:class:`str`
Unicode codec ID to encode/decode :py:class:`unicode` (Python 2) or
:py:class:`str` (Python 3) the payload when |ASN.1| object is used
in octet-stream context.
Returns
-------
:
new instance of |ASN.1| type/value
"""
return univ.OctetString.clone(self, value, **kwargs)
def subtype(self, value=noValue, **kwargs):
"""Creates a copy of a |ASN.1| type or object.
Any parameters to the *subtype()* method will be added to the corresponding
properties of the |ASN.1| object.
Parameters
----------
value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object
unicode object (Python 2) or string (Python 3), alternatively string
(Python 2) or bytes (Python 3) representing octet-stream of serialized
unicode string (note `encoding` parameter) or |ASN.1| class instance.
implicitTag: :py:class:`~pyasn1.type.tag.Tag`
Implicitly apply given ASN.1 tag object to caller's
:py:class:`~pyasn1.type.tag.TagSet`, then use the result as
new object's ASN.1 tag(s).
explicitTag: :py:class:`~pyasn1.type.tag.Tag`
Explicitly apply given ASN.1 tag object to caller's
:py:class:`~pyasn1.type.tag.TagSet`, then use the result as
new object's ASN.1 tag(s).
subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
Object representing non-default ASN.1 subtype constraint(s)
encoding: :py:class:`str`
Unicode codec ID to encode/decode :py:class:`unicode` (Python 2) or
:py:class:`str` (Python 3) the payload when |ASN.1| object is used
in octet-stream context.
Returns
-------
:
new instance of |ASN.1| type/value
"""
return univ.OctetString.subtype(self, value, **kwargs)
class NumericString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18)
)
)
encoding = 'us-ascii'
class PrintableString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class PrintableString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19)
)
)
encoding = 'us-ascii'
class TeletexString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class TeletexString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20)
)
)
encoding = 'iso-8859-1'
class VideotexString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class T61String(TeletexString):
__doc__ = TeletexString.__doc__
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class VideotexString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21)
)
)
encoding = 'iso-8859-1'
class IA5String(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class IA5String(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22)
)
)
encoding = 'us-ascii'
class GraphicString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class GraphicString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25)
)
)
encoding = 'iso-8859-1'
class VisibleString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class VisibleString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26)
)
)
encoding = 'us-ascii'
class GeneralString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class ISO646String(VisibleString):
__doc__ = VisibleString.__doc__
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class GeneralString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27)
)
)
encoding = 'iso-8859-1'
class UniversalString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class UniversalString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28)
)
)
encoding = "utf-32-be"
class BMPString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly(
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class BMPString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30)
)
)
encoding = "utf-16-be"
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class UTF8String(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (on class, not on instance) or return a
#: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
#: associated with |ASN.1| type.
tagSet = AbstractCharacterString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
)
encoding = "utf-8"
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()

View File

@ -1,86 +1,124 @@
#
# ASN.1 subtype constraints classes.
# This file is part of pyasn1 software.
#
# 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.
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
# 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.
# Original concept and code by Mike C. Fletcher.
#
import sys
from pyasn1.type import error
class AbstractConstraint:
__all__ = ['SingleValueConstraint', 'ContainedSubtypeConstraint', 'ValueRangeConstraint',
'ValueSizeConstraint', 'PermittedAlphabetConstraint', 'InnerTypeConstraint',
'ConstraintsExclusion', 'ConstraintsIntersection', 'ConstraintsUnion']
class AbstractConstraint(object):
"""Abstract base-class for constraint objects
Constraints should be stored in a simple sequence in the
namespace of their client Asn1Item sub-classes.
namespace of their client Asn1Item sub-classes in cases
when ASN.1 constraint is define.
"""
def __init__(self, *values):
self._valueMap = {}
self._valueMap = set()
self._setValues(values)
self.__hashedValues = None
self.__hash = hash((self.__class__.__name__, self._values))
def __call__(self, value, idx=None):
if not self._values:
return
try:
self._testValue(value, idx)
except error.ValueConstraintError:
raise error.ValueConstraintError(
'%s failed at: \"%s\"' % (self, sys.exc_info()[1])
'%s failed at: %r' % (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
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)
def __nonzero__(self):
return self._values and True or False
else:
def __bool__(self): return bool(self._values)
def __bool__(self):
return self._values and True or False
def __hash__(self):
if self.__hashedValues is None:
self.__hashedValues = hash((self.__class__.__name__, self._values))
return self.__hashedValues
return self.__hash
def _setValues(self, values):
self._values = values
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 getValueMap(self):
return self._valueMap
def isSuperTypeOf(self, otherConstraint):
return self in otherConstraint.getValueMap() or \
otherConstraint is self or otherConstraint == self
# TODO: fix possible comparison of set vs scalars here
return (otherConstraint is self or
not self._values or
otherConstraint == self or
self in otherConstraint.getValueMap())
def isSubTypeOf(self, otherConstraint):
return otherConstraint in self._valueMap or \
otherConstraint is self or otherConstraint == self
return (otherConstraint is self or
not self or
otherConstraint == self or
otherConstraint in self._valueMap)
class SingleValueConstraint(AbstractConstraint):
"""Value must be part of defined values constraint"""
def _setValues(self, values):
self._values = values
self._set = set(values)
def _testValue(self, value, idx):
# XXX index vals for performance?
if value not in self._values:
if value not in self._set:
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)
@ -89,7 +127,7 @@ class ValueRangeConstraint(AbstractConstraint):
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(
@ -99,28 +137,31 @@ class ValueRangeConstraint(AbstractConstraint):
)
)
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:
valueSize = len(value)
if valueSize < self.start or valueSize > 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)
self._values = values
self._set = set(values)
def _testValue(self, value, idx):
for v in value:
if v not in self._values:
raise error.ValueConstraintError(value)
if not self._set.issuperset(value):
raise error.ValueConstraintError(value)
# This is a bit kludgy, meaning two op modes within a single constraing
# This is a bit kludgy, meaning two op modes within a single constraint
class InnerTypeConstraint(AbstractConstraint):
"""Value must satisfy type and presense constraints"""
def _testValue(self, value, idx):
if self.__singleTypeConstraint:
self.__singleTypeConstraint(value)
@ -128,7 +169,7 @@ class InnerTypeConstraint(AbstractConstraint):
if idx not in self.__multipleTypeConstraint:
raise error.ValueConstraintError(value)
constraint, status = self.__multipleTypeConstraint[idx]
if status == 'ABSENT': # XXX presense is not checked!
if status == 'ABSENT': # XXX presense is not checked!
raise error.ValueConstraintError(value)
constraint(value)
@ -142,10 +183,12 @@ class InnerTypeConstraint(AbstractConstraint):
self.__singleTypeConstraint = v
AbstractConstraint._setValues(self, values)
# Boolean ops on constraints
# 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)
@ -159,42 +202,57 @@ class ConstraintsExclusion(AbstractConstraint):
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 __getitem__(self, idx):
return self._values[idx]
def __len__(self): return len(self._values)
def __iter__(self):
return iter(self._values)
def __add__(self, value):
return self.__class__(*(self._values + (value,)))
def __radd__(self, value):
return self.__class__(*((value,) + self._values))
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())
for constraint in values:
if constraint:
self._valueMap.add(constraint)
self._valueMap.update(constraint.getValueMap())
class ConstraintsIntersection(AbstractConstraintSet):
"""Value must satisfy all constraints"""
def _testValue(self, value, idx):
for v in self._values:
v(value, idx)
for constraint in self._values:
constraint(value, idx)
class ConstraintsUnion(AbstractConstraintSet):
"""Value must satisfy at least one constraint"""
def _testValue(self, value, idx):
for v in self._values:
for constraint in self._values:
try:
v(value, idx)
constraint(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

View File

@ -1,3 +1,11 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
from pyasn1.error import PyAsn1Error
class ValueConstraintError(PyAsn1Error): pass
class ValueConstraintError(PyAsn1Error):
pass

View File

@ -1,132 +1,511 @@
# NamedType specification for constructed types
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
import sys
from pyasn1.type import tagmap
from pyasn1.type import tag, 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 = {}
__all__ = ['NamedType', 'OptionalNamedType', 'DefaultedNamedType', 'NamedTypes']
class NamedType(object):
"""Create named field object for a constructed ASN.1 type.
The |NamedType| object represents a single name and ASN.1 type of a constructed ASN.1 type.
|NamedType| objects are immutable and duck-type Python :class:`tuple` objects
holding *name* and *asn1Object* components.
Parameters
----------
name: :py:class:`str`
Field name
asn1Object:
ASN.1 type object
"""
isOptional = False
isDefaulted = False
def __init__(self, name, asn1Object):
self.__name = name
self.__type = asn1Object
self.__nameAndType = name, asn1Object
def __repr__(self):
r = '%s(' % self.__class__.__name__
for n in self.__namedTypes:
r = r + '%r, ' % (n,)
return r + ')'
return '%s(%r, %r)' % (self.__class__.__name__, self.__name, self.__type)
def __eq__(self, other):
return self.__nameAndType == other
def __ne__(self, other):
return self.__nameAndType != other
def __lt__(self, other):
return self.__nameAndType < other
def __le__(self, other):
return self.__nameAndType <= other
def __gt__(self, other):
return self.__nameAndType > other
def __ge__(self, other):
return self.__nameAndType >= other
def __hash__(self):
return hash(self.__nameAndType)
def __getitem__(self, idx):
return self.__nameAndType[idx]
def __iter__(self):
return iter(self.__nameAndType)
@property
def name(self):
return self.__name
def __getitem__(self, idx): return self.__namedTypes[idx]
@property
def asn1Object(self):
return self.__type
# Backward compatibility
def getName(self):
return self.name
def getType(self):
return self.asn1Object
class OptionalNamedType(NamedType):
__doc__ = NamedType.__doc__
isOptional = True
class DefaultedNamedType(NamedType):
__doc__ = NamedType.__doc__
isDefaulted = True
class NamedTypes(object):
"""Create a collection of named fields for a constructed ASN.1 type.
The NamedTypes object represents a collection of named fields of a constructed ASN.1 type.
*NamedTypes* objects are immutable and duck-type Python :class:`dict` objects
holding *name* as keys and ASN.1 type object as values.
Parameters
----------
*namedTypes: :class:`~pyasn1.type.namedtype.NamedType`
"""
def __init__(self, *namedTypes, **kwargs):
self.__namedTypes = namedTypes
self.__namedTypesLen = len(self.__namedTypes)
self.__minTagSet = self.__computeMinTagSet()
self.__nameToPosMap = self.__computeNameToPosMap()
self.__tagToPosMap = self.__computeTagToPosMap()
self.__ambiguousTypes = 'terminal' not in kwargs and self.__computeAmbiguousTypes() or {}
self.__uniqueTagMap = self.__computeTagMaps(unique=True)
self.__nonUniqueTagMap = self.__computeTagMaps(unique=False)
self.__hasOptionalOrDefault = bool([True for namedType in self.__namedTypes
if namedType.isDefaulted or namedType.isOptional])
self.__requiredComponents = frozenset(
[idx for idx, nt in enumerate(self.__namedTypes) if not nt.isOptional and not nt.isDefaulted]
)
self.__keys = frozenset([namedType.name for namedType in self.__namedTypes])
self.__values = tuple([namedType.asn1Object for namedType in self.__namedTypes])
self.__items = tuple([(namedType.name, namedType.asn1Object) for namedType in self.__namedTypes])
def __repr__(self):
return '%s(%s)' % (
self.__class__.__name__, ', '.join([repr(x) for x in self.__namedTypes])
)
def __eq__(self, other):
return self.__namedTypes == other
def __ne__(self, other):
return self.__namedTypes != other
def __lt__(self, other):
return self.__namedTypes < other
def __le__(self, other):
return self.__namedTypes <= other
def __gt__(self, other):
return self.__namedTypes > other
def __ge__(self, other):
return self.__namedTypes >= other
def __hash__(self):
return hash(self.__namedTypes)
def __getitem__(self, idx):
try:
return self.__namedTypes[idx]
except TypeError:
return self.__namedTypes[self.__nameToPosMap[idx]]
def __contains__(self, key):
return key in self.__nameToPosMap
def __iter__(self):
return (x[0] for x in self.__namedTypes)
if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self.__namedTypesLen)
def __nonzero__(self):
return self.__namedTypesLen > 0
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 __bool__(self):
return self.__namedTypesLen > 0
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
def __len__(self):
return self.__namedTypesLen
# Python dict protocol
def values(self):
return self.__values
def keys(self):
return self.__keys
def items(self):
return self.__items
def clone(self):
return self.__class__(*self.__namedTypes)
class PostponedError(object):
def __init__(self, errorMsg):
self.__errorMsg = errorMsg
def __getitem__(self, item):
raise error.PyAsn1Error(self.__errorMsg)
def __computeTagToPosMap(self):
tagToPosMap = {}
for idx, namedType in enumerate(self.__namedTypes):
tagMap = namedType.asn1Object.tagMap
if isinstance(tagMap, NamedTypes.PostponedError):
return tagMap
if not tagMap:
continue
for _tagSet in tagMap.presentTypes:
if _tagSet in tagToPosMap:
return NamedTypes.PostponedError('Duplicate component tag %s at %s' % (_tagSet, namedType))
tagToPosMap[_tagSet] = idx
return tagToPosMap
def __computeNameToPosMap(self):
nameToPosMap = {}
for idx, namedType in enumerate(self.__namedTypes):
if namedType.name in nameToPosMap:
return NamedTypes.PostponedError('Duplicate component name %s at %s' % (namedType.name, namedType))
nameToPosMap[namedType.name] = idx
return nameToPosMap
def __computeAmbiguousTypes(self):
ambigiousTypes = {}
partialAmbigiousTypes = ()
for idx, namedType in reversed(tuple(enumerate(self.__namedTypes))):
if namedType.isOptional or namedType.isDefaulted:
partialAmbigiousTypes = (namedType,) + partialAmbigiousTypes
else:
partialAmbigiousTypes = (namedType,)
if len(partialAmbigiousTypes) == len(self.__namedTypes):
ambigiousTypes[idx] = self
else:
ambigiousTypes[idx] = NamedTypes(*partialAmbigiousTypes, **dict(terminal=True))
return ambigiousTypes
def getTypeByPosition(self, idx):
"""Return ASN.1 type object by its position in fields set.
Parameters
----------
idx: :py:class:`int`
Field index
Returns
-------
:
ASN.1 type
Raises
------
: :class:`~pyasn1.error.PyAsn1Error`
If given position is out of fields range
"""
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()
return self.__namedTypes[idx].asn1Object
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
def getPositionByType(self, tagSet):
"""Return field position by its ASN.1 type.
Parameters
----------
tagSet: :class:`~pysnmp.type.tag.TagSet`
ASN.1 tag set distinguishing one ASN.1 type from others.
Returns
-------
: :py:class:`int`
ASN.1 type position in fields set
Raises
------
: :class:`~pyasn1.error.PyAsn1Error`
If *tagSet* is not present or ASN.1 types are not unique within callee *NamedTypes*
"""
try:
return self.__nameToPosIdx[name]
return self.__tagToPosMap[tagSet]
except KeyError:
raise error.PyAsn1Error('Type %s not found' % (tagSet,))
def getNameByPosition(self, idx):
"""Return field name by its position in fields set.
Parameters
----------
idx: :py:class:`idx`
Field index
Returns
-------
: :py:class:`str`
Field name
Raises
------
: :class:`~pyasn1.error.PyAsn1Error`
If given field name is not present in callee *NamedTypes*
"""
try:
return self.__namedTypes[idx].name
except IndexError:
raise error.PyAsn1Error('Type position out of range')
def getPositionByName(self, name):
"""Return field position by filed name.
Parameters
----------
name: :py:class:`str`
Field name
Returns
-------
: :py:class:`int`
Field position in fields set
Raises
------
: :class:`~pyasn1.error.PyAsn1Error`
If *name* is not present or not unique within callee *NamedTypes*
"""
try:
return self.__nameToPosMap[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()
"""Return ASN.1 types that are allowed at or past given field position.
Some ASN.1 serialization allow for skipping optional and defaulted fields.
Some constructed ASN.1 types allow reordering of the fields. When recovering
such objects it may be important to know which types can possibly be
present at any given position in the field sets.
Parameters
----------
idx: :py:class:`int`
Field index
Returns
-------
: :class:`~pyasn1.type.tagmap.TagMap`
Map if ASN.1 types allowed at given field position
Raises
------
: :class:`~pyasn1.error.PyAsn1Error`
If given position is out of fields range
"""
try:
return self.__ambigiousTypes[idx].getTagMap()
return self.__ambiguousTypes[idx].tagMap
except KeyError:
raise error.PyAsn1Error('Type position out of range')
def getPositionNearType(self, tagSet, idx):
if not self.__ambigiousTypes: self.__buildAmbigiousTagMap()
"""Return the closest field position where given ASN.1 type is allowed.
Some ASN.1 serialization allow for skipping optional and defaulted fields.
Some constructed ASN.1 types allow reordering of the fields. When recovering
such objects it may be important to know at which field position, in field set,
given *tagSet* is allowed at or past *idx* position.
Parameters
----------
tagSet: :class:`~pyasn1.type.tag.TagSet`
ASN.1 type which field position to look up
idx: :py:class:`int`
Field position at or past which to perform ASN.1 type look up
Returns
-------
: :py:class:`int`
Field position in fields set
Raises
------
: :class:`~pyasn1.error.PyAsn1Error`
If *tagSet* is not present or not unique within callee *NamedTypes*
or *idx* is out of fields range
"""
try:
return idx+self.__ambigiousTypes[idx].getPositionByType(tagSet)
return idx + self.__ambiguousTypes[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
def __computeMinTagSet(self):
minTagSet = None
for namedType in self.__namedTypes:
asn1Object = namedType.asn1Object
try:
tagSet = asn1Object.minTagSet
except AttributeError:
tagSet = asn1Object.tagSet
if minTagSet is None or tagSet < minTagSet:
minTagSet = tagSet
return minTagSet or tag.TagSet()
@property
def minTagSet(self):
"""Return the minimal TagSet among ASN.1 type in callee *NamedTypes*.
Some ASN.1 types/serialization protocols require ASN.1 types to be
arranged based on their numerical tag value. The *minTagSet* property
returns that.
Returns
-------
: :class:`~pyasn1.type.tagset.TagSet`
Minimal TagSet among ASN.1 types in callee *NamedTypes*
"""
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]
def __computeTagMaps(self, unique):
presentTypes = {}
skipTypes = {}
defaultType = None
for namedType in self.__namedTypes:
tagMap = namedType.asn1Object.tagMap
if isinstance(tagMap, NamedTypes.PostponedError):
return tagMap
for tagSet in tagMap:
if unique and tagSet in presentTypes:
return NamedTypes.PostponedError('Non-unique tagSet %s of %s at %s' % (tagSet, namedType, self))
presentTypes[tagSet] = namedType.asn1Object
skipTypes.update(tagMap.skipTypes)
if defaultType is None:
defaultType = tagMap.defaultType
elif tagMap.defaultType is not None:
return NamedTypes.PostponedError('Duplicate default ASN.1 type at %s' % (self,))
return tagmap.TagMap(presentTypes, skipTypes, defaultType)
@property
def tagMap(self):
"""Return a *TagMap* object from tags and types recursively.
Return a :class:`~pyasn1.type.tagmap.TagMap` object by
combining tags from *TagMap* objects of children types and
associating them with their immediate child type.
Example
-------
.. code-block:: python
OuterType ::= CHOICE {
innerType INTEGER
}
Calling *.tagMap* on *OuterType* will yield a map like this:
.. code-block:: python
Integer.tagSet -> Choice
"""
return self.__nonUniqueTagMap
@property
def tagMapUnique(self):
"""Return a *TagMap* object from unique tags and types recursively.
Return a :class:`~pyasn1.type.tagmap.TagMap` object by
combining tags from *TagMap* objects of children types and
associating them with their immediate child type.
Example
-------
.. code-block:: python
OuterType ::= CHOICE {
innerType INTEGER
}
Calling *.tagMapUnique* on *OuterType* will yield a map like this:
.. code-block:: python
Integer.tagSet -> Choice
Note
----
Duplicate *TagSet* objects found in the tree of children
types would cause error.
"""
return self.__uniqueTagMap
@property
def hasOptionalOrDefault(self):
return self.__hasOptionalOrDefault
@property
def namedTypes(self):
return iter(self.__namedTypes)
@property
def requiredComponents(self):
return self.__requiredComponents

View File

@ -1,46 +1,181 @@
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# License: http://pyasn1.sf.net/license.html
#
# ASN.1 named integers
#
from pyasn1 import error
__all__ = [ 'NamedValues' ]
__all__ = ['NamedValues']
class NamedValues(object):
"""Create named values object.
The |NamedValues| object represents a collection of string names
associated with numeric IDs. These objects are used for giving
names to otherwise numerical values.
|NamedValues| objects are immutable and duck-type Python
:class:`dict` object mapping ID to name and vice-versa.
Parameters
----------
\*args: variable number of two-element :py:class:`tuple`
\*\*kwargs: keyword parameters of:
name: :py:class:`str`
Value name
value: :py:class:`int`
A numerical value
Examples
--------
>>> nv = namedval.NamedValues('a', 'b', ('c', 0), d=1)
>>> nv
>>> {'c': 0, 'd': 1, 'a': 2, 'b': 3}
>>> nv[0]
'c'
>>> nv['a']
2
"""
def __init__(self, *args, **kwargs):
self.__names = {}
self.__numbers = {}
anonymousNames = []
for namedValue in args:
if isinstance(namedValue, (tuple, list)):
try:
name, number = namedValue
except ValueError:
raise error.PyAsn1Error('Not a proper attribute-value pair %r' % (namedValue,))
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]
anonymousNames.append(namedValue)
continue
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)
if name in self.__names:
raise error.PyAsn1Error('Duplicate name %s' % (name,))
if number in self.__numbers:
raise error.PyAsn1Error('Duplicate number %s=%s' % (name, number))
self.__names[name] = number
self.__numbers[number] = name
for name, number in kwargs.items():
if name in self.__names:
raise error.PyAsn1Error('Duplicate name %s' % (name,))
if number in self.__numbers:
raise error.PyAsn1Error('Duplicate number %s=%s' % (name, number))
self.__names[name] = number
self.__numbers[number] = name
if anonymousNames:
number = self.__numbers and max(self.__numbers) + 1 or 0
for name in anonymousNames:
if name in self.__names:
raise error.PyAsn1Error('Duplicate name %s' % (name,))
self.__names[name] = number
self.__numbers[number] = name
number += 1
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, tuple(self.items()))
def __str__(self):
return str(self.items())
def __eq__(self, other):
return dict(self) == other
def __ne__(self, other):
return dict(self) != other
def __lt__(self, other):
return dict(self) < other
def __le__(self, other):
return dict(self) <= other
def __gt__(self, other):
return dict(self) > other
def __ge__(self, other):
return dict(self) >= other
def __hash__(self):
return hash(self.items())
# Python dict protocol (read-only)
def __getitem__(self, key):
try:
return self.__numbers[key]
except KeyError:
return self.__names[key]
def __len__(self):
return len(self.__names)
def __contains__(self, key):
return key in self.__names or key in self.__numbers
def __iter__(self):
return iter(self.__names)
def values(self):
return iter(self.__numbers)
def keys(self):
return iter(self.__names)
def items(self):
for name in self.__names:
yield name, self.__names[name]
# support merging
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)
return self.__class__(*tuple(self.items()) + tuple(namedValues.items()))
# XXX clone/subtype?
# XXX clone/subtype?
def clone(self, *args, **kwargs):
new = self.__class__(*args, **kwargs)
return self + new
# legacy protocol
def getName(self, value):
if value in self.__numbers:
return self.__numbers[value]
def getValue(self, name):
if name in self.__names:
return self.__names[name]
def getValues(self, *names):
try:
return [self.__names[name] for name in names]
except KeyError:
raise error.PyAsn1Error(
'Unknown bit identifier(s): %s' % (set(names).difference(self.__names),)
)

Some files were not shown because too many files have changed in this diff Show More