Update pyasn to 0.2.4

This commit is contained in:
shortcutme 2017-04-06 19:18:33 +02:00
parent 2be3ff02bd
commit 12a0c955bc
No known key found for this signature in database
GPG key ID: 5B63BAE6CB9613AE
32 changed files with 6210 additions and 2110 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

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. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View file

@ -1,26 +0,0 @@
Metadata-Version: 1.0
Name: pyasn1
Version: 0.1.7
Summary: ASN.1 types and codecs
Home-page: http://sourceforge.net/projects/pyasn1/
Author: Ilya Etingof <ilya@glas.net>
Author-email: ilya@glas.net
License: BSD
Description: A pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208).
Platform: any
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Education
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Telecommunications Industry
Classifier: License :: OSI Approved :: BSD License
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Communications
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules

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

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?

View file

@ -1,8 +1,8 @@
import sys import sys
# http://www.python.org/dev/peps/pep-0396/ # http://www.python.org/dev/peps/pep-0396/
__version__ = '0.1.7' __version__ = '0.2.4'
if sys.version_info[:2] < (2, 4): 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,319 @@
# BER 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 base, tag, univ, char, useful from pyasn1.type import base, tag, univ, char, useful
from pyasn1.codec.ber import eoo from pyasn1.codec.ber import eoo
from pyasn1.compat.octets import int2oct, oct2int, ints2octs, null, str2octs from pyasn1.compat.octets import int2oct, oct2int, ints2octs, null, str2octs
from pyasn1.compat.integer import to_bytes
from pyasn1 import debug, error from pyasn1 import debug, error
class Error(Exception): pass __all__ = ['encode']
class AbstractItemEncoder:
class AbstractItemEncoder(object):
supportIndefLenMode = 1 supportIndefLenMode = 1
def encodeTag(self, t, isConstructed):
tagClass, tagFormat, tagId = t.asTuple() # this is a hotspot # noinspection PyMethodMayBeStatic
v = tagClass | tagFormat def encodeTag(self, singleTag, isConstructed):
tagClass, tagFormat, tagId = singleTag
encodedTag = tagClass | tagFormat
if isConstructed: if isConstructed:
v = v|tag.tagFormatConstructed encodedTag |= tag.tagFormatConstructed
if tagId < 31: if tagId < 31:
return int2oct(v|tagId) return (encodedTag | tagId,)
else: else:
s = int2oct(tagId&0x7f) substrate = (tagId & 0x7f,)
tagId = tagId >> 7 tagId >>= 7
while tagId: while tagId:
s = int2oct(0x80|(tagId&0x7f)) + s substrate = (0x80 | (tagId & 0x7f),) + substrate
tagId = tagId >> 7 tagId >>= 7
return int2oct(v|0x1F) + s return (encodedTag | 0x1F,) + substrate
def encodeLength(self, length, defMode): def encodeLength(self, length, defMode):
if not defMode and self.supportIndefLenMode: if not defMode and self.supportIndefLenMode:
return int2oct(0x80) return (0x80,)
if length < 0x80: if length < 0x80:
return int2oct(length) return (length,)
else: else:
substrate = null substrate = ()
while length: while length:
substrate = int2oct(length&0xff) + substrate substrate = (length & 0xff,) + substrate
length = length >> 8 length >>= 8
substrateLen = len(substrate) substrateLen = len(substrate)
if substrateLen > 126: if substrateLen > 126:
raise Error('Length octets overflow (%d)' % substrateLen) raise error.PyAsn1Error('Length octets overflow (%d)' % substrateLen)
return int2oct(0x80 | substrateLen) + substrate return (0x80 | substrateLen,) + substrate
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
raise Error('Not implemented') raise error.PyAsn1Error('Not implemented')
def _encodeEndOfOctets(self, encodeFun, defMode): def _encodeEndOfOctets(self, encodeFun, defMode):
if defMode or not self.supportIndefLenMode: if defMode or not self.supportIndefLenMode:
return null return null
else: else:
return encodeFun(eoo.endOfOctets, defMode) return encodeFun(eoo.endOfOctets, defMode)
def encode(self, encodeFun, value, defMode, maxChunkSize): def encode(self, encodeFun, value, defMode, maxChunkSize):
substrate, isConstructed = self.encodeValue( substrate, isConstructed, isOctets = self.encodeValue(
encodeFun, value, defMode, maxChunkSize encodeFun, value, defMode, maxChunkSize
) )
tagSet = value.getTagSet() tagSet = value.tagSet
# tagged value?
if tagSet: if tagSet:
if not isConstructed: # primitive form implies definite mode if not isConstructed: # primitive form implies definite mode
defMode = 1 defMode = True
return self.encodeTag( header = self.encodeTag(tagSet[-1], isConstructed)
tagSet[-1], isConstructed header += self.encodeLength(len(substrate), defMode)
) + self.encodeLength(
len(substrate), defMode if isOctets:
) + substrate + self._encodeEndOfOctets(encodeFun, defMode) substrate = ints2octs(header) + substrate
else: else:
return substrate # untagged value substrate = ints2octs(header + substrate)
eoo = self._encodeEndOfOctets(encodeFun, defMode)
if eoo:
substrate += eoo
return substrate
class EndOfOctetsEncoder(AbstractItemEncoder): class EndOfOctetsEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return null, 0 return null, False, True
class ExplicitlyTaggedItemEncoder(AbstractItemEncoder): class ExplicitlyTaggedItemEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if isinstance(value, base.AbstractConstructedAsn1Item): if isinstance(value, base.AbstractConstructedAsn1Item):
value = value.clone(tagSet=value.getTagSet()[:-1], value = value.clone(tagSet=value.tagSet[:-1], cloneValueFlag=1)
cloneValueFlag=1)
else: else:
value = value.clone(tagSet=value.getTagSet()[:-1]) value = value.clone(tagSet=value.tagSet[:-1])
return encodeFun(value, defMode, maxChunkSize), 1 return encodeFun(value, defMode, maxChunkSize), True, True
explicitlyTaggedItemEncoder = ExplicitlyTaggedItemEncoder() explicitlyTaggedItemEncoder = ExplicitlyTaggedItemEncoder()
class BooleanEncoder(AbstractItemEncoder): class BooleanEncoder(AbstractItemEncoder):
supportIndefLenMode = 0 supportIndefLenMode = False
_true = ints2octs((1,))
_false = ints2octs((0,))
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return value and self._true or self._false, 0 return value and (1,) or (0,), False, False
class IntegerEncoder(AbstractItemEncoder): class IntegerEncoder(AbstractItemEncoder):
supportIndefLenMode = 0 supportIndefLenMode = False
supportCompactZero = False supportCompactZero = False
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if value == 0: # shortcut for zero value if value == 0:
# de-facto way to encode zero
if self.supportCompactZero: if self.supportCompactZero:
# this seems to be a correct way for encoding zeros return (), False, False
return null, 0
else: else:
# this seems to be a widespread way for encoding zeros return (0,), False, False
return ints2octs((0,)), 0
octets = [] return to_bytes(int(value), signed=True), False, True
value = int(value) # to save on ops on asn1 type
while 1:
octets.insert(0, value & 0xff)
if value == 0 or value == -1:
break
value = value >> 8
if value == 0 and octets[0] & 0x80:
octets.insert(0, 0)
while len(octets) > 1 and \
(octets[0] == 0 and octets[1] & 0x80 == 0 or \
octets[0] == 0xff and octets[1] & 0x80 != 0):
del octets[0]
return ints2octs(octets), 0
class BitStringEncoder(AbstractItemEncoder): class BitStringEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if not maxChunkSize or len(value) <= maxChunkSize*8: valueLength = len(value)
r = {}; l = len(value); p = 0; j = 7 if valueLength % 8:
while p < l: alignedValue = value << (8 - valueLength % 8)
i, j = divmod(p, 8)
r[i] = r.get(i,0) | value[p]<<(7-j)
p = p + 1
keys = list(r); keys.sort()
return int2oct(7-j) + ints2octs([r[k] for k in keys]), 0
else: else:
pos = 0; substrate = null alignedValue = value
while 1:
# count in octets if not maxChunkSize or len(alignedValue) <= maxChunkSize * 8:
v = value.clone(value[pos*8:pos*8+maxChunkSize*8]) substrate = alignedValue.asOctets()
if not v: return int2oct(len(substrate) * 8 - valueLength) + substrate, False, True
break
substrate = substrate + encodeFun(v, defMode, maxChunkSize) stop = 0
pos = pos + maxChunkSize substrate = null
return substrate, 1 while stop < valueLength:
start = stop
stop = min(start + maxChunkSize * 8, valueLength)
substrate += encodeFun(alignedValue[start:stop], defMode, maxChunkSize)
return substrate, True, True
class OctetStringEncoder(AbstractItemEncoder): class OctetStringEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if not maxChunkSize or len(value) <= maxChunkSize: if not maxChunkSize or len(value) <= maxChunkSize:
return value.asOctets(), 0 return value.asOctets(), False, True
else: else:
pos = 0; substrate = null pos = 0
while 1: substrate = null
v = value.clone(value[pos:pos+maxChunkSize]) while True:
v = value.clone(value[pos:pos + maxChunkSize])
if not v: if not v:
break break
substrate = substrate + encodeFun(v, defMode, maxChunkSize) substrate += encodeFun(v, defMode, maxChunkSize)
pos = pos + maxChunkSize pos += maxChunkSize
return substrate, 1
return substrate, True, True
class NullEncoder(AbstractItemEncoder): class NullEncoder(AbstractItemEncoder):
supportIndefLenMode = 0 supportIndefLenMode = False
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return null, 0 return null, False, True
class ObjectIdentifierEncoder(AbstractItemEncoder): class ObjectIdentifierEncoder(AbstractItemEncoder):
supportIndefLenMode = 0 supportIndefLenMode = False
precomputedValues = {
(1, 3, 6, 1, 2): (43, 6, 1, 2), def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
(1, 3, 6, 1, 4): (43, 6, 1, 4)
}
def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
oid = value.asTuple() 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 # Build the first pair
if oid[0] > 6 or oid[1] > 39 or oid[0] == 6 and oid[1] > 15: try:
raise error.PyAsn1Error( first = oid[0]
'Initial sub-ID overflow %s in OID %s' % (oid[:2], value) second = oid[1]
)
octets = (oid[0] * 40 + oid[1],)
index = 2
# Cycle through subids except IndexError:
for subid in oid[index:]: raise error.PyAsn1Error('Short OID %s' % (value,))
if subid > -1 and subid < 128:
# Optimize for the common case if 0 <= second <= 39:
octets = octets + (subid & 0x7f,) if first == 1:
elif subid < 0 or subid > 0xFFFFFFFF: oid = (second + 40,) + oid[2:]
raise error.PyAsn1Error( elif first == 0:
'SubId overflow %s in %s' % (subid, value) oid = (second,) + oid[2:]
) elif first == 2:
oid = (second + 80,) + oid[2:]
else: 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 # Pack large Sub-Object IDs
res = (subid & 0x7f,) res = (subOid & 0x7f,)
subid = subid >> 7 subOid >>= 7
while subid > 0: while subOid:
res = (0x80 | (subid & 0x7f),) + res res = (0x80 | (subOid & 0x7f),) + res
subid = subid >> 7 subOid >>= 7
# Add packed Sub-Object ID to resulted Object ID # Add packed Sub-Object ID to resulted Object ID
octets += res octets += res
else:
return ints2octs(octets), 0 raise error.PyAsn1Error('Negative OID arc %s at %s' % (subOid, value))
return octets, False, False
class RealEncoder(AbstractItemEncoder): class RealEncoder(AbstractItemEncoder):
supportIndefLenMode = 0 supportIndefLenMode = 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, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
if value.isPlusInfinity(): if value.isPlusInfinity():
return int2oct(0x40), 0 return (0x40,), False, False
if value.isMinusInfinity(): if value.isMinusInfinity():
return int2oct(0x41), 0 return (0x41,), False, False
m, b, e = value m, b, e = value
if not m: if not m:
return null, 0 return null, False, True
if b == 10: 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: elif b == 2:
fo = 0x80 # binary enoding fo = 0x80 # binary encoding
if m < 0: ms, m, encbase, e = self._chooseEncBase(value)
fo = fo | 0x40 # sign bit if ms < 0: # mantissa sign
m = -m fo |= 0x40 # sign bit
while int(m) != m: # drop floating point # exponenta & mantissa normalization
m *= 2 if encbase == 2:
e -= 1 while m & 0x1 == 0:
while m & 0x1 == 0: # mantissa normalization 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 m >>= 1
e += 1 sf += 1
if sf > 3:
raise error.PyAsn1Error('Scale factor overflow') # bug if raised
fo |= sf << 2
eo = null eo = null
while e not in (0, -1): if e == 0 or e == -1:
eo = int2oct(e&0xff) + eo eo = int2oct(e & 0xff)
e >>= 8 else:
if e == 0 and eo and oct2int(eo[0]) & 0x80: while e not in (0, -1):
eo = int2oct(0) + eo 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) n = len(eo)
if n > 0xff: if n > 0xff:
raise error.PyAsn1Error('Real exponent overflow') raise error.PyAsn1Error('Real exponent overflow')
@ -235,51 +325,54 @@ class RealEncoder(AbstractItemEncoder):
fo |= 2 fo |= 2
else: else:
fo |= 3 fo |= 3
eo = int2oct(n//0xff+1) + eo eo = int2oct(n & 0xff) + eo
po = null po = null
while m: while m:
po = int2oct(m&0xff) + po po = int2oct(m & 0xff) + po
m >>= 8 m >>= 8
substrate = int2oct(fo) + eo + po substrate = int2oct(fo) + eo + po
return substrate, 0 return substrate, False, True
else: else:
raise error.PyAsn1Error('Prohibited Real base %s' % b) raise error.PyAsn1Error('Prohibited Real base %s' % b)
class SequenceEncoder(AbstractItemEncoder): class SequenceEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
value.setDefaultComponents()
value.verifySizeSpec() value.verifySizeSpec()
substrate = null; idx = len(value) namedTypes = value.getComponentType()
substrate = null
idx = len(value)
while idx > 0: while idx > 0:
idx = idx - 1 idx -= 1
if value[idx] is None: # Optional component if namedTypes:
continue if namedTypes[idx].isOptional and not value[idx].isValue:
component = value.getDefaultComponentByPosition(idx) continue
if component is not None and component == value[idx]: if namedTypes[idx].isDefaulted and value[idx] == namedTypes[idx].asn1Object:
continue continue
substrate = encodeFun( substrate = encodeFun(value[idx], defMode, maxChunkSize) + substrate
value[idx], defMode, maxChunkSize return substrate, True, True
) + substrate
return substrate, 1
class SequenceOfEncoder(AbstractItemEncoder): class SequenceOfEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
value.verifySizeSpec() value.verifySizeSpec()
substrate = null; idx = len(value) substrate = null
idx = len(value)
while idx > 0: while idx > 0:
idx = idx - 1 idx -= 1
substrate = encodeFun( substrate = encodeFun(value[idx], defMode, maxChunkSize) + substrate
value[idx], defMode, maxChunkSize return substrate, True, True
) + substrate
return substrate, 1
class ChoiceEncoder(AbstractItemEncoder): class ChoiceEncoder(AbstractItemEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return encodeFun(value.getComponent(), defMode, maxChunkSize), 1 return encodeFun(value.getComponent(), defMode, maxChunkSize), True, True
class AnyEncoder(OctetStringEncoder): class AnyEncoder(OctetStringEncoder):
def encodeValue(self, encodeFun, value, defMode, maxChunkSize): def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
return value.asOctets(), defMode == 0 return value.asOctets(), defMode == False, True
tagMap = { tagMap = {
eoo.endOfOctets.tagSet: EndOfOctetsEncoder(), eoo.endOfOctets.tagSet: EndOfOctetsEncoder(),
@ -308,46 +401,106 @@ tagMap = {
char.UniversalString.tagSet: OctetStringEncoder(), char.UniversalString.tagSet: OctetStringEncoder(),
char.BMPString.tagSet: OctetStringEncoder(), char.BMPString.tagSet: OctetStringEncoder(),
# useful types # useful types
useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
useful.GeneralizedTime.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 = { 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.Set.typeId: SequenceEncoder(),
univ.SetOf.typeId: SequenceOfEncoder(), univ.SetOf.typeId: SequenceOfEncoder(),
univ.Sequence.typeId: SequenceEncoder(), univ.Sequence.typeId: SequenceEncoder(),
univ.SequenceOf.typeId: SequenceOfEncoder(), univ.SequenceOf.typeId: SequenceOfEncoder(),
univ.Choice.typeId: ChoiceEncoder(), 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):
supportIndefLength = True
# noinspection PyDefaultArgument
def __init__(self, tagMap, typeMap={}): def __init__(self, tagMap, typeMap={}):
self.__tagMap = tagMap self.__tagMap = tagMap
self.__typeMap = typeMap self.__typeMap = typeMap
def __call__(self, value, defMode=1, maxChunkSize=0): def __call__(self, value, defMode=True, 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())) if not defMode and not self.supportIndefLength:
tagSet = value.getTagSet() raise error.PyAsn1Error('Indefinite length encoding not supported by this codec')
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.prettyPrintType(), value.prettyPrint()))
tagSet = value.tagSet
if len(tagSet) > 1: if len(tagSet) > 1:
concreteEncoder = explicitlyTaggedItemEncoder concreteEncoder = explicitlyTaggedItemEncoder
else: else:
if value.typeId is not None and value.typeId in self.__typeMap: try:
concreteEncoder = self.__typeMap[value.typeId] concreteEncoder = self.__typeMap[value.typeId]
elif tagSet in self.__tagMap: except KeyError:
concreteEncoder = self.__tagMap[tagSet] # use base type for codec lookup to recover untagged types
else: baseTagSet = tag.TagSet(value.tagSet.baseTag, value.tagSet.baseTag)
tagSet = value.baseTagSet try:
if tagSet in self.__tagMap: concreteEncoder = self.__tagMap[baseTagSet]
concreteEncoder = self.__tagMap[tagSet] except KeyError:
else: raise error.PyAsn1Error('No encoder for %s' % (value,))
raise Error('No encoder for %s' % (value,)) debug.logger & debug.flagEncoder and debug.logger(
debug.logger & debug.flagEncoder and debug.logger('using value codec %s chosen by %r' % (concreteEncoder.__class__.__name__, tagSet)) 'using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet))
substrate = concreteEncoder.encode( substrate = concreteEncoder.encode(
self, value, defMode, maxChunkSize self, value, defMode, maxChunkSize
) )
debug.logger & debug.flagEncoder and debug.logger('built %s octets of substrate: %s\nencoder completed' % (len(substrate), debug.hexdump(substrate))) debug.logger & debug.flagEncoder and debug.logger(
'built %s octets of substrate: %s\nencoder completed' % (len(substrate), debug.hexdump(substrate)))
return 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) 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 from pyasn1.type import base, tag
class EndOfOctets(base.AbstractSimpleAsn1Item): class EndOfOctets(base.AbstractSimpleAsn1Item):
defaultValue = 0 defaultValue = 0
tagSet = tag.initTagSet( tagSet = tag.initTagSet(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00)
) )
_instance = None
def __new__(cls, *args):
if cls._instance is None:
cls._instance = object.__new__(cls, *args)
return cls._instance
endOfOctets = EndOfOctets() endOfOctets = EndOfOctets()

View file

@ -1,16 +1,25 @@
# 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.type import univ
from pyasn1.codec.ber import decoder from pyasn1.codec.ber import decoder
from pyasn1.compat.octets import oct2int from pyasn1.compat.octets import oct2int
from pyasn1 import error from pyasn1 import error
__all__ = ['decode']
class BooleanDecoder(decoder.AbstractSimpleDecoder): class BooleanDecoder(decoder.AbstractSimpleDecoder):
protoComponent = univ.Boolean(0) protoComponent = univ.Boolean(0)
def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length, def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length,
state, decodeFun, substrateFun): state, decodeFun, substrateFun):
head, tail = substrate[:length], substrate[length:] head, tail = substrate[:length], substrate[length:]
if not head: if not head or length != 1:
raise error.PyAsn1Error('Empty substrate') raise error.PyAsn1Error('Not single-octet Boolean payload')
byte = oct2int(head[0]) byte = oct2int(head[0])
# CER/DER specifies encoding of TRUE as 0xFF and FALSE as 0x0, while # 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 # BER allows any non-zero value as TRUE; cf. sections 8.2.2. and 11.1
@ -20,16 +29,59 @@ class BooleanDecoder(decoder.AbstractSimpleDecoder):
elif byte == 0x00: elif byte == 0x00:
value = 0 value = 0
else: else:
raise error.PyAsn1Error('Boolean CER violation: %s' % byte) raise error.PyAsn1Error('Unexpected Boolean payload: %s' % byte)
return self._createComponent(asn1Spec, tagSet, value), tail 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 = decoder.tagMap.copy()
tagMap.update({ tagMap.update(
univ.Boolean.tagSet: BooleanDecoder() {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():
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) decode = Decoder(tagMap, decoder.typeMap)

View file

@ -1,87 +1,179 @@
# 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 univ
from pyasn1.type import useful
from pyasn1.codec.ber import encoder from pyasn1.codec.ber import encoder
from pyasn1.compat.octets import int2oct, null from pyasn1.compat.octets import int2oct, str2octs, null
from pyasn1 import error
__all__ = ['encode']
class BooleanEncoder(encoder.IntegerEncoder): class BooleanEncoder(encoder.IntegerEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize): def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
if client == 0: if client == 0:
substrate = int2oct(0) substrate = (0,)
else: else:
substrate = int2oct(255) substrate = (255,)
return substrate, 0 return substrate, False, False
class BitStringEncoder(encoder.BitStringEncoder): class BitStringEncoder(encoder.BitStringEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize): def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
return encoder.BitStringEncoder.encodeValue( return encoder.BitStringEncoder.encodeValue(
self, encodeFun, client, defMode, 1000 self, encodeFun, client, defMode, 1000
) )
class OctetStringEncoder(encoder.OctetStringEncoder): class OctetStringEncoder(encoder.OctetStringEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize): def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
return encoder.OctetStringEncoder.encodeValue( return encoder.OctetStringEncoder.encodeValue(
self, encodeFun, client, defMode, 1000 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 GeneralStringEncoder here
# specialized GeneralizedTimeEncoder here
# specialized UTCTimeEncoder here class GeneralizedTimeEncoder(OctetStringEncoder):
zchar = str2octs('Z')
pluschar = str2octs('+')
minuschar = str2octs('-')
zero = str2octs('0')
def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
octets = client.asOctets()
# This breaks too many existing data items
# if '.' not in octets:
# raise error.PyAsn1Error('Format must include fraction of second: %r' % octets)
if len(octets) < 15:
raise error.PyAsn1Error('Bad UTC time length: %r' % octets)
if self.pluschar in octets or self.minuschar in octets:
raise error.PyAsn1Error('Must be UTC time: %r' % octets)
if octets[-1] != self.zchar[0]:
raise error.PyAsn1Error('Missing timezone specifier: %r' % octets)
return encoder.OctetStringEncoder.encodeValue(
self, encodeFun, client, defMode, 1000
)
class UTCTimeEncoder(encoder.OctetStringEncoder):
zchar = str2octs('Z')
pluschar = str2octs('+')
minuschar = str2octs('-')
def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
octets = client.asOctets()
if self.pluschar in octets or self.minuschar in octets:
raise error.PyAsn1Error('Must be UTC time: %r' % octets)
if octets and octets[-1] != self.zchar[0]:
client = client.clone(octets + self.zchar)
if len(client) != 13:
raise error.PyAsn1Error('Bad UTC time length: %r' % client)
return encoder.OctetStringEncoder.encodeValue(
self, encodeFun, client, defMode, 1000
)
class SetOfEncoder(encoder.SequenceOfEncoder): class SetOfEncoder(encoder.SequenceOfEncoder):
def encodeValue(self, encodeFun, client, defMode, maxChunkSize): def encodeValue(self, encodeFun, client, defMode, maxChunkSize):
if isinstance(client, univ.SequenceAndSetBase):
client.setDefaultComponents()
client.verifySizeSpec() client.verifySizeSpec()
substrate = null; idx = len(client) substrate = null
idx = len(client)
# This is certainly a hack but how else do I distinguish SetOf # This is certainly a hack but how else do I distinguish SetOf
# from Set if they have the same tags&constraints? # from Set if they have the same tags&constraints?
if isinstance(client, univ.SequenceAndSetBase): if isinstance(client, univ.SequenceAndSetBase):
# Set # Set
namedTypes = client.getComponentType()
comps = [] comps = []
while idx > 0: while idx > 0:
idx = idx - 1 idx -= 1
if client[idx] is None: # Optional component if namedTypes[idx].isOptional and not client[idx].isValue:
continue continue
if client.getDefaultComponentByPosition(idx) == client[idx]: if namedTypes[idx].isDefaulted and client[idx] == namedTypes[idx].asn1Object:
continue continue
comps.append(client[idx]) comps.append(client[idx])
comps.sort(key=lambda x: isinstance(x, univ.Choice) and \ comps.sort(key=lambda x: isinstance(x, univ.Choice) and x.getMinTagSet() or x.tagSet)
x.getMinTagSet() or x.getTagSet())
for c in comps: for c in comps:
substrate += encodeFun(c, defMode, maxChunkSize) substrate += encodeFun(c, defMode, maxChunkSize)
else: else:
# SetOf # SetOf
compSubs = [] compSubs = []
while idx > 0: while idx > 0:
idx = idx - 1 idx -= 1
compSubs.append( compSubs.append(
encodeFun(client[idx], defMode, maxChunkSize) encodeFun(client[idx], defMode, maxChunkSize)
) )
compSubs.sort() # perhaps padding's not needed compSubs.sort() # perhaps padding's not needed
substrate = null substrate = null
for compSub in compSubs: for compSub in compSubs:
substrate += compSub substrate += compSub
return substrate, 1 return substrate, True, True
tagMap = encoder.tagMap.copy() tagMap = encoder.tagMap.copy()
tagMap.update({ tagMap.update({
univ.Boolean.tagSet: BooleanEncoder(), univ.Boolean.tagSet: BooleanEncoder(),
univ.BitString.tagSet: BitStringEncoder(), univ.BitString.tagSet: BitStringEncoder(),
univ.OctetString.tagSet: OctetStringEncoder(), univ.OctetString.tagSet: OctetStringEncoder(),
univ.Real.tagSet: RealEncoder(),
useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(),
useful.UTCTime.tagSet: UTCTimeEncoder(),
univ.SetOf().tagSet: SetOfEncoder() # conflcts with Set univ.SetOf().tagSet: SetOfEncoder() # conflcts with Set
}) })
typeMap = encoder.typeMap.copy() typeMap = encoder.typeMap.copy()
typeMap.update({ typeMap.update({
univ.Boolean.typeId: BooleanEncoder(),
univ.BitString.typeId: BitStringEncoder(),
univ.OctetString.typeId: OctetStringEncoder(),
univ.Real.typeId: RealEncoder(),
useful.GeneralizedTime.typeId: GeneralizedTimeEncoder(),
useful.UTCTime.typeId: UTCTimeEncoder(),
univ.Set.typeId: SetOfEncoder(), univ.Set.typeId: SetOfEncoder(),
univ.SetOf.typeId: SetOfEncoder() univ.SetOf.typeId: SetOfEncoder()
}) })
class Encoder(encoder.Encoder): class Encoder(encoder.Encoder):
def __call__(self, client, defMode=0, maxChunkSize=0): def __call__(self, client, defMode=False, maxChunkSize=0):
return encoder.Encoder.__call__(self, client, defMode, maxChunkSize) return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
#: 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) encode = Encoder(tagMap, typeMap)
# EncoderFactory queries class instance and builds a map of tags -> encoders # EncoderFactory queries class instance and builds a map of tags -> encoders

View file

@ -1,9 +1,69 @@
# 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.type import univ
from pyasn1.codec.cer import decoder from pyasn1.codec.cer import decoder
tagMap = decoder.tagMap __all__ = ['decode']
typeMap = decoder.typeMap
Decoder = decoder.Decoder
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():
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) decode = Decoder(tagMap, typeMap)

View file

@ -1,28 +1,67 @@
# 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.type import univ
from pyasn1.codec.cer import encoder from pyasn1.codec.cer import encoder
from pyasn1 import error
__all__ = ['encode']
class SetOfEncoder(encoder.SetOfEncoder): class SetOfEncoder(encoder.SetOfEncoder):
def _cmpSetComponents(self, c1, c2): @staticmethod
tagSet1 = isinstance(c1, univ.Choice) and \ def _cmpSetComponents(c1, c2):
c1.getEffectiveTagSet() or c1.getTagSet() tagSet1 = isinstance(c1, univ.Choice) and c1.effectiveTagSet or c1.tagSet
tagSet2 = isinstance(c2, univ.Choice) and \ tagSet2 = isinstance(c2, univ.Choice) and c2.effectiveTagSet or c2.tagSet
c2.getEffectiveTagSet() or c2.getTagSet()
return cmp(tagSet1, tagSet2) return cmp(tagSet1, tagSet2)
tagMap = encoder.tagMap.copy() tagMap = encoder.tagMap.copy()
tagMap.update({ tagMap.update({
# Overload CER encodrs with BER ones (a bit hackerish XXX) # Overload CER encoders with BER ones (a bit hackerish XXX)
univ.BitString.tagSet: encoder.encoder.BitStringEncoder(), univ.BitString.tagSet: encoder.encoder.BitStringEncoder(),
univ.OctetString.tagSet: encoder.encoder.OctetStringEncoder(), univ.OctetString.tagSet: encoder.encoder.OctetStringEncoder(),
# Set & SetOf have same tags # Set & SetOf have same tags
univ.SetOf().tagSet: SetOfEncoder() univ.SetOf().tagSet: SetOfEncoder()
}) })
typeMap = encoder.typeMap.copy()
typeMap = encoder.typeMap
class Encoder(encoder.Encoder): class Encoder(encoder.Encoder):
def __call__(self, client, defMode=1, maxChunkSize=0): supportIndefLength = False
def __call__(self, client, defMode=True, maxChunkSize=0):
if not defMode or maxChunkSize:
raise error.PyAsn1Error('DER forbids indefinite length mode')
return encoder.Encoder.__call__(self, client, defMode, maxChunkSize) return encoder.Encoder.__call__(self, client, defMode, maxChunkSize)
#: 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) encode = Encoder(tagMap, typeMap)

View file

@ -0,0 +1 @@
# This file is necessary to make this directory a package.

View file

@ -0,0 +1,188 @@
#
# 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, decoderFunc=None):
return asn1Spec.clone(pyObject)
class BitStringDecoder(AbstractScalarDecoder):
def __call__(self, pyObject, asn1Spec, decoderFunc=None):
return asn1Spec.clone(univ.BitString.fromBinaryString(pyObject))
class SequenceOrSetDecoder(object):
def __call__(self, pyObject, asn1Spec, decoderFunc):
asn1Value = asn1Spec.clone()
componentsTypes = asn1Spec.getComponentType()
for field in asn1Value:
if field in pyObject:
asn1Value[field] = decoderFunc(pyObject[field], componentsTypes[field].asn1Object)
return asn1Value
class SequenceOfOrSetOfDecoder(object):
def __call__(self, pyObject, asn1Spec, decoderFunc):
asn1Value = asn1Spec.clone()
for pyValue in pyObject:
asn1Value.append(decoderFunc(pyValue, asn1Spec.getComponentType()))
return asn1Value
class ChoiceDecoder(object):
def __call__(self, pyObject, asn1Spec, decoderFunc):
asn1Value = asn1Spec.clone()
componentsTypes = asn1Spec.getComponentType()
for field in pyObject:
if field in componentsTypes:
asn1Value[field] = decoderFunc(pyObject[field], componentsTypes[field].asn1Object)
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):
if debug.logger & debug.flagDecoder:
debug.scope.push(type(pyObject).__name__)
debug.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 debug.logger & debug.flagDecoder:
debug.logger('calling decoder %s on Python type %s <%s>' % (type(valueDecoder).__name__, type(pyObject).__name__, repr(pyObject)))
value = valueDecoder(pyObject, asn1Spec, self)
if debug.logger & debug.flagDecoder:
debug.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,215 @@
#
# 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, char, useful
from pyasn1 import debug, error
__all__ = ['encode']
class AbstractItemEncoder(object):
def encode(self, encodeFun, value):
raise error.PyAsn1Error('Not implemented')
class ExplicitlyTaggedItemEncoder(AbstractItemEncoder):
def encode(self, encodeFun, value):
if isinstance(value, base.AbstractConstructedAsn1Item):
value = value.clone(tagSet=value.tagSet[:-1],
cloneValueFlag=1)
else:
value = value.clone(tagSet=value.tagSet[:-1])
return encodeFun(value)
explicitlyTaggedItemEncoder = ExplicitlyTaggedItemEncoder()
class BooleanEncoder(AbstractItemEncoder):
def encode(self, encodeFun, value):
return bool(value)
class IntegerEncoder(AbstractItemEncoder):
def encode(self, encodeFun, value):
return int(value)
class BitStringEncoder(AbstractItemEncoder):
def encode(self, encodeFun, value):
return str(value)
class OctetStringEncoder(AbstractItemEncoder):
def encode(self, encodeFun, value):
return value.asOctets()
class TextStringEncoder(AbstractItemEncoder):
def encode(self, encodeFun, value):
return value.prettyPrint()
class NullEncoder(AbstractItemEncoder):
def encode(self, encodeFun, value):
return None
class ObjectIdentifierEncoder(AbstractItemEncoder):
def encode(self, encodeFun, value):
return str(value)
class RealEncoder(AbstractItemEncoder):
def encode(self, encodeFun, value):
return float(value)
class SetEncoder(AbstractItemEncoder):
protoDict = dict
def encode(self, encodeFun, value):
value.verifySizeSpec()
namedTypes = value.getComponentType()
substrate = self.protoDict()
for idx, (key, subValue) in enumerate(value.items()):
if namedTypes[idx].isOptional and not value[idx].isValue:
continue
substrate[key] = encodeFun(subValue)
return substrate
class SequenceEncoder(SetEncoder):
protoDict = OrderedDict
class SequenceOfEncoder(AbstractItemEncoder):
def encode(self, encodeFun, value):
value.verifySizeSpec()
return [encodeFun(x) for x in value]
class ChoiceEncoder(SequenceEncoder):
pass
class AnyEncoder(AbstractItemEncoder):
def encode(self, encodeFun, value):
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, asn1Value):
if not isinstance(asn1Value, base.Asn1Item):
raise error.PyAsn1Error('value is not valid (should be an instance of an ASN.1 Item)')
if debug.logger & debug.flagEncoder:
debug.scope.push(type(asn1Value).__name__)
debug.logger('encoder called for type %s <%s>' % (type(asn1Value).__name__, asn1Value.prettyPrint()))
tagSet = asn1Value.tagSet
if len(tagSet) > 1:
concreteEncoder = explicitlyTaggedItemEncoder
else:
if asn1Value.typeId is not None and asn1Value.typeId in self.__typeMap:
concreteEncoder = self.__typeMap[asn1Value.typeId]
elif tagSet in self.__tagMap:
concreteEncoder = self.__tagMap[tagSet]
else:
tagSet = asn1Value.baseTagSet
if tagSet in self.__tagMap:
concreteEncoder = self.__tagMap[tagSet]
else:
raise error.PyAsn1Error('No encoder for %s' % (asn1Value,))
debug.logger & debug.flagEncoder and debug.logger('using value codec %s chosen by %s' % (type(concreteEncoder).__name__, tagSet))
pyObject = concreteEncoder.encode(self, asn1Value)
if debug.logger & debug.flagEncoder:
debug.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,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 sys import version_info
if version_info[0:2] < (2, 6):
def bin(value):
bitstring = []
while value:
if value & 1 == 1:
bitstring.append('1')
else:
bitstring.append('0')
value >>= 1
bitstring.reverse()
return '0b' + ''.join(bitstring)
else:
bin = bin

View file

@ -0,0 +1,96 @@
#
# 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
if sys.version_info[0:2] < (3, 2):
from binascii import a2b_hex, b2a_hex
from pyasn1.compat.octets import oct2int, null
if sys.version_info[0:2] < (3, 2):
def from_bytes(octets, signed=False):
value = long(b2a_hex(str(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 from sys import version_info
if version_info[0] <= 2: if version_info[0] <= 2:
int2oct = chr 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 = '' null = ''
oct2int = ord oct2int = ord
octs2ints = lambda s: [ oct2int(x) for x in s ] # noinspection PyPep8
octs2ints = lambda s: [oct2int(x) for x in s]
# noinspection PyPep8
str2octs = lambda x: x str2octs = lambda x: x
# noinspection PyPep8
octs2str = lambda x: x octs2str = lambda x: x
# noinspection PyPep8
isOctetsType = lambda s: isinstance(s, str) isOctetsType = lambda s: isinstance(s, str)
# noinspection PyPep8
isStringType = lambda s: isinstance(s, (str, unicode))
# noinspection PyPep8
ensureString = str
else: else:
ints2octs = bytes ints2octs = bytes
# noinspection PyPep8
int2oct = lambda x: ints2octs((x,)) int2oct = lambda x: ints2octs((x,))
null = ints2octs() null = ints2octs()
# noinspection PyPep8
oct2int = lambda x: x oct2int = lambda x: x
octs2ints = lambda s: [ x for x in s ] # noinspection PyPep8
str2octs = lambda x: x.encode() octs2ints = lambda x: x
octs2str = lambda x: x.decode() # 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) isOctetsType = lambda s: isinstance(s, bytes)
# noinspection PyPep8
isStringType = lambda s: isinstance(s, str)
# noinspection PyPep8
ensureString = bytes

View file

@ -1,36 +1,96 @@
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.compat.octets import octs2ints
from pyasn1 import error from pyasn1 import error
from pyasn1 import __version__ from pyasn1 import __version__
flagNone = 0x0000 __all__ = ['Debug', 'setLogger', 'hexdump']
flagEncoder = 0x0001
flagDecoder = 0x0002 flagNone = 0x0000
flagAll = 0xffff flagEncoder = 0x0001
flagDecoder = 0x0002
flagAll = 0xffff
flagMap = { flagMap = {
'encoder': flagEncoder, 'encoder': flagEncoder,
'decoder': flagDecoder, 'decoder': flagDecoder,
'all': flagAll 'all': flagAll
} }
class Debug:
defaultPrinter = sys.stderr.write class Printer(object):
def __init__(self, *flags): # 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 built-in 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 = None
def __init__(self, *flags, **options):
self._flags = flagNone self._flags = flagNone
self._printer = self.defaultPrinter if options.get('printer') is not None:
self._printer = options.get('printer')
elif self.defaultPrinter is not None:
self._printer = self.defaultPrinter
if 'loggerName' in options:
# route our logs to parent logger
self._printer = Printer(
logger=logging.getLogger(options['loggerName']),
handler=NullHandler()
)
else:
self._printer = Printer()
self('running pyasn1 version %s' % __version__) self('running pyasn1 version %s' % __version__)
for f in flags: for f in flags:
if f not in flagMap: inverse = f and f[0] in ('!', '~')
raise error.PyAsn1Error('bad debug flag %s' % (f,)) if inverse:
self._flags = self._flags | flagMap[f] f = f[1:]
self('debug category \'%s\' enabled' % f) try:
if inverse:
self._flags &= ~flagMap[f]
else:
self._flags |= flagMap[f]
except KeyError:
raise error.PyAsn1Error('bad debug flag %s' % f)
self('debug category \'%s\' %s' % (f, inverse and 'disabled' or 'enabled'))
def __str__(self): def __str__(self):
return 'logger %s, flags %x' % (self._printer, self._flags) return 'logger %s, flags %x' % (self._printer, self._flags)
def __call__(self, msg): def __call__(self, msg):
self._printer('DBG: %s\n' % msg) self._printer(msg)
def __and__(self, flag): def __and__(self, flag):
return self._flags & flag return self._flags & flag
@ -38,19 +98,23 @@ class Debug:
def __rand__(self, flag): def __rand__(self, flag):
return flag & self._flags return flag & self._flags
logger = 0 logger = 0
def setLogger(l): def setLogger(l):
global logger global logger
logger = l logger = l
def hexdump(octets): def hexdump(octets):
return ' '.join( return ' '.join(
[ '%s%.2X' % (n%16 == 0 and ('\n%.5d: ' % n) or '', x) ['%s%.2X' % (n % 16 == 0 and ('\n%.5d: ' % n) or '', x)
for n,x in zip(range(len(octets)), octs2ints(octets)) ] for n, x in zip(range(len(octets)), octs2ints(octets))]
) )
class Scope:
class Scope(object):
def __init__(self): def __init__(self):
self._list = [] self._list = []
@ -62,4 +126,5 @@ class Scope:
def pop(self): def pop(self):
return self._list.pop() return self._list.pop()
scope = Scope() scope = Scope()

View file

@ -1,3 +1,18 @@
class PyAsn1Error(Exception): pass #
class ValueConstraintError(PyAsn1Error): pass # This file is part of pyasn1 software.
class SubstrateUnderrunError(PyAsn1Error): pass #
# 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,402 @@
# 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 import sys
from pyasn1.type import constraint, tagmap from pyasn1.type import constraint, tagmap, tag
from pyasn1 import error 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): class Asn1ItemBase(Asn1Item):
# Set of tags for this ASN.1 type #: Set or return a :py:class:`~pyasn1.type.tag.TagSet` object representing
tagSet = () #: ASN.1 tag(s) associated with |ASN.1| type.
tagSet = tag.TagSet()
# A list of constraint.Constraint instances for checking values
#: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
#: object imposing constraints on initialization values.
subtypeSpec = constraint.ConstraintsIntersection() subtypeSpec = constraint.ConstraintsIntersection()
# Used for ambiguous ASN.1 types identification # Disambiguation ASN.1 types identification
typeId = None typeId = None
def __init__(self, tagSet=None, subtypeSpec=None): def __init__(self, tagSet=None, subtypeSpec=None):
if tagSet is None: if tagSet is None:
self._tagSet = self.tagSet self._tagSet = self.__class__.tagSet
else: else:
self._tagSet = tagSet self._tagSet = tagSet
if subtypeSpec is None: if subtypeSpec is None:
self._subtypeSpec = self.subtypeSpec self._subtypeSpec = self.__class__.subtypeSpec
else: else:
self._subtypeSpec = subtypeSpec self._subtypeSpec = subtypeSpec
def _verifySubtypeSpec(self, value, idx=None): @property
try: def effectiveTagSet(self):
self._subtypeSpec(value, idx) """For |ASN.1| type is equivalent to *tagSet*
except error.PyAsn1Error: """
c, i, t = sys.exc_info() return self._tagSet # used by untagged types
raise c('%s at %s' % (i, self.__class__.__name__))
@property
def getSubtypeSpec(self): return self._subtypeSpec def tagMap(self):
"""Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects within callee object.
def getTagSet(self): return self._tagSet """
def getEffectiveTagSet(self): return self._tagSet # used by untagged types try:
def getTagMap(self): return tagmap.TagMap({self._tagSet: self}) return self._tagMap
def isSameTypeWith(self, other): except AttributeError:
return self is other or \ self._tagMap = tagmap.TagMap({self._tagSet: self})
self._tagSet == other.getTagSet() and \ return self._tagMap
self._subtypeSpec == other.getSubtypeSpec()
def isSuperTypeOf(self, other): def isSameTypeWith(self, other, matchTags=True, matchConstraints=True):
"""Returns true if argument is a ASN1 subtype of ourselves""" """Examine |ASN.1| type for equality with other ASN.1 type.
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 |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.
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
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 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): def __getattr__(self, attr):
raise error.PyAsn1Error('No value for %s()' % attr) if attr in self.skipMethods:
def __getitem__(self, i): raise AttributeError('attribute %s not present' % attr)
raise error.PyAsn1Error('No value') raise error.PyAsn1Error('No value for "%s"' % attr)
noValue = __NoValue() def __repr__(self):
return '%s()' % self.__class__.__name__
noValue = NoValue()
# Base class for "simple" ASN.1 objects. These are immutable. # Base class for "simple" ASN.1 objects. These are immutable.
class AbstractSimpleAsn1Item(Asn1ItemBase): class AbstractSimpleAsn1Item(Asn1ItemBase):
#: Default payload value
defaultValue = noValue defaultValue = noValue
def __init__(self, value=None, tagSet=None, subtypeSpec=None):
def __init__(self, value=noValue, tagSet=None, subtypeSpec=None):
Asn1ItemBase.__init__(self, tagSet, subtypeSpec) Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
if value is None or value is noValue: if value is None or value is noValue:
value = self.defaultValue value = self.defaultValue
if value is None or value is noValue:
self.__hashedValue = value = noValue
else: else:
value = self.prettyIn(value) value = self.prettyIn(value)
self._verifySubtypeSpec(value) try:
self.__hashedValue = hash(value) self._subtypeSpec(value)
except error.PyAsn1Error:
exType, exValue, exTb = sys.exc_info()
raise exType('%s at %s' % (exValue, self.__class__.__name__))
self.__hashedValue = None
self._value = value self._value = value
self._len = None self._len = None
def __repr__(self): def __repr__(self):
if self._value is noValue: representation = []
return self.__class__.__name__ + '()' if self._value is not self.defaultValue:
else: representation.append(self.prettyOut(self._value))
return self.__class__.__name__ + '(%s)' % (self.prettyOut(self._value),) if self._tagSet is not self.__class__.tagSet:
def __str__(self): return str(self._value) representation.append('tagSet=%r' % (self._tagSet,))
if self._subtypeSpec is not self.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): def __eq__(self, other):
return self is other and True or self._value == 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 __ne__(self, other):
def __le__(self, other): return self._value <= other return self._value != other
def __gt__(self, other): return self._value > other
def __ge__(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: if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self._value) def __nonzero__(self):
return self._value and True or False
else: else:
def __bool__(self): return bool(self._value) def __bool__(self):
def __hash__(self): return self.__hashedValue return self._value and True or False
def clone(self, value=None, tagSet=None, subtypeSpec=None): def __hash__(self):
if value is None and tagSet is None and subtypeSpec is None: if self.__hashedValue is None:
self.__hashedValue = hash(self._value)
return self.__hashedValue
@property
def isValue(self):
"""Indicate if |ASN.1| object represents ASN.1 type or ASN.1 value.
The PyASN1 type objects can only participate in types comparison
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.
Returns
-------
: :class:`bool`
:class:`True` if object represents ASN.1 value and type,
:class:`False` if object represents just ASN.1 type.
"""
return self._value is not noValue
def clone(self, value=noValue, tagSet=None, subtypeSpec=None):
"""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
"""
isModified = False
if value is None or value is noValue:
value = self._value
else:
isModified = True
if tagSet is None or tagSet is noValue:
tagSet = self._tagSet
else:
isModified = True
if subtypeSpec is None or subtypeSpec is noValue:
subtypeSpec = self._subtypeSpec
else:
isModified = True
if isModified:
return self.__class__(value, tagSet, subtypeSpec)
else:
return self return self
if value is None:
value = self._value
if tagSet is None:
tagSet = self._tagSet
if subtypeSpec is None:
subtypeSpec = self._subtypeSpec
return self.__class__(value, tagSet, subtypeSpec)
def subtype(self, value=None, implicitTag=None, explicitTag=None, def subtype(self, value=noValue, implicitTag=None, explicitTag=None,
subtypeSpec=None): subtypeSpec=None):
if value is None: """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
"""
isModified = False
if value is None or value is noValue:
value = self._value value = self._value
if implicitTag is not None: else:
isModified = True
if implicitTag is not None and implicitTag is not noValue:
tagSet = self._tagSet.tagImplicitly(implicitTag) tagSet = self._tagSet.tagImplicitly(implicitTag)
elif explicitTag is not None: isModified = True
elif explicitTag is not None and explicitTag is not noValue:
tagSet = self._tagSet.tagExplicitly(explicitTag) tagSet = self._tagSet.tagExplicitly(explicitTag)
isModified = True
else: else:
tagSet = self._tagSet tagSet = self._tagSet
if subtypeSpec is None: if subtypeSpec is None or subtypeSpec is noValue:
subtypeSpec = self._subtypeSpec subtypeSpec = self._subtypeSpec
else: else:
subtypeSpec = subtypeSpec + self._subtypeSpec subtypeSpec = self._subtypeSpec + subtypeSpec
return self.__class__(value, tagSet, subtypeSpec) isModified = True
def prettyIn(self, value): return value if isModified:
def prettyOut(self, value): return str(value) return self.__class__(value, tagSet, subtypeSpec)
else:
return self
def prettyIn(self, value):
return value
def prettyOut(self, value):
return str(value)
def prettyPrint(self, scope=0): def prettyPrint(self, scope=0):
if self._value is noValue: """Provide human-friendly printable object representation.
return '<no value>'
else: Returns
-------
: :class:`str`
human-friendly type and/or value representation.
"""
if self.isValue:
return self.prettyOut(self._value) return self.prettyOut(self._value)
else:
return '<no value>'
# XXX Compatibility stub # 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__)
# backward compatibility
def hasValue(self):
return self.isValue
# #
# Constructed types: # Constructed types:
# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice # * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice
@ -148,9 +416,29 @@ class AbstractSimpleAsn1Item(Asn1ItemBase):
# of types for Sequence/Set/Choice. # 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): class AbstractConstructedAsn1Item(Asn1ItemBase):
componentType = None
sizeSpec = constraint.ConstraintsIntersection() #: If `True`, requires exact component type matching,
#: otherwise subtype relation is only enforced
strictConstraints = False
def __init__(self, componentType=None, tagSet=None, def __init__(self, componentType=None, tagSet=None,
subtypeSpec=None, sizeSpec=None): subtypeSpec=None, sizeSpec=None):
Asn1ItemBase.__init__(self, tagSet, subtypeSpec) Asn1ItemBase.__init__(self, tagSet, subtypeSpec)
@ -163,87 +451,167 @@ class AbstractConstructedAsn1Item(Asn1ItemBase):
else: else:
self._sizeSpec = sizeSpec self._sizeSpec = sizeSpec
self._componentValues = [] self._componentValues = []
self._componentValuesSet = 0
def __repr__(self): def __repr__(self):
r = self.__class__.__name__ + '()' representation = []
for idx in range(len(self._componentValues)): if self._componentType is not self.componentType:
if self._componentValues[idx] is None: representation.append('componentType=%r' % (self._componentType,))
continue if self._tagSet is not self.__class__.tagSet:
r = r + '.setComponentByPosition(%s, %r)' % ( representation.append('tagSet=%r' % (self._tagSet,))
idx, self._componentValues[idx] if self._subtypeSpec is not self.subtypeSpec:
) representation.append('subtypeSpec=%r' % (self._subtypeSpec,))
return r 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): def __eq__(self, other):
return self is other and True or self._componentValues == 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 __ne__(self, other):
def __le__(self, other): return self._componentValues <= other return self._componentValues != other
def __gt__(self, other): return self._componentValues > other
def __ge__(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: if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self._componentValues) def __nonzero__(self):
return self._componentValues and True or False
else: else:
def __bool__(self): return bool(self._componentValues) def __bool__(self):
return self._componentValues and True or False
def getComponentTagMap(self): def _cloneComponentValues(self, myClone, cloneValueFlag):
raise error.PyAsn1Error('Method not implemented') pass
def _cloneComponentValues(self, myClone, cloneValueFlag): pass def clone(self, tagSet=None, subtypeSpec=None, sizeSpec=None, cloneValueFlag=None):
"""Create a copy of a |ASN.1| type or object.
def clone(self, tagSet=None, subtypeSpec=None, sizeSpec=None, Any parameters to the *clone()* method will replace corresponding
cloneValueFlag=None): 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
"""
if tagSet is None: if tagSet is None:
tagSet = self._tagSet tagSet = self._tagSet
if subtypeSpec is None: if subtypeSpec is None:
subtypeSpec = self._subtypeSpec subtypeSpec = self._subtypeSpec
if sizeSpec is None: if sizeSpec is None:
sizeSpec = self._sizeSpec sizeSpec = self._sizeSpec
r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec) clone = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
if cloneValueFlag: if cloneValueFlag:
self._cloneComponentValues(r, cloneValueFlag) self._cloneComponentValues(clone, cloneValueFlag)
return r return clone
def subtype(self, implicitTag=None, explicitTag=None, subtypeSpec=None, def subtype(self, implicitTag=None, explicitTag=None, subtypeSpec=None,
sizeSpec=None, cloneValueFlag=None): sizeSpec=None, cloneValueFlag=None):
if implicitTag is not None: """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
"""
if implicitTag is not None and implicitTag is not noValue:
tagSet = self._tagSet.tagImplicitly(implicitTag) tagSet = self._tagSet.tagImplicitly(implicitTag)
elif explicitTag is not None: elif explicitTag is not None and explicitTag is not noValue:
tagSet = self._tagSet.tagExplicitly(explicitTag) tagSet = self._tagSet.tagExplicitly(explicitTag)
else: else:
tagSet = self._tagSet tagSet = self._tagSet
if subtypeSpec is None: if subtypeSpec is None or subtypeSpec is noValue:
subtypeSpec = self._subtypeSpec subtypeSpec = self._subtypeSpec
else: else:
subtypeSpec = subtypeSpec + self._subtypeSpec subtypeSpec = self._subtypeSpec + subtypeSpec
if sizeSpec is None: if sizeSpec is None or sizeSpec is noValue:
sizeSpec = self._sizeSpec sizeSpec = self._sizeSpec
else: else:
sizeSpec = sizeSpec + self._sizeSpec sizeSpec += self._sizeSpec
r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec) clone = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec)
if cloneValueFlag: if cloneValueFlag:
self._cloneComponentValues(r, cloneValueFlag) self._cloneComponentValues(clone, cloneValueFlag)
return r return clone
def _verifyComponent(self, idx, value): pass def verifySizeSpec(self):
self._sizeSpec(self)
def verifySizeSpec(self): self._sizeSpec(self)
def getComponentByPosition(self, idx): def getComponentByPosition(self, idx):
raise error.PyAsn1Error('Method not implemented') raise error.PyAsn1Error('Method not implemented')
def setComponentByPosition(self, idx, value, verifyConstraints=True): def setComponentByPosition(self, idx, value, verifyConstraints=True):
raise error.PyAsn1Error('Method not implemented') 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 getComponentType(self):
def __setitem__(self, idx, value): self.setComponentByPosition(idx, value) return self._componentType
# backward compatibility -- no-op
def setDefaultComponents(self):
pass
@property
def componentTagMap(self):
raise error.PyAsn1Error('Method not implemented')
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): def clear(self):
self._componentValues = [] self._componentValues = []
self._componentValuesSet = 0
def setDefaultComponents(self): pass # backward compatibility
def getComponentTagMap(self):
return self.componentTagMap

View file

@ -1,61 +1,378 @@
# 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.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): __all__ = ['NumericString', 'PrintableString', 'TeletexString', 'T61String', 'VideotexString',
tagSet = univ.OctetString.tagSet.tagImplicitly( '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 \'%s\' codec' % (self._value, self._encoding)
)
def __unicode__(self):
return unicode(self._value)
def prettyIn(self, value):
if isinstance(value, unicode):
return value
elif isinstance(value, str):
try:
return value.decode(self._encoding)
except (LookupError, UnicodeDecodeError):
raise error.PyAsn1Error(
'Can\'t decode string \'%s\' with \'%s\' codec' % (value, self._encoding)
)
elif isinstance(value, (tuple, list)):
try:
return self.prettyIn(''.join([chr(x) for x in value]))
except ValueError:
raise error.PyAsn1Error(
'Bad %s initializer \'%s\'' % (self.__class__.__name__, value)
)
else:
try:
return unicode(value)
except UnicodeDecodeError:
raise error.PyAsn1Error(
'Can\'t turn object \'%s\' into unicode' % (value,)
)
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 \'%s\' codec' % (self._value, self._encoding)
)
def prettyIn(self, value):
if isinstance(value, str):
return value
elif isinstance(value, bytes):
try:
return value.decode(self._encoding)
except UnicodeDecodeError:
raise error.PyAsn1Error(
'Can\'t decode string \'%s\' with \'%s\' codec' % (value, self._encoding)
)
elif isinstance(value, (tuple, list)):
return self.prettyIn(bytes(value))
else:
try:
return str(value)
except (UnicodeDecodeError, ValueError):
raise error.PyAsn1Error(
'Can\'t turn object \'%s\' into unicode' % (value,)
)
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, tagSet=None, subtypeSpec=None,
encoding=None, binValue=noValue, hexValue=noValue):
"""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, tagSet, subtypeSpec, encoding, binValue, hexValue)
def subtype(self, value=noValue, implicitTag=None, explicitTag=None,
subtypeSpec=None, encoding=None, binValue=noValue, hexValue=noValue):
"""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, implicitTag, explicitTag, subtypeSpec, encoding, binValue, hexValue)
class NumericString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (class attribute) or return (class or instance attribute) 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) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18)
) )
encoding = 'us-ascii'
class PrintableString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class PrintableString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (class attribute) or return (class or instance attribute) 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) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19)
) )
encoding = 'us-ascii'
class TeletexString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class TeletexString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (class attribute) or return (class or instance attribute) 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) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20)
) )
encoding = 'iso-8859-1'
class VideotexString(univ.OctetString):
tagSet = univ.OctetString.tagSet.tagImplicitly( class T61String(TeletexString):
__doc__ = TeletexString.__doc__
class VideotexString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (class attribute) or return (class or instance attribute) 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) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21)
) )
encoding = 'iso-8859-1'
class IA5String(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class IA5String(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (class attribute) or return (class or instance attribute) 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) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22)
) )
encoding = 'us-ascii'
class GraphicString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class GraphicString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (class attribute) or return (class or instance attribute) 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) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25)
) )
encoding = 'iso-8859-1'
class VisibleString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class VisibleString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (class attribute) or return (class or instance attribute) 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) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26)
) )
encoding = 'us-ascii'
class GeneralString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class ISO646String(VisibleString):
__doc__ = VisibleString.__doc__
class GeneralString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (class attribute) or return (class or instance attribute) 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) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27)
) )
encoding = 'iso-8859-1'
class UniversalString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class UniversalString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (class attribute) or return (class or instance attribute) 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) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28)
) )
encoding = "utf-32-be" encoding = "utf-32-be"
class BMPString(univ.OctetString): # Optimization for faster codec lookup
tagSet = univ.OctetString.tagSet.tagImplicitly( typeId = AbstractCharacterString.getTypeId()
class BMPString(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (class attribute) or return (class or instance attribute) 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) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30)
) )
encoding = "utf-16-be" encoding = "utf-16-be"
# Optimization for faster codec lookup
typeId = AbstractCharacterString.getTypeId()
class UTF8String(AbstractCharacterString):
__doc__ = AbstractCharacterString.__doc__
#: Set (class attribute) or return (class or instance attribute) 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,149 @@
# #
# ASN.1 subtype constraints classes. # This file is part of pyasn1 software.
# #
# Constraints are relatively rare, but every ASN1 object # Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
# is doing checks all the time for whether they have any # License: http://pyasn1.sf.net/license.html
# constraints and whether they are applicable to the object.
# #
# What we're going to do is define objects/functions that # Original concept and code by Mike C. Fletcher.
# can be called unconditionally if they are present, and that
# are simply not present if there are no constraints.
#
# Original concept and code by Mike C. Fletcher.
# #
import sys import sys
from pyasn1.type import error 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 """Abstract base-class for constraint objects
Constraints should be stored in a simple sequence in the 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): def __init__(self, *values):
self._valueMap = {} self._valueMap = set()
self._setValues(values) self._setValues(values)
self.__hashedValues = None self.__hashedValues = None
def __call__(self, value, idx=None): def __call__(self, value, idx=None):
if not self._values:
return
try: try:
self._testValue(value, idx) self._testValue(value, idx)
except error.ValueConstraintError: except error.ValueConstraintError:
raise 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): def __repr__(self):
return '%s(%s)' % ( return '%s(%s)' % (
self.__class__.__name__, self.__class__.__name__,
', '.join([repr(x) for x in self._values]) ', '.join([repr(x) for x in self._values])
) )
def __eq__(self, other): def __eq__(self, other):
return self is other and True or self._values == 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 __ne__(self, other):
def __le__(self, other): return self._values <= other return self._values != other
def __gt__(self, other): return self._values > other
def __ge__(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: if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self._values) def __nonzero__(self):
return self._values and True or False
else: else:
def __bool__(self): return bool(self._values) def __bool__(self):
return self._values and True or False
def __hash__(self): def __hash__(self):
if self.__hashedValues is None: if self.__hashedValues is None:
self.__hashedValues = hash((self.__class__.__name__, self._values)) self.__hashedValues = hash((self.__class__.__name__, self._values))
return self.__hashedValues return self.__hashedValues
def _setValues(self, values): self._values = values # descriptor protocol
def __get__(self, instance, owner):
if instance is None:
return self
# This is a bit of hack: look up instance attribute first,
# then try class attribute if instance attribute with that
# name is not available.
# The rationale is to have `.subtypeSpec`/`.sizeSpec` readable-writeable
# as a class attribute and read-only as instance attribute.
try:
return instance._subtypeSpec
except AttributeError:
try:
return instance._sizeSpec
except AttributeError:
return self
def __set__(self, instance, value):
raise AttributeError('attribute is read-only')
def _setValues(self, values):
self._values = values
def _testValue(self, value, idx): def _testValue(self, value, idx):
raise error.ValueConstraintError(value) raise error.ValueConstraintError(value)
# Constraints derivation logic # Constraints derivation logic
def getValueMap(self): return self._valueMap def getValueMap(self):
return self._valueMap
def isSuperTypeOf(self, otherConstraint): def isSuperTypeOf(self, otherConstraint):
return self in otherConstraint.getValueMap() or \ return (otherConstraint is self or
otherConstraint is self or otherConstraint == self not self._values or
otherConstraint == self or
self in otherConstraint.getValueMap())
def isSubTypeOf(self, otherConstraint): def isSubTypeOf(self, otherConstraint):
return otherConstraint in self._valueMap or \ return (otherConstraint is self or
otherConstraint is self or otherConstraint == self not self or
otherConstraint == self or
otherConstraint in self._valueMap)
class SingleValueConstraint(AbstractConstraint): class SingleValueConstraint(AbstractConstraint):
"""Value must be part of defined values constraint""" """Value must be part of defined values constraint"""
def _setValues(self, values):
self._values = values
self._set = set(values)
def _testValue(self, value, idx): def _testValue(self, value, idx):
# XXX index vals for performance? if value not in self._set:
if value not in self._values:
raise error.ValueConstraintError(value) raise error.ValueConstraintError(value)
class ContainedSubtypeConstraint(AbstractConstraint): class ContainedSubtypeConstraint(AbstractConstraint):
"""Value must satisfy all of defined set of constraints""" """Value must satisfy all of defined set of constraints"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
for c in self._values: for c in self._values:
c(value, idx) c(value, idx)
class ValueRangeConstraint(AbstractConstraint): class ValueRangeConstraint(AbstractConstraint):
"""Value must be within start and stop values (inclusive)""" """Value must be within start and stop values (inclusive)"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
if value < self.start or value > self.stop: if value < self.start or value > self.stop:
raise error.ValueConstraintError(value) raise error.ValueConstraintError(value)
@ -89,7 +152,7 @@ class ValueRangeConstraint(AbstractConstraint):
if len(values) != 2: if len(values) != 2:
raise error.PyAsn1Error( raise error.PyAsn1Error(
'%s: bad constraint values' % (self.__class__.__name__,) '%s: bad constraint values' % (self.__class__.__name__,)
) )
self.start, self.stop = values self.start, self.stop = values
if self.start > self.stop: if self.start > self.stop:
raise error.PyAsn1Error( raise error.PyAsn1Error(
@ -99,28 +162,31 @@ class ValueRangeConstraint(AbstractConstraint):
) )
) )
AbstractConstraint._setValues(self, values) AbstractConstraint._setValues(self, values)
class ValueSizeConstraint(ValueRangeConstraint): class ValueSizeConstraint(ValueRangeConstraint):
"""len(value) must be within start and stop values (inclusive)""" """len(value) must be within start and stop values (inclusive)"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
l = len(value) valueSize = len(value)
if l < self.start or l > self.stop: if valueSize < self.start or valueSize > self.stop:
raise error.ValueConstraintError(value) raise error.ValueConstraintError(value)
class PermittedAlphabetConstraint(SingleValueConstraint): class PermittedAlphabetConstraint(SingleValueConstraint):
def _setValues(self, values): def _setValues(self, values):
self._values = () self._values = values
for v in values: self._set = set(values)
self._values = self._values + tuple(v)
def _testValue(self, value, idx): def _testValue(self, value, idx):
for v in value: if not self._set.issuperset(value):
if v not in self._values: raise error.ValueConstraintError(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): class InnerTypeConstraint(AbstractConstraint):
"""Value must satisfy type and presense constraints""" """Value must satisfy type and presense constraints"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
if self.__singleTypeConstraint: if self.__singleTypeConstraint:
self.__singleTypeConstraint(value) self.__singleTypeConstraint(value)
@ -128,7 +194,7 @@ class InnerTypeConstraint(AbstractConstraint):
if idx not in self.__multipleTypeConstraint: if idx not in self.__multipleTypeConstraint:
raise error.ValueConstraintError(value) raise error.ValueConstraintError(value)
constraint, status = self.__multipleTypeConstraint[idx] 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) raise error.ValueConstraintError(value)
constraint(value) constraint(value)
@ -142,10 +208,12 @@ class InnerTypeConstraint(AbstractConstraint):
self.__singleTypeConstraint = v self.__singleTypeConstraint = v
AbstractConstraint._setValues(self, values) AbstractConstraint._setValues(self, values)
# Boolean ops on constraints
# Boolean ops on constraints
class ConstraintsExclusion(AbstractConstraint): class ConstraintsExclusion(AbstractConstraint):
"""Value must not fit the single constraint""" """Value must not fit the single constraint"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
try: try:
self._values[0](value, idx) self._values[0](value, idx)
@ -159,42 +227,57 @@ class ConstraintsExclusion(AbstractConstraint):
raise error.PyAsn1Error('Single constraint expected') raise error.PyAsn1Error('Single constraint expected')
AbstractConstraint._setValues(self, values) AbstractConstraint._setValues(self, values)
class AbstractConstraintSet(AbstractConstraint): class AbstractConstraintSet(AbstractConstraint):
"""Value must not satisfy the single constraint""" """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 __getitem__(self, idx):
def __radd__(self, value): return self.__class__(self, value) 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 # Constraints inclusion in sets
def _setValues(self, values): def _setValues(self, values):
self._values = values self._values = values
for v in values: for constraint in values:
self._valueMap[v] = 1 if constraint:
self._valueMap.update(v.getValueMap()) self._valueMap.add(constraint)
self._valueMap.update(constraint.getValueMap())
class ConstraintsIntersection(AbstractConstraintSet): class ConstraintsIntersection(AbstractConstraintSet):
"""Value must satisfy all constraints""" """Value must satisfy all constraints"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
for v in self._values: for constraint in self._values:
v(value, idx) constraint(value, idx)
class ConstraintsUnion(AbstractConstraintSet): class ConstraintsUnion(AbstractConstraintSet):
"""Value must satisfy at least one constraint""" """Value must satisfy at least one constraint"""
def _testValue(self, value, idx): def _testValue(self, value, idx):
for v in self._values: for constraint in self._values:
try: try:
v(value, idx) constraint(value, idx)
except error.ValueConstraintError: except error.ValueConstraintError:
pass pass
else: else:
return return
raise error.ValueConstraintError( raise error.ValueConstraintError(
'all of %s failed for \"%s\"' % (self._values, value) 'all of %s failed for \"%s\"' % (self._values, value)
) )
# XXX # XXX
# add tests for type check # 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 from pyasn1.error import PyAsn1Error
class ValueConstraintError(PyAsn1Error): pass
class ValueConstraintError(PyAsn1Error):
pass

View file

@ -1,132 +1,475 @@
# 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 import sys
from pyasn1.type import tagmap from pyasn1.type import tagmap
from pyasn1 import error from pyasn1 import error
class NamedType: __all__ = ['NamedType', 'OptionalNamedType', 'DefaultedNamedType', 'NamedTypes']
isOptional = 0
isDefaulted = 0
def __init__(self, name, t): class NamedType(object):
self.__name = name; self.__type = t """Create named field object for a constructed ASN.1 type.
def __repr__(self): return '%s(%s, %s)' % (
self.__class__.__name__, self.__name, self.__type The |NamedType| object represents a single name and ASN.1 type of a constructed ASN.1 type.
)
def getType(self): return self.__type |NamedType| objects are immutable and duck-type Python :class:`tuple` objects
def getName(self): return self.__name 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):
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): def __getitem__(self, idx):
if idx == 0: return self.__name return self.__nameAndType[idx]
if idx == 1: return self.__type
raise IndexError() def __iter__(self):
return iter(self.__nameAndType)
@property
def name(self):
return self.__name
@property
def asn1Object(self):
return self.__type
# Backward compatibility
def getName(self):
return self.name
def getType(self):
return self.asn1Object
class OptionalNamedType(NamedType): class OptionalNamedType(NamedType):
isOptional = 1 __doc__ = NamedType.__doc__
isOptional = True
class DefaultedNamedType(NamedType): class DefaultedNamedType(NamedType):
isDefaulted = 1 __doc__ = NamedType.__doc__
class NamedTypes: 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): def __init__(self, *namedTypes):
self.__namedTypes = namedTypes self.__namedTypes = namedTypes
self.__namedTypesLen = len(self.__namedTypes) self.__namedTypesLen = len(self.__namedTypes)
self.__minTagSet = None self.__minTagSet = None
self.__tagToPosIdx = {}; self.__nameToPosIdx = {} self.__tagToPosMapImpl = None
self.__tagMap = { False: None, True: None } self.__nameToPosMapImpl = None
self.__ambigiousTypes = {} self.__ambigiousTypesImpl = None
self.__tagMap = {}
self.__hasOptionalOrDefault = None
self.__requiredComponents = None
def __repr__(self): def __repr__(self):
r = '%s(' % self.__class__.__name__ return '%s(%s)' % (
for n in self.__namedTypes: self.__class__.__name__, ', '.join([repr(x) for x in self.__namedTypes])
r = r + '%r, ' % (n,) )
return r + ')'
def __eq__(self, other):
def __getitem__(self, idx): return self.__namedTypes[idx] 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: if sys.version_info[0] <= 2:
def __nonzero__(self): return bool(self.__namedTypesLen) def __nonzero__(self):
return self.__namedTypesLen > 0
else: else:
def __bool__(self): return bool(self.__namedTypesLen) def __bool__(self):
def __len__(self): return self.__namedTypesLen return self.__namedTypesLen > 0
def getTypeByPosition(self, idx):
if idx < 0 or idx >= self.__namedTypesLen:
raise error.PyAsn1Error('Type position out of range')
else:
return self.__namedTypes[idx].getType()
def getPositionByType(self, tagSet): def __len__(self):
if not self.__tagToPosIdx: return self.__namedTypesLen
idx = self.__namedTypesLen
while idx > 0: # Python dict protocol
idx = idx - 1
tagMap = self.__namedTypes[idx].getType().getTagMap() def values(self):
for t in tagMap.getPosMap(): return (namedType.asn1Object for namedType in self.__namedTypes)
if t in self.__tagToPosIdx:
raise error.PyAsn1Error('Duplicate type %s' % (t,)) def keys(self):
self.__tagToPosIdx[t] = idx return (namedType.name for namedType in self.__namedTypes)
def items(self):
return ((namedType.name, namedType.asn1Object) for namedType in self.__namedTypes)
def clone(self):
return self.__class__(*self.__namedTypes)
@property
def __tagToPosMap(self):
if self.__tagToPosMapImpl is None:
self.__tagToPosMapImpl = {}
for idx, namedType in enumerate(self.__namedTypes):
tagMap = namedType.asn1Object.tagMap
if not tagMap:
continue
for _tagSet in tagMap.presentTypes:
if _tagSet in self.__tagToPosMapImpl:
raise error.PyAsn1Error('Duplicate type %s in %s' % (_tagSet, namedType))
self.__tagToPosMapImpl[_tagSet] = idx
return self.__tagToPosMapImpl
@property
def __nameToPosMap(self):
if self.__nameToPosMapImpl is None:
self.__nameToPosMapImpl = {}
for idx, namedType in enumerate(self.__namedTypes):
if namedType.name in self.__nameToPosMapImpl:
raise error.PyAsn1Error('Duplicate name %s in %s' % (namedType.name, namedType))
self.__nameToPosMapImpl[namedType.name] = idx
return self.__nameToPosMapImpl
@property
def __ambigiousTypes(self):
if self.__ambigiousTypesImpl is None:
self.__ambigiousTypesImpl = {}
ambigiousTypes = ()
for idx, namedType in reversed(tuple(enumerate(self.__namedTypes))):
if namedType.isOptional or namedType.isDefaulted:
ambigiousTypes = (namedType,) + ambigiousTypes
else:
ambigiousTypes = (namedType,)
self.__ambigiousTypesImpl[idx] = NamedTypes(*ambigiousTypes)
return self.__ambigiousTypesImpl
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: try:
return self.__tagToPosIdx[tagSet] return self.__namedTypes[idx].asn1Object
except KeyError:
raise error.PyAsn1Error('Type %s not found' % (tagSet,))
def getNameByPosition(self, idx):
try:
return self.__namedTypes[idx].getName()
except IndexError: except IndexError:
raise error.PyAsn1Error('Type position out of range') raise error.PyAsn1Error('Type position out of range')
def getPositionByName(self, name):
if not self.__nameToPosIdx: def getPositionByType(self, tagSet):
idx = self.__namedTypesLen """Return field position by its ASN.1 type.
while idx > 0:
idx = idx - 1 Parameters
n = self.__namedTypes[idx].getName() ----------
if n in self.__nameToPosIdx: tagSet: :class:`~pysnmp.type.tag.TagSet`
raise error.PyAsn1Error('Duplicate name %s' % (n,)) ASN.1 tag set distinguishing one ASN.1 type from others.
self.__nameToPosIdx[n] = idx
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: 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: except KeyError:
raise error.PyAsn1Error('Name %s not found' % (name,)) 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): 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: try:
return self.__ambigiousTypes[idx].getTagMap() return self.__ambigiousTypes[idx].getTagMap()
except KeyError: except KeyError:
raise error.PyAsn1Error('Type position out of range') raise error.PyAsn1Error('Type position out of range')
def getPositionNearType(self, tagSet, idx): 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: try:
return idx+self.__ambigiousTypes[idx].getPositionByType(tagSet) return idx + self.__ambigiousTypes[idx].getPositionByType(tagSet)
except KeyError: except KeyError:
raise error.PyAsn1Error('Type position out of range') raise error.PyAsn1Error('Type position out of range')
def genMinTagSet(self): @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*
"""
if self.__minTagSet is None: if self.__minTagSet is None:
for t in self.__namedTypes: for namedType in self.__namedTypes:
__type = t.getType() asn1Object = namedType.asn1Object
tagSet = getattr(__type,'getMinTagSet',__type.getTagSet)() try:
tagSet = asn1Object.getMinTagSet()
except AttributeError:
tagSet = asn1Object.tagSet
if self.__minTagSet is None or tagSet < self.__minTagSet: if self.__minTagSet is None or tagSet < self.__minTagSet:
self.__minTagSet = tagSet self.__minTagSet = tagSet
return self.__minTagSet return self.__minTagSet
def getTagMap(self, uniq=False): def getTagMap(self, unique=False):
if self.__tagMap[uniq] is None: """Create a *TagMap* object from tags and types recursively.
tagMap = tagmap.TagMap()
for nt in self.__namedTypes: Create a new :class:`~pyasn1.type.tagmap.TagMap` object by
tagMap = tagMap.clone( combining tags from *TagMap* objects of children types and
nt.getType(), nt.getType().getTagMap(), uniq associating them with their immediate child type.
)
self.__tagMap[uniq] = tagMap Example
return self.__tagMap[uniq] -------
.. code-block:: python
OuterType ::= CHOICE {
innerType INTEGER
}
Calling *.getTagMap()* on *OuterType* will yield a map like this:
.. code-block:: python
Integer.tagSet -> Choice
Parameters
----------
unique: :py:class:`bool`
If `True`, duplicate *TagSet* objects occurring while building
new *TagMap* would cause error.
Returns
-------
: :class:`~pyasn1.type.tagmap.TagMap`
New *TagMap* holding *TagSet* object gathered from childen types.
"""
if unique not in self.__tagMap:
presentTypes = {}
skipTypes = {}
defaultType = None
for namedType in self.__namedTypes:
tagMap = namedType.asn1Object.tagMap
for tagSet in tagMap:
if unique and tagSet in presentTypes:
raise error.PyAsn1Error('Non-unique tagSet %s' % (tagSet,))
presentTypes[tagSet] = namedType.asn1Object
skipTypes.update(tagMap.skipTypes)
if defaultType is None:
defaultType = tagMap.defaultType
elif tagMap.defaultType is not None:
raise error.PyAsn1Error('Duplicate default ASN.1 type at %s' % (self,))
self.__tagMap[unique] = tagmap.TagMap(presentTypes, skipTypes, defaultType)
return self.__tagMap[unique]
@property
def hasOptionalOrDefault(self):
if self.__hasOptionalOrDefault is None:
self.__hasOptionalOrDefault = bool([True for namedType in self.__namedTypes if namedType.isDefaulted or namedType.isOptional])
return self.__hasOptionalOrDefault
@property
def namedTypes(self):
return iter(self.__namedTypes)
@property
def requiredComponents(self):
if self.__requiredComponents is None:
self.__requiredComponents = frozenset(
[idx for idx, nt in enumerate(self.__namedTypes) if not nt.isOptional and not nt.isDefaulted]
)
return self.__requiredComponents

