diff options
author | alexv-smirnov <alex@ydb.tech> | 2023-12-01 12:02:50 +0300 |
---|---|---|
committer | alexv-smirnov <alex@ydb.tech> | 2023-12-01 13:28:10 +0300 |
commit | 0e578a4c44d4abd539d9838347b9ebafaca41dfb (patch) | |
tree | a0c1969c37f818c830ebeff9c077eacf30be6ef8 /contrib/python/pyasn1/py3 | |
parent | 84f2d3d4cc985e63217cff149bd2e6d67ae6fe22 (diff) | |
download | ydb-0e578a4c44d4abd539d9838347b9ebafaca41dfb.tar.gz |
Change "ya.make"
Diffstat (limited to 'contrib/python/pyasn1/py3')
76 files changed, 21774 insertions, 0 deletions
diff --git a/contrib/python/pyasn1/py3/.dist-info/METADATA b/contrib/python/pyasn1/py3/.dist-info/METADATA new file mode 100644 index 0000000000..530fe5bf7b --- /dev/null +++ b/contrib/python/pyasn1/py3/.dist-info/METADATA @@ -0,0 +1,230 @@ +Metadata-Version: 2.1 +Name: pyasn1 +Version: 0.5.0 +Summary: Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208) +Home-page: https://github.com/pyasn1/pyasn1 +Author: Ilya Etingof +Author-email: etingof@gmail.com +Maintainer: pyasn1 maintenance organization +Maintainer-email: Christian Heimes <christian@python.org> +License: BSD-2-Clause +Project-URL: Documentation, https://pyasn1.readthedocs.io +Project-URL: Source, https://github.com/pyasn1/pyasn1 +Project-URL: Issues, https://github.com/pyasn1/pyasn1/issues +Project-URL: Changelog, https://pyasn1.readthedocs.io/en/latest/changelog.html +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 :: System Administrators +Classifier: Intended Audience :: Telecommunications Industry +Classifier: License :: OSI Approved :: BSD License +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Communications +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7 +Description-Content-Type: text/markdown +License-File: LICENSE.rst + + +ASN.1 library for Python +------------------------ +[![PyPI](https://img.shields.io/pypi/v/pyasn1.svg?maxAge=2592000)](https://pypi.org/project/pyasn1) +[![Python Versions](https://img.shields.io/pypi/pyversions/pyasn1.svg)](https://pypi.org/project/pyasn1/) +[![Build status](https://github.com/pyasn1/pyasn1/actions/workflows/main.yml/badge.svg)](https://github.com/pyasn1/pyasn1/actions/workflows/main.yml) +[![Coverage Status](https://img.shields.io/codecov/c/github/pyasn1/pyasn1.svg)](https://codecov.io/github/pyasn1/pyasn1) +[![GitHub license](https://img.shields.io/badge/license-BSD-blue.svg)](https://raw.githubusercontent.com/pyasn1/pyasn1/master/LICENSE.txt) + +This is a free and open source implementation of ASN.1 types and codecs +as a Python package. It has been first written to support particular +protocol (SNMP) but then generalized to be suitable for a wide range +of protocols based on +[ASN.1 specification](https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-X.208-198811-W!!PDF-E&type=items). + +**NOTE:** The package is now maintained by *Christian Heimes* and +*Simon Pichugin* in project https://github.com/pyasn1/pyasn1. + +Features +-------- + +* Generic implementation of ASN.1 types (X.208) +* Standards compliant BER/CER/DER codecs +* Can operate on streams of serialized data +* Dumps/loads ASN.1 structures from Python types +* 100% Python, works with Python 2.7 and 3.6+ +* MT-safe +* Contributed ASN.1 compiler [Asn1ate](https://github.com/kimgr/asn1ate) + +Why using pyasn1 +---------------- + +ASN.1 solves the data serialisation problem. This solution was +designed long ago by the wise Ancients. Back then, they did not +have the luxury of wasting bits. That is why ASN.1 is designed +to serialise data structures of unbounded complexity into +something compact and efficient when it comes to processing +the data. + +That probably explains why many network protocols and file formats +still rely on the 30+ years old technology. Including a number of +high-profile Internet protocols and file formats. + +Quite a number of books cover the topic of ASN.1. +[Communication between heterogeneous systems](http://www.oss.com/asn1/dubuisson.html) +by Olivier Dubuisson is one of those high quality books freely +available on the Internet. + +The pyasn1 package is designed to help Python programmers tackling +network protocols and file formats at the comfort of their Python +prompt. The tool struggles to capture all aspects of a rather +complicated ASN.1 system and to represent it on the Python terms. + +How to use pyasn1 +----------------- + +With pyasn1 you can build Python objects from ASN.1 data structures. +For example, the following ASN.1 data structure: + +```bash +Record ::= SEQUENCE { + id INTEGER, + room [0] INTEGER OPTIONAL, + house [1] INTEGER DEFAULT 0 +} +``` + +Could be expressed in pyasn1 like this: + +```python +class Record(Sequence): + componentType = NamedTypes( + NamedType('id', Integer()), + OptionalNamedType( + 'room', Integer().subtype( + implicitTag=Tag(tagClassContext, tagFormatSimple, 0) + ) + ), + DefaultedNamedType( + 'house', Integer(0).subtype( + implicitTag=Tag(tagClassContext, tagFormatSimple, 1) + ) + ) + ) +``` + +It is in the spirit of ASN.1 to take abstract data description +and turn it into a programming language specific form. +Once you have your ASN.1 data structure expressed in Python, you +can use it along the lines of similar Python type (e.g. ASN.1 +`SET` is similar to Python `dict`, `SET OF` to `list`): + +```python +>>> record = Record() +>>> record['id'] = 123 +>>> record['room'] = 321 +>>> str(record) +Record: + id=123 + room=321 +>>> +``` + +Part of the power of ASN.1 comes from its serialisation features. You +can serialise your data structure and send it over the network. + +```python +>>> from pyasn1.codec.der.encoder import encode +>>> substrate = encode(record) +>>> hexdump(substrate) +00000: 30 07 02 01 7B 80 02 01 41 +``` + +Conversely, you can turn serialised ASN.1 content, as received from +network or read from a file, into a Python object which you can +introspect, modify, encode and send back. + +```python +>>> from pyasn1.codec.der.decoder import decode +>>> received_record, rest_of_substrate = decode(substrate, asn1Spec=Record()) +>>> +>>> for field in received_record: +>>> print('{} is {}'.format(field, received_record[field])) +id is 123 +room is 321 +house is 0 +>>> +>>> record == received_record +True +>>> received_record.update(room=123) +>>> substrate = encode(received_record) +>>> hexdump(substrate) +00000: 30 06 02 01 7B 80 01 7B +``` + +The pyasn1 classes struggle to emulate their Python prototypes (e.g. int, +list, dict etc.). But ASN.1 types exhibit more complicated behaviour. +To make life easier for a Pythonista, they can turn their pyasn1 +classes into Python built-ins: + +```python +>>> from pyasn1.codec.native.encoder import encode +>>> encode(record) +{'id': 123, 'room': 321, 'house': 0} +``` + +Or vice-versa -- you can initialize an ASN.1 structure from a tree of +Python objects: + +```python +>>> from pyasn1.codec.native.decoder import decode +>>> record = decode({'id': 123, 'room': 321, 'house': 0}, asn1Spec=Record()) +>>> str(record) +Record: + id=123 + room=321 +>>> +``` + +With ASN.1 design, serialisation codecs are decoupled from data objects, +so you could turn every single ASN.1 object into many different +serialised forms. As of this moment, pyasn1 supports BER, DER, CER and +Python built-ins codecs. The extremely compact PER encoding is expected +to be introduced in the upcoming pyasn1 release. + +More information on pyasn1 APIs can be found in the +[documentation](https://pyasn1.readthedocs.io/en/latest/pyasn1/contents.html), +compiled ASN.1 modules for different protocols and file formats +could be found in the pyasn1-modules +[repo](https://github.com/pyasn1/pyasn1-modules). + +How to get pyasn1 +----------------- + +The pyasn1 package is distributed under terms and conditions of 2-clause +BSD [license](https://pyasn1.readthedocs.io/en/latest/license.html). Source code is freely +available as a GitHub [repo](https://github.com/pyasn1/pyasn1). + +You could `pip install pyasn1` or download it from [PyPI](https://pypi.org/project/pyasn1). + +If something does not work as expected, +[open an issue](https://github.com/epyasn1/pyasn1/issues) at GitHub or +post your question [on Stack Overflow](https://stackoverflow.com/questions/ask) +or try browsing pyasn1 +[mailing list archives](https://sourceforge.net/p/pyasn1/mailman/pyasn1-users/). + +Copyright (c) 2005-2020, [Ilya Etingof](mailto:etingof@gmail.com). +All rights reserved. diff --git a/contrib/python/pyasn1/py3/.dist-info/top_level.txt b/contrib/python/pyasn1/py3/.dist-info/top_level.txt new file mode 100644 index 0000000000..38fe414575 --- /dev/null +++ b/contrib/python/pyasn1/py3/.dist-info/top_level.txt @@ -0,0 +1 @@ +pyasn1 diff --git a/contrib/python/pyasn1/py3/LICENSE.rst b/contrib/python/pyasn1/py3/LICENSE.rst new file mode 100644 index 0000000000..598b8430ef --- /dev/null +++ b/contrib/python/pyasn1/py3/LICENSE.rst @@ -0,0 +1,24 @@ +Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/contrib/python/pyasn1/py3/README.md b/contrib/python/pyasn1/py3/README.md new file mode 100644 index 0000000000..e1f9501b49 --- /dev/null +++ b/contrib/python/pyasn1/py3/README.md @@ -0,0 +1,188 @@ + +ASN.1 library for Python +------------------------ +[![PyPI](https://img.shields.io/pypi/v/pyasn1.svg?maxAge=2592000)](https://pypi.org/project/pyasn1) +[![Python Versions](https://img.shields.io/pypi/pyversions/pyasn1.svg)](https://pypi.org/project/pyasn1/) +[![Build status](https://github.com/pyasn1/pyasn1/actions/workflows/main.yml/badge.svg)](https://github.com/pyasn1/pyasn1/actions/workflows/main.yml) +[![Coverage Status](https://img.shields.io/codecov/c/github/pyasn1/pyasn1.svg)](https://codecov.io/github/pyasn1/pyasn1) +[![GitHub license](https://img.shields.io/badge/license-BSD-blue.svg)](https://raw.githubusercontent.com/pyasn1/pyasn1/master/LICENSE.txt) + +This is a free and open source implementation of ASN.1 types and codecs +as a Python package. It has been first written to support particular +protocol (SNMP) but then generalized to be suitable for a wide range +of protocols based on +[ASN.1 specification](https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-X.208-198811-W!!PDF-E&type=items). + +**NOTE:** The package is now maintained by *Christian Heimes* and +*Simon Pichugin* in project https://github.com/pyasn1/pyasn1. + +Features +-------- + +* Generic implementation of ASN.1 types (X.208) +* Standards compliant BER/CER/DER codecs +* Can operate on streams of serialized data +* Dumps/loads ASN.1 structures from Python types +* 100% Python, works with Python 2.7 and 3.6+ +* MT-safe +* Contributed ASN.1 compiler [Asn1ate](https://github.com/kimgr/asn1ate) + +Why using pyasn1 +---------------- + +ASN.1 solves the data serialisation problem. This solution was +designed long ago by the wise Ancients. Back then, they did not +have the luxury of wasting bits. That is why ASN.1 is designed +to serialise data structures of unbounded complexity into +something compact and efficient when it comes to processing +the data. + +That probably explains why many network protocols and file formats +still rely on the 30+ years old technology. Including a number of +high-profile Internet protocols and file formats. + +Quite a number of books cover the topic of ASN.1. +[Communication between heterogeneous systems](http://www.oss.com/asn1/dubuisson.html) +by Olivier Dubuisson is one of those high quality books freely +available on the Internet. + +The pyasn1 package is designed to help Python programmers tackling +network protocols and file formats at the comfort of their Python +prompt. The tool struggles to capture all aspects of a rather +complicated ASN.1 system and to represent it on the Python terms. + +How to use pyasn1 +----------------- + +With pyasn1 you can build Python objects from ASN.1 data structures. +For example, the following ASN.1 data structure: + +```bash +Record ::= SEQUENCE { + id INTEGER, + room [0] INTEGER OPTIONAL, + house [1] INTEGER DEFAULT 0 +} +``` + +Could be expressed in pyasn1 like this: + +```python +class Record(Sequence): + componentType = NamedTypes( + NamedType('id', Integer()), + OptionalNamedType( + 'room', Integer().subtype( + implicitTag=Tag(tagClassContext, tagFormatSimple, 0) + ) + ), + DefaultedNamedType( + 'house', Integer(0).subtype( + implicitTag=Tag(tagClassContext, tagFormatSimple, 1) + ) + ) + ) +``` + +It is in the spirit of ASN.1 to take abstract data description +and turn it into a programming language specific form. +Once you have your ASN.1 data structure expressed in Python, you +can use it along the lines of similar Python type (e.g. ASN.1 +`SET` is similar to Python `dict`, `SET OF` to `list`): + +```python +>>> record = Record() +>>> record['id'] = 123 +>>> record['room'] = 321 +>>> str(record) +Record: + id=123 + room=321 +>>> +``` + +Part of the power of ASN.1 comes from its serialisation features. You +can serialise your data structure and send it over the network. + +```python +>>> from pyasn1.codec.der.encoder import encode +>>> substrate = encode(record) +>>> hexdump(substrate) +00000: 30 07 02 01 7B 80 02 01 41 +``` + +Conversely, you can turn serialised ASN.1 content, as received from +network or read from a file, into a Python object which you can +introspect, modify, encode and send back. + +```python +>>> from pyasn1.codec.der.decoder import decode +>>> received_record, rest_of_substrate = decode(substrate, asn1Spec=Record()) +>>> +>>> for field in received_record: +>>> print('{} is {}'.format(field, received_record[field])) +id is 123 +room is 321 +house is 0 +>>> +>>> record == received_record +True +>>> received_record.update(room=123) +>>> substrate = encode(received_record) +>>> hexdump(substrate) +00000: 30 06 02 01 7B 80 01 7B +``` + +The pyasn1 classes struggle to emulate their Python prototypes (e.g. int, +list, dict etc.). But ASN.1 types exhibit more complicated behaviour. +To make life easier for a Pythonista, they can turn their pyasn1 +classes into Python built-ins: + +```python +>>> from pyasn1.codec.native.encoder import encode +>>> encode(record) +{'id': 123, 'room': 321, 'house': 0} +``` + +Or vice-versa -- you can initialize an ASN.1 structure from a tree of +Python objects: + +```python +>>> from pyasn1.codec.native.decoder import decode +>>> record = decode({'id': 123, 'room': 321, 'house': 0}, asn1Spec=Record()) +>>> str(record) +Record: + id=123 + room=321 +>>> +``` + +With ASN.1 design, serialisation codecs are decoupled from data objects, +so you could turn every single ASN.1 object into many different +serialised forms. As of this moment, pyasn1 supports BER, DER, CER and +Python built-ins codecs. The extremely compact PER encoding is expected +to be introduced in the upcoming pyasn1 release. + +More information on pyasn1 APIs can be found in the +[documentation](https://pyasn1.readthedocs.io/en/latest/pyasn1/contents.html), +compiled ASN.1 modules for different protocols and file formats +could be found in the pyasn1-modules +[repo](https://github.com/pyasn1/pyasn1-modules). + +How to get pyasn1 +----------------- + +The pyasn1 package is distributed under terms and conditions of 2-clause +BSD [license](https://pyasn1.readthedocs.io/en/latest/license.html). Source code is freely +available as a GitHub [repo](https://github.com/pyasn1/pyasn1). + +You could `pip install pyasn1` or download it from [PyPI](https://pypi.org/project/pyasn1). + +If something does not work as expected, +[open an issue](https://github.com/epyasn1/pyasn1/issues) at GitHub or +post your question [on Stack Overflow](https://stackoverflow.com/questions/ask) +or try browsing pyasn1 +[mailing list archives](https://sourceforge.net/p/pyasn1/mailman/pyasn1-users/). + +Copyright (c) 2005-2020, [Ilya Etingof](mailto:etingof@gmail.com). +All rights reserved. diff --git a/contrib/python/pyasn1/py3/pyasn1/__init__.py b/contrib/python/pyasn1/py3/pyasn1/__init__.py new file mode 100644 index 0000000000..a979d291f2 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/__init__.py @@ -0,0 +1,2 @@ +# https://www.python.org/dev/peps/pep-0396/ +__version__ = '0.5.0' diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/__init__.py b/contrib/python/pyasn1/py3/pyasn1/codec/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/ber/__init__.py b/contrib/python/pyasn1/py3/pyasn1/codec/ber/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/ber/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/ber/decoder.py b/contrib/python/pyasn1/py3/pyasn1/codec/ber/decoder.py new file mode 100644 index 0000000000..070733fd28 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/ber/decoder.py @@ -0,0 +1,2071 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import os + +from pyasn1 import debug +from pyasn1 import error +from pyasn1.codec.ber import eoo +from pyasn1.codec.streaming import asSeekableStream +from pyasn1.codec.streaming import isEndOfStream +from pyasn1.codec.streaming import peekIntoStream +from pyasn1.codec.streaming import readFromStream +from pyasn1.compat import _MISSING +from pyasn1.compat.integer import from_bytes +from pyasn1.compat.octets import oct2int, octs2ints, ints2octs, null +from pyasn1.error import PyAsn1Error +from pyasn1.type import base +from pyasn1.type import char +from pyasn1.type import tag +from pyasn1.type import tagmap +from pyasn1.type import univ +from pyasn1.type import useful + +__all__ = ['StreamingDecoder', 'Decoder', 'decode'] + +LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_DECODER) + +noValue = base.noValue + +SubstrateUnderrunError = error.SubstrateUnderrunError + + +class AbstractPayloadDecoder(object): + protoComponent = None + + def valueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + """Decode value with fixed byte length. + + The decoder is allowed to consume as many bytes as necessary. + """ + raise error.PyAsn1Error('SingleItemDecoder not implemented for %s' % (tagSet,)) # TODO: Seems more like an NotImplementedError? + + def indefLenValueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + """Decode value with undefined length. + + The decoder is allowed to consume as many bytes as necessary. + """ + raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,)) # TODO: Seems more like an NotImplementedError? + + @staticmethod + def _passAsn1Object(asn1Object, options): + if 'asn1Object' not in options: + options['asn1Object'] = asn1Object + + return options + + +class AbstractSimplePayloadDecoder(AbstractPayloadDecoder): + @staticmethod + def substrateCollector(asn1Object, substrate, length, options): + for chunk in readFromStream(substrate, length, options): + yield chunk + + def _createComponent(self, asn1Spec, tagSet, value, **options): + if options.get('native'): + return value + elif asn1Spec is None: + return self.protoComponent.clone(value, tagSet=tagSet) + elif value is noValue: + return asn1Spec + else: + return asn1Spec.clone(value) + + +class RawPayloadDecoder(AbstractSimplePayloadDecoder): + protoComponent = univ.Any('') + + def valueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + if substrateFun: + asn1Object = self._createComponent(asn1Spec, tagSet, '', **options) + + for chunk in substrateFun(asn1Object, substrate, length, options): + yield chunk + + return + + for value in decodeFun(substrate, asn1Spec, tagSet, length, **options): + yield value + + def indefLenValueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + if substrateFun: + asn1Object = self._createComponent(asn1Spec, tagSet, '', **options) + + for chunk in substrateFun(asn1Object, substrate, length, options): + yield chunk + + return + + while True: + for value in decodeFun( + substrate, asn1Spec, tagSet, length, + allowEoo=True, **options): + + if value is eoo.endOfOctets: + return + + yield value + + +rawPayloadDecoder = RawPayloadDecoder() + + +class IntegerPayloadDecoder(AbstractSimplePayloadDecoder): + protoComponent = univ.Integer(0) + + def valueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + + if tagSet[0].tagFormat != tag.tagFormatSimple: + raise error.PyAsn1Error('Simple tag format expected') + + for chunk in readFromStream(substrate, length, options): + if isinstance(chunk, SubstrateUnderrunError): + yield chunk + + if chunk: + value = from_bytes(chunk, signed=True) + + else: + value = 0 + + yield self._createComponent(asn1Spec, tagSet, value, **options) + + +class BooleanPayloadDecoder(IntegerPayloadDecoder): + protoComponent = univ.Boolean(0) + + def _createComponent(self, asn1Spec, tagSet, value, **options): + return IntegerPayloadDecoder._createComponent( + self, asn1Spec, tagSet, value and 1 or 0, **options) + + +class BitStringPayloadDecoder(AbstractSimplePayloadDecoder): + protoComponent = univ.BitString(()) + supportConstructedForm = True + + def valueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + + if substrateFun: + asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options) + + for chunk in substrateFun(asn1Object, substrate, length, options): + yield chunk + + return + + if not length: + raise error.PyAsn1Error('Empty BIT STRING substrate') + + for chunk in isEndOfStream(substrate): + if isinstance(chunk, SubstrateUnderrunError): + yield chunk + + if chunk: + raise error.PyAsn1Error('Empty BIT STRING substrate') + + if tagSet[0].tagFormat == tag.tagFormatSimple: # XXX what tag to check? + + for trailingBits in readFromStream(substrate, 1, options): + if isinstance(trailingBits, SubstrateUnderrunError): + yield trailingBits + + trailingBits = ord(trailingBits) + if trailingBits > 7: + raise error.PyAsn1Error( + 'Trailing bits overflow %s' % trailingBits + ) + + for chunk in readFromStream(substrate, length - 1, options): + if isinstance(chunk, SubstrateUnderrunError): + yield chunk + + value = self.protoComponent.fromOctetString( + chunk, internalFormat=True, padding=trailingBits) + + yield self._createComponent(asn1Spec, tagSet, value, **options) + + return + + if not self.supportConstructedForm: + raise error.PyAsn1Error('Constructed encoding form prohibited ' + 'at %s' % self.__class__.__name__) + + if LOG: + LOG('assembling constructed serialization') + + # All inner fragments are of the same type, treat them as octet string + substrateFun = self.substrateCollector + + bitString = self.protoComponent.fromOctetString(null, internalFormat=True) + + current_position = substrate.tell() + + while substrate.tell() - current_position < length: + for component in decodeFun( + substrate, self.protoComponent, substrateFun=substrateFun, + **options): + if isinstance(component, SubstrateUnderrunError): + yield component + + trailingBits = oct2int(component[0]) + if trailingBits > 7: + raise error.PyAsn1Error( + 'Trailing bits overflow %s' % trailingBits + ) + + bitString = self.protoComponent.fromOctetString( + component[1:], internalFormat=True, + prepend=bitString, padding=trailingBits + ) + + yield self._createComponent(asn1Spec, tagSet, bitString, **options) + + def indefLenValueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + + if substrateFun: + asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options) + + for chunk in substrateFun(asn1Object, substrate, length, options): + yield chunk + + return + + # All inner fragments are of the same type, treat them as octet string + substrateFun = self.substrateCollector + + bitString = self.protoComponent.fromOctetString(null, internalFormat=True) + + while True: # loop over fragments + + for component in decodeFun( + substrate, self.protoComponent, substrateFun=substrateFun, + allowEoo=True, **options): + + if component is eoo.endOfOctets: + break + + if isinstance(component, SubstrateUnderrunError): + yield component + + if component is eoo.endOfOctets: + break + + trailingBits = oct2int(component[0]) + if trailingBits > 7: + raise error.PyAsn1Error( + 'Trailing bits overflow %s' % trailingBits + ) + + bitString = self.protoComponent.fromOctetString( + component[1:], internalFormat=True, + prepend=bitString, padding=trailingBits + ) + + yield self._createComponent(asn1Spec, tagSet, bitString, **options) + + +class OctetStringPayloadDecoder(AbstractSimplePayloadDecoder): + protoComponent = univ.OctetString('') + supportConstructedForm = True + + def valueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + if substrateFun: + asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options) + + for chunk in substrateFun(asn1Object, substrate, length, options): + yield chunk + + return + + if tagSet[0].tagFormat == tag.tagFormatSimple: # XXX what tag to check? + for chunk in readFromStream(substrate, length, options): + if isinstance(chunk, SubstrateUnderrunError): + yield chunk + + yield self._createComponent(asn1Spec, tagSet, chunk, **options) + + return + + if not self.supportConstructedForm: + raise error.PyAsn1Error('Constructed encoding form prohibited at %s' % self.__class__.__name__) + + if LOG: + LOG('assembling constructed serialization') + + # All inner fragments are of the same type, treat them as octet string + substrateFun = self.substrateCollector + + header = null + + original_position = substrate.tell() + # head = popSubstream(substrate, length) + while substrate.tell() - original_position < length: + for component in decodeFun( + substrate, self.protoComponent, substrateFun=substrateFun, + **options): + if isinstance(component, SubstrateUnderrunError): + yield component + + header += component + + yield self._createComponent(asn1Spec, tagSet, header, **options) + + def indefLenValueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + if substrateFun and substrateFun is not self.substrateCollector: + asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options) + + for chunk in substrateFun(asn1Object, substrate, length, options): + yield chunk + + return + + # All inner fragments are of the same type, treat them as octet string + substrateFun = self.substrateCollector + + header = null + + while True: # loop over fragments + + for component in decodeFun( + substrate, self.protoComponent, substrateFun=substrateFun, + allowEoo=True, **options): + + if isinstance(component, SubstrateUnderrunError): + yield component + + if component is eoo.endOfOctets: + break + + if component is eoo.endOfOctets: + break + + header += component + + yield self._createComponent(asn1Spec, tagSet, header, **options) + + +class NullPayloadDecoder(AbstractSimplePayloadDecoder): + protoComponent = univ.Null('') + + def valueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + + if tagSet[0].tagFormat != tag.tagFormatSimple: + raise error.PyAsn1Error('Simple tag format expected') + + for chunk in readFromStream(substrate, length, options): + if isinstance(chunk, SubstrateUnderrunError): + yield chunk + + component = self._createComponent(asn1Spec, tagSet, '', **options) + + if chunk: + raise error.PyAsn1Error('Unexpected %d-octet substrate for Null' % length) + + yield component + + +class ObjectIdentifierPayloadDecoder(AbstractSimplePayloadDecoder): + protoComponent = univ.ObjectIdentifier(()) + + def valueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + if tagSet[0].tagFormat != tag.tagFormatSimple: + raise error.PyAsn1Error('Simple tag format expected') + + for chunk in readFromStream(substrate, length, options): + if isinstance(chunk, SubstrateUnderrunError): + yield chunk + + if not chunk: + raise error.PyAsn1Error('Empty substrate') + + chunk = octs2ints(chunk) + + oid = () + index = 0 + substrateLen = len(chunk) + while index < substrateLen: + subId = chunk[index] + index += 1 + if subId < 128: + oid += (subId,) + elif subId > 128: + # Construct subid from a number of octets + nextSubId = subId + subId = 0 + while nextSubId >= 128: + subId = (subId << 7) + (nextSubId & 0x7F) + if index >= substrateLen: + raise error.SubstrateUnderrunError( + 'Short substrate for sub-OID past %s' % (oid,) + ) + nextSubId = chunk[index] + index += 1 + oid += ((subId << 7) + nextSubId,) + elif subId == 128: + # ASN.1 spec forbids leading zeros (0x80) in OID + # encoding, tolerating it opens a vulnerability. See + # https://www.esat.kuleuven.be/cosic/publications/article-1432.pdf + # page 7 + raise error.PyAsn1Error('Invalid octet 0x80 in OID encoding') + + # Decode two leading arcs + if 0 <= oid[0] <= 39: + oid = (0,) + oid + elif 40 <= oid[0] <= 79: + oid = (1, oid[0] - 40) + oid[1:] + elif oid[0] >= 80: + oid = (2, oid[0] - 80) + oid[1:] + else: + raise error.PyAsn1Error('Malformed first OID octet: %s' % chunk[0]) + + yield self._createComponent(asn1Spec, tagSet, oid, **options) + + +class RealPayloadDecoder(AbstractSimplePayloadDecoder): + protoComponent = univ.Real() + + def valueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + if tagSet[0].tagFormat != tag.tagFormatSimple: + raise error.PyAsn1Error('Simple tag format expected') + + for chunk in readFromStream(substrate, length, options): + if isinstance(chunk, SubstrateUnderrunError): + yield chunk + + if not chunk: + yield self._createComponent(asn1Spec, tagSet, 0.0, **options) + return + + fo = oct2int(chunk[0]) + chunk = chunk[1:] + if fo & 0x80: # binary encoding + if not chunk: + raise error.PyAsn1Error("Incomplete floating-point value") + + if LOG: + LOG('decoding binary encoded REAL') + + n = (fo & 0x03) + 1 + + if n == 4: + n = oct2int(chunk[0]) + chunk = chunk[1:] + + eo, chunk = chunk[:n], chunk[n:] + + if not eo or not chunk: + raise error.PyAsn1Error('Real exponent screwed') + + e = oct2int(eo[0]) & 0x80 and -1 or 0 + + while eo: # exponent + e <<= 8 + e |= oct2int(eo[0]) + eo = eo[1:] + + b = fo >> 4 & 0x03 # base bits + + if b > 2: + raise error.PyAsn1Error('Illegal Real base') + + if b == 1: # encbase = 8 + e *= 3 + + elif b == 2: # encbase = 16 + e *= 4 + p = 0 + + while chunk: # value + p <<= 8 + p |= oct2int(chunk[0]) + chunk = chunk[1:] + + if fo & 0x40: # sign bit + p = -p + + sf = fo >> 2 & 0x03 # scale bits + p *= 2 ** sf + value = (p, 2, e) + + elif fo & 0x40: # infinite value + if LOG: + LOG('decoding infinite REAL') + + value = fo & 0x01 and '-inf' or 'inf' + + elif fo & 0xc0 == 0: # character encoding + if not chunk: + raise error.PyAsn1Error("Incomplete floating-point value") + + if LOG: + LOG('decoding character encoded REAL') + + try: + if fo & 0x3 == 0x1: # NR1 + value = (int(chunk), 10, 0) + + elif fo & 0x3 == 0x2: # NR2 + value = float(chunk) + + elif fo & 0x3 == 0x3: # NR3 + value = float(chunk) + + else: + raise error.SubstrateUnderrunError( + 'Unknown NR (tag %s)' % fo + ) + + except ValueError: + raise error.SubstrateUnderrunError( + 'Bad character Real syntax' + ) + + else: + raise error.SubstrateUnderrunError( + 'Unknown encoding (tag %s)' % fo + ) + + yield self._createComponent(asn1Spec, tagSet, value, **options) + + +class AbstractConstructedPayloadDecoder(AbstractPayloadDecoder): + protoComponent = None + + +class ConstructedPayloadDecoderBase(AbstractConstructedPayloadDecoder): + protoRecordComponent = None + protoSequenceComponent = None + + def _getComponentTagMap(self, asn1Object, idx): + raise NotImplementedError() + + def _getComponentPositionByType(self, asn1Object, tagSet, idx): + raise NotImplementedError() + + def _decodeComponentsSchemaless( + self, substrate, tagSet=None, decodeFun=None, + length=None, **options): + + asn1Object = None + + components = [] + componentTypes = set() + + original_position = substrate.tell() + + while length == -1 or substrate.tell() < original_position + length: + for component in decodeFun(substrate, **options): + if isinstance(component, SubstrateUnderrunError): + yield component + + if length == -1 and component is eoo.endOfOctets: + break + + components.append(component) + componentTypes.add(component.tagSet) + + # Now we have to guess is it SEQUENCE/SET or SEQUENCE OF/SET OF + # The heuristics is: + # * 1+ components of different types -> likely SEQUENCE/SET + # * otherwise -> likely SEQUENCE OF/SET OF + if len(componentTypes) > 1: + protoComponent = self.protoRecordComponent + + else: + protoComponent = self.protoSequenceComponent + + asn1Object = protoComponent.clone( + # construct tagSet from base tag from prototype ASN.1 object + # and additional tags recovered from the substrate + tagSet=tag.TagSet(protoComponent.tagSet.baseTag, *tagSet.superTags) + ) + + if LOG: + LOG('guessed %r container type (pass `asn1Spec` to guide the ' + 'decoder)' % asn1Object) + + for idx, component in enumerate(components): + asn1Object.setComponentByPosition( + idx, component, + verifyConstraints=False, + matchTags=False, matchConstraints=False + ) + + yield asn1Object + + def valueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + if tagSet[0].tagFormat != tag.tagFormatConstructed: + raise error.PyAsn1Error('Constructed tag format expected') + + original_position = substrate.tell() + + if substrateFun: + if asn1Spec is not None: + asn1Object = asn1Spec.clone() + + elif self.protoComponent is not None: + asn1Object = self.protoComponent.clone(tagSet=tagSet) + + else: + asn1Object = self.protoRecordComponent, self.protoSequenceComponent + + for chunk in substrateFun(asn1Object, substrate, length, options): + yield chunk + + return + + if asn1Spec is None: + for asn1Object in self._decodeComponentsSchemaless( + substrate, tagSet=tagSet, decodeFun=decodeFun, + length=length, **options): + if isinstance(asn1Object, SubstrateUnderrunError): + yield asn1Object + + if substrate.tell() < original_position + length: + if LOG: + for trailing in readFromStream(substrate, context=options): + if isinstance(trailing, SubstrateUnderrunError): + yield trailing + + LOG('Unused trailing %d octets encountered: %s' % ( + len(trailing), debug.hexdump(trailing))) + + yield asn1Object + + return + + asn1Object = asn1Spec.clone() + asn1Object.clear() + + options = self._passAsn1Object(asn1Object, options) + + if asn1Spec.typeId in (univ.Sequence.typeId, univ.Set.typeId): + + namedTypes = asn1Spec.componentType + + isSetType = asn1Spec.typeId == univ.Set.typeId + isDeterministic = not isSetType and not namedTypes.hasOptionalOrDefault + + if LOG: + LOG('decoding %sdeterministic %s type %r chosen by type ID' % ( + not isDeterministic and 'non-' or '', isSetType and 'SET' or '', + asn1Spec)) + + seenIndices = set() + idx = 0 + while substrate.tell() - original_position < length: + if not namedTypes: + componentType = None + + elif isSetType: + componentType = namedTypes.tagMapUnique + + else: + try: + if isDeterministic: + componentType = namedTypes[idx].asn1Object + + elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted: + componentType = namedTypes.getTagMapNearPosition(idx) + + else: + componentType = namedTypes[idx].asn1Object + + except IndexError: + raise error.PyAsn1Error( + 'Excessive components decoded at %r' % (asn1Spec,) + ) + + for component in decodeFun(substrate, componentType, **options): + if isinstance(component, SubstrateUnderrunError): + yield component + + if not isDeterministic and namedTypes: + if isSetType: + idx = namedTypes.getPositionByType(component.effectiveTagSet) + + elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted: + idx = namedTypes.getPositionNearType(component.effectiveTagSet, idx) + + asn1Object.setComponentByPosition( + idx, component, + verifyConstraints=False, + matchTags=False, matchConstraints=False + ) + + seenIndices.add(idx) + idx += 1 + + if LOG: + LOG('seen component indices %s' % seenIndices) + + if namedTypes: + if not namedTypes.requiredComponents.issubset(seenIndices): + raise error.PyAsn1Error( + 'ASN.1 object %s has uninitialized ' + 'components' % asn1Object.__class__.__name__) + + if namedTypes.hasOpenTypes: + + openTypes = options.get('openTypes', {}) + + if LOG: + LOG('user-specified open types map:') + + for k, v in openTypes.items(): + LOG('%s -> %r' % (k, v)) + + if openTypes or options.get('decodeOpenTypes', False): + + for idx, namedType in enumerate(namedTypes.namedTypes): + if not namedType.openType: + continue + + if namedType.isOptional and not asn1Object.getComponentByPosition(idx).isValue: + continue + + governingValue = asn1Object.getComponentByName( + namedType.openType.name + ) + + try: + openType = openTypes[governingValue] + + except KeyError: + + if LOG: + LOG('default open types map of component ' + '"%s.%s" governed by component "%s.%s"' + ':' % (asn1Object.__class__.__name__, + namedType.name, + asn1Object.__class__.__name__, + namedType.openType.name)) + + for k, v in namedType.openType.items(): + LOG('%s -> %r' % (k, v)) + + try: + openType = namedType.openType[governingValue] + + except KeyError: + if LOG: + LOG('failed to resolve open type by governing ' + 'value %r' % (governingValue,)) + continue + + if LOG: + LOG('resolved open type %r by governing ' + 'value %r' % (openType, governingValue)) + + containerValue = asn1Object.getComponentByPosition(idx) + + if containerValue.typeId in ( + univ.SetOf.typeId, univ.SequenceOf.typeId): + + for pos, containerElement in enumerate( + containerValue): + + stream = asSeekableStream(containerValue[pos].asOctets()) + + for component in decodeFun(stream, asn1Spec=openType, **options): + if isinstance(component, SubstrateUnderrunError): + yield component + + containerValue[pos] = component + + else: + stream = asSeekableStream(asn1Object.getComponentByPosition(idx).asOctets()) + + for component in decodeFun(stream, asn1Spec=openType, **options): + if isinstance(component, SubstrateUnderrunError): + yield component + + asn1Object.setComponentByPosition(idx, component) + + else: + inconsistency = asn1Object.isInconsistent + if inconsistency: + raise inconsistency + + else: + componentType = asn1Spec.componentType + + if LOG: + LOG('decoding type %r chosen by given `asn1Spec`' % componentType) + + idx = 0 + + while substrate.tell() - original_position < length: + for component in decodeFun(substrate, componentType, **options): + if isinstance(component, SubstrateUnderrunError): + yield component + + asn1Object.setComponentByPosition( + idx, component, + verifyConstraints=False, + matchTags=False, matchConstraints=False + ) + + idx += 1 + + yield asn1Object + + def indefLenValueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + if tagSet[0].tagFormat != tag.tagFormatConstructed: + raise error.PyAsn1Error('Constructed tag format expected') + + if substrateFun is not None: + if asn1Spec is not None: + asn1Object = asn1Spec.clone() + + elif self.protoComponent is not None: + asn1Object = self.protoComponent.clone(tagSet=tagSet) + + else: + asn1Object = self.protoRecordComponent, self.protoSequenceComponent + + for chunk in substrateFun(asn1Object, substrate, length, options): + yield chunk + + return + + if asn1Spec is None: + for asn1Object in self._decodeComponentsSchemaless( + substrate, tagSet=tagSet, decodeFun=decodeFun, + length=length, **dict(options, allowEoo=True)): + if isinstance(asn1Object, SubstrateUnderrunError): + yield asn1Object + + yield asn1Object + + return + + asn1Object = asn1Spec.clone() + asn1Object.clear() + + options = self._passAsn1Object(asn1Object, options) + + if asn1Spec.typeId in (univ.Sequence.typeId, univ.Set.typeId): + + namedTypes = asn1Object.componentType + + isSetType = asn1Object.typeId == univ.Set.typeId + isDeterministic = not isSetType and not namedTypes.hasOptionalOrDefault + + if LOG: + LOG('decoding %sdeterministic %s type %r chosen by type ID' % ( + not isDeterministic and 'non-' or '', isSetType and 'SET' or '', + asn1Spec)) + + seenIndices = set() + + idx = 0 + + while True: # loop over components + if len(namedTypes) <= idx: + asn1Spec = None + + elif isSetType: + asn1Spec = namedTypes.tagMapUnique + + else: + try: + if isDeterministic: + asn1Spec = namedTypes[idx].asn1Object + + elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted: + asn1Spec = namedTypes.getTagMapNearPosition(idx) + + else: + asn1Spec = namedTypes[idx].asn1Object + + except IndexError: + raise error.PyAsn1Error( + 'Excessive components decoded at %r' % (asn1Object,) + ) + + for component in decodeFun(substrate, asn1Spec, allowEoo=True, **options): + + if isinstance(component, SubstrateUnderrunError): + yield component + + if component is eoo.endOfOctets: + break + + if component is eoo.endOfOctets: + break + + if not isDeterministic and namedTypes: + if isSetType: + idx = namedTypes.getPositionByType(component.effectiveTagSet) + + elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted: + idx = namedTypes.getPositionNearType(component.effectiveTagSet, idx) + + asn1Object.setComponentByPosition( + idx, component, + verifyConstraints=False, + matchTags=False, matchConstraints=False + ) + + seenIndices.add(idx) + idx += 1 + + if LOG: + LOG('seen component indices %s' % seenIndices) + + if namedTypes: + if not namedTypes.requiredComponents.issubset(seenIndices): + raise error.PyAsn1Error( + 'ASN.1 object %s has uninitialized ' + 'components' % asn1Object.__class__.__name__) + + if namedTypes.hasOpenTypes: + + openTypes = options.get('openTypes', {}) + + if LOG: + LOG('user-specified open types map:') + + for k, v in openTypes.items(): + LOG('%s -> %r' % (k, v)) + + if openTypes or options.get('decodeOpenTypes', False): + + for idx, namedType in enumerate(namedTypes.namedTypes): + if not namedType.openType: + continue + + if namedType.isOptional and not asn1Object.getComponentByPosition(idx).isValue: + continue + + governingValue = asn1Object.getComponentByName( + namedType.openType.name + ) + + try: + openType = openTypes[governingValue] + + except KeyError: + + if LOG: + LOG('default open types map of component ' + '"%s.%s" governed by component "%s.%s"' + ':' % (asn1Object.__class__.__name__, + namedType.name, + asn1Object.__class__.__name__, + namedType.openType.name)) + + for k, v in namedType.openType.items(): + LOG('%s -> %r' % (k, v)) + + try: + openType = namedType.openType[governingValue] + + except KeyError: + if LOG: + LOG('failed to resolve open type by governing ' + 'value %r' % (governingValue,)) + continue + + if LOG: + LOG('resolved open type %r by governing ' + 'value %r' % (openType, governingValue)) + + containerValue = asn1Object.getComponentByPosition(idx) + + if containerValue.typeId in ( + univ.SetOf.typeId, univ.SequenceOf.typeId): + + for pos, containerElement in enumerate( + containerValue): + + stream = asSeekableStream(containerValue[pos].asOctets()) + + for component in decodeFun(stream, asn1Spec=openType, + **dict(options, allowEoo=True)): + if isinstance(component, SubstrateUnderrunError): + yield component + + if component is eoo.endOfOctets: + break + + containerValue[pos] = component + + else: + stream = asSeekableStream(asn1Object.getComponentByPosition(idx).asOctets()) + for component in decodeFun(stream, asn1Spec=openType, + **dict(options, allowEoo=True)): + if isinstance(component, SubstrateUnderrunError): + yield component + + if component is eoo.endOfOctets: + break + + asn1Object.setComponentByPosition(idx, component) + + else: + inconsistency = asn1Object.isInconsistent + if inconsistency: + raise inconsistency + + else: + componentType = asn1Spec.componentType + + if LOG: + LOG('decoding type %r chosen by given `asn1Spec`' % componentType) + + idx = 0 + + while True: + + for component in decodeFun( + substrate, componentType, allowEoo=True, **options): + + if isinstance(component, SubstrateUnderrunError): + yield component + + if component is eoo.endOfOctets: + break + + if component is eoo.endOfOctets: + break + + asn1Object.setComponentByPosition( + idx, component, + verifyConstraints=False, + matchTags=False, matchConstraints=False + ) + + idx += 1 + + yield asn1Object + + +class SequenceOrSequenceOfPayloadDecoder(ConstructedPayloadDecoderBase): + protoRecordComponent = univ.Sequence() + protoSequenceComponent = univ.SequenceOf() + + +class SequencePayloadDecoder(SequenceOrSequenceOfPayloadDecoder): + protoComponent = univ.Sequence() + + +class SequenceOfPayloadDecoder(SequenceOrSequenceOfPayloadDecoder): + protoComponent = univ.SequenceOf() + + +class SetOrSetOfPayloadDecoder(ConstructedPayloadDecoderBase): + protoRecordComponent = univ.Set() + protoSequenceComponent = univ.SetOf() + + +class SetPayloadDecoder(SetOrSetOfPayloadDecoder): + protoComponent = univ.Set() + + +class SetOfPayloadDecoder(SetOrSetOfPayloadDecoder): + protoComponent = univ.SetOf() + + +class ChoicePayloadDecoder(ConstructedPayloadDecoderBase): + protoComponent = univ.Choice() + + def valueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + if asn1Spec is None: + asn1Object = self.protoComponent.clone(tagSet=tagSet) + + else: + asn1Object = asn1Spec.clone() + + if substrateFun: + for chunk in substrateFun(asn1Object, substrate, length, options): + yield chunk + + return + + options = self._passAsn1Object(asn1Object, options) + + if asn1Object.tagSet == tagSet: + if LOG: + LOG('decoding %s as explicitly tagged CHOICE' % (tagSet,)) + + for component in decodeFun( + substrate, asn1Object.componentTagMap, **options): + if isinstance(component, SubstrateUnderrunError): + yield component + + else: + if LOG: + LOG('decoding %s as untagged CHOICE' % (tagSet,)) + + for component in decodeFun( + substrate, asn1Object.componentTagMap, tagSet, length, + state, **options): + if isinstance(component, SubstrateUnderrunError): + yield component + + effectiveTagSet = component.effectiveTagSet + + if LOG: + LOG('decoded component %s, effective tag set %s' % (component, effectiveTagSet)) + + asn1Object.setComponentByType( + effectiveTagSet, component, + verifyConstraints=False, + matchTags=False, matchConstraints=False, + innerFlag=False + ) + + yield asn1Object + + def indefLenValueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + if asn1Spec is None: + asn1Object = self.protoComponent.clone(tagSet=tagSet) + + else: + asn1Object = asn1Spec.clone() + + if substrateFun: + for chunk in substrateFun(asn1Object, substrate, length, options): + yield chunk + + return + + options = self._passAsn1Object(asn1Object, options) + + isTagged = asn1Object.tagSet == tagSet + + if LOG: + LOG('decoding %s as %stagged CHOICE' % ( + tagSet, isTagged and 'explicitly ' or 'un')) + + while True: + + if isTagged: + iterator = decodeFun( + substrate, asn1Object.componentType.tagMapUnique, + **dict(options, allowEoo=True)) + + else: + iterator = decodeFun( + substrate, asn1Object.componentType.tagMapUnique, + tagSet, length, state, **dict(options, allowEoo=True)) + + for component in iterator: + + if isinstance(component, SubstrateUnderrunError): + yield component + + if component is eoo.endOfOctets: + break + + effectiveTagSet = component.effectiveTagSet + + if LOG: + LOG('decoded component %s, effective tag set ' + '%s' % (component, effectiveTagSet)) + + asn1Object.setComponentByType( + effectiveTagSet, component, + verifyConstraints=False, + matchTags=False, matchConstraints=False, + innerFlag=False + ) + + if not isTagged: + break + + if not isTagged or component is eoo.endOfOctets: + break + + yield asn1Object + + +class AnyPayloadDecoder(AbstractSimplePayloadDecoder): + protoComponent = univ.Any() + + def valueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + if asn1Spec is None: + isUntagged = True + + elif asn1Spec.__class__ is tagmap.TagMap: + isUntagged = tagSet not in asn1Spec.tagMap + + else: + isUntagged = tagSet != asn1Spec.tagSet + + if isUntagged: + fullPosition = substrate.markedPosition + currentPosition = substrate.tell() + + substrate.seek(fullPosition, os.SEEK_SET) + length += currentPosition - fullPosition + + if LOG: + for chunk in peekIntoStream(substrate, length): + if isinstance(chunk, SubstrateUnderrunError): + yield chunk + LOG('decoding as untagged ANY, substrate ' + '%s' % debug.hexdump(chunk)) + + if substrateFun: + for chunk in substrateFun( + self._createComponent(asn1Spec, tagSet, noValue, **options), + substrate, length, options): + yield chunk + + return + + for chunk in readFromStream(substrate, length, options): + if isinstance(chunk, SubstrateUnderrunError): + yield chunk + + yield self._createComponent(asn1Spec, tagSet, chunk, **options) + + def indefLenValueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + if asn1Spec is None: + isTagged = False + + elif asn1Spec.__class__ is tagmap.TagMap: + isTagged = tagSet in asn1Spec.tagMap + + else: + isTagged = tagSet == asn1Spec.tagSet + + if isTagged: + # tagged Any type -- consume header substrate + chunk = null + + if LOG: + LOG('decoding as tagged ANY') + + else: + # TODO: Seems not to be tested + fullPosition = substrate.markedPosition + currentPosition = substrate.tell() + + substrate.seek(fullPosition, os.SEEK_SET) + for chunk in readFromStream(substrate, currentPosition - fullPosition, options): + if isinstance(chunk, SubstrateUnderrunError): + yield chunk + + if LOG: + LOG('decoding as untagged ANY, header substrate %s' % debug.hexdump(chunk)) + + # Any components do not inherit initial tag + asn1Spec = self.protoComponent + + if substrateFun and substrateFun is not self.substrateCollector: + asn1Object = self._createComponent( + asn1Spec, tagSet, noValue, **options) + + for chunk in substrateFun( + asn1Object, chunk + substrate, length + len(chunk), options): + yield chunk + + return + + if LOG: + LOG('assembling constructed serialization') + + # All inner fragments are of the same type, treat them as octet string + substrateFun = self.substrateCollector + + while True: # loop over fragments + + for component in decodeFun( + substrate, asn1Spec, substrateFun=substrateFun, + allowEoo=True, **options): + + if isinstance(component, SubstrateUnderrunError): + yield component + + if component is eoo.endOfOctets: + break + + if component is eoo.endOfOctets: + break + + chunk += component + + if substrateFun: + yield chunk # TODO: Weird + + else: + yield self._createComponent(asn1Spec, tagSet, chunk, **options) + + +# character string types +class UTF8StringPayloadDecoder(OctetStringPayloadDecoder): + protoComponent = char.UTF8String() + + +class NumericStringPayloadDecoder(OctetStringPayloadDecoder): + protoComponent = char.NumericString() + + +class PrintableStringPayloadDecoder(OctetStringPayloadDecoder): + protoComponent = char.PrintableString() + + +class TeletexStringPayloadDecoder(OctetStringPayloadDecoder): + protoComponent = char.TeletexString() + + +class VideotexStringPayloadDecoder(OctetStringPayloadDecoder): + protoComponent = char.VideotexString() + + +class IA5StringPayloadDecoder(OctetStringPayloadDecoder): + protoComponent = char.IA5String() + + +class GraphicStringPayloadDecoder(OctetStringPayloadDecoder): + protoComponent = char.GraphicString() + + +class VisibleStringPayloadDecoder(OctetStringPayloadDecoder): + protoComponent = char.VisibleString() + + +class GeneralStringPayloadDecoder(OctetStringPayloadDecoder): + protoComponent = char.GeneralString() + + +class UniversalStringPayloadDecoder(OctetStringPayloadDecoder): + protoComponent = char.UniversalString() + + +class BMPStringPayloadDecoder(OctetStringPayloadDecoder): + protoComponent = char.BMPString() + + +# "useful" types +class ObjectDescriptorPayloadDecoder(OctetStringPayloadDecoder): + protoComponent = useful.ObjectDescriptor() + + +class GeneralizedTimePayloadDecoder(OctetStringPayloadDecoder): + protoComponent = useful.GeneralizedTime() + + +class UTCTimePayloadDecoder(OctetStringPayloadDecoder): + protoComponent = useful.UTCTime() + + +TAG_MAP = { + univ.Integer.tagSet: IntegerPayloadDecoder(), + univ.Boolean.tagSet: BooleanPayloadDecoder(), + univ.BitString.tagSet: BitStringPayloadDecoder(), + univ.OctetString.tagSet: OctetStringPayloadDecoder(), + univ.Null.tagSet: NullPayloadDecoder(), + univ.ObjectIdentifier.tagSet: ObjectIdentifierPayloadDecoder(), + univ.Enumerated.tagSet: IntegerPayloadDecoder(), + univ.Real.tagSet: RealPayloadDecoder(), + univ.Sequence.tagSet: SequenceOrSequenceOfPayloadDecoder(), # conflicts with SequenceOf + univ.Set.tagSet: SetOrSetOfPayloadDecoder(), # conflicts with SetOf + univ.Choice.tagSet: ChoicePayloadDecoder(), # conflicts with Any + # character string types + char.UTF8String.tagSet: UTF8StringPayloadDecoder(), + char.NumericString.tagSet: NumericStringPayloadDecoder(), + char.PrintableString.tagSet: PrintableStringPayloadDecoder(), + char.TeletexString.tagSet: TeletexStringPayloadDecoder(), + char.VideotexString.tagSet: VideotexStringPayloadDecoder(), + char.IA5String.tagSet: IA5StringPayloadDecoder(), + char.GraphicString.tagSet: GraphicStringPayloadDecoder(), + char.VisibleString.tagSet: VisibleStringPayloadDecoder(), + char.GeneralString.tagSet: GeneralStringPayloadDecoder(), + char.UniversalString.tagSet: UniversalStringPayloadDecoder(), + char.BMPString.tagSet: BMPStringPayloadDecoder(), + # useful types + useful.ObjectDescriptor.tagSet: ObjectDescriptorPayloadDecoder(), + useful.GeneralizedTime.tagSet: GeneralizedTimePayloadDecoder(), + useful.UTCTime.tagSet: UTCTimePayloadDecoder() +} + +# Type-to-codec map for ambiguous ASN.1 types +TYPE_MAP = { + univ.Set.typeId: SetPayloadDecoder(), + univ.SetOf.typeId: SetOfPayloadDecoder(), + univ.Sequence.typeId: SequencePayloadDecoder(), + univ.SequenceOf.typeId: SequenceOfPayloadDecoder(), + univ.Choice.typeId: ChoicePayloadDecoder(), + univ.Any.typeId: AnyPayloadDecoder() +} + +# deprecated aliases, https://github.com/pyasn1/pyasn1/issues/9 +tagMap = TAG_MAP +typeMap = TYPE_MAP + +# Put in non-ambiguous types for faster codec lookup +for typeDecoder in TAG_MAP.values(): + if typeDecoder.protoComponent is not None: + typeId = typeDecoder.protoComponent.__class__.typeId + if typeId is not None and typeId not in TYPE_MAP: + TYPE_MAP[typeId] = typeDecoder + + +(stDecodeTag, + stDecodeLength, + stGetValueDecoder, + stGetValueDecoderByAsn1Spec, + stGetValueDecoderByTag, + stTryAsExplicitTag, + stDecodeValue, + stDumpRawValue, + stErrorCondition, + stStop) = [x for x in range(10)] + + +EOO_SENTINEL = ints2octs((0, 0)) + + +class SingleItemDecoder(object): + defaultErrorState = stErrorCondition + #defaultErrorState = stDumpRawValue + defaultRawDecoder = AnyPayloadDecoder() + + supportIndefLength = True + + TAG_MAP = TAG_MAP + TYPE_MAP = TYPE_MAP + + def __init__(self, tagMap=_MISSING, typeMap=_MISSING, **ignored): + self._tagMap = tagMap if tagMap is not _MISSING else self.TAG_MAP + self._typeMap = typeMap if typeMap is not _MISSING else self.TYPE_MAP + + # Tag & TagSet objects caches + self._tagCache = {} + self._tagSetCache = {} + + def __call__(self, substrate, asn1Spec=None, + tagSet=None, length=None, state=stDecodeTag, + decodeFun=None, substrateFun=None, + **options): + + allowEoo = options.pop('allowEoo', False) + + if LOG: + LOG('decoder called at scope %s with state %d, working with up ' + 'to %s octets of substrate: ' + '%s' % (debug.scope, state, length, substrate)) + + # Look for end-of-octets sentinel + if allowEoo and self.supportIndefLength: + + for eoo_candidate in readFromStream(substrate, 2, options): + if isinstance(eoo_candidate, SubstrateUnderrunError): + yield eoo_candidate + + if eoo_candidate == EOO_SENTINEL: + if LOG: + LOG('end-of-octets sentinel found') + yield eoo.endOfOctets + return + + else: + substrate.seek(-2, os.SEEK_CUR) + + tagMap = self._tagMap + typeMap = self._typeMap + tagCache = self._tagCache + tagSetCache = self._tagSetCache + + value = noValue + + substrate.markedPosition = substrate.tell() + + while state is not stStop: + + if state is stDecodeTag: + # Decode tag + isShortTag = True + + for firstByte in readFromStream(substrate, 1, options): + if isinstance(firstByte, SubstrateUnderrunError): + yield firstByte + + firstOctet = ord(firstByte) + + try: + lastTag = tagCache[firstOctet] + + except KeyError: + integerTag = firstOctet + tagClass = integerTag & 0xC0 + tagFormat = integerTag & 0x20 + tagId = integerTag & 0x1F + + if tagId == 0x1F: + isShortTag = False + lengthOctetIdx = 0 + tagId = 0 + + while True: + for integerByte in readFromStream(substrate, 1, options): + if isinstance(integerByte, SubstrateUnderrunError): + yield integerByte + + if not integerByte: + raise error.SubstrateUnderrunError( + 'Short octet stream on long tag decoding' + ) + + integerTag = ord(integerByte) + lengthOctetIdx += 1 + tagId <<= 7 + tagId |= (integerTag & 0x7F) + + if not integerTag & 0x80: + break + + lastTag = tag.Tag( + tagClass=tagClass, tagFormat=tagFormat, tagId=tagId + ) + + if isShortTag: + # cache short tags + tagCache[firstOctet] = lastTag + + if tagSet is None: + if isShortTag: + try: + tagSet = tagSetCache[firstOctet] + + except KeyError: + # base tag not recovered + tagSet = tag.TagSet((), lastTag) + tagSetCache[firstOctet] = tagSet + else: + tagSet = tag.TagSet((), lastTag) + + else: + tagSet = lastTag + tagSet + + state = stDecodeLength + + if LOG: + LOG('tag decoded into %s, decoding length' % tagSet) + + if state is stDecodeLength: + # Decode length + for firstOctet in readFromStream(substrate, 1, options): + if isinstance(firstOctet, SubstrateUnderrunError): + yield firstOctet + + firstOctet = ord(firstOctet) + + if firstOctet < 128: + length = firstOctet + + elif firstOctet > 128: + size = firstOctet & 0x7F + # encoded in size bytes + for encodedLength in readFromStream(substrate, size, options): + if isinstance(encodedLength, SubstrateUnderrunError): + yield encodedLength + encodedLength = list(encodedLength) + # missing check on maximum size, which shouldn't be a + # problem, we can handle more than is possible + if len(encodedLength) != size: + raise error.SubstrateUnderrunError( + '%s<%s at %s' % (size, len(encodedLength), tagSet) + ) + + length = 0 + for lengthOctet in encodedLength: + length <<= 8 + length |= oct2int(lengthOctet) + size += 1 + + else: # 128 means indefinite + length = -1 + + if length == -1 and not self.supportIndefLength: + raise error.PyAsn1Error('Indefinite length encoding not supported by this codec') + + state = stGetValueDecoder + + if LOG: + LOG('value length decoded into %d' % length) + + if state is stGetValueDecoder: + if asn1Spec is None: + state = stGetValueDecoderByTag + + else: + state = stGetValueDecoderByAsn1Spec + # + # There're two ways of creating subtypes in ASN.1 what influences + # decoder operation. These methods are: + # 1) Either base types used in or no IMPLICIT tagging has been + # applied on subtyping. + # 2) Subtype syntax drops base type information (by means of + # IMPLICIT tagging. + # The first case allows for complete tag recovery from substrate + # while the second one requires original ASN.1 type spec for + # decoding. + # + # In either case a set of tags (tagSet) is coming from substrate + # in an incremental, tag-by-tag fashion (this is the case of + # EXPLICIT tag which is most basic). Outermost tag comes first + # from the wire. + # + if state is stGetValueDecoderByTag: + try: + concreteDecoder = tagMap[tagSet] + + except KeyError: + concreteDecoder = None + + if concreteDecoder: + state = stDecodeValue + + else: + try: + concreteDecoder = tagMap[tagSet[:1]] + + except KeyError: + concreteDecoder = None + + if concreteDecoder: + state = stDecodeValue + else: + state = stTryAsExplicitTag + + if LOG: + LOG('codec %s chosen by a built-in type, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as explicit tag')) + debug.scope.push(concreteDecoder is None and '?' or concreteDecoder.protoComponent.__class__.__name__) + + if state is stGetValueDecoderByAsn1Spec: + + if asn1Spec.__class__ is tagmap.TagMap: + try: + chosenSpec = asn1Spec[tagSet] + + except KeyError: + chosenSpec = None + + if LOG: + LOG('candidate ASN.1 spec is a map of:') + + for firstOctet, v in asn1Spec.presentTypes.items(): + LOG(' %s -> %s' % (firstOctet, v.__class__.__name__)) + + if asn1Spec.skipTypes: + LOG('but neither of: ') + for firstOctet, v in asn1Spec.skipTypes.items(): + LOG(' %s -> %s' % (firstOctet, v.__class__.__name__)) + LOG('new candidate ASN.1 spec is %s, chosen by %s' % (chosenSpec is None and '<none>' or chosenSpec.prettyPrintType(), tagSet)) + + elif tagSet == asn1Spec.tagSet or tagSet in asn1Spec.tagMap: + chosenSpec = asn1Spec + if LOG: + LOG('candidate ASN.1 spec is %s' % asn1Spec.__class__.__name__) + + else: + chosenSpec = None + + if chosenSpec is not None: + try: + # ambiguous type or just faster codec lookup + concreteDecoder = typeMap[chosenSpec.typeId] + + if LOG: + LOG('value decoder chosen for an ambiguous type by type ID %s' % (chosenSpec.typeId,)) + + except KeyError: + # use base type for codec lookup to recover untagged types + baseTagSet = tag.TagSet(chosenSpec.tagSet.baseTag, chosenSpec.tagSet.baseTag) + try: + # base type or tagged subtype + concreteDecoder = tagMap[baseTagSet] + + if LOG: + LOG('value decoder chosen by base %s' % (baseTagSet,)) + + except KeyError: + concreteDecoder = None + + if concreteDecoder: + asn1Spec = chosenSpec + state = stDecodeValue + + else: + state = stTryAsExplicitTag + + else: + concreteDecoder = None + state = stTryAsExplicitTag + + if LOG: + LOG('codec %s chosen by ASN.1 spec, decoding %s' % (state is stDecodeValue and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as explicit tag')) + debug.scope.push(chosenSpec is None and '?' or chosenSpec.__class__.__name__) + + if state is stDecodeValue: + if not options.get('recursiveFlag', True) and not substrateFun: # deprecate this + substrateFun = lambda a, b, c: (a, b[:c]) + + original_position = substrate.tell() + + if length == -1: # indef length + for value in concreteDecoder.indefLenValueDecoder( + substrate, asn1Spec, + tagSet, length, stGetValueDecoder, + self, substrateFun, **options): + if isinstance(value, SubstrateUnderrunError): + yield value + + else: + for value in concreteDecoder.valueDecoder( + substrate, asn1Spec, + tagSet, length, stGetValueDecoder, + self, substrateFun, **options): + if isinstance(value, SubstrateUnderrunError): + yield value + + bytesRead = substrate.tell() - original_position + if bytesRead != length: + raise PyAsn1Error( + "Read %s bytes instead of expected %s." % (bytesRead, length)) + + if LOG: + LOG('codec %s yields type %s, value:\n%s\n...' % ( + concreteDecoder.__class__.__name__, value.__class__.__name__, + isinstance(value, base.Asn1Item) and value.prettyPrint() or value)) + + state = stStop + break + + if state is stTryAsExplicitTag: + if (tagSet and + tagSet[0].tagFormat == tag.tagFormatConstructed and + tagSet[0].tagClass != tag.tagClassUniversal): + # Assume explicit tagging + concreteDecoder = rawPayloadDecoder + state = stDecodeValue + + else: + concreteDecoder = None + state = self.defaultErrorState + + if LOG: + LOG('codec %s chosen, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as failure')) + + if state is stDumpRawValue: + concreteDecoder = self.defaultRawDecoder + + if LOG: + LOG('codec %s chosen, decoding value' % concreteDecoder.__class__.__name__) + + state = stDecodeValue + + if state is stErrorCondition: + raise error.PyAsn1Error( + '%s not in asn1Spec: %r' % (tagSet, asn1Spec) + ) + + if LOG: + debug.scope.pop() + LOG('decoder left scope %s, call completed' % debug.scope) + + yield value + + +class StreamingDecoder(object): + """Create an iterator that turns BER/CER/DER byte stream into ASN.1 objects. + + On each iteration, consume whatever BER/CER/DER serialization is + available in the `substrate` stream-like object and turns it into + one or more, possibly nested, ASN.1 objects. + + Parameters + ---------- + substrate: :py:class:`file`, :py:class:`io.BytesIO` + BER/CER/DER serialization in form of a byte stream + + Keyword Args + ------------ + asn1Spec: :py:class:`~pyasn1.type.base.PyAsn1Item` + 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. One of the reasons why `asn1Spec` may + me required is that ASN.1 structure is encoded in the *IMPLICIT* + tagging mode. + + Yields + ------ + : :py:class:`~pyasn1.type.base.PyAsn1Item`, :py:class:`~pyasn1.error.SubstrateUnderrunError` + Decoded ASN.1 object (possibly, nested) or + :py:class:`~pyasn1.error.SubstrateUnderrunError` object indicating + insufficient BER/CER/DER serialization on input to fully recover ASN.1 + objects from it. + + In the latter case the caller is advised to ensure some more data in + the input stream, then call the iterator again. The decoder will resume + the decoding process using the newly arrived data. + + The `context` property of :py:class:`~pyasn1.error.SubstrateUnderrunError` + object might hold a reference to the partially populated ASN.1 object + being reconstructed. + + Raises + ------ + ~pyasn1.error.PyAsn1Error, ~pyasn1.error.EndOfStreamError + `PyAsn1Error` on deserialization error, `EndOfStreamError` on + premature stream closure. + + Examples + -------- + Decode BER serialisation without ASN.1 schema + + .. code-block:: pycon + + >>> stream = io.BytesIO( + ... b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03') + >>> + >>> for asn1Object in StreamingDecoder(stream): + ... print(asn1Object) + >>> + SequenceOf: + 1 2 3 + + Decode BER serialisation with ASN.1 schema + + .. code-block:: pycon + + >>> stream = io.BytesIO( + ... b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03') + >>> + >>> schema = SequenceOf(componentType=Integer()) + >>> + >>> decoder = StreamingDecoder(stream, asn1Spec=schema) + >>> for asn1Object in decoder: + ... print(asn1Object) + >>> + SequenceOf: + 1 2 3 + """ + + SINGLE_ITEM_DECODER = SingleItemDecoder + + def __init__(self, substrate, asn1Spec=None, **options): + self._singleItemDecoder = self.SINGLE_ITEM_DECODER(**options) + self._substrate = asSeekableStream(substrate) + self._asn1Spec = asn1Spec + self._options = options + + def __iter__(self): + while True: + for asn1Object in self._singleItemDecoder( + self._substrate, self._asn1Spec, **self._options): + yield asn1Object + + for chunk in isEndOfStream(self._substrate): + if isinstance(chunk, SubstrateUnderrunError): + yield + + break + + if chunk: + break + + +class Decoder(object): + """Create a BER decoder object. + + Parse BER/CER/DER octet-stream into one, possibly nested, ASN.1 object. + """ + STREAMING_DECODER = StreamingDecoder + + @classmethod + def __call__(cls, substrate, asn1Spec=None, **options): + """Turns BER/CER/DER octet stream into an ASN.1 object. + + Takes BER/CER/DER octet-stream in form of :py:class:`bytes` (Python 3) + or :py:class:`str` (Python 2) 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) + BER/CER/DER octet-stream to parse + + Keyword Args + ------------ + asn1Spec: :py:class:`~pyasn1.type.base.PyAsn1Item` + A pyasn1 type object (:py:class:`~pyasn1.type.base.PyAsn1Item` + derivative) 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 :py:class:`~pyasn1.type.base.PyAsn1Item` object + recovered from BER/CER/DER substrate and the unprocessed trailing + portion of the `substrate` (may be empty) + + Raises + ------ + : :py:class:`~pyasn1.error.PyAsn1Error` + :py:class:`~pyasn1.error.SubstrateUnderrunError` on insufficient + input or :py:class:`~pyasn1.error.PyAsn1Error` on decoding error. + + Examples + -------- + Decode BER/CER/DER serialisation without ASN.1 schema + + .. code-block:: pycon + + >>> s, unprocessed = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03') + >>> str(s) + SequenceOf: + 1 2 3 + + Decode BER/CER/DER serialisation with ASN.1 schema + + .. code-block:: pycon + + >>> seq = SequenceOf(componentType=Integer()) + >>> s, unprocessed = decode( + b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq) + >>> str(s) + SequenceOf: + 1 2 3 + + """ + substrate = asSeekableStream(substrate) + + streamingDecoder = cls.STREAMING_DECODER( + substrate, asn1Spec, **options) + + for asn1Object in streamingDecoder: + if isinstance(asn1Object, SubstrateUnderrunError): + raise error.SubstrateUnderrunError('Short substrate on input') + + try: + tail = next(readFromStream(substrate)) + + except error.EndOfStreamError: + tail = null + + return asn1Object, tail + + +#: Turns BER octet stream into an ASN.1 object. +#: +#: Takes BER octet-stream 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) +#: BER octet-stream +#: +#: Keyword Args +#: ------------ +#: 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 BER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative) +#: and the unprocessed trailing portion of the *substrate* (may be empty) +#: +#: Raises +#: ------ +#: ~pyasn1.error.PyAsn1Error, ~pyasn1.error.SubstrateUnderrunError +#: On decoding errors +#: +#: Notes +#: ----- +#: This function is deprecated. Please use :py:class:`Decoder` or +#: :py:class:`StreamingDecoder` class instance. +#: +#: Examples +#: -------- +#: Decode BER serialisation without ASN.1 schema +#: +#: .. code-block:: pycon +#: +#: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03') +#: >>> str(s) +#: SequenceOf: +#: 1 2 3 +#: +#: Decode BER serialisation with ASN.1 schema +#: +#: .. code-block:: pycon +#: +#: >>> seq = SequenceOf(componentType=Integer()) +#: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq) +#: >>> str(s) +#: SequenceOf: +#: 1 2 3 +#: +decode = Decoder() diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/ber/encoder.py b/contrib/python/pyasn1/py3/pyasn1/codec/ber/encoder.py new file mode 100644 index 0000000000..c59b43e455 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/ber/encoder.py @@ -0,0 +1,917 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys + +from pyasn1 import debug +from pyasn1 import error +from pyasn1.codec.ber import eoo +from pyasn1.compat import _MISSING +from pyasn1.compat.integer import to_bytes +from pyasn1.compat.octets import (int2oct, oct2int, ints2octs, null, + str2octs, isOctetsType) +from pyasn1.type import char +from pyasn1.type import tag +from pyasn1.type import univ +from pyasn1.type import useful + +__all__ = ['Encoder', 'encode'] + +LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_ENCODER) + + +class AbstractItemEncoder(object): + supportIndefLenMode = True + + # An outcome of otherwise legit call `encodeFun(eoo.endOfOctets)` + eooIntegerSubstrate = (0, 0) + eooOctetsSubstrate = ints2octs(eooIntegerSubstrate) + + # noinspection PyMethodMayBeStatic + def encodeTag(self, singleTag, isConstructed): + tagClass, tagFormat, tagId = singleTag + encodedTag = tagClass | tagFormat + if isConstructed: + encodedTag |= tag.tagFormatConstructed + + if tagId < 31: + return encodedTag | tagId, + + else: + substrate = tagId & 0x7f, + + tagId >>= 7 + + while tagId: + substrate = (0x80 | (tagId & 0x7f),) + substrate + tagId >>= 7 + + return (encodedTag | 0x1F,) + substrate + + def encodeLength(self, length, defMode): + if not defMode and self.supportIndefLenMode: + return (0x80,) + + if length < 0x80: + return length, + + else: + substrate = () + while length: + substrate = (length & 0xff,) + substrate + length >>= 8 + + substrateLen = len(substrate) + + if substrateLen > 126: + raise error.PyAsn1Error('Length octets overflow (%d)' % substrateLen) + + return (0x80 | substrateLen,) + substrate + + def encodeValue(self, value, asn1Spec, encodeFun, **options): + raise error.PyAsn1Error('Not implemented') + + def encode(self, value, asn1Spec=None, encodeFun=None, **options): + + if asn1Spec is None: + tagSet = value.tagSet + else: + tagSet = asn1Spec.tagSet + + # untagged item? + if not tagSet: + substrate, isConstructed, isOctets = self.encodeValue( + value, asn1Spec, encodeFun, **options + ) + return substrate + + defMode = options.get('defMode', True) + + substrate = null + + for idx, singleTag in enumerate(tagSet.superTags): + + defModeOverride = defMode + + # base tag? + if not idx: + try: + substrate, isConstructed, isOctets = self.encodeValue( + value, asn1Spec, encodeFun, **options + ) + + except error.PyAsn1Error: + exc = sys.exc_info() + raise error.PyAsn1Error( + 'Error encoding %r: %s' % (value, exc[1])) + + if LOG: + LOG('encoded %svalue %s into %s' % ( + isConstructed and 'constructed ' or '', value, substrate + )) + + if not substrate and isConstructed and options.get('ifNotEmpty', False): + return substrate + + if not isConstructed: + defModeOverride = True + + if LOG: + LOG('overridden encoding mode into definitive for primitive type') + + header = self.encodeTag(singleTag, isConstructed) + + if LOG: + LOG('encoded %stag %s into %s' % ( + isConstructed and 'constructed ' or '', + singleTag, debug.hexdump(ints2octs(header)))) + + header += self.encodeLength(len(substrate), defModeOverride) + + if LOG: + LOG('encoded %s octets (tag + payload) into %s' % ( + len(substrate), debug.hexdump(ints2octs(header)))) + + if isOctets: + substrate = ints2octs(header) + substrate + + if not defModeOverride: + substrate += self.eooOctetsSubstrate + + else: + substrate = header + substrate + + if not defModeOverride: + substrate += self.eooIntegerSubstrate + + if not isOctets: + substrate = ints2octs(substrate) + + return substrate + + +class EndOfOctetsEncoder(AbstractItemEncoder): + def encodeValue(self, value, asn1Spec, encodeFun, **options): + return null, False, True + + +class BooleanEncoder(AbstractItemEncoder): + supportIndefLenMode = False + + def encodeValue(self, value, asn1Spec, encodeFun, **options): + return value and (1,) or (0,), False, False + + +class IntegerEncoder(AbstractItemEncoder): + supportIndefLenMode = False + supportCompactZero = False + + def encodeValue(self, value, asn1Spec, encodeFun, **options): + if value == 0: + if LOG: + LOG('encoding %spayload for zero INTEGER' % ( + self.supportCompactZero and 'no ' or '' + )) + + # de-facto way to encode zero + if self.supportCompactZero: + return (), False, False + else: + return (0,), False, False + + return to_bytes(int(value), signed=True), False, True + + +class BitStringEncoder(AbstractItemEncoder): + def encodeValue(self, value, asn1Spec, encodeFun, **options): + if asn1Spec is not None: + # TODO: try to avoid ASN.1 schema instantiation + value = asn1Spec.clone(value) + + valueLength = len(value) + if valueLength % 8: + alignedValue = value << (8 - valueLength % 8) + else: + alignedValue = value + + maxChunkSize = options.get('maxChunkSize', 0) + if not maxChunkSize or len(alignedValue) <= maxChunkSize * 8: + substrate = alignedValue.asOctets() + return int2oct(len(substrate) * 8 - valueLength) + substrate, False, True + + if LOG: + LOG('encoding into up to %s-octet chunks' % maxChunkSize) + + baseTag = value.tagSet.baseTag + + # strip off explicit tags + if baseTag: + tagSet = tag.TagSet(baseTag, baseTag) + + else: + tagSet = tag.TagSet() + + alignedValue = alignedValue.clone(tagSet=tagSet) + + stop = 0 + substrate = null + while stop < valueLength: + start = stop + stop = min(start + maxChunkSize * 8, valueLength) + substrate += encodeFun(alignedValue[start:stop], asn1Spec, **options) + + return substrate, True, True + + +class OctetStringEncoder(AbstractItemEncoder): + + def encodeValue(self, value, asn1Spec, encodeFun, **options): + + if asn1Spec is None: + substrate = value.asOctets() + + elif not isOctetsType(value): + substrate = asn1Spec.clone(value).asOctets() + + else: + substrate = value + + maxChunkSize = options.get('maxChunkSize', 0) + + if not maxChunkSize or len(substrate) <= maxChunkSize: + return substrate, False, True + + if LOG: + LOG('encoding into up to %s-octet chunks' % maxChunkSize) + + # strip off explicit tags for inner chunks + + if asn1Spec is None: + baseTag = value.tagSet.baseTag + + # strip off explicit tags + if baseTag: + tagSet = tag.TagSet(baseTag, baseTag) + + else: + tagSet = tag.TagSet() + + asn1Spec = value.clone(tagSet=tagSet) + + elif not isOctetsType(value): + baseTag = asn1Spec.tagSet.baseTag + + # strip off explicit tags + if baseTag: + tagSet = tag.TagSet(baseTag, baseTag) + + else: + tagSet = tag.TagSet() + + asn1Spec = asn1Spec.clone(tagSet=tagSet) + + pos = 0 + substrate = null + + while True: + chunk = value[pos:pos + maxChunkSize] + if not chunk: + break + + substrate += encodeFun(chunk, asn1Spec, **options) + pos += maxChunkSize + + return substrate, True, True + + +class NullEncoder(AbstractItemEncoder): + supportIndefLenMode = False + + def encodeValue(self, value, asn1Spec, encodeFun, **options): + return null, False, True + + +class ObjectIdentifierEncoder(AbstractItemEncoder): + supportIndefLenMode = False + + def encodeValue(self, value, asn1Spec, encodeFun, **options): + if asn1Spec is not None: + value = asn1Spec.clone(value) + + oid = value.asTuple() + + # Build the first pair + try: + first = oid[0] + second = oid[1] + + except IndexError: + raise error.PyAsn1Error('Short OID %s' % (value,)) + + if 0 <= second <= 39: + if first == 1: + oid = (second + 40,) + oid[2:] + elif first == 0: + oid = (second,) + oid[2:] + elif first == 2: + oid = (second + 80,) + oid[2:] + else: + raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,)) + + elif first == 2: + oid = (second + 80,) + oid[2:] + + else: + raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,)) + + octets = () + + # Cycle through subIds + for subOid in oid: + if 0 <= subOid <= 127: + # Optimize for the common case + octets += (subOid,) + + elif subOid > 127: + # Pack large Sub-Object IDs + res = (subOid & 0x7f,) + subOid >>= 7 + + while subOid: + res = (0x80 | (subOid & 0x7f),) + res + subOid >>= 7 + + # Add packed Sub-Object ID to resulted Object ID + octets += res + + else: + raise error.PyAsn1Error('Negative OID arc %s at %s' % (subOid, value)) + + return octets, False, False + + +class RealEncoder(AbstractItemEncoder): + supportIndefLenMode = False + 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 # exponent 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] + exponent = [e, e, e] + sign = 1 + encbase = 2 + e = float('inf') + + for i in range(3): + (sign, + mantissa[i], + encBase[i], + exponent[i]) = self._dropFloatingPoint(mantissa[i], encBase[i], exponent[i]) + + if abs(exponent[i]) < abs(e) or (abs(exponent[i]) == abs(e) and mantissa[i] < m): + e = exponent[i] + m = int(mantissa[i]) + encbase = encBase[i] + + if LOG: + LOG('automatically chosen REAL encoding base %s, sign %s, mantissa %s, ' + 'exponent %s' % (encbase, sign, m, e)) + + return sign, m, encbase, e + + def encodeValue(self, value, asn1Spec, encodeFun, **options): + if asn1Spec is not None: + value = asn1Spec.clone(value) + + if value.isPlusInf: + return (0x40,), False, False + + if value.isMinusInf: + return (0x41,), False, False + + m, b, e = value + + if not m: + return null, False, True + + if b == 10: + if LOG: + LOG('encoding REAL into character form') + + return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), False, True + + elif b == 2: + fo = 0x80 # binary encoding + ms, m, encbase, e = self._chooseEncBase(value) + + if ms < 0: # mantissa sign + fo |= 0x40 # sign bit + + # exponent & mantissa normalization + if encbase == 2: + while m & 0x1 == 0: + m >>= 1 + e += 1 + + elif encbase == 8: + while m & 0x7 == 0: + m >>= 3 + e += 1 + fo |= 0x10 + + else: # encbase = 16 + while m & 0xf == 0: + m >>= 4 + e += 1 + fo |= 0x20 + + sf = 0 # scale factor + + while m & 0x1 == 0: + m >>= 1 + sf += 1 + + if sf > 3: + raise error.PyAsn1Error('Scale factor overflow') # bug if raised + + fo |= sf << 2 + eo = null + if e == 0 or e == -1: + eo = int2oct(e & 0xff) + + else: + while e not in (0, -1): + eo = int2oct(e & 0xff) + eo + e >>= 8 + + if e == 0 and eo and oct2int(eo[0]) & 0x80: + eo = int2oct(0) + eo + + if e == -1 and eo and not (oct2int(eo[0]) & 0x80): + eo = int2oct(0xff) + eo + + n = len(eo) + if n > 0xff: + raise error.PyAsn1Error('Real exponent overflow') + + if n == 1: + pass + + elif n == 2: + fo |= 1 + + elif n == 3: + fo |= 2 + + else: + fo |= 3 + eo = int2oct(n & 0xff) + eo + + po = null + + while m: + po = int2oct(m & 0xff) + po + m >>= 8 + + substrate = int2oct(fo) + eo + po + + return substrate, False, True + + else: + raise error.PyAsn1Error('Prohibited Real base %s' % b) + + +class SequenceEncoder(AbstractItemEncoder): + omitEmptyOptionals = False + + # TODO: handling three flavors of input is too much -- split over codecs + + def encodeValue(self, value, asn1Spec, encodeFun, **options): + + substrate = null + + omitEmptyOptionals = options.get( + 'omitEmptyOptionals', self.omitEmptyOptionals) + + if LOG: + LOG('%sencoding empty OPTIONAL components' % ( + omitEmptyOptionals and 'not ' or '')) + + if asn1Spec is None: + # instance of ASN.1 schema + inconsistency = value.isInconsistent + if inconsistency: + raise inconsistency + + namedTypes = value.componentType + + for idx, component in enumerate(value.values()): + if namedTypes: + namedType = namedTypes[idx] + + if namedType.isOptional and not component.isValue: + if LOG: + LOG('not encoding OPTIONAL component %r' % (namedType,)) + continue + + if namedType.isDefaulted and component == namedType.asn1Object: + if LOG: + LOG('not encoding DEFAULT component %r' % (namedType,)) + continue + + if omitEmptyOptionals: + options.update(ifNotEmpty=namedType.isOptional) + + # wrap open type blob if needed + if namedTypes and namedType.openType: + + wrapType = namedType.asn1Object + + if wrapType.typeId in ( + univ.SetOf.typeId, univ.SequenceOf.typeId): + + substrate += encodeFun( + component, asn1Spec, + **dict(options, wrapType=wrapType.componentType)) + + else: + chunk = encodeFun(component, asn1Spec, **options) + + if wrapType.isSameTypeWith(component): + substrate += chunk + + else: + substrate += encodeFun(chunk, wrapType, **options) + + if LOG: + LOG('wrapped with wrap type %r' % (wrapType,)) + + else: + substrate += encodeFun(component, asn1Spec, **options) + + else: + # bare Python value + ASN.1 schema + for idx, namedType in enumerate(asn1Spec.componentType.namedTypes): + + try: + component = value[namedType.name] + + except KeyError: + raise error.PyAsn1Error('Component name "%s" not found in %r' % ( + namedType.name, value)) + + if namedType.isOptional and namedType.name not in value: + if LOG: + LOG('not encoding OPTIONAL component %r' % (namedType,)) + continue + + if namedType.isDefaulted and component == namedType.asn1Object: + if LOG: + LOG('not encoding DEFAULT component %r' % (namedType,)) + continue + + if omitEmptyOptionals: + options.update(ifNotEmpty=namedType.isOptional) + + componentSpec = namedType.asn1Object + + # wrap open type blob if needed + if namedType.openType: + + if componentSpec.typeId in ( + univ.SetOf.typeId, univ.SequenceOf.typeId): + + substrate += encodeFun( + component, componentSpec, + **dict(options, wrapType=componentSpec.componentType)) + + else: + chunk = encodeFun(component, componentSpec, **options) + + if componentSpec.isSameTypeWith(component): + substrate += chunk + + else: + substrate += encodeFun(chunk, componentSpec, **options) + + if LOG: + LOG('wrapped with wrap type %r' % (componentSpec,)) + + else: + substrate += encodeFun(component, componentSpec, **options) + + return substrate, True, True + + +class SequenceOfEncoder(AbstractItemEncoder): + def _encodeComponents(self, value, asn1Spec, encodeFun, **options): + + if asn1Spec is None: + inconsistency = value.isInconsistent + if inconsistency: + raise inconsistency + + else: + asn1Spec = asn1Spec.componentType + + chunks = [] + + wrapType = options.pop('wrapType', None) + + for idx, component in enumerate(value): + chunk = encodeFun(component, asn1Spec, **options) + + if (wrapType is not None and + not wrapType.isSameTypeWith(component)): + # wrap encoded value with wrapper container (e.g. ANY) + chunk = encodeFun(chunk, wrapType, **options) + + if LOG: + LOG('wrapped with wrap type %r' % (wrapType,)) + + chunks.append(chunk) + + return chunks + + def encodeValue(self, value, asn1Spec, encodeFun, **options): + chunks = self._encodeComponents( + value, asn1Spec, encodeFun, **options) + + return null.join(chunks), True, True + + +class ChoiceEncoder(AbstractItemEncoder): + def encodeValue(self, value, asn1Spec, encodeFun, **options): + if asn1Spec is None: + component = value.getComponent() + else: + names = [namedType.name for namedType in asn1Spec.componentType.namedTypes + if namedType.name in value] + if len(names) != 1: + raise error.PyAsn1Error('%s components for Choice at %r' % (len(names) and 'Multiple ' or 'None ', value)) + + name = names[0] + + component = value[name] + asn1Spec = asn1Spec[name] + + return encodeFun(component, asn1Spec, **options), True, True + + +class AnyEncoder(OctetStringEncoder): + def encodeValue(self, value, asn1Spec, encodeFun, **options): + if asn1Spec is None: + value = value.asOctets() + elif not isOctetsType(value): + value = asn1Spec.clone(value).asOctets() + + return value, not options.get('defMode', True), True + + +TAG_MAP = { + eoo.endOfOctets.tagSet: EndOfOctetsEncoder(), + univ.Boolean.tagSet: BooleanEncoder(), + univ.Integer.tagSet: IntegerEncoder(), + univ.BitString.tagSet: BitStringEncoder(), + univ.OctetString.tagSet: OctetStringEncoder(), + univ.Null.tagSet: NullEncoder(), + univ.ObjectIdentifier.tagSet: ObjectIdentifierEncoder(), + univ.Enumerated.tagSet: IntegerEncoder(), + univ.Real.tagSet: RealEncoder(), + # Sequence & Set have same tags as SequenceOf & SetOf + univ.SequenceOf.tagSet: SequenceOfEncoder(), + univ.SetOf.tagSet: SequenceOfEncoder(), + univ.Choice.tagSet: ChoiceEncoder(), + # character string types + char.UTF8String.tagSet: OctetStringEncoder(), + char.NumericString.tagSet: OctetStringEncoder(), + char.PrintableString.tagSet: OctetStringEncoder(), + char.TeletexString.tagSet: OctetStringEncoder(), + char.VideotexString.tagSet: OctetStringEncoder(), + char.IA5String.tagSet: OctetStringEncoder(), + char.GraphicString.tagSet: OctetStringEncoder(), + char.VisibleString.tagSet: OctetStringEncoder(), + char.GeneralString.tagSet: OctetStringEncoder(), + char.UniversalString.tagSet: OctetStringEncoder(), + char.BMPString.tagSet: OctetStringEncoder(), + # useful types + useful.ObjectDescriptor.tagSet: OctetStringEncoder(), + useful.GeneralizedTime.tagSet: OctetStringEncoder(), + useful.UTCTime.tagSet: OctetStringEncoder() +} + +# Put in ambiguous & non-ambiguous types for faster codec lookup +TYPE_MAP = { + univ.Boolean.typeId: BooleanEncoder(), + univ.Integer.typeId: IntegerEncoder(), + univ.BitString.typeId: BitStringEncoder(), + univ.OctetString.typeId: OctetStringEncoder(), + univ.Null.typeId: NullEncoder(), + univ.ObjectIdentifier.typeId: ObjectIdentifierEncoder(), + univ.Enumerated.typeId: IntegerEncoder(), + univ.Real.typeId: RealEncoder(), + # Sequence & Set have same tags as SequenceOf & SetOf + univ.Set.typeId: SequenceEncoder(), + univ.SetOf.typeId: SequenceOfEncoder(), + univ.Sequence.typeId: SequenceEncoder(), + univ.SequenceOf.typeId: SequenceOfEncoder(), + univ.Choice.typeId: ChoiceEncoder(), + univ.Any.typeId: AnyEncoder(), + # 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() +} + +# deprecated aliases, https://github.com/pyasn1/pyasn1/issues/9 +tagMap = TAG_MAP +typeMap = TYPE_MAP + + +class SingleItemEncoder(object): + fixedDefLengthMode = None + fixedChunkSize = None + + TAG_MAP = TAG_MAP + TYPE_MAP = TYPE_MAP + + def __init__(self, tagMap=_MISSING, typeMap=_MISSING, **ignored): + self._tagMap = tagMap if tagMap is not _MISSING else self.TAG_MAP + self._typeMap = typeMap if typeMap is not _MISSING else self.TYPE_MAP + + def __call__(self, value, asn1Spec=None, **options): + try: + if asn1Spec is None: + typeId = value.typeId + else: + typeId = asn1Spec.typeId + + except AttributeError: + raise error.PyAsn1Error('Value %r is not ASN.1 type instance ' + 'and "asn1Spec" not given' % (value,)) + + if LOG: + LOG('encoder called in %sdef mode, chunk size %s for type %s, ' + 'value:\n%s' % (not options.get('defMode', True) and 'in' or '', + options.get('maxChunkSize', 0), + asn1Spec is None and value.prettyPrintType() or + asn1Spec.prettyPrintType(), value)) + + if self.fixedDefLengthMode is not None: + options.update(defMode=self.fixedDefLengthMode) + + if self.fixedChunkSize is not None: + options.update(maxChunkSize=self.fixedChunkSize) + + try: + concreteEncoder = self._typeMap[typeId] + + if LOG: + LOG('using value codec %s chosen by type ID ' + '%s' % (concreteEncoder.__class__.__name__, typeId)) + + except KeyError: + if asn1Spec is None: + tagSet = value.tagSet + else: + tagSet = asn1Spec.tagSet + + # use base type for codec lookup to recover untagged types + baseTagSet = tag.TagSet(tagSet.baseTag, tagSet.baseTag) + + try: + concreteEncoder = self._tagMap[baseTagSet] + + except KeyError: + raise error.PyAsn1Error('No encoder for %r (%s)' % (value, tagSet)) + + if LOG: + LOG('using value codec %s chosen by tagSet ' + '%s' % (concreteEncoder.__class__.__name__, tagSet)) + + substrate = concreteEncoder.encode(value, asn1Spec, self, **options) + + if LOG: + LOG('codec %s built %s octets of substrate: %s\nencoder ' + 'completed' % (concreteEncoder, len(substrate), + debug.hexdump(substrate))) + + return substrate + + +class Encoder(object): + SINGLE_ITEM_ENCODER = SingleItemEncoder + + def __init__(self, tagMap=_MISSING, typeMap=_MISSING, **options): + self._singleItemEncoder = self.SINGLE_ITEM_ENCODER( + tagMap=tagMap, typeMap=typeMap, **options + ) + + def __call__(self, pyObject, asn1Spec=None, **options): + return self._singleItemEncoder( + pyObject, asn1Spec=asn1Spec, **options) + + +#: 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: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) +#: A Python or pyasn1 object to encode. If Python object is given, `asnSpec` +#: parameter is required to guide the encoding process. +#: +#: Keyword Args +#: ------------ +#: asn1Spec: +#: Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative +#: +#: defMode: :py:class:`bool` +#: If :obj:`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 +#: ------ +#: ~pyasn1.error.PyAsn1Error +#: On encoding errors +#: +#: Examples +#: -------- +#: Encode Python value into BER with ASN.1 schema +#: +#: .. code-block:: pycon +#: +#: >>> seq = SequenceOf(componentType=Integer()) +#: >>> encode([1, 2, 3], asn1Spec=seq) +#: b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03' +#: +#: Encode ASN.1 value object into BER +#: +#: .. code-block:: pycon +#: +#: >>> seq = SequenceOf(componentType=Integer()) +#: >>> seq.extend([1, 2, 3]) +#: >>> encode(seq) +#: b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03' +#: +encode = Encoder() diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/ber/eoo.py b/contrib/python/pyasn1/py3/pyasn1/codec/ber/eoo.py new file mode 100644 index 0000000000..8c91a3d285 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/ber/eoo.py @@ -0,0 +1,28 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +from pyasn1.type import base +from pyasn1.type import tag + +__all__ = ['endOfOctets'] + + +class EndOfOctets(base.SimpleAsn1Type): + defaultValue = 0 + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00) + ) + + _instance = None + + def __new__(cls, *args, **kwargs): + if cls._instance is None: + cls._instance = object.__new__(cls, *args, **kwargs) + + return cls._instance + + +endOfOctets = EndOfOctets() diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/cer/__init__.py b/contrib/python/pyasn1/py3/pyasn1/codec/cer/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/cer/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/cer/decoder.py b/contrib/python/pyasn1/py3/pyasn1/codec/cer/decoder.py new file mode 100644 index 0000000000..ed6391ff35 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/cer/decoder.py @@ -0,0 +1,146 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +from pyasn1 import error +from pyasn1.codec.streaming import readFromStream +from pyasn1.codec.ber import decoder +from pyasn1.compat.octets import oct2int +from pyasn1.type import univ + +__all__ = ['decode', 'StreamingDecoder'] + +SubstrateUnderrunError = error.SubstrateUnderrunError + + +class BooleanPayloadDecoder(decoder.AbstractSimplePayloadDecoder): + protoComponent = univ.Boolean(0) + + def valueDecoder(self, substrate, asn1Spec, + tagSet=None, length=None, state=None, + decodeFun=None, substrateFun=None, + **options): + + if length != 1: + raise error.PyAsn1Error('Not single-octet Boolean payload') + + for chunk in readFromStream(substrate, length, options): + if isinstance(chunk, SubstrateUnderrunError): + yield chunk + + byte = oct2int(chunk[0]) + + # CER/DER specifies encoding of TRUE as 0xFF and FALSE as 0x0, while + # BER allows any non-zero value as TRUE; cf. sections 8.2.2. and 11.1 + # in https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + if byte == 0xff: + value = 1 + + elif byte == 0x00: + value = 0 + + else: + raise error.PyAsn1Error('Unexpected Boolean payload: %s' % byte) + + yield self._createComponent(asn1Spec, tagSet, value, **options) + + +# TODO: prohibit non-canonical encoding +BitStringPayloadDecoder = decoder.BitStringPayloadDecoder +OctetStringPayloadDecoder = decoder.OctetStringPayloadDecoder +RealPayloadDecoder = decoder.RealPayloadDecoder + +TAG_MAP = decoder.TAG_MAP.copy() +TAG_MAP.update( + {univ.Boolean.tagSet: BooleanPayloadDecoder(), + univ.BitString.tagSet: BitStringPayloadDecoder(), + univ.OctetString.tagSet: OctetStringPayloadDecoder(), + univ.Real.tagSet: RealPayloadDecoder()} +) + +TYPE_MAP = decoder.TYPE_MAP.copy() + +# deprecated aliases, https://github.com/pyasn1/pyasn1/issues/9 +tagMap = TAG_MAP +typeMap = TYPE_MAP + +# Put in non-ambiguous types for faster codec lookup +for typeDecoder in TAG_MAP.values(): + if typeDecoder.protoComponent is not None: + typeId = typeDecoder.protoComponent.__class__.typeId + if typeId is not None and typeId not in TYPE_MAP: + TYPE_MAP[typeId] = typeDecoder + + +class SingleItemDecoder(decoder.SingleItemDecoder): + __doc__ = decoder.SingleItemDecoder.__doc__ + + TAG_MAP = TAG_MAP + TYPE_MAP = TYPE_MAP + + +class StreamingDecoder(decoder.StreamingDecoder): + __doc__ = decoder.StreamingDecoder.__doc__ + + SINGLE_ITEM_DECODER = SingleItemDecoder + + +class Decoder(decoder.Decoder): + __doc__ = decoder.Decoder.__doc__ + + STREAMING_DECODER = StreamingDecoder + + +#: Turns CER octet stream into an ASN.1 object. +#: +#: Takes CER octet-stream 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 octet-stream +#: +#: Keyword Args +#: ------------ +#: 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 +#: ------ +#: ~pyasn1.error.PyAsn1Error, ~pyasn1.error.SubstrateUnderrunError +#: On decoding errors +#: +#: Examples +#: -------- +#: Decode CER serialisation without ASN.1 schema +#: +#: .. code-block:: pycon +#: +#: >>> s, _ = decode(b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00') +#: >>> str(s) +#: SequenceOf: +#: 1 2 3 +#: +#: Decode CER serialisation with ASN.1 schema +#: +#: .. code-block:: pycon +#: +#: >>> seq = SequenceOf(componentType=Integer()) +#: >>> s, _ = decode(b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00', asn1Spec=seq) +#: >>> str(s) +#: SequenceOf: +#: 1 2 3 +#: +decode = Decoder() diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/cer/encoder.py b/contrib/python/pyasn1/py3/pyasn1/codec/cer/encoder.py new file mode 100644 index 0000000000..0a198e3fdf --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/cer/encoder.py @@ -0,0 +1,327 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +from pyasn1 import error +from pyasn1.codec.ber import encoder +from pyasn1.compat.octets import str2octs, null +from pyasn1.type import univ +from pyasn1.type import useful + +__all__ = ['Encoder', 'encode'] + + +class BooleanEncoder(encoder.IntegerEncoder): + def encodeValue(self, value, asn1Spec, encodeFun, **options): + if value == 0: + substrate = (0,) + else: + substrate = (255,) + return substrate, False, False + + +class RealEncoder(encoder.RealEncoder): + def _chooseEncBase(self, value): + m, b, e = value + return self._dropFloatingPoint(m, b, e) + + +# specialized GeneralStringEncoder here + +class TimeEncoderMixIn(object): + Z_CHAR = ord('Z') + PLUS_CHAR = ord('+') + MINUS_CHAR = ord('-') + COMMA_CHAR = ord(',') + DOT_CHAR = ord('.') + ZERO_CHAR = ord('0') + + MIN_LENGTH = 12 + MAX_LENGTH = 19 + + def encodeValue(self, value, asn1Spec, encodeFun, **options): + # CER encoding constraints: + # - minutes are mandatory, seconds are optional + # - sub-seconds must NOT be zero / no meaningless zeros + # - no hanging fraction dot + # - time in UTC (Z) + # - only dot is allowed for fractions + + if asn1Spec is not None: + value = asn1Spec.clone(value) + + numbers = value.asNumbers() + + if self.PLUS_CHAR in numbers or self.MINUS_CHAR in numbers: + raise error.PyAsn1Error('Must be UTC time: %r' % value) + + if numbers[-1] != self.Z_CHAR: + raise error.PyAsn1Error('Missing "Z" time zone specifier: %r' % value) + + if self.COMMA_CHAR in numbers: + raise error.PyAsn1Error('Comma in fractions disallowed: %r' % value) + + if self.DOT_CHAR in numbers: + + isModified = False + + numbers = list(numbers) + + searchIndex = min(numbers.index(self.DOT_CHAR) + 4, len(numbers) - 1) + + while numbers[searchIndex] != self.DOT_CHAR: + if numbers[searchIndex] == self.ZERO_CHAR: + del numbers[searchIndex] + isModified = True + + searchIndex -= 1 + + searchIndex += 1 + + if searchIndex < len(numbers): + if numbers[searchIndex] == self.Z_CHAR: + # drop hanging comma + del numbers[searchIndex - 1] + isModified = True + + if isModified: + value = value.clone(numbers) + + if not self.MIN_LENGTH < len(numbers) < self.MAX_LENGTH: + raise error.PyAsn1Error('Length constraint violated: %r' % value) + + options.update(maxChunkSize=1000) + + return encoder.OctetStringEncoder.encodeValue( + self, value, asn1Spec, encodeFun, **options + ) + + +class GeneralizedTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder): + MIN_LENGTH = 12 + MAX_LENGTH = 20 + + +class UTCTimeEncoder(TimeEncoderMixIn, encoder.OctetStringEncoder): + MIN_LENGTH = 10 + MAX_LENGTH = 14 + + +class SetOfEncoder(encoder.SequenceOfEncoder): + def encodeValue(self, value, asn1Spec, encodeFun, **options): + chunks = self._encodeComponents( + value, asn1Spec, encodeFun, **options) + + # sort by serialised and padded components + if len(chunks) > 1: + zero = str2octs('\x00') + maxLen = max(map(len, chunks)) + paddedChunks = [ + (x.ljust(maxLen, zero), x) for x in chunks + ] + paddedChunks.sort(key=lambda x: x[0]) + + chunks = [x[1] for x in paddedChunks] + + return null.join(chunks), True, True + + +class SequenceOfEncoder(encoder.SequenceOfEncoder): + def encodeValue(self, value, asn1Spec, encodeFun, **options): + + if options.get('ifNotEmpty', False) and not len(value): + return null, True, True + + chunks = self._encodeComponents( + value, asn1Spec, encodeFun, **options) + + return null.join(chunks), True, True + + +class SetEncoder(encoder.SequenceEncoder): + @staticmethod + def _componentSortKey(componentAndType): + """Sort SET components by tag + + Sort regardless of the Choice value (static sort) + """ + component, asn1Spec = componentAndType + + if asn1Spec is None: + asn1Spec = component + + if asn1Spec.typeId == univ.Choice.typeId and not asn1Spec.tagSet: + if asn1Spec.tagSet: + return asn1Spec.tagSet + else: + return asn1Spec.componentType.minTagSet + else: + return asn1Spec.tagSet + + def encodeValue(self, value, asn1Spec, encodeFun, **options): + + substrate = null + + comps = [] + compsMap = {} + + if asn1Spec is None: + # instance of ASN.1 schema + inconsistency = value.isInconsistent + if inconsistency: + raise inconsistency + + namedTypes = value.componentType + + for idx, component in enumerate(value.values()): + if namedTypes: + namedType = namedTypes[idx] + + if namedType.isOptional and not component.isValue: + continue + + if namedType.isDefaulted and component == namedType.asn1Object: + continue + + compsMap[id(component)] = namedType + + else: + compsMap[id(component)] = None + + comps.append((component, asn1Spec)) + + else: + # bare Python value + ASN.1 schema + for idx, namedType in enumerate(asn1Spec.componentType.namedTypes): + + try: + component = value[namedType.name] + + except KeyError: + raise error.PyAsn1Error('Component name "%s" not found in %r' % (namedType.name, value)) + + if namedType.isOptional and namedType.name not in value: + continue + + if namedType.isDefaulted and component == namedType.asn1Object: + continue + + compsMap[id(component)] = namedType + comps.append((component, asn1Spec[idx])) + + for comp, compType in sorted(comps, key=self._componentSortKey): + namedType = compsMap[id(comp)] + + if namedType: + options.update(ifNotEmpty=namedType.isOptional) + + chunk = encodeFun(comp, compType, **options) + + # wrap open type blob if needed + if namedType and namedType.openType: + wrapType = namedType.asn1Object + if wrapType.tagSet and not wrapType.isSameTypeWith(comp): + chunk = encodeFun(chunk, wrapType, **options) + + substrate += chunk + + return substrate, True, True + + +class SequenceEncoder(encoder.SequenceEncoder): + omitEmptyOptionals = True + + +TAG_MAP = encoder.TAG_MAP.copy() + +TAG_MAP.update({ + univ.Boolean.tagSet: BooleanEncoder(), + univ.Real.tagSet: RealEncoder(), + useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(), + useful.UTCTime.tagSet: UTCTimeEncoder(), + # Sequence & Set have same tags as SequenceOf & SetOf + univ.SetOf.tagSet: SetOfEncoder(), + univ.Sequence.typeId: SequenceEncoder() +}) + +TYPE_MAP = encoder.TYPE_MAP.copy() + +TYPE_MAP.update({ + univ.Boolean.typeId: BooleanEncoder(), + univ.Real.typeId: RealEncoder(), + useful.GeneralizedTime.typeId: GeneralizedTimeEncoder(), + useful.UTCTime.typeId: UTCTimeEncoder(), + # Sequence & Set have same tags as SequenceOf & SetOf + univ.Set.typeId: SetEncoder(), + univ.SetOf.typeId: SetOfEncoder(), + univ.Sequence.typeId: SequenceEncoder(), + univ.SequenceOf.typeId: SequenceOfEncoder() +}) + +# deprecated aliases, https://github.com/pyasn1/pyasn1/issues/9 +tagMap = TAG_MAP +typeMap = TYPE_MAP + + +class SingleItemEncoder(encoder.SingleItemEncoder): + fixedDefLengthMode = False + fixedChunkSize = 1000 + + TAG_MAP = TAG_MAP + TYPE_MAP = TYPE_MAP + + +class Encoder(encoder.Encoder): + SINGLE_ITEM_ENCODER = SingleItemEncoder + + +#: 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: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) +#: A Python or pyasn1 object to encode. If Python object is given, `asnSpec` +#: parameter is required to guide the encoding process. +#: +#: Keyword Args +#: ------------ +#: asn1Spec: +#: Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative +#: +#: Returns +#: ------- +#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2) +#: Given ASN.1 object encoded into BER octet-stream +#: +#: Raises +#: ------ +#: ~pyasn1.error.PyAsn1Error +#: On encoding errors +#: +#: Examples +#: -------- +#: Encode Python value into CER with ASN.1 schema +#: +#: .. code-block:: pycon +#: +#: >>> seq = SequenceOf(componentType=Integer()) +#: >>> encode([1, 2, 3], asn1Spec=seq) +#: b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00' +#: +#: Encode ASN.1 value object into CER +#: +#: .. code-block:: pycon +#: +#: >>> seq = SequenceOf(componentType=Integer()) +#: >>> seq.extend([1, 2, 3]) +#: >>> encode(seq) +#: b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00' +#: +encode = Encoder() + +# EncoderFactory queries class instance and builds a map of tags -> encoders diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/der/__init__.py b/contrib/python/pyasn1/py3/pyasn1/codec/der/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/der/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/der/decoder.py b/contrib/python/pyasn1/py3/pyasn1/codec/der/decoder.py new file mode 100644 index 0000000000..215b72d9fd --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/der/decoder.py @@ -0,0 +1,116 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +from pyasn1.codec.cer import decoder +from pyasn1.type import univ + +__all__ = ['decode', 'StreamingDecoder'] + + +class BitStringPayloadDecoder(decoder.BitStringPayloadDecoder): + supportConstructedForm = False + + +class OctetStringPayloadDecoder(decoder.OctetStringPayloadDecoder): + supportConstructedForm = False + + +# TODO: prohibit non-canonical encoding +RealPayloadDecoder = decoder.RealPayloadDecoder + +TAG_MAP = decoder.TAG_MAP.copy() +TAG_MAP.update( + {univ.BitString.tagSet: BitStringPayloadDecoder(), + univ.OctetString.tagSet: OctetStringPayloadDecoder(), + univ.Real.tagSet: RealPayloadDecoder()} +) + +TYPE_MAP = decoder.TYPE_MAP.copy() + +# deprecated aliases, https://github.com/pyasn1/pyasn1/issues/9 +tagMap = TAG_MAP +typeMap = TYPE_MAP + +# Put in non-ambiguous types for faster codec lookup +for typeDecoder in TAG_MAP.values(): + if typeDecoder.protoComponent is not None: + typeId = typeDecoder.protoComponent.__class__.typeId + if typeId is not None and typeId not in TYPE_MAP: + TYPE_MAP[typeId] = typeDecoder + + +class SingleItemDecoder(decoder.SingleItemDecoder): + __doc__ = decoder.SingleItemDecoder.__doc__ + + TAG_MAP = TAG_MAP + TYPE_MAP = TYPE_MAP + + supportIndefLength = False + + +class StreamingDecoder(decoder.StreamingDecoder): + __doc__ = decoder.StreamingDecoder.__doc__ + + SINGLE_ITEM_DECODER = SingleItemDecoder + + +class Decoder(decoder.Decoder): + __doc__ = decoder.Decoder.__doc__ + + STREAMING_DECODER = StreamingDecoder + + +#: Turns DER octet stream into an ASN.1 object. +#: +#: Takes DER octet-stream 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 octet-stream +#: +#: Keyword Args +#: ------------ +#: 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 +#: ------ +#: ~pyasn1.error.PyAsn1Error, ~pyasn1.error.SubstrateUnderrunError +#: On decoding errors +#: +#: Examples +#: -------- +#: Decode DER serialisation without ASN.1 schema +#: +#: .. code-block:: pycon +#: +#: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03') +#: >>> str(s) +#: SequenceOf: +#: 1 2 3 +#: +#: Decode DER serialisation with ASN.1 schema +#: +#: .. code-block:: pycon +#: +#: >>> seq = SequenceOf(componentType=Integer()) +#: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq) +#: >>> str(s) +#: SequenceOf: +#: 1 2 3 +#: +decode = Decoder() diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/der/encoder.py b/contrib/python/pyasn1/py3/pyasn1/codec/der/encoder.py new file mode 100644 index 0000000000..c231edc164 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/der/encoder.py @@ -0,0 +1,122 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +from pyasn1 import error +from pyasn1.codec.cer import encoder +from pyasn1.type import univ + +__all__ = ['Encoder', 'encode'] + + +class SetEncoder(encoder.SetEncoder): + @staticmethod + def _componentSortKey(componentAndType): + """Sort SET components by tag + + Sort depending on the actual Choice value (dynamic sort) + """ + component, asn1Spec = componentAndType + + if asn1Spec is None: + compType = component + else: + compType = asn1Spec + + if compType.typeId == univ.Choice.typeId and not compType.tagSet: + if asn1Spec is None: + return component.getComponent().tagSet + else: + # TODO: move out of sorting key function + names = [namedType.name for namedType in asn1Spec.componentType.namedTypes + if namedType.name in component] + if len(names) != 1: + raise error.PyAsn1Error( + '%s components for Choice at %r' % (len(names) and 'Multiple ' or 'None ', component)) + + # TODO: support nested CHOICE ordering + return asn1Spec[names[0]].tagSet + + else: + return compType.tagSet + + +TAG_MAP = encoder.TAG_MAP.copy() + +TAG_MAP.update({ + # Set & SetOf have same tags + univ.Set.tagSet: SetEncoder() +}) + +TYPE_MAP = encoder.TYPE_MAP.copy() + +TYPE_MAP.update({ + # Set & SetOf have same tags + univ.Set.typeId: SetEncoder() +}) + +# deprecated aliases, https://github.com/pyasn1/pyasn1/issues/9 +tagMap = TAG_MAP +typeMap = TYPE_MAP + + +class SingleItemEncoder(encoder.SingleItemEncoder): + fixedDefLengthMode = True + fixedChunkSize = 0 + + TAG_MAP = TAG_MAP + TYPE_MAP = TYPE_MAP + + +class Encoder(encoder.Encoder): + SINGLE_ITEM_ENCODER = SingleItemEncoder + + +#: 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: either a Python or pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) +#: A Python or pyasn1 object to encode. If Python object is given, `asnSpec` +#: parameter is required to guide the encoding process. +#: +#: Keyword Args +#: ------------ +#: asn1Spec: +#: Optional ASN.1 schema or value object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative +#: +#: Returns +#: ------- +#: : :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2) +#: Given ASN.1 object encoded into BER octet-stream +#: +#: Raises +#: ------ +#: ~pyasn1.error.PyAsn1Error +#: On encoding errors +#: +#: Examples +#: -------- +#: Encode Python value into DER with ASN.1 schema +#: +#: .. code-block:: pycon +#: +#: >>> seq = SequenceOf(componentType=Integer()) +#: >>> encode([1, 2, 3], asn1Spec=seq) +#: b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03' +#: +#: Encode ASN.1 value object into DER +#: +#: .. code-block:: pycon +#: +#: >>> seq = SequenceOf(componentType=Integer()) +#: >>> seq.extend([1, 2, 3]) +#: >>> encode(seq) +#: b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03' +#: +encode = Encoder() diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/native/__init__.py b/contrib/python/pyasn1/py3/pyasn1/codec/native/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/native/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/native/decoder.py b/contrib/python/pyasn1/py3/pyasn1/codec/native/decoder.py new file mode 100644 index 0000000000..e23f40ca4b --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/native/decoder.py @@ -0,0 +1,238 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +from pyasn1 import debug +from pyasn1 import error +from pyasn1.compat import _MISSING +from pyasn1.type import base +from pyasn1.type import char +from pyasn1.type import tag +from pyasn1.type import univ +from pyasn1.type import useful + +__all__ = ['decode'] + +LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_DECODER) + + +class AbstractScalarPayloadDecoder(object): + def __call__(self, pyObject, asn1Spec, decodeFun=None, **options): + return asn1Spec.clone(pyObject) + + +class BitStringPayloadDecoder(AbstractScalarPayloadDecoder): + def __call__(self, pyObject, asn1Spec, decodeFun=None, **options): + return asn1Spec.clone(univ.BitString.fromBinaryString(pyObject)) + + +class SequenceOrSetPayloadDecoder(object): + def __call__(self, pyObject, asn1Spec, decodeFun=None, **options): + asn1Value = asn1Spec.clone() + + componentsTypes = asn1Spec.componentType + + for field in asn1Value: + if field in pyObject: + asn1Value[field] = decodeFun(pyObject[field], componentsTypes[field].asn1Object, **options) + + return asn1Value + + +class SequenceOfOrSetOfPayloadDecoder(object): + def __call__(self, pyObject, asn1Spec, decodeFun=None, **options): + asn1Value = asn1Spec.clone() + + for pyValue in pyObject: + asn1Value.append(decodeFun(pyValue, asn1Spec.componentType), **options) + + return asn1Value + + +class ChoicePayloadDecoder(object): + def __call__(self, pyObject, asn1Spec, decodeFun=None, **options): + asn1Value = asn1Spec.clone() + + componentsTypes = asn1Spec.componentType + + for field in pyObject: + if field in componentsTypes: + asn1Value[field] = decodeFun(pyObject[field], componentsTypes[field].asn1Object, **options) + break + + return asn1Value + + +TAG_MAP = { + univ.Integer.tagSet: AbstractScalarPayloadDecoder(), + univ.Boolean.tagSet: AbstractScalarPayloadDecoder(), + univ.BitString.tagSet: BitStringPayloadDecoder(), + univ.OctetString.tagSet: AbstractScalarPayloadDecoder(), + univ.Null.tagSet: AbstractScalarPayloadDecoder(), + univ.ObjectIdentifier.tagSet: AbstractScalarPayloadDecoder(), + univ.Enumerated.tagSet: AbstractScalarPayloadDecoder(), + univ.Real.tagSet: AbstractScalarPayloadDecoder(), + univ.Sequence.tagSet: SequenceOrSetPayloadDecoder(), # conflicts with SequenceOf + univ.Set.tagSet: SequenceOrSetPayloadDecoder(), # conflicts with SetOf + univ.Choice.tagSet: ChoicePayloadDecoder(), # conflicts with Any + # character string types + char.UTF8String.tagSet: AbstractScalarPayloadDecoder(), + char.NumericString.tagSet: AbstractScalarPayloadDecoder(), + char.PrintableString.tagSet: AbstractScalarPayloadDecoder(), + char.TeletexString.tagSet: AbstractScalarPayloadDecoder(), + char.VideotexString.tagSet: AbstractScalarPayloadDecoder(), + char.IA5String.tagSet: AbstractScalarPayloadDecoder(), + char.GraphicString.tagSet: AbstractScalarPayloadDecoder(), + char.VisibleString.tagSet: AbstractScalarPayloadDecoder(), + char.GeneralString.tagSet: AbstractScalarPayloadDecoder(), + char.UniversalString.tagSet: AbstractScalarPayloadDecoder(), + char.BMPString.tagSet: AbstractScalarPayloadDecoder(), + # useful types + useful.ObjectDescriptor.tagSet: AbstractScalarPayloadDecoder(), + useful.GeneralizedTime.tagSet: AbstractScalarPayloadDecoder(), + useful.UTCTime.tagSet: AbstractScalarPayloadDecoder() +} + +# Put in ambiguous & non-ambiguous types for faster codec lookup +TYPE_MAP = { + univ.Integer.typeId: AbstractScalarPayloadDecoder(), + univ.Boolean.typeId: AbstractScalarPayloadDecoder(), + univ.BitString.typeId: BitStringPayloadDecoder(), + univ.OctetString.typeId: AbstractScalarPayloadDecoder(), + univ.Null.typeId: AbstractScalarPayloadDecoder(), + univ.ObjectIdentifier.typeId: AbstractScalarPayloadDecoder(), + univ.Enumerated.typeId: AbstractScalarPayloadDecoder(), + univ.Real.typeId: AbstractScalarPayloadDecoder(), + # ambiguous base types + univ.Set.typeId: SequenceOrSetPayloadDecoder(), + univ.SetOf.typeId: SequenceOfOrSetOfPayloadDecoder(), + univ.Sequence.typeId: SequenceOrSetPayloadDecoder(), + univ.SequenceOf.typeId: SequenceOfOrSetOfPayloadDecoder(), + univ.Choice.typeId: ChoicePayloadDecoder(), + univ.Any.typeId: AbstractScalarPayloadDecoder(), + # character string types + char.UTF8String.typeId: AbstractScalarPayloadDecoder(), + char.NumericString.typeId: AbstractScalarPayloadDecoder(), + char.PrintableString.typeId: AbstractScalarPayloadDecoder(), + char.TeletexString.typeId: AbstractScalarPayloadDecoder(), + char.VideotexString.typeId: AbstractScalarPayloadDecoder(), + char.IA5String.typeId: AbstractScalarPayloadDecoder(), + char.GraphicString.typeId: AbstractScalarPayloadDecoder(), + char.VisibleString.typeId: AbstractScalarPayloadDecoder(), + char.GeneralString.typeId: AbstractScalarPayloadDecoder(), + char.UniversalString.typeId: AbstractScalarPayloadDecoder(), + char.BMPString.typeId: AbstractScalarPayloadDecoder(), + # useful types + useful.ObjectDescriptor.typeId: AbstractScalarPayloadDecoder(), + useful.GeneralizedTime.typeId: AbstractScalarPayloadDecoder(), + useful.UTCTime.typeId: AbstractScalarPayloadDecoder() +} + +# deprecated aliases, https://github.com/pyasn1/pyasn1/issues/9 +tagMap = TAG_MAP +typeMap = TYPE_MAP + + +class SingleItemDecoder(object): + + TAG_MAP = TAG_MAP + TYPE_MAP = TYPE_MAP + + def __init__(self, tagMap=_MISSING, typeMap=_MISSING, **ignored): + self._tagMap = tagMap if tagMap is not _MISSING else self.TAG_MAP + self._typeMap = typeMap if typeMap is not _MISSING else self.TYPE_MAP + + def __call__(self, pyObject, asn1Spec, **options): + + if LOG: + debug.scope.push(type(pyObject).__name__) + LOG('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 LOG: + LOG('calling decoder %s on Python type %s ' + '<%s>' % (type(valueDecoder).__name__, + type(pyObject).__name__, repr(pyObject))) + + value = valueDecoder(pyObject, asn1Spec, self, **options) + + if LOG: + LOG('decoder %s produced ASN.1 type %s ' + '<%s>' % (type(valueDecoder).__name__, + type(value).__name__, repr(value))) + debug.scope.pop() + + return value + + +class Decoder(object): + SINGLE_ITEM_DECODER = SingleItemDecoder + + def __init__(self, **options): + self._singleItemDecoder = self.SINGLE_ITEM_DECODER(**options) + + def __call__(self, pyObject, asn1Spec=None, **kwargs): + return self._singleItemDecoder(pyObject, asn1Spec=asn1Spec, **kwargs) + + +#: 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 +#: +#: Keyword Args +#: ------------ +#: 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 +#: ------ +#: ~pyasn1.error.PyAsn1Error +#: On decoding errors +#: +#: Examples +#: -------- +#: Decode native Python object into ASN.1 objects with ASN.1 schema +#: +#: .. code-block:: pycon +#: +#: >>> seq = SequenceOf(componentType=Integer()) +#: >>> s, _ = decode([1, 2, 3], asn1Spec=seq) +#: >>> str(s) +#: SequenceOf: +#: 1 2 3 +#: +decode = Decoder() diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/native/encoder.py b/contrib/python/pyasn1/py3/pyasn1/codec/native/encoder.py new file mode 100644 index 0000000000..a0d9f1c444 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/native/encoder.py @@ -0,0 +1,274 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +from collections import OrderedDict + +from pyasn1 import debug +from pyasn1 import error +from pyasn1.compat import _MISSING +from pyasn1.type import base +from pyasn1.type import char +from pyasn1.type import tag +from pyasn1.type import univ +from pyasn1.type import useful + +__all__ = ['encode'] + +LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_ENCODER) + + +class AbstractItemEncoder(object): + def encode(self, value, encodeFun, **options): + raise error.PyAsn1Error('Not implemented') + + +class BooleanEncoder(AbstractItemEncoder): + def encode(self, value, encodeFun, **options): + return bool(value) + + +class IntegerEncoder(AbstractItemEncoder): + def encode(self, value, encodeFun, **options): + return int(value) + + +class BitStringEncoder(AbstractItemEncoder): + def encode(self, value, encodeFun, **options): + return str(value) + + +class OctetStringEncoder(AbstractItemEncoder): + def encode(self, value, encodeFun, **options): + return value.asOctets() + + +class TextStringEncoder(AbstractItemEncoder): + def encode(self, value, encodeFun, **options): + return str(value) + + +class NullEncoder(AbstractItemEncoder): + def encode(self, value, encodeFun, **options): + return None + + +class ObjectIdentifierEncoder(AbstractItemEncoder): + def encode(self, value, encodeFun, **options): + return str(value) + + +class RealEncoder(AbstractItemEncoder): + def encode(self, value, encodeFun, **options): + return float(value) + + +class SetEncoder(AbstractItemEncoder): + protoDict = dict + + def encode(self, value, encodeFun, **options): + inconsistency = value.isInconsistent + if inconsistency: + raise inconsistency + + namedTypes = value.componentType + substrate = self.protoDict() + + for idx, (key, subValue) in enumerate(value.items()): + if namedTypes and namedTypes[idx].isOptional and not value[idx].isValue: + continue + substrate[key] = encodeFun(subValue, **options) + return substrate + + +class SequenceEncoder(SetEncoder): + protoDict = OrderedDict + + +class SequenceOfEncoder(AbstractItemEncoder): + def encode(self, value, encodeFun, **options): + inconsistency = value.isInconsistent + if inconsistency: + raise inconsistency + return [encodeFun(x, **options) for x in value] + + +class ChoiceEncoder(SequenceEncoder): + pass + + +class AnyEncoder(AbstractItemEncoder): + def encode(self, value, encodeFun, **options): + return value.asOctets() + + +TAG_MAP = { + 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() +} + + +# Put in ambiguous & non-ambiguous types for faster codec lookup +TYPE_MAP = { + 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: SetEncoder(), + univ.SetOf.typeId: SequenceOfEncoder(), + univ.Sequence.typeId: SequenceEncoder(), + univ.SequenceOf.typeId: SequenceOfEncoder(), + univ.Choice.typeId: ChoiceEncoder(), + 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() +} + +# deprecated aliases, https://github.com/pyasn1/pyasn1/issues/9 +tagMap = TAG_MAP +typeMap = TYPE_MAP + + +class SingleItemEncoder(object): + + TAG_MAP = TAG_MAP + TYPE_MAP = TYPE_MAP + + def __init__(self, tagMap=_MISSING, typeMap=_MISSING, **ignored): + self._tagMap = tagMap if tagMap is not _MISSING else self.TAG_MAP + self._typeMap = typeMap if typeMap is not _MISSING else self.TYPE_MAP + + def __call__(self, value, **options): + if not isinstance(value, base.Asn1Item): + raise error.PyAsn1Error( + 'value is not valid (should be an instance of an ASN.1 Item)') + + if LOG: + debug.scope.push(type(value).__name__) + LOG('encoder called for type %s ' + '<%s>' % (type(value).__name__, value.prettyPrint())) + + tagSet = value.tagSet + + try: + concreteEncoder = self._typeMap[value.typeId] + + except KeyError: + # use base type for codec lookup to recover untagged types + baseTagSet = tag.TagSet( + value.tagSet.baseTag, value.tagSet.baseTag) + + try: + concreteEncoder = self._tagMap[baseTagSet] + + except KeyError: + raise error.PyAsn1Error('No encoder for %s' % (value,)) + + if LOG: + LOG('using value codec %s chosen by ' + '%s' % (concreteEncoder.__class__.__name__, tagSet)) + + pyObject = concreteEncoder.encode(value, self, **options) + + if LOG: + LOG('encoder %s produced: ' + '%s' % (type(concreteEncoder).__name__, repr(pyObject))) + debug.scope.pop() + + return pyObject + + +class Encoder(object): + SINGLE_ITEM_ENCODER = SingleItemEncoder + + def __init__(self, **options): + self._singleItemEncoder = self.SINGLE_ITEM_ENCODER(**options) + + def __call__(self, pyObject, asn1Spec=None, **options): + return self._singleItemEncoder( + pyObject, asn1Spec=asn1Spec, **options) + + +#: 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` +#: is used 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 +#: ------ +#: ~pyasn1.error.PyAsn1Error +#: On encoding errors +#: +#: Examples +#: -------- +#: Encode ASN.1 value object into native Python types +#: +#: .. code-block:: pycon +#: +#: >>> seq = SequenceOf(componentType=Integer()) +#: >>> seq.extend([1, 2, 3]) +#: >>> encode(seq) +#: [1, 2, 3] +#: +encode = SingleItemEncoder() diff --git a/contrib/python/pyasn1/py3/pyasn1/codec/streaming.py b/contrib/python/pyasn1/py3/pyasn1/codec/streaming.py new file mode 100644 index 0000000000..231681c177 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/codec/streaming.py @@ -0,0 +1,244 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import io +import os +import sys + +from pyasn1 import error +from pyasn1.type import univ + +_PY2 = sys.version_info < (3,) + + +class CachingStreamWrapper(io.IOBase): + """Wrapper around non-seekable streams. + + Note that the implementation is tied to the decoder, + not checking for dangerous arguments for the sake + of performance. + + The read bytes are kept in an internal cache until + setting _markedPosition which may reset the cache. + """ + def __init__(self, raw): + self._raw = raw + self._cache = io.BytesIO() + self._markedPosition = 0 + + def peek(self, n): + result = self.read(n) + self._cache.seek(-len(result), os.SEEK_CUR) + return result + + def seekable(self): + return True + + def seek(self, n=-1, whence=os.SEEK_SET): + # Note that this not safe for seeking forward. + return self._cache.seek(n, whence) + + def read(self, n=-1): + read_from_cache = self._cache.read(n) + if n != -1: + n -= len(read_from_cache) + if not n: # 0 bytes left to read + return read_from_cache + + read_from_raw = self._raw.read(n) + + self._cache.write(read_from_raw) + + return read_from_cache + read_from_raw + + @property + def markedPosition(self): + """Position where the currently processed element starts. + + This is used for back-tracking in SingleItemDecoder.__call__ + and (indefLen)ValueDecoder and should not be used for other purposes. + The client is not supposed to ever seek before this position. + """ + return self._markedPosition + + @markedPosition.setter + def markedPosition(self, value): + # By setting the value, we ensure we won't seek back before it. + # `value` should be the same as the current position + # We don't check for this for performance reasons. + self._markedPosition = value + + # Whenever we set _marked_position, we know for sure + # that we will not return back, and thus it is + # safe to drop all cached data. + if self._cache.tell() > io.DEFAULT_BUFFER_SIZE: + self._cache = io.BytesIO(self._cache.read()) + self._markedPosition = 0 + + def tell(self): + return self._cache.tell() + + +def asSeekableStream(substrate): + """Convert object to seekable byte-stream. + + Parameters + ---------- + substrate: :py:class:`bytes` or :py:class:`io.IOBase` or :py:class:`univ.OctetString` + + Returns + ------- + : :py:class:`io.IOBase` + + Raises + ------ + : :py:class:`~pyasn1.error.PyAsn1Error` + If the supplied substrate cannot be converted to a seekable stream. + """ + if isinstance(substrate, io.BytesIO): + return substrate + + elif isinstance(substrate, bytes): + return io.BytesIO(substrate) + + elif isinstance(substrate, univ.OctetString): + return io.BytesIO(substrate.asOctets()) + + try: + # Special case: impossible to set attributes on `file` built-in + # XXX: broken, BufferedReader expects a "readable" attribute. + if _PY2 and isinstance(substrate, file): + return io.BufferedReader(substrate) + + elif substrate.seekable(): # Will fail for most invalid types + return substrate + + else: + return CachingStreamWrapper(substrate) + + except AttributeError: + raise error.UnsupportedSubstrateError( + "Cannot convert " + substrate.__class__.__name__ + + " to a seekable bit stream.") + + +def isEndOfStream(substrate): + """Check whether we have reached the end of a stream. + + Although it is more effective to read and catch exceptions, this + function + + Parameters + ---------- + substrate: :py:class:`IOBase` + Stream to check + + Returns + ------- + : :py:class:`bool` + """ + if isinstance(substrate, io.BytesIO): + cp = substrate.tell() + substrate.seek(0, os.SEEK_END) + result = substrate.tell() == cp + substrate.seek(cp, os.SEEK_SET) + yield result + + else: + received = substrate.read(1) + if received is None: + yield + + if received: + substrate.seek(-1, os.SEEK_CUR) + + yield not received + + +def peekIntoStream(substrate, size=-1): + """Peek into stream. + + Parameters + ---------- + substrate: :py:class:`IOBase` + Stream to read from. + + size: :py:class:`int` + How many bytes to peek (-1 = all available) + + Returns + ------- + : :py:class:`bytes` or :py:class:`str` + The return type depends on Python major version + """ + if hasattr(substrate, "peek"): + received = substrate.peek(size) + if received is None: + yield + + while len(received) < size: + yield + + yield received + + else: + current_position = substrate.tell() + try: + for chunk in readFromStream(substrate, size): + yield chunk + + finally: + substrate.seek(current_position) + + +def readFromStream(substrate, size=-1, context=None): + """Read from the stream. + + Parameters + ---------- + substrate: :py:class:`IOBase` + Stream to read from. + + Keyword parameters + ------------------ + size: :py:class:`int` + How many bytes to read (-1 = all available) + + context: :py:class:`dict` + Opaque caller context will be attached to exception objects created + by this function. + + Yields + ------ + : :py:class:`bytes` or :py:class:`str` or :py:class:`SubstrateUnderrunError` + Read data or :py:class:`~pyasn1.error.SubstrateUnderrunError` + object if no `size` bytes is readily available in the stream. The + data type depends on Python major version + + Raises + ------ + : :py:class:`~pyasn1.error.EndOfStreamError` + Input stream is exhausted + """ + while True: + # this will block unless stream is non-blocking + received = substrate.read(size) + if received is None: # non-blocking stream can do this + yield error.SubstrateUnderrunError(context=context) + + elif not received and size != 0: # end-of-stream + raise error.EndOfStreamError(context=context) + + elif len(received) < size: + substrate.seek(-len(received), os.SEEK_CUR) + + # behave like a non-blocking stream + yield error.SubstrateUnderrunError(context=context) + + else: + break + + yield received diff --git a/contrib/python/pyasn1/py3/pyasn1/compat/__init__.py b/contrib/python/pyasn1/py3/pyasn1/compat/__init__.py new file mode 100644 index 0000000000..d3e676ac6a --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/compat/__init__.py @@ -0,0 +1,4 @@ +# This file is necessary to make this directory a package. + +# sentinal for missing argument +_MISSING = object() diff --git a/contrib/python/pyasn1/py3/pyasn1/compat/integer.py b/contrib/python/pyasn1/py3/pyasn1/compat/integer.py new file mode 100644 index 0000000000..b41d849fcd --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/compat/integer.py @@ -0,0 +1,103 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import platform + +from pyasn1.compat.octets import oct2int, null, ensureString + + +implementation = platform.python_implementation() + +if sys.version_info[0] < 3: + from binascii import a2b_hex, b2a_hex + + def from_bytes(octets, signed=False): + if not octets: + return 0 + + value = long(b2a_hex(ensureString(octets)), 16) + + if signed and oct2int(octets[0]) & 0x80: + return value - (1 << len(octets) * 8) + + return value + + def to_bytes(value, signed=False, length=0): + if value < 0: + if signed: + bits = bitLength(value) + + # two's complement form + maxValue = 1 << bits + valueToEncode = (value + maxValue) % maxValue + + else: + raise OverflowError('can\'t convert negative int to unsigned') + elif value == 0 and length == 0: + return null + else: + bits = 0 + valueToEncode = value + + hexValue = hex(valueToEncode)[2:] + if hexValue.endswith('L'): + hexValue = hexValue[:-1] + + if len(hexValue) & 1: + hexValue = '0' + hexValue + + # padding may be needed for two's complement encoding + if value != valueToEncode or length: + hexLength = len(hexValue) * 4 + + padLength = max(length, bits) + + if padLength > hexLength: + hexValue = '00' * ((padLength - hexLength - 1) // 8 + 1) + hexValue + elif length and hexLength - length > 7: + raise OverflowError('int too big to convert') + + firstOctet = int(hexValue[:2], 16) + + if signed: + if firstOctet & 0x80: + if value >= 0: + hexValue = '00' + hexValue + elif value < 0: + hexValue = 'ff' + hexValue + + octets_value = a2b_hex(hexValue) + + return octets_value + + def bitLength(number): + # bits in unsigned number + hexValue = hex(abs(number)) + bits = len(hexValue) - 2 + if hexValue.endswith('L'): + bits -= 1 + if bits & 1: + bits += 1 + bits *= 4 + # TODO: strip lhs zeros + return bits + +else: + + def from_bytes(octets, signed=False): + return int.from_bytes(bytes(octets), 'big', signed=signed) + + def to_bytes(value, signed=False, length=0): + length = max(value.bit_length(), length) + + if signed and length % 8 == 0: + length += 1 + + return value.to_bytes(length // 8 + (length % 8 and 1 or 0), 'big', signed=signed) + + def bitLength(number): + return int(number).bit_length() diff --git a/contrib/python/pyasn1/py3/pyasn1/compat/octets.py b/contrib/python/pyasn1/py3/pyasn1/compat/octets.py new file mode 100644 index 0000000000..d871f46c8a --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/compat/octets.py @@ -0,0 +1,46 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +from sys import version_info + +if version_info[0] <= 2: + int2oct = chr + # noinspection PyPep8 + ints2octs = lambda s: ''.join([int2oct(x) for x in s]) + null = '' + oct2int = ord + # TODO: refactor to return a sequence of ints + # noinspection PyPep8 + octs2ints = lambda s: [oct2int(x) for x in s] + # noinspection PyPep8 + str2octs = lambda x: x + # noinspection PyPep8 + octs2str = lambda x: x + # noinspection PyPep8 + isOctetsType = lambda s: isinstance(s, str) + # noinspection PyPep8 + isStringType = lambda s: isinstance(s, (str, unicode)) + # noinspection PyPep8 + ensureString = str +else: + ints2octs = bytes + # noinspection PyPep8 + int2oct = lambda x: ints2octs((x,)) + null = ints2octs() + # noinspection PyPep8 + oct2int = lambda x: x + # noinspection PyPep8 + octs2ints = lambda x: x + # noinspection PyPep8 + str2octs = lambda x: x.encode('iso-8859-1') + # noinspection PyPep8 + octs2str = lambda x: x.decode('iso-8859-1') + # noinspection PyPep8 + isOctetsType = lambda s: isinstance(s, bytes) + # noinspection PyPep8 + isStringType = lambda s: isinstance(s, str) + # noinspection PyPep8 + ensureString = bytes diff --git a/contrib/python/pyasn1/py3/pyasn1/debug.py b/contrib/python/pyasn1/py3/pyasn1/debug.py new file mode 100644 index 0000000000..6be80c3a70 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/debug.py @@ -0,0 +1,147 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import logging +import sys + +from pyasn1 import __version__ +from pyasn1 import error +from pyasn1.compat.octets import octs2ints + +__all__ = ['Debug', 'setLogger', 'hexdump'] + +DEBUG_NONE = 0x0000 +DEBUG_ENCODER = 0x0001 +DEBUG_DECODER = 0x0002 +DEBUG_ALL = 0xffff + +FLAG_MAP = { + 'none': DEBUG_NONE, + 'encoder': DEBUG_ENCODER, + 'decoder': DEBUG_DECODER, + 'all': DEBUG_ALL +} + +LOGGEE_MAP = {} + + +class Printer(object): + # noinspection PyShadowingNames + def __init__(self, logger=None, handler=None, formatter=None): + if logger is None: + logger = logging.getLogger('pyasn1') + + logger.setLevel(logging.DEBUG) + + if handler is None: + handler = logging.StreamHandler() + + if formatter is None: + formatter = logging.Formatter('%(asctime)s %(name)s: %(message)s') + + handler.setFormatter(formatter) + handler.setLevel(logging.DEBUG) + logger.addHandler(handler) + + self.__logger = logger + + def __call__(self, msg): + self.__logger.debug(msg) + + def __str__(self): + return '<python logging>' + + +class Debug(object): + defaultPrinter = Printer() + + def __init__(self, *flags, **options): + self._flags = DEBUG_NONE + + if 'loggerName' in options: + # route our logs to parent logger + self._printer = Printer( + logger=logging.getLogger(options['loggerName']), + handler=logging.NullHandler() + ) + + elif 'printer' in options: + self._printer = options.get('printer') + + else: + self._printer = self.defaultPrinter + + self._printer('running pyasn1 %s, debug flags %s' % (__version__, ', '.join(flags))) + + for flag in flags: + inverse = flag and flag[0] in ('!', '~') + if inverse: + flag = flag[1:] + try: + if inverse: + self._flags &= ~FLAG_MAP[flag] + else: + self._flags |= FLAG_MAP[flag] + except KeyError: + raise error.PyAsn1Error('bad debug flag %s' % flag) + + self._printer("debug category '%s' %s" % (flag, inverse and 'disabled' or 'enabled')) + + def __str__(self): + return 'logger %s, flags %x' % (self._printer, self._flags) + + def __call__(self, msg): + self._printer(msg) + + def __and__(self, flag): + return self._flags & flag + + def __rand__(self, flag): + return flag & self._flags + +_LOG = DEBUG_NONE + + +def setLogger(userLogger): + global _LOG + + if userLogger: + _LOG = userLogger + else: + _LOG = DEBUG_NONE + + # Update registered logging clients + for module, (name, flags) in LOGGEE_MAP.items(): + setattr(module, name, _LOG & flags and _LOG or DEBUG_NONE) + + +def registerLoggee(module, name='LOG', flags=DEBUG_NONE): + LOGGEE_MAP[sys.modules[module]] = name, flags + setLogger(_LOG) + return _LOG + + +def hexdump(octets): + return ' '.join( + ['%s%.2X' % (n % 16 == 0 and ('\n%.5d: ' % n) or '', x) + for n, x in zip(range(len(octets)), octs2ints(octets))] + ) + + +class Scope(object): + def __init__(self): + self._list = [] + + def __str__(self): return '.'.join(self._list) + + def push(self, token): + self._list.append(token) + + def pop(self): + return self._list.pop() + + +scope = Scope() diff --git a/contrib/python/pyasn1/py3/pyasn1/error.py b/contrib/python/pyasn1/py3/pyasn1/error.py new file mode 100644 index 0000000000..75c9a3f4cd --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/error.py @@ -0,0 +1,116 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# + + +class PyAsn1Error(Exception): + """Base pyasn1 exception + + `PyAsn1Error` is the base exception class (based on + :class:`Exception`) that represents all possible ASN.1 related + errors. + + Parameters + ---------- + args: + Opaque positional parameters + + Keyword Args + ------------ + kwargs: + Opaque keyword parameters + + """ + def __init__(self, *args, **kwargs): + self._args = args + self._kwargs = kwargs + + @property + def context(self): + """Return exception context + + When exception object is created, the caller can supply some opaque + context for the upper layers to better understand the cause of the + exception. + + Returns + ------- + : :py:class:`dict` + Dict holding context specific data + """ + return self._kwargs.get('context', {}) + + +class ValueConstraintError(PyAsn1Error): + """ASN.1 type constraints violation exception + + The `ValueConstraintError` exception indicates an ASN.1 value + constraint violation. + + It might happen on value object instantiation (for scalar types) or on + serialization (for constructed types). + """ + + +class SubstrateUnderrunError(PyAsn1Error): + """ASN.1 data structure deserialization error + + The `SubstrateUnderrunError` exception indicates insufficient serialised + data on input of a de-serialization codec. + """ + + +class EndOfStreamError(SubstrateUnderrunError): + """ASN.1 data structure deserialization error + + The `EndOfStreamError` exception indicates the condition of the input + stream has been closed. + """ + + +class UnsupportedSubstrateError(PyAsn1Error): + """Unsupported substrate type to parse as ASN.1 data.""" + + +class PyAsn1UnicodeError(PyAsn1Error, UnicodeError): + """Unicode text processing error + + The `PyAsn1UnicodeError` exception is a base class for errors relating to + unicode text de/serialization. + + Apart from inheriting from :class:`PyAsn1Error`, it also inherits from + :class:`UnicodeError` to help the caller catching unicode-related errors. + """ + def __init__(self, message, unicode_error=None): + if isinstance(unicode_error, UnicodeError): + UnicodeError.__init__(self, *unicode_error.args) + PyAsn1Error.__init__(self, message) + + +class PyAsn1UnicodeDecodeError(PyAsn1UnicodeError, UnicodeDecodeError): + """Unicode text decoding error + + The `PyAsn1UnicodeDecodeError` exception represents a failure to + deserialize unicode text. + + Apart from inheriting from :class:`PyAsn1UnicodeError`, it also inherits + from :class:`UnicodeDecodeError` to help the caller catching unicode-related + errors. + """ + + +class PyAsn1UnicodeEncodeError(PyAsn1UnicodeError, UnicodeEncodeError): + """Unicode text encoding error + + The `PyAsn1UnicodeEncodeError` exception represents a failure to + serialize unicode text. + + Apart from inheriting from :class:`PyAsn1UnicodeError`, it also inherits + from :class:`UnicodeEncodeError` to help the caller catching + unicode-related errors. + """ + + diff --git a/contrib/python/pyasn1/py3/pyasn1/type/__init__.py b/contrib/python/pyasn1/py3/pyasn1/type/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/type/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/pyasn1/type/base.py b/contrib/python/pyasn1/py3/pyasn1/type/base.py new file mode 100644 index 0000000000..ac92c51afb --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/type/base.py @@ -0,0 +1,706 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys + +from pyasn1 import error +from pyasn1.type import constraint +from pyasn1.type import tag +from pyasn1.type import tagmap + +__all__ = ['Asn1Item', 'Asn1Type', 'SimpleAsn1Type', + 'ConstructedAsn1Type'] + + +class Asn1Item(object): + @classmethod + def getTypeId(cls, increment=1): + try: + Asn1Item._typeCounter += increment + except AttributeError: + Asn1Item._typeCounter = increment + return Asn1Item._typeCounter + + +class Asn1Type(Asn1Item): + """Base class for all classes representing ASN.1 types. + + In the user code, |ASN.1| class is normally used only for telling + ASN.1 objects from others. + + Note + ---- + For as long as ASN.1 is concerned, a way to compare ASN.1 types + is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods. + """ + #: Set or return a :py:class:`~pyasn1.type.tag.TagSet` object representing + #: ASN.1 tag(s) associated with |ASN.1| type. + tagSet = tag.TagSet() + + #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` + #: object imposing constraints on initialization values. + subtypeSpec = constraint.ConstraintsIntersection() + + # Disambiguation ASN.1 types identification + typeId = None + + def __init__(self, **kwargs): + readOnly = { + 'tagSet': self.tagSet, + 'subtypeSpec': self.subtypeSpec + } + + readOnly.update(kwargs) + + self.__dict__.update(readOnly) + + self._readOnly = readOnly + + def __setattr__(self, name, value): + if name[0] != '_' and name in self._readOnly: + raise error.PyAsn1Error('read-only instance attribute "%s"' % name) + + self.__dict__[name] = value + + def __str__(self): + return self.prettyPrint() + + @property + def readOnly(self): + return self._readOnly + + @property + def effectiveTagSet(self): + """For |ASN.1| type is equivalent to *tagSet* + """ + return self.tagSet # used by untagged types + + @property + def tagMap(self): + """Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects within callee object. + """ + return tagmap.TagMap({self.tagSet: self}) + + def isSameTypeWith(self, other, matchTags=True, matchConstraints=True): + """Examine |ASN.1| type for equality with other ASN.1 type. + + ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints + (:py:mod:`~pyasn1.type.constraint`) are examined when carrying + out ASN.1 types comparison. + + Python class inheritance relationship is NOT considered. + + Parameters + ---------- + other: a pyasn1 type object + Class instance representing ASN.1 type. + + Returns + ------- + : :class:`bool` + :obj:`True` if *other* is |ASN.1| type, + :obj:`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. + + Python class inheritance relationship is NOT considered. + + Parameters + ---------- + other: a pyasn1 type object + Class instance representing ASN.1 type. + + Returns + ------- + : :class:`bool` + :obj:`True` if *other* is a subtype of |ASN.1| type, + :obj:`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 noValue: + return False + return True + + def prettyPrint(self, scope=0): + raise NotImplementedError() + + # 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 + + # backward compatibility + def hasValue(self): + return self.isValue + +# Backward compatibility +Asn1ItemBase = Asn1Type + + +class NoValue(object): + """Create a singleton instance of NoValue class. + + The *NoValue* sentinel object represents an instance of ASN.1 schema + object as opposed to ASN.1 value object. + + Only ASN.1 schema-related operations can be performed on ASN.1 + schema objects. + + Warning + ------- + Any operation attempted on the *noValue* object will raise the + *PyAsn1Error* exception. + """ + skipMethods = { + '__slots__', + # attributes + '__getattribute__', + '__getattr__', + '__setattr__', + '__delattr__', + # class instance + '__class__', + '__init__', + '__del__', + '__new__', + '__repr__', + '__qualname__', + '__objclass__', + 'im_class', + '__sizeof__', + # pickle protocol + '__reduce__', + '__reduce_ex__', + '__getnewargs__', + '__getinitargs__', + '__getstate__', + '__setstate__', + } + + _instance = None + + def __new__(cls): + if cls._instance is None: + def getPlug(name): + def plug(self, *args, **kw): + raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % 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 + + def __getattr__(self, attr): + if attr in self.skipMethods: + raise AttributeError('Attribute %s not present' % attr) + + raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % attr) + + def __repr__(self): + return '<%s object>' % self.__class__.__name__ + + +noValue = NoValue() + + +class SimpleAsn1Type(Asn1Type): + """Base class for all simple classes representing ASN.1 types. + + ASN.1 distinguishes types by their ability to hold other objects. + Scalar types are known as *simple* in ASN.1. + + In the user code, |ASN.1| class is normally used only for telling + ASN.1 objects from others. + + Note + ---- + For as long as ASN.1 is concerned, a way to compare ASN.1 types + is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods. + """ + #: Default payload value + defaultValue = noValue + + def __init__(self, value=noValue, **kwargs): + Asn1Type.__init__(self, **kwargs) + if value is noValue: + value = self.defaultValue + else: + value = self.prettyIn(value) + try: + self.subtypeSpec(value) + + except error.PyAsn1Error: + exType, exValue, exTb = sys.exc_info() + raise exType('%s at %s' % (exValue, self.__class__.__name__)) + + self._value = value + + def __repr__(self): + representation = '%s %s object' % ( + self.__class__.__name__, self.isValue and 'value' or 'schema') + + for attr, value in self.readOnly.items(): + if value: + representation += ', %s %s' % (attr, value) + + if self.isValue: + value = self.prettyPrint() + if len(value) > 32: + value = value[:16] + '...' + value[-16:] + representation += ', payload [%s]' % value + + return '<%s>' % representation + + def __eq__(self, other): + return self is other and True or self._value == other + + def __ne__(self, other): + return self._value != other + + def __lt__(self, other): + return self._value < other + + def __le__(self, other): + return self._value <= other + + def __gt__(self, other): + return self._value > other + + def __ge__(self, other): + return self._value >= other + + if sys.version_info[0] <= 2: + def __nonzero__(self): + return self._value and True or False + else: + def __bool__(self): + return self._value and True or False + + def __hash__(self): + return hash(self._value) + + @property + def isValue(self): + """Indicate that |ASN.1| object represents ASN.1 value. + + If *isValue* is :obj:`False` then this object represents just + ASN.1 schema. + + If *isValue* is :obj:`True` then, in addition to its ASN.1 schema + features, this object can also be used like a Python built-in object + (e.g. :class:`int`, :class:`str`, :class:`dict` etc.). + + Returns + ------- + : :class:`bool` + :obj:`False` if object represents just ASN.1 schema. + :obj:`True` if object represents ASN.1 schema and can be used as a normal value. + + Note + ---- + There is an important distinction between PyASN1 schema and value objects. + The PyASN1 schema objects can only participate in ASN.1 schema-related + operations (e.g. defining or testing the structure of the data). Most + obvious uses of ASN.1 schema is to guide serialisation codecs whilst + encoding/decoding serialised ASN.1 contents. + + The PyASN1 value objects can **additionally** participate in many operations + involving regular Python objects (e.g. arithmetic, comprehension etc). + """ + return self._value is not noValue + + def clone(self, value=noValue, **kwargs): + """Create a modified version of |ASN.1| schema or value object. + + The `clone()` method accepts the same set arguments as |ASN.1| + class takes on instantiation except that all arguments + of the `clone()` method are optional. + + Whatever arguments are supplied, they are used to create a copy + of `self` taking precedence over the ones used to instantiate `self`. + + Note + ---- + Due to the immutable nature of the |ASN.1| object, if no arguments + are supplied, no new |ASN.1| object will be created and `self` will + be returned instead. + """ + if value is noValue: + if not kwargs: + return self + + value = self._value + + initializers = self.readOnly.copy() + initializers.update(kwargs) + + return self.__class__(value, **initializers) + + def subtype(self, value=noValue, **kwargs): + """Create a specialization of |ASN.1| schema or value object. + + The subtype relationship between ASN.1 types has no correlation with + subtype relationship between Python types. ASN.1 type is mainly identified + by its tag(s) (:py:class:`~pyasn1.type.tag.TagSet`) and value range + constraints (:py:class:`~pyasn1.type.constraint.ConstraintsIntersection`). + These ASN.1 type properties are implemented as |ASN.1| attributes. + + The `subtype()` method accepts the same set arguments as |ASN.1| + class takes on instantiation except that all parameters + of the `subtype()` method are optional. + + With the exception of the arguments described below, the rest of + supplied arguments they are used to create a copy of `self` taking + precedence over the ones used to instantiate `self`. + + The following arguments to `subtype()` create a ASN.1 subtype out of + |ASN.1| type: + + Other Parameters + ---------------- + implicitTag: :py:class:`~pyasn1.type.tag.Tag` + Implicitly apply given ASN.1 tag object to `self`'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 `self`'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 `self`'s, then + use the result as new object's ASN.1 constraints. + + Returns + ------- + : + new instance of |ASN.1| schema or value object + + Note + ---- + Due to the immutable nature of the |ASN.1| object, if no arguments + are supplied, no new |ASN.1| object will be created and `self` will + be returned instead. + """ + if value is noValue: + if not kwargs: + return self + + value = self._value + + initializers = self.readOnly.copy() + + implicitTag = kwargs.pop('implicitTag', None) + if implicitTag is not None: + initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag) + + explicitTag = kwargs.pop('explicitTag', None) + if explicitTag is not None: + initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag) + + for arg, option in kwargs.items(): + initializers[arg] += option + + return self.__class__(value, **initializers) + + def prettyIn(self, value): + return value + + def prettyOut(self, value): + return str(value) + + def prettyPrint(self, scope=0): + return self.prettyOut(self._value) + + def prettyPrintType(self, scope=0): + return '%s -> %s' % (self.tagSet, self.__class__.__name__) + +# Backward compatibility +AbstractSimpleAsn1Item = SimpleAsn1Type + +# +# Constructed types: +# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice +# * ASN1 types and values are represened by Python class instances +# * Value initialization is made for defaulted components only +# * Primary method of component addressing is by-position. Data model for base +# type is Python sequence. Additional type-specific addressing methods +# may be implemented for particular types. +# * SequenceOf and SetOf types do not implement any additional methods +# * Sequence, Set and Choice types also implement by-identifier addressing +# * Sequence, Set and Choice types also implement by-asn1-type (tag) addressing +# * Sequence and Set types may include optional and defaulted +# components +# * Constructed types hold a reference to component types used for value +# verification and ordering. +# * Component type is a scalar type for SequenceOf/SetOf types and a list +# of types for Sequence/Set/Choice. +# + + +class ConstructedAsn1Type(Asn1Type): + """Base class for all constructed classes representing ASN.1 types. + + ASN.1 distinguishes types by their ability to hold other objects. + Those "nesting" types are known as *constructed* in ASN.1. + + In the user code, |ASN.1| class is normally used only for telling + ASN.1 objects from others. + + Note + ---- + For as long as ASN.1 is concerned, a way to compare ASN.1 types + is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods. + """ + + #: If :obj:`True`, requires exact component type matching, + #: otherwise subtype relation is only enforced + strictConstraints = False + + componentType = None + + # backward compatibility, unused + sizeSpec = constraint.ConstraintsIntersection() + + def __init__(self, **kwargs): + readOnly = { + 'componentType': self.componentType, + # backward compatibility, unused + 'sizeSpec': self.sizeSpec + } + + # backward compatibility: preserve legacy sizeSpec support + kwargs = self._moveSizeSpec(**kwargs) + + readOnly.update(kwargs) + + Asn1Type.__init__(self, **readOnly) + + def _moveSizeSpec(self, **kwargs): + # backward compatibility, unused + sizeSpec = kwargs.pop('sizeSpec', self.sizeSpec) + if sizeSpec: + subtypeSpec = kwargs.pop('subtypeSpec', self.subtypeSpec) + if subtypeSpec: + subtypeSpec = sizeSpec + + else: + subtypeSpec += sizeSpec + + kwargs['subtypeSpec'] = subtypeSpec + + return kwargs + + def __repr__(self): + representation = '%s %s object' % ( + self.__class__.__name__, self.isValue and 'value' or 'schema' + ) + + for attr, value in self.readOnly.items(): + if value is not noValue: + representation += ', %s=%r' % (attr, value) + + if self.isValue and self.components: + representation += ', payload [%s]' % ', '.join( + [repr(x) for x in self.components]) + + return '<%s>' % representation + + def __eq__(self, other): + return self is other or self.components == other + + def __ne__(self, other): + return self.components != other + + def __lt__(self, other): + return self.components < other + + def __le__(self, other): + return self.components <= other + + def __gt__(self, other): + return self.components > other + + def __ge__(self, other): + return self.components >= other + + if sys.version_info[0] <= 2: + def __nonzero__(self): + return bool(self.components) + else: + def __bool__(self): + return bool(self.components) + + @property + def components(self): + raise error.PyAsn1Error('Method not implemented') + + def _cloneComponentValues(self, myClone, cloneValueFlag): + pass + + def clone(self, **kwargs): + """Create a modified version of |ASN.1| schema object. + + The `clone()` method accepts the same set arguments as |ASN.1| + class takes on instantiation except that all arguments + of the `clone()` method are optional. + + Whatever arguments are supplied, they are used to create a copy + of `self` taking precedence over the ones used to instantiate `self`. + + Possible values of `self` are never copied over thus `clone()` can + only create a new schema object. + + Returns + ------- + : + new instance of |ASN.1| type/value + + Note + ---- + Due to the mutable nature of the |ASN.1| object, even if no arguments + are supplied, a new |ASN.1| object will be created and returned. + """ + cloneValueFlag = kwargs.pop('cloneValueFlag', False) + + initializers = self.readOnly.copy() + initializers.update(kwargs) + + clone = self.__class__(**initializers) + + if cloneValueFlag: + self._cloneComponentValues(clone, cloneValueFlag) + + return clone + + def subtype(self, **kwargs): + """Create a specialization of |ASN.1| schema object. + + The `subtype()` method accepts the same set arguments as |ASN.1| + class takes on instantiation except that all parameters + of the `subtype()` method are optional. + + With the exception of the arguments described below, the rest of + supplied arguments they are used to create a copy of `self` taking + precedence over the ones used to instantiate `self`. + + The following arguments to `subtype()` create a ASN.1 subtype out of + |ASN.1| type. + + Other Parameters + ---------------- + implicitTag: :py:class:`~pyasn1.type.tag.Tag` + Implicitly apply given ASN.1 tag object to `self`'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 `self`'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 `self`'s, then + use the result as new object's ASN.1 constraints. + + + Returns + ------- + : + new instance of |ASN.1| type/value + + Note + ---- + Due to the mutable nature of the |ASN.1| object, even if no arguments + are supplied, a new |ASN.1| object will be created and returned. + """ + + initializers = self.readOnly.copy() + + cloneValueFlag = kwargs.pop('cloneValueFlag', False) + + implicitTag = kwargs.pop('implicitTag', None) + if implicitTag is not None: + initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag) + + explicitTag = kwargs.pop('explicitTag', None) + if explicitTag is not None: + initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag) + + for arg, option in kwargs.items(): + initializers[arg] += option + + clone = self.__class__(**initializers) + + if cloneValueFlag: + self._cloneComponentValues(clone, cloneValueFlag) + + return clone + + def getComponentByPosition(self, idx): + raise error.PyAsn1Error('Method not implemented') + + def setComponentByPosition(self, idx, value, verifyConstraints=True): + raise error.PyAsn1Error('Method not implemented') + + def setComponents(self, *args, **kwargs): + for idx, value in enumerate(args): + self[idx] = value + for k in kwargs: + self[k] = kwargs[k] + return self + + # backward compatibility + + def setDefaultComponents(self): + pass + + def getComponentType(self): + return self.componentType + + # backward compatibility, unused + def verifySizeSpec(self): + self.subtypeSpec(self) + + + # Backward compatibility +AbstractConstructedAsn1Item = ConstructedAsn1Type diff --git a/contrib/python/pyasn1/py3/pyasn1/type/char.py b/contrib/python/pyasn1/py3/pyasn1/type/char.py new file mode 100644 index 0000000000..13fbc7fa27 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/type/char.py @@ -0,0 +1,335 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys + +from pyasn1 import error +from pyasn1.type import tag +from pyasn1.type import univ + +__all__ = ['NumericString', 'PrintableString', 'TeletexString', 'T61String', 'VideotexString', + 'IA5String', 'GraphicString', 'VisibleString', 'ISO646String', + 'GeneralString', 'UniversalString', 'BMPString', 'UTF8String'] + +NoValue = univ.NoValue +noValue = univ.noValue + + +class AbstractCharacterString(univ.OctetString): + """Creates |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, + its objects are immutable and duck-type Python 2 :class:`str` or Python 3 + :class:`bytes`. When used in octet-stream context, |ASN.1| type assumes + "|encoding|" encoding. + + Keyword Args + ------------ + value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object + :class:`unicode` object (Python 2) or :class:`str` (Python 3), + alternatively :class:`str` (Python 2) or :class:`bytes` (Python 3) + representing octet-stream of serialised unicode string + (note `encoding` parameter) or |ASN.1| class instance. + If `value` is not given, schema object will be created. + + 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). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. + + 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 + ------ + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error + On constraint violation or bad initializer. + """ + + if sys.version_info[0] <= 2: + def __str__(self): + try: + # `str` is Py2 text representation + return self._value.encode(self.encoding) + + except UnicodeEncodeError: + exc = sys.exc_info()[1] + raise error.PyAsn1UnicodeEncodeError( + "Can't encode string '%s' with codec " + "%s" % (self._value, self.encoding), exc + ) + + def __unicode__(self): + return unicode(self._value) + + def prettyIn(self, value): + try: + if isinstance(value, unicode): + return value + elif isinstance(value, str): + return value.decode(self.encoding) + elif isinstance(value, (tuple, list)): + return self.prettyIn(''.join([chr(x) for x in value])) + elif isinstance(value, univ.OctetString): + return value.asOctets().decode(self.encoding) + else: + return unicode(value) + + except (UnicodeDecodeError, LookupError): + exc = sys.exc_info()[1] + raise error.PyAsn1UnicodeDecodeError( + "Can't decode string '%s' with codec " + "%s" % (value, self.encoding), exc + ) + + 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): + # `unicode` is Py3 text representation + return str(self._value) + + def __bytes__(self): + try: + return self._value.encode(self.encoding) + except UnicodeEncodeError: + exc = sys.exc_info()[1] + raise error.PyAsn1UnicodeEncodeError( + "Can't encode string '%s' with codec " + "%s" % (self._value, self.encoding), exc + ) + + def prettyIn(self, value): + try: + if isinstance(value, str): + return value + elif isinstance(value, bytes): + return value.decode(self.encoding) + elif isinstance(value, (tuple, list)): + return self.prettyIn(bytes(value)) + elif isinstance(value, univ.OctetString): + return value.asOctets().decode(self.encoding) + else: + return str(value) + + except (UnicodeDecodeError, LookupError): + exc = sys.exc_info()[1] + raise error.PyAsn1UnicodeDecodeError( + "Can't decode string '%s' with codec " + "%s" % (value, self.encoding), exc + ) + + def asOctets(self, padding=True): + return bytes(self) + + def asNumbers(self, padding=True): + return tuple(bytes(self)) + + # + # See OctetString.prettyPrint() for the explanation + # + + def prettyOut(self, value): + return value + + def prettyPrint(self, scope=0): + # first see if subclass has its own .prettyOut() + value = self.prettyOut(self._value) + + if value is not self._value: + return value + + return AbstractCharacterString.__str__(self) + + def __reversed__(self): + return reversed(self._value) + + +class NumericString(AbstractCharacterString): + __doc__ = AbstractCharacterString.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = AbstractCharacterString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18) + ) + encoding = 'us-ascii' + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() + + +class PrintableString(AbstractCharacterString): + __doc__ = AbstractCharacterString.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = AbstractCharacterString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19) + ) + encoding = 'us-ascii' + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() + + +class TeletexString(AbstractCharacterString): + __doc__ = AbstractCharacterString.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = AbstractCharacterString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20) + ) + encoding = 'iso-8859-1' + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() + + +class T61String(TeletexString): + __doc__ = TeletexString.__doc__ + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() + + +class VideotexString(AbstractCharacterString): + __doc__ = AbstractCharacterString.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = AbstractCharacterString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21) + ) + encoding = 'iso-8859-1' + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() + + +class IA5String(AbstractCharacterString): + __doc__ = AbstractCharacterString.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = AbstractCharacterString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22) + ) + encoding = 'us-ascii' + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() + + +class GraphicString(AbstractCharacterString): + __doc__ = AbstractCharacterString.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = AbstractCharacterString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25) + ) + encoding = 'iso-8859-1' + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() + + +class VisibleString(AbstractCharacterString): + __doc__ = AbstractCharacterString.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = AbstractCharacterString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26) + ) + encoding = 'us-ascii' + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() + + +class ISO646String(VisibleString): + __doc__ = VisibleString.__doc__ + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() + +class GeneralString(AbstractCharacterString): + __doc__ = AbstractCharacterString.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = AbstractCharacterString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27) + ) + encoding = 'iso-8859-1' + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() + + +class UniversalString(AbstractCharacterString): + __doc__ = AbstractCharacterString.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = AbstractCharacterString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28) + ) + encoding = "utf-32-be" + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() + + +class BMPString(AbstractCharacterString): + __doc__ = AbstractCharacterString.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = AbstractCharacterString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30) + ) + encoding = "utf-16-be" + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() + + +class UTF8String(AbstractCharacterString): + __doc__ = AbstractCharacterString.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = AbstractCharacterString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) + ) + encoding = "utf-8" + + # Optimization for faster codec lookup + typeId = AbstractCharacterString.getTypeId() diff --git a/contrib/python/pyasn1/py3/pyasn1/type/constraint.py b/contrib/python/pyasn1/py3/pyasn1/type/constraint.py new file mode 100644 index 0000000000..34b0060d9f --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/type/constraint.py @@ -0,0 +1,756 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +# Original concept and code by Mike C. Fletcher. +# +import sys + +from pyasn1.type import error + +__all__ = ['SingleValueConstraint', 'ContainedSubtypeConstraint', + 'ValueRangeConstraint', 'ValueSizeConstraint', + 'PermittedAlphabetConstraint', 'InnerTypeConstraint', + 'ConstraintsExclusion', 'ConstraintsIntersection', + 'ConstraintsUnion'] + + +class AbstractConstraint(object): + + def __init__(self, *values): + self._valueMap = set() + self._setValues(values) + self.__hash = hash((self.__class__.__name__, self._values)) + + def __call__(self, value, idx=None): + if not self._values: + return + + try: + self._testValue(value, idx) + + except error.ValueConstraintError: + raise error.ValueConstraintError( + '%s failed at: %r' % (self, sys.exc_info()[1]) + ) + + def __repr__(self): + representation = '%s object' % (self.__class__.__name__) + + if self._values: + representation += ', consts %s' % ', '.join( + [repr(x) for x in self._values]) + + return '<%s>' % representation + + def __eq__(self, other): + return self is other and True or self._values == other + + def __ne__(self, other): + return self._values != other + + def __lt__(self, other): + return self._values < other + + def __le__(self, other): + return self._values <= other + + def __gt__(self, other): + return self._values > other + + def __ge__(self, other): + return self._values >= other + + if sys.version_info[0] <= 2: + def __nonzero__(self): + return self._values and True or False + else: + def __bool__(self): + return self._values and True or False + + def __hash__(self): + return self.__hash + + def _setValues(self, values): + self._values = values + + def _testValue(self, value, idx): + raise error.ValueConstraintError(value) + + # Constraints derivation logic + def getValueMap(self): + return self._valueMap + + def isSuperTypeOf(self, otherConstraint): + # TODO: fix possible comparison of set vs scalars here + return (otherConstraint is self or + not self._values or + otherConstraint == self or + self in otherConstraint.getValueMap()) + + def isSubTypeOf(self, otherConstraint): + return (otherConstraint is self or + not self or + otherConstraint == self or + otherConstraint in self._valueMap) + + +class SingleValueConstraint(AbstractConstraint): + """Create a SingleValueConstraint object. + + The SingleValueConstraint satisfies any value that + is present in the set of permitted values. + + Objects of this type are iterable (emitting constraint values) and + can act as operands for some arithmetic operations e.g. addition + and subtraction. The latter can be used for combining multiple + SingleValueConstraint objects into one. + + The SingleValueConstraint object can be applied to + any ASN.1 type. + + Parameters + ---------- + *values: :class:`int` + Full set of values permitted by this constraint object. + + Examples + -------- + .. code-block:: python + + class DivisorOfSix(Integer): + ''' + ASN.1 specification: + + Divisor-Of-6 ::= INTEGER (1 | 2 | 3 | 6) + ''' + subtypeSpec = SingleValueConstraint(1, 2, 3, 6) + + # this will succeed + divisor_of_six = DivisorOfSix(1) + + # this will raise ValueConstraintError + divisor_of_six = DivisorOfSix(7) + """ + def _setValues(self, values): + self._values = values + self._set = set(values) + + def _testValue(self, value, idx): + if value not in self._set: + raise error.ValueConstraintError(value) + + # Constrains can be merged or reduced + + def __contains__(self, item): + return item in self._set + + def __iter__(self): + return iter(self._set) + + def __sub__(self, constraint): + return self.__class__(*(self._set.difference(constraint))) + + def __add__(self, constraint): + return self.__class__(*(self._set.union(constraint))) + + def __sub__(self, constraint): + return self.__class__(*(self._set.difference(constraint))) + + +class ContainedSubtypeConstraint(AbstractConstraint): + """Create a ContainedSubtypeConstraint object. + + The ContainedSubtypeConstraint satisfies any value that + is present in the set of permitted values and also + satisfies included constraints. + + The ContainedSubtypeConstraint object can be applied to + any ASN.1 type. + + Parameters + ---------- + *values: + Full set of values and constraint objects permitted + by this constraint object. + + Examples + -------- + .. code-block:: python + + class DivisorOfEighteen(Integer): + ''' + ASN.1 specification: + + Divisors-of-18 ::= INTEGER (INCLUDES Divisors-of-6 | 9 | 18) + ''' + subtypeSpec = ContainedSubtypeConstraint( + SingleValueConstraint(1, 2, 3, 6), 9, 18 + ) + + # this will succeed + divisor_of_eighteen = DivisorOfEighteen(9) + + # this will raise ValueConstraintError + divisor_of_eighteen = DivisorOfEighteen(10) + """ + def _testValue(self, value, idx): + for constraint in self._values: + if isinstance(constraint, AbstractConstraint): + constraint(value, idx) + elif value not in self._set: + raise error.ValueConstraintError(value) + + +class ValueRangeConstraint(AbstractConstraint): + """Create a ValueRangeConstraint object. + + The ValueRangeConstraint satisfies any value that + falls in the range of permitted values. + + The ValueRangeConstraint object can only be applied + to :class:`~pyasn1.type.univ.Integer` and + :class:`~pyasn1.type.univ.Real` types. + + Parameters + ---------- + start: :class:`int` + Minimum permitted value in the range (inclusive) + + end: :class:`int` + Maximum permitted value in the range (inclusive) + + Examples + -------- + .. code-block:: python + + class TeenAgeYears(Integer): + ''' + ASN.1 specification: + + TeenAgeYears ::= INTEGER (13 .. 19) + ''' + subtypeSpec = ValueRangeConstraint(13, 19) + + # this will succeed + teen_year = TeenAgeYears(18) + + # this will raise ValueConstraintError + teen_year = TeenAgeYears(20) + """ + def _testValue(self, value, idx): + if value < self.start or value > self.stop: + raise error.ValueConstraintError(value) + + def _setValues(self, values): + if len(values) != 2: + raise error.PyAsn1Error( + '%s: bad constraint values' % (self.__class__.__name__,) + ) + self.start, self.stop = values + if self.start > self.stop: + raise error.PyAsn1Error( + '%s: screwed constraint values (start > stop): %s > %s' % ( + self.__class__.__name__, + self.start, self.stop + ) + ) + AbstractConstraint._setValues(self, values) + + +class ValueSizeConstraint(ValueRangeConstraint): + """Create a ValueSizeConstraint object. + + The ValueSizeConstraint satisfies any value for + as long as its size falls within the range of + permitted sizes. + + The ValueSizeConstraint object can be applied + to :class:`~pyasn1.type.univ.BitString`, + :class:`~pyasn1.type.univ.OctetString` (including + all :ref:`character ASN.1 types <type.char>`), + :class:`~pyasn1.type.univ.SequenceOf` + and :class:`~pyasn1.type.univ.SetOf` types. + + Parameters + ---------- + minimum: :class:`int` + Minimum permitted size of the value (inclusive) + + maximum: :class:`int` + Maximum permitted size of the value (inclusive) + + Examples + -------- + .. code-block:: python + + class BaseballTeamRoster(SetOf): + ''' + ASN.1 specification: + + BaseballTeamRoster ::= SET SIZE (1..25) OF PlayerNames + ''' + componentType = PlayerNames() + subtypeSpec = ValueSizeConstraint(1, 25) + + # this will succeed + team = BaseballTeamRoster() + team.extend(['Jan', 'Matej']) + encode(team) + + # this will raise ValueConstraintError + team = BaseballTeamRoster() + team.extend(['Jan'] * 26) + encode(team) + + Note + ---- + Whenever ValueSizeConstraint is applied to mutable types + (e.g. :class:`~pyasn1.type.univ.SequenceOf`, + :class:`~pyasn1.type.univ.SetOf`), constraint + validation only happens at the serialisation phase rather + than schema instantiation phase (as it is with immutable + types). + """ + def _testValue(self, value, idx): + valueSize = len(value) + if valueSize < self.start or valueSize > self.stop: + raise error.ValueConstraintError(value) + + +class PermittedAlphabetConstraint(SingleValueConstraint): + """Create a PermittedAlphabetConstraint object. + + The PermittedAlphabetConstraint satisfies any character + string for as long as all its characters are present in + the set of permitted characters. + + Objects of this type are iterable (emitting constraint values) and + can act as operands for some arithmetic operations e.g. addition + and subtraction. + + The PermittedAlphabetConstraint object can only be applied + to the :ref:`character ASN.1 types <type.char>` such as + :class:`~pyasn1.type.char.IA5String`. + + Parameters + ---------- + *alphabet: :class:`str` + Full set of characters permitted by this constraint object. + + Example + ------- + .. code-block:: python + + class BooleanValue(IA5String): + ''' + ASN.1 specification: + + BooleanValue ::= IA5String (FROM ('T' | 'F')) + ''' + subtypeSpec = PermittedAlphabetConstraint('T', 'F') + + # this will succeed + truth = BooleanValue('T') + truth = BooleanValue('TF') + + # this will raise ValueConstraintError + garbage = BooleanValue('TAF') + + ASN.1 `FROM ... EXCEPT ...` clause can be modelled by combining multiple + PermittedAlphabetConstraint objects into one: + + Example + ------- + .. code-block:: python + + class Lipogramme(IA5String): + ''' + ASN.1 specification: + + Lipogramme ::= + IA5String (FROM (ALL EXCEPT ("e"|"E"))) + ''' + subtypeSpec = ( + PermittedAlphabetConstraint(*string.printable) - + PermittedAlphabetConstraint('e', 'E') + ) + + # this will succeed + lipogramme = Lipogramme('A work of fiction?') + + # this will raise ValueConstraintError + lipogramme = Lipogramme('Eel') + + Note + ---- + Although `ConstraintsExclusion` object could seemingly be used for this + purpose, practically, for it to work, it needs to represent its operand + constraints as sets and intersect one with the other. That would require + the insight into the constraint values (and their types) that are otherwise + hidden inside the constraint object. + + Therefore it's more practical to model `EXCEPT` clause at + `PermittedAlphabetConstraint` level instead. + """ + def _setValues(self, values): + self._values = values + self._set = set(values) + + def _testValue(self, value, idx): + if not self._set.issuperset(value): + raise error.ValueConstraintError(value) + + +class ComponentPresentConstraint(AbstractConstraint): + """Create a ComponentPresentConstraint object. + + The ComponentPresentConstraint is only satisfied when the value + is not `None`. + + The ComponentPresentConstraint object is typically used with + `WithComponentsConstraint`. + + Examples + -------- + .. code-block:: python + + present = ComponentPresentConstraint() + + # this will succeed + present('whatever') + + # this will raise ValueConstraintError + present(None) + """ + def _setValues(self, values): + self._values = ('<must be present>',) + + if values: + raise error.PyAsn1Error('No arguments expected') + + def _testValue(self, value, idx): + if value is None: + raise error.ValueConstraintError( + 'Component is not present:') + + +class ComponentAbsentConstraint(AbstractConstraint): + """Create a ComponentAbsentConstraint object. + + The ComponentAbsentConstraint is only satisfied when the value + is `None`. + + The ComponentAbsentConstraint object is typically used with + `WithComponentsConstraint`. + + Examples + -------- + .. code-block:: python + + absent = ComponentAbsentConstraint() + + # this will succeed + absent(None) + + # this will raise ValueConstraintError + absent('whatever') + """ + def _setValues(self, values): + self._values = ('<must be absent>',) + + if values: + raise error.PyAsn1Error('No arguments expected') + + def _testValue(self, value, idx): + if value is not None: + raise error.ValueConstraintError( + 'Component is not absent: %r' % value) + + +class WithComponentsConstraint(AbstractConstraint): + """Create a WithComponentsConstraint object. + + The `WithComponentsConstraint` satisfies any mapping object that has + constrained fields present or absent, what is indicated by + `ComponentPresentConstraint` and `ComponentAbsentConstraint` + objects respectively. + + The `WithComponentsConstraint` object is typically applied + to :class:`~pyasn1.type.univ.Set` or + :class:`~pyasn1.type.univ.Sequence` types. + + Parameters + ---------- + *fields: :class:`tuple` + Zero or more tuples of (`field`, `constraint`) indicating constrained + fields. + + Notes + ----- + On top of the primary use of `WithComponentsConstraint` (ensuring presence + or absence of particular components of a :class:`~pyasn1.type.univ.Set` or + :class:`~pyasn1.type.univ.Sequence`), it is also possible to pass any other + constraint objects or their combinations. In case of scalar fields, these + constraints will be verified in addition to the constraints belonging to + scalar components themselves. However, formally, these additional + constraints do not change the type of these ASN.1 objects. + + Examples + -------- + + .. code-block:: python + + class Item(Sequence): # Set is similar + ''' + ASN.1 specification: + + Item ::= SEQUENCE { + id INTEGER OPTIONAL, + name OCTET STRING OPTIONAL + } WITH COMPONENTS id PRESENT, name ABSENT | id ABSENT, name PRESENT + ''' + componentType = NamedTypes( + OptionalNamedType('id', Integer()), + OptionalNamedType('name', OctetString()) + ) + withComponents = ConstraintsUnion( + WithComponentsConstraint( + ('id', ComponentPresentConstraint()), + ('name', ComponentAbsentConstraint()) + ), + WithComponentsConstraint( + ('id', ComponentAbsentConstraint()), + ('name', ComponentPresentConstraint()) + ) + ) + + item = Item() + + # This will succeed + item['id'] = 1 + + # This will succeed + item.reset() + item['name'] = 'John' + + # This will fail (on encoding) + item.reset() + descr['id'] = 1 + descr['name'] = 'John' + """ + def _testValue(self, value, idx): + for field, constraint in self._values: + constraint(value.get(field)) + + def _setValues(self, values): + AbstractConstraint._setValues(self, values) + + +# This is a bit kludgy, meaning two op modes within a single constraint +class InnerTypeConstraint(AbstractConstraint): + """Value must satisfy the type and presence constraints""" + + def _testValue(self, value, idx): + if self.__singleTypeConstraint: + self.__singleTypeConstraint(value) + elif self.__multipleTypeConstraint: + if idx not in self.__multipleTypeConstraint: + raise error.ValueConstraintError(value) + constraint, status = self.__multipleTypeConstraint[idx] + if status == 'ABSENT': # XXX presence is not checked! + raise error.ValueConstraintError(value) + constraint(value) + + def _setValues(self, values): + self.__multipleTypeConstraint = {} + self.__singleTypeConstraint = None + for v in values: + if isinstance(v, tuple): + self.__multipleTypeConstraint[v[0]] = v[1], v[2] + else: + self.__singleTypeConstraint = v + AbstractConstraint._setValues(self, values) + + +# Logic operations on constraints + +class ConstraintsExclusion(AbstractConstraint): + """Create a ConstraintsExclusion logic operator object. + + The ConstraintsExclusion logic operator succeeds when the + value does *not* satisfy the operand constraint. + + The ConstraintsExclusion object can be applied to + any constraint and logic operator object. + + Parameters + ---------- + *constraints: + Constraint or logic operator objects. + + Examples + -------- + .. code-block:: python + + class LuckyNumber(Integer): + subtypeSpec = ConstraintsExclusion( + SingleValueConstraint(13) + ) + + # this will succeed + luckyNumber = LuckyNumber(12) + + # this will raise ValueConstraintError + luckyNumber = LuckyNumber(13) + + Note + ---- + The `FROM ... EXCEPT ...` ASN.1 clause should be modeled by combining + constraint objects into one. See `PermittedAlphabetConstraint` for more + information. + """ + def _testValue(self, value, idx): + for constraint in self._values: + try: + constraint(value, idx) + + except error.ValueConstraintError: + continue + + raise error.ValueConstraintError(value) + + def _setValues(self, values): + AbstractConstraint._setValues(self, values) + + +class AbstractConstraintSet(AbstractConstraint): + + def __getitem__(self, idx): + return self._values[idx] + + def __iter__(self): + return iter(self._values) + + def __add__(self, value): + return self.__class__(*(self._values + (value,))) + + def __radd__(self, value): + return self.__class__(*((value,) + self._values)) + + def __len__(self): + return len(self._values) + + # Constraints inclusion in sets + + def _setValues(self, values): + self._values = values + for constraint in values: + if constraint: + self._valueMap.add(constraint) + self._valueMap.update(constraint.getValueMap()) + + +class ConstraintsIntersection(AbstractConstraintSet): + """Create a ConstraintsIntersection logic operator object. + + The ConstraintsIntersection logic operator only succeeds + if *all* its operands succeed. + + The ConstraintsIntersection object can be applied to + any constraint and logic operator objects. + + The ConstraintsIntersection object duck-types the immutable + container object like Python :py:class:`tuple`. + + Parameters + ---------- + *constraints: + Constraint or logic operator objects. + + Examples + -------- + .. code-block:: python + + class CapitalAndSmall(IA5String): + ''' + ASN.1 specification: + + CapitalAndSmall ::= + IA5String (FROM ("A".."Z"|"a".."z")) + ''' + subtypeSpec = ConstraintsIntersection( + PermittedAlphabetConstraint('A', 'Z'), + PermittedAlphabetConstraint('a', 'z') + ) + + # this will succeed + capital_and_small = CapitalAndSmall('Hello') + + # this will raise ValueConstraintError + capital_and_small = CapitalAndSmall('hello') + """ + def _testValue(self, value, idx): + for constraint in self._values: + constraint(value, idx) + + +class ConstraintsUnion(AbstractConstraintSet): + """Create a ConstraintsUnion logic operator object. + + The ConstraintsUnion logic operator succeeds if + *at least* a single operand succeeds. + + The ConstraintsUnion object can be applied to + any constraint and logic operator objects. + + The ConstraintsUnion object duck-types the immutable + container object like Python :py:class:`tuple`. + + Parameters + ---------- + *constraints: + Constraint or logic operator objects. + + Examples + -------- + .. code-block:: python + + class CapitalOrSmall(IA5String): + ''' + ASN.1 specification: + + CapitalOrSmall ::= + IA5String (FROM ("A".."Z") | FROM ("a".."z")) + ''' + subtypeSpec = ConstraintsUnion( + PermittedAlphabetConstraint('A', 'Z'), + PermittedAlphabetConstraint('a', 'z') + ) + + # this will succeed + capital_or_small = CapitalAndSmall('Hello') + + # this will raise ValueConstraintError + capital_or_small = CapitalOrSmall('hello!') + """ + def _testValue(self, value, idx): + for constraint in self._values: + try: + constraint(value, idx) + except error.ValueConstraintError: + pass + else: + return + + raise error.ValueConstraintError( + 'all of %s failed for "%s"' % (self._values, value) + ) + +# TODO: +# refactor InnerTypeConstraint +# add tests for type check +# implement other constraint types +# make constraint validation easy to skip diff --git a/contrib/python/pyasn1/py3/pyasn1/type/error.py b/contrib/python/pyasn1/py3/pyasn1/type/error.py new file mode 100644 index 0000000000..0ff082abc2 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/type/error.py @@ -0,0 +1,11 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +from pyasn1.error import PyAsn1Error + + +class ValueConstraintError(PyAsn1Error): + pass diff --git a/contrib/python/pyasn1/py3/pyasn1/type/namedtype.py b/contrib/python/pyasn1/py3/pyasn1/type/namedtype.py new file mode 100644 index 0000000000..8dbc81f3c7 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/type/namedtype.py @@ -0,0 +1,561 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys + +from pyasn1 import error +from pyasn1.type import tag +from pyasn1.type import tagmap + +__all__ = ['NamedType', 'OptionalNamedType', 'DefaultedNamedType', + 'NamedTypes'] + +try: + any + +except NameError: + any = lambda x: bool(filter(bool, x)) + + +class NamedType(object): + """Create named field object for a constructed ASN.1 type. + + The |NamedType| object represents a single name and ASN.1 type of a constructed ASN.1 type. + + |NamedType| objects are immutable and duck-type Python :class:`tuple` objects + holding *name* and *asn1Object* components. + + Parameters + ---------- + name: :py:class:`str` + Field name + + asn1Object: + ASN.1 type object + """ + isOptional = False + isDefaulted = False + + def __init__(self, name, asn1Object, openType=None): + self.__name = name + self.__type = asn1Object + self.__nameAndType = name, asn1Object + self.__openType = openType + + def __repr__(self): + representation = '%s=%r' % (self.name, self.asn1Object) + + if self.openType: + representation += ', open type %r' % self.openType + + return '<%s object, type %s>' % ( + self.__class__.__name__, representation) + + def __eq__(self, other): + return self.__nameAndType == other + + def __ne__(self, other): + return self.__nameAndType != other + + def __lt__(self, other): + return self.__nameAndType < other + + def __le__(self, other): + return self.__nameAndType <= other + + def __gt__(self, other): + return self.__nameAndType > other + + def __ge__(self, other): + return self.__nameAndType >= other + + def __hash__(self): + return hash(self.__nameAndType) + + def __getitem__(self, idx): + return self.__nameAndType[idx] + + def __iter__(self): + return iter(self.__nameAndType) + + @property + def name(self): + return self.__name + + @property + def asn1Object(self): + return self.__type + + @property + def openType(self): + return self.__openType + + # Backward compatibility + + def getName(self): + return self.name + + def getType(self): + return self.asn1Object + + +class OptionalNamedType(NamedType): + __doc__ = NamedType.__doc__ + + isOptional = True + + +class DefaultedNamedType(NamedType): + __doc__ = NamedType.__doc__ + + isDefaulted = True + + +class NamedTypes(object): + """Create a collection of named fields for a constructed ASN.1 type. + + The NamedTypes object represents a collection of named fields of a constructed ASN.1 type. + + *NamedTypes* objects are immutable and duck-type Python :class:`dict` objects + holding *name* as keys and ASN.1 type object as values. + + Parameters + ---------- + *namedTypes: :class:`~pyasn1.type.namedtype.NamedType` + + Examples + -------- + + .. code-block:: python + + class Description(Sequence): + ''' + ASN.1 specification: + + Description ::= SEQUENCE { + surname IA5String, + first-name IA5String OPTIONAL, + age INTEGER DEFAULT 40 + } + ''' + componentType = NamedTypes( + NamedType('surname', IA5String()), + OptionalNamedType('first-name', IA5String()), + DefaultedNamedType('age', Integer(40)) + ) + + descr = Description() + descr['surname'] = 'Smith' + descr['first-name'] = 'John' + """ + def __init__(self, *namedTypes, **kwargs): + self.__namedTypes = namedTypes + self.__namedTypesLen = len(self.__namedTypes) + self.__minTagSet = self.__computeMinTagSet() + self.__nameToPosMap = self.__computeNameToPosMap() + self.__tagToPosMap = self.__computeTagToPosMap() + self.__ambiguousTypes = 'terminal' not in kwargs and self.__computeAmbiguousTypes() or {} + self.__uniqueTagMap = self.__computeTagMaps(unique=True) + self.__nonUniqueTagMap = self.__computeTagMaps(unique=False) + self.__hasOptionalOrDefault = any([True for namedType in self.__namedTypes + if namedType.isDefaulted or namedType.isOptional]) + self.__hasOpenTypes = any([True for namedType in self.__namedTypes + if namedType.openType]) + + self.__requiredComponents = frozenset( + [idx for idx, nt in enumerate(self.__namedTypes) if not nt.isOptional and not nt.isDefaulted] + ) + self.__keys = frozenset([namedType.name for namedType in self.__namedTypes]) + self.__values = tuple([namedType.asn1Object for namedType in self.__namedTypes]) + self.__items = tuple([(namedType.name, namedType.asn1Object) for namedType in self.__namedTypes]) + + def __repr__(self): + representation = ', '.join(['%r' % x for x in self.__namedTypes]) + return '<%s object, types %s>' % ( + self.__class__.__name__, representation) + + def __eq__(self, other): + return self.__namedTypes == other + + def __ne__(self, other): + return self.__namedTypes != other + + def __lt__(self, other): + return self.__namedTypes < other + + def __le__(self, other): + return self.__namedTypes <= other + + def __gt__(self, other): + return self.__namedTypes > other + + def __ge__(self, other): + return self.__namedTypes >= other + + def __hash__(self): + return hash(self.__namedTypes) + + def __getitem__(self, idx): + try: + return self.__namedTypes[idx] + + except TypeError: + return self.__namedTypes[self.__nameToPosMap[idx]] + + def __contains__(self, key): + return key in self.__nameToPosMap + + def __iter__(self): + return (x[0] for x in self.__namedTypes) + + if sys.version_info[0] <= 2: + def __nonzero__(self): + return self.__namedTypesLen > 0 + else: + def __bool__(self): + return self.__namedTypesLen > 0 + + def __len__(self): + return self.__namedTypesLen + + # Python dict protocol + + def values(self): + return self.__values + + def keys(self): + return self.__keys + + def items(self): + return self.__items + + def clone(self): + return self.__class__(*self.__namedTypes) + + class PostponedError(object): + def __init__(self, errorMsg): + self.__errorMsg = errorMsg + + def __getitem__(self, item): + raise error.PyAsn1Error(self.__errorMsg) + + def __computeTagToPosMap(self): + tagToPosMap = {} + for idx, namedType in enumerate(self.__namedTypes): + tagMap = namedType.asn1Object.tagMap + if isinstance(tagMap, NamedTypes.PostponedError): + return tagMap + if not tagMap: + continue + for _tagSet in tagMap.presentTypes: + if _tagSet in tagToPosMap: + return NamedTypes.PostponedError('Duplicate component tag %s at %s' % (_tagSet, namedType)) + tagToPosMap[_tagSet] = idx + + return tagToPosMap + + def __computeNameToPosMap(self): + nameToPosMap = {} + for idx, namedType in enumerate(self.__namedTypes): + if namedType.name in nameToPosMap: + return NamedTypes.PostponedError('Duplicate component name %s at %s' % (namedType.name, namedType)) + nameToPosMap[namedType.name] = idx + + return nameToPosMap + + def __computeAmbiguousTypes(self): + ambiguousTypes = {} + partialAmbiguousTypes = () + for idx, namedType in reversed(tuple(enumerate(self.__namedTypes))): + if namedType.isOptional or namedType.isDefaulted: + partialAmbiguousTypes = (namedType,) + partialAmbiguousTypes + else: + partialAmbiguousTypes = (namedType,) + if len(partialAmbiguousTypes) == len(self.__namedTypes): + ambiguousTypes[idx] = self + else: + ambiguousTypes[idx] = NamedTypes(*partialAmbiguousTypes, **dict(terminal=True)) + return ambiguousTypes + + 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 + ------ + ~pyasn1.error.PyAsn1Error + If given position is out of fields range + """ + try: + return self.__namedTypes[idx].asn1Object + + except IndexError: + raise error.PyAsn1Error('Type position out of range') + + def getPositionByType(self, tagSet): + """Return field position by its ASN.1 type. + + Parameters + ---------- + tagSet: :class:`~pysnmp.type.tag.TagSet` + ASN.1 tag set distinguishing one ASN.1 type from others. + + Returns + ------- + : :py:class:`int` + ASN.1 type position in fields set + + Raises + ------ + ~pyasn1.error.PyAsn1Error + If *tagSet* is not present or ASN.1 types are not unique within callee *NamedTypes* + """ + try: + 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 + ------ + ~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 + ------ + ~pyasn1.error.PyAsn1Error + If *name* is not present or not unique within callee *NamedTypes* + """ + try: + return self.__nameToPosMap[name] + + except KeyError: + raise error.PyAsn1Error('Name %s not found' % (name,)) + + def getTagMapNearPosition(self, idx): + """Return ASN.1 types that are allowed at or past given field position. + + Some ASN.1 serialisation 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 + ------ + ~pyasn1.error.PyAsn1Error + If given position is out of fields range + """ + try: + return self.__ambiguousTypes[idx].tagMap + + except KeyError: + raise error.PyAsn1Error('Type position out of range') + + def getPositionNearType(self, tagSet, idx): + """Return the closest field position where given ASN.1 type is allowed. + + Some ASN.1 serialisation 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 + ------ + ~pyasn1.error.PyAsn1Error + If *tagSet* is not present or not unique within callee *NamedTypes* + or *idx* is out of fields range + """ + try: + return idx + self.__ambiguousTypes[idx].getPositionByType(tagSet) + + except KeyError: + raise error.PyAsn1Error('Type position out of range') + + def __computeMinTagSet(self): + minTagSet = None + for namedType in self.__namedTypes: + asn1Object = namedType.asn1Object + + try: + tagSet = asn1Object.minTagSet + + except AttributeError: + tagSet = asn1Object.tagSet + + if minTagSet is None or tagSet < minTagSet: + minTagSet = tagSet + + return minTagSet or tag.TagSet() + + @property + def minTagSet(self): + """Return the minimal TagSet among ASN.1 type in callee *NamedTypes*. + + Some ASN.1 types/serialisation protocols require ASN.1 types to be + arranged based on their numerical tag value. The *minTagSet* property + returns that. + + Returns + ------- + : :class:`~pyasn1.type.tagset.TagSet` + Minimal TagSet among ASN.1 types in callee *NamedTypes* + """ + return self.__minTagSet + + def __computeTagMaps(self, unique): + presentTypes = {} + skipTypes = {} + defaultType = None + for namedType in self.__namedTypes: + tagMap = namedType.asn1Object.tagMap + if isinstance(tagMap, NamedTypes.PostponedError): + return tagMap + for tagSet in tagMap: + if unique and tagSet in presentTypes: + return NamedTypes.PostponedError('Non-unique tagSet %s of %s at %s' % (tagSet, namedType, self)) + presentTypes[tagSet] = namedType.asn1Object + skipTypes.update(tagMap.skipTypes) + + if defaultType is None: + defaultType = tagMap.defaultType + elif tagMap.defaultType is not None: + return NamedTypes.PostponedError('Duplicate default ASN.1 type at %s' % (self,)) + + return tagmap.TagMap(presentTypes, skipTypes, defaultType) + + @property + def tagMap(self): + """Return a *TagMap* object from tags and types recursively. + + Return a :class:`~pyasn1.type.tagmap.TagMap` object by + combining tags from *TagMap* objects of children types and + associating them with their immediate child type. + + Example + ------- + .. code-block:: python + + OuterType ::= CHOICE { + innerType INTEGER + } + + Calling *.tagMap* on *OuterType* will yield a map like this: + + .. code-block:: python + + Integer.tagSet -> Choice + """ + return self.__nonUniqueTagMap + + @property + def tagMapUnique(self): + """Return a *TagMap* object from unique tags and types recursively. + + Return a :class:`~pyasn1.type.tagmap.TagMap` object by + combining tags from *TagMap* objects of children types and + associating them with their immediate child type. + + Example + ------- + .. code-block:: python + + OuterType ::= CHOICE { + innerType INTEGER + } + + Calling *.tagMapUnique* on *OuterType* will yield a map like this: + + .. code-block:: python + + Integer.tagSet -> Choice + + Note + ---- + + Duplicate *TagSet* objects found in the tree of children + types would cause error. + """ + return self.__uniqueTagMap + + @property + def hasOptionalOrDefault(self): + return self.__hasOptionalOrDefault + + @property + def hasOpenTypes(self): + return self.__hasOpenTypes + + @property + def namedTypes(self): + return tuple(self.__namedTypes) + + @property + def requiredComponents(self): + return self.__requiredComponents diff --git a/contrib/python/pyasn1/py3/pyasn1/type/namedval.py b/contrib/python/pyasn1/py3/pyasn1/type/namedval.py new file mode 100644 index 0000000000..46a6496d03 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/type/namedval.py @@ -0,0 +1,192 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +# ASN.1 named integers +# +from pyasn1 import error + +__all__ = ['NamedValues'] + + +class NamedValues(object): + """Create named values object. + + The |NamedValues| object represents a collection of string names + associated with numeric IDs. These objects are used for giving + names to otherwise numerical values. + + |NamedValues| objects are immutable and duck-type Python + :class:`dict` object mapping ID to name and vice-versa. + + Parameters + ---------- + *args: variable number of two-element :py:class:`tuple` + + name: :py:class:`str` + Value label + + value: :py:class:`int` + Numeric value + + Keyword Args + ------------ + name: :py:class:`str` + Value label + + value: :py:class:`int` + Numeric value + + Examples + -------- + + .. code-block:: pycon + + >>> nv = NamedValues('a', 'b', ('c', 0), d=1) + >>> nv + >>> {'c': 0, 'd': 1, 'a': 2, 'b': 3} + >>> nv[0] + 'c' + >>> nv['a'] + 2 + """ + def __init__(self, *args, **kwargs): + self.__names = {} + self.__numbers = {} + + anonymousNames = [] + + for namedValue in args: + if isinstance(namedValue, (tuple, list)): + try: + name, number = namedValue + + except ValueError: + raise error.PyAsn1Error('Not a proper attribute-value pair %r' % (namedValue,)) + + else: + anonymousNames.append(namedValue) + continue + + if name in self.__names: + raise error.PyAsn1Error('Duplicate name %s' % (name,)) + + if number in self.__numbers: + raise error.PyAsn1Error('Duplicate number %s=%s' % (name, number)) + + self.__names[name] = number + self.__numbers[number] = name + + for name, number in kwargs.items(): + if name in self.__names: + raise error.PyAsn1Error('Duplicate name %s' % (name,)) + + if number in self.__numbers: + raise error.PyAsn1Error('Duplicate number %s=%s' % (name, number)) + + self.__names[name] = number + self.__numbers[number] = name + + if anonymousNames: + + number = self.__numbers and max(self.__numbers) + 1 or 0 + + for name in anonymousNames: + + if name in self.__names: + raise error.PyAsn1Error('Duplicate name %s' % (name,)) + + self.__names[name] = number + self.__numbers[number] = name + + number += 1 + + def __repr__(self): + representation = ', '.join(['%s=%d' % x for x in self.items()]) + + if len(representation) > 64: + representation = representation[:32] + '...' + representation[-32:] + + return '<%s object, enums %s>' % ( + self.__class__.__name__, representation) + + def __eq__(self, other): + return dict(self) == other + + def __ne__(self, other): + return dict(self) != other + + def __lt__(self, other): + return dict(self) < other + + def __le__(self, other): + return dict(self) <= other + + def __gt__(self, other): + return dict(self) > other + + def __ge__(self, other): + return dict(self) >= other + + def __hash__(self): + return hash(self.items()) + + # Python dict protocol (read-only) + + def __getitem__(self, key): + try: + return self.__numbers[key] + + except KeyError: + return self.__names[key] + + def __len__(self): + return len(self.__names) + + def __contains__(self, key): + return key in self.__names or key in self.__numbers + + def __iter__(self): + return iter(self.__names) + + def values(self): + return iter(self.__numbers) + + def keys(self): + return iter(self.__names) + + def items(self): + for name in self.__names: + yield name, self.__names[name] + + # support merging + + def __add__(self, namedValues): + return self.__class__(*tuple(self.items()) + tuple(namedValues.items())) + + # XXX clone/subtype? + + def clone(self, *args, **kwargs): + new = self.__class__(*args, **kwargs) + return self + new + + # legacy protocol + + def getName(self, value): + if value in self.__numbers: + return self.__numbers[value] + + def getValue(self, name): + if name in self.__names: + return self.__names[name] + + def getValues(self, *names): + try: + return [self.__names[name] for name in names] + + except KeyError: + raise error.PyAsn1Error( + 'Unknown bit identifier(s): %s' % (set(names).difference(self.__names),) + ) diff --git a/contrib/python/pyasn1/py3/pyasn1/type/opentype.py b/contrib/python/pyasn1/py3/pyasn1/type/opentype.py new file mode 100644 index 0000000000..5a15f896da --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/type/opentype.py @@ -0,0 +1,104 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# + +__all__ = ['OpenType'] + + +class OpenType(object): + """Create ASN.1 type map indexed by a value + + The *OpenType* object models an untyped field of a constructed ASN.1 + type. In ASN.1 syntax it is usually represented by the + `ANY DEFINED BY` for scalars or `SET OF ANY DEFINED BY`, + `SEQUENCE OF ANY DEFINED BY` for container types clauses. Typically + used together with :class:`~pyasn1.type.univ.Any` object. + + OpenType objects duck-type a read-only Python :class:`dict` objects, + however the passed `typeMap` is not copied, but stored by reference. + That means the user can manipulate `typeMap` at run time having this + reflected on *OpenType* object behavior. + + The |OpenType| class models an untyped field of a constructed ASN.1 + type. In ASN.1 syntax it is usually represented by the + `ANY DEFINED BY` for scalars or `SET OF ANY DEFINED BY`, + `SEQUENCE OF ANY DEFINED BY` for container types clauses. Typically + used with :class:`~pyasn1.type.univ.Any` type. + + Parameters + ---------- + name: :py:class:`str` + Field name + + typeMap: :py:class:`dict` + A map of value->ASN.1 type. It's stored by reference and can be + mutated later to register new mappings. + + Examples + -------- + + For untyped scalars: + + .. code-block:: python + + openType = OpenType( + 'id', {1: Integer(), + 2: OctetString()} + ) + Sequence( + componentType=NamedTypes( + NamedType('id', Integer()), + NamedType('blob', Any(), openType=openType) + ) + ) + + For untyped `SET OF` or `SEQUENCE OF` vectors: + + .. code-block:: python + + openType = OpenType( + 'id', {1: Integer(), + 2: OctetString()} + ) + Sequence( + componentType=NamedTypes( + NamedType('id', Integer()), + NamedType('blob', SetOf(componentType=Any()), + openType=openType) + ) + ) + """ + + def __init__(self, name, typeMap=None): + self.__name = name + if typeMap is None: + self.__typeMap = {} + else: + self.__typeMap = typeMap + + @property + def name(self): + return self.__name + + # Python dict protocol + + def values(self): + return self.__typeMap.values() + + def keys(self): + return self.__typeMap.keys() + + def items(self): + return self.__typeMap.items() + + def __contains__(self, key): + return key in self.__typeMap + + def __getitem__(self, key): + return self.__typeMap[key] + + def __iter__(self): + return iter(self.__typeMap) diff --git a/contrib/python/pyasn1/py3/pyasn1/type/tag.py b/contrib/python/pyasn1/py3/pyasn1/type/tag.py new file mode 100644 index 0000000000..a21a405eb1 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/type/tag.py @@ -0,0 +1,335 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +from pyasn1 import error + +__all__ = ['tagClassUniversal', 'tagClassApplication', 'tagClassContext', + 'tagClassPrivate', 'tagFormatSimple', 'tagFormatConstructed', + 'tagCategoryImplicit', 'tagCategoryExplicit', + 'tagCategoryUntagged', 'Tag', 'TagSet'] + +#: Identifier for ASN.1 class UNIVERSAL +tagClassUniversal = 0x00 + +#: Identifier for ASN.1 class APPLICATION +tagClassApplication = 0x40 + +#: Identifier for ASN.1 class context-specific +tagClassContext = 0x80 + +#: Identifier for ASN.1 class private +tagClassPrivate = 0xC0 + +#: Identifier for "simple" ASN.1 structure (e.g. scalar) +tagFormatSimple = 0x00 + +#: Identifier for "constructed" ASN.1 structure (e.g. may have inner components) +tagFormatConstructed = 0x20 + +tagCategoryImplicit = 0x01 +tagCategoryExplicit = 0x02 +tagCategoryUntagged = 0x04 + + +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): + if tagId < 0: + raise error.PyAsn1Error('Negative tag ID (%s) not allowed' % tagId) + self.__tagClass = tagClass + self.__tagFormat = tagFormat + self.__tagId = tagId + self.__tagClassId = tagClass, tagId + self.__hash = hash(self.__tagClassId) + + def __repr__(self): + representation = '[%s:%s:%s]' % ( + self.__tagClass, self.__tagFormat, self.__tagId) + return '<%s object, tag %s>' % ( + self.__class__.__name__, representation) + + def __eq__(self, other): + return self.__tagClassId == other + + def __ne__(self, other): + return self.__tagClassId != other + + def __lt__(self, other): + return self.__tagClassId < other + + 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): + return self.__hash + + 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): + return self.__class__(self.__tagClass & otherTag.tagClass, + self.__tagFormat & otherTag.tagFormat, + self.__tagId & otherTag.tagId) + + def __or__(self, otherTag): + return self.__class__(self.__tagClass | otherTag.tagClass, + self.__tagFormat | otherTag.tagFormat, + self.__tagId | otherTag.tagId) + + @property + def tagClass(self): + """ASN.1 tag class + + 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. + + Examples + -------- + .. code-block:: python + + class OrderNumber(NumericString): + ''' + ASN.1 specification + + Order-number ::= + [APPLICATION 5] IMPLICIT NumericString + ''' + tagSet = NumericString.tagSet.tagImplicitly( + Tag(tagClassApplication, tagFormatSimple, 5) + ) + + orderNumber = OrderNumber('1234') + """ + def __init__(self, baseTag=(), *superTags): + self.__baseTag = baseTag + self.__superTags = superTags + self.__superTagsClassId = tuple( + [(superTag.tagClass, superTag.tagId) for superTag in superTags] + ) + self.__lenOfSuperTags = len(superTags) + self.__hash = hash(self.__superTagsClassId) + + def __repr__(self): + representation = '-'.join(['%s:%s:%s' % (x.tagClass, x.tagFormat, x.tagId) + for x in self.__superTags]) + if representation: + representation = 'tags ' + representation + else: + representation = 'untagged' + + return '<%s object, %s>' % (self.__class__.__name__, representation) + + def __add__(self, superTag): + return self.__class__(self.__baseTag, *self.__superTags + (superTag,)) + + def __radd__(self, superTag): + return self.__class__(self.__baseTag, *(superTag,) + self.__superTags) + + def __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.__superTagsClassId == other + + def __ne__(self, other): + return self.__superTagsClassId != other + + def __lt__(self, other): + return self.__superTagsClassId < other + + def __le__(self, other): + return self.__superTagsClassId <= other + + def __gt__(self, other): + return self.__superTagsClassId > other + + def __ge__(self, other): + return self.__superTagsClassId >= other + + def __hash__(self): + return self.__hash + + def __len__(self): + return self.__lenOfSuperTags + + @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): + """Return explicitly tagged *TagSet* + + Create a new *TagSet* representing callee *TagSet* explicitly tagged + with passed tag(s). With explicit tagging mode, new tags are appended + to existing tag(s). + + 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 + + def tagImplicitly(self, 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: + superTag = Tag(superTag.tagClass, self.__superTags[-1].tagFormat, superTag.tagId) + return self[:-1] + superTag + + 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` + :obj:`True` if callee is a supertype of *tagSet* + """ + if len(tagSet) < self.__lenOfSuperTags: + return False + return self.__superTags == tagSet[:self.__lenOfSuperTags] + + # Backward compatibility + + def getBaseTag(self): + return self.__baseTag + +def initTagSet(tag): + return TagSet(tag, tag) diff --git a/contrib/python/pyasn1/py3/pyasn1/type/tagmap.py b/contrib/python/pyasn1/py3/pyasn1/type/tagmap.py new file mode 100644 index 0000000000..2f0e660264 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/type/tagmap.py @@ -0,0 +1,96 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +from pyasn1 import error + +__all__ = ['TagMap'] + + +class TagMap(object): + """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): + return (tagSet in self.__presentTypes or + self.__defaultType is not None and tagSet not in self.__skipTypes) + + def __getitem__(self, tagSet): + try: + return self.__presentTypes[tagSet] + except KeyError: + if self.__defaultType is None: + raise KeyError() + elif tagSet in self.__skipTypes: + raise error.PyAsn1Error('Key in negative map') + else: + return self.__defaultType + + def __iter__(self): + return iter(self.__presentTypes) + + def __repr__(self): + representation = '%s object' % self.__class__.__name__ + + if self.__presentTypes: + representation += ', present %s' % repr(self.__presentTypes) + + if self.__skipTypes: + representation += ', skip %s' % repr(self.__skipTypes) + + if self.__defaultType is not None: + representation += ', default %s' % repr(self.__defaultType) + + return '<%s>' % representation + + @property + def presentTypes(self): + """Return *TagSet* to ASN.1 type map present in callee *TagMap*""" + return self.__presentTypes + + @property + def skipTypes(self): + """Return *TagSet* collection unconditionally absent in callee *TagMap*""" + return self.__skipTypes + + @property + def defaultType(self): + """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 diff --git a/contrib/python/pyasn1/py3/pyasn1/type/univ.py b/contrib/python/pyasn1/py3/pyasn1/type/univ.py new file mode 100644 index 0000000000..c5d0778096 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/type/univ.py @@ -0,0 +1,3305 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import math +import sys + +from pyasn1 import error +from pyasn1.codec.ber import eoo +from pyasn1.compat import integer +from pyasn1.compat import octets +from pyasn1.type import base +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import namedval +from pyasn1.type import tag +from pyasn1.type import tagmap + +NoValue = base.NoValue +noValue = NoValue() + +__all__ = ['Integer', 'Boolean', 'BitString', 'OctetString', 'Null', + 'ObjectIdentifier', 'Real', 'Enumerated', + 'SequenceOfAndSetOfBase', 'SequenceOf', 'SetOf', + 'SequenceAndSetBase', 'Sequence', 'Set', 'Choice', 'Any', + 'NoValue', 'noValue'] + +# "Simple" ASN.1 types (yet incomplete) + + +class Integer(base.SimpleAsn1Type): + """Create |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python :class:`int` objects. + + Keyword Args + ------------ + value: :class:`int`, :class:`str` or |ASN.1| object + Python :class:`int` or :class:`str` literal or |ASN.1| class + instance. If `value` is not given, schema object will be created. + + 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). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. + + namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` + Object representing non-default symbolic aliases for numbers + + Raises + ------ + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error + On constraint violation or bad initializer. + + Examples + -------- + + .. code-block:: python + + class ErrorCode(Integer): + ''' + ASN.1 specification: + + ErrorCode ::= + INTEGER { disk-full(1), no-disk(-1), + disk-not-formatted(2) } + + error ErrorCode ::= disk-full + ''' + namedValues = NamedValues( + ('disk-full', 1), ('no-disk', -1), + ('disk-not-formatted', 2) + ) + + error = ErrorCode('disk-full') + """ + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02) + ) + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = constraint.ConstraintsIntersection() + + #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object + #: representing symbolic aliases for numbers + namedValues = namedval.NamedValues() + + # Optimization for faster codec lookup + typeId = base.SimpleAsn1Type.getTypeId() + + def __init__(self, value=noValue, **kwargs): + if 'namedValues' not in kwargs: + kwargs['namedValues'] = self.namedValues + + base.SimpleAsn1Type.__init__(self, value, **kwargs) + + def __and__(self, value): + return self.clone(self._value & value) + + def __rand__(self, value): + return self.clone(value & self._value) + + def __or__(self, value): + return self.clone(self._value | value) + + def __ror__(self, value): + return self.clone(value | self._value) + + def __xor__(self, value): + return self.clone(self._value ^ value) + + def __rxor__(self, value): + return self.clone(value ^ self._value) + + def __lshift__(self, value): + return self.clone(self._value << value) + + def __rshift__(self, value): + return self.clone(self._value >> value) + + def __add__(self, value): + return self.clone(self._value + value) + + def __radd__(self, value): + return self.clone(value + self._value) + + def __sub__(self, value): + return self.clone(self._value - value) + + def __rsub__(self, value): + return self.clone(value - self._value) + + def __mul__(self, value): + return self.clone(self._value * value) + + def __rmul__(self, value): + return self.clone(value * self._value) + + def __mod__(self, value): + return self.clone(self._value % value) + + def __rmod__(self, value): + return self.clone(value % self._value) + + def __pow__(self, value, modulo=None): + return self.clone(pow(self._value, value, modulo)) + + def __rpow__(self, value): + return self.clone(pow(value, self._value)) + + def __floordiv__(self, value): + return self.clone(self._value // value) + + def __rfloordiv__(self, value): + return self.clone(value // self._value) + + if sys.version_info[0] <= 2: + def __div__(self, value): + if isinstance(value, float): + return Real(self._value / value) + else: + return self.clone(self._value / value) + + def __rdiv__(self, value): + if isinstance(value, float): + return Real(value / self._value) + else: + return self.clone(value / self._value) + else: + def __truediv__(self, value): + return Real(self._value / value) + + def __rtruediv__(self, value): + return Real(value / self._value) + + def __divmod__(self, value): + return self.clone(divmod(self._value, value)) + + def __rdivmod__(self, value): + return self.clone(divmod(value, self._value)) + + __hash__ = base.SimpleAsn1Type.__hash__ + + def __int__(self): + return int(self._value) + + if sys.version_info[0] <= 2: + def __long__(self): + return long(self._value) + + def __float__(self): + return float(self._value) + + def __abs__(self): + return self.clone(abs(self._value)) + + def __index__(self): + return int(self._value) + + def __pos__(self): + return self.clone(+self._value) + + def __neg__(self): + return self.clone(-self._value) + + def __invert__(self): + return self.clone(~self._value) + + def __round__(self, n=0): + r = round(self._value, n) + if n: + return self.clone(r) + else: + return r + + def __floor__(self): + return math.floor(self._value) + + def __ceil__(self): + return math.ceil(self._value) + + def __trunc__(self): + return self.clone(math.trunc(self._value)) + + def __lt__(self, value): + return self._value < value + + def __le__(self, value): + return self._value <= value + + def __eq__(self, value): + return self._value == value + + def __ne__(self, value): + return self._value != value + + def __gt__(self, value): + return self._value > value + + def __ge__(self, value): + return self._value >= value + + def prettyIn(self, value): + try: + return int(value) + + except ValueError: + try: + return self.namedValues[value] + + except KeyError: + raise error.PyAsn1Error( + 'Can\'t coerce %r into integer: %s' % (value, sys.exc_info()[1]) + ) + + def prettyOut(self, value): + try: + return str(self.namedValues[value]) + + except KeyError: + return str(value) + + # backward compatibility + + def getNamedValues(self): + return self.namedValues + + +class Boolean(Integer): + """Create |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python :class:`int` objects. + + Keyword Args + ------------ + value: :class:`int`, :class:`str` or |ASN.1| object + Python :class:`int` or :class:`str` literal or |ASN.1| class + instance. If `value` is not given, schema object will be created. + + 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).Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. + + namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` + Object representing non-default symbolic aliases for numbers + + Raises + ------ + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error + On constraint violation or bad initializer. + + Examples + -------- + .. code-block:: python + + class RoundResult(Boolean): + ''' + ASN.1 specification: + + RoundResult ::= BOOLEAN + + ok RoundResult ::= TRUE + ko RoundResult ::= FALSE + ''' + ok = RoundResult(True) + ko = RoundResult(False) + """ + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01), + ) + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = Integer.subtypeSpec + constraint.SingleValueConstraint(0, 1) + + #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object + #: representing symbolic aliases for numbers + namedValues = namedval.NamedValues(('False', 0), ('True', 1)) + + # Optimization for faster codec lookup + typeId = Integer.getTypeId() + +if sys.version_info[0] < 3: + SizedIntegerBase = long +else: + SizedIntegerBase = int + + +class SizedInteger(SizedIntegerBase): + bitLength = leadingZeroBits = None + + def setBitLength(self, bitLength): + self.bitLength = bitLength + self.leadingZeroBits = max(bitLength - integer.bitLength(self), 0) + return self + + def __len__(self): + if self.bitLength is None: + self.setBitLength(integer.bitLength(self)) + + return self.bitLength + + +class BitString(base.SimpleAsn1Type): + """Create |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type both Python :class:`tuple` (as a tuple + of bits) and :class:`int` objects. + + Keyword Args + ------------ + value: :class:`int`, :class:`str` or |ASN.1| object + Python :class:`int` or :class:`str` literal representing binary + or hexadecimal number or sequence of integer bits or |ASN.1| object. + If `value` is not given, schema object will be created. + + 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). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. + + namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` + Object representing non-default symbolic aliases for numbers + + binValue: :py:class:`str` + Binary string initializer to use instead of the *value*. + Example: '10110011'. + + hexValue: :py:class:`str` + Hexadecimal string initializer to use instead of the *value*. + Example: 'DEADBEEF'. + + Raises + ------ + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error + On constraint violation or bad initializer. + + Examples + -------- + .. code-block:: python + + class Rights(BitString): + ''' + ASN.1 specification: + + Rights ::= BIT STRING { user-read(0), user-write(1), + group-read(2), group-write(3), + other-read(4), other-write(5) } + + group1 Rights ::= { group-read, group-write } + group2 Rights ::= '0011'B + group3 Rights ::= '3'H + ''' + namedValues = NamedValues( + ('user-read', 0), ('user-write', 1), + ('group-read', 2), ('group-write', 3), + ('other-read', 4), ('other-write', 5) + ) + + group1 = Rights(('group-read', 'group-write')) + group2 = Rights('0011') + group3 = Rights(0x3) + """ + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03) + ) + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = constraint.ConstraintsIntersection() + + #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object + #: representing symbolic aliases for numbers + namedValues = namedval.NamedValues() + + # Optimization for faster codec lookup + typeId = base.SimpleAsn1Type.getTypeId() + + defaultBinValue = defaultHexValue = noValue + + def __init__(self, value=noValue, **kwargs): + if value is noValue: + if kwargs: + try: + value = self.fromBinaryString(kwargs.pop('binValue'), internalFormat=True) + + except KeyError: + pass + + try: + value = self.fromHexString(kwargs.pop('hexValue'), internalFormat=True) + + except KeyError: + pass + + if value is noValue: + if self.defaultBinValue is not noValue: + value = self.fromBinaryString(self.defaultBinValue, internalFormat=True) + + elif self.defaultHexValue is not noValue: + value = self.fromHexString(self.defaultHexValue, internalFormat=True) + + if 'namedValues' not in kwargs: + kwargs['namedValues'] = self.namedValues + + base.SimpleAsn1Type.__init__(self, value, **kwargs) + + def __str__(self): + return self.asBinary() + + def __eq__(self, other): + other = self.prettyIn(other) + return self is other or self._value == other and len(self._value) == len(other) + + def __ne__(self, other): + other = self.prettyIn(other) + return self._value != other or len(self._value) != len(other) + + def __lt__(self, other): + other = self.prettyIn(other) + return len(self._value) < len(other) or len(self._value) == len(other) and self._value < other + + def __le__(self, other): + other = self.prettyIn(other) + return len(self._value) <= len(other) or len(self._value) == len(other) and self._value <= other + + def __gt__(self, other): + other = self.prettyIn(other) + return len(self._value) > len(other) or len(self._value) == len(other) and self._value > other + + def __ge__(self, other): + other = self.prettyIn(other) + return len(self._value) >= len(other) or len(self._value) == len(other) and self._value >= other + + # Immutable sequence object protocol + + def __len__(self): + return len(self._value) + + def __getitem__(self, i): + if i.__class__ is slice: + return self.clone([self[x] for x in range(*i.indices(len(self)))]) + else: + length = len(self._value) - 1 + if i > length or i < 0: + raise IndexError('bit index out of range') + return (self._value >> (length - i)) & 1 + + def __iter__(self): + length = len(self._value) + while length: + length -= 1 + yield (self._value >> length) & 1 + + def __reversed__(self): + return reversed(tuple(self)) + + # arithmetic operators + + def __add__(self, value): + value = self.prettyIn(value) + return self.clone(SizedInteger(self._value << len(value) | value).setBitLength(len(self._value) + len(value))) + + def __radd__(self, value): + value = self.prettyIn(value) + return self.clone(SizedInteger(value << len(self._value) | self._value).setBitLength(len(self._value) + len(value))) + + def __mul__(self, value): + bitString = self._value + while value > 1: + bitString <<= len(self._value) + bitString |= self._value + value -= 1 + return self.clone(bitString) + + def __rmul__(self, value): + return self * value + + def __lshift__(self, count): + return self.clone(SizedInteger(self._value << count).setBitLength(len(self._value) + count)) + + def __rshift__(self, count): + return self.clone(SizedInteger(self._value >> count).setBitLength(max(0, len(self._value) - count))) + + def __int__(self): + return int(self._value) + + def __float__(self): + return float(self._value) + + if sys.version_info[0] < 3: + def __long__(self): + return self._value + + def asNumbers(self): + """Get |ASN.1| value as a sequence of 8-bit integers. + + If |ASN.1| object length is not a multiple of 8, result + will be left-padded with zeros. + """ + return tuple(octets.octs2ints(self.asOctets())) + + def asOctets(self): + """Get |ASN.1| value as a sequence of octets. + + If |ASN.1| object length is not a multiple of 8, result + will be left-padded with zeros. + """ + return integer.to_bytes(self._value, length=len(self)) + + def asInteger(self): + """Get |ASN.1| value as a single integer value. + """ + return self._value + + def asBinary(self): + """Get |ASN.1| value as a text string of bits. + """ + binString = bin(self._value)[2:] + return '0' * (len(self._value) - len(binString)) + binString + + @classmethod + def fromHexString(cls, value, internalFormat=False, prepend=None): + """Create a |ASN.1| object initialized from the hex string. + + Parameters + ---------- + value: :class:`str` + Text string like 'DEADBEEF' + """ + try: + value = SizedInteger(value, 16).setBitLength(len(value) * 4) + + except ValueError: + raise error.PyAsn1Error('%s.fromHexString() error: %s' % (cls.__name__, sys.exc_info()[1])) + + if prepend is not None: + value = SizedInteger( + (SizedInteger(prepend) << len(value)) | value + ).setBitLength(len(prepend) + len(value)) + + if not internalFormat: + value = cls(value) + + return value + + @classmethod + def fromBinaryString(cls, value, internalFormat=False, prepend=None): + """Create a |ASN.1| object initialized from a string of '0' and '1'. + + Parameters + ---------- + value: :class:`str` + Text string like '1010111' + """ + try: + value = SizedInteger(value or '0', 2).setBitLength(len(value)) + + except ValueError: + raise error.PyAsn1Error('%s.fromBinaryString() error: %s' % (cls.__name__, sys.exc_info()[1])) + + if prepend is not None: + value = SizedInteger( + (SizedInteger(prepend) << len(value)) | value + ).setBitLength(len(prepend) + len(value)) + + if not internalFormat: + value = cls(value) + + return value + + @classmethod + def fromOctetString(cls, value, internalFormat=False, prepend=None, padding=0): + """Create a |ASN.1| object initialized from a string. + + Parameters + ---------- + value: :class:`str` (Py2) or :class:`bytes` (Py3) + Text string like '\\\\x01\\\\xff' (Py2) or b'\\\\x01\\\\xff' (Py3) + """ + value = SizedInteger(integer.from_bytes(value) >> padding).setBitLength(len(value) * 8 - padding) + + if prepend is not None: + value = SizedInteger( + (SizedInteger(prepend) << len(value)) | value + ).setBitLength(len(prepend) + len(value)) + + if not internalFormat: + value = cls(value) + + return value + + def prettyIn(self, value): + if isinstance(value, SizedInteger): + return value + elif octets.isStringType(value): + if not value: + return SizedInteger(0).setBitLength(0) + + elif value[0] == '\'': # "'1011'B" -- ASN.1 schema representation (deprecated) + if value[-2:] == '\'B': + return self.fromBinaryString(value[1:-2], internalFormat=True) + elif value[-2:] == '\'H': + return self.fromHexString(value[1:-2], internalFormat=True) + else: + raise error.PyAsn1Error( + 'Bad BIT STRING value notation %s' % (value,) + ) + + elif self.namedValues and not value.isdigit(): # named bits like 'Urgent, Active' + names = [x.strip() for x in value.split(',')] + + try: + + bitPositions = [self.namedValues[name] for name in names] + + except KeyError: + raise error.PyAsn1Error('unknown bit name(s) in %r' % (names,)) + + rightmostPosition = max(bitPositions) + + number = 0 + for bitPosition in bitPositions: + number |= 1 << (rightmostPosition - bitPosition) + + return SizedInteger(number).setBitLength(rightmostPosition + 1) + + elif value.startswith('0x'): + return self.fromHexString(value[2:], internalFormat=True) + + elif value.startswith('0b'): + return self.fromBinaryString(value[2:], internalFormat=True) + + else: # assume plain binary string like '1011' + return self.fromBinaryString(value, internalFormat=True) + + elif isinstance(value, (tuple, list)): + return self.fromBinaryString(''.join([b and '1' or '0' for b in value]), internalFormat=True) + + elif isinstance(value, BitString): + return SizedInteger(value).setBitLength(len(value)) + + elif isinstance(value, intTypes): + return SizedInteger(value) + + else: + raise error.PyAsn1Error( + 'Bad BitString initializer type \'%s\'' % (value,) + ) + + +class OctetString(base.SimpleAsn1Type): + """Create |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python 2 :class:`str` or + Python 3 :class:`bytes`. When used in Unicode context, |ASN.1| type + assumes "|encoding|" serialisation. + + Keyword Args + ------------ + value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object + class:`str` (Python 2) or :class:`bytes` (Python 3), alternatively + class:`unicode` object (Python 2) or :class:`str` (Python 3) + representing character string to be serialised into octets + (note `encoding` parameter) or |ASN.1| object. + If `value` is not given, schema object will be created. + + 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). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. + + 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 text string context. + + binValue: :py:class:`str` + Binary string initializer to use instead of the *value*. + Example: '10110011'. + + hexValue: :py:class:`str` + Hexadecimal string initializer to use instead of the *value*. + Example: 'DEADBEEF'. + + Raises + ------ + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error + On constraint violation or bad initializer. + + Examples + -------- + .. code-block:: python + + class Icon(OctetString): + ''' + ASN.1 specification: + + Icon ::= OCTET STRING + + icon1 Icon ::= '001100010011001000110011'B + icon2 Icon ::= '313233'H + ''' + icon1 = Icon.fromBinaryString('001100010011001000110011') + icon2 = Icon.fromHexString('313233') + """ + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04) + ) + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = constraint.ConstraintsIntersection() + + # Optimization for faster codec lookup + typeId = base.SimpleAsn1Type.getTypeId() + + defaultBinValue = defaultHexValue = noValue + encoding = 'iso-8859-1' + + def __init__(self, value=noValue, **kwargs): + if kwargs: + if value is noValue: + try: + value = self.fromBinaryString(kwargs.pop('binValue')) + + except KeyError: + pass + + try: + value = self.fromHexString(kwargs.pop('hexValue')) + + except KeyError: + pass + + if value is noValue: + if self.defaultBinValue is not noValue: + value = self.fromBinaryString(self.defaultBinValue) + + elif self.defaultHexValue is not noValue: + value = self.fromHexString(self.defaultHexValue) + + if 'encoding' not in kwargs: + kwargs['encoding'] = self.encoding + + base.SimpleAsn1Type.__init__(self, value, **kwargs) + + if sys.version_info[0] <= 2: + def prettyIn(self, value): + if isinstance(value, str): + return value + + elif isinstance(value, unicode): + try: + return value.encode(self.encoding) + + except (LookupError, UnicodeEncodeError): + exc = sys.exc_info()[1] + raise error.PyAsn1UnicodeEncodeError( + "Can't encode string '%s' with codec " + "%s" % (value, self.encoding), exc + ) + + elif isinstance(value, (tuple, list)): + try: + return ''.join([chr(x) for x in value]) + + except ValueError: + raise error.PyAsn1Error( + "Bad %s initializer '%s'" % (self.__class__.__name__, value) + ) + + else: + return str(value) + + def __str__(self): + return str(self._value) + + def __unicode__(self): + try: + return self._value.decode(self.encoding) + + except UnicodeDecodeError: + exc = sys.exc_info()[1] + raise error.PyAsn1UnicodeDecodeError( + "Can't decode string '%s' with codec " + "%s" % (self._value, self.encoding), exc + ) + + def asOctets(self): + return str(self._value) + + def asNumbers(self): + return tuple([ord(x) for x in self._value]) + + else: + def prettyIn(self, value): + if isinstance(value, bytes): + return value + + elif isinstance(value, str): + try: + return value.encode(self.encoding) + + except UnicodeEncodeError: + exc = sys.exc_info()[1] + raise error.PyAsn1UnicodeEncodeError( + "Can't encode string '%s' with '%s' " + "codec" % (value, self.encoding), exc + ) + elif isinstance(value, OctetString): # a shortcut, bytes() would work the same way + return value.asOctets() + + elif isinstance(value, base.SimpleAsn1Type): # this mostly targets Integer objects + return self.prettyIn(str(value)) + + elif isinstance(value, (tuple, list)): + return self.prettyIn(bytes(value)) + + else: + return bytes(value) + + def __str__(self): + try: + return self._value.decode(self.encoding) + + except UnicodeDecodeError: + exc = sys.exc_info()[1] + raise error.PyAsn1UnicodeDecodeError( + "Can't decode string '%s' with '%s' codec at " + "'%s'" % (self._value, self.encoding, + self.__class__.__name__), exc + ) + + def __bytes__(self): + return bytes(self._value) + + def asOctets(self): + return bytes(self._value) + + def asNumbers(self): + return tuple(self._value) + + # + # Normally, `.prettyPrint()` is called from `__str__()`. Historically, + # OctetString.prettyPrint() used to return hexified payload + # representation in cases when non-printable content is present. At the + # same time `str()` used to produce either octet-stream (Py2) or + # text (Py3) representations. + # + # Therefore `OctetString.__str__()` -> `.prettyPrint()` call chain is + # reversed to preserve the original behaviour. + # + # Eventually we should deprecate `.prettyPrint()` / `.prettyOut()` harness + # and end up with just `__str__()` producing hexified representation while + # both text and octet-stream representation should only be requested via + # the `.asOctets()` method. + # + # Note: ASN.1 OCTET STRING is never mean to contain text! + # + + def prettyOut(self, value): + return value + + def prettyPrint(self, scope=0): + # first see if subclass has its own .prettyOut() + value = self.prettyOut(self._value) + + if value is not self._value: + return value + + numbers = self.asNumbers() + + for x in numbers: + # hexify if needed + if x < 32 or x > 126: + return '0x' + ''.join(('%.2x' % x for x in numbers)) + else: + # this prevents infinite recursion + return OctetString.__str__(self) + + @staticmethod + def fromBinaryString(value): + """Create a |ASN.1| object initialized from a string of '0' and '1'. + + Parameters + ---------- + value: :class:`str` + Text string like '1010111' + """ + bitNo = 8 + byte = 0 + r = [] + for v in value: + if bitNo: + bitNo -= 1 + else: + bitNo = 7 + r.append(byte) + byte = 0 + if v in ('0', '1'): + v = int(v) + else: + raise error.PyAsn1Error( + 'Non-binary OCTET STRING initializer %s' % (v,) + ) + byte |= v << bitNo + + r.append(byte) + + return octets.ints2octs(r) + + @staticmethod + def fromHexString(value): + """Create a |ASN.1| object initialized from the hex string. + + Parameters + ---------- + value: :class:`str` + Text string like 'DEADBEEF' + """ + r = [] + p = [] + for v in value: + if p: + r.append(int(p + v, 16)) + p = None + else: + p = v + if p: + r.append(int(p + '0', 16)) + + return octets.ints2octs(r) + + # Immutable sequence object protocol + + def __len__(self): + return len(self._value) + + def __getitem__(self, i): + if i.__class__ is slice: + return self.clone(self._value[i]) + else: + return self._value[i] + + def __iter__(self): + return iter(self._value) + + def __contains__(self, value): + return value in self._value + + def __add__(self, value): + return self.clone(self._value + self.prettyIn(value)) + + def __radd__(self, value): + return self.clone(self.prettyIn(value) + self._value) + + def __mul__(self, value): + return self.clone(self._value * value) + + def __rmul__(self, value): + return self * value + + def __int__(self): + return int(self._value) + + def __float__(self): + return float(self._value) + + def __reversed__(self): + return reversed(self._value) + + +class Null(OctetString): + """Create |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python :class:`str` objects + (always empty). + + Keyword Args + ------------ + value: :class:`str` or |ASN.1| object + Python empty :class:`str` literal or any object that evaluates to :obj:`False` + If `value` is not given, schema object will be created. + + tagSet: :py:class:`~pyasn1.type.tag.TagSet` + Object representing non-default ASN.1 tag(s) + + Raises + ------ + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error + On constraint violation or bad initializer. + + Examples + -------- + .. code-block:: python + + class Ack(Null): + ''' + ASN.1 specification: + + Ack ::= NULL + ''' + ack = Ack('') + """ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05) + ) + subtypeSpec = OctetString.subtypeSpec + constraint.SingleValueConstraint(octets.str2octs('')) + + # Optimization for faster codec lookup + typeId = OctetString.getTypeId() + + def prettyIn(self, value): + if value: + return value + + return octets.str2octs('') + +if sys.version_info[0] <= 2: + intTypes = (int, long) +else: + intTypes = (int,) + +numericTypes = intTypes + (float,) + + +class ObjectIdentifier(base.SimpleAsn1Type): + """Create |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python :class:`tuple` objects + (tuple of non-negative integers). + + Keyword Args + ------------ + value: :class:`tuple`, :class:`str` or |ASN.1| object + Python sequence of :class:`int` or :class:`str` literal or |ASN.1| object. + If `value` is not given, schema object will be created. + + 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). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. + + Raises + ------ + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error + On constraint violation or bad initializer. + + Examples + -------- + .. code-block:: python + + class ID(ObjectIdentifier): + ''' + ASN.1 specification: + + ID ::= OBJECT IDENTIFIER + + id-edims ID ::= { joint-iso-itu-t mhs-motif(6) edims(7) } + id-bp ID ::= { id-edims 11 } + ''' + id_edims = ID('2.6.7') + id_bp = id_edims + (11,) + """ + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06) + ) + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = constraint.ConstraintsIntersection() + + # Optimization for faster codec lookup + typeId = base.SimpleAsn1Type.getTypeId() + + def __add__(self, other): + return self.clone(self._value + other) + + def __radd__(self, other): + return self.clone(other + self._value) + + def asTuple(self): + return self._value + + # Sequence object protocol + + def __len__(self): + return len(self._value) + + def __getitem__(self, i): + if i.__class__ is slice: + return self.clone(self._value[i]) + else: + return self._value[i] + + def __iter__(self): + return iter(self._value) + + def __contains__(self, value): + return value in self._value + + def index(self, suboid): + return self._value.index(suboid) + + def isPrefixOf(self, other): + """Indicate if this |ASN.1| object is a prefix of other |ASN.1| object. + + Parameters + ---------- + other: |ASN.1| object + |ASN.1| object + + Returns + ------- + : :class:`bool` + :obj:`True` if this |ASN.1| object is a parent (e.g. prefix) of the other |ASN.1| object + or :obj:`False` otherwise. + """ + l = len(self) + if l <= len(other): + if self._value[:l] == other[:l]: + return True + return False + + def prettyIn(self, value): + if isinstance(value, ObjectIdentifier): + return tuple(value) + elif octets.isStringType(value): + if '-' in value: + raise error.PyAsn1Error( + 'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1]) + ) + try: + return tuple([int(subOid) for subOid in value.split('.') if subOid]) + except ValueError: + raise error.PyAsn1Error( + 'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1]) + ) + + try: + tupleOfInts = tuple([int(subOid) for subOid in value if subOid >= 0]) + + except (ValueError, TypeError): + raise error.PyAsn1Error( + 'Malformed Object ID %s at %s: %s' % (value, self.__class__.__name__, sys.exc_info()[1]) + ) + + if len(tupleOfInts) == len(value): + return tupleOfInts + + raise error.PyAsn1Error('Malformed Object ID %s at %s' % (value, self.__class__.__name__)) + + def prettyOut(self, value): + return '.'.join([str(x) for x in value]) + + +class Real(base.SimpleAsn1Type): + """Create |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python :class:`float` objects. + Additionally, |ASN.1| objects behave like a :class:`tuple` in which case its + elements are mantissa, base and exponent. + + Keyword Args + ------------ + value: :class:`tuple`, :class:`float` or |ASN.1| object + Python sequence of :class:`int` (representing mantissa, base and + exponent) or :class:`float` instance or |ASN.1| object. + If `value` is not given, schema object will be created. + + 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). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. + + Raises + ------ + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error + On constraint violation or bad initializer. + + Examples + -------- + .. code-block:: python + + class Pi(Real): + ''' + ASN.1 specification: + + Pi ::= REAL + + pi Pi ::= { mantissa 314159, base 10, exponent -5 } + + ''' + pi = Pi((314159, 10, -5)) + """ + binEncBase = None # binEncBase = 16 is recommended for large numbers + + try: + _plusInf = float('inf') + _minusInf = float('-inf') + _inf = _plusInf, _minusInf + + except ValueError: + # Infinity support is platform and Python dependent + _plusInf = _minusInf = None + _inf = () + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09) + ) + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = constraint.ConstraintsIntersection() + + # Optimization for faster codec lookup + typeId = base.SimpleAsn1Type.getTypeId() + + @staticmethod + def __normalizeBase10(value): + m, b, e = value + while m and m % 10 == 0: + m /= 10 + e += 1 + return m, b, e + + def prettyIn(self, value): + if isinstance(value, tuple) and len(value) == 3: + if (not isinstance(value[0], numericTypes) or + not isinstance(value[1], intTypes) or + not isinstance(value[2], intTypes)): + raise error.PyAsn1Error('Lame Real value syntax: %s' % (value,)) + if (isinstance(value[0], float) and + self._inf and value[0] in self._inf): + return value[0] + if value[1] not in (2, 10): + raise error.PyAsn1Error( + 'Prohibited base for Real value: %s' % (value[1],) + ) + if value[1] == 10: + value = self.__normalizeBase10(value) + return value + elif isinstance(value, intTypes): + return self.__normalizeBase10((value, 10, 0)) + elif isinstance(value, float) or octets.isStringType(value): + if octets.isStringType(value): + try: + value = float(value) + except ValueError: + raise error.PyAsn1Error( + 'Bad real value syntax: %s' % (value,) + ) + if self._inf and value in self._inf: + return value + else: + e = 0 + while int(value) != value: + value *= 10 + e -= 1 + return self.__normalizeBase10((int(value), 10, e)) + elif isinstance(value, Real): + return tuple(value) + raise error.PyAsn1Error( + 'Bad real value syntax: %s' % (value,) + ) + + def prettyPrint(self, scope=0): + try: + return self.prettyOut(float(self)) + + except OverflowError: + return '<overflow>' + + @property + def isPlusInf(self): + """Indicate PLUS-INFINITY object value + + Returns + ------- + : :class:`bool` + :obj:`True` if calling object represents plus infinity + or :obj:`False` otherwise. + + """ + return self._value == self._plusInf + + @property + def isMinusInf(self): + """Indicate MINUS-INFINITY object value + + Returns + ------- + : :class:`bool` + :obj:`True` if calling object represents minus infinity + or :obj:`False` otherwise. + """ + return self._value == self._minusInf + + @property + def isInf(self): + return self._value in self._inf + + def __add__(self, value): + return self.clone(float(self) + value) + + def __radd__(self, value): + return self + value + + def __mul__(self, value): + return self.clone(float(self) * value) + + def __rmul__(self, value): + return self * value + + def __sub__(self, value): + return self.clone(float(self) - value) + + def __rsub__(self, value): + return self.clone(value - float(self)) + + def __mod__(self, value): + return self.clone(float(self) % value) + + def __rmod__(self, value): + return self.clone(value % float(self)) + + def __pow__(self, value, modulo=None): + return self.clone(pow(float(self), value, modulo)) + + def __rpow__(self, value): + return self.clone(pow(value, float(self))) + + if sys.version_info[0] <= 2: + def __div__(self, value): + return self.clone(float(self) / value) + + def __rdiv__(self, value): + return self.clone(value / float(self)) + else: + def __truediv__(self, value): + return self.clone(float(self) / value) + + def __rtruediv__(self, value): + return self.clone(value / float(self)) + + def __divmod__(self, value): + return self.clone(float(self) // value) + + def __rdivmod__(self, value): + return self.clone(value // float(self)) + + def __int__(self): + return int(float(self)) + + if sys.version_info[0] <= 2: + def __long__(self): + return long(float(self)) + + def __float__(self): + if self._value in self._inf: + return self._value + else: + return float( + self._value[0] * pow(self._value[1], self._value[2]) + ) + + def __abs__(self): + return self.clone(abs(float(self))) + + def __pos__(self): + return self.clone(+float(self)) + + def __neg__(self): + return self.clone(-float(self)) + + def __round__(self, n=0): + r = round(float(self), n) + if n: + return self.clone(r) + else: + return r + + def __floor__(self): + return self.clone(math.floor(float(self))) + + def __ceil__(self): + return self.clone(math.ceil(float(self))) + + def __trunc__(self): + return self.clone(math.trunc(float(self))) + + def __lt__(self, value): + return float(self) < value + + def __le__(self, value): + return float(self) <= value + + def __eq__(self, value): + return float(self) == value + + def __ne__(self, value): + return float(self) != value + + def __gt__(self, value): + return float(self) > value + + def __ge__(self, value): + return float(self) >= value + + if sys.version_info[0] <= 2: + def __nonzero__(self): + return bool(float(self)) + else: + def __bool__(self): + return bool(float(self)) + + __hash__ = base.SimpleAsn1Type.__hash__ + + def __getitem__(self, idx): + if self._value in self._inf: + raise error.PyAsn1Error('Invalid infinite value operation') + else: + return self._value[idx] + + # compatibility stubs + + def isPlusInfinity(self): + return self.isPlusInf + + def isMinusInfinity(self): + return self.isMinusInf + + def isInfinity(self): + return self.isInf + + +class Enumerated(Integer): + """Create |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, its + objects are immutable and duck-type Python :class:`int` objects. + + Keyword Args + ------------ + value: :class:`int`, :class:`str` or |ASN.1| object + Python :class:`int` or :class:`str` literal or |ASN.1| object. + If `value` is not given, schema object will be created. + + 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). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. + + namedValues: :py:class:`~pyasn1.type.namedval.NamedValues` + Object representing non-default symbolic aliases for numbers + + Raises + ------ + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error + On constraint violation or bad initializer. + + Examples + -------- + + .. code-block:: python + + class RadioButton(Enumerated): + ''' + ASN.1 specification: + + RadioButton ::= ENUMERATED { button1(0), button2(1), + button3(2) } + + selected-by-default RadioButton ::= button1 + ''' + namedValues = NamedValues( + ('button1', 0), ('button2', 1), + ('button3', 2) + ) + + selected_by_default = RadioButton('button1') + """ + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x0A) + ) + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = constraint.ConstraintsIntersection() + + # Optimization for faster codec lookup + typeId = Integer.getTypeId() + + #: Default :py:class:`~pyasn1.type.namedval.NamedValues` object + #: representing symbolic aliases for numbers + namedValues = namedval.NamedValues() + + +# "Structured" ASN.1 types + +class SequenceOfAndSetOfBase(base.ConstructedAsn1Type): + """Create |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.ConstructedAsn1Type`, + its objects are mutable and duck-type Python :class:`list` objects. + + Keyword Args + ------------ + componentType : :py:class:`~pyasn1.type.base.PyAsn1Item` derivative + A pyasn1 object representing ASN.1 type allowed within |ASN.1| type + + 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). Constraints + verification for |ASN.1| type can only occur on explicit + `.isInconsistent` call. + + Examples + -------- + + .. code-block:: python + + class LotteryDraw(SequenceOf): # SetOf is similar + ''' + ASN.1 specification: + + LotteryDraw ::= SEQUENCE OF INTEGER + ''' + componentType = Integer() + + lotteryDraw = LotteryDraw() + lotteryDraw.extend([123, 456, 789]) + """ + def __init__(self, *args, **kwargs): + # support positional params for backward compatibility + if args: + for key, value in zip(('componentType', 'tagSet', + 'subtypeSpec'), args): + if key in kwargs: + raise error.PyAsn1Error('Conflicting positional and keyword params!') + kwargs['componentType'] = value + + self._componentValues = noValue + + base.ConstructedAsn1Type.__init__(self, **kwargs) + + # Python list protocol + + def __getitem__(self, idx): + try: + return self.getComponentByPosition(idx) + + except error.PyAsn1Error: + raise IndexError(sys.exc_info()[1]) + + def __setitem__(self, idx, value): + try: + self.setComponentByPosition(idx, value) + + except error.PyAsn1Error: + raise IndexError(sys.exc_info()[1]) + + def append(self, value): + if self._componentValues is noValue: + pos = 0 + + else: + pos = len(self._componentValues) + + self[pos] = value + + def count(self, value): + return list(self._componentValues.values()).count(value) + + def extend(self, values): + for value in values: + self.append(value) + + if self._componentValues is noValue: + self._componentValues = {} + + def index(self, value, start=0, stop=None): + if stop is None: + stop = len(self) + + indices, values = zip(*self._componentValues.items()) + + # TODO: remove when Py2.5 support is gone + values = list(values) + + try: + return indices[values.index(value, start, stop)] + + except error.PyAsn1Error: + raise ValueError(sys.exc_info()[1]) + + def reverse(self): + self._componentValues.reverse() + + def sort(self, key=None, reverse=False): + self._componentValues = dict( + enumerate(sorted(self._componentValues.values(), + key=key, reverse=reverse))) + + def __len__(self): + if self._componentValues is noValue or not self._componentValues: + return 0 + + return max(self._componentValues) + 1 + + def __iter__(self): + for idx in range(0, len(self)): + yield self.getComponentByPosition(idx) + + def _cloneComponentValues(self, myClone, cloneValueFlag): + for idx, componentValue in self._componentValues.items(): + if componentValue is not noValue: + if isinstance(componentValue, base.ConstructedAsn1Type): + myClone.setComponentByPosition( + idx, componentValue.clone(cloneValueFlag=cloneValueFlag) + ) + else: + myClone.setComponentByPosition(idx, componentValue.clone()) + + def getComponentByPosition(self, idx, default=noValue, instantiate=True): + """Return |ASN.1| type component value by position. + + Equivalent to Python sequence subscription operation (e.g. `[]`). + + Parameters + ---------- + idx : :class:`int` + Component index (zero-based). Must either refer to an existing + component or to N+1 component (if *componentType* is set). In the latter + case a new component type gets instantiated and appended to the |ASN.1| + sequence. + + Keyword Args + ------------ + default: :class:`object` + If set and requested component is a schema object, return the `default` + object instead of the requested component. + + instantiate: :class:`bool` + If :obj:`True` (default), inner component will be automatically instantiated. + If :obj:`False` either existing component or the :class:`NoValue` object will be + returned. + + Returns + ------- + : :py:class:`~pyasn1.type.base.PyAsn1Item` + Instantiate |ASN.1| component type or return existing component value + + Examples + -------- + + .. code-block:: python + + # can also be SetOf + class MySequenceOf(SequenceOf): + componentType = OctetString() + + s = MySequenceOf() + + # returns component #0 with `.isValue` property False + s.getComponentByPosition(0) + + # returns None + s.getComponentByPosition(0, default=None) + + s.clear() + + # returns noValue + s.getComponentByPosition(0, instantiate=False) + + # sets component #0 to OctetString() ASN.1 schema + # object and returns it + s.getComponentByPosition(0, instantiate=True) + + # sets component #0 to ASN.1 value object + s.setComponentByPosition(0, 'ABCD') + + # returns OctetString('ABCD') value object + s.getComponentByPosition(0, instantiate=False) + + s.clear() + + # returns noValue + s.getComponentByPosition(0, instantiate=False) + """ + if isinstance(idx, slice): + indices = tuple(range(len(self))) + return [self.getComponentByPosition(subidx, default, instantiate) + for subidx in indices[idx]] + + if idx < 0: + idx = len(self) + idx + if idx < 0: + raise error.PyAsn1Error( + 'SequenceOf/SetOf index is out of range') + + try: + componentValue = self._componentValues[idx] + + except (KeyError, error.PyAsn1Error): + if not instantiate: + return default + + self.setComponentByPosition(idx) + + componentValue = self._componentValues[idx] + + if default is noValue or componentValue.isValue: + return componentValue + else: + return default + + def setComponentByPosition(self, idx, value=noValue, + verifyConstraints=True, + matchTags=True, + matchConstraints=True): + """Assign |ASN.1| type component by position. + + Equivalent to Python sequence item assignment operation (e.g. `[]`) + or list.append() (when idx == len(self)). + + Parameters + ---------- + idx: :class:`int` + Component index (zero-based). Must either refer to existing + component or to N+1 component. In the latter case a new component + type gets instantiated (if *componentType* is set, or given ASN.1 + object is taken otherwise) and appended to the |ASN.1| sequence. + + Keyword Args + ------------ + value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative + A Python value to initialize |ASN.1| component with (if *componentType* is set) + or ASN.1 value object to assign to |ASN.1| component. + If `value` is not given, schema object will be set as a component. + + verifyConstraints: :class:`bool` + If :obj:`False`, skip constraints validation + + matchTags: :class:`bool` + If :obj:`False`, skip component tags matching + + matchConstraints: :class:`bool` + If :obj:`False`, skip component constraints matching + + Returns + ------- + self + + Raises + ------ + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error + On constraint violation or bad initializer + IndexError + When idx > len(self) + """ + if isinstance(idx, slice): + indices = tuple(range(len(self))) + startIdx = indices and indices[idx][0] or 0 + for subIdx, subValue in enumerate(value): + self.setComponentByPosition( + startIdx + subIdx, subValue, verifyConstraints, + matchTags, matchConstraints) + return self + + if idx < 0: + idx = len(self) + idx + if idx < 0: + raise error.PyAsn1Error( + 'SequenceOf/SetOf index is out of range') + + componentType = self.componentType + + if self._componentValues is noValue: + componentValues = {} + + else: + componentValues = self._componentValues + + currentValue = componentValues.get(idx, noValue) + + if value is noValue: + if componentType is not None: + value = componentType.clone() + + elif currentValue is noValue: + raise error.PyAsn1Error('Component type not defined') + + elif not isinstance(value, base.Asn1Item): + if (componentType is not None and + isinstance(componentType, base.SimpleAsn1Type)): + value = componentType.clone(value=value) + + elif (currentValue is not noValue and + isinstance(currentValue, base.SimpleAsn1Type)): + value = currentValue.clone(value=value) + + else: + raise error.PyAsn1Error( + 'Non-ASN.1 value %r and undefined component' + ' type at %r' % (value, self)) + + elif componentType is not None and (matchTags or matchConstraints): + subtypeChecker = ( + self.strictConstraints and + componentType.isSameTypeWith or + componentType.isSuperTypeOf) + + if not subtypeChecker(value, verifyConstraints and matchTags, + verifyConstraints and matchConstraints): + # TODO: we should wrap componentType with UnnamedType to carry + # additional properties associated with componentType + if componentType.typeId != Any.typeId: + raise error.PyAsn1Error( + 'Component value is tag-incompatible: %r vs ' + '%r' % (value, componentType)) + + componentValues[idx] = value + + self._componentValues = componentValues + + return self + + @property + def componentTagMap(self): + if self.componentType is not None: + return self.componentType.tagMap + + @property + def components(self): + return [self._componentValues[idx] + for idx in sorted(self._componentValues)] + + def clear(self): + """Remove all components and become an empty |ASN.1| value object. + + Has the same effect on |ASN.1| object as it does on :class:`list` + built-in. + """ + self._componentValues = {} + return self + + def reset(self): + """Remove all components and become a |ASN.1| schema object. + + See :meth:`isValue` property for more information on the + distinction between value and schema objects. + """ + self._componentValues = noValue + return self + + def prettyPrint(self, scope=0): + scope += 1 + representation = self.__class__.__name__ + ':\n' + + if not self.isValue: + return representation + + for idx, componentValue in enumerate(self): + representation += ' ' * scope + if (componentValue is noValue and + self.componentType is not None): + representation += '<empty>' + else: + representation += componentValue.prettyPrint(scope) + + return representation + + def prettyPrintType(self, scope=0): + scope += 1 + representation = '%s -> %s {\n' % (self.tagSet, self.__class__.__name__) + if self.componentType is not None: + representation += ' ' * scope + representation += self.componentType.prettyPrintType(scope) + return representation + '\n' + ' ' * (scope - 1) + '}' + + + @property + def isValue(self): + """Indicate that |ASN.1| object represents ASN.1 value. + + If *isValue* is :obj:`False` then this object represents just ASN.1 schema. + + If *isValue* is :obj:`True` then, in addition to its ASN.1 schema features, + this object can also be used like a Python built-in object + (e.g. :class:`int`, :class:`str`, :class:`dict` etc.). + + Returns + ------- + : :class:`bool` + :obj:`False` if object represents just ASN.1 schema. + :obj:`True` if object represents ASN.1 schema and can be used as a normal value. + + Note + ---- + There is an important distinction between PyASN1 schema and value objects. + The PyASN1 schema objects can only participate in ASN.1 schema-related + operations (e.g. defining or testing the structure of the data). Most + obvious uses of ASN.1 schema is to guide serialisation codecs whilst + encoding/decoding serialised ASN.1 contents. + + The PyASN1 value objects can **additionally** participate in many operations + involving regular Python objects (e.g. arithmetic, comprehension etc). + """ + if self._componentValues is noValue: + return False + + if len(self._componentValues) != len(self): + return False + + for componentValue in self._componentValues.values(): + if componentValue is noValue or not componentValue.isValue: + return False + + return True + + @property + def isInconsistent(self): + """Run necessary checks to ensure |ASN.1| object consistency. + + Default action is to verify |ASN.1| object against constraints imposed + by `subtypeSpec`. + + Raises + ------ + :py:class:`~pyasn1.error.PyAsn1tError` on any inconsistencies found + """ + if self.componentType is noValue or not self.subtypeSpec: + return False + + if self._componentValues is noValue: + return True + + mapping = {} + + for idx, value in self._componentValues.items(): + # Absent fields are not in the mapping + if value is noValue: + continue + + mapping[idx] = value + + try: + # Represent SequenceOf/SetOf as a bare dict to constraints chain + self.subtypeSpec(mapping) + + except error.PyAsn1Error: + exc = sys.exc_info()[1] + return exc + + return False + +class SequenceOf(SequenceOfAndSetOfBase): + __doc__ = SequenceOfAndSetOfBase.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) + ) + + #: Default :py:class:`~pyasn1.type.base.PyAsn1Item` derivative + #: object representing ASN.1 type allowed within |ASN.1| type + componentType = None + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = constraint.ConstraintsIntersection() + + # Disambiguation ASN.1 types identification + typeId = SequenceOfAndSetOfBase.getTypeId() + + +class SetOf(SequenceOfAndSetOfBase): + __doc__ = SequenceOfAndSetOfBase.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) + ) + + #: Default :py:class:`~pyasn1.type.base.PyAsn1Item` derivative + #: object representing ASN.1 type allowed within |ASN.1| type + componentType = None + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = constraint.ConstraintsIntersection() + + # Disambiguation ASN.1 types identification + typeId = SequenceOfAndSetOfBase.getTypeId() + + +class SequenceAndSetBase(base.ConstructedAsn1Type): + """Create |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.ConstructedAsn1Type`, + its objects are mutable and duck-type Python :class:`dict` objects. + + Keyword Args + ------------ + componentType: :py:class:`~pyasn1.type.namedtype.NamedType` + Object holding named ASN.1 types allowed within this collection + + 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). Constraints + verification for |ASN.1| type can only occur on explicit + `.isInconsistent` call. + + Examples + -------- + + .. code-block:: python + + class Description(Sequence): # Set is similar + ''' + ASN.1 specification: + + Description ::= SEQUENCE { + surname IA5String, + first-name IA5String OPTIONAL, + age INTEGER DEFAULT 40 + } + ''' + componentType = NamedTypes( + NamedType('surname', IA5String()), + OptionalNamedType('first-name', IA5String()), + DefaultedNamedType('age', Integer(40)) + ) + + descr = Description() + descr['surname'] = 'Smith' + descr['first-name'] = 'John' + """ + #: Default :py:class:`~pyasn1.type.namedtype.NamedTypes` + #: object representing named ASN.1 types allowed within |ASN.1| type + componentType = namedtype.NamedTypes() + + + class DynamicNames(object): + """Fields names/positions mapping for component-less objects""" + def __init__(self): + self._keyToIdxMap = {} + self._idxToKeyMap = {} + + def __len__(self): + return len(self._keyToIdxMap) + + def __contains__(self, item): + return item in self._keyToIdxMap or item in self._idxToKeyMap + + def __iter__(self): + return (self._idxToKeyMap[idx] for idx in range(len(self._idxToKeyMap))) + + def __getitem__(self, item): + try: + return self._keyToIdxMap[item] + + except KeyError: + return self._idxToKeyMap[item] + + def getNameByPosition(self, idx): + try: + return self._idxToKeyMap[idx] + + except KeyError: + raise error.PyAsn1Error('Type position out of range') + + def getPositionByName(self, name): + try: + return self._keyToIdxMap[name] + + except KeyError: + raise error.PyAsn1Error('Name %s not found' % (name,)) + + def addField(self, idx): + self._keyToIdxMap['field-%d' % idx] = idx + self._idxToKeyMap[idx] = 'field-%d' % idx + + + def __init__(self, **kwargs): + base.ConstructedAsn1Type.__init__(self, **kwargs) + self._componentTypeLen = len(self.componentType) + if self._componentTypeLen: + self._componentValues = [] + else: + self._componentValues = noValue + self._dynamicNames = self._componentTypeLen or self.DynamicNames() + + def __getitem__(self, idx): + if octets.isStringType(idx): + try: + return self.getComponentByName(idx) + + except error.PyAsn1Error: + # duck-typing dict + raise KeyError(sys.exc_info()[1]) + + else: + try: + return self.getComponentByPosition(idx) + + except error.PyAsn1Error: + # duck-typing list + raise IndexError(sys.exc_info()[1]) + + def __setitem__(self, idx, value): + if octets.isStringType(idx): + try: + self.setComponentByName(idx, value) + + except error.PyAsn1Error: + # duck-typing dict + raise KeyError(sys.exc_info()[1]) + + else: + try: + self.setComponentByPosition(idx, value) + + except error.PyAsn1Error: + # duck-typing list + raise IndexError(sys.exc_info()[1]) + + def __contains__(self, key): + if self._componentTypeLen: + return key in self.componentType + else: + return key in self._dynamicNames + + def __len__(self): + return len(self._componentValues) + + def __iter__(self): + return iter(self.componentType or self._dynamicNames) + + # Python dict protocol + + def values(self): + for idx in range(self._componentTypeLen or len(self._dynamicNames)): + yield self[idx] + + def keys(self): + return iter(self) + + def items(self): + for idx in range(self._componentTypeLen or len(self._dynamicNames)): + if self._componentTypeLen: + yield self.componentType[idx].name, self[idx] + else: + yield self._dynamicNames[idx], self[idx] + + def update(self, *iterValue, **mappingValue): + for k, v in iterValue: + self[k] = v + for k in mappingValue: + self[k] = mappingValue[k] + + def clear(self): + """Remove all components and become an empty |ASN.1| value object. + + Has the same effect on |ASN.1| object as it does on :class:`dict` + built-in. + """ + self._componentValues = [] + self._dynamicNames = self.DynamicNames() + return self + + def reset(self): + """Remove all components and become a |ASN.1| schema object. + + See :meth:`isValue` property for more information on the + distinction between value and schema objects. + """ + self._componentValues = noValue + self._dynamicNames = self.DynamicNames() + return self + + @property + def components(self): + return self._componentValues + + def _cloneComponentValues(self, myClone, cloneValueFlag): + if self._componentValues is noValue: + return + + for idx, componentValue in enumerate(self._componentValues): + if componentValue is not noValue: + if isinstance(componentValue, base.ConstructedAsn1Type): + myClone.setComponentByPosition( + idx, componentValue.clone(cloneValueFlag=cloneValueFlag) + ) + else: + myClone.setComponentByPosition(idx, componentValue.clone()) + + def getComponentByName(self, name, default=noValue, instantiate=True): + """Returns |ASN.1| type component by name. + + Equivalent to Python :class:`dict` subscription operation (e.g. `[]`). + + Parameters + ---------- + name: :class:`str` + |ASN.1| type component name + + Keyword Args + ------------ + default: :class:`object` + If set and requested component is a schema object, return the `default` + object instead of the requested component. + + instantiate: :class:`bool` + If :obj:`True` (default), inner component will be automatically + instantiated. + If :obj:`False` either existing component or the :class:`NoValue` + object will be returned. + + Returns + ------- + : :py:class:`~pyasn1.type.base.PyAsn1Item` + Instantiate |ASN.1| component type or return existing + component value + """ + if self._componentTypeLen: + idx = self.componentType.getPositionByName(name) + else: + try: + idx = self._dynamicNames.getPositionByName(name) + + except KeyError: + raise error.PyAsn1Error('Name %s not found' % (name,)) + + return self.getComponentByPosition(idx, default=default, instantiate=instantiate) + + def setComponentByName(self, name, value=noValue, + verifyConstraints=True, + matchTags=True, + matchConstraints=True): + """Assign |ASN.1| type component by name. + + Equivalent to Python :class:`dict` item assignment operation (e.g. `[]`). + + Parameters + ---------- + name: :class:`str` + |ASN.1| type component name + + Keyword Args + ------------ + value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative + A Python value to initialize |ASN.1| component with (if *componentType* is set) + or ASN.1 value object to assign to |ASN.1| component. + If `value` is not given, schema object will be set as a component. + + verifyConstraints: :class:`bool` + If :obj:`False`, skip constraints validation + + matchTags: :class:`bool` + If :obj:`False`, skip component tags matching + + matchConstraints: :class:`bool` + If :obj:`False`, skip component constraints matching + + Returns + ------- + self + """ + if self._componentTypeLen: + idx = self.componentType.getPositionByName(name) + else: + try: + idx = self._dynamicNames.getPositionByName(name) + + except KeyError: + raise error.PyAsn1Error('Name %s not found' % (name,)) + + return self.setComponentByPosition( + idx, value, verifyConstraints, matchTags, matchConstraints + ) + + def getComponentByPosition(self, idx, default=noValue, instantiate=True): + """Returns |ASN.1| type component by index. + + Equivalent to Python sequence subscription operation (e.g. `[]`). + + Parameters + ---------- + idx: :class:`int` + Component index (zero-based). Must either refer to an existing + component or (if *componentType* is set) new ASN.1 schema object gets + instantiated. + + Keyword Args + ------------ + default: :class:`object` + If set and requested component is a schema object, return the `default` + object instead of the requested component. + + instantiate: :class:`bool` + If :obj:`True` (default), inner component will be automatically + instantiated. + If :obj:`False` either existing component or the :class:`NoValue` + object will be returned. + + Returns + ------- + : :py:class:`~pyasn1.type.base.PyAsn1Item` + a PyASN1 object + + Examples + -------- + + .. code-block:: python + + # can also be Set + class MySequence(Sequence): + componentType = NamedTypes( + NamedType('id', OctetString()) + ) + + s = MySequence() + + # returns component #0 with `.isValue` property False + s.getComponentByPosition(0) + + # returns None + s.getComponentByPosition(0, default=None) + + s.clear() + + # returns noValue + s.getComponentByPosition(0, instantiate=False) + + # sets component #0 to OctetString() ASN.1 schema + # object and returns it + s.getComponentByPosition(0, instantiate=True) + + # sets component #0 to ASN.1 value object + s.setComponentByPosition(0, 'ABCD') + + # returns OctetString('ABCD') value object + s.getComponentByPosition(0, instantiate=False) + + s.clear() + + # returns noValue + s.getComponentByPosition(0, instantiate=False) + """ + try: + if self._componentValues is noValue: + componentValue = noValue + + else: + componentValue = self._componentValues[idx] + + except IndexError: + componentValue = noValue + + if not instantiate: + if componentValue is noValue or not componentValue.isValue: + return default + else: + return componentValue + + if componentValue is noValue: + self.setComponentByPosition(idx) + + componentValue = self._componentValues[idx] + + if default is noValue or componentValue.isValue: + return componentValue + else: + return default + + def setComponentByPosition(self, idx, value=noValue, + verifyConstraints=True, + matchTags=True, + matchConstraints=True): + """Assign |ASN.1| type component by position. + + Equivalent to Python sequence item assignment operation (e.g. `[]`). + + Parameters + ---------- + idx : :class:`int` + Component index (zero-based). Must either refer to existing + component (if *componentType* is set) or to N+1 component + otherwise. In the latter case a new component of given ASN.1 + type gets instantiated and appended to |ASN.1| sequence. + + Keyword Args + ------------ + value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative + A Python value to initialize |ASN.1| component with (if *componentType* is set) + or ASN.1 value object to assign to |ASN.1| component. + If `value` is not given, schema object will be set as a component. + + verifyConstraints : :class:`bool` + If :obj:`False`, skip constraints validation + + matchTags: :class:`bool` + If :obj:`False`, skip component tags matching + + matchConstraints: :class:`bool` + If :obj:`False`, skip component constraints matching + + Returns + ------- + self + """ + componentType = self.componentType + componentTypeLen = self._componentTypeLen + + if self._componentValues is noValue: + componentValues = [] + + else: + componentValues = self._componentValues + + try: + currentValue = componentValues[idx] + + except IndexError: + currentValue = noValue + if componentTypeLen: + if componentTypeLen < idx: + raise error.PyAsn1Error('component index out of range') + + componentValues = [noValue] * componentTypeLen + + if value is noValue: + if componentTypeLen: + value = componentType.getTypeByPosition(idx) + if isinstance(value, base.ConstructedAsn1Type): + value = value.clone(cloneValueFlag=componentType[idx].isDefaulted) + + elif currentValue is noValue: + raise error.PyAsn1Error('Component type not defined') + + elif not isinstance(value, base.Asn1Item): + if componentTypeLen: + subComponentType = componentType.getTypeByPosition(idx) + if isinstance(subComponentType, base.SimpleAsn1Type): + value = subComponentType.clone(value=value) + + else: + raise error.PyAsn1Error('%s can cast only scalar values' % componentType.__class__.__name__) + + elif currentValue is not noValue and isinstance(currentValue, base.SimpleAsn1Type): + value = currentValue.clone(value=value) + + else: + raise error.PyAsn1Error('%s undefined component type' % componentType.__class__.__name__) + + elif ((verifyConstraints or matchTags or matchConstraints) and + componentTypeLen): + subComponentType = componentType.getTypeByPosition(idx) + if subComponentType is not noValue: + subtypeChecker = (self.strictConstraints and + subComponentType.isSameTypeWith or + subComponentType.isSuperTypeOf) + + if not subtypeChecker(value, verifyConstraints and matchTags, + verifyConstraints and matchConstraints): + if not componentType[idx].openType: + raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType)) + + if componentTypeLen or idx in self._dynamicNames: + componentValues[idx] = value + + elif len(componentValues) == idx: + componentValues.append(value) + self._dynamicNames.addField(idx) + + else: + raise error.PyAsn1Error('Component index out of range') + + self._componentValues = componentValues + + return self + + @property + def isValue(self): + """Indicate that |ASN.1| object represents ASN.1 value. + + If *isValue* is :obj:`False` then this object represents just ASN.1 schema. + + If *isValue* is :obj:`True` then, in addition to its ASN.1 schema features, + this object can also be used like a Python built-in object (e.g. + :class:`int`, :class:`str`, :class:`dict` etc.). + + Returns + ------- + : :class:`bool` + :obj:`False` if object represents just ASN.1 schema. + :obj:`True` if object represents ASN.1 schema and can be used as a + normal value. + + Note + ---- + There is an important distinction between PyASN1 schema and value objects. + The PyASN1 schema objects can only participate in ASN.1 schema-related + operations (e.g. defining or testing the structure of the data). Most + obvious uses of ASN.1 schema is to guide serialisation codecs whilst + encoding/decoding serialised ASN.1 contents. + + The PyASN1 value objects can **additionally** participate in many operations + involving regular Python objects (e.g. arithmetic, comprehension etc). + + It is sufficient for |ASN.1| objects to have all non-optional and non-defaulted + components being value objects to be considered as a value objects as a whole. + In other words, even having one or more optional components not turned into + value objects, |ASN.1| object is still considered as a value object. Defaulted + components are normally value objects by default. + """ + if self._componentValues is noValue: + return False + + componentType = self.componentType + + if componentType: + for idx, subComponentType in enumerate(componentType.namedTypes): + if subComponentType.isDefaulted or subComponentType.isOptional: + continue + + if not self._componentValues: + return False + + componentValue = self._componentValues[idx] + if componentValue is noValue or not componentValue.isValue: + return False + + else: + for componentValue in self._componentValues: + if componentValue is noValue or not componentValue.isValue: + return False + + return True + + @property + def isInconsistent(self): + """Run necessary checks to ensure |ASN.1| object consistency. + + Default action is to verify |ASN.1| object against constraints imposed + by `subtypeSpec`. + + Raises + ------ + :py:class:`~pyasn1.error.PyAsn1tError` on any inconsistencies found + """ + if self.componentType is noValue or not self.subtypeSpec: + return False + + if self._componentValues is noValue: + return True + + mapping = {} + + for idx, value in enumerate(self._componentValues): + # Absent fields are not in the mapping + if value is noValue: + continue + + name = self.componentType.getNameByPosition(idx) + + mapping[name] = value + + try: + # Represent Sequence/Set as a bare dict to constraints chain + self.subtypeSpec(mapping) + + except error.PyAsn1Error: + exc = sys.exc_info()[1] + return exc + + return False + + def prettyPrint(self, scope=0): + """Return an object representation string. + + Returns + ------- + : :class:`str` + Human-friendly object representation. + """ + scope += 1 + representation = self.__class__.__name__ + ':\n' + for idx, componentValue in enumerate(self._componentValues): + if componentValue is not noValue and componentValue.isValue: + representation += ' ' * scope + if self.componentType: + representation += self.componentType.getNameByPosition(idx) + else: + representation += self._dynamicNames.getNameByPosition(idx) + representation = '%s=%s\n' % ( + representation, componentValue.prettyPrint(scope) + ) + return representation + + def prettyPrintType(self, scope=0): + scope += 1 + representation = '%s -> %s {\n' % (self.tagSet, self.__class__.__name__) + for idx, componentType in enumerate(self.componentType.values() or self._componentValues): + representation += ' ' * scope + if self.componentType: + representation += '"%s"' % self.componentType.getNameByPosition(idx) + else: + representation += '"%s"' % self._dynamicNames.getNameByPosition(idx) + representation = '%s = %s\n' % ( + representation, componentType.prettyPrintType(scope) + ) + return representation + '\n' + ' ' * (scope - 1) + '}' + + # backward compatibility + + def setDefaultComponents(self): + return self + + def getComponentType(self): + if self._componentTypeLen: + return self.componentType + + def getNameByPosition(self, idx): + if self._componentTypeLen: + return self.componentType[idx].name + +class Sequence(SequenceAndSetBase): + __doc__ = SequenceAndSetBase.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) + ) + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = constraint.ConstraintsIntersection() + + #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`) + #: object imposing size constraint on |ASN.1| objects + componentType = namedtype.NamedTypes() + + # Disambiguation ASN.1 types identification + typeId = SequenceAndSetBase.getTypeId() + + # backward compatibility + + def getComponentTagMapNearPosition(self, idx): + if self.componentType: + return self.componentType.getTagMapNearPosition(idx) + + def getComponentPositionNearType(self, tagSet, idx): + if self.componentType: + return self.componentType.getPositionNearType(tagSet, idx) + else: + return idx + + +class Set(SequenceAndSetBase): + __doc__ = SequenceAndSetBase.__doc__ + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) + ) + + #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`) + #: object representing ASN.1 type allowed within |ASN.1| type + componentType = namedtype.NamedTypes() + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = constraint.ConstraintsIntersection() + + # Disambiguation ASN.1 types identification + typeId = SequenceAndSetBase.getTypeId() + + def getComponent(self, innerFlag=False): + return self + + def getComponentByType(self, tagSet, default=noValue, + instantiate=True, innerFlag=False): + """Returns |ASN.1| type component by ASN.1 tag. + + Parameters + ---------- + tagSet : :py:class:`~pyasn1.type.tag.TagSet` + Object representing ASN.1 tags to identify one of + |ASN.1| object component + + Keyword Args + ------------ + default: :class:`object` + If set and requested component is a schema object, return the `default` + object instead of the requested component. + + instantiate: :class:`bool` + If :obj:`True` (default), inner component will be automatically + instantiated. + If :obj:`False` either existing component or the :class:`noValue` + object will be returned. + + Returns + ------- + : :py:class:`~pyasn1.type.base.PyAsn1Item` + a pyasn1 object + """ + componentValue = self.getComponentByPosition( + self.componentType.getPositionByType(tagSet), + default=default, instantiate=instantiate + ) + if innerFlag and isinstance(componentValue, Set): + # get inner component by inner tagSet + return componentValue.getComponent(innerFlag=True) + else: + # get outer component by inner tagSet + return componentValue + + def setComponentByType(self, tagSet, value=noValue, + verifyConstraints=True, + matchTags=True, + matchConstraints=True, + innerFlag=False): + """Assign |ASN.1| type component by ASN.1 tag. + + Parameters + ---------- + tagSet : :py:class:`~pyasn1.type.tag.TagSet` + Object representing ASN.1 tags to identify one of + |ASN.1| object component + + Keyword Args + ------------ + value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative + A Python value to initialize |ASN.1| component with (if *componentType* is set) + or ASN.1 value object to assign to |ASN.1| component. + If `value` is not given, schema object will be set as a component. + + verifyConstraints : :class:`bool` + If :obj:`False`, skip constraints validation + + matchTags: :class:`bool` + If :obj:`False`, skip component tags matching + + matchConstraints: :class:`bool` + If :obj:`False`, skip component constraints matching + + innerFlag: :class:`bool` + If :obj:`True`, search for matching *tagSet* recursively. + + Returns + ------- + self + """ + idx = self.componentType.getPositionByType(tagSet) + + if innerFlag: # set inner component by inner tagSet + componentType = self.componentType.getTypeByPosition(idx) + + if componentType.tagSet: + return self.setComponentByPosition( + idx, value, verifyConstraints, matchTags, matchConstraints + ) + else: + componentType = self.getComponentByPosition(idx) + return componentType.setComponentByType( + tagSet, value, verifyConstraints, matchTags, matchConstraints, innerFlag=innerFlag + ) + else: # set outer component by inner tagSet + return self.setComponentByPosition( + idx, value, verifyConstraints, matchTags, matchConstraints + ) + + @property + def componentTagMap(self): + if self.componentType: + return self.componentType.tagMapUnique + + +class Choice(Set): + """Create |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.ConstructedAsn1Type`, + its objects are mutable and duck-type Python :class:`list` objects. + + Keyword Args + ------------ + componentType: :py:class:`~pyasn1.type.namedtype.NamedType` + Object holding named ASN.1 types allowed within this collection + + 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). Constraints + verification for |ASN.1| type can only occur on explicit + `.isInconsistent` call. + + Examples + -------- + + .. code-block:: python + + class Afters(Choice): + ''' + ASN.1 specification: + + Afters ::= CHOICE { + cheese [0] IA5String, + dessert [1] IA5String + } + ''' + componentType = NamedTypes( + NamedType('cheese', IA5String().subtype( + implicitTag=Tag(tagClassContext, tagFormatSimple, 0) + ), + NamedType('dessert', IA5String().subtype( + implicitTag=Tag(tagClassContext, tagFormatSimple, 1) + ) + ) + + afters = Afters() + afters['cheese'] = 'Mascarpone' + """ + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.TagSet() # untagged + + #: Default collection of ASN.1 types of component (e.g. :py:class:`~pyasn1.type.namedtype.NamedType`) + #: object representing ASN.1 type allowed within |ASN.1| type + componentType = namedtype.NamedTypes() + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = constraint.ConstraintsIntersection( + constraint.ValueSizeConstraint(1, 1) + ) + + # Disambiguation ASN.1 types identification + typeId = Set.getTypeId() + + _currentIdx = None + + def __eq__(self, other): + if self._componentValues: + return self._componentValues[self._currentIdx] == other + return NotImplemented + + def __ne__(self, other): + if self._componentValues: + return self._componentValues[self._currentIdx] != other + return NotImplemented + + def __lt__(self, other): + if self._componentValues: + return self._componentValues[self._currentIdx] < other + return NotImplemented + + def __le__(self, other): + if self._componentValues: + return self._componentValues[self._currentIdx] <= other + return NotImplemented + + def __gt__(self, other): + if self._componentValues: + return self._componentValues[self._currentIdx] > other + return NotImplemented + + def __ge__(self, other): + if self._componentValues: + return self._componentValues[self._currentIdx] >= other + return NotImplemented + + if sys.version_info[0] <= 2: + def __nonzero__(self): + return self._componentValues and True or False + else: + def __bool__(self): + return self._componentValues and True or False + + def __len__(self): + return self._currentIdx is not None and 1 or 0 + + def __contains__(self, key): + if self._currentIdx is None: + return False + return key == self.componentType[self._currentIdx].getName() + + def __iter__(self): + if self._currentIdx is None: + raise StopIteration + yield self.componentType[self._currentIdx].getName() + + # Python dict protocol + + def values(self): + if self._currentIdx is not None: + yield self._componentValues[self._currentIdx] + + def keys(self): + if self._currentIdx is not None: + yield self.componentType[self._currentIdx].getName() + + def items(self): + if self._currentIdx is not None: + yield self.componentType[self._currentIdx].getName(), self[self._currentIdx] + + def checkConsistency(self): + if self._currentIdx is None: + raise error.PyAsn1Error('Component not chosen') + + def _cloneComponentValues(self, myClone, cloneValueFlag): + try: + component = self.getComponent() + except error.PyAsn1Error: + pass + else: + if isinstance(component, Choice): + tagSet = component.effectiveTagSet + else: + tagSet = component.tagSet + if isinstance(component, base.ConstructedAsn1Type): + myClone.setComponentByType( + tagSet, component.clone(cloneValueFlag=cloneValueFlag) + ) + else: + myClone.setComponentByType(tagSet, component.clone()) + + def getComponentByPosition(self, idx, default=noValue, instantiate=True): + __doc__ = Set.__doc__ + + if self._currentIdx is None or self._currentIdx != idx: + return Set.getComponentByPosition(self, idx, default=default, + instantiate=instantiate) + + return self._componentValues[idx] + + def setComponentByPosition(self, idx, value=noValue, + verifyConstraints=True, + matchTags=True, + matchConstraints=True): + """Assign |ASN.1| type component by position. + + Equivalent to Python sequence item assignment operation (e.g. `[]`). + + Parameters + ---------- + idx: :class:`int` + Component index (zero-based). Must either refer to existing + component or to N+1 component. In the latter case a new component + type gets instantiated (if *componentType* is set, or given ASN.1 + object is taken otherwise) and appended to the |ASN.1| sequence. + + Keyword Args + ------------ + value: :class:`object` or :py:class:`~pyasn1.type.base.PyAsn1Item` derivative + A Python value to initialize |ASN.1| component with (if *componentType* is set) + or ASN.1 value object to assign to |ASN.1| component. Once a new value is + set to *idx* component, previous value is dropped. + If `value` is not given, schema object will be set as a component. + + verifyConstraints : :class:`bool` + If :obj:`False`, skip constraints validation + + matchTags: :class:`bool` + If :obj:`False`, skip component tags matching + + matchConstraints: :class:`bool` + If :obj:`False`, skip component constraints matching + + Returns + ------- + self + """ + oldIdx = self._currentIdx + Set.setComponentByPosition(self, idx, value, verifyConstraints, matchTags, matchConstraints) + self._currentIdx = idx + if oldIdx is not None and oldIdx != idx: + self._componentValues[oldIdx] = noValue + return self + + @property + def effectiveTagSet(self): + """Return a :class:`~pyasn1.type.tag.TagSet` object of the currently initialized component or self (if |ASN.1| is tagged).""" + if self.tagSet: + return self.tagSet + else: + component = self.getComponent() + return component.effectiveTagSet + + @property + def tagMap(self): + """"Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping + ASN.1 tags to ASN.1 objects contained within callee. + """ + if self.tagSet: + return Set.tagMap.fget(self) + else: + return self.componentType.tagMapUnique + + def getComponent(self, innerFlag=False): + """Return currently assigned component of the |ASN.1| object. + + Returns + ------- + : :py:class:`~pyasn1.type.base.PyAsn1Item` + a PyASN1 object + """ + if self._currentIdx is None: + raise error.PyAsn1Error('Component not chosen') + else: + c = self._componentValues[self._currentIdx] + if innerFlag and isinstance(c, Choice): + return c.getComponent(innerFlag) + else: + return c + + def getName(self, innerFlag=False): + """Return the name of currently assigned component of the |ASN.1| object. + + Returns + ------- + : :py:class:`str` + |ASN.1| component name + """ + if self._currentIdx is None: + raise error.PyAsn1Error('Component not chosen') + else: + if innerFlag: + c = self._componentValues[self._currentIdx] + if isinstance(c, Choice): + return c.getName(innerFlag) + return self.componentType.getNameByPosition(self._currentIdx) + + @property + def isValue(self): + """Indicate that |ASN.1| object represents ASN.1 value. + + If *isValue* is :obj:`False` then this object represents just ASN.1 schema. + + If *isValue* is :obj:`True` then, in addition to its ASN.1 schema features, + this object can also be used like a Python built-in object (e.g. + :class:`int`, :class:`str`, :class:`dict` etc.). + + Returns + ------- + : :class:`bool` + :obj:`False` if object represents just ASN.1 schema. + :obj:`True` if object represents ASN.1 schema and can be used as a normal + value. + + Note + ---- + There is an important distinction between PyASN1 schema and value objects. + The PyASN1 schema objects can only participate in ASN.1 schema-related + operations (e.g. defining or testing the structure of the data). Most + obvious uses of ASN.1 schema is to guide serialisation codecs whilst + encoding/decoding serialised ASN.1 contents. + + The PyASN1 value objects can **additionally** participate in many operations + involving regular Python objects (e.g. arithmetic, comprehension etc). + """ + if self._currentIdx is None: + return False + + componentValue = self._componentValues[self._currentIdx] + + return componentValue is not noValue and componentValue.isValue + + def clear(self): + self._currentIdx = None + return Set.clear(self) + + # compatibility stubs + + def getMinTagSet(self): + return self.minTagSet + + +class Any(OctetString): + """Create |ASN.1| schema or value object. + + |ASN.1| class is based on :class:`~pyasn1.type.base.SimpleAsn1Type`, + its objects are immutable and duck-type Python 2 :class:`str` or Python 3 + :class:`bytes`. When used in Unicode context, |ASN.1| type assumes + "|encoding|" serialisation. + + Keyword Args + ------------ + value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object + :class:`str` (Python 2) or :class:`bytes` (Python 3), alternatively + :class:`unicode` object (Python 2) or :class:`str` (Python 3) + representing character string to be serialised into octets (note + `encoding` parameter) or |ASN.1| object. + If `value` is not given, schema object will be created. + + 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). Constraints + verification for |ASN.1| type occurs automatically on object + instantiation. + + 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 text string context. + + binValue: :py:class:`str` + Binary string initializer to use instead of the *value*. + Example: '10110011'. + + hexValue: :py:class:`str` + Hexadecimal string initializer to use instead of the *value*. + Example: 'DEADBEEF'. + + Raises + ------ + ~pyasn1.error.ValueConstraintError, ~pyasn1.error.PyAsn1Error + On constraint violation or bad initializer. + + Examples + -------- + .. code-block:: python + + class Error(Sequence): + ''' + ASN.1 specification: + + Error ::= SEQUENCE { + code INTEGER, + parameter ANY DEFINED BY code -- Either INTEGER or REAL + } + ''' + componentType=NamedTypes( + NamedType('code', Integer()), + NamedType('parameter', Any(), + openType=OpenType('code', {1: Integer(), + 2: Real()})) + ) + + error = Error() + error['code'] = 1 + error['parameter'] = Integer(1234) + """ + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s) + #: associated with |ASN.1| type. + tagSet = tag.TagSet() # untagged + + #: Set (on class, not on instance) or return a + #: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection` object + #: imposing constraints on |ASN.1| type initialization values. + subtypeSpec = constraint.ConstraintsIntersection() + + # Disambiguation ASN.1 types identification + typeId = OctetString.getTypeId() + + @property + def tagMap(self): + """"Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping + ASN.1 tags to ASN.1 objects contained within callee. + """ + try: + return self._tagMap + + except AttributeError: + self._tagMap = tagmap.TagMap( + {self.tagSet: self}, + {eoo.endOfOctets.tagSet: eoo.endOfOctets}, + self + ) + + return self._tagMap + +# XXX +# coercion rules? diff --git a/contrib/python/pyasn1/py3/pyasn1/type/useful.py b/contrib/python/pyasn1/py3/pyasn1/type/useful.py new file mode 100644 index 0000000000..a8ae874057 --- /dev/null +++ b/contrib/python/pyasn1/py3/pyasn1/type/useful.py @@ -0,0 +1,189 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import datetime + +from pyasn1 import error +from pyasn1.type import char +from pyasn1.type import tag +from pyasn1.type import univ + +__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) + ) + + # Optimization for faster codec lookup + typeId = char.GraphicString.getTypeId() + + +class TimeMixIn(object): + + _yearsDigits = 4 + _hasSubsecond = False + _optionalMinutes = False + _shortTZ = False + + class FixedOffset(datetime.tzinfo): + """Fixed offset in minutes east from UTC.""" + + # defaulted arguments required + # https: // docs.python.org / 2.3 / lib / datetime - tzinfo.html + def __init__(self, offset=0, name='UTC'): + self.__offset = datetime.timedelta(minutes=offset) + self.__name = name + + def utcoffset(self, dt): + return self.__offset + + def tzname(self, dt): + return self.__name + + def dst(self, dt): + return datetime.timedelta(0) + + UTC = FixedOffset() + + @property + def asDateTime(self): + """Create :py:class:`datetime.datetime` object from a |ASN.1| object. + + Returns + ------- + : + new instance of :py:class:`datetime.datetime` object + """ + text = str(self) + if text.endswith('Z'): + tzinfo = TimeMixIn.UTC + text = text[:-1] + + elif '-' in text or '+' in text: + if '+' in text: + text, plusminus, tz = text.partition('+') + else: + text, plusminus, tz = text.partition('-') + + if self._shortTZ and len(tz) == 2: + tz += '00' + + if len(tz) != 4: + raise error.PyAsn1Error('malformed time zone offset %s' % tz) + + try: + minutes = int(tz[:2]) * 60 + int(tz[2:]) + if plusminus == '-': + minutes *= -1 + + except ValueError: + raise error.PyAsn1Error('unknown time specification %s' % self) + + tzinfo = TimeMixIn.FixedOffset(minutes, '?') + + else: + tzinfo = None + + if '.' in text or ',' in text: + if '.' in text: + text, _, ms = text.partition('.') + else: + text, _, ms = text.partition(',') + + try: + ms = int(ms) * 1000 + + except ValueError: + raise error.PyAsn1Error('bad sub-second time specification %s' % self) + + else: + ms = 0 + + if self._optionalMinutes and len(text) - self._yearsDigits == 6: + text += '0000' + elif len(text) - self._yearsDigits == 8: + text += '00' + + try: + dt = datetime.datetime.strptime(text, self._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S') + + except ValueError: + raise error.PyAsn1Error('malformed datetime format %s' % self) + + return dt.replace(microsecond=ms, tzinfo=tzinfo) + + @classmethod + def fromDateTime(cls, dt): + """Create |ASN.1| object from a :py:class:`datetime.datetime` object. + + Parameters + ---------- + dt: :py:class:`datetime.datetime` object + The `datetime.datetime` object to initialize the |ASN.1| object + from + + Returns + ------- + : + new instance of |ASN.1| value + """ + text = dt.strftime(cls._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S') + if cls._hasSubsecond: + text += '.%d' % (dt.microsecond // 1000) + + if dt.utcoffset(): + seconds = dt.utcoffset().seconds + if seconds < 0: + text += '-' + else: + text += '+' + text += '%.2d%.2d' % (seconds // 3600, seconds % 3600) + else: + text += 'Z' + + return cls(text) + + +class GeneralizedTime(char.VisibleString, TimeMixIn): + __doc__ = char.VisibleString.__doc__ + + #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects + tagSet = char.VisibleString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24) + ) + + # Optimization for faster codec lookup + typeId = char.VideotexString.getTypeId() + + _yearsDigits = 4 + _hasSubsecond = True + _optionalMinutes = True + _shortTZ = True + + +class UTCTime(char.VisibleString, TimeMixIn): + __doc__ = char.VisibleString.__doc__ + + #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects + tagSet = char.VisibleString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23) + ) + + # Optimization for faster codec lookup + typeId = char.VideotexString.getTypeId() + + _yearsDigits = 2 + _hasSubsecond = False + _optionalMinutes = False + _shortTZ = False diff --git a/contrib/python/pyasn1/py3/tests/__init__.py b/contrib/python/pyasn1/py3/tests/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/tests/__main__.py b/contrib/python/pyasn1/py3/tests/__main__.py new file mode 100644 index 0000000000..d32d511557 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/__main__.py @@ -0,0 +1,18 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import unittest + +suite = unittest.TestLoader().loadTestsFromNames( + ['tests.test_debug.suite', + 'tests.type.__main__.suite', + 'tests.codec.__main__.suite', + 'tests.compat.__main__.suite'] +) + + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/base.py b/contrib/python/pyasn1/py3/tests/base.py new file mode 100644 index 0000000000..f7513d8d9e --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/base.py @@ -0,0 +1,18 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import unittest + +from pyasn1 import debug + + +class BaseTestCase(unittest.TestCase): + + def setUp(self): + debug.setLogger(debug.Debug('all', printer=lambda *x: None)) + + def tearDown(self): + debug.setLogger(None) diff --git a/contrib/python/pyasn1/py3/tests/codec/__init__.py b/contrib/python/pyasn1/py3/tests/codec/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/tests/codec/__main__.py b/contrib/python/pyasn1/py3/tests/codec/__main__.py new file mode 100644 index 0000000000..b02f0723ca --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/__main__.py @@ -0,0 +1,19 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import unittest + +suite = unittest.TestLoader().loadTestsFromNames( + ['tests.codec.test_streaming.suite', + 'tests.codec.ber.__main__.suite', + 'tests.codec.cer.__main__.suite', + 'tests.codec.der.__main__.suite', + 'tests.codec.native.__main__.suite'] +) + + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/ber/__init__.py b/contrib/python/pyasn1/py3/tests/codec/ber/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/ber/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/tests/codec/ber/__main__.py b/contrib/python/pyasn1/py3/tests/codec/ber/__main__.py new file mode 100644 index 0000000000..ff38c97011 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/ber/__main__.py @@ -0,0 +1,16 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import unittest + +suite = unittest.TestLoader().loadTestsFromNames( + ['tests.codec.ber.test_encoder.suite', + 'tests.codec.ber.test_decoder.suite'] +) + + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/ber/test_decoder.py b/contrib/python/pyasn1/py3/tests/codec/ber/test_decoder.py new file mode 100644 index 0000000000..9e238cd458 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/ber/test_decoder.py @@ -0,0 +1,1847 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import gzip +import io +import os +import sys +import tempfile +import unittest +import zipfile + +from __tests__.base import BaseTestCase + +from pyasn1.type import tag +from pyasn1.type import namedtype +from pyasn1.type import opentype +from pyasn1.type import univ +from pyasn1.type import char +from pyasn1.codec import streaming +from pyasn1.codec.ber import decoder +from pyasn1.codec.ber import eoo +from pyasn1.compat.octets import ints2octs, str2octs, null +from pyasn1 import error + + +class LargeTagDecoderTestCase(BaseTestCase): + def testLargeTag(self): + assert decoder.decode(ints2octs((127, 141, 245, 182, 253, 47, 3, 2, 1, 1))) == (1, null) + + def testLongTag(self): + assert decoder.decode(ints2octs((0x1f, 2, 1, 0)))[0].tagSet == univ.Integer.tagSet + + def testTagsEquivalence(self): + integer = univ.Integer(2).subtype(implicitTag=tag.Tag(tag.tagClassContext, 0, 0)) + assert decoder.decode(ints2octs((0x9f, 0x80, 0x00, 0x02, 0x01, 0x02)), asn1Spec=integer) == decoder.decode( + ints2octs((0x9f, 0x00, 0x02, 0x01, 0x02)), asn1Spec=integer) + + +class DecoderCacheTestCase(BaseTestCase): + def testCache(self): + assert decoder.decode(ints2octs((0x1f, 2, 1, 0))) == decoder.decode(ints2octs((0x1f, 2, 1, 0))) + + +class IntegerDecoderTestCase(BaseTestCase): + def testPosInt(self): + assert decoder.decode(ints2octs((2, 1, 12))) == (12, null) + + def testNegInt(self): + assert decoder.decode(ints2octs((2, 1, 244))) == (-12, null) + + def testZero(self): + assert decoder.decode(ints2octs((2, 0))) == (0, null) + + def testZeroLong(self): + assert decoder.decode(ints2octs((2, 1, 0))) == (0, null) + + def testMinusOne(self): + assert decoder.decode(ints2octs((2, 1, 255))) == (-1, null) + + def testPosLong(self): + assert decoder.decode( + ints2octs((2, 9, 0, 255, 255, 255, 255, 255, 255, 255, 255)) + ) == (0xffffffffffffffff, null) + + def testNegLong(self): + assert decoder.decode( + ints2octs((2, 9, 255, 0, 0, 0, 0, 0, 0, 0, 1)) + ) == (-0xffffffffffffffff, null) + + def testSpec(self): + try: + decoder.decode( + ints2octs((2, 1, 12)), asn1Spec=univ.Null() + ) == (12, null) + except error.PyAsn1Error: + pass + else: + assert 0, 'wrong asn1Spec worked out' + assert decoder.decode( + ints2octs((2, 1, 12)), asn1Spec=univ.Integer() + ) == (12, null) + + def testTagFormat(self): + try: + decoder.decode(ints2octs((34, 1, 12))) + except error.PyAsn1Error: + pass + else: + assert 0, 'wrong tagFormat worked out' + + +class BooleanDecoderTestCase(BaseTestCase): + def testTrue(self): + assert decoder.decode(ints2octs((1, 1, 1))) == (1, null) + + def testTrueNeg(self): + assert decoder.decode(ints2octs((1, 1, 255))) == (1, null) + + def testExtraTrue(self): + assert decoder.decode(ints2octs((1, 1, 1, 0, 120, 50, 50))) == (1, ints2octs((0, 120, 50, 50))) + + def testFalse(self): + assert decoder.decode(ints2octs((1, 1, 0))) == (0, null) + + def testTagFormat(self): + try: + decoder.decode(ints2octs((33, 1, 1))) + except error.PyAsn1Error: + pass + else: + assert 0, 'wrong tagFormat worked out' + + +class BitStringDecoderTestCase(BaseTestCase): + def testDefMode(self): + assert decoder.decode( + ints2octs((3, 3, 1, 169, 138)) + ) == ((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1), null) + + def testIndefMode(self): + assert decoder.decode( + ints2octs((3, 3, 1, 169, 138)) + ) == ((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1), null) + + def testDefModeChunked(self): + assert decoder.decode( + ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138)) + ) == ((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1), null) + + def testIndefModeChunked(self): + assert decoder.decode( + ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0)) + ) == ((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1), null) + + def testDefModeChunkedSubst(self): + assert decoder.decode( + ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138)), + substrateFun=lambda a, b, c, d: streaming.readFromStream(b, c) + ) == (ints2octs((3, 2, 0, 169, 3, 2, 1, 138)), str2octs('')) + + def testIndefModeChunkedSubst(self): + assert decoder.decode( + ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0)), + substrateFun=lambda a, b, c, d: streaming.readFromStream(b, c) + ) == (ints2octs((3, 2, 0, 169, 3, 2, 1, 138, 0, 0)), str2octs('')) + + def testTypeChecking(self): + try: + decoder.decode(ints2octs((35, 4, 2, 2, 42, 42))) + except error.PyAsn1Error: + pass + else: + assert 0, 'accepted mis-encoded bit-string constructed out of an integer' + + +class OctetStringDecoderTestCase(BaseTestCase): + def testDefMode(self): + assert decoder.decode( + ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)) + ) == (str2octs('Quick brown fox'), null) + + def testIndefMode(self): + assert decoder.decode( + ints2octs((36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0)) + ) == (str2octs('Quick brown fox'), null) + + def testDefModeChunked(self): + assert decoder.decode( + ints2octs( + (36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120)) + ) == (str2octs('Quick brown fox'), null) + + def testIndefModeChunked(self): + assert decoder.decode( + ints2octs((36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0)) + ) == (str2octs('Quick brown fox'), null) + + def testDefModeChunkedSubst(self): + assert decoder.decode( + ints2octs( + (36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120)), + substrateFun=lambda a, b, c, d: streaming.readFromStream(b, c) + ) == (ints2octs((4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120)), str2octs('')) + + def testIndefModeChunkedSubst(self): + assert decoder.decode( + ints2octs((36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, + 120, 0, 0)), + substrateFun=lambda a, b, c, d: streaming.readFromStream(b, c) + ) == (ints2octs( + (4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0)), str2octs('')) + + +class ExpTaggedOctetStringDecoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.o = univ.OctetString( + 'Quick brown fox', + tagSet=univ.OctetString.tagSet.tagExplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 5) + )) + + def testDefMode(self): + o, r = decoder.decode( + ints2octs((101, 17, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)) + ) + assert not r + assert self.o == o + assert self.o.tagSet == o.tagSet + assert self.o.isSameTypeWith(o) + + def testIndefMode(self): + o, r = decoder.decode( + ints2octs((101, 128, 36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0, 0, 0)) + ) + assert not r + assert self.o == o + assert self.o.tagSet == o.tagSet + assert self.o.isSameTypeWith(o) + + def testDefModeChunked(self): + o, r = decoder.decode( + ints2octs((101, 25, 36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120)) + ) + assert not r + assert self.o == o + assert self.o.tagSet == o.tagSet + assert self.o.isSameTypeWith(o) + + def testIndefModeChunked(self): + o, r = decoder.decode( + ints2octs((101, 128, 36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0, 0, 0)) + ) + assert not r + assert self.o == o + assert self.o.tagSet == o.tagSet + assert self.o.isSameTypeWith(o) + + def testDefModeSubst(self): + assert decoder.decode( + ints2octs((101, 17, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)), + substrateFun=lambda a, b, c, d: streaming.readFromStream(b, c) + ) == (ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)), str2octs('')) + + def testIndefModeSubst(self): + assert decoder.decode( + ints2octs(( + 101, 128, 36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, + 0, 0, 0)), + substrateFun=lambda a, b, c, d: streaming.readFromStream(b, c) + ) == (ints2octs( + (36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0, 0, 0)), str2octs('')) + + +class NullDecoderTestCase(BaseTestCase): + def testNull(self): + assert decoder.decode(ints2octs((5, 0))) == (null, null) + + def testTagFormat(self): + try: + decoder.decode(ints2octs((37, 0))) + except error.PyAsn1Error: + pass + else: + assert 0, 'wrong tagFormat worked out' + + +# Useful analysis of OID encoding issues could be found here: +# https://misc.daniel-marschall.de/asn.1/oid_facts.html +class ObjectIdentifierDecoderTestCase(BaseTestCase): + def testOne(self): + assert decoder.decode( + ints2octs((6, 6, 43, 6, 0, 191, 255, 126)) + ) == ((1, 3, 6, 0, 0xffffe), null) + + def testEdge1(self): + assert decoder.decode( + ints2octs((6, 1, 39)) + ) == ((0, 39), null) + + def testEdge2(self): + assert decoder.decode( + ints2octs((6, 1, 79)) + ) == ((1, 39), null) + + def testEdge3(self): + assert decoder.decode( + ints2octs((6, 1, 120)) + ) == ((2, 40), null) + + def testEdge4(self): + assert decoder.decode( + ints2octs((6, 5, 0x90, 0x80, 0x80, 0x80, 0x4F)) + ) == ((2, 0xffffffff), null) + + def testEdge5(self): + assert decoder.decode( + ints2octs((6, 1, 0x7F)) + ) == ((2, 47), null) + + def testEdge6(self): + assert decoder.decode( + ints2octs((6, 2, 0x81, 0x00)) + ) == ((2, 48), null) + + def testEdge7(self): + assert decoder.decode( + ints2octs((6, 3, 0x81, 0x34, 0x03)) + ) == ((2, 100, 3), null) + + def testEdge8(self): + assert decoder.decode( + ints2octs((6, 2, 133, 0)) + ) == ((2, 560), null) + + def testEdge9(self): + assert decoder.decode( + ints2octs((6, 4, 0x88, 0x84, 0x87, 0x02)) + ) == ((2, 16843570), null) + + def testNonLeading0x80(self): + assert decoder.decode( + ints2octs((6, 5, 85, 4, 129, 128, 0)), + ) == ((2, 5, 4, 16384), null) + + def testLeading0x80Case1(self): + try: + decoder.decode( + ints2octs((6, 5, 85, 4, 128, 129, 0)) + ) + except error.PyAsn1Error: + pass + else: + assert 0, 'Leading 0x80 tolerated' + + def testLeading0x80Case2(self): + try: + decoder.decode( + ints2octs((6, 7, 1, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F)) + ) + except error.PyAsn1Error: + pass + else: + assert 0, 'Leading 0x80 tolerated' + + def testLeading0x80Case3(self): + try: + decoder.decode( + ints2octs((6, 2, 0x80, 1)) + ) + except error.PyAsn1Error: + pass + else: + assert 0, 'Leading 0x80 tolerated' + + def testLeading0x80Case4(self): + try: + decoder.decode( + ints2octs((6, 2, 0x80, 0x7F)) + ) + except error.PyAsn1Error: + pass + else: + assert 0, 'Leading 0x80 tolerated' + + def testTagFormat(self): + try: + decoder.decode(ints2octs((38, 1, 239))) + except error.PyAsn1Error: + pass + else: + assert 0, 'wrong tagFormat worked out' + + def testZeroLength(self): + try: + decoder.decode(ints2octs((6, 0, 0))) + except error.PyAsn1Error: + pass + else: + assert 0, 'zero length tolerated' + + def testIndefiniteLength(self): + try: + decoder.decode(ints2octs((6, 128, 0))) + except error.PyAsn1Error: + pass + else: + assert 0, 'indefinite length tolerated' + + def testReservedLength(self): + try: + decoder.decode(ints2octs((6, 255, 0))) + except error.PyAsn1Error: + pass + else: + assert 0, 'reserved length tolerated' + + def testLarge1(self): + assert decoder.decode( + ints2octs((0x06, 0x11, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB, 0xE2, 0xB7, 0x17)) + ) == ((2, 18446744073709551535184467440737095), null) + + def testLarge2(self): + assert decoder.decode( + ints2octs((0x06, 0x13, 0x88, 0x37, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB, 0xE2, 0xB6, 0x47)) + ) == ((2, 999, 18446744073709551535184467440737095), null) + + +class RealDecoderTestCase(BaseTestCase): + def testChar(self): + assert decoder.decode( + ints2octs((9, 7, 3, 49, 50, 51, 69, 49, 49)) + ) == (univ.Real((123, 10, 11)), null) + + def testBin1(self): # check base = 2 + assert decoder.decode( # (0.5, 2, 0) encoded with base = 2 + ints2octs((9, 3, 128, 255, 1)) + ) == (univ.Real((1, 2, -1)), null) + + def testBin2(self): # check base = 2 and scale factor + assert decoder.decode( # (3.25, 2, 0) encoded with base = 8 + ints2octs((9, 3, 148, 255, 13)) + ) == (univ.Real((26, 2, -3)), null) + + def testBin3(self): # check base = 16 + assert decoder.decode( # (0.00390625, 2, 0) encoded with base = 16 + ints2octs((9, 3, 160, 254, 1)) + ) == (univ.Real((1, 2, -8)), null) + + def testBin4(self): # check exponent = 0 + assert decoder.decode( # (1, 2, 0) encoded with base = 2 + ints2octs((9, 3, 128, 0, 1)) + ) == (univ.Real((1, 2, 0)), null) + + def testBin5(self): # case of 2 octs for exponent and negative exponent + assert decoder.decode( # (3, 2, -1020) encoded with base = 16 + ints2octs((9, 4, 161, 255, 1, 3)) + ) == (univ.Real((3, 2, -1020)), null) + +# TODO: this requires Real type comparison fix + +# def testBin6(self): +# assert decoder.decode( +# ints2octs((9, 5, 162, 0, 255, 255, 1)) +# ) == (univ.Real((1, 2, 262140)), null) + +# def testBin7(self): +# assert decoder.decode( +# ints2octs((9, 7, 227, 4, 1, 35, 69, 103, 1)) +# ) == (univ.Real((-1, 2, 76354972)), null) + + def testPlusInf(self): + assert decoder.decode( + ints2octs((9, 1, 64)) + ) == (univ.Real('inf'), null) + + def testMinusInf(self): + assert decoder.decode( + ints2octs((9, 1, 65)) + ) == (univ.Real('-inf'), null) + + def testEmpty(self): + assert decoder.decode( + ints2octs((9, 0)) + ) == (univ.Real(0.0), null) + + def testTagFormat(self): + try: + decoder.decode(ints2octs((41, 0))) + except error.PyAsn1Error: + pass + else: + assert 0, 'wrong tagFormat worked out' + + def testShortEncoding(self): + try: + decoder.decode(ints2octs((9, 1, 131))) + except error.PyAsn1Error: + pass + else: + assert 0, 'accepted too-short real' + + +class UniversalStringDecoderTestCase(BaseTestCase): + def testDecoder(self): + assert decoder.decode(ints2octs((28, 12, 0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99))) == (char.UniversalString(sys.version_info[0] >= 3 and 'abc' or unicode('abc')), null) + + +class BMPStringDecoderTestCase(BaseTestCase): + def testDecoder(self): + assert decoder.decode(ints2octs((30, 6, 0, 97, 0, 98, 0, 99))) == (char.BMPString(sys.version_info[0] >= 3 and 'abc' or unicode('abc')), null) + + +class UTF8StringDecoderTestCase(BaseTestCase): + def testDecoder(self): + assert decoder.decode(ints2octs((12, 3, 97, 98, 99))) == (char.UTF8String(sys.version_info[0] >= 3 and 'abc' or unicode('abc')), null) + + +class SequenceOfDecoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.s = univ.SequenceOf(componentType=univ.OctetString()) + self.s.setComponentByPosition(0, univ.OctetString('quick brown')) + + def testDefMode(self): + assert decoder.decode( + ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + ) == (self.s, null) + + def testIndefMode(self): + assert decoder.decode( + ints2octs((48, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)) + ) == (self.s, null) + + def testDefModeChunked(self): + assert decoder.decode( + ints2octs((48, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)) + ) == (self.s, null) + + def testIndefModeChunked(self): + assert decoder.decode( + ints2octs((48, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)) + ) == (self.s, null) + + def testSchemalessDecoder(self): + assert decoder.decode( + ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=univ.SequenceOf() + ) == (self.s, null) + + +class ExpTaggedSequenceOfDecoderTestCase(BaseTestCase): + + def testWithSchema(self): + s = univ.SequenceOf().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)) + s2, r = decoder.decode( + ints2octs((163, 15, 48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=s + ) + assert not r + assert s2 == [str2octs('quick brown')] + assert s.tagSet == s2.tagSet + + def testWithoutSchema(self): + s = univ.SequenceOf().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)) + s2, r = decoder.decode( + ints2octs((163, 15, 48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + ) + assert not r + assert s2 == [str2octs('quick brown')] + assert s.tagSet == s2.tagSet + + +class SequenceOfDecoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.SequenceOf(componentType=univ.OctetString()) + self.s.setComponentByPosition(0, univ.OctetString('quick brown')) + + def testDefMode(self): + assert decoder.decode( + ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s + ) == (self.s, null) + + def testIndefMode(self): + assert decoder.decode( + ints2octs((48, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testDefModeChunked(self): + assert decoder.decode( + ints2octs((48, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)), asn1Spec=self.s + ) == (self.s, null) + + def testIndefModeChunked(self): + assert decoder.decode( + ints2octs((48, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + +class SetOfDecoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.SetOf(componentType=univ.OctetString()) + self.s.setComponentByPosition(0, univ.OctetString('quick brown')) + + def testDefMode(self): + assert decoder.decode( + ints2octs((49, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + ) == (self.s, null) + + def testIndefMode(self): + assert decoder.decode( + ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)) + ) == (self.s, null) + + def testDefModeChunked(self): + assert decoder.decode( + ints2octs((49, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)) + ) == (self.s, null) + + def testIndefModeChunked(self): + assert decoder.decode( + ints2octs((49, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)) + ) == (self.s, null) + + def testSchemalessDecoder(self): + assert decoder.decode( + ints2octs((49, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=univ.SetOf() + ) == (self.s, null) + + +class SetOfDecoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.SetOf(componentType=univ.OctetString()) + self.s.setComponentByPosition(0, univ.OctetString('quick brown')) + + def testDefMode(self): + assert decoder.decode( + ints2octs((49, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s + ) == (self.s, null) + + def testIndefMode(self): + assert decoder.decode( + ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testDefModeChunked(self): + assert decoder.decode( + ints2octs((49, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)), asn1Spec=self.s + ) == (self.s, null) + + def testIndefModeChunked(self): + assert decoder.decode( + ints2octs((49, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + +class SequenceDecoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null(null)), + namedtype.NamedType('first-name', univ.OctetString(null)), + namedtype.NamedType('age', univ.Integer(33)) + ) + ) + self.s.setComponentByPosition(0, univ.Null(null)) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testWithOptionalAndDefaultedDefMode(self): + assert decoder.decode( + ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)) + ) == (self.s, null) + + def testWithOptionalAndDefaultedIndefMode(self): + assert decoder.decode( + ints2octs((48, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)) + ) == (self.s, null) + + def testWithOptionalAndDefaultedDefModeChunked(self): + assert decoder.decode( + ints2octs( + (48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)) + ) == (self.s, null) + + def testWithOptionalAndDefaultedIndefModeChunked(self): + assert decoder.decode( + ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)) + ) == (self.s, null) + + def testWithOptionalAndDefaultedDefModeSubst(self): + assert decoder.decode( + ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)), + substrateFun=lambda a, b, c, d: streaming.readFromStream(b, c) + ) == (ints2octs((5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)), str2octs('')) + + def testWithOptionalAndDefaultedIndefModeSubst(self): + assert decoder.decode( + ints2octs((48, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), + substrateFun=lambda a, b, c, d: streaming.readFromStream(b, c) + ) == (ints2octs( + (5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), str2octs('')) + + def testTagFormat(self): + try: + decoder.decode( + ints2octs((16, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)) + ) + except error.PyAsn1Error: + pass + else: + assert 0, 'wrong tagFormat worked out' + + +class SequenceDecoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null(null)), + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + ) + ) + + def __init(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null(null)) + + def __initWithOptional(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null(null)) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + + def __initWithDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null(null)) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def __initWithOptionalAndDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null(null)) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testDefMode(self): + self.__init() + assert decoder.decode( + ints2octs((48, 2, 5, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testIndefMode(self): + self.__init() + assert decoder.decode( + ints2octs((48, 128, 5, 0, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testDefModeChunked(self): + self.__init() + assert decoder.decode( + ints2octs((48, 2, 5, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testIndefModeChunked(self): + self.__init() + assert decoder.decode( + ints2octs((48, 128, 5, 0, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalDefMode(self): + self.__initWithOptional() + assert decoder.decode( + ints2octs((48, 15, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionaIndefMode(self): + self.__initWithOptional() + assert decoder.decode( + ints2octs((48, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 0, 0)), + asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalDefModeChunked(self): + self.__initWithOptional() + assert decoder.decode( + ints2octs((48, 21, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)), + asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalIndefModeChunked(self): + self.__initWithOptional() + assert decoder.decode( + ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, + 0, 0, 0)), + asn1Spec=self.s + ) == (self.s, null) + + def testWithDefaultedDefMode(self): + self.__initWithDefaulted() + assert decoder.decode( + ints2octs((48, 5, 5, 0, 2, 1, 1)), asn1Spec=self.s + ) == (self.s, null) + + def testWithDefaultedIndefMode(self): + self.__initWithDefaulted() + assert decoder.decode( + ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithDefaultedDefModeChunked(self): + self.__initWithDefaulted() + assert decoder.decode( + ints2octs((48, 5, 5, 0, 2, 1, 1)), asn1Spec=self.s + ) == (self.s, null) + + def testWithDefaultedIndefModeChunked(self): + self.__initWithDefaulted() + assert decoder.decode( + ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalAndDefaultedDefMode(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)), + asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalAndDefaultedIndefMode(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + ints2octs((48, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, + 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalAndDefaultedDefModeChunked(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + ints2octs( + (48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)), + asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalAndDefaultedIndefModeChunked(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, + 0, 2, 1, 1, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + +class SequenceDecoderWithUntaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any(), openType=openType) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 6, 2, 1, 1, 2, 1, 12)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1] == 12 + + def testDecodeOpenTypesChoiceTwo(self): + s, r = decoder.decode( + ints2octs((48, 16, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 2 + assert s[1] == univ.OctetString('quick brown') + + def testDecodeOpenTypesUnknownType(self): + try: + s, r = decoder.decode( + ints2octs((48, 6, 2, 1, 2, 6, 1, 39)), asn1Spec=self.s, + decodeOpenTypes=True + ) + + except error.PyAsn1Error: + pass + + else: + assert False, 'unknown open type tolerated' + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 6, 2, 1, 3, 6, 1, 39)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1] == univ.OctetString(hexValue='060127') + + def testDontDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 6, 2, 1, 1, 2, 1, 12)), asn1Spec=self.s + ) + assert not r + assert s[0] == 1 + assert s[1] == ints2octs((2, 1, 12)) + + def testDontDecodeOpenTypesChoiceTwo(self): + s, r = decoder.decode( + ints2octs((48, 16, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s + ) + assert not r + assert s[0] == 2 + assert s[1] == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + +class SequenceDecoderWithImplicitlyTaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType( + 'blob', univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType + ) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 1, 131, 3, 2, 1, 12)), asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1] == 12 + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 3, 131, 3, 2, 1, 12)), asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1] == univ.OctetString(hexValue='02010C') + + +class SequenceDecoderWithExplicitlyTaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType( + 'blob', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType + ) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 1, 163, 3, 2, 1, 12)), asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1] == 12 + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 3, 163, 3, 2, 1, 12)), asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1] == univ.OctetString(hexValue='02010C') + + +class SequenceDecoderWithUnaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.SetOf(componentType=univ.Any()), + openType=openType) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 1, 49, 3, 2, 1, 12)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1][0] == 12 + + def testDecodeOpenTypesChoiceTwo(self): + s, r = decoder.decode( + ints2octs((48, 18, 2, 1, 2, 49, 13, 4, 11, 113, 117, 105, 99, + 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 2 + assert s[1][0] == univ.OctetString('quick brown') + + def testDecodeOpenTypesUnknownType(self): + try: + s, r = decoder.decode( + ints2octs((48, 6, 2, 1, 2, 6, 1, 39)), asn1Spec=self.s, + decodeOpenTypes=True + ) + + except error.PyAsn1Error: + pass + + else: + assert False, 'unknown open type tolerated' + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 3, 49, 3, 2, 1, 12)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1][0] == univ.OctetString(hexValue='02010c') + + def testDontDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 1, 49, 3, 2, 1, 12)), asn1Spec=self.s + ) + assert not r + assert s[0] == 1 + assert s[1][0] == ints2octs((2, 1, 12)) + + def testDontDecodeOpenTypesChoiceTwo(self): + s, r = decoder.decode( + ints2octs((48, 18, 2, 1, 2, 49, 13, 4, 11, 113, 117, 105, 99, + 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s + ) + assert not r + assert s[0] == 2 + assert s[1][0] == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, + 111, 119, 110)) + + +class SequenceDecoderWithImplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType( + 'blob', univ.SetOf( + componentType=univ.Any().subtype( + implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 3))), + openType=openType + ) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 10, 2, 1, 1, 49, 5, 131, 3, 2, 1, 12)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1][0] == 12 + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 10, 2, 1, 3, 49, 5, 131, 3, 2, 1, 12)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1][0] == univ.OctetString(hexValue='02010C') + + +class SequenceDecoderWithExplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType( + 'blob', univ.SetOf( + componentType=univ.Any().subtype( + explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 3))), + openType=openType + ) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 10, 2, 1, 1, 49, 5, 131, 3, 2, 1, 12)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1][0] == 12 + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs( (48, 10, 2, 1, 3, 49, 5, 131, 3, 2, 1, 12)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1][0] == univ.OctetString(hexValue='02010C') + + +class SetDecoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Set( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null(null)), + namedtype.NamedType('first-name', univ.OctetString(null)), + namedtype.NamedType('age', univ.Integer(33)) + ) + ) + self.s.setComponentByPosition(0, univ.Null(null)) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testWithOptionalAndDefaultedDefMode(self): + assert decoder.decode( + ints2octs((49, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)) + ) == (self.s, null) + + def testWithOptionalAndDefaultedIndefMode(self): + assert decoder.decode( + ints2octs((49, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)) + ) == (self.s, null) + + def testWithOptionalAndDefaultedDefModeChunked(self): + assert decoder.decode( + ints2octs( + (49, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)) + ) == (self.s, null) + + def testWithOptionalAndDefaultedIndefModeChunked(self): + assert decoder.decode( + ints2octs((49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)) + ) == (self.s, null) + + def testWithOptionalAndDefaultedDefModeSubst(self): + assert decoder.decode( + ints2octs((49, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)), + substrateFun=lambda a, b, c, d: streaming.readFromStream(b, c) + ) == (ints2octs((5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)), str2octs('')) + + def testWithOptionalAndDefaultedIndefModeSubst(self): + assert decoder.decode( + ints2octs((49, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), + substrateFun=lambda a, b, c, d: streaming.readFromStream(b, c) + ) == (ints2octs( + (5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), str2octs('')) + + def testTagFormat(self): + try: + decoder.decode( + ints2octs((16, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)) + ) + except error.PyAsn1Error: + pass + else: + assert 0, 'wrong tagFormat worked out' + + +class SetDecoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Set( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null(null)), + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + ) + ) + + def __init(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null(null)) + + def __initWithOptional(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null(null)) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + + def __initWithDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null(null)) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def __initWithOptionalAndDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null(null)) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testDefMode(self): + self.__init() + assert decoder.decode( + ints2octs((49, 128, 5, 0, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testIndefMode(self): + self.__init() + assert decoder.decode( + ints2octs((49, 128, 5, 0, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testDefModeChunked(self): + self.__init() + assert decoder.decode( + ints2octs((49, 2, 5, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testIndefModeChunked(self): + self.__init() + assert decoder.decode( + ints2octs((49, 128, 5, 0, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalDefMode(self): + self.__initWithOptional() + assert decoder.decode( + ints2octs((49, 15, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalIndefMode(self): + self.__initWithOptional() + assert decoder.decode( + ints2octs((49, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalDefModeChunked(self): + self.__initWithOptional() + assert decoder.decode( + ints2octs((49, 21, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalIndefModeChunked(self): + self.__initWithOptional() + assert decoder.decode( + ints2octs((49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithDefaultedDefMode(self): + self.__initWithDefaulted() + assert decoder.decode( + ints2octs((49, 5, 5, 0, 2, 1, 1)), asn1Spec=self.s + ) == (self.s, null) + + def testWithDefaultedIndefMode(self): + self.__initWithDefaulted() + assert decoder.decode( + ints2octs((49, 128, 5, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithDefaultedDefModeChunked(self): + self.__initWithDefaulted() + assert decoder.decode( + ints2octs((49, 5, 5, 0, 2, 1, 1)), asn1Spec=self.s + ) == (self.s, null) + + def testWithDefaultedIndefModeChunked(self): + self.__initWithDefaulted() + assert decoder.decode( + ints2octs((49, 128, 5, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalAndDefaultedDefMode(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + ints2octs((49, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalAndDefaultedDefModeReordered(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + ints2octs((49, 18, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalAndDefaultedIndefMode(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + ints2octs((49, 128, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalAndDefaultedIndefModeReordered(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + ints2octs((49, 128, 2, 1, 1, 5, 0, 36, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalAndDefaultedDefModeChunked(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + ints2octs((49, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)), asn1Spec=self.s + ) == (self.s, null) + + def testWithOptionalAndDefaultedIndefModeChunked(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + ints2octs((49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)), asn1Spec=self.s + ) == (self.s, null) + + +class SequenceOfWithExpTaggedOctetStringDecoder(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.SequenceOf( + componentType=univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)) + ) + self.s.setComponentByPosition(0, 'q') + self.s2 = univ.SequenceOf() + + def testDefModeSchema(self): + s, r = decoder.decode(ints2octs((48, 5, 163, 3, 4, 1, 113)), asn1Spec=self.s) + assert not r + assert s == self.s + assert s.tagSet == self.s.tagSet + + def testIndefModeSchema(self): + s, r = decoder.decode(ints2octs((48, 128, 163, 128, 4, 1, 113, 0, 0, 0, 0)), asn1Spec=self.s) + assert not r + assert s == self.s + assert s.tagSet == self.s.tagSet + + def testDefModeNoComponent(self): + s, r = decoder.decode(ints2octs((48, 5, 163, 3, 4, 1, 113)), asn1Spec=self.s2) + assert not r + assert s == self.s + assert s.tagSet == self.s.tagSet + + def testIndefModeNoComponent(self): + s, r = decoder.decode(ints2octs((48, 128, 163, 128, 4, 1, 113, 0, 0, 0, 0)), asn1Spec=self.s2) + assert not r + assert s == self.s + assert s.tagSet == self.s.tagSet + + def testDefModeSchemaless(self): + s, r = decoder.decode(ints2octs((48, 5, 163, 3, 4, 1, 113))) + assert not r + assert s == self.s + assert s.tagSet == self.s.tagSet + + def testIndefModeSchemaless(self): + s, r = decoder.decode(ints2octs((48, 128, 163, 128, 4, 1, 113, 0, 0, 0, 0))) + assert not r + assert s == self.s + assert s.tagSet == self.s.tagSet + + +class SequenceWithExpTaggedOctetStringDecoder(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType( + 'x', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)) + ) + ) + ) + self.s.setComponentByPosition(0, 'q') + self.s2 = univ.Sequence() + + def testDefModeSchema(self): + s, r = decoder.decode(ints2octs((48, 5, 163, 3, 4, 1, 113)), asn1Spec=self.s) + assert not r + assert s == self.s + assert s.tagSet == self.s.tagSet + + def testIndefModeSchema(self): + s, r = decoder.decode(ints2octs((48, 128, 163, 128, 4, 1, 113, 0, 0, 0, 0)), asn1Spec=self.s) + assert not r + assert s == self.s + assert s.tagSet == self.s.tagSet + + def testDefModeNoComponent(self): + s, r = decoder.decode(ints2octs((48, 5, 163, 3, 4, 1, 113)), asn1Spec=self.s2) + assert not r + assert s == self.s + assert s.tagSet == self.s.tagSet + + def testIndefModeNoComponent(self): + s, r = decoder.decode(ints2octs((48, 128, 163, 128, 4, 1, 113, 0, 0, 0, 0)), asn1Spec=self.s2) + assert not r + assert s == self.s + assert s.tagSet == self.s.tagSet + + def testDefModeSchemaless(self): + s, r = decoder.decode(ints2octs((48, 5, 163, 3, 4, 1, 113))) + assert not r + assert s == self.s + assert s.tagSet == self.s.tagSet + + def testIndefModeSchemaless(self): + s, r = decoder.decode(ints2octs((48, 128, 163, 128, 4, 1, 113, 0, 0, 0, 0))) + assert not r + assert s == self.s + assert s.tagSet == self.s.tagSet + + +class ChoiceDecoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null(null)), + namedtype.NamedType('number', univ.Integer(0)), + namedtype.NamedType('string', univ.OctetString()) + ) + ) + + def testBySpec(self): + self.s.setComponentByPosition(0, univ.Null(null)) + assert decoder.decode( + ints2octs((5, 0)), asn1Spec=self.s + ) == (self.s, null) + + def testWithoutSpec(self): + self.s.setComponentByPosition(0, univ.Null(null)) + assert decoder.decode(ints2octs((5, 0))) == (self.s, null) + assert decoder.decode(ints2octs((5, 0))) == (univ.Null(null), null) + + def testUndefLength(self): + self.s.setComponentByPosition(2, univ.OctetString('abcdefgh')) + assert decoder.decode(ints2octs((36, 128, 4, 3, 97, 98, 99, 4, 3, 100, 101, 102, 4, 2, 103, 104, 0, 0)), + asn1Spec=self.s) == (self.s, null) + + def testExplicitTag(self): + s = self.s.subtype(explicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatConstructed, 4)) + s.setComponentByPosition(0, univ.Null(null)) + assert decoder.decode(ints2octs((164, 2, 5, 0)), asn1Spec=s) == (s, null) + + def testExplicitTagUndefLength(self): + s = self.s.subtype(explicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatConstructed, 4)) + s.setComponentByPosition(0, univ.Null(null)) + assert decoder.decode(ints2octs((164, 128, 5, 0, 0, 0)), asn1Spec=s) == (s, null) + + +class AnyDecoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Any() + + def testByUntagged(self): + assert decoder.decode( + ints2octs((4, 3, 102, 111, 120)), asn1Spec=self.s + ) == (univ.Any('\004\003fox'), null) + + def testTaggedEx(self): + s = univ.Any('\004\003fox').subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)) + assert decoder.decode(ints2octs((164, 5, 4, 3, 102, 111, 120)), asn1Spec=s) == (s, null) + + def testTaggedIm(self): + s = univ.Any('\004\003fox').subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)) + assert decoder.decode(ints2octs((132, 5, 4, 3, 102, 111, 120)), asn1Spec=s) == (s, null) + + def testByUntaggedIndefMode(self): + assert decoder.decode( + ints2octs((4, 3, 102, 111, 120)), asn1Spec=self.s + ) == (univ.Any('\004\003fox'), null) + + def testTaggedExIndefMode(self): + s = univ.Any('\004\003fox').subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)) + assert decoder.decode(ints2octs((164, 128, 4, 3, 102, 111, 120, 0, 0)), asn1Spec=s) == (s, null) + + def testTaggedImIndefMode(self): + s = univ.Any('\004\003fox').subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)) + assert decoder.decode(ints2octs((164, 128, 4, 3, 102, 111, 120, 0, 0)), asn1Spec=s) == (s, null) + + def testByUntaggedSubst(self): + assert decoder.decode( + ints2octs((4, 3, 102, 111, 120)), + asn1Spec=self.s, + substrateFun=lambda a, b, c, d: streaming.readFromStream(b, c) + ) == (ints2octs((4, 3, 102, 111, 120)), str2octs('')) + + def testTaggedExSubst(self): + assert decoder.decode( + ints2octs((164, 5, 4, 3, 102, 111, 120)), + asn1Spec=self.s, + substrateFun=lambda a, b, c, d: streaming.readFromStream(b, c) + ) == (ints2octs((164, 5, 4, 3, 102, 111, 120)), str2octs('')) + + +class EndOfOctetsTestCase(BaseTestCase): + def testUnexpectedEoo(self): + try: + decoder.decode(ints2octs((0, 0))) + except error.PyAsn1Error: + pass + else: + assert 0, 'end-of-contents octets accepted at top level' + + def testExpectedEoo(self): + result, remainder = decoder.decode(ints2octs((0, 0)), allowEoo=True) + assert eoo.endOfOctets.isSameTypeWith(result) and result == eoo.endOfOctets and result is eoo.endOfOctets + assert remainder == null + + def testDefiniteNoEoo(self): + try: + decoder.decode(ints2octs((0x23, 0x02, 0x00, 0x00))) + except error.PyAsn1Error: + pass + else: + assert 0, 'end-of-contents octets accepted inside definite-length encoding' + + def testIndefiniteEoo(self): + result, remainder = decoder.decode(ints2octs((0x23, 0x80, 0x00, 0x00))) + assert result == () and remainder == null, 'incorrect decoding of indefinite length end-of-octets' + + def testNoLongFormEoo(self): + try: + decoder.decode(ints2octs((0x23, 0x80, 0x00, 0x81, 0x00))) + except error.PyAsn1Error: + pass + else: + assert 0, 'end-of-contents octets accepted with invalid long-form length' + + def testNoConstructedEoo(self): + try: + decoder.decode(ints2octs((0x23, 0x80, 0x20, 0x00))) + except error.PyAsn1Error: + pass + else: + assert 0, 'end-of-contents octets accepted with invalid constructed encoding' + + def testNoEooData(self): + try: + decoder.decode(ints2octs((0x23, 0x80, 0x00, 0x01, 0x00))) + except error.PyAsn1Error: + pass + else: + assert 0, 'end-of-contents octets accepted with unexpected data' + + +class NonStringDecoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null(null)), + namedtype.NamedType('first-name', univ.OctetString(null)), + namedtype.NamedType('age', univ.Integer(33)) + ) + ) + self.s.setComponentByPosition(0, univ.Null(null)) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + self.substrate = ints2octs([48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1]) + + def testOctetString(self): + s = list(decoder.StreamingDecoder( + univ.OctetString(self.substrate), asn1Spec=self.s)) + assert [self.s] == s + + def testAny(self): + s = list(decoder.StreamingDecoder( + univ.Any(self.substrate), asn1Spec=self.s)) + assert [self.s] == s + + +class ErrorOnDecodingTestCase(BaseTestCase): + + def testErrorCondition(self): + decode = decoder.SingleItemDecoder( + tagMap=decoder.TAG_MAP, typeMap=decoder.TYPE_MAP) + substrate = ints2octs((00, 1, 2)) + stream = streaming.asSeekableStream(substrate) + + try: + asn1Object = next(decode(stream)) + + except error.PyAsn1Error: + exc = sys.exc_info()[1] + assert isinstance(exc, error.PyAsn1Error), ( + 'Unexpected exception raised %r' % (exc,)) + + else: + assert False, 'Unexpected decoder result %r' % (asn1Object,) + + def testRawDump(self): + substrate = ints2octs((31, 8, 2, 1, 1, 131, 3, 2, 1, 12)) + stream = streaming.asSeekableStream(substrate) + + class SingleItemEncoder(decoder.SingleItemDecoder): + defaultErrorState = decoder.stDumpRawValue + + class StreamingDecoder(decoder.StreamingDecoder): + SINGLE_ITEM_DECODER = SingleItemEncoder + + class OneShotDecoder(decoder.Decoder): + STREAMING_DECODER = StreamingDecoder + + d = OneShotDecoder() + + asn1Object, rest = d(stream) + + assert isinstance(asn1Object, univ.Any), ( + 'Unexpected raw dump type %r' % (asn1Object,)) + assert asn1Object.asNumbers() == (31, 8, 2, 1, 1), ( + 'Unexpected raw dump value %r' % (asn1Object,)) + assert rest == ints2octs((131, 3, 2, 1, 12)), ( + 'Unexpected rest of substrate after raw dump %r' % rest) + + +@unittest.skipIf(sys.version_info < (3,), "Unsupported on Python 2") +class BinaryFileTestCase(BaseTestCase): + """Assure that decode works on open binary files.""" + def testOneObject(self): + _, path = tempfile.mkstemp() + try: + with open(path, "wb") as out: + out.write(ints2octs((2, 1, 12))) + + with open(path, "rb") as source: + values = list(decoder.StreamingDecoder(source)) + + assert values == [12] + finally: + os.remove(path) + + def testMoreObjects(self): + _, path = tempfile.mkstemp() + try: + with open(path, "wb") as out: + out.write(ints2octs((2, 1, 12, 35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0))) + + with open(path, "rb") as source: + values = list(decoder.StreamingDecoder(source)) + + assert values == [12, (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1)] + + finally: + os.remove(path) + + def testInvalidFileContent(self): + _, path = tempfile.mkstemp() + try: + with open(path, "wb") as out: + out.write(ints2octs((2, 1, 12, 35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0, 7))) + + with open(path, "rb") as source: + list(decoder.StreamingDecoder(source)) + + except error.EndOfStreamError: + pass + + finally: + os.remove(path) + + +class BytesIOTestCase(BaseTestCase): + def testRead(self): + source = ints2octs((2, 1, 12, 35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0)) + stream = io.BytesIO(source) + values = list(decoder.StreamingDecoder(stream)) + assert values == [12, (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1)] + + +class UnicodeTestCase(BaseTestCase): + def testFail(self): + # This ensures that unicode objects in Python 2 & str objects in Python 3.7 cannot be parsed. + source = ints2octs((2, 1, 12, 35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0)).decode("latin-1") + try: + next(decoder.StreamingDecoder(source)) + + except error.UnsupportedSubstrateError: + pass + + else: + assert False, 'Tolerated parsing broken unicode strings' + + +class RestartableDecoderTestCase(BaseTestCase): + + class NonBlockingStream(io.BytesIO): + block = False + + def read(self, size=-1): + self.block = not self.block + if self.block: + return # this is what non-blocking streams sometimes do + + return io.BytesIO.read(self, size) + + def setUp(self): + BaseTestCase.setUp(self) + + self.s = univ.SequenceOf(componentType=univ.OctetString()) + self.s.setComponentByPosition(0, univ.OctetString('quick brown')) + source = ints2octs( + (48, 26, + 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, + 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + self.stream = self.NonBlockingStream(source) + + def testPartialReadingFromNonBlockingStream(self): + iterator = iter(decoder.StreamingDecoder(self.stream, asn1Spec=self.s)) + + res = next(iterator) + + assert isinstance(res, error.SubstrateUnderrunError) + assert 'asn1Object' not in res.context + + res = next(iterator) + + assert isinstance(res, error.SubstrateUnderrunError) + assert 'asn1Object' not in res.context + + res = next(iterator) + + assert isinstance(res, error.SubstrateUnderrunError) + assert 'asn1Object' in res.context + assert isinstance(res.context['asn1Object'], univ.SequenceOf) + assert res.context['asn1Object'].isValue + assert len(res.context['asn1Object']) == 0 + + res = next(iterator) + + assert isinstance(res, error.SubstrateUnderrunError) + assert 'asn1Object' in res.context + assert isinstance(res.context['asn1Object'], univ.SequenceOf) + assert res.context['asn1Object'].isValue + assert len(res.context['asn1Object']) == 0 + + res = next(iterator) + + assert isinstance(res, error.SubstrateUnderrunError) + assert 'asn1Object' in res.context + assert isinstance(res.context['asn1Object'], univ.SequenceOf) + assert res.context['asn1Object'].isValue + assert len(res.context['asn1Object']) == 0 + + res = next(iterator) + + assert isinstance(res, error.SubstrateUnderrunError) + assert 'asn1Object' in res.context + assert isinstance(res.context['asn1Object'], univ.SequenceOf) + assert res.context['asn1Object'].isValue + assert len(res.context['asn1Object']) == 1 + + res = next(iterator) + + assert isinstance(res, error.SubstrateUnderrunError) + assert 'asn1Object' in res.context + assert isinstance(res.context['asn1Object'], univ.SequenceOf) + assert res.context['asn1Object'].isValue + assert len(res.context['asn1Object']) == 1 + + res = next(iterator) + + assert isinstance(res, error.SubstrateUnderrunError) + assert 'asn1Object' in res.context + assert isinstance(res.context['asn1Object'], univ.SequenceOf) + assert res.context['asn1Object'].isValue + assert len(res.context['asn1Object']) == 1 + + res = next(iterator) + + assert isinstance(res, univ.SequenceOf) + assert res.isValue + assert len(res) == 2 + + try: + next(iterator) + + except StopIteration: + pass + + else: + assert False, 'End of stream not raised' + + +class CompressedFilesTestCase(BaseTestCase): + def testGzip(self): + _, path = tempfile.mkstemp(suffix=".gz") + try: + with gzip.open(path, "wb") as out: + out.write(ints2octs((2, 1, 12, 35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0))) + + with gzip.open(path, "rb") as source: + values = list(decoder.StreamingDecoder(source)) + + assert values == [12, (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1)] + + finally: + os.remove(path) + + def testZipfile(self): + # File from ZIP archive is a good example of non-seekable stream in Python 2.7 + # In Python 3.7, it is a seekable stream. + _, path = tempfile.mkstemp(suffix=".zip") + try: + with zipfile.ZipFile(path, "w") as myzip: + myzip.writestr("data", ints2octs((2, 1, 12, 35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0))) + + with zipfile.ZipFile(path, "r") as myzip: + with myzip.open("data", "r") as source: + values = list(decoder.StreamingDecoder(source)) + assert values == [12, (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1)] + finally: + os.remove(path) + + def testZipfileMany(self): + _, path = tempfile.mkstemp(suffix=".zip") + try: + with zipfile.ZipFile(path, "w") as myzip: + #for i in range(100): + myzip.writestr("data", ints2octs((2, 1, 12, 35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0)) * 1000) + + with zipfile.ZipFile(path, "r") as myzip: + with myzip.open("data", "r") as source: + values = list(decoder.StreamingDecoder(source)) + assert values == [12, (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1)] * 1000 + finally: + os.remove(path) + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/ber/test_encoder.py b/contrib/python/pyasn1/py3/tests/codec/ber/test_encoder.py new file mode 100644 index 0000000000..7701348d06 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/ber/test_encoder.py @@ -0,0 +1,1497 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import tag +from pyasn1.type import namedtype +from pyasn1.type import opentype +from pyasn1.type import univ +from pyasn1.type import char +from pyasn1.codec.ber import encoder +from pyasn1.compat.octets import ints2octs +from pyasn1.error import PyAsn1Error + + +class LargeTagEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.o = univ.Integer().subtype( + value=1, explicitTag=tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 0xdeadbeaf) + ) + + def testEncoder(self): + assert encoder.encode(self.o) == ints2octs((127, 141, 245, 182, 253, 47, 3, 2, 1, 1)) + + +class IntegerEncoderTestCase(BaseTestCase): + def testPosInt(self): + assert encoder.encode(univ.Integer(12)) == ints2octs((2, 1, 12)) + + def testNegInt(self): + assert encoder.encode(univ.Integer(-12)) == ints2octs((2, 1, 244)) + + def testZero(self): + assert encoder.encode(univ.Integer(0)) == ints2octs((2, 1, 0)) + + def testCompactZero(self): + encoder.IntegerEncoder.supportCompactZero = True + substrate = encoder.encode(univ.Integer(0)) + encoder.IntegerEncoder.supportCompactZero = False + assert substrate == ints2octs((2, 0)) + + def testMinusOne(self): + assert encoder.encode(univ.Integer(-1)) == ints2octs((2, 1, 255)) + + def testPosLong(self): + assert encoder.encode( + univ.Integer(0xffffffffffffffff) + ) == ints2octs((2, 9, 0, 255, 255, 255, 255, 255, 255, 255, 255)) + + def testNegLong(self): + assert encoder.encode( + univ.Integer(-0xffffffffffffffff) + ) == ints2octs((2, 9, 255, 0, 0, 0, 0, 0, 0, 0, 1)) + + +class IntegerEncoderWithSchemaTestCase(BaseTestCase): + def testPosInt(self): + assert encoder.encode(12, asn1Spec=univ.Integer()) == ints2octs((2, 1, 12)) + + def testNegInt(self): + assert encoder.encode(-12, asn1Spec=univ.Integer()) == ints2octs((2, 1, 244)) + + def testZero(self): + assert encoder.encode(0, asn1Spec=univ.Integer()) == ints2octs((2, 1, 0)) + + def testPosLong(self): + assert encoder.encode( + 0xffffffffffffffff, asn1Spec=univ.Integer() + ) == ints2octs((2, 9, 0, 255, 255, 255, 255, 255, 255, 255, 255)) + + +class BooleanEncoderTestCase(BaseTestCase): + def testTrue(self): + assert encoder.encode(univ.Boolean(1)) == ints2octs((1, 1, 1)) + + def testFalse(self): + assert encoder.encode(univ.Boolean(0)) == ints2octs((1, 1, 0)) + + +class BooleanEncoderWithSchemaTestCase(BaseTestCase): + def testTrue(self): + assert encoder.encode(True, asn1Spec=univ.Boolean()) == ints2octs((1, 1, 1)) + + def testFalse(self): + assert encoder.encode(False, asn1Spec=univ.Boolean()) == ints2octs((1, 1, 0)) + + +class BitStringEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.b = univ.BitString((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1)) + + def testDefMode(self): + assert encoder.encode(self.b) == ints2octs((3, 3, 1, 169, 138)) + + def testIndefMode(self): + assert encoder.encode( + self.b, defMode=False + ) == ints2octs((3, 3, 1, 169, 138)) + + def testDefModeChunked(self): + assert encoder.encode( + self.b, maxChunkSize=1 + ) == ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138)) + + def testIndefModeChunked(self): + assert encoder.encode( + self.b, defMode=False, maxChunkSize=1 + ) == ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0)) + + def testEmptyValue(self): + assert encoder.encode(univ.BitString([])) == ints2octs((3, 1, 0)) + + +class BitStringEncoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.b = (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1) + self.s = univ.BitString() + + def testDefMode(self): + assert encoder.encode(self.b, asn1Spec=self.s) == ints2octs((3, 3, 1, 169, 138)) + + def testIndefMode(self): + assert encoder.encode( + self.b, asn1Spec=self.s, defMode=False + ) == ints2octs((3, 3, 1, 169, 138)) + + def testDefModeChunked(self): + assert encoder.encode( + self.b, asn1Spec=self.s, maxChunkSize=1 + ) == ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138)) + + def testIndefModeChunked(self): + assert encoder.encode( + self.b, asn1Spec=self.s, defMode=False, maxChunkSize=1 + ) == ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0)) + + def testEmptyValue(self): + assert encoder.encode([], asn1Spec=self.s) == ints2octs((3, 1, 0)) + + +class OctetStringEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.o = univ.OctetString('Quick brown fox') + + def testDefMode(self): + assert encoder.encode(self.o) == ints2octs( + (4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)) + + def testIndefMode(self): + assert encoder.encode( + self.o, defMode=False + ) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)) + + def testDefModeChunked(self): + assert encoder.encode( + self.o, maxChunkSize=4 + ) == ints2octs((36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, + 110, 32, 4, 3, 102, 111, 120)) + + def testIndefModeChunked(self): + assert encoder.encode( + self.o, defMode=False, maxChunkSize=4 + ) == ints2octs((36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, + 32, 4, 3, 102, 111, 120, 0, 0)) + + +class OctetStringEncoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.OctetString() + self.o = 'Quick brown fox' + + def testDefMode(self): + assert encoder.encode(self.o, asn1Spec=self.s) == ints2octs( + (4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)) + + def testIndefMode(self): + assert encoder.encode( + self.o, asn1Spec=self.s, defMode=False + ) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)) + + def testDefModeChunked(self): + assert encoder.encode( + self.o, asn1Spec=self.s, maxChunkSize=4 + ) == ints2octs((36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, + 110, 32, 4, 3, 102, 111, 120)) + + def testIndefModeChunked(self): + assert encoder.encode( + self.o, asn1Spec=self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, + 32, 4, 3, 102, 111, 120, 0, 0)) + + +class ExpTaggedOctetStringEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.o = univ.OctetString().subtype( + value='Quick brown fox', + explicitTag=tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 5) + ) + + def testDefMode(self): + assert encoder.encode(self.o) == ints2octs( + (101, 17, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)) + + def testIndefMode(self): + assert encoder.encode( + self.o, defMode=False + ) == ints2octs((101, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0)) + + def testDefModeChunked(self): + assert encoder.encode( + self.o, defMode=True, maxChunkSize=4 + ) == ints2octs((101, 25, 36, 23, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, + 102, 111, 120)) + + def testIndefModeChunked(self): + assert encoder.encode( + self.o, defMode=False, maxChunkSize=4 + ) == ints2octs((101, 128, 36, 128, 4, 4, 81, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 4, 111, 119, 110, 32, 4, 3, 102, 111, 120, 0, 0, 0, 0)) + + +class NullEncoderTestCase(BaseTestCase): + def testNull(self): + assert encoder.encode(univ.Null('')) == ints2octs((5, 0)) + + +class NullEncoderWithSchemaTestCase(BaseTestCase): + def testNull(self): + assert encoder.encode(None, univ.Null()) == ints2octs((5, 0)) + + +class ObjectIdentifierEncoderTestCase(BaseTestCase): + def testOne(self): + assert encoder.encode( + univ.ObjectIdentifier((1, 3, 6, 0, 0xffffe)) + ) == ints2octs((6, 6, 43, 6, 0, 191, 255, 126)) + + def testEdge1(self): + assert encoder.encode( + univ.ObjectIdentifier((0, 39)) + ) == ints2octs((6, 1, 39)) + + def testEdge2(self): + assert encoder.encode( + univ.ObjectIdentifier((1, 39)) + ) == ints2octs((6, 1, 79)) + + def testEdge3(self): + # 01111111 + assert encoder.encode( + univ.ObjectIdentifier((2, 40)) + ) == ints2octs((6, 1, 120)) + + def testEdge4(self): + # 10010000|10000000|10000000|10000000|01001111 + assert encoder.encode( + univ.ObjectIdentifier((2, 0xffffffff)) + ) == ints2octs((6, 5, 0x90, 0x80, 0x80, 0x80, 0x4F)) + + def testEdge5(self): + # 01111111 + assert encoder.encode( + univ.ObjectIdentifier((2, 47)) + ) == ints2octs((6, 1, 0x7F)) + + def testEdge6(self): + # 10000001|00000000 + assert encoder.encode( + univ.ObjectIdentifier((2, 48)) + ) == ints2octs((6, 2, 0x81, 0x00)) + + def testEdge7(self): + # 10000001|00110100|00000003 + assert encoder.encode( + univ.ObjectIdentifier((2, 100, 3)) + ) == ints2octs((6, 3, 0x81, 0x34, 0x03)) + + def testEdge8(self): + # 10000101|00000000 + assert encoder.encode( + univ.ObjectIdentifier((2, 560)) + ) == ints2octs((6, 2, 133, 0)) + + def testEdge9(self): + # 10001000|10000100|10000111|0000010 + assert encoder.encode( + univ.ObjectIdentifier((2, 16843570)) + ) == ints2octs((6, 4, 0x88, 0x84, 0x87, 0x02)) + + def testEdgeA(self): + assert encoder.encode( + univ.ObjectIdentifier((2, 5)) + ) == ints2octs((6, 1, 85)) + + def testImpossible1(self): + try: + encoder.encode(univ.ObjectIdentifier((3, 1, 2))) + except PyAsn1Error: + pass + else: + assert 0, 'impossible leading arc tolerated' + + def testImpossible2(self): + try: + encoder.encode(univ.ObjectIdentifier((0,))) + except PyAsn1Error: + pass + else: + assert 0, 'single arc OID tolerated' + + def testImpossible3(self): + try: + encoder.encode(univ.ObjectIdentifier((0, 40))) + except PyAsn1Error: + pass + else: + assert 0, 'second arc overflow tolerated' + + def testImpossible4(self): + try: + encoder.encode(univ.ObjectIdentifier((1, 40))) + except PyAsn1Error: + pass + else: + assert 0, 'second arc overflow tolerated' + + def testLarge1(self): + assert encoder.encode( + univ.ObjectIdentifier((2, 18446744073709551535184467440737095)) + ) == ints2octs((0x06, 0x11, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB, + 0xE2, 0xB7, 0x17)) + + def testLarge2(self): + assert encoder.encode( + univ.ObjectIdentifier((2, 999, 18446744073709551535184467440737095)) + ) == ints2octs((0x06, 0x13, 0x88, 0x37, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, + 0xB8, 0xCB, 0xE2, 0xB6, 0x47)) + + +class ObjectIdentifierWithSchemaEncoderTestCase(BaseTestCase): + def testOne(self): + assert encoder.encode( + (1, 3, 6, 0, 0xffffe), asn1Spec=univ.ObjectIdentifier() + ) == ints2octs((6, 6, 43, 6, 0, 191, 255, 126)) + + +class RealEncoderTestCase(BaseTestCase): + def testChar(self): + assert encoder.encode( + univ.Real((123, 10, 11)) + ) == ints2octs((9, 7, 3, 49, 50, 51, 69, 49, 49)) + + def testBin1(self): + assert encoder.encode( # default binEncBase = 2 + univ.Real((0.5, 2, 0)) # check encbase = 2 and exponent = -1 + ) == ints2octs((9, 3, 128, 255, 1)) + + def testBin2(self): + r = univ.Real((3.25, 2, 0)) + r.binEncBase = 8 # change binEncBase only for this instance of Real + assert encoder.encode( + r # check encbase = 8 + ) == ints2octs((9, 3, 148, 255, 13)) + + def testBin3(self): + # change binEncBase in the RealEncoder instance => for all further Real + binEncBase, encoder.TYPE_MAP[univ.Real.typeId].binEncBase = encoder.TYPE_MAP[univ.Real.typeId].binEncBase, 16 + assert encoder.encode( + univ.Real((0.00390625, 2, 0)) # check encbase = 16 + ) == ints2octs((9, 3, 160, 254, 1)) + encoder.TYPE_MAP[univ.Real.typeId].binEncBase = binEncBase + + def testBin4(self): + # choose binEncBase automatically for all further Real (testBin[4-7]) + binEncBase, encoder.TYPE_MAP[univ.Real.typeId].binEncBase = encoder.TYPE_MAP[univ.Real.typeId].binEncBase, None + assert encoder.encode( + univ.Real((1, 2, 0)) # check exponent = 0 + ) == ints2octs((9, 3, 128, 0, 1)) + encoder.TYPE_MAP[univ.Real.typeId].binEncBase = binEncBase + + def testBin5(self): + assert encoder.encode( + univ.Real((3, 2, -1020)) # case of 2 octs for exponent and + # negative exponent and abs(exponent) is + # all 1's and fills the whole octet(s) + ) == ints2octs((9, 4, 129, 252, 4, 3)) + + def testBin6(self): + assert encoder.encode( + univ.Real((1, 2, 262140)) # case of 3 octs for exponent and + # check that first 9 bits for exponent + # are not all 1's + ) == ints2octs((9, 5, 130, 3, 255, 252, 1)) + + def testBin7(self): + assert encoder.encode( + univ.Real((-1, 2, 76354972)) # case of >3 octs for exponent and + # mantissa < 0 + ) == ints2octs((9, 7, 195, 4, 4, 141, 21, 156, 1)) + + def testPlusInf(self): + assert encoder.encode(univ.Real('inf')) == ints2octs((9, 1, 64)) + + def testMinusInf(self): + assert encoder.encode(univ.Real('-inf')) == ints2octs((9, 1, 65)) + + def testZero(self): + assert encoder.encode(univ.Real(0)) == ints2octs((9, 0)) + + +class RealEncoderWithSchemaTestCase(BaseTestCase): + def testChar(self): + assert encoder.encode( + (123, 10, 11), asn1Spec=univ.Real() + ) == ints2octs((9, 7, 3, 49, 50, 51, 69, 49, 49)) + + +class UniversalStringEncoderTestCase(BaseTestCase): + def testEncoding(self): + assert encoder.encode(char.UniversalString(sys.version_info[0] >= 3 and 'abc' or unicode('abc'))) == ints2octs( + (28, 12, 0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99)), 'Incorrect encoding' + + +class UniversalStringEncoderWithSchemaTestCase(BaseTestCase): + def testEncoding(self): + assert encoder.encode( + sys.version_info[0] >= 3 and 'abc' or unicode('abc'), asn1Spec=char.UniversalString() + ) == ints2octs((28, 12, 0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99)), 'Incorrect encoding' + + +class BMPStringEncoderTestCase(BaseTestCase): + def testEncoding(self): + assert encoder.encode(char.BMPString(sys.version_info[0] >= 3 and 'abc' or unicode('abc'))) == ints2octs( + (30, 6, 0, 97, 0, 98, 0, 99)), 'Incorrect encoding' + + +class BMPStringEncoderWithSchemaTestCase(BaseTestCase): + def testEncoding(self): + assert encoder.encode( + sys.version_info[0] >= 3 and 'abc' or unicode('abc'), asn1Spec=char.BMPString() + ) == ints2octs((30, 6, 0, 97, 0, 98, 0, 99)), 'Incorrect encoding' + + +class UTF8StringEncoderTestCase(BaseTestCase): + def testEncoding(self): + assert encoder.encode(char.UTF8String(sys.version_info[0] >= 3 and 'abc' or unicode('abc'))) == ints2octs( + (12, 3, 97, 98, 99)), 'Incorrect encoding' + + +class UTF8StringEncoderWithSchemaTestCase(BaseTestCase): + def testEncoding(self): + assert encoder.encode( + sys.version_info[0] >= 3 and 'abc' or unicode('abc'), asn1Spec=char.UTF8String() + ) == ints2octs((12, 3, 97, 98, 99)), 'Incorrect encoding' + + +class SequenceOfEncoderTestCase(BaseTestCase): + def testEmpty(self): + s = univ.SequenceOf() + s.clear() + assert encoder.encode(s) == ints2octs((48, 0)) + + def testDefMode(self): + s = univ.SequenceOf() + s.setComponentByPosition(0, univ.OctetString('quick brown')) + assert encoder.encode(s) == ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + def testIndefMode(self): + s = univ.SequenceOf() + s.setComponentByPosition(0, univ.OctetString('quick brown')) + assert encoder.encode( + s, defMode=False + ) == ints2octs((48, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)) + + def testDefModeChunked(self): + s = univ.SequenceOf() + s.setComponentByPosition(0, univ.OctetString('quick brown')) + assert encoder.encode( + s, defMode=True, maxChunkSize=4 + ) == ints2octs((48, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)) + + def testIndefModeChunked(self): + s = univ.SequenceOf() + s.setComponentByPosition(0, univ.OctetString('quick brown')) + assert encoder.encode( + s, defMode=False, maxChunkSize=4 + ) == ints2octs((48, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)) + + +class SequenceOfEncoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.SequenceOf(componentType=univ.OctetString()) + self.v = ['quick brown'] + + def testEmpty(self): + assert encoder.encode([], asn1Spec=self.s) == ints2octs((48, 0)) + + def testDefMode(self): + assert encoder.encode( + self.v, asn1Spec=self.s + ) == ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + def testIndefMode(self): + assert encoder.encode( + self.v, asn1Spec=self.s, defMode=False + ) == ints2octs((48, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)) + + def testDefModeChunked(self): + assert encoder.encode( + self.v, asn1Spec=self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((48, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)) + + def testIndefModeChunked(self): + assert encoder.encode( + self.v, asn1Spec=self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((48, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)) + + +class SequenceOfEncoderWithComponentsSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.SequenceOf(componentType=univ.OctetString()) + + def __init(self): + self.s.clear() + self.s.setComponentByPosition(0, 'quick brown') + + def testDefMode(self): + self.__init() + assert encoder.encode(self.s) == ints2octs((48, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + def testIndefMode(self): + self.__init() + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((48, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)) + + def testDefModeChunked(self): + self.__init() + assert encoder.encode( + self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((48, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)) + + def testIndefModeChunked(self): + self.__init() + assert encoder.encode( + self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((48, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)) + + +class SetOfEncoderTestCase(BaseTestCase): + def testEmpty(self): + s = univ.SetOf() + s.clear() + assert encoder.encode(s) == ints2octs((49, 0)) + + def testDefMode(self): + s = univ.SetOf() + s.setComponentByPosition(0, univ.OctetString('quick brown')) + assert encoder.encode(s) == ints2octs((49, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + def testIndefMode(self): + s = univ.SetOf() + s.setComponentByPosition(0, univ.OctetString('quick brown')) + assert encoder.encode( + s, defMode=False + ) == ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)) + + def testDefModeChunked(self): + s = univ.SetOf() + s.setComponentByPosition(0, univ.OctetString('quick brown')) + assert encoder.encode( + s, defMode=True, maxChunkSize=4 + ) == ints2octs((49, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)) + + def testIndefModeChunked(self): + s = univ.SetOf() + s.setComponentByPosition(0, univ.OctetString('quick brown')) + assert encoder.encode( + s, defMode=False, maxChunkSize=4 + ) == ints2octs((49, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)) + + +class SetOfEncoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.SetOf(componentType=univ.OctetString()) + self.v = ['quick brown'] + + def testEmpty(self): + s = univ.SetOf() + assert encoder.encode([], asn1Spec=self.s) == ints2octs((49, 0)) + + def testDefMode(self): + assert encoder.encode( + self.v, asn1Spec=self.s + ) == ints2octs((49, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + def testIndefMode(self): + assert encoder.encode( + self.v, asn1Spec=self.s, defMode=False + ) == ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)) + + def testDefModeChunked(self): + assert encoder.encode( + self.v, asn1Spec=self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((49, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)) + + def testIndefModeChunked(self): + assert encoder.encode( + self.v, asn1Spec=self.s, defMode=False, maxChunkSize=4 + ) == ints2octs( + (49, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)) + + +class SetOfEncoderWithComponentsSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.SetOf(componentType=univ.OctetString()) + + def __init(self): + self.s.clear() + self.s.setComponentByPosition(0, 'quick brown') + + def testDefMode(self): + self.__init() + assert encoder.encode(self.s) == ints2octs((49, 13, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + def testIndefMode(self): + self.__init() + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)) + + def testDefModeChunked(self): + self.__init() + assert encoder.encode( + self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((49, 19, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)) + + def testIndefModeChunked(self): + self.__init() + assert encoder.encode( + self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((49, 128, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)) + + +class SequenceEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Sequence() + self.s.setComponentByPosition(0, univ.Null('')) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testDefMode(self): + assert encoder.encode(self.s) == ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)) + + def testIndefMode(self): + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0)) + + def testDefModeChunked(self): + assert encoder.encode( + self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)) + + def testIndefModeChunked(self): + assert encoder.encode( + self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)) + + +class SequenceEncoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null()), + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + ) + ) + self.v = { + 'place-holder': None, + 'first-name': 'quick brown', + 'age': 1 + } + + def testEmpty(self): + try: + assert encoder.encode({}, asn1Spec=self.s) + + except PyAsn1Error: + pass + + else: + assert False, 'empty bare sequence tolerated' + + def testDefMode(self): + assert encoder.encode( + self.v, asn1Spec=self.s + ) == ints2octs((48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)) + + def testIndefMode(self): + assert encoder.encode( + self.v, asn1Spec=self.s, defMode=False + ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0)) + + def testDefModeChunked(self): + assert encoder.encode( + self.v, asn1Spec=self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)) + + def testIndefModeChunked(self): + assert encoder.encode( + self.v, asn1Spec=self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)) + + +class SequenceEncoderWithUntaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any(), openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1] = univ.Integer(12) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 5, 2, 1, 1, 49, 50) + ) + + def testEncodeOpenTypeChoiceTwo(self): + self.s.clear() + + self.s[0] = 2 + self.s[1] = univ.OctetString('quick brown') + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 14, 2, 1, 2, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110) + ) + + def testEncodeOpenTypeUnknownId(self): + self.s.clear() + + self.s[0] = 2 + self.s[1] = univ.ObjectIdentifier('1.3.6') + + try: + encoder.encode(self.s, asn1Spec=self.s) + + except PyAsn1Error: + assert False, 'incompatible open type tolerated' + + def testEncodeOpenTypeIncompatibleType(self): + self.s.clear() + + self.s[0] = 2 + self.s[1] = univ.ObjectIdentifier('1.3.6') + + try: + encoder.encode(self.s, asn1Spec=self.s) + + except PyAsn1Error: + assert False, 'incompatible open type tolerated' + + +class SequenceEncoderWithImplicitlyTaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1] = univ.Integer(12) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 9, 2, 1, 1, 131, 4, 131, 2, 49, 50) + ) + + +class SequenceEncoderWithExplicitlyTaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1] = univ.Integer(12) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 9, 2, 1, 1, 163, 4, 163, 2, 49, 50) + ) + + +class SequenceEncoderWithUntaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.SetOf( + componentType=univ.Any()), openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1].append(univ.Integer(12)) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 7, 2, 1, 1, 49, 2, 49, 50) + ) + + def testEncodeOpenTypeChoiceTwo(self): + self.s.clear() + + self.s[0] = 2 + self.s[1].append(univ.OctetString('quick brown')) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 16, 2, 1, 2, 49, 11, 113, 117, 105, 99, 107, 32, 98, 114, + 111, 119, 110) + ) + + def testEncodeOpenTypeUnknownId(self): + self.s.clear() + + self.s[0] = 2 + self.s[1].append(univ.ObjectIdentifier('1.3.6')) + + try: + encoder.encode(self.s, asn1Spec=self.s) + + except PyAsn1Error: + assert False, 'incompatible open type tolerated' + + def testEncodeOpenTypeIncompatibleType(self): + self.s.clear() + + self.s[0] = 2 + self.s[1].append(univ.ObjectIdentifier('1.3.6')) + + try: + encoder.encode(self.s, asn1Spec=self.s) + + except PyAsn1Error: + assert False, 'incompatible open type tolerated' + + +class SequenceEncoderWithImplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.SetOf( + componentType=univ.Any().subtype( + implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 3))), + openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1].append(univ.Integer(12)) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 11, 2, 1, 1, 49, 6, 131, 4, 131, 2, 49, 50) + ) + + +class SequenceEncoderWithExplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.SetOf( + componentType=univ.Any().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))), + openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1].append(univ.Integer(12)) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 11, 2, 1, 1, 49, 6, 163, 4, 163, 2, 49, 50) + ) + + +class SequenceEncoderWithComponentsSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null()), + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + ) + ) + + def __init(self): + self.s.clear() + self.s.setComponentByPosition(0, '') + + def __initWithOptional(self): + self.s.clear() + self.s.setComponentByPosition(0, '') + self.s.setComponentByPosition(1, 'quick brown') + + def __initWithDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, '') + self.s.setComponentByPosition(2, 1) + + def __initWithOptionalAndDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null('')) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testDefMode(self): + self.__init() + assert encoder.encode(self.s) == ints2octs((48, 2, 5, 0)) + + def testIndefMode(self): + self.__init() + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((48, 128, 5, 0, 0, 0)) + + def testDefModeChunked(self): + self.__init() + assert encoder.encode( + self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((48, 2, 5, 0)) + + def testIndefModeChunked(self): + self.__init() + assert encoder.encode( + self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((48, 128, 5, 0, 0, 0)) + + def testWithOptionalDefMode(self): + self.__initWithOptional() + assert encoder.encode(self.s) == ints2octs( + (48, 15, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + def testWithOptionalIndefMode(self): + self.__initWithOptional() + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)) + + def testWithOptionalDefModeChunked(self): + self.__initWithOptional() + assert encoder.encode( + self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((48, 21, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)) + + def testWithOptionalIndefModeChunked(self): + self.__initWithOptional() + assert encoder.encode( + self.s, defMode=False, maxChunkSize=4 + ) == ints2octs( + (48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)) + + def testWithDefaultedDefMode(self): + self.__initWithDefaulted() + assert encoder.encode(self.s) == ints2octs((48, 5, 5, 0, 2, 1, 1)) + + def testWithDefaultedIndefMode(self): + self.__initWithDefaulted() + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0)) + + def testWithDefaultedDefModeChunked(self): + self.__initWithDefaulted() + assert encoder.encode( + self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((48, 5, 5, 0, 2, 1, 1)) + + def testWithDefaultedIndefModeChunked(self): + self.__initWithDefaulted() + assert encoder.encode( + self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0)) + + def testWithOptionalAndDefaultedDefMode(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode(self.s) == ints2octs( + (48, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)) + + def testWithOptionalAndDefaultedIndefMode(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0)) + + def testWithOptionalAndDefaultedDefModeChunked(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode( + self.s, defMode=True, maxChunkSize=4 + ) == ints2octs( + (48, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)) + + def testWithOptionalAndDefaultedIndefModeChunked(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode( + self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((48, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, + 0, 2, 1, 1, 0, 0)) + + +class ExpTaggedSequenceEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('number', univ.Integer()), + ) + ) + + s = s.subtype( + explicitTag=tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 5) + ) + + s[0] = 12 + + self.s = s + + def testDefMode(self): + assert encoder.encode(self.s) == ints2octs((101, 5, 48, 3, 2, 1, 12)) + + def testIndefMode(self): + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((101, 128, 48, 128, 2, 1, 12, 0, 0, 0, 0)) + + +class ExpTaggedSequenceComponentEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('number', univ.Boolean().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + ) + ) + + self.s[0] = True + + def testDefMode(self): + assert encoder.encode(self.s) == ints2octs((48, 5, 160, 3, 1, 1, 1)) + + def testIndefMode(self): + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((48, 128, 160, 3, 1, 1, 1, 0, 0, 0, 0)) + + +class SetEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Set() + self.s.setComponentByPosition(0, univ.Null('')) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testDefMode(self): + assert encoder.encode(self.s) == ints2octs((49, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)) + + def testIndefMode(self): + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((49, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0)) + + def testDefModeChunked(self): + assert encoder.encode( + self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((49, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)) + + def testIndefModeChunked(self): + assert encoder.encode( + self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)) + + +class SetEncoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Set( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null()), + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + ) + ) + self.v = { + 'place-holder': None, + 'first-name': 'quick brown', + 'age': 1 + } + + def testEmpty(self): + try: + assert encoder.encode({}, asn1Spec=self.s) + + except PyAsn1Error: + pass + + else: + assert False, 'empty bare SET tolerated' + + def testDefMode(self): + assert encoder.encode( + self.v, asn1Spec=self.s + ) == ints2octs((49, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)) + + def testIndefMode(self): + assert encoder.encode( + self.v, asn1Spec=self.s, defMode=False + ) == ints2octs((49, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0)) + + def testDefModeChunked(self): + assert encoder.encode( + self.v, asn1Spec=self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((49, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)) + + def testIndefModeChunked(self): + assert encoder.encode( + self.v, asn1Spec=self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)) + + +class SetEncoderWithComponentsSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Set( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null()), + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + ) + ) + + def __init(self): + self.s.clear() + self.s.setComponentByPosition(0, '') + + def __initWithOptional(self): + self.s.clear() + self.s.setComponentByPosition(0, '') + self.s.setComponentByPosition(1, 'quick brown') + + def __initWithDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, '') + self.s.setComponentByPosition(2, 1) + + def __initWithOptionalAndDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null('')) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testDefMode(self): + self.__init() + assert encoder.encode(self.s) == ints2octs((49, 2, 5, 0)) + + def testIndefMode(self): + self.__init() + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((49, 128, 5, 0, 0, 0)) + + def testDefModeChunked(self): + self.__init() + assert encoder.encode( + self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((49, 2, 5, 0)) + + def testIndefModeChunked(self): + self.__init() + assert encoder.encode( + self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((49, 128, 5, 0, 0, 0)) + + def testWithOptionalDefMode(self): + self.__initWithOptional() + assert encoder.encode(self.s) == ints2octs( + (49, 15, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + def testWithOptionalIndefMode(self): + self.__initWithOptional() + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((49, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)) + + def testWithOptionalDefModeChunked(self): + self.__initWithOptional() + assert encoder.encode( + self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((49, 21, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)) + + def testWithOptionalIndefModeChunked(self): + self.__initWithOptional() + assert encoder.encode( + self.s, defMode=False, maxChunkSize=4 + ) == ints2octs( + (49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 0, 0)) + + def testWithDefaultedDefMode(self): + self.__initWithDefaulted() + assert encoder.encode(self.s) == ints2octs((49, 5, 5, 0, 2, 1, 1)) + + def testWithDefaultedIndefMode(self): + self.__initWithDefaulted() + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((49, 128, 5, 0, 2, 1, 1, 0, 0)) + + def testWithDefaultedDefModeChunked(self): + self.__initWithDefaulted() + assert encoder.encode( + self.s, defMode=True, maxChunkSize=4 + ) == ints2octs((49, 5, 5, 0, 2, 1, 1)) + + def testWithDefaultedIndefModeChunked(self): + self.__initWithDefaulted() + assert encoder.encode( + self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((49, 128, 5, 0, 2, 1, 1, 0, 0)) + + def testWithOptionalAndDefaultedDefMode(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode(self.s) == ints2octs( + (49, 18, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1)) + + def testWithOptionalAndDefaultedIndefMode(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode( + self.s, defMode=False + ) == ints2octs((49, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0)) + + def testWithOptionalAndDefaultedDefModeChunked(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode( + self.s, defMode=True, maxChunkSize=4 + ) == ints2octs( + (49, 24, 5, 0, 36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 2, 1, 1)) + + def testWithOptionalAndDefaultedIndefModeChunked(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode( + self.s, defMode=False, maxChunkSize=4 + ) == ints2octs((49, 128, 5, 0, 36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0, 2, 1, 1, 0, 0)) + + +class ChoiceEncoderTestCase(BaseTestCase): + + def testEmpty(self): + s = univ.Choice() + try: + encoder.encode(s) + except PyAsn1Error: + pass + else: + assert 0, 'encoded unset choice' + + def testDefModeOptionOne(self): + s = univ.Choice() + s.setComponentByPosition(0, univ.Null('')) + assert encoder.encode(s) == ints2octs((5, 0)) + + def testDefModeOptionTwo(self): + s = univ.Choice() + s.setComponentByPosition(0, univ.OctetString('quick brown')) + assert encoder.encode(s) == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + def testIndefMode(self): + s = univ.Choice() + s.setComponentByPosition(0, univ.OctetString('quick brown')) + assert encoder.encode( + s, defMode=False + ) == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + def testDefModeChunked(self): + s = univ.Choice() + s.setComponentByPosition(0, univ.OctetString('quick brown')) + assert encoder.encode( + s, defMode=True, maxChunkSize=4 + ) == ints2octs((36, 17, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110)) + + def testIndefModeChunked(self): + s = univ.Choice() + s.setComponentByPosition(0, univ.OctetString('quick brown')) + assert encoder.encode( + s, defMode=False, maxChunkSize=4 + ) == ints2octs((36, 128, 4, 4, 113, 117, 105, 99, 4, 4, 107, 32, 98, 114, 4, 3, 111, 119, 110, 0, 0)) + + +class ChoiceEncoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null('')), + namedtype.NamedType('number', univ.Integer(0)), + namedtype.NamedType('string', univ.OctetString()) + ) + ) + self.v = { + 'place-holder': None + } + + def testFilled(self): + assert encoder.encode( + self.v, asn1Spec=self.s + ) == ints2octs((5, 0)) + + +class ChoiceEncoderWithComponentsSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null('')), + namedtype.NamedType('number', univ.Integer(0)), + namedtype.NamedType('string', univ.OctetString()) + ) + ) + + def testEmpty(self): + try: + encoder.encode(self.s) + except PyAsn1Error: + pass + else: + assert 0, 'encoded unset choice' + + def testFilled(self): + self.s.setComponentByPosition(0, univ.Null('')) + assert encoder.encode(self.s) == ints2octs((5, 0)) + + def testTagged(self): + s = self.s.subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4) + ) + s.setComponentByPosition(0, univ.Null('')) + assert encoder.encode(s) == ints2octs((164, 2, 5, 0)) + + def testUndefLength(self): + self.s.setComponentByPosition(2, univ.OctetString('abcdefgh')) + assert encoder.encode(self.s, defMode=False, maxChunkSize=3) == ints2octs( + (36, 128, 4, 3, 97, 98, 99, 4, 3, 100, 101, 102, 4, 2, 103, 104, 0, 0)) + + def testTaggedUndefLength(self): + s = self.s.subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4) + ) + s.setComponentByPosition(2, univ.OctetString('abcdefgh')) + assert encoder.encode(s, defMode=False, maxChunkSize=3) == ints2octs( + (164, 128, 36, 128, 4, 3, 97, 98, 99, 4, 3, 100, 101, 102, 4, 2, 103, 104, 0, 0, 0, 0)) + + +class AnyEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Any(encoder.encode(univ.OctetString('fox'))) + + def testUntagged(self): + assert encoder.encode(self.s) == ints2octs((4, 3, 102, 111, 120)) + + def testTaggedEx(self): + s = self.s.subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4) + ) + assert encoder.encode(s) == ints2octs((164, 5, 4, 3, 102, 111, 120)) + + def testTaggedIm(self): + s = self.s.subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4) + ) + assert encoder.encode(s) == ints2octs((132, 5, 4, 3, 102, 111, 120)) + + +class AnyEncoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Any() + self.v = encoder.encode(univ.OctetString('fox')) + + def testUntagged(self): + assert encoder.encode(self.v, asn1Spec=self.s) == ints2octs((4, 3, 102, 111, 120)) + + def testTaggedEx(self): + s = self.s.subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4) + ) + assert encoder.encode(self.v, asn1Spec=s) == ints2octs((164, 5, 4, 3, 102, 111, 120)) + + def testTaggedIm(self): + s = self.s.subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4) + ) + assert encoder.encode(self.v, asn1Spec=s) == ints2octs((132, 5, 4, 3, 102, 111, 120)) + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/cer/__init__.py b/contrib/python/pyasn1/py3/tests/codec/cer/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/cer/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/tests/codec/cer/__main__.py b/contrib/python/pyasn1/py3/tests/codec/cer/__main__.py new file mode 100644 index 0000000000..122d7275b3 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/cer/__main__.py @@ -0,0 +1,16 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import unittest + +suite = unittest.TestLoader().loadTestsFromNames( + ['tests.codec.cer.test_encoder.suite', + 'tests.codec.cer.test_decoder.suite'] +) + + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/cer/test_decoder.py b/contrib/python/pyasn1/py3/tests/codec/cer/test_decoder.py new file mode 100644 index 0000000000..fddd36bb57 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/cer/test_decoder.py @@ -0,0 +1,370 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import tag +from pyasn1.type import namedtype +from pyasn1.type import opentype +from pyasn1.type import univ +from pyasn1.codec.cer import decoder +from pyasn1.compat.octets import ints2octs, str2octs, null +from pyasn1.error import PyAsn1Error + + +class BooleanDecoderTestCase(BaseTestCase): + def testTrue(self): + assert decoder.decode(ints2octs((1, 1, 255))) == (1, null) + + def testFalse(self): + assert decoder.decode(ints2octs((1, 1, 0))) == (0, null) + + def testEmpty(self): + try: + decoder.decode(ints2octs((1, 0))) + except PyAsn1Error: + pass + + def testOverflow(self): + try: + decoder.decode(ints2octs((1, 2, 0, 0))) + except PyAsn1Error: + pass + + +class BitStringDecoderTestCase(BaseTestCase): + def testShortMode(self): + assert decoder.decode( + ints2octs((3, 3, 6, 170, 128)) + ) == (((1, 0) * 5), null) + + def testLongMode(self): + assert decoder.decode( + ints2octs((3, 127, 6) + (170,) * 125 + (128,)) + ) == (((1, 0) * 501), null) + + # TODO: test failures on short chunked and long unchunked substrate samples + + +class OctetStringDecoderTestCase(BaseTestCase): + def testShortMode(self): + assert decoder.decode( + ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)), + ) == (str2octs('Quick brown fox'), null) + + def testLongMode(self): + assert decoder.decode( + ints2octs((36, 128, 4, 130, 3, 232) + (81,) * 1000 + (4, 1, 81, 0, 0)) + ) == (str2octs('Q' * 1001), null) + + # TODO: test failures on short chunked and long unchunked substrate samples + + +class SequenceDecoderWithUntaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any(), openType=openType) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 1, 2, 1, 12, 0, 0)), + asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1] == 12 + + def testDecodeOpenTypesChoiceTwo(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, + 114, 111, 119, 110, 0, 0)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 2 + assert s[1] == univ.OctetString('quick brown') + + def testDecodeOpenTypesUnknownType(self): + try: + s, r = decoder.decode( + ints2octs((48, 128, 6, 1, 1, 2, 1, 12, 0, 0)), asn1Spec=self.s, + decodeOpenTypes=True + ) + + except PyAsn1Error: + pass + + else: + assert False, 'unknown open type tolerated' + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 3, 6, 1, 12, 0, 0)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1] == univ.OctetString(hexValue='06010c') + + def testDontDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 1, 2, 1, 12, 0, 0)), asn1Spec=self.s + ) + assert not r + assert s[0] == 1 + assert s[1] == ints2octs((2, 1, 12)) + + def testDontDecodeOpenTypesChoiceTwo(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, + 114, 111, 119, 110, 0, 0)), asn1Spec=self.s + ) + assert not r + assert s[0] == 2 + assert s[1] == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + +class SequenceDecoderWithImplicitlyTaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType( + 'blob', univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType + ) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 1, 163, 128, 2, 1, 12, 0, 0, 0, 0)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1] == 12 + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 3, 163, 128, 2, 1, 12, 0, 0, 0, 0)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1] == univ.OctetString(hexValue='02010C') + + +class SequenceDecoderWithExplicitlyTaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType( + 'blob', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType + ) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 1, 163, 128, 2, 1, 12, 0, 0, 0, 0)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1] == 12 + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 3, 163, 128, 2, 1, 12, 0, 0, 0, 0)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1] == univ.OctetString(hexValue='02010C') + + +class SequenceDecoderWithUntaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.SetOf(componentType=univ.Any()), + openType=openType) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 1, 49, 128, 2, 1, 12, 0, 0, 0, 0)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1][0] == 12 + + def testDecodeOpenTypesChoiceTwo(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 2, 49, 128, 4, 11, 113, 117, 105, 99, + 107, 32, 98, 114, 111, 119, 110, 0, 0, 0, 0)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 2 + assert s[1][0] == univ.OctetString('quick brown') + + def testDecodeOpenTypesUnknownType(self): + try: + s, r = decoder.decode( + ints2octs((48, 128, 6, 1, 1, 49, 128, 2, 1, 12, 0, 0, 0, 0)), + asn1Spec=self.s, decodeOpenTypes=True + ) + + except PyAsn1Error: + pass + + else: + assert False, 'unknown open type tolerated' + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 3, 49, 128, 2, 1, 12, 0, 0, 0, 0)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1][0] == univ.OctetString(hexValue='02010c') + + def testDontDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 1, 49, 128, 2, 1, 12, 0, 0, 0, 0)), + asn1Spec=self.s + ) + assert not r + assert s[0] == 1 + assert s[1][0] == ints2octs((2, 1, 12)) + + def testDontDecodeOpenTypesChoiceTwo(self): + s, r = decoder.decode( + ints2octs((48, 128, 2, 1, 2, 49, 128, 4, 11, 113, 117, 105, 99, 107, 32, + 98, 114, 111, 119, 110, 0, 0, 0, 0)), asn1Spec=self.s + ) + assert not r + assert s[0] == 2 + assert s[1][0] == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, + 111, 119, 110)) + + +class SequenceDecoderWithImplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType( + 'blob', univ.SetOf( + componentType=univ.Any().subtype( + implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 3))), + openType=openType + ) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 10, 2, 1, 1, 49, 5, 131, 3, 2, 1, 12)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1][0] == 12 + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 10, 2, 1, 3, 49, 5, 131, 3, 2, 1, 12)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1][0] == univ.OctetString(hexValue='02010C') + + +class SequenceDecoderWithExplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType( + 'blob', univ.SetOf( + componentType=univ.Any().subtype( + explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 3))), + openType=openType + ) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 10, 2, 1, 1, 49, 5, 131, 3, 2, 1, 12)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1][0] == 12 + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs( (48, 10, 2, 1, 3, 49, 5, 131, 3, 2, 1, 12)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1][0] == univ.OctetString(hexValue='02010C') + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/cer/test_encoder.py b/contrib/python/pyasn1/py3/tests/codec/cer/test_encoder.py new file mode 100644 index 0000000000..680f720c3f --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/cer/test_encoder.py @@ -0,0 +1,956 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import tag +from pyasn1.type import namedtype +from pyasn1.type import opentype +from pyasn1.type import univ +from pyasn1.type import useful +from pyasn1.codec.cer import encoder +from pyasn1.compat.octets import ints2octs +from pyasn1.error import PyAsn1Error + + +class BooleanEncoderTestCase(BaseTestCase): + def testTrue(self): + assert encoder.encode(univ.Boolean(1)) == ints2octs((1, 1, 255)) + + def testFalse(self): + assert encoder.encode(univ.Boolean(0)) == ints2octs((1, 1, 0)) + + +class BitStringEncoderTestCase(BaseTestCase): + def testShortMode(self): + assert encoder.encode( + univ.BitString((1, 0) * 5) + ) == ints2octs((3, 3, 6, 170, 128)) + + def testLongMode(self): + assert encoder.encode(univ.BitString((1, 0) * 501)) == ints2octs((3, 127, 6) + (170,) * 125 + (128,)) + + +class OctetStringEncoderTestCase(BaseTestCase): + def testShortMode(self): + assert encoder.encode( + univ.OctetString('Quick brown fox') + ) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)) + + def testLongMode(self): + assert encoder.encode( + univ.OctetString('Q' * 1001) + ) == ints2octs((36, 128, 4, 130, 3, 232) + (81,) * 1000 + (4, 1, 81, 0, 0)) + + +class GeneralizedTimeEncoderTestCase(BaseTestCase): + # def testExtraZeroInSeconds(self): + # try: + # assert encoder.encode( + # useful.GeneralizedTime('20150501120112.10Z') + # ) + # except PyAsn1Error: + # pass + # else: + # assert 0, 'Meaningless trailing zero in fraction part tolerated' + + def testLocalTimezone(self): + try: + assert encoder.encode( + useful.GeneralizedTime('20150501120112.1+0200') + ) + except PyAsn1Error: + pass + else: + assert 0, 'Local timezone tolerated' + + def testMissingTimezone(self): + try: + assert encoder.encode( + useful.GeneralizedTime('20150501120112.1') + ) + except PyAsn1Error: + pass + else: + assert 0, 'Missing timezone tolerated' + + def testDecimalCommaPoint(self): + try: + assert encoder.encode( + useful.GeneralizedTime('20150501120112,1Z') + ) + except PyAsn1Error: + pass + else: + assert 0, 'Decimal comma tolerated' + + def testWithSubseconds(self): + assert encoder.encode( + useful.GeneralizedTime('20170801120112.59Z') + ) == ints2octs((24, 18, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 46, 53, 57, 90)) + + def testWithSubsecondsWithZeros(self): + assert encoder.encode( + useful.GeneralizedTime('20170801120112.099Z') + ) == ints2octs((24, 18, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 46, 57, 57, 90)) + + def testWithSubsecondsMax(self): + assert encoder.encode( + useful.GeneralizedTime('20170801120112.999Z') + ) == ints2octs((24, 19, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 46, 57, 57, 57, 90)) + + def testWithSubsecondsMin(self): + assert encoder.encode( + useful.GeneralizedTime('20170801120112.000Z') + ) == ints2octs((24, 15, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 90)) + + def testWithSubsecondsDanglingDot(self): + assert encoder.encode( + useful.GeneralizedTime('20170801120112.Z') + ) == ints2octs((24, 15, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 90)) + + def testWithSeconds(self): + assert encoder.encode( + useful.GeneralizedTime('20170801120112Z') + ) == ints2octs((24, 15, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 90)) + + def testWithMinutes(self): + assert encoder.encode( + useful.GeneralizedTime('201708011201Z') + ) == ints2octs((24, 13, 50, 48, 49, 55, 48, 56, 48, 49, 49, 50, 48, 49, 90)) + + +class UTCTimeEncoderTestCase(BaseTestCase): + def testFractionOfSecond(self): + try: + assert encoder.encode( + useful.UTCTime('150501120112.10Z') + ) + except PyAsn1Error: + pass + else: + assert 0, 'Decimal point tolerated' + + def testMissingTimezone(self): + try: + assert encoder.encode( + useful.UTCTime('150501120112') + ) == ints2octs((23, 13, 49, 53, 48, 53, 48, 49, 49, 50, 48, 49, 49, 50, 90)) + except PyAsn1Error: + pass + else: + assert 0, 'Missing timezone tolerated' + + def testLocalTimezone(self): + try: + assert encoder.encode( + useful.UTCTime('150501120112+0200') + ) + except PyAsn1Error: + pass + else: + assert 0, 'Local timezone tolerated' + + def testWithSeconds(self): + assert encoder.encode( + useful.UTCTime('990801120112Z') + ) == ints2octs((23, 13, 57, 57, 48, 56, 48, 49, 49, 50, 48, 49, 49, 50, 90)) + + def testWithMinutes(self): + assert encoder.encode( + useful.UTCTime('9908011201Z') + ) == ints2octs((23, 11, 57, 57, 48, 56, 48, 49, 49, 50, 48, 49, 90)) + + +class SequenceOfEncoderTestCase(BaseTestCase): + def testEmpty(self): + s = univ.SequenceOf() + s.clear() + assert encoder.encode(s) == ints2octs((48, 128, 0, 0)) + + def testDefMode1(self): + s = univ.SequenceOf() + s.append(univ.OctetString('a')) + s.append(univ.OctetString('ab')) + assert encoder.encode(s) == ints2octs((48, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0)) + + def testDefMode2(self): + s = univ.SequenceOf() + s.append(univ.OctetString('ab')) + s.append(univ.OctetString('a')) + assert encoder.encode(s) == ints2octs((48, 128, 4, 2, 97, 98, 4, 1, 97, 0, 0)) + + def testDefMode3(self): + s = univ.SequenceOf() + s.append(univ.OctetString('b')) + s.append(univ.OctetString('a')) + assert encoder.encode(s) == ints2octs((48, 128, 4, 1, 98, 4, 1, 97, 0, 0)) + + def testDefMode4(self): + s = univ.SequenceOf() + s.append(univ.OctetString('a')) + s.append(univ.OctetString('b')) + assert encoder.encode(s) == ints2octs((48, 128, 4, 1, 97, 4, 1, 98, 0, 0)) + + +class SequenceOfEncoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.SequenceOf(componentType=univ.OctetString()) + + def testEmpty(self): + self.s.clear() + assert encoder.encode(self.s) == ints2octs((48, 128, 0, 0)) + + def testIndefMode1(self): + self.s.clear() + self.s.append('a') + self.s.append('ab') + assert encoder.encode(self.s) == ints2octs((48, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0)) + + def testIndefMode2(self): + self.s.clear() + self.s.append('ab') + self.s.append('a') + assert encoder.encode(self.s) == ints2octs((48, 128, 4, 2, 97, 98, 4, 1, 97, 0, 0)) + + def testIndefMode3(self): + self.s.clear() + self.s.append('b') + self.s.append('a') + assert encoder.encode(self.s) == ints2octs((48, 128, 4, 1, 98, 4, 1, 97, 0, 0)) + + def testIndefMode4(self): + self.s.clear() + self.s.append('a') + self.s.append('b') + assert encoder.encode(self.s) == ints2octs((48, 128, 4, 1, 97, 4, 1, 98, 0, 0)) + + +class SetOfEncoderTestCase(BaseTestCase): + def testEmpty(self): + s = univ.SetOf() + s.clear() + assert encoder.encode(s) == ints2octs((49, 128, 0, 0)) + + def testDefMode1(self): + s = univ.SetOf() + s.append(univ.OctetString('a')) + s.append(univ.OctetString('ab')) + assert encoder.encode(s) == ints2octs((49, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0)) + + def testDefMode2(self): + s = univ.SetOf() + s.append(univ.OctetString('ab')) + s.append(univ.OctetString('a')) + assert encoder.encode(s) == ints2octs((49, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0)) + + def testDefMode3(self): + s = univ.SetOf() + s.append(univ.OctetString('b')) + s.append(univ.OctetString('a')) + assert encoder.encode(s) == ints2octs((49, 128, 4, 1, 97, 4, 1, 98, 0, 0)) + + def testDefMode4(self): + s = univ.SetOf() + s.append(univ.OctetString('a')) + s.append(univ.OctetString('b')) + assert encoder.encode(s) == ints2octs((49, 128, 4, 1, 97, 4, 1, 98, 0, 0)) + + +class SetOfEncoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.SetOf(componentType=univ.OctetString()) + + def testEmpty(self): + self.s.clear() + assert encoder.encode(self.s) == ints2octs((49, 128, 0, 0)) + + def testIndefMode1(self): + self.s.clear() + self.s.append('a') + self.s.append('ab') + + assert encoder.encode(self.s) == ints2octs((49, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0)) + + def testIndefMode2(self): + self.s.clear() + self.s.append('ab') + self.s.append('a') + + assert encoder.encode(self.s) == ints2octs((49, 128, 4, 1, 97, 4, 2, 97, 98, 0, 0)) + + def testIndefMode3(self): + self.s.clear() + self.s.append('b') + self.s.append('a') + + assert encoder.encode(self.s) == ints2octs((49, 128, 4, 1, 97, 4, 1, 98, 0, 0)) + + def testIndefMode4(self): + self.s.clear() + self.s.append('a') + self.s.append('b') + + assert encoder.encode(self.s) == ints2octs((49, 128, 4, 1, 97, 4, 1, 98, 0, 0)) + + +class SetEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Set() + self.s.setComponentByPosition(0, univ.Null('')) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testIndefMode(self): + assert encoder.encode(self.s) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0)) + + def testWithOptionalIndefMode(self): + assert encoder.encode( + self.s + ) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0)) + + def testWithDefaultedIndefMode(self): + assert encoder.encode( + self.s + ) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0)) + + def testWithOptionalAndDefaultedIndefMode(self): + assert encoder.encode( + self.s + ) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0)) + + +class SetEncoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Set(componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null('')), + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)) + )) + + def __init(self): + self.s.clear() + self.s.setComponentByPosition(0) + + def __initWithOptional(self): + self.s.clear() + self.s.setComponentByPosition(0) + self.s.setComponentByPosition(1, 'quick brown') + + def __initWithDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0) + self.s.setComponentByPosition(2, 1) + + def __initWithOptionalAndDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null('')) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testIndefMode(self): + self.__init() + assert encoder.encode(self.s) == ints2octs((49, 128, 5, 0, 0, 0)) + + def testWithOptionalIndefMode(self): + self.__initWithOptional() + assert encoder.encode( + self.s + ) == ints2octs((49, 128, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0)) + + def testWithDefaultedIndefMode(self): + self.__initWithDefaulted() + assert encoder.encode( + self.s + ) == ints2octs((49, 128, 2, 1, 1, 5, 0, 0, 0)) + + def testWithOptionalAndDefaultedIndefMode(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode( + self.s + ) == ints2octs((49, 128, 2, 1, 1, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 5, 0, 0, 0)) + + +class SetEncoderWithChoiceWithSchemaEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + c = univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('actual', univ.Boolean(0)) + )) + self.s = univ.Set(componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null('')), + namedtype.NamedType('status', c) + )) + + def testIndefMode(self): + self.s.setComponentByPosition(0) + self.s.setComponentByName('status') + self.s.getComponentByName('status').setComponentByPosition(0, 1) + assert encoder.encode(self.s) == ints2octs((49, 128, 1, 1, 255, 5, 0, 0, 0)) + + +class SetEncoderWithTaggedChoiceEncoderTestCase(BaseTestCase): + + def testWithUntaggedChoice(self): + + c = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('premium', univ.Boolean()) + ) + ) + + s = univ.Set( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('customer', c) + ) + ) + + s.setComponentByName('name', 'A') + s.getComponentByName('customer').setComponentByName('premium', True) + + assert encoder.encode(s) == ints2octs((49, 128, 1, 1, 255, 4, 1, 65, 0, 0)) + + def testWithTaggedChoice(self): + + c = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('premium', univ.Boolean()) + ) + ).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 7)) + + s = univ.Set( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('customer', c) + ) + ) + + s.setComponentByName('name', 'A') + s.getComponentByName('customer').setComponentByName('premium', True) + + assert encoder.encode(s) == ints2octs((49, 128, 4, 1, 65, 167, 128, 1, 1, 255, 0, 0, 0, 0)) + + +class SequenceEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Sequence() + self.s.setComponentByPosition(0, univ.Null('')) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testIndefMode(self): + assert encoder.encode(self.s) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0)) + + def testWithOptionalIndefMode(self): + assert encoder.encode( + self.s + ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0)) + + def testWithDefaultedIndefMode(self): + assert encoder.encode( + self.s + ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0)) + + def testWithOptionalAndDefaultedIndefMode(self): + assert encoder.encode( + self.s + ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0)) + + +class SequenceEncoderWithSchemaTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null('')), + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)) + ) + ) + + def __init(self): + self.s.clear() + self.s.setComponentByPosition(0) + + def __initWithOptional(self): + self.s.clear() + self.s.setComponentByPosition(0) + self.s.setComponentByPosition(1, 'quick brown') + + def __initWithDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0) + self.s.setComponentByPosition(2, 1) + + def __initWithOptionalAndDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null('')) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testIndefMode(self): + self.__init() + assert encoder.encode(self.s) == ints2octs((48, 128, 5, 0, 0, 0)) + + def testWithOptionalIndefMode(self): + self.__initWithOptional() + assert encoder.encode( + self.s + ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 0, 0)) + + def testWithDefaultedIndefMode(self): + self.__initWithDefaulted() + assert encoder.encode( + self.s + ) == ints2octs((48, 128, 5, 0, 2, 1, 1, 0, 0)) + + def testWithOptionalAndDefaultedIndefMode(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode( + self.s + ) == ints2octs((48, 128, 5, 0, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 2, 1, 1, 0, 0)) + + +class SequenceEncoderWithUntaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any(), openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1] = univ.Integer(12) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 128, 2, 1, 1, 49, 50, 0, 0) + ) + + def testEncodeOpenTypeChoiceTwo(self): + self.s.clear() + + self.s[0] = 2 + self.s[1] = univ.OctetString('quick brown') + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 128, 2, 1, 2, 113, 117, 105, 99, 107, 32, 98, 114, + 111, 119, 110, 0, 0) + ) + + def testEncodeOpenTypeUnknownId(self): + self.s.clear() + + self.s[0] = 2 + self.s[1] = univ.ObjectIdentifier('1.3.6') + + try: + encoder.encode(self.s, asn1Spec=self.s) + + except PyAsn1Error: + assert False, 'incompatible open type tolerated' + + def testEncodeOpenTypeIncompatibleType(self): + self.s.clear() + + self.s[0] = 2 + self.s[1] = univ.ObjectIdentifier('1.3.6') + + try: + encoder.encode(self.s, asn1Spec=self.s) + + except PyAsn1Error: + assert False, 'incompatible open type tolerated' + + +class SequenceEncoderWithImplicitlyTaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1] = univ.Integer(12) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 128, 2, 1, 1, 163, 128, 163, 128, 49, 50, 0, 0, 0, 0, 0, 0) + ) + + +class SequenceEncoderWithExplicitlyTaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1] = univ.Integer(12) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 128, 2, 1, 1, 163, 128, 163, 128, 49, 50, 0, 0, 0, 0, 0, 0) + ) + + +class SequenceEncoderWithUntaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.SetOf( + componentType=univ.Any()), openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1].append(univ.Integer(12)) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 128, 2, 1, 1, 49, 128, 49, 50, 0, 0, 0, 0) + ) + + def testEncodeOpenTypeChoiceTwo(self): + self.s.clear() + + self.s[0] = 2 + self.s[1].append(univ.OctetString('quick brown')) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 128, 2, 1, 2, 49, 128, 113, 117, 105, 99, 107, 32, 98, 114, + 111, 119, 110, 0, 0, 0, 0) + ) + + def testEncodeOpenTypeUnknownId(self): + self.s.clear() + + self.s[0] = 2 + self.s[1].append(univ.ObjectIdentifier('1.3.6')) + + try: + encoder.encode(self.s, asn1Spec=self.s) + + except PyAsn1Error: + assert False, 'incompatible open type tolerated' + + def testEncodeOpenTypeIncompatibleType(self): + self.s.clear() + + self.s[0] = 2 + self.s[1].append(univ.ObjectIdentifier('1.3.6')) + + try: + encoder.encode(self.s, asn1Spec=self.s) + + except PyAsn1Error: + assert False, 'incompatible open type tolerated' + + +class SequenceEncoderWithImplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.SetOf( + componentType=univ.Any().subtype( + implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 3))), + openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1].append(univ.Integer(12)) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 128, 2, 1, 1, 49, 128, 163, 128, 163, 128, 49, 50, 0, 0, + 0, 0, 0, 0, 0, 0) + ) + + +class SequenceEncoderWithExplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.SetOf( + componentType=univ.Any().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))), + openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1].append(univ.Integer(12)) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 128, 2, 1, 1, 49, 128, 163, 128, 163, 128, 49, 50, 0, 0, + 0, 0, 0, 0, 0, 0) + ) + + +class NestedOptionalSequenceEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + inner = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + ) + ) + + outerWithOptional = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('inner', inner), + ) + ) + + outerWithDefault = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.DefaultedNamedType('inner', inner), + ) + ) + + self.s1 = outerWithOptional + self.s2 = outerWithDefault + + def __initOptionalWithDefaultAndOptional(self): + self.s1.clear() + self.s1[0][0] = 'test' + self.s1[0][1] = 123 + return self.s1 + + def __initOptionalWithDefault(self): + self.s1.clear() + self.s1[0][1] = 123 + return self.s1 + + def __initOptionalWithOptional(self): + self.s1.clear() + self.s1[0][0] = 'test' + return self.s1 + + def __initOptional(self): + self.s1.clear() + return self.s1 + + def __initDefaultWithDefaultAndOptional(self): + self.s2.clear() + self.s2[0][0] = 'test' + self.s2[0][1] = 123 + return self.s2 + + def __initDefaultWithDefault(self): + self.s2.clear() + self.s2[0][0] = 'test' + return self.s2 + + def __initDefaultWithOptional(self): + self.s2.clear() + self.s2[0][1] = 123 + return self.s2 + + def testOptionalWithDefaultAndOptional(self): + s = self.__initOptionalWithDefaultAndOptional() + assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 2, 1, 123, 0, 0, 0, 0)) + + def testOptionalWithDefault(self): + s = self.__initOptionalWithDefault() + assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 2, 1, 123, 0, 0, 0, 0)) + + def testOptionalWithOptional(self): + s = self.__initOptionalWithOptional() + assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0)) + + def testOptional(self): + s = self.__initOptional() + assert encoder.encode(s) == ints2octs((48, 128, 0, 0)) + + def testDefaultWithDefaultAndOptional(self): + s = self.__initDefaultWithDefaultAndOptional() + assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 2, 1, 123, 0, 0, 0, 0)) + + def testDefaultWithDefault(self): + s = self.__initDefaultWithDefault() + assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0)) + + def testDefaultWithOptional(self): + s = self.__initDefaultWithOptional() + assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 2, 1, 123, 0, 0, 0, 0)) + + +class NestedOptionalChoiceEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + layer3 = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + ) + ) + + layer2 = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('inner', layer3), + namedtype.NamedType('first-name', univ.OctetString()) + ) + ) + + layer1 = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('inner', layer2), + ) + ) + + self.s = layer1 + + def __initOptionalWithDefaultAndOptional(self): + self.s.clear() + self.s[0][0][0] = 'test' + self.s[0][0][1] = 123 + return self.s + + def __initOptionalWithDefault(self): + self.s.clear() + self.s[0][0][1] = 123 + return self.s + + def __initOptionalWithOptional(self): + self.s.clear() + self.s[0][0][0] = 'test' + return self.s + + def __initOptional(self): + self.s.clear() + return self.s + + def testOptionalWithDefaultAndOptional(self): + s = self.__initOptionalWithDefaultAndOptional() + assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 2, 1, 123, 0, 0, 0, 0)) + + def testOptionalWithDefault(self): + s = self.__initOptionalWithDefault() + assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 2, 1, 123, 0, 0, 0, 0)) + + def testOptionalWithOptional(self): + s = self.__initOptionalWithOptional() + assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0)) + + def testOptional(self): + s = self.__initOptional() + assert encoder.encode(s) == ints2octs((48, 128, 0, 0)) + + +class NestedOptionalSequenceOfEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + layer2 = univ.SequenceOf( + componentType=univ.OctetString() + ) + + layer1 = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('inner', layer2), + ) + ) + + self.s = layer1 + + def __initOptionalWithValue(self): + self.s.clear() + self.s[0][0] = 'test' + return self.s + + def __initOptional(self): + self.s.clear() + return self.s + + def testOptionalWithValue(self): + s = self.__initOptionalWithValue() + assert encoder.encode(s) == ints2octs((48, 128, 48, 128, 4, 4, 116, 101, 115, 116, 0, 0, 0, 0)) + + def testOptional(self): + s = self.__initOptional() + assert encoder.encode(s) == ints2octs((48, 128, 0, 0)) + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/der/__init__.py b/contrib/python/pyasn1/py3/tests/codec/der/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/der/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/tests/codec/der/__main__.py b/contrib/python/pyasn1/py3/tests/codec/der/__main__.py new file mode 100644 index 0000000000..23560098fd --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/der/__main__.py @@ -0,0 +1,16 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import unittest + +suite = unittest.TestLoader().loadTestsFromNames( + ['tests.codec.der.test_encoder.suite', + 'tests.codec.der.test_decoder.suite'] +) + + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/der/test_decoder.py b/contrib/python/pyasn1/py3/tests/codec/der/test_decoder.py new file mode 100644 index 0000000000..5f61408317 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/der/test_decoder.py @@ -0,0 +1,368 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import tag +from pyasn1.type import namedtype +from pyasn1.type import opentype +from pyasn1.type import univ +from pyasn1.codec.der import decoder +from pyasn1.compat.octets import ints2octs, null +from pyasn1.error import PyAsn1Error + + +class BitStringDecoderTestCase(BaseTestCase): + def testShortMode(self): + assert decoder.decode( + ints2octs((3, 127, 6) + (170,) * 125 + (128,)) + ) == (((1, 0) * 501), null) + + def testIndefMode(self): + try: + decoder.decode( + ints2octs((35, 128, 3, 2, 0, 169, 3, 2, 1, 138, 0, 0)) + ) + except PyAsn1Error: + pass + else: + assert 0, 'indefinite length encoding tolerated' + + def testDefModeChunked(self): + try: + assert decoder.decode( + ints2octs((35, 8, 3, 2, 0, 169, 3, 2, 1, 138)) + ) + except PyAsn1Error: + pass + else: + assert 0, 'chunked encoding tolerated' + + +class OctetStringDecoderTestCase(BaseTestCase): + def testShortMode(self): + assert decoder.decode( + '\004\017Quick brown fox'.encode() + ) == ('Quick brown fox'.encode(), ''.encode()) + + def testIndefMode(self): + try: + decoder.decode( + ints2octs((36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0)) + ) + except PyAsn1Error: + pass + else: + assert 0, 'indefinite length encoding tolerated' + + def testChunkedMode(self): + try: + decoder.decode( + ints2octs((36, 23, 4, 2, 81, 117, 4, 2, 105, 99, 4, 2, 107, 32, 4, 2, 98, 114, 4, 2, 111, 119, 4, 1, 110)) + ) + except PyAsn1Error: + pass + else: + assert 0, 'chunked encoding tolerated' + + +class SequenceDecoderWithUntaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any(), openType=openType) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 6, 2, 1, 1, 2, 1, 12)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1] == 12 + + def testDecodeOpenTypesChoiceTwo(self): + s, r = decoder.decode( + ints2octs((48, 16, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 2 + assert s[1] == univ.OctetString('quick brown') + + def testDecodeOpenTypesUnknownType(self): + try: + s, r = decoder.decode( + ints2octs((48, 6, 2, 1, 2, 6, 1, 39)), asn1Spec=self.s, + decodeOpenTypes=True + ) + + except PyAsn1Error: + pass + + else: + assert False, 'unknown open type tolerated' + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 6, 2, 1, 3, 6, 1, 39)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1] == univ.OctetString(hexValue='060127') + + def testDontDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 6, 2, 1, 1, 2, 1, 12)), asn1Spec=self.s + ) + assert not r + assert s[0] == 1 + assert s[1] == ints2octs((2, 1, 12)) + + def testDontDecodeOpenTypesChoiceTwo(self): + s, r = decoder.decode( + ints2octs((48, 16, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s + ) + assert not r + assert s[0] == 2 + assert s[1] == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)) + + +class SequenceDecoderWithImplicitlyTaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType( + 'blob', univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType + ) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 1, 131, 3, 2, 1, 12)), asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1] == 12 + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 3, 131, 3, 2, 1, 12)), asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1] == univ.OctetString(hexValue='02010C') + + +class SequenceDecoderWithExplicitlyTaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType( + 'blob', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType + ) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 1, 163, 3, 2, 1, 12)), asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1] == 12 + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 3, 163, 3, 2, 1, 12)), asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1] == univ.OctetString(hexValue='02010C') + + +class SequenceDecoderWithUnaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.SetOf(componentType=univ.Any()), + openType=openType) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 1, 49, 3, 2, 1, 12)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1][0] == 12 + + def testDecodeOpenTypesChoiceTwo(self): + s, r = decoder.decode( + ints2octs((48, 18, 2, 1, 2, 49, 13, 4, 11, 113, 117, 105, 99, + 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 2 + assert s[1][0] == univ.OctetString('quick brown') + + def testDecodeOpenTypesUnknownType(self): + try: + s, r = decoder.decode( + ints2octs((48, 6, 2, 1, 2, 6, 1, 39)), asn1Spec=self.s, + decodeOpenTypes=True + ) + + except PyAsn1Error: + pass + + else: + assert False, 'unknown open type tolerated' + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 3, 49, 3, 2, 1, 12)), asn1Spec=self.s, + decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1][0] == univ.OctetString(hexValue='02010c') + + def testDontDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 8, 2, 1, 1, 49, 3, 2, 1, 12)), asn1Spec=self.s + ) + assert not r + assert s[0] == 1 + assert s[1][0] == ints2octs((2, 1, 12)) + + def testDontDecodeOpenTypesChoiceTwo(self): + s, r = decoder.decode( + ints2octs((48, 18, 2, 1, 2, 49, 13, 4, 11, 113, 117, 105, 99, + 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s + ) + assert not r + assert s[0] == 2 + assert s[1][0] == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, + 111, 119, 110)) + + +class SequenceDecoderWithImplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType( + 'blob', univ.SetOf( + componentType=univ.Any().subtype( + implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 3))), + openType=openType + ) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 10, 2, 1, 1, 49, 5, 131, 3, 2, 1, 12)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1][0] == 12 + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs((48, 10, 2, 1, 3, 49, 5, 131, 3, 2, 1, 12)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1][0] == univ.OctetString(hexValue='02010C') + + +class SequenceDecoderWithExplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType( + 'blob', univ.SetOf( + componentType=univ.Any().subtype( + explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 3))), + openType=openType + ) + ) + ) + + def testDecodeOpenTypesChoiceOne(self): + s, r = decoder.decode( + ints2octs((48, 10, 2, 1, 1, 49, 5, 131, 3, 2, 1, 12)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 1 + assert s[1][0] == 12 + + def testDecodeOpenTypesUnknownId(self): + s, r = decoder.decode( + ints2octs( (48, 10, 2, 1, 3, 49, 5, 131, 3, 2, 1, 12)), + asn1Spec=self.s, decodeOpenTypes=True + ) + assert not r + assert s[0] == 3 + assert s[1][0] == univ.OctetString(hexValue='02010C') + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/der/test_encoder.py b/contrib/python/pyasn1/py3/tests/codec/der/test_encoder.py new file mode 100644 index 0000000000..6500396115 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/der/test_encoder.py @@ -0,0 +1,665 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import tag +from pyasn1.type import namedtype +from pyasn1.type import opentype +from pyasn1.type import univ +from pyasn1.codec.der import encoder +from pyasn1.compat.octets import ints2octs + + +class OctetStringEncoderTestCase(BaseTestCase): + def testDefModeShort(self): + assert encoder.encode( + univ.OctetString('Quick brown fox') + ) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)) + + def testDefModeLong(self): + assert encoder.encode( + univ.OctetString('Q' * 10000) + ) == ints2octs((4, 130, 39, 16) + (81,) * 10000) + + +class BitStringEncoderTestCase(BaseTestCase): + def testDefModeShort(self): + assert encoder.encode( + univ.BitString((1,)) + ) == ints2octs((3, 2, 7, 128)) + + def testDefModeLong(self): + assert encoder.encode( + univ.BitString((1,) * 80000) + ) == ints2octs((3, 130, 39, 17, 0) + (255,) * 10000) + + +class SetOfEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.s = univ.SetOf(componentType=univ.OctetString()) + + def testDefMode1(self): + self.s.clear() + self.s.append('a') + self.s.append('ab') + + assert encoder.encode(self.s) == ints2octs((49, 7, 4, 1, 97, 4, 2, 97, 98)) + + def testDefMode2(self): + self.s.clear() + self.s.append('ab') + self.s.append('a') + + assert encoder.encode(self.s) == ints2octs((49, 7, 4, 1, 97, 4, 2, 97, 98)) + + def testDefMode3(self): + self.s.clear() + self.s.append('b') + self.s.append('a') + + assert encoder.encode(self.s) == ints2octs((49, 6, 4, 1, 97, 4, 1, 98)) + + def testDefMode4(self): + self.s.clear() + self.s.append('a') + self.s.append('b') + + assert encoder.encode(self.s) == ints2octs((49, 6, 4, 1, 97, 4, 1, 98)) + + +class SetWithAlternatingChoiceEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + c = univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('amount', univ.Boolean())) + ) + + self.s = univ.Set(componentType=namedtype.NamedTypes( + namedtype.NamedType('value', univ.Integer(5)), + namedtype.NamedType('status', c)) + ) + + def testComponentsOrdering1(self): + self.s.setComponentByName('status') + self.s.getComponentByName('status').setComponentByPosition(0, 'A') + assert encoder.encode(self.s) == ints2octs((49, 6, 2, 1, 5, 4, 1, 65)) + + def testComponentsOrdering2(self): + self.s.setComponentByName('status') + self.s.getComponentByName('status').setComponentByPosition(1, True) + assert encoder.encode(self.s) == ints2octs((49, 6, 1, 1, 255, 2, 1, 5)) + + +class SetWithTaggedChoiceEncoderTestCase(BaseTestCase): + + def testWithUntaggedChoice(self): + + c = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('premium', univ.Boolean()) + ) + ) + + s = univ.Set( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('customer', c) + ) + ) + + s.setComponentByName('name', 'A') + s.getComponentByName('customer').setComponentByName('premium', True) + + assert encoder.encode(s) == ints2octs((49, 6, 1, 1, 255, 4, 1, 65)) + + def testWithTaggedChoice(self): + + c = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('premium', univ.Boolean()) + ) + ).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 7)) + + s = univ.Set( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('customer', c) + ) + ) + + s.setComponentByName('name', 'A') + s.getComponentByName('customer').setComponentByName('premium', True) + + assert encoder.encode(s) == ints2octs((49, 8, 4, 1, 65, 167, 3, 1, 1, 255)) + + +class SequenceEncoderWithUntaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any(), openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1] = univ.Integer(12) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 5, 2, 1, 1, 49, 50) + ) + + def testEncodeOpenTypeChoiceTwo(self): + self.s.clear() + + self.s[0] = 2 + self.s[1] = univ.OctetString('quick brown') + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 14, 2, 1, 2, 113, 117, 105, 99, 107, 32, + 98, 114, 111, 119, 110) + ) + + def testEncodeOpenTypeUnknownId(self): + self.s.clear() + + self.s[0] = 2 + self.s[1] = univ.ObjectIdentifier('1.3.6') + + try: + encoder.encode(self.s, asn1Spec=self.s) + + except PyAsn1Error: + assert False, 'incompatible open type tolerated' + + def testEncodeOpenTypeIncompatibleType(self): + self.s.clear() + + self.s[0] = 2 + self.s[1] = univ.ObjectIdentifier('1.3.6') + + try: + encoder.encode(self.s, asn1Spec=self.s) + + except PyAsn1Error: + assert False, 'incompatible open type tolerated' + + +class SequenceEncoderWithImplicitlyTaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1] = univ.Integer(12) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 9, 2, 1, 1, 131, 4, 131, 2, 49, 50) + ) + + +class SequenceEncoderWithExplicitlyTaggedOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)), openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1] = univ.Integer(12) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 9, 2, 1, 1, 163, 4, 163, 2, 49, 50) + ) + + +class SequenceEncoderWithUntaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.SetOf( + componentType=univ.Any()), openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1].append(univ.Integer(12)) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 7, 2, 1, 1, 49, 2, 49, 50) + ) + + def testEncodeOpenTypeChoiceTwo(self): + self.s.clear() + + self.s[0] = 2 + self.s[1].append(univ.OctetString('quick brown')) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 16, 2, 1, 2, 49, 11, 113, 117, 105, 99, 107, 32, 98, 114, + 111, 119, 110) + ) + + def testEncodeOpenTypeUnknownId(self): + self.s.clear() + + self.s[0] = 2 + self.s[1].append(univ.ObjectIdentifier('1.3.6')) + + try: + encoder.encode(self.s, asn1Spec=self.s) + + except PyAsn1Error: + assert False, 'incompatible open type tolerated' + + def testEncodeOpenTypeIncompatibleType(self): + self.s.clear() + + self.s[0] = 2 + self.s[1].append(univ.ObjectIdentifier('1.3.6')) + + try: + encoder.encode(self.s, asn1Spec=self.s) + + except PyAsn1Error: + assert False, 'incompatible open type tolerated' + + +class SequenceEncoderWithImplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.SetOf( + componentType=univ.Any().subtype( + implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 3))), + openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1].append(univ.Integer(12)) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 11, 2, 1, 1, 49, 6, 131, 4, 131, 2, 49, 50) + ) + + +class SequenceEncoderWithExplicitlyTaggedSetOfOpenTypesTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + openType = opentype.OpenType( + 'id', + {1: univ.Integer(), + 2: univ.OctetString()} + ) + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.SetOf( + componentType=univ.Any().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))), + openType=openType) + ) + ) + + def testEncodeOpenTypeChoiceOne(self): + self.s.clear() + + self.s[0] = 1 + self.s[1].append(univ.Integer(12)) + + assert encoder.encode(self.s, asn1Spec=self.s) == ints2octs( + (48, 11, 2, 1, 1, 49, 6, 163, 4, 163, 2, 49, 50) + ) + + +class NestedOptionalSequenceEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + inner = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + ) + ) + + outerWithOptional = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('inner', inner), + ) + ) + + outerWithDefault = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.DefaultedNamedType('inner', inner), + ) + ) + + self.s1 = outerWithOptional + self.s2 = outerWithDefault + + def __initOptionalWithDefaultAndOptional(self): + self.s1.clear() + self.s1[0][0] = 'test' + self.s1[0][1] = 123 + return self.s1 + + def __initOptionalWithDefault(self): + self.s1.clear() + self.s1[0][1] = 123 + return self.s1 + + def __initOptionalWithOptional(self): + self.s1.clear() + self.s1[0][0] = 'test' + return self.s1 + + def __initOptional(self): + self.s1.clear() + return self.s1 + + def __initDefaultWithDefaultAndOptional(self): + self.s2.clear() + self.s2[0][0] = 'test' + self.s2[0][1] = 123 + return self.s2 + + def __initDefaultWithDefault(self): + self.s2.clear() + self.s2[0][0] = 'test' + return self.s2 + + def __initDefaultWithOptional(self): + self.s2.clear() + self.s2[0][1] = 123 + return self.s2 + + def testDefModeOptionalWithDefaultAndOptional(self): + s = self.__initOptionalWithDefaultAndOptional() + assert encoder.encode(s) == ints2octs((48, 11, 48, 9, 4, 4, 116, 101, 115, 116, 2, 1, 123)) + + def testDefModeOptionalWithDefault(self): + s = self.__initOptionalWithDefault() + assert encoder.encode(s) == ints2octs((48, 5, 48, 3, 2, 1, 123)) + + def testDefModeOptionalWithOptional(self): + s = self.__initOptionalWithOptional() + assert encoder.encode(s) == ints2octs((48, 8, 48, 6, 4, 4, 116, 101, 115, 116)) + + def testDefModeOptional(self): + s = self.__initOptional() + assert encoder.encode(s) == ints2octs((48, 0)) + + def testDefModeDefaultWithDefaultAndOptional(self): + s = self.__initDefaultWithDefaultAndOptional() + assert encoder.encode(s) == ints2octs((48, 11, 48, 9, 4, 4, 116, 101, 115, 116, 2, 1, 123)) + + def testDefModeDefaultWithDefault(self): + s = self.__initDefaultWithDefault() + assert encoder.encode(s) == ints2octs((48, 8, 48, 6, 4, 4, 116, 101, 115, 116)) + + def testDefModeDefaultWithOptional(self): + s = self.__initDefaultWithOptional() + assert encoder.encode(s) == ints2octs((48, 5, 48, 3, 2, 1, 123)) + + +class NestedOptionalChoiceEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + layer3 = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + ) + ) + + layer2 = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('inner', layer3), + namedtype.NamedType('first-name', univ.OctetString()) + ) + ) + + layer1 = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('inner', layer2), + ) + ) + + self.s = layer1 + + def __initOptionalWithDefaultAndOptional(self): + self.s.clear() + self.s[0][0][0] = 'test' + self.s[0][0][1] = 123 + return self.s + + def __initOptionalWithDefault(self): + self.s.clear() + self.s[0][0][1] = 123 + return self.s + + def __initOptionalWithOptional(self): + self.s.clear() + self.s[0][0][0] = 'test' + return self.s + + def __initOptional(self): + self.s.clear() + return self.s + + def testDefModeOptionalWithDefaultAndOptional(self): + s = self.__initOptionalWithDefaultAndOptional() + assert encoder.encode(s) == ints2octs((48, 11, 48, 9, 4, 4, 116, 101, 115, 116, 2, 1, 123)) + + def testDefModeOptionalWithDefault(self): + s = self.__initOptionalWithDefault() + assert encoder.encode(s) == ints2octs((48, 5, 48, 3, 2, 1, 123)) + + def testDefModeOptionalWithOptional(self): + s = self.__initOptionalWithOptional() + assert encoder.encode(s) == ints2octs((48, 8, 48, 6, 4, 4, 116, 101, 115, 116)) + + def testDefModeOptional(self): + s = self.__initOptional() + assert encoder.encode(s) == ints2octs((48, 0)) + + +class NestedOptionalSequenceOfEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + layer2 = univ.SequenceOf( + componentType=univ.OctetString() + ) + + layer1 = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('inner', layer2), + ) + ) + + self.s = layer1 + + def __initOptionalWithValue(self): + self.s.clear() + self.s[0][0] = 'test' + return self.s + + def __initOptional(self): + self.s.clear() + return self.s + + def testDefModeOptionalWithValue(self): + s = self.__initOptionalWithValue() + assert encoder.encode(s) == ints2octs((48, 8, 48, 6, 4, 4, 116, 101, 115, 116)) + + def testDefModeOptional(self): + s = self.__initOptional() + assert encoder.encode(s) == ints2octs((48, 0)) + + +class EmptyInnerFieldOfSequenceEncoderTestCase(BaseTestCase): + + def testInitializedOptionalNullIsEncoded(self): + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('null', univ.Null()) + ) + ) + + self.s.clear() + self.s[0] = '' + assert encoder.encode(self.s) == ints2octs((48, 2, 5, 0)) + + def testUninitializedOptionalNullIsNotEncoded(self): + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('null', univ.Null()) + ) + ) + + self.s.clear() + assert encoder.encode(self.s) == ints2octs((48, 0)) + + def testInitializedDefaultNullIsNotEncoded(self): + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.DefaultedNamedType('null', univ.Null('')) + ) + ) + + self.s.clear() + self.s[0] = '' + assert encoder.encode(self.s) == ints2octs((48, 0)) + + def testInitializedOptionalOctetStringIsEncoded(self): + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('str', univ.OctetString()) + ) + ) + + self.s.clear() + self.s[0] = '' + assert encoder.encode(self.s) == ints2octs((48, 2, 4, 0)) + + def testUninitializedOptionalOctetStringIsNotEncoded(self): + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.OptionalNamedType('str', univ.OctetString()) + ) + ) + + self.s.clear() + assert encoder.encode(self.s) == ints2octs((48, 0)) + + def testInitializedDefaultOctetStringIsNotEncoded(self): + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.DefaultedNamedType('str', univ.OctetString('')) + ) + ) + + self.s.clear() + self.s[0] = '' + assert encoder.encode(self.s) == ints2octs((48, 0)) + + +class ClassConstructorTestCase(BaseTestCase): + def testKeywords(self): + tagmap = {"tagmap": True} + typemap = {"typemap": True} + + sie = encoder.Encoder()._singleItemEncoder + self.assertIs(sie._tagMap, encoder.TAG_MAP) + self.assertIs(sie._typeMap, encoder.TYPE_MAP) + + sie = encoder.Encoder( + tagMap=tagmap, typeMap=typemap + )._singleItemEncoder + self.assertIs(sie._tagMap, tagmap) + self.assertIs(sie._typeMap, typemap) + + sie = encoder.Encoder(tagmap, typemap)._singleItemEncoder + self.assertIs(sie._tagMap, tagmap) + self.assertIs(sie._typeMap, typemap) + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/native/__init__.py b/contrib/python/pyasn1/py3/tests/codec/native/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/native/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/tests/codec/native/__main__.py b/contrib/python/pyasn1/py3/tests/codec/native/__main__.py new file mode 100644 index 0000000000..ab7faea877 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/native/__main__.py @@ -0,0 +1,15 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import unittest + +suite = unittest.TestLoader().loadTestsFromNames( + ['tests.codec.native.test_encoder.suite', + 'tests.codec.native.test_decoder.suite'] +) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/native/test_decoder.py b/contrib/python/pyasn1/py3/tests/codec/native/test_decoder.py new file mode 100644 index 0000000000..be7fd7ec0a --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/native/test_decoder.py @@ -0,0 +1,120 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import namedtype +from pyasn1.type import univ +from pyasn1.codec.native import decoder +from pyasn1.error import PyAsn1Error + + +class BadAsn1SpecTestCase(BaseTestCase): + def testBadSpec(self): + try: + decoder.decode('', asn1Spec='not an Asn1Item') + except PyAsn1Error: + pass + else: + assert 0, 'Invalid asn1Spec accepted' + + +class IntegerDecoderTestCase(BaseTestCase): + def testPosInt(self): + assert decoder.decode(12, asn1Spec=univ.Integer()) == univ.Integer(12) + + def testNegInt(self): + assert decoder.decode(-12, asn1Spec=univ.Integer()) == univ.Integer(-12) + + +class BooleanDecoderTestCase(BaseTestCase): + def testTrue(self): + assert decoder.decode(True, asn1Spec=univ.Boolean()) == univ.Boolean(True) + + def testTrueNeg(self): + assert decoder.decode(False, asn1Spec=univ.Boolean()) == univ.Boolean(False) + + +class BitStringDecoderTestCase(BaseTestCase): + def testSimple(self): + assert decoder.decode('11111111', asn1Spec=univ.BitString()) == univ.BitString(hexValue='ff') + + +class OctetStringDecoderTestCase(BaseTestCase): + def testSimple(self): + assert decoder.decode('Quick brown fox', asn1Spec=univ.OctetString()) == univ.OctetString('Quick brown fox') + + +class NullDecoderTestCase(BaseTestCase): + def testNull(self): + assert decoder.decode(None, asn1Spec=univ.Null()) == univ.Null('') + + +class ObjectIdentifierDecoderTestCase(BaseTestCase): + def testOne(self): + assert decoder.decode('1.3.6.11', asn1Spec=univ.ObjectIdentifier()) == univ.ObjectIdentifier('1.3.6.11') + + +class RealDecoderTestCase(BaseTestCase): + def testSimple(self): + assert decoder.decode(1.33, asn1Spec=univ.Real()) == univ.Real(1.33) + + +class SequenceDecoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.s = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null()), + namedtype.NamedType('first-name', univ.OctetString()), + namedtype.NamedType('age', univ.Integer(33)) + ) + ) + + def testSimple(self): + s = self.s.clone() + s[0] = univ.Null('') + s[1] = univ.OctetString('xx') + s[2] = univ.Integer(33) + assert decoder.decode({'place-holder': None, 'first-name': 'xx', 'age': 33}, asn1Spec=self.s) == s + + +class ChoiceDecoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.s = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null()), + namedtype.NamedType('first-name', univ.OctetString()), + namedtype.NamedType('age', univ.Integer(33)) + ) + ) + + def testSimple(self): + s = self.s.clone() + s[1] = univ.OctetString('xx') + assert decoder.decode({'first-name': 'xx'}, asn1Spec=self.s) == s + + +class AnyDecoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.s = univ.Any() + + def testSimple(self): + assert decoder.decode('fox', asn1Spec=univ.Any()) == univ.Any('fox') + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/native/test_encoder.py b/contrib/python/pyasn1/py3/tests/codec/native/test_encoder.py new file mode 100644 index 0000000000..662c284b3c --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/native/test_encoder.py @@ -0,0 +1,141 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import namedtype +from pyasn1.type import univ +from pyasn1.codec.native import encoder +from pyasn1.compat.octets import str2octs +from pyasn1.error import PyAsn1Error + + +class BadAsn1SpecTestCase(BaseTestCase): + def testBadValueType(self): + try: + encoder.encode('not an Asn1Item') + + except PyAsn1Error: + pass + + else: + assert 0, 'Invalid value type accepted' + + +class IntegerEncoderTestCase(BaseTestCase): + def testPosInt(self): + assert encoder.encode(univ.Integer(12)) == 12 + + def testNegInt(self): + assert encoder.encode(univ.Integer(-12)) == -12 + + +class BooleanEncoderTestCase(BaseTestCase): + def testTrue(self): + assert encoder.encode(univ.Boolean(1)) is True + + def testFalse(self): + assert encoder.encode(univ.Boolean(0)) is False + + +class BitStringEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.b = univ.BitString((1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1)) + + def testValue(self): + assert encoder.encode(self.b) == '101010011000101' + + +class OctetStringEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.o = univ.OctetString('Quick brown fox') + + def testValue(self): + assert encoder.encode(self.o) == str2octs('Quick brown fox') + + +class NullEncoderTestCase(BaseTestCase): + def testNull(self): + assert encoder.encode(univ.Null('')) is None + + +class ObjectIdentifierEncoderTestCase(BaseTestCase): + def testOne(self): + assert encoder.encode(univ.ObjectIdentifier((1, 3, 6, 0, 12345))) == '1.3.6.0.12345' + + +class RealEncoderTestCase(BaseTestCase): + def testChar(self): + assert encoder.encode(univ.Real((123, 10, 11))) == 1.23e+13 + + def testPlusInf(self): + assert encoder.encode(univ.Real('inf')) == float('inf') + + def testMinusInf(self): + assert encoder.encode(univ.Real('-inf')) == float('-inf') + + +class SequenceEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.s = univ.Sequence(componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null('')), + namedtype.OptionalNamedType('first-name', univ.OctetString('')), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + )) + + def testSimple(self): + s = self.s.clone() + s[0] = univ.Null('') + s[1] = 'abc' + s[2] = 123 + assert encoder.encode(s) == {'place-holder': None, 'first-name': str2octs('abc'), 'age': 123} + + +class ChoiceEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.s = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null('')), + namedtype.NamedType('number', univ.Integer(0)), + namedtype.NamedType('string', univ.OctetString()) + ) + ) + + def testEmpty(self): + try: + encoder.encode(self.s) + except PyAsn1Error: + pass + else: + assert False, 'encoded unset choice' + + def testFilled(self): + self.s.setComponentByPosition(0, univ.Null('')) + assert encoder.encode(self.s) == {'place-holder': None} + + +class AnyEncoderTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s = univ.Any(encoder.encode(univ.OctetString('fox'))) + + def testSimple(self): + assert encoder.encode(self.s) == str2octs('fox') + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/codec/test_streaming.py b/contrib/python/pyasn1/py3/tests/codec/test_streaming.py new file mode 100644 index 0000000000..7dc87257f2 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/codec/test_streaming.py @@ -0,0 +1,75 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import io +import sys + +try: + import unittest2 as unittest + +except ImportError: + import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.codec import streaming + + +class CachingStreamWrapperTestCase(BaseTestCase): + def setUp(self): + self.shortText = b"abcdefghij" + self.longText = self.shortText * (io.DEFAULT_BUFFER_SIZE * 5) + self.shortStream = io.BytesIO(self.shortText) + self.longStream = io.BytesIO(self.longText) + + def testReadJustFromCache(self): + wrapper = streaming.CachingStreamWrapper(self.shortStream) + wrapper.read(6) + wrapper.seek(3) + assert wrapper.read(1) == b"d" + assert wrapper.read(1) == b"e" + assert wrapper.tell() == 5 + + def testReadFromCacheAndStream(self): + wrapper = streaming.CachingStreamWrapper(self.shortStream) + wrapper.read(6) + wrapper.seek(3) + assert wrapper.read(4) == b"defg" + assert wrapper.tell() == 7 + + def testReadJustFromStream(self): + wrapper = streaming.CachingStreamWrapper(self.shortStream) + assert wrapper.read(6) == b"abcdef" + assert wrapper.tell() == 6 + + def testPeek(self): + wrapper = streaming.CachingStreamWrapper(self.longStream) + read_bytes = wrapper.peek(io.DEFAULT_BUFFER_SIZE + 73) + assert len(read_bytes) == io.DEFAULT_BUFFER_SIZE + 73 + assert read_bytes.startswith(b"abcdefg") + assert wrapper.tell() == 0 + assert wrapper.read(4) == b"abcd" + + def testMarkedPositionResets(self): + wrapper = streaming.CachingStreamWrapper(self.longStream) + wrapper.read(10) + wrapper.markedPosition = wrapper.tell() + assert wrapper.markedPosition == 10 + + # Reach the maximum capacity of cache + wrapper.read(io.DEFAULT_BUFFER_SIZE) + assert wrapper.tell() == 10 + io.DEFAULT_BUFFER_SIZE + + # The following should clear the cache + wrapper.markedPosition = wrapper.tell() + assert wrapper.markedPosition == 0 + assert len(wrapper._cache.getvalue()) == 0 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/compat/__init__.py b/contrib/python/pyasn1/py3/tests/compat/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/compat/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/tests/compat/__main__.py b/contrib/python/pyasn1/py3/tests/compat/__main__.py new file mode 100644 index 0000000000..94436847ba --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/compat/__main__.py @@ -0,0 +1,16 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import unittest + +suite = unittest.TestLoader().loadTestsFromNames( + ['tests.compat.test_integer.suite', + 'tests.compat.test_octets.suite'] +) + + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/compat/test_integer.py b/contrib/python/pyasn1/py3/tests/compat/test_integer.py new file mode 100644 index 0000000000..4026b75402 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/compat/test_integer.py @@ -0,0 +1,49 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.compat import integer + + +class IntegerTestCase(BaseTestCase): + + if sys.version_info[0] > 2: + + def test_from_bytes_zero(self): + assert 0 == integer.from_bytes(bytes([0]), signed=False) + + def test_from_bytes_unsigned(self): + assert -66051 == integer.from_bytes(bytes([254, 253, 253]), signed=True) + + def test_from_bytes_signed(self): + assert 66051 == integer.from_bytes(bytes([0, 1, 2, 3]), signed=False) + + def test_from_bytes_empty(self): + assert 0 == integer.from_bytes(bytes([])) + + else: + + def test_from_bytes_zero(self): + assert 0 == integer.from_bytes('\x00', signed=False) + + def test_from_bytes_unsigned(self): + assert -66051 == integer.from_bytes('\xfe\xfd\xfd', signed=True) + + def test_from_bytes_signed(self): + assert 66051 == integer.from_bytes('\x01\x02\x03', signed=False) + + def test_from_bytes_empty(self): + assert 0 == integer.from_bytes('') + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/compat/test_octets.py b/contrib/python/pyasn1/py3/tests/compat/test_octets.py new file mode 100644 index 0000000000..4133950704 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/compat/test_octets.py @@ -0,0 +1,113 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.compat import octets + + +class OctetsTestCase(BaseTestCase): + + if sys.version_info[0] > 2: + + def test_ints2octs(self): + assert [1, 2, 3] == list(octets.ints2octs([1, 2, 3])) + + def test_ints2octs_empty(self): + assert not octets.ints2octs([]) + + def test_int2oct(self): + assert [12] == list(octets.int2oct(12)) + + def test_octs2ints(self): + assert [1, 2, 3] == list(octets.octs2ints(bytes([1, 2, 3]))) + + def test_octs2ints_empty(self): + assert not octets.octs2ints(bytes([])) + + def test_oct2int(self): + assert 12 == octets.oct2int(bytes([12]))[0] + + def test_str2octs(self): + assert bytes([1, 2, 3]) == octets.str2octs('\x01\x02\x03') + + def test_str2octs_empty(self): + assert not octets.str2octs('') + + def test_octs2str(self): + assert '\x01\x02\x03' == octets.octs2str(bytes([1, 2, 3])) + + def test_octs2str_empty(self): + assert not octets.octs2str(bytes([])) + + def test_isOctetsType(self): + assert octets.isOctetsType('abc') == False + assert octets.isOctetsType(123) == False + assert octets.isOctetsType(bytes()) == True + + def test_isStringType(self): + assert octets.isStringType('abc') == True + assert octets.isStringType(123) == False + assert octets.isStringType(bytes()) == False + + def test_ensureString(self): + assert 'abc'.encode() == octets.ensureString('abc'.encode()) + assert bytes([1, 2, 3]) == octets.ensureString([1, 2, 3]) + + else: + + def test_ints2octs(self): + assert '\x01\x02\x03' == octets.ints2octs([1, 2, 3]) + + def test_ints2octs_empty(self): + assert not octets.ints2octs([]) + + def test_int2oct(self): + assert '\x0c' == octets.int2oct(12) + + def test_octs2ints(self): + assert [1, 2, 3] == octets.octs2ints('\x01\x02\x03') + + def test_octs2ints_empty(self): + assert not octets.octs2ints('') + + def test_oct2int(self): + assert 12 == octets.oct2int('\x0c') + + def test_str2octs(self): + assert '\x01\x02\x03' == octets.str2octs('\x01\x02\x03') + + def test_str2octs_empty(self): + assert not octets.str2octs('') + + def test_octs2str(self): + assert '\x01\x02\x03' == octets.octs2str('\x01\x02\x03') + + def test_octs2str_empty(self): + assert not octets.octs2str('') + + def test_isOctetsType(self): + assert octets.isOctetsType('abc') == True + assert octets.isOctetsType(123) == False + assert octets.isOctetsType(unicode('abc')) == False + + def test_isStringType(self): + assert octets.isStringType('abc') == True + assert octets.isStringType(123) == False + assert octets.isStringType(unicode('abc')) == True + + def test_ensureString(self): + assert 'abc' == octets.ensureString('abc') + assert '123' == octets.ensureString(123) + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/test_debug.py b/contrib/python/pyasn1/py3/tests/test_debug.py new file mode 100644 index 0000000000..84ba4f44c4 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/test_debug.py @@ -0,0 +1,37 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1 import debug +from pyasn1 import error + +class DebugCaseBase(BaseTestCase): + def testKnownFlags(self): + debug.setLogger(0) + debug.setLogger(debug.Debug('all', 'encoder', 'decoder')) + debug.setLogger(0) + + def testUnknownFlags(self): + try: + debug.setLogger(debug.Debug('all', 'unknown', loggerName='xxx')) + + except error.PyAsn1Error: + debug.setLogger(0) + return + + else: + debug.setLogger(0) + assert 0, 'unknown debug flag tolerated' + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/type/__init__.py b/contrib/python/pyasn1/py3/tests/type/__init__.py new file mode 100644 index 0000000000..8c3066b2e6 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/type/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/contrib/python/pyasn1/py3/tests/type/__main__.py b/contrib/python/pyasn1/py3/tests/type/__main__.py new file mode 100644 index 0000000000..67ff23e3c0 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/type/__main__.py @@ -0,0 +1,22 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import unittest + +suite = unittest.TestLoader().loadTestsFromNames( + ['tests.type.test_constraint.suite', + 'tests.type.test_opentype.suite', + 'tests.type.test_namedtype.suite', + 'tests.type.test_namedval.suite', + 'tests.type.test_tag.suite', + 'tests.type.test_univ.suite', + 'tests.type.test_char.suite', + 'tests.type.test_useful.suite'] +) + + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/type/test_char.py b/contrib/python/pyasn1/py3/tests/type/test_char.py new file mode 100644 index 0000000000..efa179eb0e --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/type/test_char.py @@ -0,0 +1,169 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import pickle +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import char +from pyasn1.type import univ +from pyasn1.type import constraint +from pyasn1.compat.octets import ints2octs +from pyasn1.error import PyAsn1Error + + +class AbstractStringTestCase(object): + + initializer = () + encoding = 'us-ascii' + asn1Type = None + + def setUp(self): + BaseTestCase.setUp(self) + + self.asn1String = self.asn1Type(ints2octs(self.initializer), encoding=self.encoding) + self.pythonString = ints2octs(self.initializer).decode(self.encoding) + + def testUnicode(self): + assert self.asn1String == self.pythonString, 'unicode init fails' + + def testLength(self): + assert len(self.asn1String) == len(self.pythonString), 'unicode len() fails' + + def testSizeConstraint(self): + asn1Spec = self.asn1Type(subtypeSpec=constraint.ValueSizeConstraint(1, 1)) + + try: + asn1Spec.clone(self.pythonString) + except PyAsn1Error: + pass + else: + assert False, 'Size constraint tolerated' + + try: + asn1Spec.clone(self.pythonString[0]) + except PyAsn1Error: + assert False, 'Size constraint failed' + + def testSerialised(self): + if sys.version_info[0] < 3: + assert str(self.asn1String) == self.pythonString.encode(self.encoding), '__str__() fails' + else: + assert bytes(self.asn1String) == self.pythonString.encode(self.encoding), '__str__() fails' + + def testPrintable(self): + if sys.version_info[0] < 3: + assert unicode(self.asn1String) == self.pythonString, '__str__() fails' + else: + assert str(self.asn1String) == self.pythonString, '__str__() fails' + + def testInit(self): + assert self.asn1Type(self.pythonString) == self.pythonString + assert self.asn1Type(self.pythonString.encode(self.encoding)) == self.pythonString + assert self.asn1Type(univ.OctetString(self.pythonString.encode(self.encoding))) == self.pythonString + assert self.asn1Type(self.asn1Type(self.pythonString)) == self.pythonString + assert self.asn1Type(self.initializer, encoding=self.encoding) == self.pythonString + + def testInitFromAsn1(self): + assert self.asn1Type(self.asn1Type(self.pythonString)) == self.pythonString + assert self.asn1Type(univ.OctetString(self.pythonString.encode(self.encoding), encoding=self.encoding)) == self.pythonString + + def testAsOctets(self): + assert self.asn1String.asOctets() == self.pythonString.encode(self.encoding), 'testAsOctets() fails' + + def testAsNumbers(self): + assert self.asn1String.asNumbers() == self.initializer, 'testAsNumbers() fails' + + def testSeq(self): + assert self.asn1String[0] == self.pythonString[0], '__getitem__() fails' + + def testEmpty(self): + try: + str(self.asn1Type()) + except PyAsn1Error: + pass + else: + assert 0, 'Value operation on ASN1 type tolerated' + + def testAdd(self): + assert self.asn1String + self.pythonString.encode(self.encoding) == self.pythonString + self.pythonString, '__add__() fails' + + def testRadd(self): + assert self.pythonString.encode(self.encoding) + self.asn1String == self.pythonString + self.pythonString, '__radd__() fails' + + def testMul(self): + assert self.asn1String * 2 == self.pythonString * 2, '__mul__() fails' + + def testRmul(self): + assert 2 * self.asn1String == 2 * self.pythonString, '__rmul__() fails' + + def testContains(self): + assert self.pythonString in self.asn1String + assert self.pythonString + self.pythonString not in self.asn1String + + def testReverse(self): + assert list(reversed(self.asn1String)) == list(reversed(self.pythonString)) + + def testSchemaPickling(self): + old_asn1 = self.asn1Type() + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == self.asn1Type + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = self.asn1String + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 == self.asn1String + + +class VisibleStringTestCase(AbstractStringTestCase, BaseTestCase): + + initializer = (97, 102) + encoding = 'us-ascii' + asn1Type = char.VisibleString + + +class GeneralStringTestCase(AbstractStringTestCase, BaseTestCase): + + initializer = (169, 174) + encoding = 'iso-8859-1' + asn1Type = char.GeneralString + + +class UTF8StringTestCase(AbstractStringTestCase, BaseTestCase): + + initializer = (209, 132, 208, 176) + encoding = 'utf-8' + asn1Type = char.UTF8String + + +class BMPStringTestCase(AbstractStringTestCase, BaseTestCase): + + initializer = (4, 48, 4, 68) + encoding = 'utf-16-be' + asn1Type = char.BMPString + + +if sys.version_info[0] > 2: + + # Somehow comparison of UTF-32 encoded strings does not work in Py2 + + class UniversalStringTestCase(AbstractStringTestCase, BaseTestCase): + initializer = (0, 0, 4, 48, 0, 0, 4, 68) + encoding = 'utf-32-be' + asn1Type = char.UniversalString + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/type/test_constraint.py b/contrib/python/pyasn1/py3/tests/type/test_constraint.py new file mode 100644 index 0000000000..1ae95ef61a --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/type/test_constraint.py @@ -0,0 +1,420 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import constraint +from pyasn1.type import error + + +class SingleValueConstraintTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.v1 = 1, 2 + self.v2 = 3, 4 + self.c1 = constraint.SingleValueConstraint(*self.v1) + self.c2 = constraint.SingleValueConstraint(*self.v2) + + def testCmp(self): + assert self.c1 == self.c1, 'comparison fails' + + def testHash(self): + assert hash(self.c1) != hash(self.c2), 'hash() fails' + + def testGoodVal(self): + try: + self.c1(1) + + except error.ValueConstraintError: + assert 0, 'constraint check fails' + + def testBadVal(self): + try: + self.c1(4) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + + def testContains(self): + for v in self.v1: + assert v in self.c1 + assert v not in self.c2 + + for v in self.v2: + assert v in self.c2 + assert v not in self.c1 + + def testIter(self): + assert set(self.v1) == set(self.c1) + assert set(self.v2) == set(self.c2) + + def testSub(self): + subconst = self.c1 - constraint.SingleValueConstraint(self.v1[0]) + assert list(subconst) == [self.v1[1]] + + def testAdd(self): + superconst = self.c1 + self.c2 + assert set(superconst) == set(self.v1 + self.v2) + + +class ContainedSubtypeConstraintTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.c1 = constraint.ContainedSubtypeConstraint( + constraint.SingleValueConstraint(12) + ) + + def testGoodVal(self): + try: + self.c1(12) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + + def testBadVal(self): + try: + self.c1(4) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + + +class ValueRangeConstraintTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.c1 = constraint.ValueRangeConstraint(1, 4) + + def testGoodVal(self): + try: + self.c1(1) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + + def testBadVal(self): + try: + self.c1(-5) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + + +class ValueSizeConstraintTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.c1 = constraint.ValueSizeConstraint(1, 2) + + def testGoodVal(self): + try: + self.c1('a') + except error.ValueConstraintError: + assert 0, 'constraint check fails' + + def testBadVal(self): + try: + self.c1('abc') + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + + +class PermittedAlphabetConstraintTestCase(SingleValueConstraintTestCase): + def setUp(self): + self.v1 = 'A', 'B' + self.v2 = 'C', 'D' + self.c1 = constraint.PermittedAlphabetConstraint(*self.v1) + self.c2 = constraint.PermittedAlphabetConstraint(*self.v2) + + def testGoodVal(self): + try: + self.c1('A') + + except error.ValueConstraintError: + assert 0, 'constraint check fails' + + def testBadVal(self): + try: + self.c1('E') + + except error.ValueConstraintError: + pass + + else: + assert 0, 'constraint check fails' + + +class WithComponentsConstraintTestCase(BaseTestCase): + + def testGoodVal(self): + c = constraint.WithComponentsConstraint( + ('A', constraint.ComponentPresentConstraint()), + ('B', constraint.ComponentAbsentConstraint())) + + try: + c({'A': 1}) + + except error.ValueConstraintError: + assert 0, 'constraint check fails' + + def testGoodValWithExtraFields(self): + c = constraint.WithComponentsConstraint( + ('A', constraint.ComponentPresentConstraint()), + ('B', constraint.ComponentAbsentConstraint()) + ) + + try: + c({'A': 1, 'C': 2}) + + except error.ValueConstraintError: + assert 0, 'constraint check fails' + + def testEmptyConstraint(self): + c = constraint.WithComponentsConstraint() + + try: + c({'A': 1}) + + except error.ValueConstraintError: + assert 0, 'constraint check fails' + + def testBadVal(self): + c = constraint.WithComponentsConstraint( + ('A', constraint.ComponentPresentConstraint()) + ) + + try: + c({'B': 2}) + + except error.ValueConstraintError: + pass + + else: + assert 0, 'constraint check fails' + + def testBadValExtraFields(self): + c = constraint.WithComponentsConstraint( + ('A', constraint.ComponentPresentConstraint()) + ) + + try: + c({'B': 2, 'C': 3}) + + except error.ValueConstraintError: + pass + + else: + assert 0, 'constraint check fails' + + +class ConstraintsIntersectionTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.c1 = constraint.ConstraintsIntersection( + constraint.SingleValueConstraint(4), + constraint.ValueRangeConstraint(2, 4) + ) + + def testCmp1(self): + assert constraint.SingleValueConstraint(4) in self.c1, '__cmp__() fails' + + def testCmp2(self): + assert constraint.SingleValueConstraint(5) not in self.c1, \ + '__cmp__() fails' + + def testCmp3(self): + c = constraint.ConstraintsUnion(constraint.ConstraintsIntersection( + constraint.SingleValueConstraint(4), + constraint.ValueRangeConstraint(2, 4)) + ) + assert self.c1 in c, '__cmp__() fails' + + def testCmp4(self): + c = constraint.ConstraintsUnion( + constraint.ConstraintsIntersection(constraint.SingleValueConstraint(5)) + ) + assert self.c1 not in c, '__cmp__() fails' + + def testGoodVal(self): + try: + self.c1(4) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + + def testBadVal(self): + try: + self.c1(-5) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + + +class InnerTypeConstraintTestCase(BaseTestCase): + def testConst1(self): + c = constraint.InnerTypeConstraint( + constraint.SingleValueConstraint(4) + ) + try: + c(4, 32) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + try: + c(5, 32) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + + def testConst2(self): + c = constraint.InnerTypeConstraint( + (0, constraint.SingleValueConstraint(4), 'PRESENT'), + (1, constraint.SingleValueConstraint(4), 'ABSENT') + ) + try: + c(4, 0) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + try: + c(4, 1) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + try: + c(3, 0) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + + # Constraints compositions + + +class ConstraintsIntersectionRangeTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.c1 = constraint.ConstraintsIntersection( + constraint.ValueRangeConstraint(1, 9), + constraint.ValueRangeConstraint(2, 5) + ) + + def testGoodVal(self): + try: + self.c1(3) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + + def testBadVal(self): + try: + self.c1(0) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + + +class ConstraintsUnionTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.c1 = constraint.ConstraintsUnion( + constraint.SingleValueConstraint(5), + constraint.ValueRangeConstraint(1, 3) + ) + + def testGoodVal(self): + try: + self.c1(2) + self.c1(5) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + + def testBadVal(self): + try: + self.c1(-5) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + + +class ConstraintsExclusionTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.c1 = constraint.ConstraintsExclusion( + constraint.ValueRangeConstraint(2, 4) + ) + + def testGoodVal(self): + try: + self.c1(6) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + + def testBadVal(self): + try: + self.c1(2) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + + +# Constraints derivations + +class DirectDerivationTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.c1 = constraint.SingleValueConstraint(5) + + self.c2 = constraint.ConstraintsUnion( + self.c1, constraint.ValueRangeConstraint(1, 3) + ) + + def testGoodVal(self): + assert self.c1.isSuperTypeOf(self.c2), 'isSuperTypeOf failed' + assert not self.c1.isSubTypeOf(self.c2), 'isSubTypeOf failed' + + def testBadVal(self): + assert not self.c2.isSuperTypeOf(self.c1), 'isSuperTypeOf failed' + assert self.c2.isSubTypeOf(self.c1), 'isSubTypeOf failed' + + +class IndirectDerivationTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.c1 = constraint.ConstraintsIntersection( + constraint.ValueRangeConstraint(1, 30) + ) + + self.c2 = constraint.ConstraintsIntersection( + self.c1, constraint.ValueRangeConstraint(1, 20) + ) + + self.c2 = constraint.ConstraintsIntersection( + self.c2, constraint.ValueRangeConstraint(1, 10) + ) + + def testGoodVal(self): + assert self.c1.isSuperTypeOf(self.c2), 'isSuperTypeOf failed' + assert not self.c1.isSubTypeOf(self.c2), 'isSubTypeOf failed' + + def testBadVal(self): + assert not self.c2.isSuperTypeOf(self.c1), 'isSuperTypeOf failed' + assert self.c2.isSubTypeOf(self.c1), 'isSubTypeOf failed' + +# TODO: how to apply size constraints to constructed types? + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/type/test_namedtype.py b/contrib/python/pyasn1/py3/tests/type/test_namedtype.py new file mode 100644 index 0000000000..4585984e6a --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/type/test_namedtype.py @@ -0,0 +1,135 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import namedtype +from pyasn1.type import univ +from pyasn1.error import PyAsn1Error + + +class NamedTypeCaseBase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.e = namedtype.NamedType('age', univ.Integer(0)) + + def testIter(self): + n, t = self.e + assert n == 'age' or t == univ.Integer(), 'unpack fails' + + def testRepr(self): + assert 'age' in repr(self.e) + + +class NamedTypesCaseBase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.e = namedtype.NamedTypes( + namedtype.NamedType('first-name', univ.OctetString('')), + namedtype.OptionalNamedType('age', univ.Integer(0)), + namedtype.NamedType('family-name', univ.OctetString('')) + ) + + def testRepr(self): + assert 'first-name' in repr(self.e) + + def testContains(self): + assert 'first-name' in self.e + assert '<missing>' not in self.e + + # noinspection PyUnusedLocal + def testGetItem(self): + assert self.e[0] == namedtype.NamedType('first-name', univ.OctetString('')) + + def testIter(self): + assert list(self.e) == ['first-name', 'age', 'family-name'] + + def testGetTypeByPosition(self): + assert self.e.getTypeByPosition(0) == univ.OctetString(''), \ + 'getTypeByPosition() fails' + + def testGetNameByPosition(self): + assert self.e.getNameByPosition(0) == 'first-name', \ + 'getNameByPosition() fails' + + def testGetPositionByName(self): + assert self.e.getPositionByName('first-name') == 0, \ + 'getPositionByName() fails' + + def testGetTypesNearPosition(self): + assert self.e.getTagMapNearPosition(0).presentTypes == { + univ.OctetString.tagSet: univ.OctetString('') + } + assert self.e.getTagMapNearPosition(1).presentTypes == { + univ.Integer.tagSet: univ.Integer(0), + univ.OctetString.tagSet: univ.OctetString('') + } + assert self.e.getTagMapNearPosition(2).presentTypes == { + univ.OctetString.tagSet: univ.OctetString('') + } + + def testGetTagMap(self): + assert self.e.tagMap.presentTypes == { + univ.OctetString.tagSet: univ.OctetString(''), + univ.Integer.tagSet: univ.Integer(0) + } + + def testStrTagMap(self): + assert 'TagMap' in str(self.e.tagMap) + assert 'OctetString' in str(self.e.tagMap) + assert 'Integer' in str(self.e.tagMap) + + def testReprTagMap(self): + assert 'TagMap' in repr(self.e.tagMap) + assert 'OctetString' in repr(self.e.tagMap) + assert 'Integer' in repr(self.e.tagMap) + + def testGetTagMapWithDups(self): + try: + self.e.tagMapUnique[0] + except PyAsn1Error: + pass + else: + assert 0, 'Duped types not noticed' + + def testGetPositionNearType(self): + assert self.e.getPositionNearType(univ.OctetString.tagSet, 0) == 0 + assert self.e.getPositionNearType(univ.Integer.tagSet, 1) == 1 + assert self.e.getPositionNearType(univ.OctetString.tagSet, 2) == 2 + + +class OrderedNamedTypesCaseBase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.e = namedtype.NamedTypes( + namedtype.NamedType('first-name', univ.OctetString('')), + namedtype.NamedType('age', univ.Integer(0)) + ) + + def testGetTypeByPosition(self): + assert self.e.getTypeByPosition(0) == univ.OctetString(''), \ + 'getTypeByPosition() fails' + + +class DuplicateNamedTypesCaseBase(BaseTestCase): + def testDuplicateDefaultTags(self): + nt = namedtype.NamedTypes( + namedtype.NamedType('first-name', univ.Any()), + namedtype.NamedType('age', univ.Any()) + ) + + assert isinstance(nt.tagMap, namedtype.NamedTypes.PostponedError) + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/type/test_namedval.py b/contrib/python/pyasn1/py3/tests/type/test_namedval.py new file mode 100644 index 0000000000..fda2da2a95 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/type/test_namedval.py @@ -0,0 +1,53 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import namedval + + +class NamedValuesCaseBase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.e = namedval.NamedValues(('off', 0), ('on', 1)) + + def testDict(self): + assert set(self.e.items()) == {('off', 0), ('on', 1)} + assert set(self.e.keys()) == {'off', 'on'} + assert set(self.e) == {'off', 'on'} + assert set(self.e.values()) == {0, 1} + assert 'on' in self.e and 'off' in self.e and 'xxx' not in self.e + assert 0 in self.e and 1 in self.e and 2 not in self.e + + def testInit(self): + assert namedval.NamedValues(off=0, on=1) == {'off': 0, 'on': 1} + assert namedval.NamedValues('off', 'on') == {'off': 0, 'on': 1} + assert namedval.NamedValues(('c', 0)) == {'c': 0} + assert namedval.NamedValues('a', 'b', ('c', 0), d=1) == {'c': 0, 'd': 1, 'a': 2, 'b': 3} + + def testLen(self): + assert len(self.e) == 2 + assert len(namedval.NamedValues()) == 0 + + def testAdd(self): + assert namedval.NamedValues(off=0) + namedval.NamedValues(on=1) == {'off': 0, 'on': 1} + + def testClone(self): + assert namedval.NamedValues(off=0).clone(('on', 1)) == {'off': 0, 'on': 1} + assert namedval.NamedValues(off=0).clone(on=1) == {'off': 0, 'on': 1} + + def testStrRepr(self): + assert str(self.e) + assert repr(self.e) + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/type/test_opentype.py b/contrib/python/pyasn1/py3/tests/type/test_opentype.py new file mode 100644 index 0000000000..5ae9715f40 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/type/test_opentype.py @@ -0,0 +1,101 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import univ +from pyasn1.type import tag +from pyasn1.type import namedtype +from pyasn1.type import opentype +from pyasn1.compat.octets import str2octs +from pyasn1.error import PyAsn1Error + + +class UntaggedAnyTestCase(BaseTestCase): + + def setUp(self): + BaseTestCase.setUp(self) + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', univ.Any()) + ) + + self.s = Sequence() + + def testTypeCheckOnAssignment(self): + + self.s.clear() + + self.s['blob'] = univ.Any(str2octs('xxx')) + + # this should succeed because Any is untagged and unconstrained + self.s['blob'] = univ.Integer(123) + + +class TaggedAnyTestCase(BaseTestCase): + + def setUp(self): + BaseTestCase.setUp(self) + + self.taggedAny = univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 20)) + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', self.taggedAny) + ) + + self.s = Sequence() + + def testTypeCheckOnAssignment(self): + + self.s.clear() + + self.s['blob'] = self.taggedAny.clone('xxx') + + try: + self.s.setComponentByName('blob', univ.Integer(123)) + + except PyAsn1Error: + pass + + else: + assert False, 'non-open type assignment tolerated' + + +class TaggedAnyOpenTypeTestCase(BaseTestCase): + + def setUp(self): + BaseTestCase.setUp(self) + + self.taggedAny = univ.Any().subtype(implicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 20)) + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer()), + namedtype.NamedType('blob', self.taggedAny, openType=opentype.OpenType(name='id')) + ) + + self.s = Sequence() + + def testTypeCheckOnAssignment(self): + + self.s.clear() + + self.s['blob'] = univ.Any(str2octs('xxx')) + self.s['blob'] = univ.Integer(123) + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/type/test_tag.py b/contrib/python/pyasn1/py3/tests/type/test_tag.py new file mode 100644 index 0000000000..5d27b72b8b --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/type/test_tag.py @@ -0,0 +1,133 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import tag + + +class TagTestCaseBase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.t1 = tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 3) + self.t2 = tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 3) + + +class TagReprTestCase(TagTestCaseBase): + def testRepr(self): + assert 'Tag' in repr(self.t1) + + +class TagCmpTestCase(TagTestCaseBase): + def testCmp(self): + assert self.t1 == self.t2, 'tag comparison fails' + + def testHash(self): + assert hash(self.t1) == hash(self.t2), 'tag hash comparison fails' + + def testSequence(self): + assert self.t1[0] == self.t2[0] and \ + self.t1[1] == self.t2[1] and \ + self.t1[2] == self.t2[2], 'tag sequence protocol fails' + + +class TagSetTestCaseBase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.ts1 = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) + ) + + self.ts2 = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) + ) + + +class TagSetReprTestCase(TagSetTestCaseBase): + def testRepr(self): + assert 'TagSet' in repr(self.ts1) + + +class TagSetCmpTestCase(TagSetTestCaseBase): + def testCmp(self): + assert self.ts1 == self.ts2, 'tag set comparison fails' + + def testHash(self): + assert hash(self.ts1) == hash(self.ts2), 'tag set hash comp. fails' + + def testLen(self): + assert len(self.ts1) == len(self.ts2), 'tag length comparison fails' + + +class TaggingTestSuite(TagSetTestCaseBase): + def testImplicitTag(self): + t = self.ts1.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 14) + ) + assert t == tag.TagSet( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 14) + ), 'implicit tagging went wrong' + + def testExplicitTag(self): + t = self.ts1.tagExplicitly( + tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 32) + ) + assert t == tag.TagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassPrivate, tag.tagFormatConstructed, 32) + ), 'explicit tagging went wrong' + + +class TagSetAddTestSuite(TagSetTestCaseBase): + def testAdd(self): + t = self.ts1 + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2) + assert t == tag.TagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2) + ), 'TagSet.__add__() fails' + + def testRadd(self): + t = tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2) + self.ts1 + assert t == tag.TagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) + ), 'TagSet.__radd__() fails' + + +class SuperTagSetTestCase(TagSetTestCaseBase): + def testSuperTagCheck1(self): + assert self.ts1.isSuperTagSetOf( + tag.TagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) + )), 'isSuperTagSetOf() fails' + + def testSuperTagCheck2(self): + assert not self.ts1.isSuperTagSetOf( + tag.TagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 13) + )), 'isSuperTagSetOf() fails' + + def testSuperTagCheck3(self): + assert self.ts1.isSuperTagSetOf( + tag.TagSet((), tag.Tag(tag.tagClassUniversal, + tag.tagFormatSimple, 12)) + ), 'isSuperTagSetOf() fails' + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/type/test_univ.py b/contrib/python/pyasn1/py3/tests/type/test_univ.py new file mode 100644 index 0000000000..001f978d48 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/type/test_univ.py @@ -0,0 +1,2184 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import math +import pickle +import platform +import sys +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import univ +from pyasn1.type import tag +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import namedval +from pyasn1.type import error +from pyasn1.compat.octets import str2octs, ints2octs, octs2ints, octs2str +from pyasn1.error import PyAsn1Error +from pyasn1.error import PyAsn1UnicodeEncodeError, PyAsn1UnicodeDecodeError + + +class NoValueTestCase(BaseTestCase): + def testSingleton(self): + assert univ.NoValue() is univ.NoValue(), 'NoValue is not a singleton' + + def testRepr(self): + try: + repr(univ.noValue) + + except PyAsn1Error: + assert False, 'repr() on NoValue object fails' + + def testIsInstance(self): + try: + assert isinstance(univ.noValue, univ.NoValue), 'isinstance() on NoValue() object fails' + + except PyAsn1Error: + assert False, 'isinstance() on NoValue object fails' + + def testStr(self): + try: + str(univ.noValue) + + except PyAsn1Error: + pass + + else: + assert False, 'str() works for NoValue object' + + def testLen(self): + try: + len(univ.noValue) + + except PyAsn1Error: + pass + + else: + assert False, 'len() works for NoValue object' + + def testCmp(self): + try: + univ.noValue == 1 + + except PyAsn1Error: + pass + + else: + assert False, 'comparison works for NoValue object' + + def testSubs(self): + try: + univ.noValue[0] + + except PyAsn1Error: + pass + + else: + assert False, '__getitem__() works for NoValue object' + + def testKey(self): + try: + univ.noValue['key'] + + except PyAsn1Error: + pass + + else: + assert False, '__getitem__() works for NoValue object' + + def testKeyAssignment(self): + try: + univ.noValue['key'] = 123 + + except PyAsn1Error: + pass + + else: + assert False, '__setitem__() works for NoValue object' + + def testInt(self): + try: + int(univ.noValue) + + except PyAsn1Error: + pass + + else: + assert False, 'integer conversion works for NoValue object' + + def testAdd(self): + try: + univ.noValue + univ.noValue + + except PyAsn1Error: + pass + + else: + assert False, 'addition works for NoValue object' + + def testBitShift(self): + try: + univ.noValue << 1 + + except PyAsn1Error: + pass + + else: + assert False, 'bitshift works for NoValue object' + + def testBooleanEvaluation(self): + try: + if univ.noValue: + pass + + except PyAsn1Error: + pass + + else: + assert False, 'boolean evaluation works for NoValue object' + + @unittest.skipIf( + platform.python_implementation() == "PyPy", + "getsizeof() raises TypeError on PyPy" + ) + def testSizeOf(self): + try: + sys.getsizeof(univ.noValue) + + except PyAsn1Error: + assert False, 'sizeof failed for NoValue object' + + +class IntegerTestCase(BaseTestCase): + def testStr(self): + assert str(univ.Integer(1)) in ('1', '1L'), 'str() fails' + + def testRepr(self): + assert '123' in repr(univ.Integer(123)) + + def testAnd(self): + assert univ.Integer(1) & 0 == 0, '__and__() fails' + + def testOr(self): + assert univ.Integer(1) | 0 == 1, '__or__() fails' + + def testXor(self): + assert univ.Integer(1) ^ 0 == 1, '__xor__() fails' + + def testRand(self): + assert 0 & univ.Integer(1) == 0, '__rand__() fails' + + def testRor(self): + assert 0 | univ.Integer(1) == 1, '__ror__() fails' + + def testRxor(self): + assert 0 ^ univ.Integer(1) == 1, '__rxor__() fails' + + def testAdd(self): + assert univ.Integer(-4) + 6 == 2, '__add__() fails' + + def testRadd(self): + assert 4 + univ.Integer(5) == 9, '__radd__() fails' + + def testSub(self): + assert univ.Integer(3) - 6 == -3, '__sub__() fails' + + def testRsub(self): + assert 6 - univ.Integer(3) == 3, '__rsub__() fails' + + def testMul(self): + assert univ.Integer(3) * -3 == -9, '__mul__() fails' + + def testRmul(self): + assert 2 * univ.Integer(3) == 6, '__rmul__() fails' + + def testDivInt(self): + assert univ.Integer(4) / 2 == 2, '__div__() fails' + + if sys.version_info[0] > 2: + def testDivFloat(self): + assert univ.Integer(3) / 2 == 1.5, '__div__() fails' + + def testRdivFloat(self): + assert 3 / univ.Integer(2) == 1.5, '__rdiv__() fails' + else: + def testDivFloat(self): + assert univ.Integer(3) / 2 == 1, '__div__() fails' + + def testRdivFloat(self): + assert 3 / univ.Integer(2) == 1, '__rdiv__() fails' + + def testRdivInt(self): + assert 6 / univ.Integer(3) == 2, '__rdiv__() fails' + + if sys.version_info[0] > 2: + def testTrueDiv(self): + assert univ.Integer(3) / univ.Integer(2) == 1.5, '__truediv__() fails' + + def testFloorDiv(self): + assert univ.Integer(3) // univ.Integer(2) == 1, '__floordiv__() fails' + + def testMod(self): + assert univ.Integer(3) % 2 == 1, '__mod__() fails' + + def testRmod(self): + assert 4 % univ.Integer(3) == 1, '__rmod__() fails' + + def testPow(self): + assert univ.Integer(3) ** 2 == 9, '__pow__() fails' + + def testRpow(self): + assert 2 ** univ.Integer(2) == 4, '__rpow__() fails' + + def testLshift(self): + assert univ.Integer(1) << 1 == 2, '<< fails' + + def testRshift(self): + assert univ.Integer(2) >> 1 == 1, '>> fails' + + def testInt(self): + assert int(univ.Integer(3)) == 3, '__int__() fails' + + def testLong(self): + assert int(univ.Integer(8)) == 8, '__long__() fails' + + def testFloat(self): + assert float(univ.Integer(4)) == 4.0, '__float__() fails' + + def testPos(self): + assert +univ.Integer(1) == 1, '__pos__() fails' + + def testNeg(self): + assert -univ.Integer(1) == -1, '__neg__() fails' + + def testInvert(self): + assert ~univ.Integer(1) == -2, '__invert__() fails' + + def testRound(self): + assert round(univ.Integer(1), 3) == 1.0, '__round__() fails' + + def testFloor(self): + assert math.floor(univ.Integer(1)) == 1, '__floor__() fails' + + def testCeil(self): + assert math.ceil(univ.Integer(1)) == 1, '__ceil__() fails' + + def testTrunc(self): + assert math.trunc(univ.Integer(1)) == 1, '__trunc__() fails' + + def testPrettyIn(self): + assert univ.Integer('3') == 3, 'prettyIn() fails' + + def testTag(self): + assert univ.Integer().tagSet == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02) + ) + + def testNamedVals(self): + + class Integer(univ.Integer): + namedValues = univ.Integer.namedValues.clone(('asn1', 1)) + + assert Integer('asn1') == 1, 'named val fails' + assert int(Integer('asn1')) == 1, 'named val fails' + assert str(Integer('asn1')) == 'asn1', 'named val __str__() fails' + + def testSubtype(self): + assert univ.Integer().subtype( + value=1, + implicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 2), + subtypeSpec=constraint.SingleValueConstraint(1, 3) + ) == univ.Integer( + value=1, + tagSet=tag.TagSet(tag.Tag(tag.tagClassPrivate, + tag.tagFormatSimple, 2)), + subtypeSpec=constraint.ConstraintsIntersection(constraint.SingleValueConstraint(1, 3)) + ) + + +class IntegerPicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = univ.Integer() + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == univ.Integer + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = univ.Integer(-123) + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 == -123 + + +class BooleanTestCase(BaseTestCase): + def testTruth(self): + assert univ.Boolean(True) and univ.Boolean(1), 'Truth initializer fails' + + def testFalse(self): + assert not univ.Boolean(False) and not univ.Boolean(0), 'False initializer fails' + + def testStr(self): + assert str(univ.Boolean(1)) == 'True', 'str() fails' + + def testInt(self): + assert int(univ.Boolean(1)) == 1, 'int() fails' + + def testRepr(self): + assert 'Boolean' in repr(univ.Boolean(1)) + + def testTag(self): + assert univ.Boolean().tagSet == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01) + ) + + def testConstraints(self): + + class Boolean(univ.Boolean): + pass + + try: + Boolean(2) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint fail' + + +class BooleanPicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = univ.Boolean() + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == univ.Boolean + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = univ.Boolean(True) + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 == True + + +class BitStringTestCase(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.b = univ.BitString( + namedValues=namedval.NamedValues(('Active', 0), ('Urgent', 1)) + ) + + def testBinDefault(self): + + class BinDefault(univ.BitString): + defaultBinValue = '1010100110001010' + + assert BinDefault() == univ.BitString(binValue='1010100110001010') + + def testHexDefault(self): + + class HexDefault(univ.BitString): + defaultHexValue = 'A98A' + + assert HexDefault() == univ.BitString(hexValue='A98A') + + def testSet(self): + assert self.b.clone('Active') == (1,) + assert self.b.clone('Urgent') == (0, 1) + assert self.b.clone('Urgent, Active') == (1, 1) + assert self.b.clone("'1010100110001010'B") == (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0) + assert self.b.clone("'A98A'H") == (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0) + assert self.b.clone(binValue='1010100110001010') == (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0) + assert self.b.clone(hexValue='A98A') == (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0) + assert self.b.clone('1010100110001010') == (1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0) + assert self.b.clone((1, 0, 1)) == (1, 0, 1) + + def testStr(self): + assert str(self.b.clone('Urgent')) == '01' + + def testRepr(self): + assert 'BitString' in repr(self.b.clone('Urgent,Active')) + + def testTag(self): + assert univ.BitString().tagSet == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03) + ) + + def testLen(self): + assert len(self.b.clone("'A98A'H")) == 16 + + def testGetItem(self): + assert self.b.clone("'A98A'H")[0] == 1 + assert self.b.clone("'A98A'H")[1] == 0 + assert self.b.clone("'A98A'H")[2] == 1 + + def testReverse(self): + assert list(reversed(univ.BitString([0, 0, 1]))) == list(univ.BitString([1, 0, 0])) + + def testAsOctets(self): + assert self.b.clone(hexValue='A98A').asOctets() == ints2octs((0xa9, 0x8a)), 'testAsOctets() fails' + + def testAsInts(self): + assert self.b.clone(hexValue='A98A').asNumbers() == (0xa9, 0x8a), 'testAsNumbers() fails' + + def testMultipleOfEightPadding(self): + assert self.b.clone((1, 0, 1)).asNumbers() == (5,) + + def testAsInteger(self): + assert self.b.clone('11000000011001').asInteger() == 12313 + assert self.b.clone('1100110011011111').asInteger() == 52447 + + def testStaticDef(self): + + class BitString(univ.BitString): + pass + + assert BitString('11000000011001').asInteger() == 12313 + + +class BitStringPicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = univ.BitString() + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == univ.BitString + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = univ.BitString((1, 0, 1, 0)) + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 == (1, 0, 1, 0) + + +class OctetStringWithUnicodeMixIn(object): + + initializer = () + encoding = 'us-ascii' + + def setUp(self): + self.pythonString = ints2octs(self.initializer).decode(self.encoding) + self.encodedPythonString = self.pythonString.encode(self.encoding) + self.numbersString = tuple(octs2ints(self.encodedPythonString)) + + def testInit(self): + assert univ.OctetString(self.encodedPythonString) == self.encodedPythonString, '__init__() fails' + + def testInitFromAsn1(self): + assert univ.OctetString(univ.OctetString(self.encodedPythonString)) == self.encodedPythonString + assert univ.OctetString(univ.Integer(123)) == univ.OctetString('123') + + def testSerialised(self): + if sys.version_info[0] < 3: + assert str(univ.OctetString(self.encodedPythonString, encoding=self.encoding)) == self.encodedPythonString, '__str__() fails' + else: + assert bytes(univ.OctetString(self.encodedPythonString, encoding=self.encoding)) == self.encodedPythonString, '__str__() fails' + + def testPrintable(self): + if sys.version_info[0] < 3: + assert str(univ.OctetString(self.encodedPythonString, encoding=self.encoding)) == self.encodedPythonString, '__str__() fails' + assert unicode(univ.OctetString(self.pythonString, encoding=self.encoding)) == self.pythonString, 'unicode init fails' + else: + assert str(univ.OctetString(self.pythonString, encoding=self.encoding)) == self.pythonString, 'unicode init fails' + + def testSeq(self): + assert univ.OctetString(self.encodedPythonString)[0] == self.encodedPythonString[0], '__getitem__() fails' + + def testRepr(self): + assert 'abc' in repr(univ.OctetString('abc')) + + def testAsOctets(self): + assert univ.OctetString(self.encodedPythonString).asOctets() == self.encodedPythonString, 'testAsOctets() fails' + + def testAsInts(self): + assert univ.OctetString(self.encodedPythonString).asNumbers() == self.numbersString, 'testAsNumbers() fails' + + def testAdd(self): + assert univ.OctetString(self.encodedPythonString) + self.encodedPythonString == self.encodedPythonString + self.encodedPythonString, '__add__() fails' + + def testRadd(self): + assert self.encodedPythonString + univ.OctetString(self.encodedPythonString) == self.encodedPythonString + self.encodedPythonString, '__radd__() fails' + + def testMul(self): + assert univ.OctetString(self.encodedPythonString) * 2 == self.encodedPythonString * 2, '__mul__() fails' + + def testRmul(self): + assert 2 * univ.OctetString(self.encodedPythonString) == 2 * self.encodedPythonString, '__rmul__() fails' + + def testContains(self): + s = univ.OctetString(self.encodedPythonString) + assert self.encodedPythonString in s + assert self.encodedPythonString * 2 not in s + + def testReverse(self): + assert list(reversed(univ.OctetString(self.encodedPythonString))) == list(reversed(self.encodedPythonString)) + + +class OctetStringWithAsciiTestCase(OctetStringWithUnicodeMixIn, BaseTestCase): + initializer = (97, 102) + encoding = 'us-ascii' + + +class OctetStringUnicodeErrorTestCase(BaseTestCase): + def testEncodeError(self): + serialized = ints2octs((0xff, 0xfe)) + + if sys.version_info < (3, 0): + text = serialized.decode('iso-8859-1') + + else: + text = octs2str(serialized) + + try: + univ.OctetString(text, encoding='us-ascii') + + except PyAsn1UnicodeEncodeError: + pass + + def testDecodeError(self): + serialized = ints2octs((0xff, 0xfe)) + + octetString = univ.OctetString(serialized, encoding='us-ascii') + + try: + if sys.version_info < (3, 0): + unicode(octetString) + + else: + str(octetString) + + except PyAsn1UnicodeDecodeError: + pass + + +class OctetStringWithUtf8TestCase(OctetStringWithUnicodeMixIn, BaseTestCase): + initializer = (208, 176, 208, 177, 208, 178) + encoding = 'utf-8' + + +class OctetStringWithUtf16TestCase(OctetStringWithUnicodeMixIn, BaseTestCase): + initializer = (4, 48, 4, 49, 4, 50) + encoding = 'utf-16-be' + + +if sys.version_info[0] > 2: + + # Somehow comparison of UTF-32 encoded strings does not work in Py2 + + class OctetStringWithUtf32TestCase(OctetStringWithUnicodeMixIn, BaseTestCase): + initializer = (0, 0, 4, 48, 0, 0, 4, 49, 0, 0, 4, 50) + encoding = 'utf-32-be' + + +class OctetStringTestCase(BaseTestCase): + + def testBinDefault(self): + + class BinDefault(univ.OctetString): + defaultBinValue = '1000010111101110101111000000111011' + + assert BinDefault() == univ.OctetString(binValue='1000010111101110101111000000111011') + + def testHexDefault(self): + + class HexDefault(univ.OctetString): + defaultHexValue = 'FA9823C43E43510DE3422' + + assert HexDefault() == univ.OctetString(hexValue='FA9823C43E43510DE3422') + + def testBinStr(self): + assert univ.OctetString(binValue="1000010111101110101111000000111011") == ints2octs((133, 238, 188, 14, 192)), 'bin init fails' + + def testHexStr(self): + assert univ.OctetString(hexValue="FA9823C43E43510DE3422") == ints2octs((250, 152, 35, 196, 62, 67, 81, 13, 227, 66, 32)), 'hex init fails' + + def testTuple(self): + assert univ.OctetString((1, 2, 3, 4, 5)) == ints2octs((1, 2, 3, 4, 5)), 'tuple init failed' + + def testRepr(self): + assert 'abc' in repr(univ.OctetString('abc')) + + def testEmpty(self): + try: + str(univ.OctetString()) + except PyAsn1Error: + pass + else: + assert 0, 'empty OctetString() not reported' + + def testTag(self): + assert univ.OctetString().tagSet == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04) + ) + + def testStaticDef(self): + + class OctetString(univ.OctetString): + pass + + assert OctetString(hexValue="FA9823C43E43510DE3422") == ints2octs((250, 152, 35, 196, 62, 67, 81, 13, 227, 66, 32)) + + +class OctetStringPicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = univ.BitString() + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == univ.BitString + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = univ.BitString((1, 0, 1, 0)) + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 == (1, 0, 1, 0) + + +class Null(BaseTestCase): + + def testInit(self): + assert not univ.Null().isValue + assert univ.Null(0) == str2octs('') + assert univ.Null(False) == str2octs('') + assert univ.Null('') == str2octs('') + assert univ.Null(None) == str2octs('') + + try: + assert univ.Null(True) + + except PyAsn1Error: + pass + + try: + assert univ.Null('xxx') + + except PyAsn1Error: + pass + + def testStr(self): + assert str(univ.Null('')) == '', 'str() fails' + + def testRepr(self): + assert 'Null' in repr(univ.Null('')) + + def testTag(self): + assert univ.Null().tagSet == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05) + ) + + def testConstraints(self): + try: + univ.Null(2) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint fail' + + def testStaticDef(self): + + class Null(univ.Null): + pass + + assert not Null('') + + +class NullPicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = univ.Null() + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == univ.Null + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = univ.Null('') + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert not new_asn1 + + +class RealTestCase(BaseTestCase): + def testFloat4BinEnc(self): + assert univ.Real((0.25, 2, 3)) == 2.0, 'float initializer for binary encoding fails' + + def testStr(self): + assert str(univ.Real(1.0)) == '1.0', 'str() fails' + + def testRepr(self): + assert 'Real' in repr(univ.Real(-4.1)) + assert 'Real' in repr(univ.Real(-4.1)) + assert 'inf' in repr(univ.Real('inf')) + assert '-inf' in repr(univ.Real('-inf')) + + def testAdd(self): + assert univ.Real(-4.1) + 1.4 == -2.7, '__add__() fails' + + def testRadd(self): + assert 4 + univ.Real(0.5) == 4.5, '__radd__() fails' + + def testSub(self): + assert univ.Real(3.9) - 1.7 == 2.2, '__sub__() fails' + + def testRsub(self): + assert 6.1 - univ.Real(0.1) == 6, '__rsub__() fails' + + def testMul(self): + assert univ.Real(3.0) * -3 == -9, '__mul__() fails' + + def testRmul(self): + assert 2 * univ.Real(3.0) == 6, '__rmul__() fails' + + def testDiv(self): + assert univ.Real(3.0) / 2 == 1.5, '__div__() fails' + + def testRdiv(self): + assert 6 / univ.Real(3.0) == 2, '__rdiv__() fails' + + def testMod(self): + assert univ.Real(3.0) % 2 == 1, '__mod__() fails' + + def testRmod(self): + assert 4 % univ.Real(3.0) == 1, '__rmod__() fails' + + def testPow(self): + assert univ.Real(3.0) ** 2 == 9, '__pow__() fails' + + def testRpow(self): + assert 2 ** univ.Real(2.0) == 4, '__rpow__() fails' + + def testInt(self): + assert int(univ.Real(3.0)) == 3, '__int__() fails' + + def testLong(self): + assert int(univ.Real(8.0)) == 8, '__long__() fails' + + def testFloat(self): + assert float(univ.Real(4.0)) == 4.0, '__float__() fails' + + def testPrettyIn(self): + assert univ.Real((3, 10, 0)) == 3, 'prettyIn() fails' + + # infinite float values + def testStrInf(self): + assert str(univ.Real('inf')) == 'inf', 'str() fails' + + def testAddInf(self): + assert univ.Real('inf') + 1 == float('inf'), '__add__() fails' + + def testRaddInf(self): + assert 1 + univ.Real('inf') == float('inf'), '__radd__() fails' + + def testIntInf(self): + try: + assert int(univ.Real('inf')) + except OverflowError: + pass + else: + assert 0, '__int__() fails' + + def testLongInf(self): + try: + assert int(univ.Real('inf')) + except OverflowError: + pass + else: + assert 0, '__long__() fails' + assert int(univ.Real(8.0)) == 8, '__long__() fails' + + def testFloatInf(self): + assert float(univ.Real('-inf')) == float('-inf'), '__float__() fails' + + def testPrettyInInf(self): + assert univ.Real(float('inf')) == float('inf'), 'prettyIn() fails' + + def testPlusInf(self): + assert univ.Real('inf').isPlusInf, 'isPlusInfinity failed' + + def testMinusInf(self): + assert univ.Real('-inf').isMinusInf, 'isMinusInfinity failed' + + def testPos(self): + assert +univ.Real(1.0) == 1.0, '__pos__() fails' + + def testNeg(self): + assert -univ.Real(1.0) == -1.0, '__neg__() fails' + + def testRound(self): + assert round(univ.Real(1.123), 2) == 1.12, '__round__() fails' + + def testFloor(self): + assert math.floor(univ.Real(1.6)) == 1.0, '__floor__() fails' + + def testCeil(self): + assert math.ceil(univ.Real(1.2)) == 2.0, '__ceil__() fails' + + def testTrunc(self): + assert math.trunc(univ.Real(1.1)) == 1.0, '__trunc__() fails' + + def testTag(self): + assert univ.Real().tagSet == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09) + ) + + def testStaticDef(self): + + class Real(univ.Real): + pass + + assert Real(1.0) == 1.0 + + +class RealPicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = univ.Real() + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == univ.Real + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = univ.Real((1, 10, 3)) + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 == 1000 + + +class ObjectIdentifier(BaseTestCase): + def testStr(self): + assert str(univ.ObjectIdentifier((1, 3, 6))) == '1.3.6', 'str() fails' + + def testRepr(self): + assert '1.3.6' in repr(univ.ObjectIdentifier('1.3.6')) + + def testEq(self): + assert univ.ObjectIdentifier((1, 3, 6)) == (1, 3, 6), '__cmp__() fails' + + def testAdd(self): + assert univ.ObjectIdentifier((1, 3)) + (6,) == (1, 3, 6), '__add__() fails' + + def testRadd(self): + assert (1,) + univ.ObjectIdentifier((3, 6)) == (1, 3, 6), '__radd__() fails' + + def testLen(self): + assert len(univ.ObjectIdentifier((1, 3))) == 2, '__len__() fails' + + def testPrefix(self): + o = univ.ObjectIdentifier('1.3.6') + assert o.isPrefixOf((1, 3, 6)), 'isPrefixOf() fails' + assert o.isPrefixOf((1, 3, 6, 1)), 'isPrefixOf() fails' + assert not o.isPrefixOf((1, 3)), 'isPrefixOf() fails' + + def testInput1(self): + assert univ.ObjectIdentifier('1.3.6') == (1, 3, 6), 'prettyIn() fails' + + def testInput2(self): + assert univ.ObjectIdentifier((1, 3, 6)) == (1, 3, 6), 'prettyIn() fails' + + def testInput3(self): + assert univ.ObjectIdentifier(univ.ObjectIdentifier('1.3') + (6,)) == (1, 3, 6), 'prettyIn() fails' + + def testUnicode(self): + s = '1.3.6' + if sys.version_info[0] < 3: + s = s.decode() + assert univ.ObjectIdentifier(s) == (1, 3, 6), 'unicode init fails' + + def testTag(self): + assert univ.ObjectIdentifier().tagSet == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06) + ) + + def testContains(self): + s = univ.ObjectIdentifier('1.3.6.1234.99999') + assert 1234 in s + assert 4321 not in s + + def testStaticDef(self): + + class ObjectIdentifier(univ.ObjectIdentifier): + pass + + assert str(ObjectIdentifier((1, 3, 6))) == '1.3.6' + + +class ObjectIdentifierPicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = univ.ObjectIdentifier() + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == univ.ObjectIdentifier + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = univ.ObjectIdentifier('2.3.1.1.2') + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 == (2, 3, 1, 1, 2) + + +class SequenceOf(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s1 = univ.SequenceOf( + componentType=univ.OctetString('') + ) + self.s2 = self.s1.clone() + + def testRepr(self): + assert 'a' in repr(self.s1.clone().setComponents('a', 'b')) + + def testTag(self): + assert self.s1.tagSet == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) + ), 'wrong tagSet' + + def testSeq(self): + self.s1.setComponentByPosition(0, univ.OctetString('abc')) + assert self.s1[0] == str2octs('abc'), 'set by idx fails' + self.s1[0] = 'cba' + assert self.s1[0] == str2octs('cba'), 'set by idx fails' + + def testCmp(self): + self.s1.clear() + self.s1.setComponentByPosition(0, 'abc') + self.s2.clear() + self.s2.setComponentByPosition(0, univ.OctetString('abc')) + assert self.s1 == self.s2, '__cmp__() fails' + + def testSubtypeSpec(self): + s = self.s1.clone( + componentType=univ.OctetString().subtype( + subtypeSpec=constraint.SingleValueConstraint(str2octs('abc')))) + try: + s.setComponentByPosition( + 0, univ.OctetString().subtype( + 'abc', subtypeSpec=constraint.SingleValueConstraint(str2octs('abc')))) + except PyAsn1Error: + assert 0, 'constraint fails' + try: + s.setComponentByPosition(1, univ.OctetString('Abc')) + except PyAsn1Error: + try: + s.setComponentByPosition(1, univ.OctetString('Abc'), + verifyConstraints=False) + except PyAsn1Error: + assert 0, 'constraint fails with verifyConstraints=False' + else: + assert 0, 'constraint fails' + + def testComponentTagsMatching(self): + s = self.s1.clone() + s.strictConstraints = True # This requires types equality + o = univ.OctetString('abc').subtype(explicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 12)) + try: + s.setComponentByPosition(0, o) + except PyAsn1Error: + pass + else: + assert 0, 'inner supertype tag allowed' + + def testComponentConstraintsMatching(self): + s = self.s1.clone() + o = univ.OctetString().subtype( + subtypeSpec=constraint.ConstraintsUnion(constraint.SingleValueConstraint(str2octs('cba')))) + s.strictConstraints = True # This requires types equality + try: + s.setComponentByPosition(0, o.clone('cba')) + except PyAsn1Error: + pass + else: + assert 0, 'inner supertype constraint allowed' + s.strictConstraints = False # This requires subtype relationships + try: + s.setComponentByPosition(0, o.clone('cba')) + except PyAsn1Error: + assert 0, 'inner supertype constraint disallowed' + else: + pass + + def testConsistency(self): + s = self.s1.clone(subtypeSpec=constraint.ConstraintsUnion( + constraint.ValueSizeConstraint(1, 1) + )) + s.setComponentByPosition(0, univ.OctetString('abc')) + assert not s.isInconsistent, 'size spec fails' + s.setComponentByPosition(1, univ.OctetString('abc')) + assert s.isInconsistent, 'size spec fails' + + def testGetComponentTagMap(self): + assert self.s1.componentType.tagMap.presentTypes == { + univ.OctetString.tagSet: univ.OctetString('') + } + + def testSubtype(self): + subtype = self.s1.subtype( + implicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 2), + subtypeSpec=constraint.ValueSizeConstraint(0, 1) + ) + subtype.clear() + clone = self.s1.clone( + tagSet=tag.TagSet(tag.Tag(tag.tagClassPrivate, + tag.tagFormatSimple, 2)), + subtypeSpec=constraint.ValueSizeConstraint(0, 1) + ) + clone.clear() + assert clone == subtype + + def testClone(self): + self.s1.setComponentByPosition(0, univ.OctetString('abc')) + s = self.s1.clone() + s.clear() + assert len(s) == 0 + s = self.s1.clone(cloneValueFlag=1) + assert len(s) == 1 + assert s.getComponentByPosition(0) == self.s1.getComponentByPosition(0) + + def testSetComponents(self): + assert self.s1.clone().setComponents('abc', 'def') == \ + self.s1.setComponentByPosition(0, 'abc').setComponentByPosition(1, 'def') + + def testGetItem(self): + s = self.s1.clone() + s.append('xxx') + assert s[0] + + # this is a deviation from standard sequence protocol + assert not s[2] + + def testGetItemSlice(self): + s = self.s1.clone() + s.extend(['xxx', 'yyy', 'zzz']) + assert s[:1] == [str2octs('xxx')] + assert s[-2:] == [str2octs('yyy'), str2octs('zzz')] + assert s[1:2] == [str2octs('yyy')] + + def testSetItem(self): + s = self.s1.clone() + s.append('xxx') + s[2] = 'yyy' + assert len(s) == 3 + assert s[1] == str2octs('') + + def testSetItemSlice(self): + s = self.s1.clone() + s[:1] = ['xxx'] + assert s == [str2octs('xxx')] + s[-2:] = ['yyy', 'zzz'] + assert s == [str2octs('yyy'), str2octs('zzz')] + s[1:2] = ['yyy'] + assert s == [str2octs('yyy'), str2octs('yyy')] + assert len(s) == 2 + + def testAppend(self): + self.s1.clear() + self.s1.setComponentByPosition(0, univ.OctetString('abc')) + assert len(self.s1) == 1 + self.s1.append('def') + assert len(self.s1) == 2 + assert list(self.s1) == [str2octs(x) for x in ['abc', 'def']] + + def testExtend(self): + self.s1.clear() + self.s1.setComponentByPosition(0, univ.OctetString('abc')) + assert len(self.s1) == 1 + self.s1.extend(['def', 'ghi']) + assert len(self.s1) == 3 + assert list(self.s1) == [str2octs(x) for x in ['abc', 'def', 'ghi']] + + def testCount(self): + self.s1.clear() + for x in ['abc', 'def', 'abc']: + self.s1.append(x) + assert self.s1.count(str2octs('abc')) == 2 + assert self.s1.count(str2octs('def')) == 1 + assert self.s1.count(str2octs('ghi')) == 0 + + def testIndex(self): + self.s1.clear() + for x in ['abc', 'def', 'abc']: + self.s1.append(x) + assert self.s1.index(str2octs('abc')) == 0 + assert self.s1.index(str2octs('def')) == 1 + assert self.s1.index(str2octs('abc'), 1) == 2 + + def testSort(self): + self.s1.clear() + self.s1[0] = 'b' + self.s1[1] = 'a' + assert list(self.s1) == [str2octs('b'), str2octs('a')] + self.s1.sort() + assert list(self.s1) == [str2octs('a'), str2octs('b')] + + def testStaticDef(self): + + class SequenceOf(univ.SequenceOf): + componentType = univ.OctetString('') + + s = SequenceOf() + s[0] = 'abc' + assert len(s) == 1 + assert s == [str2octs('abc')] + + def testUntyped(self): + n = univ.SequenceOf() + + assert not n.isValue + + n[0] = univ.OctetString('fox') + + assert n.isValue + + def testLegacyInitializer(self): + n = univ.SequenceOf( + componentType=univ.OctetString() + ) + o = univ.SequenceOf( + univ.OctetString() # this is the old way + ) + + assert n.isSameTypeWith(o) and o.isSameTypeWith(n) + + n[0] = 'fox' + o[0] = 'fox' + + assert n == o + + def testGetComponentWithDefault(self): + + class SequenceOf(univ.SequenceOf): + componentType = univ.OctetString() + + s = SequenceOf() + assert s.getComponentByPosition(0, default=None, instantiate=False) is None + assert s.getComponentByPosition(0, default=None) is None + s[0] = 'test' + assert s.getComponentByPosition(0, default=None) is not None + assert s.getComponentByPosition(0, default=None) == str2octs('test') + s.clear() + assert s.getComponentByPosition(0, default=None) is None + + def testGetComponentNoInstantiation(self): + + class SequenceOf(univ.SequenceOf): + componentType = univ.OctetString() + + s = SequenceOf() + assert s.getComponentByPosition(0, instantiate=False) is univ.noValue + s[0] = 'test' + assert s.getComponentByPosition(0, instantiate=False) is not univ.noValue + assert s.getComponentByPosition(0, instantiate=False) == str2octs('test') + s.clear() + assert s.getComponentByPosition(0, instantiate=False) is univ.noValue + + def testClear(self): + + class SequenceOf(univ.SequenceOf): + componentType = univ.OctetString() + + s = SequenceOf() + s.setComponentByPosition(0, 'test') + + assert s.getComponentByPosition(0) == str2octs('test') + assert len(s) == 1 + assert s.isValue + + s.clear() + + assert len(s) == 0 + assert s == [] + assert s.isValue + + def testReset(self): + + class SequenceOf(univ.SequenceOf): + componentType = univ.OctetString() + + s = SequenceOf() + s.setComponentByPosition(0, 'test') + + assert s.getComponentByPosition(0) == str2octs('test') + assert s.isValue + + s.reset() + + assert not s.isValue + + def testIsInconsistentSizeConstraint(self): + + class SequenceOf(univ.SequenceOf): + componentType = univ.OctetString() + subtypeSpec = constraint.ValueSizeConstraint(0, 1) + + s = SequenceOf() + + assert s.isInconsistent + + s[0] = 'test' + + assert not s.isInconsistent + + s[0] = 'test' + s[1] = 'test' + + assert s.isInconsistent + + s.clear() + + assert not s.isInconsistent + + s.reset() + + assert s.isInconsistent + + s[1] = 'test' + + assert not s.isInconsistent + + +class SequenceOfPicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = univ.SequenceOf(componentType=univ.OctetString()) + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == univ.SequenceOf + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = univ.SequenceOf(componentType=univ.OctetString()) + old_asn1[0] = 'test' + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 + assert new_asn1 == [str2octs('test')] + + +class Sequence(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s1 = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString('')), + namedtype.OptionalNamedType('nick', univ.OctetString('')), + namedtype.DefaultedNamedType('age', univ.Integer(34)) + ) + ) + + def testRepr(self): + assert 'name' in repr(self.s1.clone().setComponents('a', 'b')) + + def testTag(self): + assert self.s1.tagSet == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) + ), 'wrong tagSet' + + def testById(self): + self.s1.setComponentByName('name', univ.OctetString('abc')) + assert self.s1.getComponentByName('name') == str2octs('abc'), 'set by name fails' + + def testByKey(self): + self.s1['name'] = 'abc' + assert self.s1['name'] == str2octs('abc'), 'set by key fails' + + def testContains(self): + assert 'name' in self.s1 + assert '<missing>' not in self.s1 + + def testGetNearPosition(self): + assert self.s1.componentType.getTagMapNearPosition(1).presentTypes == { + univ.OctetString.tagSet: univ.OctetString(''), + univ.Integer.tagSet: univ.Integer(34) + } + assert self.s1.componentType.getPositionNearType( + univ.OctetString.tagSet, 1 + ) == 1 + + def testSetDefaultComponents(self): + self.s1.clear() + self.s1.setComponentByPosition(0, univ.OctetString('Ping')) + self.s1.setComponentByPosition(1, univ.OctetString('Pong')) + assert self.s1.getComponentByPosition(2) == 34 + + def testClone(self): + self.s1.setComponentByPosition(0, univ.OctetString('abc')) + self.s1.setComponentByPosition(1, univ.OctetString('def')) + self.s1.setComponentByPosition(2, univ.Integer(123)) + s = self.s1.clone() + assert s.getComponentByPosition(0) != self.s1.getComponentByPosition(0) + assert s.getComponentByPosition(1) != self.s1.getComponentByPosition(1) + assert s.getComponentByPosition(2) != self.s1.getComponentByPosition(2) + s = self.s1.clone(cloneValueFlag=1) + assert s.getComponentByPosition(0) == self.s1.getComponentByPosition(0) + assert s.getComponentByPosition(1) == self.s1.getComponentByPosition(1) + assert s.getComponentByPosition(2) == self.s1.getComponentByPosition(2) + + def testComponentTagsMatching(self): + s = self.s1.clone() + s.strictConstraints = True # This requires types equality + o = univ.OctetString('abc').subtype(explicitTag=tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 12)) + try: + s.setComponentByName('name', o) + except PyAsn1Error: + pass + else: + assert 0, 'inner supertype tag allowed' + + def testComponentConstraintsMatching(self): + s = self.s1.clone() + o = univ.OctetString().subtype( + subtypeSpec=constraint.ConstraintsUnion(constraint.SingleValueConstraint(str2octs('cba')))) + s.strictConstraints = True # This requires types equality + try: + s.setComponentByName('name', o.clone('cba')) + except PyAsn1Error: + pass + else: + assert 0, 'inner supertype constraint allowed' + s.strictConstraints = False # This requires subtype relationships + try: + s.setComponentByName('name', o.clone('cba')) + except PyAsn1Error: + assert 0, 'inner supertype constraint disallowed' + else: + pass + + def testSetComponents(self): + assert self.s1.clone().setComponents(name='a', nick='b', age=1) == \ + self.s1.setComponentByPosition(0, 'a').setComponentByPosition(1, 'b').setComponentByPosition(2, 1) + + def testSetToDefault(self): + s = self.s1.clone() + s.setComponentByPosition(0, univ.noValue) + s[2] = univ.noValue + assert s[0] == univ.OctetString('') + assert s[2] == univ.Integer(34) + + def testGetItem(self): + s = self.s1.clone() + s['name'] = 'xxx' + assert s['name'] + assert s[0] + + try: + s['xxx'] + + except KeyError: + pass + + else: + assert False, 'KeyError not raised' + + try: + s[100] + + except IndexError: + pass + + else: + assert False, 'IndexError not raised' + + def testSetItem(self): + s = self.s1.clone() + s['name'] = 'xxx' + + try: + + s['xxx'] = 'xxx' + + except KeyError: + pass + + else: + assert False, 'KeyError not raised' + + try: + + s[100] = 'xxx' + + except IndexError: + pass + + else: + assert False, 'IndexError not raised' + + def testIter(self): + assert list(self.s1) == ['name', 'nick', 'age'] + + def testKeys(self): + self.s1.setComponentByPosition(0, univ.OctetString('abc')) + self.s1.setComponentByPosition(1, univ.OctetString('def')) + self.s1.setComponentByPosition(2, univ.Integer(123)) + assert list(self.s1.keys()) == ['name', 'nick', 'age'] + + def testValues(self): + self.s1.setComponentByPosition(0, univ.OctetString('abc')) + self.s1.setComponentByPosition(1, univ.OctetString('def')) + self.s1.setComponentByPosition(2, univ.Integer(123)) + assert list(self.s1.values()) == [str2octs('abc'), str2octs('def'), 123] + + def testItems(self): + self.s1.setComponentByPosition(0, univ.OctetString('abc')) + self.s1.setComponentByPosition(1, univ.OctetString('def')) + self.s1.setComponentByPosition(2, univ.Integer(123)) + assert list(self.s1.items()) == [(x[0], str2octs(x[1])) for x in [('name', 'abc'), ('nick', 'def')]] + [('age', 123)] + + def testUpdate(self): + self.s1.clear() + assert list(self.s1.values()) == [str2octs(''), str2octs(''), 34] + self.s1.update(**{'name': 'abc', 'nick': 'def', 'age': 123}) + assert list(self.s1.items()) == [(x[0], str2octs(x[1])) for x in [('name', 'abc'), ('nick', 'def')]] + [('age', 123)] + self.s1.update(('name', 'ABC')) + assert list(self.s1.items()) == [(x[0], str2octs(x[1])) for x in [('name', 'ABC'), ('nick', 'def')]] + [('age', 123)] + self.s1.update(name='CBA') + assert list(self.s1.items()) == [(x[0], str2octs(x[1])) for x in [('name', 'CBA'), ('nick', 'def')]] + [('age', 123)] + + def testStaticDef(self): + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString('')), + namedtype.OptionalNamedType('nick', univ.OctetString('')), + namedtype.DefaultedNamedType('age', univ.Integer(34)) + ) + + s = Sequence() + s['name'] = 'abc' + assert s['name'] == str2octs('abc') + + def testGetComponentWithDefault(self): + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString('')), + namedtype.OptionalNamedType('nick', univ.OctetString()), + ) + + s = Sequence() + + assert s[0] == str2octs('') + assert s.getComponentByPosition(1, default=None, instantiate=False) is None + assert s.getComponentByName('nick', default=None) is None + s[1] = 'test' + assert s.getComponentByPosition(1, default=None) is not None + assert s.getComponentByPosition(1, default=None) == str2octs('test') + s.clear() + assert s.getComponentByPosition(1, default=None) is None + + def testGetComponentWithConstructedDefault(self): + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.DefaultedNamedType('nick', univ.SequenceOf( + componentType=univ.Integer() + ).setComponentByPosition(0, 1)), + ) + + s = Sequence() + + assert s.getComponentByPosition(1, default=None, instantiate=False) is None + assert s.getComponentByPosition(1, instantiate=False) is univ.noValue + assert s.getComponentByPosition(1) == [1] + + def testGetComponentNoInstantiation(self): + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString('')), + namedtype.OptionalNamedType('nick', univ.OctetString()), + ) + + s = Sequence() + assert s[0] == str2octs('') + assert s.getComponentByPosition(1, instantiate=False) is univ.noValue + assert s.getComponentByName('nick', instantiate=False) is univ.noValue + s[1] = 'test' + assert s.getComponentByPosition(1, instantiate=False) is not univ.noValue + assert s.getComponentByPosition(1, instantiate=False) == str2octs('test') + s.clear() + assert s.getComponentByPosition(1, instantiate=False) is univ.noValue + + def testSchemaWithComponents(self): + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()) + ) + + s = Sequence() + + assert not s.isValue + + s[0] = 'test' + + assert s.isValue + + s.clear() + + assert not s.isValue + + s.reset() + + assert not s.isValue + + def testSchemaWithOptionalComponents(self): + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('name', univ.OctetString()) + ) + + s = Sequence() + + assert s.isValue + + s[0] = 'test' + + assert s.isValue + + s.clear() + + assert s.isValue + + s.reset() + + assert not s.isValue + + def testSchemaWithOptionalComponents(self): + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('name', univ.OctetString('')) + ) + + s = Sequence() + + assert s.isValue + + s[0] = 'test' + + assert s.isValue + + s.clear() + + assert s.isValue + + s.reset() + + assert not s.isValue + + def testIsInconsistentWithComponentsConstraint(self): + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(65)) + ) + subtypeSpec = constraint.WithComponentsConstraint( + ('name', constraint.ComponentPresentConstraint()), + ('age', constraint.ComponentAbsentConstraint()) + ) + + s = Sequence() + + assert s.isInconsistent + + s[0] = 'test' + + assert not s.isInconsistent + + s[0] = 'test' + s[1] = 23 + + assert s.isInconsistent + + s.clear() + + assert s.isInconsistent + + s.reset() + + assert s.isInconsistent + + s[1] = 23 + + assert s.isInconsistent + + def testIsInconsistentSizeConstraint(self): + + class Sequence(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(65)) + ) + subtypeSpec = constraint.ValueSizeConstraint(0, 1) + + s = Sequence() + + assert not s.isInconsistent + + s[0] = 'test' + + assert not s.isInconsistent + + s[0] = 'test' + s[1] = 23 + + assert s.isInconsistent + + s.clear() + + assert not s.isInconsistent + + s.reset() + + assert s.isInconsistent + + s[1] = 23 + + assert not s.isInconsistent + + +class SequenceWithoutSchema(BaseTestCase): + + def testGetItem(self): + s = univ.Sequence() + s.setComponentByPosition(0, univ.OctetString('abc')) + s[0] = 'abc' + assert s['field-0'] + assert s[0] + + try: + s['field-1'] + + except KeyError: + pass + + else: + assert False, 'KeyError not raised' + + def testSetItem(self): + s = univ.Sequence() + s.setComponentByPosition(0, univ.OctetString('abc')) + s['field-0'] = 'xxx' + + try: + + s['field-1'] = 'xxx' + + except KeyError: + pass + + else: + assert False, 'KeyError not raised' + + def testIter(self): + s = univ.Sequence() + s.setComponentByPosition(0, univ.OctetString('abc')) + s.setComponentByPosition(1, univ.Integer(123)) + assert list(s) == ['field-0', 'field-1'] + + def testKeys(self): + s = univ.Sequence() + s.setComponentByPosition(0, univ.OctetString('abc')) + s.setComponentByPosition(1, univ.Integer(123)) + assert list(s.keys()) == ['field-0', 'field-1'] + + def testValues(self): + s = univ.Sequence() + s.setComponentByPosition(0, univ.OctetString('abc')) + s.setComponentByPosition(1, univ.Integer(123)) + assert list(s.values()) == [str2octs('abc'), 123] + + def testItems(self): + s = univ.Sequence() + s.setComponentByPosition(0, univ.OctetString('abc')) + s.setComponentByPosition(1, univ.Integer(123)) + assert list(s.items()) == [('field-0', str2octs('abc')), ('field-1', 123)] + + def testUpdate(self): + s = univ.Sequence().clear() + assert not s + s.setComponentByPosition(0, univ.OctetString('abc')) + s.setComponentByPosition(1, univ.Integer(123)) + assert s + assert list(s.keys()) == ['field-0', 'field-1'] + assert list(s.values()) == [str2octs('abc'), 123] + assert list(s.items()) == [('field-0', str2octs('abc')), ('field-1', 123)] + s['field-0'] = univ.OctetString('def') + assert list(s.values()) == [str2octs('def'), 123] + s['field-1'] = univ.OctetString('ghi') + assert list(s.values()) == [str2octs('def'), str2octs('ghi')] + try: + s['field-2'] = univ.OctetString('xxx') + except KeyError: + pass + else: + assert False, 'unknown field at schema-less object tolerated' + assert 'field-0' in s + s.clear() + assert 'field-0' not in s + + def testSchema(self): + + class Sequence(univ.Sequence): + pass + + s = Sequence() + + assert not s.isValue + + s[0] = univ.OctetString('test') + + assert s.isValue + + s.clear() + + assert s.isValue + + s.reset() + + assert not s.isValue + + +class SequencePicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()) + ) + ) + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == univ.Sequence + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = univ.Sequence( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()) + ) + ) + old_asn1['name'] = 'test' + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 + assert new_asn1['name'] == str2octs('test') + + +class SetOf(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + self.s1 = univ.SetOf(componentType=univ.OctetString('')) + + def testTag(self): + assert self.s1.tagSet == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) + ), 'wrong tagSet' + + def testSeq(self): + self.s1.setComponentByPosition(0, univ.OctetString('abc')) + assert self.s1[0] == str2octs('abc'), 'set by idx fails' + self.s1.setComponentByPosition(0, self.s1[0].clone('cba')) + assert self.s1[0] == str2octs('cba'), 'set by idx fails' + + def testStaticDef(self): + + class SetOf(univ.SequenceOf): + componentType = univ.OctetString('') + + s = SetOf() + s[0] = 'abc' + assert len(s) == 1 + assert s == [str2octs('abc')] + + + +class SetOfPicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = univ.SetOf(componentType=univ.OctetString()) + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == univ.SetOf + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = univ.SetOf(componentType=univ.OctetString()) + old_asn1[0] = 'test' + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 + assert new_asn1 == [str2octs('test')] + + +class Set(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + self.s1 = univ.Set( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString('')), + namedtype.OptionalNamedType('null', univ.Null('')), + namedtype.DefaultedNamedType('age', univ.Integer(34)) + ) + ) + self.s2 = self.s1.clone() + + def testTag(self): + assert self.s1.tagSet == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) + ), 'wrong tagSet' + + def testByTypeWithPythonValue(self): + self.s1.setComponentByType(univ.OctetString.tagSet, 'abc') + assert self.s1.getComponentByType( + univ.OctetString.tagSet + ) == str2octs('abc'), 'set by name fails' + + def testByTypeWithInstance(self): + self.s1.setComponentByType(univ.OctetString.tagSet, univ.OctetString('abc')) + assert self.s1.getComponentByType( + univ.OctetString.tagSet + ) == str2octs('abc'), 'set by name fails' + + def testGetTagMap(self): + assert self.s1.tagMap.presentTypes == { + univ.Set.tagSet: univ.Set().clear() + } + + def testGetComponentTagMap(self): + assert self.s1.componentType.tagMapUnique.presentTypes == { + univ.OctetString.tagSet: univ.OctetString(''), + univ.Null.tagSet: univ.Null(''), + univ.Integer.tagSet: univ.Integer(34) + } + + def testGetPositionByType(self): + assert self.s1.componentType.getPositionByType(univ.Null().tagSet) == 1 + + def testSetToDefault(self): + self.s1.setComponentByName('name', univ.noValue) + assert self.s1['name'] == univ.OctetString('') + + def testIter(self): + assert list(self.s1) == ['name', 'null', 'age'] + + def testStaticDef(self): + + class Set(univ.Set): + componentType = namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString('')), + namedtype.OptionalNamedType('nick', univ.OctetString('')), + namedtype.DefaultedNamedType('age', univ.Integer(34)) + ) + + s = Set() + s['name'] = 'abc' + assert s['name'] == str2octs('abc') + + def testGetComponentWithDefault(self): + + class Set(univ.Set): + componentType = namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer(123)), + namedtype.OptionalNamedType('nick', univ.OctetString()), + ) + + s = Set() + assert s[0] == 123 + assert s.getComponentByPosition(1, default=None, instantiate=False) is None + assert s.getComponentByName('nick', default=None) is None + s[1] = 'test' + assert s.getComponentByPosition(1, default=None) is not None + assert s.getComponentByPosition(1, default=None) == str2octs('test') + s.clear() + assert s.getComponentByPosition(1, default=None) is None + + def testGetComponentNoInstantiation(self): + + class Set(univ.Set): + componentType = namedtype.NamedTypes( + namedtype.NamedType('id', univ.Integer(123)), + namedtype.OptionalNamedType('nick', univ.OctetString()), + ) + + s = Set() + assert s[0] == 123 + assert s.getComponentByPosition(1, instantiate=False) is univ.noValue + assert s.getComponentByName('nick', instantiate=False) is univ.noValue + assert s.getComponentByType(univ.OctetString.tagSet, instantiate=False) is univ.noValue + s[1] = 'test' + assert s.getComponentByPosition(1, instantiate=False) is not univ.noValue + assert s.getComponentByPosition(1, instantiate=False) == str2octs('test') + s.clear() + assert s.getComponentByPosition(1, instantiate=False) is univ.noValue + + +class SetPicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = univ.Set( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()) + ) + ) + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == univ.Set + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = univ.Set( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()) + ) + ) + old_asn1['name'] = 'test' + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 + assert new_asn1['name'] == str2octs('test') + + +class Choice(BaseTestCase): + def setUp(self): + BaseTestCase.setUp(self) + + innerComp = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('count', univ.Integer()), + namedtype.NamedType('flag', univ.Boolean()) + ) + ) + self.s1 = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('sex', innerComp) + ) + ) + + def testTag(self): + assert self.s1.tagSet == tag.TagSet(), 'wrong tagSet' + + def testRepr(self): + assert 'Choice' in repr(self.s1.clone().setComponents('a')) + s = self.s1.clone().setComponents( + sex=self.s1.setComponentByPosition(1).getComponentByPosition(1).clone().setComponents(count=univ.Integer(123)) + ) + assert 'Choice' in repr(s) + + def testContains(self): + self.s1.setComponentByType(univ.OctetString.tagSet, 'abc') + assert 'name' in self.s1 + assert 'sex' not in self.s1 + + self.s1.setComponentByType(univ.Integer.tagSet, 123, innerFlag=True) + assert 'name' not in self.s1 + assert 'sex' in self.s1 + + def testIter(self): + self.s1.setComponentByType(univ.OctetString.tagSet, 'abc') + assert list(self.s1) == ['name'] + self.s1.setComponentByType(univ.Integer.tagSet, 123, innerFlag=True) + assert list(self.s1) == ['sex'] + + def testOuterByTypeWithPythonValue(self): + self.s1.setComponentByType(univ.OctetString.tagSet, 'abc') + assert self.s1.getComponentByType( + univ.OctetString.tagSet + ) == str2octs('abc') + + def testOuterByTypeWithInstanceValue(self): + self.s1.setComponentByType( + univ.OctetString.tagSet, univ.OctetString('abc') + ) + assert self.s1.getComponentByType( + univ.OctetString.tagSet + ) == str2octs('abc') + + def testInnerByTypeWithPythonValue(self): + self.s1.setComponentByType(univ.Integer.tagSet, 123, innerFlag=True) + assert self.s1.getComponentByType( + univ.Integer.tagSet, 1 + ) == 123 + + def testInnerByTypeWithInstanceValue(self): + self.s1.setComponentByType( + univ.Integer.tagSet, univ.Integer(123), innerFlag=True + ) + assert self.s1.getComponentByType( + univ.Integer.tagSet, 1 + ) == 123 + + def testCmp(self): + self.s1.setComponentByName('name', univ.OctetString('abc')) + assert self.s1 == str2octs('abc'), '__cmp__() fails' + + def testGetComponent(self): + self.s1.setComponentByType(univ.OctetString.tagSet, 'abc') + assert self.s1.getComponent() == str2octs('abc'), 'getComponent() fails' + + def testGetName(self): + self.s1.setComponentByType(univ.OctetString.tagSet, 'abc') + assert self.s1.getName() == 'name', 'getName() fails' + + def testSetComponentByPosition(self): + self.s1.setComponentByPosition(0, univ.OctetString('Jim')) + assert self.s1 == str2octs('Jim') + + def testClone(self): + self.s1.setComponentByPosition(0, univ.OctetString('abc')) + s = self.s1.clone() + assert len(s) == 0 + s = self.s1.clone(cloneValueFlag=1) + assert len(s) == 1 + assert s.getComponentByPosition(0) == self.s1.getComponentByPosition(0) + + def testSetToDefault(self): + s = self.s1.clone() + s.setComponentByName('sex', univ.noValue) + assert s['sex'] is not univ.noValue + + def testStaticDef(self): + + class InnerChoice(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('count', univ.Integer()), + namedtype.NamedType('flag', univ.Boolean()) + ) + + class OuterChoice(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('sex', InnerChoice()) + ) + + c = OuterChoice() + + c.setComponentByType(univ.OctetString.tagSet, 'abc') + assert c.getName() == 'name' + + def testGetComponentWithDefault(self): + + s = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('id', univ.Integer()) + ) + ) + + assert s.getComponentByPosition(0, default=None, instantiate=False) is None + assert s.getComponentByPosition(1, default=None, instantiate=False) is None + assert s.getComponentByName('name', default=None, instantiate=False) is None + assert s.getComponentByName('id', default=None, instantiate=False) is None + assert s.getComponentByType(univ.OctetString.tagSet, default=None) is None + assert s.getComponentByType(univ.Integer.tagSet, default=None) is None + s[1] = 123 + assert s.getComponentByPosition(1, default=None) is not None + assert s.getComponentByPosition(1, univ.noValue) == 123 + s.clear() + assert s.getComponentByPosition(1, default=None, instantiate=False) is None + + def testGetComponentNoInstantiation(self): + + s = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('id', univ.Integer()) + ) + ) + + assert s.getComponentByPosition(0, instantiate=False) is univ.noValue + assert s.getComponentByPosition(1, instantiate=False) is univ.noValue + assert s.getComponentByName('name', instantiate=False) is univ.noValue + assert s.getComponentByName('id', instantiate=False) is univ.noValue + assert s.getComponentByType(univ.OctetString.tagSet, instantiate=False) is univ.noValue + assert s.getComponentByType(univ.Integer.tagSet, instantiate=False) is univ.noValue + s[1] = 123 + assert s.getComponentByPosition(1, instantiate=False) is not univ.noValue + assert s.getComponentByPosition(1, instantiate=False) == 123 + s.clear() + assert s.getComponentByPosition(1, instantiate=False) is univ.noValue + + +class ChoicePicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('id', univ.Integer()) + ) + ) + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == univ.Choice + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = univ.Choice( + componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('id', univ.Integer()) + ) + ) + old_asn1['name'] = 'test' + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 + assert new_asn1['name'] == str2octs('test') + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/type/test_useful.py b/contrib/python/pyasn1/py3/tests/type/test_useful.py new file mode 100644 index 0000000000..cd5ba566f9 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/type/test_useful.py @@ -0,0 +1,138 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com> +# License: https://pyasn1.readthedocs.io/en/latest/license.html +# +import datetime +import pickle +import sys +from copy import deepcopy +import unittest + +from __tests__.base import BaseTestCase + +from pyasn1.type import useful + + +class FixedOffset(datetime.tzinfo): + def __init__(self, offset, name): + self.__offset = datetime.timedelta(minutes=offset) + self.__name = name + + def utcoffset(self, dt): + return self.__offset + + def tzname(self, dt): + return self.__name + + def dst(self, dt): + return datetime.timedelta(0) + + +UTC = FixedOffset(0, 'UTC') +UTC2 = FixedOffset(120, 'UTC') + + +class ObjectDescriptorTestCase(BaseTestCase): + pass + + +class GeneralizedTimeTestCase(BaseTestCase): + + def testFromDateTime(self): + assert useful.GeneralizedTime.fromDateTime(datetime.datetime(2017, 7, 11, 0, 1, 2, 3000, tzinfo=UTC)) == '20170711000102.3Z' + + def testToDateTime0(self): + assert datetime.datetime(2017, 7, 11, 0, 1, 2) == useful.GeneralizedTime('20170711000102').asDateTime + + def testToDateTime1(self): + assert datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC) == useful.GeneralizedTime('20170711000102Z').asDateTime + + def testToDateTime2(self): + assert datetime.datetime(2017, 7, 11, 0, 1, 2, 3000, tzinfo=UTC) == useful.GeneralizedTime('20170711000102.3Z').asDateTime + + def testToDateTime3(self): + assert datetime.datetime(2017, 7, 11, 0, 1, 2, 3000, tzinfo=UTC) == useful.GeneralizedTime('20170711000102,3Z').asDateTime + + def testToDateTime4(self): + assert datetime.datetime(2017, 7, 11, 0, 1, 2, 3000, tzinfo=UTC) == useful.GeneralizedTime('20170711000102.3+0000').asDateTime + + def testToDateTime5(self): + assert datetime.datetime(2017, 7, 11, 0, 1, 2, 3000, tzinfo=UTC2) == useful.GeneralizedTime('20170711000102.3+0200').asDateTime + + def testToDateTime6(self): + assert datetime.datetime(2017, 7, 11, 0, 1, 2, 3000, tzinfo=UTC2) == useful.GeneralizedTime('20170711000102.3+02').asDateTime + + def testToDateTime7(self): + assert datetime.datetime(2017, 7, 11, 0, 1) == useful.GeneralizedTime('201707110001').asDateTime + + def testToDateTime8(self): + assert datetime.datetime(2017, 7, 11, 0) == useful.GeneralizedTime('2017071100').asDateTime + + def testCopy(self): + dt = useful.GeneralizedTime("20170916234254+0130").asDateTime + assert dt == deepcopy(dt) + + +class GeneralizedTimePicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = useful.GeneralizedTime() + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == useful.GeneralizedTime + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = useful.GeneralizedTime("20170916234254+0130") + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 == old_asn1 + + +class UTCTimeTestCase(BaseTestCase): + + def testFromDateTime(self): + assert useful.UTCTime.fromDateTime(datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC)) == '170711000102Z' + + def testToDateTime0(self): + assert datetime.datetime(2017, 7, 11, 0, 1, 2) == useful.UTCTime('170711000102').asDateTime + + def testToDateTime1(self): + assert datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC) == useful.UTCTime('170711000102Z').asDateTime + + def testToDateTime2(self): + assert datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC) == useful.UTCTime('170711000102+0000').asDateTime + + def testToDateTime3(self): + assert datetime.datetime(2017, 7, 11, 0, 1, 2, tzinfo=UTC2) == useful.UTCTime('170711000102+0200').asDateTime + + def testToDateTime4(self): + assert datetime.datetime(2017, 7, 11, 0, 1) == useful.UTCTime('1707110001').asDateTime + + +class UTCTimePicklingTestCase(unittest.TestCase): + + def testSchemaPickling(self): + old_asn1 = useful.UTCTime() + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert type(new_asn1) == useful.UTCTime + assert old_asn1.isSameTypeWith(new_asn1) + + def testValuePickling(self): + old_asn1 = useful.UTCTime("170711000102") + serialised = pickle.dumps(old_asn1) + assert serialised + new_asn1 = pickle.loads(serialised) + assert new_asn1 == old_asn1 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/contrib/python/pyasn1/py3/tests/ya.make b/contrib/python/pyasn1/py3/tests/ya.make new file mode 100644 index 0000000000..95837ba4a4 --- /dev/null +++ b/contrib/python/pyasn1/py3/tests/ya.make @@ -0,0 +1,41 @@ +PY3TEST() + +PEERDIR( + contrib/python/pyasn1 +) + +TEST_SRCS( + __init__.py + base.py + codec/__init__.py + codec/ber/__init__.py + codec/ber/test_decoder.py + codec/ber/test_encoder.py + codec/cer/__init__.py + codec/cer/test_decoder.py + codec/cer/test_encoder.py + codec/der/__init__.py + codec/der/test_decoder.py + codec/der/test_encoder.py + codec/native/__init__.py + codec/native/test_decoder.py + codec/native/test_encoder.py + codec/test_streaming.py + compat/__init__.py + compat/test_integer.py + compat/test_octets.py + test_debug.py + type/__init__.py + type/test_char.py + type/test_constraint.py + type/test_namedtype.py + type/test_namedval.py + type/test_opentype.py + type/test_tag.py + type/test_univ.py + type/test_useful.py +) + +NO_LINT() + +END() diff --git a/contrib/python/pyasn1/py3/ya.make b/contrib/python/pyasn1/py3/ya.make new file mode 100644 index 0000000000..772312ad0e --- /dev/null +++ b/contrib/python/pyasn1/py3/ya.make @@ -0,0 +1,58 @@ +# Generated by devtools/yamaker (pypi). + +PY3_LIBRARY() + +VERSION(0.5.0) + +LICENSE(BSD-3-Clause) + +NO_LINT() + +PY_SRCS( + TOP_LEVEL + pyasn1/__init__.py + pyasn1/codec/__init__.py + pyasn1/codec/ber/__init__.py + pyasn1/codec/ber/decoder.py + pyasn1/codec/ber/encoder.py + pyasn1/codec/ber/eoo.py + pyasn1/codec/cer/__init__.py + pyasn1/codec/cer/decoder.py + pyasn1/codec/cer/encoder.py + pyasn1/codec/der/__init__.py + pyasn1/codec/der/decoder.py + pyasn1/codec/der/encoder.py + pyasn1/codec/native/__init__.py + pyasn1/codec/native/decoder.py + pyasn1/codec/native/encoder.py + pyasn1/codec/streaming.py + pyasn1/compat/__init__.py + pyasn1/compat/integer.py + pyasn1/compat/octets.py + pyasn1/debug.py + pyasn1/error.py + pyasn1/type/__init__.py + pyasn1/type/base.py + pyasn1/type/char.py + pyasn1/type/constraint.py + pyasn1/type/error.py + pyasn1/type/namedtype.py + pyasn1/type/namedval.py + pyasn1/type/opentype.py + pyasn1/type/tag.py + pyasn1/type/tagmap.py + pyasn1/type/univ.py + pyasn1/type/useful.py +) + +RESOURCE_FILES( + PREFIX contrib/python/pyasn1/py3/ + .dist-info/METADATA + .dist-info/top_level.txt +) + +END() + +RECURSE_FOR_TESTS( + tests +) |