View file

@ -1,12 +1,21 @@
#
# 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 # ASN.1 named integers
#
from pyasn1 import error from pyasn1 import error
__all__ = [ 'NamedValues' ] __all__ = ['NamedValues']
class NamedValues:
class NamedValues(object):
def __init__(self, *namedValues): def __init__(self, *namedValues):
self.nameToValIdx = {}; self.valToNameIdx = {} self.nameToValIdx = {}
self.namedValues = () self.valToNameIdx = {}
self.namedValues = ()
automaticVal = 1 automaticVal = 1
for namedValue in namedValues: for namedValue in namedValues:
if isinstance(namedValue, tuple): if isinstance(namedValue, tuple):
@ -21,9 +30,35 @@ class NamedValues:
raise error.PyAsn1Error('Duplicate value %s=%s' % (name, val)) raise error.PyAsn1Error('Duplicate value %s=%s' % (name, val))
self.valToNameIdx[val] = name self.valToNameIdx[val] = name
self.namedValues = self.namedValues + ((name, val),) self.namedValues = self.namedValues + ((name, val),)
automaticVal = automaticVal + 1 automaticVal += 1
def __str__(self): return str(self.namedValues)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, ', '.join([repr(x) for x in self.namedValues]))
def __str__(self):
return str(self.namedValues)
def __eq__(self, other):
return tuple(self) == tuple(other)
def __ne__(self, other):
return tuple(self) != tuple(other)
def __lt__(self, other):
return tuple(self) < tuple(other)
def __le__(self, other):
return tuple(self) <= tuple(other)
def __gt__(self, other):
return tuple(self) > tuple(other)
def __ge__(self, other):
return tuple(self) >= tuple(other)
def __hash__(self):
return hash(tuple(self))
def getName(self, value): def getName(self, value):
if value in self.valToNameIdx: if value in self.valToNameIdx:
return self.valToNameIdx[value] return self.valToNameIdx[value]
@ -31,15 +66,28 @@ class NamedValues:
def getValue(self, name): def getValue(self, name):
if name in self.nameToValIdx: if name in self.nameToValIdx:
return self.nameToValIdx[name] return self.nameToValIdx[name]
def __getitem__(self, i): return self.namedValues[i] def getValues(self, *names):
def __len__(self): return len(self.namedValues) try:
return [self.nameToValIdx[name] for name in names]
except KeyError:
raise error.PyAsn1Error(
'Unknown bit identifier(s): %s' % (set(names).difference(self.nameToValIdx),)
)
def __getitem__(self, i):
return self.namedValues[i]
def __len__(self):
return len(self.namedValues)
def __add__(self, namedValues): def __add__(self, namedValues):
return self.__class__(*self.namedValues + namedValues) return self.__class__(*self.namedValues + namedValues)
def __radd__(self, namedValues): def __radd__(self, namedValues):
return self.__class__(*namedValues + tuple(self)) return self.__class__(*namedValues + tuple(self))
def clone(self, *namedValues): def clone(self, *namedValues):
return self.__class__(*tuple(self) + namedValues) return self.__class__(*tuple(self) + namedValues)

View file

@ -1,122 +1,342 @@
# ASN.1 types tags #
from operator import getitem # 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 import error from pyasn1 import error
__all__ = ['tagClassUniversal', 'tagClassApplication', 'tagClassContext',
'tagClassPrivate', 'tagFormatSimple', 'tagFormatConstructed',
'tagCategoryImplicit', 'tagCategoryExplicit', 'tagCategoryUntagged',
'Tag', 'TagSet']
#: Identifier for ASN.1 class UNIVERSAL
tagClassUniversal = 0x00 tagClassUniversal = 0x00
#: Identifier for ASN.1 class APPLICATION
tagClassApplication = 0x40 tagClassApplication = 0x40
#: Identifier for ASN.1 class context-specific
tagClassContext = 0x80 tagClassContext = 0x80
#: Identifier for ASN.1 class private
tagClassPrivate = 0xC0 tagClassPrivate = 0xC0
#: Identifier for "simple" ASN.1 structure (e.g. scalar)
tagFormatSimple = 0x00 tagFormatSimple = 0x00
#: Identifier for "constructed" ASN.1 structure (e.g. may have inner components)
tagFormatConstructed = 0x20 tagFormatConstructed = 0x20
tagCategoryImplicit = 0x01 tagCategoryImplicit = 0x01
tagCategoryExplicit = 0x02 tagCategoryExplicit = 0x02
tagCategoryUntagged = 0x04 tagCategoryUntagged = 0x04
class Tag:
class Tag(object):
"""Create ASN.1 tag
Represents ASN.1 tag that can be attached to a ASN.1 type to make
types distinguishable from each other.
*Tag* objects are immutable and duck-type Python :class:`tuple` objects
holding three integer components of a tag.
Parameters
----------
tagClass: :py:class:`int`
Tag *class* value
tagFormat: :py:class:`int`
Tag *format* value
tagId: :py:class:`int`
Tag ID value
"""
def __init__(self, tagClass, tagFormat, tagId): def __init__(self, tagClass, tagFormat, tagId):
if tagId < 0: if tagId < 0:
raise error.PyAsn1Error( raise error.PyAsn1Error('Negative tag ID (%s) not allowed' % tagId)
'Negative tag ID (%s) not allowed' % (tagId,) self.__tagClass = tagClass
) self.__tagFormat = tagFormat
self.__tag = (tagClass, tagFormat, tagId) self.__tagId = tagId
self.uniq = (tagClass, tagId) self.__tagClassId = tagClass, tagId
self.__hashedUniqTag = hash(self.uniq) self.__lazyHash = None
def __str__(self):
return '[%s:%s:%s]' % (self.__tagClass, self.__tagFormat, self.__tagId)
def __repr__(self): def __repr__(self):
return '%s(tagClass=%s, tagFormat=%s, tagId=%s)' % ( return '%s(tagClass=%s, tagFormat=%s, tagId=%s)' % (
(self.__class__.__name__,) + self.__tag (self.__class__.__name__, self.__tagClass, self.__tagFormat, self.__tagId)
) )
# These is really a hotspot -- expose public "uniq" attribute to save on
# function calls def __eq__(self, other):
def __eq__(self, other): return self.uniq == other.uniq return self.__tagClassId == other
def __ne__(self, other): return self.uniq != other.uniq
def __lt__(self, other): return self.uniq < other.uniq def __ne__(self, other):
def __le__(self, other): return self.uniq <= other.uniq return self.__tagClassId != other
def __gt__(self, other): return self.uniq > other.uniq
def __ge__(self, other): return self.uniq >= other.uniq def __lt__(self, other):
def __hash__(self): return self.__hashedUniqTag return self.__tagClassId < other
def __getitem__(self, idx): return self.__tag[idx]
def __le__(self, other):
return self.__tagClassId <= other
def __gt__(self, other):
return self.__tagClassId > other
def __ge__(self, other):
return self.__tagClassId >= other
def __hash__(self):
if self.__lazyHash is None:
self.__lazyHash = hash(self.__tagClassId)
return self.__lazyHash
def __getitem__(self, idx):
if idx == 0:
return self.__tagClass
elif idx == 1:
return self.__tagFormat
elif idx == 2:
return self.__tagId
else:
raise IndexError()
def __iter__(self):
yield self.__tagClass
yield self.__tagFormat
yield self.__tagId
def __and__(self, otherTag): def __and__(self, otherTag):
(tagClass, tagFormat, tagId) = otherTag return self.__class__(self.__tagClass & otherTag.tagClass,
return self.__class__( self.__tagFormat & otherTag.tagFormat,
self.__tag&tagClass, self.__tag&tagFormat, self.__tag&tagId self.__tagId & otherTag.tagId)
)
def __or__(self, otherTag): def __or__(self, otherTag):
(tagClass, tagFormat, tagId) = otherTag return self.__class__(self.__tagClass | otherTag.tagClass,
return self.__class__( self.__tagFormat | otherTag.tagFormat,
self.__tag[0]|tagClass, self.__tagId | otherTag.tagId)
self.__tag[1]|tagFormat,
self.__tag[2]|tagId @property
) def tagClass(self):
def asTuple(self): return self.__tag # __getitem__() is slow """ASN.1 tag class
class TagSet: Returns
-------
: :py:class:`int`
Tag class
"""
return self.__tagClass
@property
def tagFormat(self):
"""ASN.1 tag format
Returns
-------
: :py:class:`int`
Tag format
"""
return self.__tagFormat
@property
def tagId(self):
"""ASN.1 tag ID
Returns
-------
: :py:class:`int`
Tag ID
"""
return self.__tagId
class TagSet(object):
"""Create a collection of ASN.1 tags
Represents a combination of :class:`~pyasn1.type.tag.Tag` objects
that can be attached to a ASN.1 type to make types distinguishable
from each other.
*TagSet* objects are immutable and duck-type Python :class:`tuple` objects
holding arbitrary number of :class:`~pyasn1.type.tag.Tag` objects.
Parameters
----------
baseTag: :class:`~pyasn1.type.tag.Tag`
Base *Tag* object. This tag survives IMPLICIT tagging.
*superTags: :class:`~pyasn1.type.tag.Tag`
Additional *Tag* objects taking part in subtyping.
"""
def __init__(self, baseTag=(), *superTags): def __init__(self, baseTag=(), *superTags):
self.__baseTag = baseTag self.__baseTag = baseTag
self.__superTags = superTags self.__superTags = superTags
self.__hashedSuperTags = hash(superTags) self.__superTagsSignature = tuple(
_uniq = () [(superTag.tagClass, superTag.tagId) for superTag in superTags]
for t in superTags: )
_uniq = _uniq + t.uniq
self.uniq = _uniq
self.__lenOfSuperTags = len(superTags) self.__lenOfSuperTags = len(superTags)
self.__lazyHash = None
def __str__(self):
return self.__superTags and '+'.join([str(x) for x in self.__superTags]) or '[untagged]'
def __repr__(self): def __repr__(self):
return '%s(%s)' % ( return '%s(%s)' % (
self.__class__.__name__, self.__class__.__name__, '(), ' + ', '.join([repr(x) for x in self.__superTags])
', '.join([repr(x) for x in self.__superTags]) )
)
def __add__(self, superTag): def __add__(self, superTag):
return self.__class__( return self.__class__(self.__baseTag, *self.__superTags + (superTag,))
self.__baseTag, *self.__superTags + (superTag,)
)
def __radd__(self, superTag): def __radd__(self, superTag):
return self.__class__( return self.__class__(self.__baseTag, *(superTag,) + self.__superTags)
self.__baseTag, *(superTag,) + self.__superTags
) def __getitem__(self, i):
if i.__class__ is slice:
return self.__class__(self.__baseTag, *self.__superTags[i])
else:
return self.__superTags[i]
def __eq__(self, other):
return self.__superTagsSignature == other
def __ne__(self, other):
return self.__superTagsSignature != other
def __lt__(self, other):
return self.__superTagsSignature < other
def __le__(self, other):
return self.__superTagsSignature <= other
def __gt__(self, other):
return self.__superTagsSignature > other
def __ge__(self, other):
return self.__superTagsSignature >= other
def __hash__(self):
if self.__lazyHash is None:
self.__lazyHash = hash(self.__superTags)
return self.__lazyHash
def __len__(self):
return self.__lenOfSuperTags
# descriptor protocol
def __get__(self, instance, owner):
if instance is None:
return self
# This is a bit of hack: look up instance attribute first,
# then try class attribute if instance attribute with that
# name is not available.
# The rationale is to have `.tagSet` readable-writeable
# as a class attribute and read-only as instance attribute.
try:
return instance._tagSet
except AttributeError:
return self
def __set__(self, instance, value):
raise AttributeError('attribute is read-only')
@property
def baseTag(self):
"""Return base ASN.1 tag
Returns
-------
: :class:`~pyasn1.type.tag.Tag`
Base tag of this *TagSet*
"""
return self.__baseTag
@property
def superTags(self):
"""Return ASN.1 tags
Returns
-------
: :py:class:`tuple`
Tuple of :class:`~pyasn1.type.tag.Tag` objects that this *TagSet* contains
"""
return self.__superTags
def tagExplicitly(self, superTag): def tagExplicitly(self, superTag):
tagClass, tagFormat, tagId = superTag """Return explicitly tagged *TagSet*
if tagClass == tagClassUniversal:
raise error.PyAsn1Error( Create a new *TagSet* representing callee *TagSet* explicitly tagged
'Can\'t tag with UNIVERSAL-class tag' with passed tag(s). With explicit tagging mode, new tags are appended
) to existing tag(s).
if tagFormat != tagFormatConstructed:
superTag = Tag(tagClass, tagFormatConstructed, tagId) Parameters
----------
superTag: :class:`~pyasn1.type.tag.Tag`
*Tag* object to tag this *TagSet*
Returns
-------
: :class:`~pyasn1.type.tag.TagSet`
New *TagSet* object
"""
if superTag.tagClass == tagClassUniversal:
raise error.PyAsn1Error('Can\'t tag with UNIVERSAL class tag')
if superTag.tagFormat != tagFormatConstructed:
superTag = Tag(superTag.tagClass, tagFormatConstructed, superTag.tagId)
return self + superTag return self + superTag
def tagImplicitly(self, superTag): def tagImplicitly(self, superTag):
tagClass, tagFormat, tagId = superTag """Return implicitly tagged *TagSet*
Create a new *TagSet* representing callee *TagSet* implicitly tagged
with passed tag(s). With implicit tagging mode, new tag(s) replace the
last existing tag.
Parameters
----------
superTag: :class:`~pyasn1.type.tag.Tag`
*Tag* object to tag this *TagSet*
Returns
-------
: :class:`~pyasn1.type.tag.TagSet`
New *TagSet* object
"""
if self.__superTags: if self.__superTags:
superTag = Tag(tagClass, self.__superTags[-1][1], tagId) superTag = Tag(superTag.tagClass, self.__superTags[-1].tagFormat, superTag.tagId)
return self[:-1] + superTag return self[:-1] + superTag
def getBaseTag(self): return self.__baseTag
def __getitem__(self, idx):
if isinstance(idx, slice):
return self.__class__(
self.__baseTag, *getitem(self.__superTags, idx)
)
return self.__superTags[idx]
def __eq__(self, other): return self.uniq == other.uniq
def __ne__(self, other): return self.uniq != other.uniq
def __lt__(self, other): return self.uniq < other.uniq
def __le__(self, other): return self.uniq <= other.uniq
def __gt__(self, other): return self.uniq > other.uniq
def __ge__(self, other): return self.uniq >= other.uniq
def __hash__(self): return self.__hashedSuperTags
def __len__(self): return self.__lenOfSuperTags
def isSuperTagSetOf(self, tagSet): def isSuperTagSetOf(self, tagSet):
"""Test type relationship against given *TagSet*
The callee is considered to be a supertype of given *TagSet*
tag-wise if all tags in *TagSet* are present in the callee and
they are in the same order.
Parameters
----------
tagSet: :class:`~pyasn1.type.tag.TagSet`
*TagSet* object to evaluate against the callee
Returns
-------
: :py:class:`bool`
`True` if callee is a supertype of *tagSet*
"""
if len(tagSet) < self.__lenOfSuperTags: if len(tagSet) < self.__lenOfSuperTags:
return return False
idx = self.__lenOfSuperTags - 1 return self.__superTags == tagSet[:self.__lenOfSuperTags]
while idx >= 0:
if self.__superTags[idx] != tagSet[idx]: # Backward compatibility
return
idx = idx - 1 def getBaseTag(self):
return 1 return self.__baseTag
def initTagSet(tag): return TagSet(tag, tag) def initTagSet(tag):
return TagSet(tag, tag)

View file

@ -1,52 +1,102 @@
#
# 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 import error from pyasn1 import error
class TagMap: __all__ = ['TagMap']
def __init__(self, posMap={}, negMap={}, defType=None):
self.__posMap = posMap.copy()
self.__negMap = negMap.copy() class TagMap(object):
self.__defType = defType """Map *TagSet* objects to ASN.1 types
Create an object mapping *TagSet* object to ASN.1 type.
*TagMap* objects are immutable and duck-type read-only Python
:class:`dict` objects holding *TagSet* objects as keys and ASN.1
type objects as values.
Parameters
----------
presentTypes: :py:class:`dict`
Map of :class:`~pyasn1.type.tag.TagSet` to ASN.1 objects considered
as being unconditionally present in the *TagMap*.
skipTypes: :py:class:`dict`
A collection of :class:`~pyasn1.type.tag.TagSet` objects considered
as absent in the *TagMap* even when *defaultType* is present.
defaultType: ASN.1 type object
An ASN.1 type object callee *TagMap* returns for any *TagSet* key not present
in *presentTypes* (unless given key is present in *skipTypes*).
"""
def __init__(self, presentTypes=None, skipTypes=None, defaultType=None):
self.__presentTypes = presentTypes or {}
self.__skipTypes = skipTypes or {}
self.__defaultType = defaultType
def __contains__(self, tagSet): def __contains__(self, tagSet):
return tagSet in self.__posMap or \ return (tagSet in self.__presentTypes or
self.__defType is not None and tagSet not in self.__negMap self.__defaultType is not None and tagSet not in self.__skipTypes)
def __getitem__(self, tagSet): def __getitem__(self, tagSet):
if tagSet in self.__posMap: try:
return self.__posMap[tagSet] return self.__presentTypes[tagSet]
elif tagSet in self.__negMap: except KeyError:
raise error.PyAsn1Error('Key in negative map') if self.__defaultType is None:
elif self.__defType is not None: raise KeyError()
return self.__defType elif tagSet in self.__skipTypes:
else: raise error.PyAsn1Error('Key in negative map')
raise KeyError() else:
return self.__defaultType
def __iter__(self):
return iter(self.__presentTypes)
def __repr__(self): def __repr__(self):
s = '%r/%r' % (self.__posMap, self.__negMap) s = self.__class__.__name__ + '('
if self.__defType is not None: if self.__presentTypes:
s = s + '/%r' % (self.__defType,) s += 'presentTypes=%r, ' % (self.__presentTypes,)
if self.__skipTypes:
s += 'skipTypes=%r, ' % (self.__skipTypes,)
if self.__defaultType is not None:
s += 'defaultType=%r' % (self.__defaultType,)
return s + ')'
def __str__(self):
s = self.__class__.__name__ + ': '
if self.__presentTypes:
s += 'presentTypes: %s, ' % ', '.join([x.prettyPrintType() for x in self.__presentTypes.values()])
if self.__skipTypes:
s += 'skipTypes: %s, ' % ', '.join([x.prettyPrintType() for x in self.__skipTypes.values()])
if self.__defaultType is not None:
s += 'defaultType: %s, ' % self.__defaultType.prettyPrintType()
return s return s
def clone(self, parentType, tagMap, uniq=False): @property
if self.__defType is not None and tagMap.getDef() is not None: def presentTypes(self):
raise error.PyAsn1Error('Duplicate default value at %s' % (self,)) """Return *TagSet* to ASN.1 type map present in callee *TagMap*"""
if tagMap.getDef() is not None: return self.__presentTypes
defType = tagMap.getDef()
else:
defType = self.__defType
posMap = self.__posMap.copy()
for k in tagMap.getPosMap():
if uniq and k in posMap:
raise error.PyAsn1Error('Duplicate positive key %s' % (k,))
posMap[k] = parentType
negMap = self.__negMap.copy() @property
negMap.update(tagMap.getNegMap()) def skipTypes(self):
"""Return *TagSet* collection unconditionally absent in callee *TagMap*"""
return self.__class__( return self.__skipTypes
posMap, negMap, defType,
)
def getPosMap(self): return self.__posMap.copy() @property
def getNegMap(self): return self.__negMap.copy() def defaultType(self):
def getDef(self): return self.__defType """Return default ASN.1 type being returned for any missing *TagSet*"""
return self.__defaultType
# Backward compatibility
def getPosMap(self):
return self.presentTypes
def getNegMap(self):
return self.skipTypes
def getDef(self):
return self.defaultType

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,39 @@
# ASN.1 "useful" types #
from pyasn1.type import char, tag # 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, char, tag
__all__ = ['ObjectDescriptor', 'GeneralizedTime', 'UTCTime']
NoValue = univ.NoValue
noValue = univ.noValue
class ObjectDescriptor(char.GraphicString):
__doc__ = char.GraphicString.__doc__
#: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
tagSet = char.GraphicString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 7)
)
class GeneralizedTime(char.VisibleString): class GeneralizedTime(char.VisibleString):
__doc__ = char.GraphicString.__doc__
#: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
tagSet = char.VisibleString.tagSet.tagImplicitly( tagSet = char.VisibleString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24)
) )
class UTCTime(char.VisibleString): class UTCTime(char.VisibleString):
__doc__ = char.GraphicString.__doc__
#: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
tagSet = char.VisibleString.tagSet.tagImplicitly( tagSet = char.VisibleString.tagSet.tagImplicitly(
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23) tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23)
) )