diff options
53 files changed, 4749 insertions, 0 deletions
@@ -0,0 +1,9 @@ +Wed Apr 6 18:48:40 MSD 2005 +---------------------------- + +* error.Asn1Error replaced with error.PyAsn1Error + +Mon Apr 4 08:37:02 MSD 2005 +---------------------------- + +* Initial public alpha release @@ -0,0 +1,28 @@ +Copyright (c) 2005, Ilya Etingof <ilya@glas.net>, all rights reserved. + +THIS SOFTWARE IS NOT FAULT TOLERANT AND SHOULD NOT BE USED IN ANY SITUATION +ENDANGERING HUMAN LIFE OR PROPERTY. + +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. + + * The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +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 REGENTS 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/MANIFEST b/MANIFEST new file mode 100644 index 0000000..b92d5c8 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,54 @@ +CHANGES +MANIFEST +README +LICENSE +doc/notes.html +pyasn1/v1/__init__.py +pyasn1/v1/error.py +pyasn1/v1/codec/ber/decoder.py +pyasn1/v1/codec/ber/encoder.py +pyasn1/v1/codec/ber/__init__.py +pyasn1/v1/codec/ber/eoo.py +pyasn1/v1/codec/cer/__init__.py +pyasn1/v1/codec/cer/encoder.py +pyasn1/v1/codec/cer/decoder.py +pyasn1/v1/codec/der/__init__.py +pyasn1/v1/codec/der/decoder.py +pyasn1/v1/codec/der/encoder.py +pyasn1/v1/codec/__init__.py +pyasn1/v1/type/base.py +pyasn1/v1/type/tag.py +pyasn1/v1/type/univ.py +pyasn1/v1/type/__init__.py +pyasn1/v1/type/namedtype.py +pyasn1/v1/type/namedval.py +pyasn1/v1/type/error.py +pyasn1/v1/type/constraint.py +pyasn1/v1/type/char.py +pyasn1/v1/type/useful.py +pyasn1/__init__.py +test/__init__.py +test/suite.py +test/codec/ber/__init__.py +test/codec/ber/encoder.py +test/codec/ber/suite.py +test/codec/ber/decoder.py +test/codec/__init__.py +test/codec/cer/__init__.py +test/codec/cer/encoder.py +test/codec/cer/suite.py +test/codec/cer/decoder.py +test/codec/der/__init__.py +test/codec/der/encoder.py +test/codec/der/suite.py +test/codec/der/decoder.py +test/type/univ.py +test/type/__init__.py +test/type/suite.py +test/type/tag.py +test/type/constraint.py +test/type/namedtype.py +examples/x509.py +examples/snmp.py +TODO +setup.py @@ -0,0 +1,69 @@ + +ASN.1 library for Python +------------------------ + +This is an implementation of ASN.1 types and codecs in Python programming +language. It has been first written to support particular protocol (SNMP) +but then generalized to be suitable for a wide range of protocols +based on ASN.1 specification. + +WARNING! WARNING! WARNING! +-------------------------- + +This is an alpha release. The code is not too reliable and API is not +quite stable. Though, dramatic changes to the API are not expected. + +FEATURES +-------- + +* Generic implementation of ASN.1 types (X.208) +* Fully standard compliant BER/CER/DER codecs +* 100% Python, works with Python 1.5 and later +* MT-safe + +MISFEATURES +----------- + +* No ASN.1 compiler (by-hand ASN.1 spec compilation into Python code required) +* Codecs are not restartable + +INSTALLATION +------------ + +The pyasn1 package uses distutils for installation: + +$ tar zxf pyasn1-0.0.1a.tar.gz +$ cd pyasn1-0.0.1a +$ python setup.py install +$ cd test +$ python suite.py # to make sure everything works alright + +OPERATION +--------- + +Perhaps a typical use would involve [by-hand] compilation of your ASN.1 +specification into pyasn1-backed Python code at your application. + +For more information on pyasn1 APIs, please, refer to the doc/notes.html +file in the distribution. + +Also refer to example modules -- one of them (x509.py) handles SSL +certificate format. + +AVAILABILITY +------------ + +The pyasn1 package is distributed under terms and conditions of BSD-style +license. See LICENSE file in the distribution. Source code is freely +available from: + +http://pyasn1.sf.net + + +FEEDBACK +-------- + +Please, send your comments and fixes to mailing lists at project web site. + +=-=-= +mailto: ilya@glas.net @@ -0,0 +1,44 @@ + +* Implement the rest of ASN.1 types (Real etc) +* Support large tag numbers at codecs +* Specialize ASN.1 character and useful types +* Come up with simpler API for deeply nested constructed objects + addressing + +ber.decoder: +* suspend codec on underrun error ? +* class-static components map (in simple type classes) +* verify error cases in test suite +* present subtypes ? +* component presence check wont work at innertypeconst +* optimize en/decoders +* add the rest of ASN1 types/codecs +* type vs value, defaultValue + +ber.encoder: +* Asn1Item.clone() / shallowcopy issue +* large tags encoder +* large length encoder? +* codec restart +* preserve compatible API whenever stateful codec gets implemented +* restartable vs incremental +* plan: make a stateless univeral decoder, then convert it to restartable + then to incremental + +type.useful: +* may need to implement prettyIn/Out + +type.char: +* may need to implement constraints + +type.univ: +* simpler API to constructed objects: value init, recursive + +type.namedtypes +* type vs tagset name convention + +general: + +* profile +* windows +* how untagged TagSet should be initialized? diff --git a/doc/notes.html b/doc/notes.html new file mode 100644 index 0000000..5b0eab3 --- /dev/null +++ b/doc/notes.html @@ -0,0 +1,382 @@ +<html> +<title> +ASN.1 tools for Python +</title> +<body> +<center> +<table width=70%> +<tr> +<td> +<h3> +ASN.1 tools for Python +</h3> + +<p> +Whenever data structures are described in some machine and programming language +independent and unambiguous way, such specification is called +<a href=http://en.wikipedia.org/wiki/Abstract_syntax>abstract syntax</a>, +by contrast with machine/language specific methods, which are called 'concrete' +or 'transfer' syntaxes. +</p> + +<p> +Abstract syntaxes appear useful in networking as a tool for engineering +protocols in a clear and portable way. Moreover, once a protocol +is described in some abstract language, protocol parsers and builders +could be automatically generated for various computing +architectures/programming languages, thus saving engineers from implementing +low-level transport details by hand. +</p> + +<p> +Abstract Syntax Notation One ( +<a href=http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_1x>ASN.1</a> +) is a set of +<a href=http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-X.693-0207w.zip> +ITU standards</a> defining particular implementation of abstract data +description language accompanied by a collection of transfer encoding methods. +Perhaps the most widely used among these data serialization methods is Basic +Encoding Rules ( +<a href=http://en.wikipedia.org/wiki/Basic_encoding_rules>BER</a> +) together with its derivatives ( +<a href=http://en.wikipedia.org/wiki/Distinguished_encoding_rules>DER</a> +and +<a href=http://en.wikipedia.org/wiki/Canonical_encoding_rules>CER</a> +), while Packed Encoding Rules +<a href=http://en.wikipedia.org/wiki/Packed_encoding_rules>PER</a> +) aims at most compact data representation whilst in the wire. +</p> + +<p> +This project is dedicated to implementation of ASN.1 types (concrete +syntax) and codecs (transfer syntaxes) for Python programming environment. +ASN.1 compiler is planned for implementation in the future. +</p> + +<h3> +Data model for ASN.1 types +</h3> + +<p> +The ASN.1 standard defines a set of <i>primitive</i>, scalar data types +(such as Integer, String etc) and a few <i>constructed</i> types, each +holding one or many other ASN.1 types as its components (constructed +types may be viewed as Pascal "records" or C "struct"ures). +</p> + +<p> +In pyasn1, those primitive ASN.1 types are implemented as +immutable scalar objects. They could be used just like corresponding +native Python types (integers, strings etc). +</p> + +<pre> +>>> from pyasn1.type import univ +>>> univ.Integer(12) - 2 +10 +>>> univ.OctetString('abc') == 'abc' +True +>>> +</pre> + +<p> +In ASN.1, constructed types (Sequence, SequenceOf, Set, SetOf, Choice) +differ from each other by allowed components combination and ordering, which +also projects to component addressing methods. +</p> + +<p> +In this Python implementation, constructed ASN.1 types behave like +Python sequence, and also support additional component addressing methods, +specific to particular constructed type. +</p> + +<p> +Components of <i>Sequence</i>s can be addressed by their position in sequence: +</p> + +<pre> +>>> from pyasn1.type import univ, namedtype +>>> seq = univ.Sequence(componentType=namedtype.NamedTypes(namedtype.NamedType('version', univ.Integer()))) +>>> seq.setComponentByPosition(0, univ.Integer()) +>>> seq.getComponentByPosition(0) +Integer(0) +>>> +</pre> + +<p> +and by [textual] component type name (also valid for <i>Set</i> and +<i>Choice</i>), +</p> + +<pre> +>>> seq.getComponentByName('version') +Integer(0) +>>> +</pre> + +<p> +as well as by type for Set and Choice: +</p> + +<pre> +>>> set = univ.Set(componentType=namedtype.NamedTypes(namedtype.NamedType('version', univ.Integer()))) +>>> set.setComponentByPosition(0, univ.Integer()) +>>> set.getComponentByType(univ.Integer().getTagSet()) +Integer(0) +>>> +</pre> + +<p> +ASN.1 types are identified by a numeric ID called <i>tag</i>. In pyasn1, +tags are implemented as immutable objects referred by ASN.1 type objects: +</p> + +<pre> +from pyasn1.type import tag +>>> tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 3) +Tag(tagClass=0, tagFormat=0, tagId=3) +>>> +</pre> + +<p> +For the purpose of making same-typed objects distinguishable from one +another, the standard allows for assigning custom tags to +ASN.1 types. These <i>tagged types</i> preserve all properties of their +parent type but possess different IDs. +</p> + +<p> +There are two methods of tagging: <i>implicit</i> and <i>explicit</i>. The +first one replaces base tag with arbitrary custom tag thus dropping all +previously existing tag information for type: +</p> + +<pre> +>>> t = tag.TagSet(tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 3)) +>>> t.tagImplicitly(tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 32)) +TagSet(Tag(tagClass=192, tagFormat=0, tagId=32)) +>>> +</pre> + +<p> +The explicit tag is build by appending new custom tag to already +existing set of type's tags. Important property of explicit tagging +is that it preserves base type information. +</p> + +<pre> +>>> t = tag.TagSet(tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 3) +>>> t.tagExplicitly(tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 32)) +TagSet(Tag(tagClass=192, tagFormat=32, tagId=32), Tag(tagClass=0, tagFormat=0, tagId=3)) +>>> +</pre> + +<p> +Besides tags, certain restrictions may be put upon ASN.1 types' values thus +creating <i>subtypes</i> from base types (in computer science, a +<a href=http://en.wikipedia.org/wiki/Data_type>data type</a> +is a name of a collection of possible values). These restrictions are called +<i>subtype constraints</i> in the ASN.1 standard. +</p> + +<p> +Several different flavors of <i>constraints</i> exist. Some obvious +include <i>ValueRangeConstraint</i>, <i>ValueSizeConstraint</i> and others. +In pyasn1, constraints take shape of immutable objects capable +of evaluating given value against constraint's specific logic. +</p> + +<pre> +>>> from pyasn1.type import constraint +>>> constraint.ValueRangeConstraint(1,2) +ValueRangeConstraint(1,2) +>>> +</pre> + +<p> +Multiple constraints can be combined altogether into sets with three basic +boolean operations (<i>ConstraintsUnion</i>, <i>ConstraintsIntersection</i> and +<i>ConstraintsExclusion</i>), which could be applied recursively. +</p> + +<pre> +>>> c = constraint.ConstraintsUnion(constraint.SingleValueConstraint(4), constraint.ValueRangeConstraint(-1, 2)) +>>> c(1) +>>> c(3) +pyasn1.type.error.ValueConstraintError: ConstraintsUnion(SingleValueConstraint(4), ValueRangeConstraint(-1, 2)) failed at: all of (SingleValueConstraint(4), ValueRangeConstraint(-1, 2)) failed for 5 +>>> +</pre> + +<p> +A constrainted ASN.1 type would then hold a reference to a top-most constraint +object in a set and pass it a value, being assigned, for verification. +</p> + +<p> +By evaluating the inclusion of all tags and constraints of one type in +tag and constraint sets of another, it's possible to figure out the +relationships between types. By way of background, types matching is used +in constructed types for by-type component addressing. +</p> + +<pre> +>>> i1 = univ.Integer(subtypeSpec=constraint.SingleValueConstraint(0,3)) +>>> i2 = univ.Integer(subtypeSpec=constraint.ConstraintsIntersection(constraint.SingleValueConstraint(0,3), + constraint.SingleValueConstraint(6,8))) +>>> i1.isSameTypeWith(i2) +False +>>> i1.isSuperTypeOf(i2) +True +>>> +</pre> + +<p> +While complete documentation on the API to all these ASN.1 items is not +yet written, please, refer to example uses and source code for additional +information. +</p> + +<h3> +Codec notes +</h3> + +<p> +In ASN.1 context, +<a href=http://en.wikipedia.org/wiki/Codec>codec</a> +is a program that transforms between concrete data structures and a stream +of octets suitable for transmission over the wire. This serialized form of +data is sometimes called <i>substrate</i> or <i>essence</i>. +</p> + +<p> +One of the properties of a codec is its ability to cope with incomplete +data and/or substrate what implies codec to be stateful. In other words, +when decoder runs out of substrate and data item being recovered is still +incomplete, stateful codec would suspend and complete data item recovery +whenever the rest of substrate becomes available. Similarly, stateful encoder +would encode data items in multiple steps waiting for source data to +arrive. +</p> + +<p> +Codec restartability is especially important when application deals with large +volumes of data and/or runs on low RAM. +</p> + +<p> +For an interesting discussion on codecs options and design choices, refer to +<a href=http://directory.apache.org/subprojects/asn1/>Apache ASN.1 project</a> +. +</p> + +<p> +As of this writing, codecs implemented in pyasn1 are all stateless, mostly +to keep the code simple. +</p> + +<p> +The pyasn1 package currently supports BER codec and its derivates -- CER and +DER. Encoder is used for transforming ASN.1 object into substrate: +</p> + +<pre> +>>> from pyasn1.type import univ +>>> from pyasn1.codec.ber import encoder +>>> encoder.encode(univ.Integer(12)) +'\x02\x01\x0c' +>>> +</pre> + +<p> +while decoder recovers ASN.1 objects from substrate: +</p> + +<pre> +>>> from pyasn1.codec.ber import decoder +>>> decoder.decode('\x02\x01\x0c') +Integer(12), '' +>>> +</pre> + +<p> +Depending of encoding and tagging methods used, decoder may require +to know ASN.1 syntax of data structure to be decoded. For example, +PER-encoded or implicitly tagged values would not recover from substrate +without knowing ASN.1 syntax of encoded data. Whenever decoder is +given ASN.1 specification, this operation mode will be referred to as +<i>guided</i> throughout this document. +</p> + +<p> +The ASN.1 specification passed to decoder running in guided mode is simply +a reference to the top-most ASN.1 type object of the concrete specification. +Decoder would neither modify this specification object in any way nor use +its current values, but rather use it as a pattern when creating new objects: +</p> + +<pre> +>>> from pyasn1.codec.ber import decoder +>>> decoder.decode('\x02\x01\x0c', asn1Spec=univ.Integer()) +Integer(12), '' +>>> +</pre> + +<p> +One of the properties of BER codec is its use of either definite or indefinite +length specification for serialized data. Although indefinite length form +is especially important for stateful codec (which could produce&consume +substrate in chunks), pyasn1 codecs fully support both length forms. +</p> + +<p> +Constructed encoding is another feature of BER, closely related to indefinite +length form. In essence, large scalar value (such as ASN.1 <i>character</i> or +<i>BitString</i> type) could be chopped into smaller chunks by encoder and +transmitted incrementally. +</p> + +<p> +The following code would BER encode ASN.1 <i>OctetString</i> object using +constructed (chopped by 4th octet) and indefinite length form: +</p> + +<pre> +>>> from pyasn1.type import univ +>>> from pyasn1.codec.ber import encoder +>>> encoder.encode(univ.OctetString('Quick brown fox'), defMode=0, maxChunkSize=4) +'$\x80\x04\x04Quic\x04\x04k br\x04\x04own \x04\x03fox\x00\x00' +>>> +</pre> + +<p> +Nothing special is required on decoding side to recover from various encoding +forms. BER decoder transparently handles all of them. +</p> + +<h3> +Availability +</h3> + +<p> +The pyasn1 package is distributed under terms and conditions of BSD-style +license. See LICENSE file in the distribution. Source code is freely +available from <a href=http://www.sf.net/projects/pyasn1/>project home</a>. +</p> + +<h3> +Feedback +</h3> + +<p> +Comments and fixes are welcome at +<a href="mailto:ilya@glas.net">ilya@glas.net</a> +. +</p> + +</td> +</tr> +</table> +</center> +</body> +</html> diff --git a/examples/snmp.py b/examples/snmp.py new file mode 100644 index 0000000..7ee73ef --- /dev/null +++ b/examples/snmp.py @@ -0,0 +1,171 @@ +# SNMP message syntax + +# Would be autogenerated by an ASN.1 parser from here: + +from pyasn1.type import univ, namedtype, namedval, tag, constraint +from pyasn1.codec.ber import encoder, decoder + +class Version(univ.Integer): + namedValues = namedval.NamedValues( + ('version-1', 0) + ) + defaultValue = 0 + +class Community(univ.OctetString): pass + +class RequestID(univ.Integer): pass +class ErrorStatus(univ.Integer): + namedValues = namedval.NamedValues( + ('noError', 0), + ('tooBig', 1), + ('noSuchName', 2), + ('badValue', 3), + ('readOnly', 4), + ('genErr', 5) + ) +class ErrorIndex(univ.Integer): pass + +class ObjectName(univ.ObjectIdentifier): pass + +class SimpleSyntax(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('number', univ.Integer()), + namedtype.NamedType('string', univ.OctetString()), + namedtype.NamedType('object', univ.ObjectIdentifier()), + namedtype.NamedType('empty', univ.Null()) + ) + +class IpAddress(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 0) + ) + subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueSizeConstraint( + 4, 4 + ) +class NetworkAddress(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('internet', IpAddress()) + ) + +class Counter(univ.Integer): + tagSet = univ.Integer.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 1) + ) + subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueRangeConstraint( + 0, 4294967295L + ) +class Gauge(univ.Integer): + tagSet = univ.Integer.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2) + ) + subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueRangeConstraint( + 0, 4294967295L + ) +class TimeTicks(univ.Integer): + tagSet = univ.Integer.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 3) + ) + subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueRangeConstraint( + 0, 4294967295L + ) +class Opaque(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 4) + ) + +class ApplicationSyntax(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('address', NetworkAddress()), + namedtype.NamedType('counter', Counter()), + namedtype.NamedType('gauge', Gauge()), + namedtype.NamedType('ticks', TimeTicks()), + namedtype.NamedType('arbitrary', Opaque()) + ) + +class ObjectSyntax(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('simple', SimpleSyntax()), + namedtype.NamedType('application-wide', ApplicationSyntax()) + ) + +class VarBind(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('name', ObjectName()), + namedtype.NamedType('value', ObjectSyntax()) + ) +class VarBindList(univ.SequenceOf): + componentType = VarBind() + +class _RequestBase(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('request-id', RequestID()), + namedtype.NamedType('error-status', ErrorStatus()), + namedtype.NamedType('error-index', ErrorIndex()), + namedtype.NamedType('variable-bindings', VarBindList()) + ) + +class GetRequestPDU(_RequestBase): + tagSet = _RequestBase.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0) + ) +class GetNextRequestPDU(_RequestBase): + tagSet = _RequestBase.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1) + ) +class GetResponsePDU(_RequestBase): + tagSet = _RequestBase.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2) + ) +class SetRequestPDU(_RequestBase): + tagSet = _RequestBase.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3) + ) + +class TrapPDU(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('enterprise', univ.ObjectIdentifier()), + namedtype.NamedType('agent-addr', NetworkAddress()), + namedtype.NamedType('generic-trap', univ.Integer().clone(namedValues=namedval.NamedValues(('coldStart', 0), ('warmStart', 1), ('linkDown', 2), ('linkUp', 3), ('authenticationFailure', 4), ('egpNeighborLoss', 5), ('enterpriseSpecific', 6)))), + namedtype.NamedType('specific-trap', univ.Integer()), + namedtype.NamedType('time-stamp', TimeTicks()), + namedtype.NamedType('variable-bindings', VarBindList()) + ) + +class Pdus(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('get-request', GetRequestPDU()), + namedtype.NamedType('get-next-request', GetNextRequestPDU()), + namedtype.NamedType('get-response', GetResponsePDU()), + namedtype.NamedType('set-request', SetRequestPDU()), + namedtype.NamedType('trap', TrapPDU()) + ) + +class Message(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('community', Community()), + namedtype.NamedType('data', Pdus()) + ) + +# ...would be autogenerated up to here + +def mkRequest(): + msg = Message() + msg.setComponentByPosition(0) + msg.setComponentByPosition(1, 'public') + # pdu + pdus = msg.setComponentByPosition(2).getComponentByPosition(2) + pdu = pdus.setComponentByPosition(0).getComponentByPosition(0) + pdu.setComponentByPosition(0, 123) + pdu.setComponentByPosition(1) + pdu.setComponentByPosition(2) + vbl = pdu.setComponentByPosition(3).getComponentByPosition(3) + vb = vbl.setComponentByPosition(0).getComponentByPosition(0) + vb.setComponentByPosition(0, (1,3,6,1,2,1,1,1,0)) + v = vb.setComponentByPosition(1).getComponentByPosition(1).setComponentByPosition(0).getComponentByPosition(0).setComponentByPosition(3).getComponentByPosition(3) + return msg + +msg = mkRequest() +rMsg = decoder.decode(encoder.encode(msg), asn1Spec=msg)[0] +#rMsg = decoder.decode(encoder.encode(msg))[0] +print rMsg.prettyPrinter() diff --git a/examples/x509.py b/examples/x509.py new file mode 100644 index 0000000..7717a46 --- /dev/null +++ b/examples/x509.py @@ -0,0 +1,147 @@ +# Read ASN.1/PEM X.509 certificates on stdin, parse each into plain text, +# then build substrate from it +import sys, string, base64 +from pyasn1.type import tag,namedtype,namedval,univ,constraint,char,useful +from pyasn1.codec.der import decoder, encoder +from pyasn1 import error + +# Would be autogenerated from ASN.1 source by a ASN.1 parser +# X.509 spec (rfc2459) + +MAX = 64 # XXX ? + +class DirectoryString(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.NamedType('ia5String', char.IA5String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) # hm, this should not be here!? XXX + ) + +class AttributeValue(DirectoryString): pass + +class AttributeType(univ.ObjectIdentifier): pass + +class AttributeTypeAndValue(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('type', AttributeType()), + namedtype.NamedType('value', AttributeValue()) + ) + +class RelativeDistinguishedName(univ.SetOf): + componentType = AttributeTypeAndValue() + +class RDNSequence(univ.SequenceOf): + componentType = RelativeDistinguishedName() + +class Name(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('', RDNSequence()) + ) + +class AlgorithmIdentifier(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('algorithm', univ.ObjectIdentifier()), + namedtype.OptionalNamedType('parameters', univ.Null()) + # XXX syntax screwed? +# namedtype.OptionalNamedType('parameters', univ.ObjectIdentifier()) + ) + +class Extension(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('extnID', univ.ObjectIdentifier()), + namedtype.DefaultedNamedType('critical', univ.Boolean('False')), + namedtype.NamedType('extnValue', univ.OctetString()) + ) + +class Extensions(univ.SequenceOf): + componentType = Extension() + sizeSpec = univ.SequenceOf.sizeSpec + constraint.ValueSizeConstraint(1, MAX) + +class SubjectPublicKeyInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('algorithm', AlgorithmIdentifier()), + namedtype.NamedType('subjectPublicKey', univ.BitString()) + ) + +class UniqueIdentifier(univ.BitString): pass + +class Time(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('utcTime', useful.UTCTime()), + namedtype.NamedType('generalTime', useful.GeneralizedTime()) + ) + +class Validity(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('notBefore', Time()), + namedtype.NamedType('notAfter', Time()) + ) + +class CertificateSerialNumber(univ.Integer): pass + +class Version(univ.Integer): + namedValues = namedval.NamedValues( + ('v1', 0), ('v2', 1), ('v3', 2) + ) + +class TBSCertificate(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', Version('v1', tagSet=Version.tagSet.tagExplicitly(tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))), + namedtype.NamedType('serialNumber', CertificateSerialNumber()), + namedtype.NamedType('signature', AlgorithmIdentifier()), + namedtype.NamedType('issuer', Name()), + namedtype.NamedType('validity', Validity()), + namedtype.NamedType('subject', Name()), + namedtype.NamedType('subjectPublicKeyInfo', SubjectPublicKeyInfo()), + namedtype.OptionalNamedType('issuerUniqueID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('subjectUniqueID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.OptionalNamedType('extensions', Extensions().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))) + ) + +class Certificate(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('tbsCertificate', TBSCertificate()), + namedtype.NamedType('signatureAlgorithm', AlgorithmIdentifier()), + namedtype.NamedType('signatureValue', univ.BitString()) + ) + +# end of ASN.1 data structures + +certType = Certificate() + +# Read PEM certs from stdin and print them out in plain text + +stSpam, stHam, stDump = 0, 1, 2 +state = stSpam +certCnt = 0 + +for certLine in sys.stdin.readlines(): + certLine = string.strip(certLine) + if state == stSpam: + if state == stSpam: + if certLine == '-----BEGIN CERTIFICATE-----': + certLines = [] + state = stHam + continue + if state == stHam: + if certLine == '-----END CERTIFICATE-----': + state = stDump + else: + certLines.append(certLine) + if state == stDump: + substrate = '' + for certLine in certLines: + substrate = substrate + base64.b64decode(certLine) + + cert = decoder.decode(substrate, asn1Spec=certType)[0] + print cert.prettyPrinter() + + assert encoder.encode(cert) == substrate, 'cert recode fails' + + certCnt = certCnt + 1 + state = stSpam + +print '*** %s PEM cert(s) de/serialized' % certCnt diff --git a/pyasn1/__init__.py b/pyasn1/__init__.py new file mode 100644 index 0000000..7de39fe --- /dev/null +++ b/pyasn1/__init__.py @@ -0,0 +1 @@ +majorVersionId = '1' diff --git a/pyasn1/codec/__init__.py b/pyasn1/codec/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/pyasn1/codec/__init__.py diff --git a/pyasn1/codec/ber/__init__.py b/pyasn1/codec/ber/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/pyasn1/codec/ber/__init__.py diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py new file mode 100644 index 0000000..d2b83df --- /dev/null +++ b/pyasn1/codec/ber/decoder.py @@ -0,0 +1,480 @@ +# BER decoder +import types +from pyasn1.type import tag, univ, char, useful +from pyasn1.codec.ber import eoo +from pyasn1 import error + +class AbstractDecoder: + protoComponent = None + def _createComponent(self, tagSet, asn1Spec): + if asn1Spec is None: + return self.protoComponent(tagSet=tagSet) + else: + return asn1Spec.clone() + + def valueDecoder(self, substrate, asn1Spec, tagSet, + length, state, decodeFun): + raise error.PyAsn1Error('Decoder not implemented for %s' % tagSet) + +class EndOfOctetsDecoder(AbstractDecoder): + def valueDecoder(self, substrate, asn1Spec, tagSet, + length, state, decodeFun): + return eoo.endOfOctets, substrate + +class IntegerDecoder(AbstractDecoder): + protoComponent = univ.Integer + def valueDecoder(self, substrate, asn1Spec, tagSet, length, + state, decodeFun): + if not substrate: + raise error.PyAsn1Error('Empty substrate') + bytes = map(ord, substrate) + if bytes[0] & 0x80: + bytes.insert(0, -1L) + value = 0L + for byte in bytes: + value = value << 8 | byte + try: + value = int(value) + except OverflowError: + pass + return self._createComponent(tagSet, asn1Spec).clone(value), substrate + +class BooleanDecoder(IntegerDecoder): + protoComponent = univ.Boolean + +class BitStringDecoder(AbstractDecoder): + protoComponent = univ.BitString + def valueDecoder(self, substrate, asn1Spec, tagSet, length, + state, decodeFun): + r = self._createComponent(tagSet, asn1Spec) # XXX use default tagset + if tagSet[0][1] == tag.tagFormatSimple: # XXX what tag to check? + if not substrate: + raise error.PyAsn1Error('Missing initial octet') + trailingBits = ord(substrate[0]) + if trailingBits > 7: + raise error.PyAsn1Error( + 'Trailing bits overflow %s' % trailingBits + ) + substrate = substrate[1:] + p = 0; l = len(substrate); b = [] + while p < l: + if p == l: + j = 7-trailingBits + else: + j = 7 + o = ord(substrate[p]) + while j >= 0: + b.append((o>>j)&0x01) + j = j - 1 + p = p + 1 + return r.clone(tuple(b)), '' + if r: r = r.clone(value=()) + if not decodeFun: + return r, substrate + while substrate: + component, substrate = decodeFun(substrate) + r = r + component + return r, substrate + + def indefLenValueDecoder(self, substrate, asn1Spec, tagSet, + length, state, decodeFun): + r = self._createComponent(tagSet, asn1Spec) # XXX use default tagset + if r: r = r.clone(value='') + if not decodeFun: + return r, substrate + while substrate: + component, substrate = decodeFun(substrate) + if component == eoo.endOfOctets: + break + r = r + component + else: + raise error.SubstrateUnderrunError( + 'No EOO seen before substrate ends' + ) + return r, substrate + +class OctetStringDecoder(AbstractDecoder): + protoComponent = univ.OctetString + def valueDecoder(self, substrate, asn1Spec, tagSet, length, + state, decodeFun): + r = self._createComponent(tagSet, asn1Spec) # XXX use default tagset + if tagSet[0][1] == tag.tagFormatSimple: # XXX what tag to check? + return r.clone(str(substrate)), '' + if r: r = r.clone(value='') + if not decodeFun: + return r, substrate + while substrate: + component, substrate = decodeFun(substrate) + r = r + component + return r, substrate + + def indefLenValueDecoder(self, substrate, asn1Spec, tagSet, + length, state, decodeFun): + r = self._createComponent(tagSet, asn1Spec) # XXX use default tagset + if r: r = r.clone(value='') + if not decodeFun: + return r, substrate + while substrate: + component, substrate = decodeFun(substrate) + if component == eoo.endOfOctets: + break + r = r + component + else: + raise error.SubstrateUnderrunError( + 'No EOO seen before substrate ends' + ) + return r, substrate + +class NullDecoder(AbstractDecoder): + protoComponent = univ.Null + def valueDecoder(self, substrate, asn1Spec, tagSet, + length, state, decodeFun): + r = self._createComponent(tagSet, asn1Spec) # XXX use default tagset + if substrate: + raise error.PyAsn1Error('Unexpected substrate for Null') + return r, substrate + +class ObjectIdentifierDecoder(AbstractDecoder): + protoComponent = univ.ObjectIdentifier + def valueDecoder(self, substrate, asn1Spec, tagSet, length, + state, decodeFun): + r = self._createComponent(tagSet, asn1Spec) # XXX use default tagset + if not substrate: + raise error.PyAsn1Error('Empty substrate') + oid = []; index = 0 + # Get the first subid + subId = ord(substrate[index]) + oid.append(int(subId / 40)) + oid.append(int(subId % 40)) + + index = index + 1 + substrateLen = len(substrate) + + while index < substrateLen: + subId = ord(substrate[index]) + if subId < 128: + oid.append(subId) + index = index + 1 + else: + # Construct subid from a number of octets + nextSubId = subId + subId = 0 + while nextSubId >= 128 and index < substrateLen: + subId = (subId << 7) + (nextSubId & 0x7F) + index = index + 1 + nextSubId = ord(substrate[index]) + if index == substrateLen: + raise error.SubstrateUnderrunError( + 'Short substrate for OID %s' % oid + ) + subId = (subId << 7) + nextSubId + oid.append(subId) + index = index + 1 + return r.clone(tuple(oid)), substrate[index:] + +class SequenceDecoder(AbstractDecoder): + protoComponent = univ.Sequence + def _getAsn1SpecByPosition(self, t, idx): + if t.getComponentType() is not None: + if hasattr(t, 'getComponentTypeMapNearPosition'): + return t.getComponentTypeMapNearPosition(idx) # Sequence + elif hasattr(t, 'getComponentTypeMap'): # XXX + return t.getComponentTypeMap() # SequenceOf + # or no asn1Specs + def _getPositionByType(self, t, c, idx): + if t.getComponentType() is not None: + if hasattr(t, 'getComponentPositionNearType'): + effectiveTagSet = getattr( + c, 'getEffectiveTagSet', c.getTagSet + )() + return t.getComponentPositionNearType(effectiveTagSet, idx) # Sequence + return idx # SequenceOf or w/o asn1Specs + + def valueDecoder(self, substrate, asn1Spec, tagSet, + length, state, decodeFun): + r = self._createComponent(tagSet, asn1Spec) + idx = 0 + if not decodeFun: + return r, substrate + while substrate: + asn1Spec = self._getAsn1SpecByPosition(r, idx) + component, substrate = decodeFun( + substrate, asn1Spec + ) + idx = self._getPositionByType(r, component, idx) + r.setComponentByPosition(idx, component) + idx = idx + 1 + if hasattr(r, 'setDefaultComponents'): + r.setDefaultComponents() + r.verifySizeSpec() + return r, substrate + + def indefLenValueDecoder(self, substrate, asn1Spec, tagSet, + length, state, decodeFun): + r = self._createComponent(tagSet, asn1Spec) + idx = 0 + while substrate: + try: + asn1Spec = self._getAsn1SpecByPosition(r, idx) + except error.PyAsn1Error: + asn1Spec = None # XXX + if not decodeFun: + return r, substrate + component, substrate = decodeFun(substrate, asn1Spec) + if component == eoo.endOfOctets: + break + idx = self._getPositionByType(r, component, idx) + r.setComponentByPosition(idx, component) + idx = idx + 1 + else: + raise error.SubstrateUnderrunError( + 'No EOO seen before substrate ends' + ) + if hasattr(r, 'setDefaultComponents'): + r.setDefaultComponents() + r.verifySizeSpec() + return r, substrate + +class SetDecoder(SequenceDecoder): + protoComponent = univ.Set + def _getAsn1SpecByPosition(self, t, idx): + if t.getComponentType() is not None: + if hasattr(t, 'getComponentTypeMap'): + return t.getComponentTypeMap() # Set/SetOf + # or no asn1Specs + def _getPositionByType(self, t, c, idx): + if t.getComponentType() is not None: + if t.getComponentType() and hasattr(t, 'getComponentPositionByType'): + effectiveTagSet = getattr( + c, 'getEffectiveTagSet', c.getTagSet + )() + return t.getComponentPositionByType(effectiveTagSet) # Set + return idx # SetOf or w/o asn1Specs + +class ChoiceDecoder(AbstractDecoder): + protoComponent = univ.Choice + def valueDecoder(self, substrate, asn1Spec, tagSet, + length, state, decodeFun): + r = self._createComponent(tagSet, asn1Spec) + if not decodeFun: + return r, substrate + component, substrate = decodeFun( + substrate, r.getComponentTypeMap(), tagSet, length, state + ) + effectiveTagSet = getattr( + component, 'getEffectiveTagSet', component.getTagSet + )() + r.setComponentByType(effectiveTagSet, component) + return r, substrate + + indefLenValueDecoder = valueDecoder + +# character string types +class UTF8StringDecoder(OctetStringDecoder): + protoComponent = char.UTF8String +class NumericStringDecoder(OctetStringDecoder): + protoComponent = char.NumericString +class PrintableStringDecoder(OctetStringDecoder): + protoComponent = char.PrintableString +class TeletexStringDecoder(OctetStringDecoder): + protoComponent = char.TeletexString +class VideotexStringDecoder(OctetStringDecoder): + protoComponent = char.VideotexString +class IA5StringDecoder(OctetStringDecoder): + protoComponent = char.IA5String +class GraphicStringDecoder(OctetStringDecoder): + protoComponent = char.GraphicString +class VisibleStringDecoder(OctetStringDecoder): + protoComponent = char.VisibleString +class GeneralStringDecoder(OctetStringDecoder): + protoComponent = char.GeneralString +class UniversalStringDecoder(OctetStringDecoder): + protoComponent = char.UniversalString +class BMPStringDecoder(OctetStringDecoder): + protoComponent = char.BMPString + +# "useful" types +class GeneralizedTimeDecoder(OctetStringDecoder): + protoComponent = useful.GeneralizedTime +class UTCTimeDecoder(OctetStringDecoder): + protoComponent = useful.UTCTime + +codecMap = { + eoo.endOfOctets.tagSet: EndOfOctetsDecoder(), + univ.Integer.tagSet: IntegerDecoder(), + univ.Boolean.tagSet: BooleanDecoder(), + univ.BitString.tagSet: BitStringDecoder(), + univ.OctetString.tagSet: OctetStringDecoder(), + univ.Null.tagSet: NullDecoder(), + univ.ObjectIdentifier.tagSet: ObjectIdentifierDecoder(), + univ.Sequence.tagSet: SequenceDecoder(), + univ.Set.tagSet: SetDecoder(), + univ.Choice.tagSet: ChoiceDecoder(), + # character string types + char.UTF8String.tagSet: UTF8StringDecoder(), + char.NumericString.tagSet: NumericStringDecoder(), + char.PrintableString.tagSet: PrintableStringDecoder(), + char.TeletexString.tagSet: TeletexStringDecoder(), + char.VideotexString.tagSet: VideotexStringDecoder(), + char.IA5String.tagSet: IA5StringDecoder(), + char.GraphicString.tagSet: GraphicStringDecoder(), + char.VisibleString.tagSet: VisibleStringDecoder(), + char.GeneralString.tagSet: GeneralStringDecoder(), + char.UniversalString.tagSet: UniversalStringDecoder(), + char.BMPString.tagSet: BMPStringDecoder(), + # useful types + useful.GeneralizedTime.tagSet: GeneralizedTimeDecoder(), + useful.UTCTime.tagSet: UTCTimeDecoder() + } + +( stDecodeTag, stDecodeLength, stGetValueDecoder, stGetValueDecoderByAsn1Spec, + stGetValueDecoderByTag, stTryAsExplicitTag, stDecodeValue, stStop ) = range(8) + +class Decoder: + def __init__(self, codecMap): + self.__codecMap = codecMap + def __call__(self, substrate, asn1Spec=None, tagSet=None, + length=None, state=stDecodeTag, recursiveFlag=1): + # Decode tag & length + while state != stStop: + if state == stDecodeTag: + # Decode tag + lenOfStream = len(substrate) + if lenOfStream < 2: + raise error.SubstrateUnderrunError( + 'Short octet stream (%d octets)' % lenOfStream + ) + t = ord(substrate[0]) + lastTag = tag.Tag( + tagClass=(t&0xC0), + tagFormat=(t&0x20), + tagId=t&0x1F + ) + if tagSet is None: + tagSet = tag.TagSet((), lastTag) # base tag is not recovered + else: + tagSet = lastTag + tagSet + substrate = substrate[1:] + state = stDecodeLength + if state == stDecodeLength: + # Decode length (we know there's at least 2 bytes from above) + firstOctet = ord(substrate[0]) + if firstOctet == 128: + size = 1 + length = -1 + elif firstOctet < 128: + length, size = firstOctet, 1 + else: + size = firstOctet & 0x7F + # encoded in size bytes + length = 0 + lengthString = substrate[1:size+1] + # missing check on maximum size, which shouldn't be a + # problem, we can handle more than is possible + if len(lengthString) != size: + raise error.SubstrateUnderrunError( + '%s<%s at %s' % + (size, len(lengthString), tagSet) + ) + for char in lengthString: + length = (length << 8) | ord(char) + size = size + 1 + state = stGetValueDecoder + substrate = substrate[size:] + if length != -1 and len(substrate) < length: + raise error.SubstrateUnderrunError( + '%d-octet short' % (length - len(substrate)) + ) + if state == stGetValueDecoder: + if asn1Spec is None: + state = stGetValueDecoderByTag + else: + state = stGetValueDecoderByAsn1Spec + # + # There're two ways of creating subtypes in ASN.1 what influences + # decoder operation. These methods are: + # 1) Either base types used in or no IMPLICIT tagging has been applied + # on subtyping. + # 2) Subtype syntax drops base type information (by means of IMPLICIT + # tagging. + # The first case allows for complete tag recovery from substrate while + # the second one requires original ASN.1 type spec for decoding. + # + # In either case a set of tags (tagSet) is coming from substrate in + # an incremental, tag-by-tag fashion (this is the case of EXPLICIT tag + # which is most basic). Outermost tag comes first from wire. + # + if state == stGetValueDecoderByTag: + concreteDecoder = self.__codecMap.get(tagSet) + if concreteDecoder: + state = stDecodeValue + else: + concreteDecoder = self.__codecMap.get(tagSet[:1]) + if concreteDecoder: + state = stDecodeValue + else: + state = stTryAsExplicitTag + if state == stGetValueDecoderByAsn1Spec: + if tagSet == eoo.endOfOctets.getTagSet(): + concreteDecoder = self.__codecMap[tagSet] + state = stDecodeValue + continue + if type(asn1Spec) == types.DictType: + __chosenSpec = asn1Spec.get(tagSet) + elif asn1Spec is not None: + __chosenSpec = asn1Spec + else: + __chosenSpec = None + if __chosenSpec is None or not\ + __chosenSpec.getTypeMap().has_key(tagSet): + state = stTryAsExplicitTag + else: + # use base type for codec lookup to recover untagged types + baseTag = __chosenSpec.getTagSet().getBaseTag() + if baseTag: # XXX ugly + baseTagSet = tag.TagSet(baseTag, baseTag) + else: + baseTagSet = tag.TagSet() + concreteDecoder = self.__codecMap.get( # tagged subtype + baseTagSet + ) + if concreteDecoder: + asn1Spec = __chosenSpec + state = stDecodeValue + else: + state = stTryAsExplicitTag + if state == stTryAsExplicitTag: + if lastTag[1] == tag.tagFormatConstructed and \ + lastTag[0] != tag.tagClassUniversal: + # Assume explicit tagging + state = stDecodeTag + else: + raise error.PyAsn1Error( + '%s not in asn1Spec: %s' % (tagSet, asn1Spec) + ) + if state == stDecodeValue: + if recursiveFlag: + decodeFun = self + else: + decodeFun = None + if length == -1: # indef length + value, substrate = concreteDecoder.indefLenValueDecoder( + substrate, asn1Spec, tagSet, length, + stGetValueDecoder, decodeFun + ) + else: + value, _substrate = concreteDecoder.valueDecoder( + substrate[:length], asn1Spec, tagSet, + length, stGetValueDecoder, decodeFun + ) + if recursiveFlag: + substrate = substrate[length:] + else: + substrate = _substrate + state = stStop + return value, substrate + +decode = Decoder(codecMap) + +# XXX +# non-recursive decoding; return position rather than substrate diff --git a/pyasn1/codec/ber/encoder.py b/pyasn1/codec/ber/encoder.py new file mode 100644 index 0000000..f1130f9 --- /dev/null +++ b/pyasn1/codec/ber/encoder.py @@ -0,0 +1,251 @@ +# BER encoder +import string +from pyasn1.type import tag, univ, char, useful +from pyasn1.codec.ber import eoo +from pyasn1 import error + +class Error(Exception): pass + +class AbstractItemEncoder: + supportIndefLenMode = 1 + def _encodeTag(self, t, isConstructed): + if isConstructed: + return chr(t[0]|t[1]|t[2]|tag.tagFormatConstructed) + else: + return chr(t[0]|t[1]|t[2]) + + def _encodeLength(self, length, defMode): + if not defMode and self.supportIndefLenMode: + return chr(0x80) + if length < 0x80: + return chr(length) + elif length < 0xFF: + return '\x81%c' % length + elif length < 0xFFFF: + return '\x82%c%c' % ( + (length >> 8) & 0xFF, length & 0xFF + ) + elif length < 0xFFFFFF: + return '\x83%c%c%c' % ( + (length >> 16) & 0xFF, + (length >> 8) & 0xFF, + length & 0xFF + ) + #...more octets may be added + else: + raise Error( + 'Too large length (%d)' % length + ) + + def _encodeValue(self, encodeFun, value, defMode, maxChunkSize): + raise Error('Not implemented') + + def _encodeEndOfOctets(self, encodeFun, defMode): + if defMode or not self.supportIndefLenMode: + return '' + else: + return encodeFun(eoo.endOfOctets, defMode) + + def encode(self, encodeFun, value, defMode, maxChunkSize): + substrate, isConstructed = self._encodeValue( + encodeFun, value, defMode, maxChunkSize + ) + tagSet = value.getTagSet() + if tagSet: + if not isConstructed: # primitive form implies definite mode + defMode = 1 + return self._encodeTag( + tagSet[-1], isConstructed + ) + self._encodeLength( + len(substrate), defMode + ) + substrate + self._encodeEndOfOctets(encodeFun, defMode) + else: + return substrate # untagged value + +class EndOfOctetsEncoder(AbstractItemEncoder): + def _encodeValue(self, encodeFun, value, defMode, maxChunkSize): + return '', 0 + +class ExplicitlyTaggedItemEncoder(AbstractItemEncoder): + def _encodeValue(self, encodeFun, value, defMode, maxChunkSize): + return encodeFun(value.clone(tagSet=value.getTagSet()[:-1]), + defMode, maxChunkSize), 1 + +explicitlyTaggedItemEncoder = ExplicitlyTaggedItemEncoder() + +class IntegerEncoder(AbstractItemEncoder): + supportIndefLenMode = 0 + def _encodeValue(self, encodeFun, value, defMode, maxChunkSize): + substrate = '' + value = long(value) # to save on ops on asn1 type + # The 0 and -1 values need to be handled separately since + # they are the terminating cases of the positive and negative + # cases repectively. + if value == 0: + substrate = '\000' + elif value == -1: + substrate = '\377' + elif value < 0: + while value <> -1: + substrate = chr(value & 0xff) + substrate + value = value >> 8 + if ord(substrate[0]) & 0x80 == 0: + substrate = chr(0xff) + substrate + else: + while value > 0: + substrate = chr(value & 0xff) + substrate + value = value >> 8 + if ord(substrate[0]) & 0x80 <> 0: + substrate = chr(0x00) + substrate + return substrate, 0 + +class BitStringEncoder(AbstractItemEncoder): + def _encodeValue(self, encodeFun, value, defMode, maxChunkSize): + maxChunkSize = maxChunkSize*8L # count in octets + if len(value) <= maxChunkSize: + r = {}; l = len(value); p = j = 0 + while p < l: + i, j = divmod(p, 8) + r[i] = r.get(i,0) | value[p]<<(7-j) + p = p + 1 + keys = r.keys(); keys.sort() + return chr(7-j) + string.join( + map(lambda k,r=r: chr(r[k]), keys),'' + ), 0 + else: + pos = 0; substrate = '' + while 1: + v = value.clone(value=value[pos:pos+maxChunkSize]) + if not v: + break + substrate = substrate + encodeFun(v, defMode, maxChunkSize) + pos = pos + maxChunkSize + return substrate, 1 + +class OctetStringEncoder(AbstractItemEncoder): + def _encodeValue(self, encodeFun, value, defMode, maxChunkSize): + if len(value) <= maxChunkSize: + return str(value), 0 + else: + pos = 0; substrate = '' + while 1: + v = value.clone(value=value[pos:pos+maxChunkSize]) + if not v: + break + substrate = substrate + encodeFun(v, defMode, maxChunkSize) + pos = pos + maxChunkSize + return substrate, 1 + +class NullEncoder(AbstractItemEncoder): + supportIndefLenMode = 0 + def _encodeValue(self, encodeFun, value, defMode, maxChunkSize): + return '', 0 + +class ObjectIdentifierEncoder(AbstractItemEncoder): + supportIndefLenMode = 0 + def _encodeValue(self, encodeFun, value, defMode, maxChunkSize): + oid = tuple(value) + if len(oid) < 2: + raise error.PyAsn1Error('Short OID %s' % value) + + # Build the first twos + index = 0 + subid = oid[index] * 40 + subid = subid + oid[index+1] + if 0 > subid > 0xff: + raise error.PyAsn1Error( + 'Initial sub-ID overflow %s in OID %s' % (oid[index:], value) + ) + octets = [ chr(subid) ] + index = index + 2 + + # Cycle through subids + for subid in oid[index:]: + if subid > -1 and subid < 128: + # Optimize for the common case + octets.append(chr(subid & 0x7f)) + elif subid < 0 or subid > 0xFFFFFFFFL: + raise error.PyAsn1Error( + 'SubId overflow %s in %s' % (subid, value) + ) + else: + # Pack large Sub-Object IDs + res = [ chr(subid & 0x7f) ] + subid = subid >> 7 + while subid > 0: + res.insert(0, chr(0x80 | (subid & 0x7f))) + subid = subid >> 7 + # Convert packed Sub-Object ID to string and add packed + # it to resulted Object ID + octets.append(string.join(res, '')) + return string.join(octets, ''), 0 + +class SequenceOfEncoder(AbstractItemEncoder): + def _encodeValue(self, encodeFun, value, defMode, maxChunkSize): + if hasattr(value, 'setDefaultComponents'): + value.setDefaultComponents() + value.verifySizeSpec() + substrate = ''; idx = len(value) + while idx > 0: + idx = idx - 1 + if value[idx] is None: # Optional component + continue + if hasattr(value, 'getDefaultComponentByPosition'): + if value.getDefaultComponentByPosition(idx) == value[idx]: + continue + substrate = encodeFun( + value[idx], defMode, maxChunkSize + ) + substrate + return substrate, 1 + +codecMap = { + eoo.endOfOctets.tagSet: EndOfOctetsEncoder(), + univ.Boolean.tagSet: IntegerEncoder(), + univ.Integer.tagSet: IntegerEncoder(), + univ.BitString.tagSet: BitStringEncoder(), + univ.OctetString.tagSet: OctetStringEncoder(), + univ.Null.tagSet: NullEncoder(), + univ.ObjectIdentifier.tagSet: ObjectIdentifierEncoder(), + # Sequence & Set have same tags as SequenceOf & SetOf + univ.SequenceOf.tagSet: SequenceOfEncoder(), + univ.SetOf.tagSet: SequenceOfEncoder(), + univ.Choice.tagSet: SequenceOfEncoder(), + # character string types + char.UTF8String.tagSet: OctetStringEncoder(), + char.NumericString.tagSet: OctetStringEncoder(), + char.PrintableString.tagSet: OctetStringEncoder(), + char.TeletexString.tagSet: OctetStringEncoder(), + char.VideotexString.tagSet: OctetStringEncoder(), + char.IA5String.tagSet: OctetStringEncoder(), + char.GraphicString.tagSet: OctetStringEncoder(), + char.VisibleString.tagSet: OctetStringEncoder(), + char.GeneralString.tagSet: OctetStringEncoder(), + char.UniversalString.tagSet: OctetStringEncoder(), + char.BMPString.tagSet: OctetStringEncoder(), + # useful types + useful.GeneralizedTime.tagSet: OctetStringEncoder(), + useful.UTCTime.tagSet: OctetStringEncoder() + } + +class Encoder: + def __init__(self, codecMap): + self.__codecMap = codecMap + + def __call__(self, value, defMode=1, maxChunkSize=0xffffffffl): + tagSet = value.getTagSet() + if len(tagSet) > 1: + concreteEncoder = explicitlyTaggedItemEncoder + else: + concreteEncoder = self.__codecMap.get(tagSet) + if not concreteEncoder: + concreteEncoder = self.__codecMap.get( + tag.TagSet(tagSet.getBaseTag(), tagSet.getBaseTag()) # XXX + ) + if concreteEncoder: + return concreteEncoder.encode( + self, value, defMode, maxChunkSize + ) + else: + raise Error('No encoder for %s' % value) + +encode = Encoder(codecMap) diff --git a/pyasn1/codec/ber/eoo.py b/pyasn1/codec/ber/eoo.py new file mode 100644 index 0000000..b37bd7f --- /dev/null +++ b/pyasn1/codec/ber/eoo.py @@ -0,0 +1,7 @@ +from pyasn1.type import base, tag + +class EndOfOctets(base.AbstractSimpleAsn1Item): + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00) + ) +endOfOctets = EndOfOctets() diff --git a/pyasn1/codec/cer/__init__.py b/pyasn1/codec/cer/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/pyasn1/codec/cer/__init__.py diff --git a/pyasn1/codec/cer/decoder.py b/pyasn1/codec/cer/decoder.py new file mode 100644 index 0000000..b26fdfb --- /dev/null +++ b/pyasn1/codec/cer/decoder.py @@ -0,0 +1,28 @@ +# CER decoder +from pyasn1.type import univ +from pyasn1.codec.ber import decoder +from pyasn1 import error + +class BooleanDecoder(decoder.AbstractDecoder): + protoComponent = univ.Boolean + def valueDecoder(self, substrate, asn1Spec, tagSet, length, + state, decodeFun): + if not substrate: + raise error.PyAsn1Error('Empty substrate') + byte = ord(substrate[0]) + if byte == 0xff: + value = 1 + elif byte == 0x00: + value = 0 + return self._createComponent( + tagSet, asn1Spec + ).clone(value), substrate[1:] + +codecMap = decoder.codecMap.copy() +codecMap.update({ + univ.Boolean.tagSet: BooleanDecoder(), + }) + +class Decoder(decoder.Decoder): pass + +decode = Decoder(codecMap) diff --git a/pyasn1/codec/cer/encoder.py b/pyasn1/codec/cer/encoder.py new file mode 100644 index 0000000..75eaa5c --- /dev/null +++ b/pyasn1/codec/cer/encoder.py @@ -0,0 +1,86 @@ +# CER encoder +import string +from pyasn1.type import univ +from pyasn1.codec.ber import encoder + +class BooleanEncoder(encoder.IntegerEncoder): + def _encodeValue(self, encodeFun, client, defMode, maxChunkSize): + if client == 0: + substrate = '\000' + else: + substrate = '\777' + return substrate, 0 + +class BitStringEncoder(encoder.BitStringEncoder): + def _encodeValue(self, encodeFun, client, defMode, maxChunkSize): + return encoder.BitStringEncoder._encodeValue( + self, encodeFun, client, defMode, 1000 + ) + +class OctetStringEncoder(encoder.OctetStringEncoder): + def _encodeValue(self, encodeFun, client, defMode, maxChunkSize): + return encoder.OctetStringEncoder._encodeValue( + self, encodeFun, client, defMode, 1000 + ) + +# specialized RealEncoder here +# specialized GeneralStringEncoder here +# specialized GeneralizedTimeEncoder here +# specialized UTCTimeEncoder here + +class SetOfEncoder(encoder.SequenceOfEncoder): + def _cmpSetComponents(self, c1, c2): + return cmp( + getattr(c1, 'getMinimalTagSet', c1.getTagSet)(), + getattr(c2, 'getMinimalTagSet', c2.getTagSet)() + ) + + def _encodeValue(self, encodeFun, client, defMode, maxChunkSize): + if hasattr(client, 'setDefaultComponents'): + client.setDefaultComponents() + client.verifySizeSpec() + # Guess client type basing on number of component types. + # This is certainly a hack but how do I distinguish one from + # another if they have the same tags&constraints? + substrate = ''; idx = len(client) + if len(client) > 1: + # Set + comps = [] + while idx > 0: + idx = idx - 1 + if client[idx] is None: # Optional component + continue + if client.getDefaultComponentByPosition(idx) == client[idx]: + continue + comps.append(client[idx]) + comps.sort(self._cmpSetComponents) + for c in comps: + substrate = substrate + encodeFun(c, defMode, maxChunkSize) + else: + # SetOf + compSubs = [] + while idx > 0: + idx = idx - 1 + compSubs.append( + encodeFun(client[idx], defMode, maxChunkSize) + ) + compSubs.sort() # perhaps padding's not needed + substrate = string.join(compSubs, '') + return substrate, 1 + +codecMap = encoder.codecMap.copy() +codecMap.update({ + univ.Boolean.tagSet: BooleanEncoder(), + univ.BitString.tagSet: BitStringEncoder(), + univ.OctetString.tagSet: OctetStringEncoder(), + # Set & SetOf have same tags + univ.SetOf().tagSet: SetOfEncoder() + }) + +class Encoder(encoder.Encoder): + def __call__(self, client, defMode=0, maxChunkSize=0xffffffffl): + return encoder.Encoder.__call__(self, client, defMode, maxChunkSize) + +encode = Encoder(codecMap) + +# EncoderFactory queries class instance and builds a map of tags -> encoders diff --git a/pyasn1/codec/der/__init__.py b/pyasn1/codec/der/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/pyasn1/codec/der/__init__.py diff --git a/pyasn1/codec/der/decoder.py b/pyasn1/codec/der/decoder.py new file mode 100644 index 0000000..9e3d7cd --- /dev/null +++ b/pyasn1/codec/der/decoder.py @@ -0,0 +1,5 @@ +# DER decoder +from pyasn1.type import univ +from pyasn1.codec.cer import decoder + +decode = decoder.Decoder(decoder.codecMap) diff --git a/pyasn1/codec/der/encoder.py b/pyasn1/codec/der/encoder.py new file mode 100644 index 0000000..0289008 --- /dev/null +++ b/pyasn1/codec/der/encoder.py @@ -0,0 +1,25 @@ +# DER encoder +from pyasn1.type import univ +from pyasn1.codec.cer import encoder + +class SetOfEncoder(encoder.SetOfEncoder): + def _cmpSetComponents(self, c1, c2): + return cmp( + getattr(c1, 'getEffectiveTagSet', c1.getTagSet)(), + getattr(c2, 'getEffectiveTagSet', c2.getTagSet)() + ) + +codecMap = encoder.codecMap.copy() +codecMap.update({ + # Overload CER encodrs with BER ones (a bit hackerish XXX) + univ.BitString.tagSet: encoder.encoder.BitStringEncoder(), + univ.OctetString.tagSet: encoder.encoder.OctetStringEncoder(), + # Set & SetOf have same tags + univ.SetOf().tagSet: SetOfEncoder() + }) + +class Encoder(encoder.Encoder): + def __call__(self, client, defMode=1, maxChunkSize=0xffffffffl): + return encoder.Encoder.__call__(self, client, defMode, maxChunkSize) + +encode = Encoder(codecMap) diff --git a/pyasn1/error.py b/pyasn1/error.py new file mode 100644 index 0000000..91c7478 --- /dev/null +++ b/pyasn1/error.py @@ -0,0 +1,3 @@ +class PyAsn1Error(StandardError): pass +class ValueConstraintError(PyAsn1Error): pass +class SubstrateUnderrunError(PyAsn1Error): pass diff --git a/pyasn1/type/__init__.py b/pyasn1/type/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/pyasn1/type/__init__.py diff --git a/pyasn1/type/base.py b/pyasn1/type/base.py new file mode 100644 index 0000000..da5790e --- /dev/null +++ b/pyasn1/type/base.py @@ -0,0 +1,210 @@ +# Base classes for ASN.1 types +try: + from sys import version_info +except ImportError: + version_info = (0, 0) # a really early version +from operator import getslice, setslice, delslice +from string import join +from types import SliceType +from pyasn1.type import constraint +from pyasn1 import error + +class Asn1Item: pass + +class Asn1ItemBase(Asn1Item): + # Set of tags for this ASN.1 type + tagSet = () + + # A list of constraint.Constraint instances for checking values + subtypeSpec = constraint.ConstraintsIntersection() + + def __init__(self, tagSet=None, subtypeSpec=None): + if tagSet is None: + self._tagSet = self.tagSet + else: + self._tagSet = tagSet + if subtypeSpec is None: + self._subtypeSpec = self.subtypeSpec + else: + self._subtypeSpec = subtypeSpec + + def _verifySubtypeSpec(self, value, idx=None): + self._subtypeSpec(value, idx) + + def getSubtypeSpec(self): return self._subtypeSpec + + def getTagSet(self): return self._tagSet + def getTypeMap(self): return { self._tagSet: self } + + def isSameTypeWith(self, other): + return self is other or \ + self._tagSet == other.getTagSet() and \ + self._subtypeSpec == other.getSubtypeSpec() + def isSuperTypeOf(self, other): + """Returns true if argument is a ASN1 subtype of ourselves""" + return self._tagSet.isSuperTagSetOf(other.getTagSet()) and \ + self._subtypeSpec.isSuperTypeOf(other.getSubtypeSpec()) + +# Base class for "simple" ASN.1 objects. These are immutable. +class AbstractSimpleAsn1Item(Asn1ItemBase): + defaultValue = None + def __init__(self, value=None, tagSet=None, subtypeSpec=None): + Asn1ItemBase.__init__(self, tagSet, subtypeSpec) + if value is None: + self._value = self.defaultValue + else: + value = self._prettyIn(value) + self._verifySubtypeSpec(value) + self._value = value + self.__hashedValue = hash( + (self._value, self._tagSet, self._subtypeSpec) + ) + + def __repr__(self): + return self.__class__.__name__ + '(' + repr( + self._prettyOut(self._value) + ) +')' + def __str__(self): return str(self._value) + def __cmp__(self, value): return cmp(self._value, value) + def __hash__(self): return self.__hashedValue + + def __nonzero__(self): return bool(self._value) + + def clone(self, value=None, tagSet=None, subtypeSpec=None): + if value is None and tagSet is None and subtypeSpec is None: + return self + if value is None and self._value is not self.defaultValue: + value = self._value + if tagSet is None: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + return self.__class__(value, tagSet, subtypeSpec) + + def subtype(self, value=None, implicitTag=None, explicitTag=None, + subtypeSpec=None): + if value is None and self._value is not self.defaultValue: + value = self._value + if implicitTag is not None: + tagSet = self._tagSet.tagImplicitly(implicitTag) + elif explicitTag is not None: + tagSet = self._tagSet.tagExplicitly(explicitTag) + else: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + else: + subtypeSpec = subtypeSpec + self._subtypeSpec + return self.__class__(value, tagSet, subtypeSpec) + + def _prettyIn(self, value): return value + def _prettyOut(self, value): return value + + def prettyPrinter(self, scope=0): return self._prettyOut(self._value) + +# +# Constructed types: +# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice +# * ASN1 types and values are represened by Python class instances +# * Value initialization is made for defaulted components only +# * Primary method of component addressing is by-position. Data model for base +# type is Python sequence. Additional type-specific addressing methods +# may be implemented for particular types. +# * SequenceOf and SetOf types do not implement any additional methods +# * Sequence, Set and Choice types also implement by-identifier addressing +# * Sequence, Set and Choice types also implement by-asn1-type (tag) addressing +# * Sequence and Set types may include optional and defaulted +# components +# * Constructed types hold a reference to component types used for value +# verification and ordering. +# * Component type is a scalar type for SequenceOf/SetOf types and a list +# of types for Sequence/Set/Choice. +# + +class AbstractConstructedAsn1Item(Asn1ItemBase): + componentType = None + sizeSpec = constraint.ConstraintsIntersection() + def __init__(self, componentType=None, tagSet=None, + subtypeSpec=None, sizeSpec=None): + Asn1ItemBase.__init__(self, tagSet, subtypeSpec) + if componentType is None: + self._componentType = self.componentType + else: + self._componentType = componentType + if sizeSpec is None: + self._sizeSpec = self.sizeSpec + else: + self._sizeSpec = sizeSpec + self._componentValues = [] + + def __repr__(self): + r = self.__class__.__name__ + '()' + for idx in range(len(self)): + if self._componentValues[idx] is None: + continue + r = r + '.setComponentByPosition(%s, %s)' % ( + idx, repr(self._componentValues[idx]) + ) + return r + + def __cmp__(self, other): return cmp(self._componentValues, other) + + def getComponentTypeMap(self): + raise error.PyAsn1Error('Method not implemented') + + def clone(self, componentType=None, tagSet=None, subtypeSpec=None, + sizeSpec=None): + if componentType is None: + componentType = self._componentType + if tagSet is None: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + if sizeSpec is None: + sizeSpec = self._sizeSpec + r = self.__class__(componentType, tagSet, subtypeSpec, sizeSpec) + if componentType is self._componentType and \ + self.getComponentType() is not None: + self._cloneComponentValues(r) # XXX when do we clone inner vals? + return r + + def subtype(self, componentType=None, implicitTag=None, explicitTag=None, + subtypeSpec=None, sizeSpec=None): + if componentType is None: + componentType = self._componentType + if implicitTag is not None: + tagSet = self._tagSet.tagImplicitly(implicitTag) + elif explicitTag is not None: + tagSet = self._tagSet.tagExplicitly(explicitTag) + else: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + else: + subtypeSpec = subtypeSpec + self._subtypeSpec + if sizeSpec is None: + sizeSpec = self._sizeSpec + else: + sizeSpec = sizeSpec + self._sizeSpec + r = self.__class__(componentType, tagSet, subtypeSpec, sizeSpec) + if componentType is self._componentType and \ + self.getComponentType() is not None: + self._cloneComponentValues(r) # XXX when do we clone inner vals? + return r + + def _verifyComponent(self, idx, value): pass + + def verifySizeSpec(self): self._sizeSpec(self) + + def getComponentByPosition(self, idx): + raise error.PyAsn1Error('Method not implemented') + def setComponentByPosition(self, idx, value): + raise error.PyAsn1Error('Method not implemented') + + def getComponentType(self): return self._componentType + + def __getitem__(self, idx): return self._componentValues[idx] + + def __len__(self): return len(self._componentValues) + + def clear(self): self._componentValues = [] diff --git a/pyasn1/type/char.py b/pyasn1/type/char.py new file mode 100644 index 0000000..25cd142 --- /dev/null +++ b/pyasn1/type/char.py @@ -0,0 +1,57 @@ +# ASN.1 "character string" types +from pyasn1.type import univ, tag + +class UTF8String(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) + ) + +class NumericString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18) + ) + +class PrintableString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19) + ) + +class TeletexString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20) + ) + +class VideotexString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21) + ) + +class IA5String(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22) + ) + +class GraphicString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25) + ) + +class VisibleString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26) + ) + +class GeneralString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27) + ) + +class UniversalString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28) + ) + +class BMPString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30) + ) diff --git a/pyasn1/type/constraint.py b/pyasn1/type/constraint.py new file mode 100644 index 0000000..c6bd135 --- /dev/null +++ b/pyasn1/type/constraint.py @@ -0,0 +1,185 @@ +""" + ASN.1 subtype constraints classes. + + Constraints are relatively rare, but every ASN1 object + is doing checks all the time for whether they have any + constraints and whether they are applicable to the object. + + What we're going to do is define objects/functions that + can be called unconditionally if they are present, and that + are simply not present if there are no constraints. + + Original concept and code by Mike C. Fletcher. +""" +import types +import string +from pyasn1.type import error + +class AbstractConstraint: + """Abstract base-class for constraint objects + + Constraints should be stored in a simple sequence in the + namespace of their client Asn1Item sub-classes. + """ + def __init__(self, *values): + self._setValues(values) + self.__hashedValues = hash((self.__class__, self._values)) + def __call__(self, value, idx=None): + try: + self._testValue(value, idx) + except error.ValueConstraintError, why: + raise error.ValueConstraintError('%s failed at: %s' % ( + self, why + )) + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + string.join(map(lambda x: str(x), self._values), ', ') + ) +# def __cmp__(self, other): +# return cmp((self.__class__, self._values), other) + def __eq__(self, other): + return self is other or self.__hashedValues == other + def __hash__(self): return self.__hashedValues + def _setValues(self, values): self._values = values + def _testValue(self, value, idx): + raise error.ValueConstraintError(value) + +class SingleValueConstraint(AbstractConstraint): + """Value must be part of defined values constraint""" + def _testValue(self, value, idx): + # XXX index vals for performance? + if value not in self._values: + raise error.ValueConstraintError(value) + +class ContainedSubtypeConstraint(AbstractConstraint): + """Value must satisfy all of defined set of constraints""" + def _testValue(self, value, idx): + for c in self._values: + c(value, idx) + +class ValueRangeConstraint(AbstractConstraint): + """Value must be within start and stop values (inclusive)""" + def _testValue(self, value, idx): + if value < self.start or value > self.stop: + raise error.ValueConstraintError(value) + + def _setValues(self, values): + if len(values) != 2: + raise error.PyAsn1Error( + '%s: bad constraint values' % (self.__class__.__name__,) + ) + self.start, self.stop = values + if self.start > self.stop: + raise error.PyAsn1Error( + '%s: screwed constraint values (start > stop): %s > %s' % ( + self.__class__.__name__, + self.start, self.stop + ) + ) + AbstractConstraint._setValues(self, values) + +class ValueSizeConstraint(ValueRangeConstraint): + """len(value) must be within start and stop values (inclusive)""" + def _testValue(self, value, idx): + l = len(value) + if l < self.start or l > self.stop: + raise error.ValueConstraintError(value) + +class PermittedAlphabetConstraint(SingleValueConstraint): pass + +# This is a bit kludgy, meaning two op modes within a single constraing +class InnerTypeConstraint(AbstractConstraint): + """Value must satisfy type and presense constraints""" + def _testValue(self, value, idx): + if self.__singleTypeConstraint: + self.__singleTypeConstraint(value) + elif self.__multipleTypeConstraint: + if not self.__multipleTypeConstraint.has_key(idx): + raise error.ValueConstraintError(value) + constraint, status = self.__multipleTypeConstraint[idx] + if status == 'ABSENT': # XXX presense is not checked! + raise error.ValueConstraintError(value) + constraint(value) + + def _setValues(self, values): + self.__multipleTypeConstraint = {} + self.__singleTypeConstraint = None + for v in values: + if type(v) == types.TupleType: + self.__multipleTypeConstraint[v[0]] = v[1], v[2] + else: + self.__singleTypeConstraint = v + AbstractConstraint._setValues(self, values) + +# Boolean ops on constraints + +class ConstraintsExclusion(AbstractConstraint): + """Value must not fit the single constraint""" + def _testValue(self, value, idx): + try: + self._values[0](value, idx) + except error.ValueConstraintError: + return + else: + raise error.ValueConstraintError(value) + + def _setValues(self, values): + if len(values) != 1: + raise error.PyAsn1Error('Single constraint expected') + AbstractConstraint._setValues(self, values) + +class AbstractConstraintSet(AbstractConstraint): + """Value must not satisfy the single constraint""" + def __getitem__(self, idx): return self._values[idx] + + def __add__(self, value): + return apply(self.__class__, (value,) + self._values) + def __radd__(self, value): + return apply(self.__class__, self._values + (value,)) + + def __len__(self): return len(self._values) + +class ConstraintsIntersection(AbstractConstraintSet): + """Value must satisfy all constraints""" + def _testValue(self, value, idx): + for v in self._values: + v(value, idx) + + # Constraints inclusion in sets + + def _setValues(self, values): + self._values = values + self.__valuesMap = {} + for v in values: self.__valuesMap[v] = 1 + + def hasConstraint(self, constraint): + if self.__valuesMap.has_key(constraint): + return 1 + else: + return 0 + + def isSuperTypeOf(self, constraintSet): + if self is constraintSet: + return 1 + for c in self._values: + if not constraintSet.hasConstraint(c): # super type must have all + return 0 # its component constraints + return 1 # included by subtype + +class ConstraintsUnion(AbstractConstraintSet): + """Value must satisfy at least one constraint""" + def _testValue(self, value, idx): + for v in self._values: + try: + v(value, idx) + except error.ValueConstraintError: + pass + else: + return + raise error.ValueConstraintError( + 'all of %s failed for %s' % (self._values, value) + ) + +# XXX +# add tests for type check diff --git a/pyasn1/type/error.py b/pyasn1/type/error.py new file mode 100644 index 0000000..3e68484 --- /dev/null +++ b/pyasn1/type/error.py @@ -0,0 +1,3 @@ +from pyasn1.error import PyAsn1Error + +class ValueConstraintError(PyAsn1Error): pass diff --git a/pyasn1/type/namedtype.py b/pyasn1/type/namedtype.py new file mode 100644 index 0000000..4d86707 --- /dev/null +++ b/pyasn1/type/namedtype.py @@ -0,0 +1,130 @@ +# NamedType specification for constructed types +from pyasn1 import error + +class NamedType: + isOptional = 0 + isDefaulted = 0 + def __init__(self, name, t): + self.__name = name; self.__type = t + def __repr__(self): return '%s(%s, %s)' % ( + self.__class__.__name__, repr(self.__name), repr(self.__type) + ) + def getType(self): return self.__type + def getName(self): return self.__name + def __getitem__(self, idx): + if idx == 0: return self.__name + if idx == 1: return self.__type + raise IndexError() + +class OptionalNamedType(NamedType): + isOptional = 1 +class DefaultedNamedType(NamedType): + isDefaulted = 1 + +class NamedTypes: + def __init__(self, *namedTypes): + self.__namedTypes = namedTypes + self.__minTagSet = None + self.__typeMap = {}; self.__tagMap = {}; self.__nameMap = {} + self.__ambigiousTypes = {} + + def __repr__(self): + r = '%s(' % self.__class__.__name__ + for n in self.__namedTypes: + r = r + '%s, ' % repr(n) + return r + ')' + + def __getitem__(self, idx): return self.__namedTypes[idx] + + def __nonzero__(self): return bool(self.__namedTypes) + def __len__(self): return len(self.__namedTypes) + + def getTypeByPosition(self, idx): + try: + return self.__namedTypes[idx].getType() + except IndexError: + raise error.PyAsn1Error('Type position out of range') + def getPositionByType(self, tagSet): + if not self.__tagMap: + idx = len(self.__namedTypes) + while idx > 0: + idx = idx - 1 + for t in self.__namedTypes[idx].getType().getTypeMap().keys(): + if self.__tagMap.has_key(t): + raise error.PyAsn1Error('Duplicate type %s' % t) + self.__tagMap[t] = idx + try: + return self.__tagMap[tagSet] + except KeyError: + raise error.PyAsn1Error('Type %s not found' % tagSet) + + def getNameByPosition(self, idx): + try: + return self.__namedTypes[idx].getName() + except IndexError: + raise error.PyAsn1Error('Type position out of range') + def getPositionByName(self, name): + if not self.__nameMap: + idx = len(self.__namedTypes) + while idx > 0: + idx = idx - 1 + n = self.__namedTypes[idx].getName() + if self.__nameMap.has_key(n): + raise error.PyAsn1Error('Duplicate name %s' % n) + self.__nameMap[n] = idx + try: + return self.__nameMap[name] + except KeyError: + raise error.PyAsn1Error('Name %s not found' % name) + + def __buildAmbigiousTagMap(self): + ambigiousTypes = () + idx = len(self.__namedTypes) + while idx > 0: + idx = idx - 1 + t = self.__namedTypes[idx] + if t.isOptional or t.isDefaulted: + ambigiousTypes = (t, ) + ambigiousTypes + else: + ambigiousTypes = (t, ) + self.__ambigiousTypes[idx] = apply(NamedTypes, ambigiousTypes) + + def getTypeMapNearPosition(self, idx): + if not self.__ambigiousTypes: self.__buildAmbigiousTagMap() + try: + return self.__ambigiousTypes[idx].getTypeMap() + except KeyError: + raise error.PyAsn1Error('Type position out of range') + + def getPositionNearType(self, tagSet, idx): + if not self.__ambigiousTypes: self.__buildAmbigiousTagMap() + try: + return idx+self.__ambigiousTypes[idx].getPositionByType(tagSet) + except KeyError: + raise error.PyAsn1Error('Type position out of range') + + def genMinTagSet(self): + if self.__minTagSet is None: + for t in self.__namedTypes: + __type = t.getType() + tagSet = getattr(__type,'getMinTagSet',__type.getTagSet)() + if self.__minTagSet is None or tagSet < self.__minTagSet: + self.__minTagSet = tagSet + return self.__minTagSet + + def getTypeMap(self, uniq=None): + if not self.__typeMap: + for t in self.__namedTypes: + __type = t.getType() + typeMap = __type.getTypeMap() + if uniq: + for k in typeMap.keys(): + if self.__typeMap.has_key(k): + raise error.PyAsn1Error( + 'Duplicate type %s in map %s'%(k,self.__typeMap) + ) + self.__typeMap[k] = __type + else: + for k in typeMap.keys(): + self.__typeMap[k] = __type + return self.__typeMap diff --git a/pyasn1/type/namedval.py b/pyasn1/type/namedval.py new file mode 100644 index 0000000..6cc14e6 --- /dev/null +++ b/pyasn1/type/namedval.py @@ -0,0 +1,42 @@ +# ASN.1 named integers +from types import TupleType +from pyasn1 import error + +__all__ = [ 'NamedValues' ] + +class NamedValues: + def __init__(self, *namedValues): + self.nameToValIdx = {}; self.valToNameIdx = {} + self.namedValues = () + automaticVal = 1 + for namedValue in namedValues: + if type(namedValue) == TupleType: + name, val = namedValue + else: + name = namedValue + val = automaticVal + if self.nameToValIdx.has_key(name): + raise error.PyAsn1Error('Duplicate name %s' % name) + self.nameToValIdx[name] = val + if self.valToNameIdx.has_key(val): + raise error.PyAsn1Error('Duplicate value %s' % name) + self.valToNameIdx[val] = name + self.namedValues = self.namedValues + ((name, val),) + automaticVal = automaticVal + 1 + def __str__(self): return str(self.namedValues) + + def getName(self, value): return self.valToNameIdx.get(value) + def getValue(self, name): return self.nameToValIdx.get(name) + + def __getitem__(self, i): return self.namedValues[i] + def __len__(self): return len(self.namedValues) + + def __add__(self, namedValues): + return apply(self.__class__, self.namedValues + namedValues) + def __radd__(self, namedValues): + return apply(self.__class__, namedValues + tuple(self)) + + def clone(self, *namedValues): + return apply(self.__class__, tuple(self) + namedValues) + +# XXX clone/subtype? diff --git a/pyasn1/type/tag.py b/pyasn1/type/tag.py new file mode 100644 index 0000000..764fed2 --- /dev/null +++ b/pyasn1/type/tag.py @@ -0,0 +1,109 @@ +# ASN.1 types tags +try: + from sys import version_info +except ImportError: + version_info = (0, 0) # a really early version +from operator import getslice +from types import SliceType +from string import join +from pyasn1 import error + +tagClassUniversal = 0x00 +tagClassApplication = 0x40 +tagClassContext = 0x80 +tagClassPrivate = 0xC0 + +tagFormatSimple = 0x00 +tagFormatConstructed = 0x20 + +tagCategoryImplicit = 0x01 +tagCategoryExplicit = 0x02 +tagCategoryUntagged = 0x04 + +class Tag: + def __init__(self, tagClass, tagFormat, tagId): + self.__tag = (tagClass, tagFormat, tagId) + self.__uniqTag = (tagClass, tagId) + self.__hashedUniqTag = hash(self.__uniqTag) + def __repr__(self): + return '%s(tagClass=%s, tagFormat=%s, tagId=%s)' % ( + (self.__class__.__name__,) + self.__tag + ) + def __cmp__(self, other): return cmp(self.__uniqTag, other) + def __eq__(self, other): + return self is other or self.__hashedUniqTag == other + def __hash__(self): return self.__hashedUniqTag + def __getitem__(self, idx): return self.__tag[idx] + def __and__(self, (tagClass, tagFormat, tagId)): + return self.__class__( + self.__tag&tagClass, self.__tag&tagFormat, self.__tag&tagId + ) + def __or__(self, (tagClass, tagFormat, tagId)): + return self.__class__( + self.__tag[0]|tagClass, self.__tag[1]|tagFormat, self.__tag[2]|tagId + ) + +class TagSet: + def __init__(self, baseTag=(), *superTags): + self.__baseTag = baseTag + self.__superTags = superTags + self.__hashedSuperTags = hash(superTags) + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + join(map(lambda x: repr(x), self.__superTags), ', ') + ) + + def __add__(self, superTag): + return apply( + self.__class__, (self.__baseTag,) + self.__superTags + (superTag,) + ) + def __radd__(self, superTag): + return apply( + self.__class__, (self.__baseTag, superTag) + self.__superTags + ) + + def tagExplicitly(self, superTag): + tagClass, tagFormat, tagId = superTag + if tagClass == tagClassUniversal: + raise error.BadArgumentError( + 'Can\'t tag with UNIVERSAL-class tag' + ) + if tagFormat != tagFormatConstructed: + superTag = Tag(tagClass, tagFormatConstructed, tagId) + return self + superTag + + def tagImplicitly(self, superTag): + tagClass, tagFormat, tagId = superTag + if self.__superTags: + superTag = Tag(tagClass, self.__superTags[-1][1], tagId) + return self[:-1] + superTag + + def getBaseTag(self): return self.__baseTag + def __getitem__(self, idx): + if type(idx) == SliceType: + return apply(self.__class__, + (self.__baseTag,) + getslice(self.__superTags, + idx.start, idx.stop)) + return self.__superTags[idx] + if version_info < (2, 0): + def __getslice__(self, i, j): + return self[max(0, i):max(0, j):] + def __cmp__(self, other): return cmp(self.__superTags, other) + def __eq__(self, other): + return self is other or self.__hashedSuperTags == other + def __hash__(self): return self.__hashedSuperTags + def __len__(self): return len(self.__superTags) + def isSuperTagSetOf(self, tagSet): + # XXX optimization? + l = len(self.__superTags) + if len(tagSet) < l: + return + idx = 0 + while idx < l: + if self.__superTags[idx] != tagSet[idx]: + return + idx = idx + 1 + return 1 + +def initTagSet(tag): return TagSet(tag, tag) diff --git a/pyasn1/type/univ.py b/pyasn1/type/univ.py new file mode 100644 index 0000000..0142b38 --- /dev/null +++ b/pyasn1/type/univ.py @@ -0,0 +1,616 @@ +# ASN.1 "universal" data types +import string +import types +import operator +from pyasn1.type import base, tag, constraint, namedtype, namedval +from pyasn1 import error + +# "Simple" ASN.1 types (yet incomplete) + +class Integer(base.AbstractSimpleAsn1Item): + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02) + ) + namedValues = namedval.NamedValues() + defaultValue = 0 + def __init__(self, value=None, tagSet=None, subtypeSpec=None, + namedValues=None): + if namedValues is None: + self.__namedValues = self.namedValues + else: + self.__namedValues = namedValues + base.AbstractSimpleAsn1Item.__init__( + self, value, tagSet, subtypeSpec + ) + +# def __getattr__(self, attr): +# print 33, attr +# return base.AbstractSimpleAsn1Item.__getattr__(self, attr) + +#XXX def __coerce__(self, other): return long(self), long(other) + 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 __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 __div__(self, value): return self.clone(self._value / value) + def __rdiv__(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, modulo=None): + return self.clone(pow(value, self._value, modulo)) + def __lshift__(self, value): return self.clone(self._value << value) + def __rshift__(self, value): return self.clone(self._value >> value) + def __int__(self): return int(self._value) + def __long__(self): return long(self._value) + def __float__(self): return float(self._value) + def __abs__(self): return abs(self._value) + + def _prettyIn(self, value): + if type(value) != types.StringType: + return long(value) + r = self.__namedValues.getValue(value) + if r is not None: + return r + try: + return string.atoi(value) + except: + try: + return string.atol(value) + except: + raise error.PyAsn1Error( + 'Can\'t coerce %s into integer' % value + ) + + def _prettyOut(self, value): + r = self.__namedValues.getName(value) + if r is not None: + return '%s(%s)' % (r, value) + else: + return value + + def getNamedValues(self): return self.__namedValues + + def clone(self, value=None, tagSet=None, subtypeSpec=None, + namedValues=None): + if value is None and self._value is not self.defaultValue: + value = self._value + if tagSet is None: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + if namedValues is None: + namedValues = self.__namedValues + return self.__class__(value, tagSet, subtypeSpec, namedValues) + + def subtype(self, value=None, implicitTag=None, explicitTag=None, + subtypeSpec=None, namedValues=None): + if value is None and self._value is not self.defaultValue: + value = self._value + if implicitTag is not None: + tagSet = self._tagSet.tagImplicitly(implicitTag) + elif explicitTag is not None: + tagSet = self._tagSet.tagExplicitly(explicitTag) + else: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + else: + subtypeSpec = subtypeSpec + self._subtypeSpec + if namedValues is None: + namedValues = self.__namedValues + else: + namedValues = namedValues + self.__namedValues + return self.__class__(value, tagSet, subtypeSpec, namedValues) + +class Boolean(Integer): + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01), + ) + subtypeSpec = Integer.subtypeSpec+constraint.SingleValueConstraint(0,1) + namedValues = Integer.namedValues.clone(('False', 0), ('True', 1)) + defaultValue = 0 + +class BitString(base.AbstractSimpleAsn1Item): + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03) + ) + namedValues = namedval.NamedValues() + defaultValue = () + def __init__(self, value=None, tagSet=None, subtypeSpec=None, + namedValues=None): + if namedValues is None: + self.__namedValues = self.namedValues + else: + self.__namedValues = namedValues + base.AbstractSimpleAsn1Item.__init__( + self, value, tagSet, subtypeSpec + ) + + def clone(self, value=None, tagSet=None, subtypeSpec=None, + namedValues=None): + if value is None: value = self._value + if tagSet is None: tagSet = self._tagSet + if subtypeSpec is None: subtypeSpec = self._subtypeSpec + if namedValues is None: namedValues = self.__namedValues + return self.__class__(value, tagSet, subtypeSpec, namedValues) + + def subtype(self, value=None, implicitTag=None, explicitTag=None, + subtypeSpec=None, namedValues=None): + if value is None and self._value is not self.defaultValue: + value = self._value + if implicitTag is not None: + tagSet = self._tagSet.tagImplicitly(implicitTag) + elif explicitTag is not None: + tagSet = self._tagSet.tagExplicitly(explicitTag) + else: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + else: + subtypeSpec = subtypeSpec + self._subtypeSpec + if namedValues is None: + namedValues = self.__namedValues + else: + namedValues = namedValues + self.__namedValues + return self.__class__(value, tagSet, subtypeSpec, namedValues) + + def __str__(self): return str(tuple(self)) + + # Immutable sequence object protocol + + def __len__(self): return len(self._value) + def __getitem__(self, i): + if type(i) == types.SliceType: + return self.clone(operator.getslice(self._value, i.start, i.stop)) + else: + return self._value[i] + def __add__(self, value): return self.clone(self._value + value) + def __radd__(self, value): return self.clone(value + self._value) + def __mul__(self, value): return self.clone(self._value * value) + def __rmul__(self, value): return self.__mul__(value) + + # They won't be defined if version is at least 2.0 final + if base.version_info < (2, 0): + def __getslice__(self, i, j): + return self[max(0, i):max(0, j):] + + def _prettyIn(self, value): + r = [] + if not value: + return () + elif type(value) != types.StringType: + return value + elif value[0] == '\'': + if value[-2:] == '\'B': + for v in value[1:-2]: + r.append(int(v)) + elif value[-2:] == '\'H': + for v in value[1:-2]: + i = 4 + v = string.atoi(v, 16) + while i: + i = i - 1 + r.append((v>>i)&0x01) + else: + raise error.PyAsn1Error( + 'Bad bitstring value notation %s' % value + ) + else: + for i in string.split(value, ','): + i = self.__namedValues.getValue(i) + if i is None: + raise error.PyAsn1Error( + 'Unknown identifier \'%s\'' % i + ) + if i >= len(r): + r.extend([0]*(i-len(r)+1)) + r[i] = 1 + return tuple(r) + + def _prettyOut(self, value): + return '\'%s\'B' % string.join(map(str, value), '') + +class OctetString(base.AbstractSimpleAsn1Item): + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04) + ) + defaultValue = '' + + def _prettyIn(self, value): return str(value) + def _prettyOut(self, value): return repr(value) + + # Immutable sequence object protocol + + def __len__(self): return len(self._value) + def __getitem__(self, i): + if type(i) == types.SliceType: + return self.clone(operator.getslice(self._value, i.start, i.stop)) + else: + return self._value[i] + def __add__(self, value): return self.clone(self._value + value) + def __radd__(self, value): + print self.__class__ + print repr(value), repr(self._value) + return self.clone(value + self._value) + def __mul__(self, value): return self.clone(self._value * value) + def __rmul__(self, value): return self.__mul__(value) + + # They won't be defined if version is at least 2.0 final + if base.version_info < (2, 0): + def __getslice__(self, i, j): + return self[max(0, i):max(0, j):] + +class Null(OctetString): + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05) + ) + subtypeSpec = OctetString.subtypeSpec+constraint.SingleValueConstraint('') + defaultValue = '' + +class ObjectIdentifier(base.AbstractSimpleAsn1Item): + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06) + ) + defaultValue = () + + def __add__(self, other): return self.clone(self._value + other) + def __radd__(self, other): return self.clone(other + self._value) + + # Sequence object protocol + + def __len__(self): return len(self._value) + def __getitem__(self, i): + if type(i) == types.SliceType: + return self.clone( + operator.getslice(self._value, i.start, i.stop) + ) + else: + return self._value[i] + + # They won't be defined if version is at least 2.0 final + if base.version_info < (2, 0): + def __getslice__(self, i, j): + return self[max(0, i):max(0, j):] + + def index(self, suboid): return self._value.index(suboid) + + def isPrefixOf(self, value): + """Returns true if argument OID resides deeper in the OID tree""" + l = len(self._value) + if l <= len(value): + if self._value[:l] == value[:l]: + return 1 + return 0 + + def _prettyIn(self, value): + """Dotted -> tuple of numerics OID converter""" + if type(value) is types.TupleType: + return value + if type(value) is not types.StringType: + return tuple(value) + r = [] + for element in filter(None, string.split(value, '.')): + try: + r.append(string.atoi(element, 0)) + except string.atoi_error: + try: + r.append(string.atol(element, 0)) + except string.atol_error, why: + raise error.PyAsn1Error( + 'Malformed Object ID %s at %s: %s' % + (str(value), self.__class__.__name__, why) + ) + return tuple(r) + + def _prettyOut(self, value): + """Tuple of numerics -> dotted string OID converter""" + r = [] + for subOid in value: + r.append(str(subOid)) + if r[-1] and r[-1][-1] == 'L': + r[-1][-1] = r[-1][:-1] + return string.join(r, '.') + +class Real(base.AbstractSimpleAsn1Item): + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09) + ) + defaultValue = 0.0 + +class Enumerated(base.AbstractSimpleAsn1Item): + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x10) + ) + defaultValue = 0 + +# "Structured" ASN.1 types + +class SetOf(base.AbstractConstructedAsn1Item): + componentType = None + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) + ) + + def _cloneComponentValues(self, myClone): + idx = 0; l = len(self._componentValues) + while idx < l: + if self._componentValues[idx] is not None: + myClone.setComponentByPosition( + idx, self._componentValues[idx].clone() + ) + idx = idx + 1 + + def _verifyComponent(self, idx, value): + if self._componentType is not None and \ + not self._componentType.isSuperTypeOf(value): + raise error.PyAsn1Error('Component type error %s' % value) + + def getComponentByPosition(self, idx): return self._componentValues[idx] + def setComponentByPosition(self, idx, value=None): + l = len(self._componentValues) + if idx >= l: + self._componentValues = self._componentValues + (idx-l+1)*[None] + if value is None: + if self._componentValues[idx] is None: + if self._componentType is None: + raise error.PyAsn1Error('Component type not defined') + self._componentValues[idx] = self._componentType.clone() + return self + elif type(value) != types.InstanceType: + if self._componentType is None: + raise error.PyAsn1Error('Component type not defined') + if isinstance(self._componentType, base.AbstractSimpleAsn1Item): + value = self._componentType.clone(value=value) + else: + raise error.PyAsn1Error('Instance value required') + if self._componentType: + self._verifyComponent(idx, value) + self._verifySubtypeSpec(value, idx) + self._componentValues[idx] = value + return self + + def getComponentTypeMap(self): + if self._componentType is not None: + return self._componentType.getTypeMap() + + def prettyPrinter(self, scope=0): + scope = scope + 1 + r = self.__class__.__name__ + ':\n' + for idx in range(len(self._componentValues)): + r = r + ' '*scope + self._componentValues[idx].prettyPrinter(scope) + return r + +class SequenceOf(SetOf): + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) + ) + +class SequenceAndSetBase(base.AbstractConstructedAsn1Item): + componentType = namedtype.NamedTypes() + def _cloneComponentValues(self, myClone): + idx = 0; l = len(self._componentValues) + while idx < l: + if self._componentValues[idx] is not None: + myClone.setComponentByPosition( + idx, self._componentValues[idx].clone() + ) + idx = idx + 1 + + def _verifyComponent(self, idx, value): + componentType = self._componentType + if componentType: + if idx >= len(componentType): + raise error.PyAsn1Error( + 'Component type error out of range' + ) + t = componentType[idx].getType() + if not t.isSuperTypeOf(value): + raise error.PyAsn1Error('Component type error %s vs %s' % + (t.getTagSet(), value.getTagSet())) + + def getComponentByName(self, name): + return self.getComponentByPosition( + self._componentType.getPositionByName(name) + ) + def setComponentByName(self, name, value=None): + return self.setComponentByPosition( + self._componentType.getPositionByName(name), value + ) + + def getComponentByPosition(self, idx): + try: + return self._componentValues[idx] + except IndexError: + if idx < len(self._componentType): + return + raise + def setComponentByPosition(self, idx, value=None): + l = len(self._componentValues) + if idx >= l: + self._componentValues = self._componentValues + (idx-l+1)*[None] + if value is None: + if self._componentValues[idx] is None: + self._componentValues[idx] = self._componentType.getTypeByPosition(idx).clone() + return self + elif type(value) != types.InstanceType: + t = self._componentType.getTypeByPosition(idx) + if isinstance(t, base.AbstractSimpleAsn1Item): + value = t.clone(value=value) + else: + raise error.PyAsn1Error('Instance value required') + if self._componentType: + self._verifyComponent(idx, value) + self._verifySubtypeSpec(value, idx) + self._componentValues[idx] = value + return self + + def getDefaultComponentByPosition(self, idx): + if self._componentType[idx].isDefaulted: + return self._componentType[idx].getType() + + def getComponentType(self): + if self._componentType: + return self._componentType + + def setDefaultComponents(self): + idx = len(self._componentType) + while idx: + idx = idx - 1 + if self._componentType[idx].isDefaulted: + if self.getComponentByPosition(idx) is None: + self.setComponentByPosition(idx) + elif not self._componentType[idx].isOptional: + if self.getComponentByPosition(idx) is None: + raise error.PyAsn1Error( + 'Uninitialized component #%s at %s' % (idx, repr(self)) + ) + + def prettyPrinter(self, scope=0): + scope = scope+1 + r = self.__class__.__name__ + ':\n' + for idx in range(len(self._componentValues)): + if self._componentValues[idx] is not None: + r = r + ' '*scope + componentType = self.getComponentType() + if componentType is None: + r = r + '??' + else: + r = r + componentType.getNameByPosition(idx) + r = '%s=%s\n' % ( + r, self._componentValues[idx].prettyPrinter(scope) + ) + return r + +class Sequence(SequenceAndSetBase): + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) + ) + + def getComponentTypeMapNearPosition(self, idx): + return self._componentType.getTypeMapNearPosition(idx) + + def getComponentPositionNearType(self, tagSet, idx): + return self._componentType.getPositionNearType(tagSet, idx) + +class Set(SequenceAndSetBase): + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) + ) + + def getComponentByType(self, tagSet, innerFlag=0): + c = self.getComponentByPosition( + self._componentType.getPositionByType(tagSet) + ) + if innerFlag and hasattr(c, 'getComponent'): + # get inner component by inner tagSet + return c.getComponent(True) + else: + # get outer component by inner tagSet + return c + + def setComponentByType(self, tagSet, value=None, innerFlag=0): + idx = self._componentType.getPositionByType(tagSet) + t = self._componentType.getTypeByPosition(idx) + if innerFlag: # set inner component by inner tagSet + if t.getTagSet(): + self.setComponentByPosition(idx, value) + else: + t = self.setComponentByPosition(idx).getComponentByPosition(idx) + t.setComponentByType(tagSet, value, innerFlag) + else: # set outer component by inner tagSet + self.setComponentByPosition(idx, value) + + def getComponentTypeMap(self): return self._componentType.getTypeMap(1) + + def getComponentPositionByType(self, tagSet): + return self._componentType.getPositionByType(tagSet) + +class Choice(Set): + tagSet = tag.TagSet() # untagged + sizeSpec = constraint.ConstraintsIntersection( + constraint.ValueSizeConstraint(1, 1) + ) + + def __cmp__(self, other): + if self._componentValues: + return cmp(self._componentValues[self._currentIdx], other) + return -1 + + def verifySizeSpec(self): self._sizeSpec( + ' '*int(self.getComponent() is not None) # hackerish XXX + ) + + def _cloneComponentValues(self, myClone): + try: + c = self.getComponent() + except error.PyAsn1Error: + pass + else: + tagSet = getattr(c, 'getEffectiveTagSet', c.getTagSet)() + myClone.setComponentByType(tagSet, c.clone()) + + def setComponentByPosition(self, idx, value=None): + l = len(self._componentValues) + if idx >= l: + self._componentValues = self._componentValues + (idx-l+1)*[None] + if hasattr(self, '_currentIdx'): + self._componentValues[self._currentIdx] = None + if value is None: + if self._componentValues[idx] is None: + self._componentValues[idx] = self._componentType.getTypeByPosition(idx).clone() + self._currentIdx = idx + return self + elif type(value) != types.InstanceType: + value = self._componentType.getTypeByPosition(idx).clone( + value=value + ) + if self._componentType: + self._verifyComponent(idx, value) + self._verifySubtypeSpec(value, idx) + self._componentValues[idx] = value + self._currentIdx = idx + return self + + def getMinTagSet(self): + if self._tagSet: + return self._tagSet + else: + return self._componentType.genMinTagSet() + + def getEffectiveTagSet(self): + if self._tagSet: + return self._tagSet + else: + c = self.getComponent() + return getattr(c, 'getEffectiveTagSet', c.getTagSet)() + + def getTypeMap(self): + if self._tagSet: + return Set.getTypeMap(self) + else: + return Set.getComponentTypeMap(self) + + def getComponent(self, innerFlag=0): + if hasattr(self, '_currentIdx'): + c = self._componentValues[self._currentIdx] + if innerFlag and hasattr(c, 'getComponent'): + return c.getComponent(innerFlag) + else: + return c + else: + raise error.PyAsn1Error('Component not chosen') + + def setDefaultComponents(self): pass + +# XXX +# coercion rules? diff --git a/pyasn1/type/useful.py b/pyasn1/type/useful.py new file mode 100644 index 0000000..a7139c2 --- /dev/null +++ b/pyasn1/type/useful.py @@ -0,0 +1,12 @@ +# ASN.1 "useful" types +from pyasn1.type import char, tag + +class GeneralizedTime(char.VisibleString): + tagSet = char.VisibleString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24) + ) + +class UTCTime(char.VisibleString): + tagSet = char.VisibleString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23) + ) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..d47573e --- /dev/null +++ b/setup.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +from distutils.core import setup + +setup(name="pyasn1", + version="0.0.2a", + description="ASN.1 library for Python", + author="Ilya Etingof", + author_email="ilya@glas.net ", + url="http://sourceforge.net/projects/pyasn1/", + packages = [ 'pyasn1', + 'pyasn1.v1', + 'pyasn1.v1.type', + 'pyasn1.v1.codec', + 'pyasn1.v1.codec.ber', + 'pyasn1.v1.codec.cer', + 'pyasn1.v1.codec.der' ], + license="BSD" + ) diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/__init__.py diff --git a/test/codec/__init__.py b/test/codec/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/codec/__init__.py diff --git a/test/codec/ber/__init__.py b/test/codec/ber/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/codec/ber/__init__.py diff --git a/test/codec/ber/decoder.py b/test/codec/ber/decoder.py new file mode 100644 index 0000000..16eb771 --- /dev/null +++ b/test/codec/ber/decoder.py @@ -0,0 +1,301 @@ +from pyasn1.type import tag, namedtype, univ +from pyasn1.codec.ber import decoder +from pyasn1 import error +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +class IntegerDecoderTestCase(unittest.TestCase): + def testPosInt(self): + assert decoder.decode('\x02\x01\x0c') == (12, '') + def testNegInt(self): + assert decoder.decode('\x02\x01\xf4') == (-12, '') + def testZero(self): + assert decoder.decode('\x02\x01\x00') == (0, '') + def testMinusOne(self): + assert decoder.decode('\x02\x01\xff') == (-1, '') + def testPosLong(self): + assert decoder.decode( + '\x02\t\x00\xff\xff\xff\xff\xff\xff\xff\xff' + ) == (0xffffffffffffffffl, '') + def testNegLong(self): + assert decoder.decode( + '\x02\t\xff\x00\x00\x00\x00\x00\x00\x00\x01' + ) == (-0xffffffffffffffffl, '') + def testSpec(self): + try: + decoder.decode( + '\x02\x01\x0c', asn1Spec=univ.Null() + ) == (12, '') + except error.PyAsn1Error: + pass + else: + assert 0, 'wrong asn1Spec worked out' + assert decoder.decode( + '\x02\x01\x0c', asn1Spec=univ.Integer() + ) == (12, '') + +class BooleanDecoderTestCase(unittest.TestCase): + def testTrue(self): + assert decoder.decode('\x01\x01\x01') == (1, '') + def testExtraTrue(self): + assert decoder.decode('\x01\x01\x01\0x22') == (1, '\0x22') + def testFalse(self): + assert decoder.decode('\x01\x01\x00') == (0, '') + +class BitStringDecoderTestCase(unittest.TestCase): + def testDefMode(self): + assert decoder.decode( + '\x03\x03\x00\xa9\x8a' + ) == ((1,0,1,0,1,0,0,1,1,0,0,0,1,0,1,0), '') + def testIndefMode(self): + assert decoder.decode( + '#\x80\x03\x03\x00\xa9\x8a\x00\x00' + ) == ((1,0,1,0,1,0,0,1,1,0,0,0,1,0,1,0), '') + + def testDefModeChunked(self): + assert decoder.decode( + '#\x08\x03\x02\x00\xa9\x03\x02\x00\x8a' + ) == ((1,0,1,0,1,0,0,1,1,0,0,0,1,0,1,0), '') + def testIndefModeChunked(self): + assert decoder.decode( + '#\x80\x03\x02\x00\xa9\x03\x02\x00\x8a\x00\x00' + ) == ((1,0,1,0,1,0,0,1,1,0,0,0,1,0,1,0), '') + +class OctetStringDecoderTestCase(unittest.TestCase): + def testDefMode(self): + assert decoder.decode( + '\x04\x0fQuick brown fox' + ) == ('Quick brown fox', '') + def testIndefMode(self): + assert decoder.decode( + '$\x80\x04\x0fQuick brown fox\x00\x00' + ) == ('Quick brown fox', '') + def testDefModeChunked(self): + assert decoder.decode( + '$\x17\x04\x04Quic\x04\x04k br\x04\x04own \x04\x03fox' + ) == ('Quick brown fox', '') + def testIndefModeChunked(self): + assert decoder.decode( + '$\x80\x04\x04Quic\x04\x04k br\x04\x04own \x04\x03fox\x00\x00' + ) == ('Quick brown fox', '') + +class ExpTaggedOctetStringDecoderTestCase(unittest.TestCase): + def setUp(self): + self.o = univ.OctetString( + 'Quick brown fox', + tagSet=univ.OctetString.tagSet.tagExplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 5) + )) + def testDefMode(self): + assert self.o.isSameTypeWith(decoder.decode( + 'e\x11\x04\x0fQuick brown fox' + )[0]) + def testIndefMode(self): + assert self.o.isSameTypeWith(decoder.decode( + 'e\x80$\x80\x04\x0fQuick brown fox\x00\x00\x00\x00' + )[0]) + def testDefModeChunked(self): + assert self.o.isSameTypeWith(decoder.decode( + 'e\x19$\x17\x04\x04Quic\x04\x04k br\x04\x04own \x04\x03fox' + )[0]) + def testIndefModeChunked(self): + assert self.o.isSameTypeWith(decoder.decode( + 'e\x80$\x80\x04\x04Quic\x04\x04k br\x04\x04own \x04\x03fox\x00\x00\x00\x00' + )[0]) + +class NullDecoderTestCase(unittest.TestCase): + def testNull(self): + assert decoder.decode('\x05\x00') == ('', '') + +class ObjectIdentifierDecoderTestCase(unittest.TestCase): + def testNull(self): + assert decoder.decode( + '\x06\x06+\x06\x00\xbf\xff~' + ) == ((1,3,6,0,0xffffe), '') + +class SequenceDecoderTestCase(unittest.TestCase): + def 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)), + )) + self.s.setComponentByPosition(0, univ.Null()) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + self.s.setDefaultComponents() + + def testWithOptionalAndDefaultedDefMode(self): + assert decoder.decode( + '0\x12\x05\x00\x04\x0bquick brown\x02\x01\x01', + ) == (self.s, '') + + def testWithOptionalAndDefaultedIndefMode(self): + assert decoder.decode( + '0\x80\x05\x00$\x80\x04\x0bquick brown\x00\x00\x02\x01\x01\x00\x00' + ) == (self.s, '') + + def testWithOptionalAndDefaultedDefModeChunked(self): + assert decoder.decode( + '0\x18\x05\x00$\x11\x04\x04quic\x04\x04k br\x04\x03own\x02\x01\x01' + ) == (self.s, '') + + def testWithOptionalAndDefaultedIndefModeChunked(self): + assert decoder.decode( + '0\x80\x05\x00$\x80\x04\x04quic\x04\x04k br\x04\x03own\x00\x00\x02\x01\x01\x00\x00' + ) == (self.s, '') + +class GuidedSequenceDecoderTestCase(unittest.TestCase): + def setUp(self): + self.s = univ.Sequence(componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null()), + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + )) + + def __init(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null()) + self.s.setDefaultComponents() + + def __initWithOptional(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null()) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setDefaultComponents() + + def __initWithDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null()) + self.s.setComponentByPosition(2, univ.Integer(1)) + self.s.setDefaultComponents() + + 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)) + self.s.setDefaultComponents() + + def testDefMode(self): + self.__init() + assert decoder.decode( + '0\x02\x05\x00', asn1Spec=self.s + ) == (self.s, '') + + def testIndefMode(self): + self.__init() + assert decoder.decode( + '0\x80\x05\x00\x00\x00', asn1Spec=self.s + ) == (self.s, '') + + def testDefModeChunked(self): + self.__init() + assert decoder.decode( + '0\x02\x05\x00', asn1Spec=self.s + ) == (self.s, '') + + def testIndefModeChunked(self): + self.__init() + assert decoder.decode( + '0\x80\x05\x00\x00\x00', asn1Spec=self.s + ) == (self.s, '') + + def testWithOptionalDefMode(self): + self.__initWithOptional() + assert decoder.decode( + '0\x0f\x05\x00\x04\x0bquick brown', asn1Spec=self.s + ) == (self.s, '') + + def testWithOptionaIndefMode(self): + self.__initWithOptional() + assert decoder.decode( + '0\x80\x05\x00$\x80\x04\x0bquick brown\x00\x00\x00\x00', + asn1Spec=self.s + ) == (self.s, '') + + def testWithOptionalDefModeChunked(self): + self.__initWithOptional() + assert decoder.decode( + '0\x15\x05\x00$\x11\x04\x04quic\x04\x04k br\x04\x03own', + asn1Spec=self.s + ) == (self.s, '') + + def testWithOptionalIndefModeChunked(self): + self.__initWithOptional() + assert decoder.decode( + '0\x80\x05\x00$\x80\x04\x04quic\x04\x04k br\x04\x03own\x00\x00\x00\x00', + asn1Spec=self.s + ) == (self.s, '') + + def testWithDefaultedDefMode(self): + self.__initWithDefaulted() + assert decoder.decode( + '0\x05\x05\x00\x02\x01\x01', asn1Spec=self.s + ) == (self.s, '') + + def testWithDefaultedIndefMode(self): + self.__initWithDefaulted() + assert decoder.decode( + '0\x80\x05\x00\x02\x01\x01\x00\x00', asn1Spec=self.s + ) == (self.s, '') + + def testWithDefaultedDefModeChunked(self): + self.__initWithDefaulted() + assert decoder.decode( + '0\x05\x05\x00\x02\x01\x01', asn1Spec=self.s + ) == (self.s, '') + + def testWithDefaultedIndefModeChunked(self): + self.__initWithDefaulted() + assert decoder.decode( + '0\x80\x05\x00\x02\x01\x01\x00\x00', asn1Spec=self.s + ) == (self.s, '') + + def testWithOptionalAndDefaultedDefMode(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + '0\x12\x05\x00\x04\x0bquick brown\x02\x01\x01', asn1Spec=self.s + ) == (self.s, '') + + def testWithOptionalAndDefaultedIndefMode(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + '0\x80\x05\x00$\x80\x04\x0bquick brown\x00\x00\x02\x01\x01\x00\x00', asn1Spec=self.s + ) == (self.s, '') + + def testWithOptionalAndDefaultedDefModeChunked(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + '0\x18\x05\x00$\x11\x04\x04quic\x04\x04k br\x04\x03own\x02\x01\x01', asn1Spec=self.s + ) == (self.s, '') + + def testWithOptionalAndDefaultedIndefModeChunked(self): + self.__initWithOptionalAndDefaulted() + assert decoder.decode( + '0\x80\x05\x00$\x80\x04\x04quic\x04\x04k br\x04\x03own\x00\x00\x02\x01\x01\x00\x00', asn1Spec=self.s + ) == (self.s, '') + +class ChoiceDecoderTestCase(unittest.TestCase): + def setUp(self): + self.s = univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null()), + namedtype.NamedType('number', univ.Integer()) + )) + + def testBySpec(self): + self.s.setComponentByPosition(0, univ.Null()) + assert decoder.decode( + '\x05\x00', asn1Spec=self.s + ) == (self.s, '') + + def testWithoutSpec(self): + self.s.setComponentByPosition(0, univ.Null()) + assert decoder.decode('\x05\x00') == (self.s, '') + assert decoder.decode('\x05\x00') == (univ.Null(), '') + +if __name__ == '__main__': unittest.main() diff --git a/test/codec/ber/encoder.py b/test/codec/ber/encoder.py new file mode 100644 index 0000000..dc86f7e --- /dev/null +++ b/test/codec/ber/encoder.py @@ -0,0 +1,240 @@ +from pyasn1.type import tag, namedtype, univ +from pyasn1.codec.ber import encoder +from pyasn1 import error +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +class IntegerEncoderTestCase(unittest.TestCase): + def testPosInt(self): + assert encoder.encode(univ.Integer(12)) == '\x02\x01\x0c' + def testNegInt(self): + assert encoder.encode(univ.Integer(-12)) == '\x02\x01\xf4' + def testZero(self): + assert encoder.encode(univ.Integer(0)) == '\x02\x01\x00' + def testMinusOne(self): + assert encoder.encode(univ.Integer(-1)) == '\x02\x01\xff' + def testPosLong(self): + assert encoder.encode( + univ.Integer(0xffffffffffffffffl) + ) == '\x02\t\x00\xff\xff\xff\xff\xff\xff\xff\xff' + def testNegLong(self): + assert encoder.encode( + univ.Integer(-0xffffffffffffffffl) + ) == '\x02\t\xff\x00\x00\x00\x00\x00\x00\x00\x01' + +class BooleanEncoderTestCase(unittest.TestCase): + def testTrue(self): + assert encoder.encode(univ.Boolean(1)) == '\x01\x01\x01' + def testFalse(self): + assert encoder.encode(univ.Boolean(0)) == '\x01\x01\x00' + +class BitStringEncoderTestCase(unittest.TestCase): + def setUp(self): + self.b = univ.BitString("'A98A'H") + def testDefMode(self): + assert encoder.encode(self.b) == '\x03\x03\x00\xa9\x8a' + + def testIndefMode(self): + assert encoder.encode( + self.b, defMode=0 + ) == '\x03\x03\x00\xa9\x8a' + + def testDefModeChunked(self): + assert encoder.encode( + self.b, maxChunkSize=1 + ) == '#\x08\x03\x02\x00\xa9\x03\x02\x00\x8a' + + def testIndefModeChunked(self): + assert encoder.encode( + self.b, defMode=0, maxChunkSize=1 + ) == '#\x80\x03\x02\x00\xa9\x03\x02\x00\x8a\x00\x00' + +class OctetStringEncoderTestCase(unittest.TestCase): + def setUp(self): + self.o = univ.OctetString('Quick brown fox') + def testDefMode(self): + assert encoder.encode(self.o) == '\x04\x0fQuick brown fox' + def testIndefMode(self): + assert encoder.encode( + self.o, defMode=0 + ) == '\x04\x0fQuick brown fox' + def testDefModeChunked(self): + assert encoder.encode( + self.o, maxChunkSize=4 + ) == '$\x17\x04\x04Quic\x04\x04k br\x04\x04own \x04\x03fox' + def testIndefModeChunked(self): + assert encoder.encode( + self.o, defMode=0, maxChunkSize=4 + ) == '$\x80\x04\x04Quic\x04\x04k br\x04\x04own \x04\x03fox\x00\x00' + +class ExpTaggedOctetStringEncoderTestCase(unittest.TestCase): + def setUp(self): + self.o = univ.OctetString().subtype( + value='Quick brown fox', + explicitTag=tag.Tag(tag.tagClassApplication,tag.tagFormatSimple,5) + ) + def testDefMode(self): + assert encoder.encode(self.o) == 'e\x11\x04\x0fQuick brown fox' + def testIndefMode(self): + assert encoder.encode( + self.o, defMode=0 + ) == 'e\x80\x04\x0fQuick brown fox\x00\x00' + def testDefModeChunked(self): + assert encoder.encode( + self.o, defMode=1, maxChunkSize=4 + ) == 'e\x19$\x17\x04\x04Quic\x04\x04k br\x04\x04own \x04\x03fox' + def testIndefModeChunked(self): + assert encoder.encode( + self.o, defMode=0, maxChunkSize=4 + ) == 'e\x80$\x80\x04\x04Quic\x04\x04k br\x04\x04own \x04\x03fox\x00\x00\x00\x00' + +class NullEncoderTestCase(unittest.TestCase): + def testNull(self): + assert encoder.encode(univ.Null()) == '\x05\x00' + +class ObjectIdentifierEncoderTestCase(unittest.TestCase): + def testNull(self): + assert encoder.encode( + univ.ObjectIdentifier((1,3,6,0,0xffffe)) + ) == '\x06\x06+\x06\x00\xbf\xff~' + +class SequenceEncoderTestCase(unittest.TestCase): + def setUp(self): + self.s = univ.Sequence(componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null()), + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)), + )) + + def __init(self): + self.s.clear() + self.s.setComponentByPosition(0) + + def __initWithOptional(self): + self.s.clear() + self.s.setComponentByPosition(0) + self.s.setComponentByPosition(1, 'quick brown') + + def __initWithDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0) + self.s.setComponentByPosition(2, 1) + + def __initWithOptionalAndDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null()) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testDefMode(self): + self.__init() + assert encoder.encode(self.s) == '0\x02\x05\x00' + + def testIndefMode(self): + self.__init() + assert encoder.encode( + self.s, defMode=0 + ) == '0\x80\x05\x00\x00\x00' + + def testDefModeChunked(self): + self.__init() + assert encoder.encode( + self.s, defMode=1, maxChunkSize=4 + ) == '0\x02\x05\x00' + + def testIndefModeChunked(self): + self.__init() + assert encoder.encode( + self.s, defMode=0, maxChunkSize=4 + ) == '0\x80\x05\x00\x00\x00' + + def testWithOptionalDefMode(self): + self.__initWithOptional() + assert encoder.encode(self.s) == '0\x0f\x05\x00\x04\x0bquick brown' + + def testWithOptionalIndefMode(self): + self.__initWithOptional() + assert encoder.encode( + self.s, defMode=0 + ) == '0\x80\x05\x00\x04\x0bquick brown\x00\x00' + + def testWithOptionalDefModeChunked(self): + self.__initWithOptional() + assert encoder.encode( + self.s, defMode=1, maxChunkSize=4 + ) == '0\x15\x05\x00$\x11\x04\x04quic\x04\x04k br\x04\x03own' + + def testWithOptionalIndefModeChunked(self): + self.__initWithOptional() + assert encoder.encode( + self.s, defMode=0, maxChunkSize=4 + ) == '0\x80\x05\x00$\x80\x04\x04quic\x04\x04k br\x04\x03own\x00\x00\x00\x00' + + def testWithDefaultedDefMode(self): + self.__initWithDefaulted() + assert encoder.encode(self.s) == '0\x05\x05\x00\x02\x01\x01' + + def testWithDefaultedIndefMode(self): + self.__initWithDefaulted() + assert encoder.encode( + self.s, defMode=0 + ) == '0\x80\x05\x00\x02\x01\x01\x00\x00' + + def testWithDefaultedDefModeChunked(self): + self.__initWithDefaulted() + assert encoder.encode( + self.s, defMode=1, maxChunkSize=4 + ) == '0\x05\x05\x00\x02\x01\x01' + + def testWithDefaultedIndefModeChunked(self): + self.__initWithDefaulted() + assert encoder.encode( + self.s, defMode=0, maxChunkSize=4 + ) == '0\x80\x05\x00\x02\x01\x01\x00\x00' + + def testWithOptionalAndDefaultedDefMode(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode(self.s) == '0\x12\x05\x00\x04\x0bquick brown\x02\x01\x01' + + def testWithOptionalAndDefaultedIndefMode(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode( + self.s, defMode=0 + ) == '0\x80\x05\x00\x04\x0bquick brown\x02\x01\x01\x00\x00' + + def testWithOptionalAndDefaultedDefModeChunked(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode( + self.s, defMode=1, maxChunkSize=4 + ) == '0\x18\x05\x00$\x11\x04\x04quic\x04\x04k br\x04\x03own\x02\x01\x01' + + def testWithOptionalAndDefaultedIndefModeChunked(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode( + self.s, defMode=0, maxChunkSize=4 + ) == '0\x80\x05\x00$\x80\x04\x04quic\x04\x04k br\x04\x03own\x00\x00\x02\x01\x01\x00\x00' + +class ChoiceEncoderTestCase(unittest.TestCase): + def setUp(self): + self.s = univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null()), + namedtype.NamedType('number', univ.Integer()) + )) + + def testEmpty(self): + try: + encoder.encode(self.s) + except error.PyAsn1Error: + pass + else: + assert 0, 'encoded unset choice' + + def testFilled(self): + self.s.setComponentByPosition(0, univ.Null()) + assert encoder.encode(self.s) == '\x05\x00' + +if __name__ == '__main__': unittest.main() diff --git a/test/codec/ber/suite.py b/test/codec/ber/suite.py new file mode 100644 index 0000000..d68c51d --- /dev/null +++ b/test/codec/ber/suite.py @@ -0,0 +1,17 @@ +import encoder, decoder +from pyasn1 import error +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +suite = unittest.TestSuite() +loader = unittest.TestLoader() +for m in (encoder, decoder): + suite.addTest(loader.loadTestsFromModule(m)) + +def runTests(): unittest.TextTestRunner(verbosity=2).run(suite) + +if __name__ == '__main__': runTests() diff --git a/test/codec/cer/__init__.py b/test/codec/cer/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/codec/cer/__init__.py diff --git a/test/codec/cer/decoder.py b/test/codec/cer/decoder.py new file mode 100644 index 0000000..c37baf5 --- /dev/null +++ b/test/codec/cer/decoder.py @@ -0,0 +1,26 @@ +from pyasn1.type import univ +from pyasn1.codec.cer import decoder +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +class BooleanDecoderTestCase(unittest.TestCase): + def testTrue(self): + assert decoder.decode('\x01\x01\xff') == (1, '') + def testFalse(self): + assert decoder.decode('\x01\x01\x00') == (0, '') + +class OctetStringDecoderTestCase(unittest.TestCase): + def testShortMode(self): + assert decoder.decode( + '\x04\x0fQuick brown fox' + ) == ('Quick brown fox', '') + def testLongMode(self): + assert decoder.decode( + '$\x80\x04\x82\x03\xe8' + 'Q'*1000 + '\x04\x01Q\x00\x00' + ) == ('Q'*1001, '') + +if __name__ == '__main__': unittest.main() diff --git a/test/codec/cer/encoder.py b/test/codec/cer/encoder.py new file mode 100644 index 0000000..a045831 --- /dev/null +++ b/test/codec/cer/encoder.py @@ -0,0 +1,102 @@ +from pyasn1.type import namedtype, univ +from pyasn1.codec.cer import encoder +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +class BooleanEncoderTestCase(unittest.TestCase): + def testTrue(self): + assert encoder.encode(univ.Boolean(1)) == '\x01\x01\xff' + def testFalse(self): + assert encoder.encode(univ.Boolean(0)) == '\x01\x01\x00' + +class BitStringEncoderTestCase(unittest.TestCase): + def testShortMode(self): + assert encoder.encode( + univ.BitString((1,0)*501) + ) == '\x03\x7f\x06' + '\xaa' * 125 + '\x80' + + def testLongMode(self): + assert encoder.encode( + univ.BitString((1,0)*501) + ) == '\x03\x7f\x06' + '\xaa' * 125 + '\x80' + +class OctetStringEncoderTestCase(unittest.TestCase): + def testShortMode(self): + assert encoder.encode( + univ.OctetString('Quick brown fox') + ) == '\x04\x0fQuick brown fox' + def testLongMode(self): + assert encoder.encode( + univ.OctetString('Q'*1001) + ) == '$\x80\x04\x82\x03\xe8' + 'Q'*1000 + '\x04\x01Q\x00\x00' + +class SetEncoderTestCase(unittest.TestCase): + def setUp(self): + self.s = univ.Set(componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null()), + namedtype.OptionalNamedType('first-name', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(33)) + )) + + def __init(self): + self.s.clear() + self.s.setComponentByPosition(0) + def __initWithOptional(self): + self.s.clear() + self.s.setComponentByPosition(0) + self.s.setComponentByPosition(1, 'quick brown') + + def __initWithDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0) + self.s.setComponentByPosition(2, 1) + + def __initWithOptionalAndDefaulted(self): + self.s.clear() + self.s.setComponentByPosition(0, univ.Null()) + self.s.setComponentByPosition(1, univ.OctetString('quick brown')) + self.s.setComponentByPosition(2, univ.Integer(1)) + + def testIndefMode(self): + self.__init() + assert encoder.encode(self.s) == '1\x80\x05\x00\x00\x00' + + def testWithOptionalIndefMode(self): + self.__initWithOptional() + assert encoder.encode( + self.s + ) == '1\x80\x04\x0bquick brown\x05\x00\x00\x00' + + def testWithDefaultedIndefMode(self): + self.__initWithDefaulted() + assert encoder.encode( + self.s + ) == '1\x80\x02\x01\x01\x05\x00\x00\x00' + + def testWithOptionalAndDefaultedIndefMode(self): + self.__initWithOptionalAndDefaulted() + assert encoder.encode( + self.s + ) == '1\x80\x02\x01\x01\x04\x0bquick brown\x05\x00\x00\x00' + +class SetWithChoiceEncoderTestCase(unittest.TestCase): + def setUp(self): + c = univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('actual', univ.Boolean()) + )) + 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) == '1\x80\x01\x01\xff\x05\x00\x00\x00' + +if __name__ == '__main__': unittest.main() diff --git a/test/codec/cer/suite.py b/test/codec/cer/suite.py new file mode 100644 index 0000000..4cbf2d6 --- /dev/null +++ b/test/codec/cer/suite.py @@ -0,0 +1,17 @@ +from pyasn1 import error +import encoder, decoder +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +suite = unittest.TestSuite() +loader = unittest.TestLoader() +for m in (encoder, decoder): + suite.addTest(loader.loadTestsFromModule(m)) + +def runTests(): unittest.TextTestRunner(verbosity=2).run(suite) + +if __name__ == '__main__': runTests() diff --git a/test/codec/der/__init__.py b/test/codec/der/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/codec/der/__init__.py diff --git a/test/codec/der/decoder.py b/test/codec/der/decoder.py new file mode 100644 index 0000000..1db0f97 --- /dev/null +++ b/test/codec/der/decoder.py @@ -0,0 +1,16 @@ +from pyasn1.type import univ +from pyasn1.codec.der import decoder +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +class OctetStringDecoderTestCase(unittest.TestCase): + def testShortMode(self): + assert decoder.decode( + '\x04\x0fQuick brown fox' + ) == ('Quick brown fox', '') + +if __name__ == '__main__': unittest.main() diff --git a/test/codec/der/encoder.py b/test/codec/der/encoder.py new file mode 100644 index 0000000..8f41ebd --- /dev/null +++ b/test/codec/der/encoder.py @@ -0,0 +1,39 @@ +from pyasn1.type import namedtype, univ +from pyasn1.codec.der import encoder +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +class OctetStringEncoderTestCase(unittest.TestCase): + def testShortMode(self): + assert encoder.encode( + univ.OctetString('Quick brown fox') + ) == '\x04\x0fQuick brown fox' + +class BitStringEncoderTestCase(unittest.TestCase): + def testShortMode(self): + assert encoder.encode( + univ.BitString((1,)) + ) == '\x03\x02\x07\x80' + +class SetWithChoiceEncoderTestCase(unittest.TestCase): + def setUp(self): + c = univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('amount', univ.Integer()) + )) + self.s = univ.Set(componentType=namedtype.NamedTypes( + namedtype.NamedType('place-holder', univ.Null()), + namedtype.NamedType('status', c) + )) + + def testDefMode(self): + self.s.setComponentByPosition(0) + self.s.setComponentByName('status') + self.s.getComponentByName('status').setComponentByPosition(0, 'ann') + assert encoder.encode(self.s) == '1\x07\x04\x03ann\x05\x00' + +if __name__ == '__main__': unittest.main() diff --git a/test/codec/der/suite.py b/test/codec/der/suite.py new file mode 100644 index 0000000..4cbf2d6 --- /dev/null +++ b/test/codec/der/suite.py @@ -0,0 +1,17 @@ +from pyasn1 import error +import encoder, decoder +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +suite = unittest.TestSuite() +loader = unittest.TestLoader() +for m in (encoder, decoder): + suite.addTest(loader.loadTestsFromModule(m)) + +def runTests(): unittest.TextTestRunner(verbosity=2).run(suite) + +if __name__ == '__main__': runTests() diff --git a/test/suite.py b/test/suite.py new file mode 100644 index 0000000..7125ac1 --- /dev/null +++ b/test/suite.py @@ -0,0 +1,23 @@ +import type.suite +import codec.ber.suite +import codec.cer.suite +import codec.der.suite +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +suite = unittest.TestSuite() +for m in ( + type.suite, + codec.ber.suite, + codec.cer.suite, + codec.der.suite + ): + suite.addTest(getattr(m, 'suite')) + +def runTests(): unittest.TextTestRunner(verbosity=2).run(suite) + +if __name__ == '__main__': runTests() diff --git a/test/type/__init__.py b/test/type/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/type/__init__.py diff --git a/test/type/constraint.py b/test/type/constraint.py new file mode 100644 index 0000000..ea02e29 --- /dev/null +++ b/test/type/constraint.py @@ -0,0 +1,217 @@ +from pyasn1.type import constraint, error +#from pyasn1.test import unittest +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +class SingleValueConstraintTestCase(unittest.TestCase): + def setUp(self): + self.c1 = constraint.SingleValueConstraint(1,2) + self.c2 = constraint.SingleValueConstraint(3,4) + + def testCmp(self): assert self.c1 == self.c1, 'comparation fails' + def testHash(self): assert hash(self.c1) != hash(self.c2), 'hash() fails' + def testGoodVal(self): + try: + self.c1(1) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + def testBadVal(self): + try: + self.c1(4) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + +class ContainedSubtypeConstraintTestCase(unittest.TestCase): + def setUp(self): + self.c1 = constraint.ContainedSubtypeConstraint( + constraint.SingleValueConstraint(12) + ) + + def testGoodVal(self): + try: + self.c1(12) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + def testBadVal(self): + try: + self.c1(4) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + +class ValueRangeConstraintTestCase(unittest.TestCase): + def setUp(self): + self.c1 = constraint.ValueRangeConstraint(1,4) + + def testGoodVal(self): + try: + self.c1(1) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + def testBadVal(self): + try: + self.c1(-5) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + +class ValueSizeConstraintTestCase(unittest.TestCase): + def setUp(self): + self.c1 = constraint.ValueSizeConstraint(1,2) + + def testGoodVal(self): + try: + self.c1('a') + except error.ValueConstraintError: + assert 0, 'constraint check fails' + def testBadVal(self): + try: + self.c1('abc') + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + +class PermittedAlphabetConstraintTestCase(SingleValueConstraintTestCase): + def testGoodVal(self): + try: + self.c1(1) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + def testBadVal(self): + try: + self.c1(4) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + +class ConstraintsIntersectionTestCase(unittest.TestCase): + def setUp(self): + self.c1 = constraint.ConstraintsIntersection( + constraint.SingleValueConstraint(4), + constraint.ValueRangeConstraint(2, 4) + ) + + def testCmp1(self): + assert constraint.SingleValueConstraint(4) in self.c1, '__cmp__() fails' + + def testCmp2(self): + assert constraint.SingleValueConstraint(5) not in self.c1, \ + '__cmp__() fails' + + def testCmp3(self): + c = constraint.ConstraintsUnion(constraint.ConstraintsIntersection( + constraint.SingleValueConstraint(4), + constraint.ValueRangeConstraint(2, 4) + )) + assert self.c1 in c, '__cmp__() fails' + def testCmp4(self): + c = constraint.ConstraintsUnion( + constraint.ConstraintsIntersection(constraint.SingleValueConstraint(5)) + ) + assert self.c1 not in c, '__cmp__() fails' + + def testGoodVal(self): + try: + self.c1(4) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + def testBadVal(self): + try: + self.c1(-5) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + +class InnerTypeConstraintTestCase(unittest.TestCase): + def testConst1(self): + c = constraint.InnerTypeConstraint( + constraint.SingleValueConstraint(4) + ) + try: + c(4, 32) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + try: + c(5, 32) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + def testConst2(self): + c = constraint.InnerTypeConstraint( + (0, constraint.SingleValueConstraint(4), 'PRESENT'), + (1, constraint.SingleValueConstraint(4), 'ABSENT') + ) + try: + c(4, 0) + except error.ValueConstraintError: + raise + assert 0, 'constraint check fails' + try: + c(4, 1) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + try: + c(3, 0) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + +# Booleans + +class ConstraintsUnionTestCase(unittest.TestCase): + def setUp(self): + self.c1 = constraint.ConstraintsUnion( + constraint.SingleValueConstraint(4), + constraint.ValueRangeConstraint(2, 4) + ) + + def testGoodVal(self): + try: + self.c1(2) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + def testBadVal(self): + try: + self.c1(-5) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + +class ConstraintsExclusionTestCase(unittest.TestCase): + def setUp(self): + self.c1 = constraint.ConstraintsExclusion( + constraint.ValueRangeConstraint(2, 4) + ) + + def testGoodVal(self): + try: + self.c1(6) + except error.ValueConstraintError: + assert 0, 'constraint check fails' + def testBadVal(self): + try: + self.c1(2) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint check fails' + +if __name__ == '__main__': unittest.main() + +# how to apply size constriants to constructed types? diff --git a/test/type/namedtype.py b/test/type/namedtype.py new file mode 100644 index 0000000..12a3f0e --- /dev/null +++ b/test/type/namedtype.py @@ -0,0 +1,83 @@ +from pyasn1.type import namedtype, univ, error +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +class NamedTypeCaseBase(unittest.TestCase): + def setUp(self): + self.e = namedtype.NamedType('age', univ.Integer()) + def testIter(self): + n, t = self.e + assert n == 'age' or t == univ.Integer(), 'unpack fails' + +class NamedTypesCaseBase(unittest.TestCase): + def setUp(self): + self.e = namedtype.NamedTypes( + namedtype.NamedType('first-name', univ.OctetString()), + namedtype.OptionalNamedType('age', univ.Integer()), + namedtype.NamedType('family-name', univ.OctetString()) + ) + def testIter(self): + for t in self.e: + break + else: + assert 0, '__getitem__() fails' + + def testGetTypeByPosition(self): + assert self.e.getTypeByPosition(0) == univ.OctetString(), \ + 'getTypeByPosition() fails' + + def testGetNameByPosition(self): + assert self.e.getNameByPosition(0) == 'first-name', \ + 'getNameByPosition() fails' + + def testGetPositionByName(self): + assert self.e.getPositionByName('first-name') == 0, \ + 'getPositionByName() fails' + + def testGetTypesNearPosition(self): + assert self.e.getTypeMapNearPosition(0) == { + univ.OctetString.tagSet: univ.OctetString() + } + assert self.e.getTypeMapNearPosition(1) == { + univ.Integer.tagSet: univ.Integer(), + univ.OctetString.tagSet: univ.OctetString() + } + assert self.e.getTypeMapNearPosition(2) == { + univ.OctetString.tagSet: univ.OctetString() + } + + def testGetTypeMap(self): + assert self.e.getTypeMap() == { + univ.OctetString.tagSet: univ.OctetString(), + univ.Integer.tagSet: univ.Integer() + } + + def testGetTypeMapWithDups(self): + try: + self.e.getTypeMap(1) + except error.PyAsn1Error: + pass + else: + assert 0, 'Duped types not noticed' + + def testGetPositionNearType(self): + assert self.e.getPositionNearType(univ.OctetString.tagSet, 0) == 0 + assert self.e.getPositionNearType(univ.Integer.tagSet, 1) == 1 + assert self.e.getPositionNearType(univ.OctetString.tagSet, 2) == 2 + +class OrderedNamedTypesCaseBase(unittest.TestCase): + def setUp(self): + self.e = namedtype.NamedTypes( + namedtype.NamedType('first-name', univ.OctetString()), + namedtype.NamedType('age', univ.Integer()) + ) + + def testGetTypeByPosition(self): + assert self.e.getTypeByPosition(0) == univ.OctetString(), \ + 'getTypeByPosition() fails' + +if __name__ == '__main__': unittest.main() diff --git a/test/type/suite.py b/test/type/suite.py new file mode 100644 index 0000000..67d89f2 --- /dev/null +++ b/test/type/suite.py @@ -0,0 +1,17 @@ +from pyasn1 import error +import tag, constraint, namedtype, univ +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +suite = unittest.TestSuite() +loader = unittest.TestLoader() +for m in (tag, constraint, namedtype, univ): + suite.addTest(loader.loadTestsFromModule(m)) + +def runTests(): unittest.TextTestRunner(verbosity=2).run(suite) + +if __name__ == '__main__': runTests() diff --git a/test/type/tag.py b/test/type/tag.py new file mode 100644 index 0000000..310b755 --- /dev/null +++ b/test/type/tag.py @@ -0,0 +1,103 @@ +from pyasn1.type import tag +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +class TagTestCaseBase(unittest.TestCase): + def setUp(self): + self.t1 = tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 3) + self.t2 = tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 3) + +class TagCmpTestCase(TagTestCaseBase): + def testCmp(self): + assert self.t1 == self.t2, 'tag comparation fails' + + def testHash(self): + assert hash(self.t1) == hash(self.t2), 'tag hash comparation fails' + + def testSequence(self): + assert self.t1[0] == self.t2[0] and \ + self.t1[1] == self.t2[1] and \ + self.t1[2] == self.t2[2], 'tag sequence protocol fails' + +class TagSetTestCaseBase(unittest.TestCase): + def setUp(self): + self.ts1 = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) + ) + self.ts2 = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) + ) + +class TagSetCmpTestCase(TagSetTestCaseBase): + def testCmp(self): + assert self.ts1 == self.ts2, 'tag set comparation fails' + + def testHash(self): + assert hash(self.ts1) == hash(self.ts2), 'tag set hash comp. fails' + + def testLen(self): + assert len(self.ts1) == len(self.ts2), 'tag length comparation fails' + +class TaggingTestSuite(TagSetTestCaseBase): + def testImplicitTag(self): + t = self.ts1.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 14) + ) + assert t == tag.TagSet( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 14) + ), 'implicit tagging went wrong' + + def testExplicitTag(self): + t = self.ts1.tagExplicitly( + tag.Tag(tag.tagClassPrivate, tag.tagFormatSimple, 32) + ) + assert t == tag.TagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassPrivate, tag.tagFormatConstructed, 32) + ), 'explicit tagging went wrong' + +class TagSetAddTestSuite(TagSetTestCaseBase): + def testAdd(self): + t = self.ts1 + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2) + assert t == tag.TagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2) + ), 'TagSet.__add__() fails' + + def testRadd(self): + t = tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2) + self.ts1 + assert t == tag.TagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) + ), 'TagSet.__radd__() fails' + +class SuperTagSetTestCase(TagSetTestCaseBase): + def testSuperTagCheck1(self): + assert self.ts1.isSuperTagSetOf( + tag.TagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) + )), 'isSuperTagSetOf() fails' + + def testSuperTagCheck2(self): + assert not self.ts1.isSuperTagSetOf( + tag.TagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 13) + )), 'isSuperTagSetOf() fails' + + def testSuperTagCheck3(self): + assert self.ts1.isSuperTagSetOf( + tag.TagSet((), tag.Tag(tag.tagClassUniversal, + tag.tagFormatSimple, 12)) + ), 'isSuperTagSetOf() fails' + +if __name__ == '__main__': unittest.main() diff --git a/test/type/univ.py b/test/type/univ.py new file mode 100644 index 0000000..9a1d441 --- /dev/null +++ b/test/type/univ.py @@ -0,0 +1,359 @@ +from pyasn1.type import univ, tag, constraint, namedtype, namedval, error +try: + import unittest +except ImportError: + raise error.PyAsn1Error( + 'PyUnit package\'s missing. See http://pyunit.sourceforge.net/' + ) + +class IntegerTestCase(unittest.TestCase): + def testStr(self): assert str(univ.Integer(1)) == '1', 'str() fails' + def testAnd(self): assert univ.Integer(1) & 0 == 0, '__and__() fails' + def testOr(self): assert univ.Integer(1) | 0 == 1, '__or__() fails' + def testXor(self): assert univ.Integer(1) ^ 0 == 1, '__xor__() fails' + def testRand(self): assert 0 & univ.Integer(1) == 0, '__rand__() fails' + def testRor(self): assert 0 | univ.Integer(1) == 1, '__ror__() fails' + def testRxor(self): assert 0 ^ univ.Integer(1) == 1, '__rxor__() fails' + def testAdd(self): assert univ.Integer(-4) + 6 == 2, '__add__() fails' + def testRadd(self): assert 4 + univ.Integer(5) == 9, '__radd__() fails' + def testSub(self): assert univ.Integer(3) - 6 == -3, '__sub__() fails' + def testRsub(self): assert 6 - univ.Integer(3) == 3, '__rsub__() fails' + def testMul(self): assert univ.Integer(3) * -3 == -9, '__mul__() fails' + def testRmul(self): assert 2 * univ.Integer(3) == 6, '__rmul__() fails' + def testDiv(self): assert univ.Integer(3) / 2 == 1, '__div__() fails' + def testRdiv(self): assert 6 / univ.Integer(3) == 2, '__rdiv__() fails' + def testMod(self): assert univ.Integer(3) % 2 == 1, '__mod__() fails' + def testRmod(self): assert 4 % univ.Integer(3) == 1, '__rmod__() fails' + def testPow(self): assert univ.Integer(3) ** 2 == 9, '__pow__() fails' + def testRpow(self): assert 2 ** univ.Integer(2) == 4, '__rpow__() fails' + def testLshift(self): assert univ.Integer(1) << 1 == 2, '<< fails' + def testRshift(self): assert univ.Integer(2) >> 1 == 1, '>> fails' + def testInt(self): assert int(univ.Integer(3)) == 3, '__int__() fails' + def testLong(self): assert int(univ.Integer(8)) == 8, '__long__() fails' + def testFloat(self): assert float(univ.Integer(4))==4.0,'__float__() fails' + def testPrettyIn(self): assert univ.Integer('3') == 3, 'prettyIn() fails' + def testTag(self): + assert univ.Integer().getTagSet() == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02) + ) + def testNamedVals(self): + i = univ.Integer( + 'asn1', namedValues=univ.Integer.namedValues.clone(('asn1', 1)) + ) + assert i == 1, 'named val fails' + assert str(i) != 'asn1', 'named val __str__() fails' + +class BooleanTestCase(unittest.TestCase): + def testStr(self): assert str(univ.Boolean(1)) == '1', 'str() fails' + def testTag(self): + assert univ.Boolean().getTagSet() == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01) + ) + def testConstraints(self): + try: + univ.Boolean(2) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint fail' + def testSubtype(self): + assert univ.Integer().subtype( + value=1, + implicitTag=tag.Tag(tag.tagClassPrivate,tag.tagFormatSimple,2), + subtypeSpec=constraint.SingleValueConstraint(1,3) + ) == univ.Integer( + value=1, + tagSet=tag.TagSet(tag.Tag(tag.tagClassPrivate, + tag.tagFormatSimple,2)), + subtypeSpec=constraint.ConstraintsIntersection(constraint.SingleValueConstraint(1,3)) + ) + +class BitStringTestCase(unittest.TestCase): + def setUp(self): + self.b = univ.BitString( + namedValues=namedval.NamedValues(('Active', 0), ('Urgent', 1)) + ) + def testSet(self): + assert self.b.clone('Active') == (1,) + assert self.b.clone("'1010100110001010'B") == (1,0,1,0,1,0,0,1,1,0,0,0,1,0,1,0) + assert self.b.clone("'A98A'H") == (1,0,1,0,1,0,0,1,1,0,0,0,1,0,1,0) + assert self.b.clone((1,0,1)) == (1,0,1) + def testStr(self): + assert str(self.b.clone('Urgent,Active')) == '(1, 1)' + def testRepr(self): + assert repr(self.b.clone('Urgent,Active')) == 'BitString("\'11\'B")' + def testTag(self): + assert univ.BitString().getTagSet() == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03) + ) + def testLen(self): assert len(self.b.clone("'A98A'H")) == 16 + def testIter(self): + assert self.b.clone("'A98A'H")[0] == 1 + assert self.b.clone("'A98A'H")[1] == 0 + assert self.b.clone("'A98A'H")[2] == 1 + +class OctetStringTestCase(unittest.TestCase): + def testStr(self): + assert str(univ.OctetString('q')) == 'q', '__str__() fails' + def testSeq(self): + assert univ.OctetString('q')[0] == 'q','__getitem__() fails' + def testAdd(self): + assert univ.OctetString() + 'q' == 'q', '__add__() fails' + def testRadd(self): + assert 'b' + univ.OctetString('q') == 'bq', '__radd__() fails' + def testMul(self): + assert univ.OctetString('a') * 2 == 'aa', '__mul__() fails' + def testRmul(self): + assert 2 * univ.OctetString('b') == 'bb', '__rmul__() fails' + def testTag(self): + assert univ.OctetString().getTagSet() == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04) + ) + +class Null(unittest.TestCase): + def testStr(self): assert str(univ.Null()) == '', 'str() fails' + def testTag(self): + assert univ.Null().getTagSet() == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05) + ) + def testConstraints(self): + try: + univ.Null(2) + except error.ValueConstraintError: + pass + else: + assert 0, 'constraint fail' + +class ObjectIdentifier(unittest.TestCase): + def testStr(self): + assert str(univ.ObjectIdentifier((1,3,6))) == '(1, 3, 6)' + def testEq(self): + assert univ.ObjectIdentifier((1,3,6)) == (1,3,6), '__cmp__() fails' + def testAdd(self): + assert univ.ObjectIdentifier((1,3)) + (6,)==(1,3,6),'__add__() fails' + def testRadd(self): + assert (1,) + univ.ObjectIdentifier((3,6))==(1,3,6),'__radd__() fails' + def testLen(self): + assert len(univ.ObjectIdentifier((1,3))) == 2,'__len__() fails' + def testPrefix(self): + o = univ.ObjectIdentifier('1.3.6') + assert o.isPrefixOf((1,3,6)), 'isPrefixOf() fails' + assert o.isPrefixOf((1,3,6,1)), 'isPrefixOf() fails' + assert not o.isPrefixOf((1,3)), 'isPrefixOf() fails' + def testInput(self): + assert univ.ObjectIdentifier('1.3.6')==(1,3,6),'prettyIn() fails' + def testTag(self): + assert univ.ObjectIdentifier().getTagSet() == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06) + ) + +class SequenceOf(unittest.TestCase): + def setUp(self): + self.s1 = univ.SequenceOf( + componentType=univ.OctetString() + ) + self.s2 = self.s1.clone() + def testTag(self): + assert self.s1.getTagSet() == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) + ), 'wrong tagSet' + def testSeq(self): + self.s1.setComponentByPosition(0, univ.OctetString('abc')) + assert self.s1[0] == 'abc', 'set by idx fails' + self.s1.setComponentByPosition(0, 'cba') + assert self.s1[0] == 'cba', 'set by idx fails' + def testCmp(self): + self.s1.clear() + self.s1.setComponentByPosition(0, 'abc') + self.s2.clear() + self.s2.setComponentByPosition(0, univ.OctetString('abc')) + assert self.s1 == self.s2, '__cmp__() fails' + def testSubtypeSpec(self): + s = self.s1.clone(subtypeSpec=constraint.ConstraintsUnion( + constraint.SingleValueConstraint('abc') + )) + try: + s.setComponentByPosition(0, univ.OctetString('abc')) + except: + assert 0, 'constraint fails' + try: + s.setComponentByPosition(1, univ.OctetString('Abc')) + except: + pass + else: + assert 0, 'constraint fails' + def testSizeSpec(self): + s = self.s1.clone(sizeSpec=constraint.ConstraintsUnion( + constraint.ValueSizeConstraint(1,1) + )) + s.setComponentByPosition(0, univ.OctetString('abc')) + try: + s.verifySizeSpec() + except: + assert 0, 'size spec fails' + s.setComponentByPosition(1, univ.OctetString('abc')) + try: + s.verifySizeSpec() + except: + pass + else: + assert 0, 'size spec fails' + def testGetComponentTypeMap(self): + assert self.s1.getComponentTypeMap() == { + univ.OctetString.tagSet: univ.OctetString() + } + def testSubtype(self): + self.s1.clear() + assert self.s1.subtype( + implicitTag=tag.Tag(tag.tagClassPrivate,tag.tagFormatSimple,2), + subtypeSpec=constraint.SingleValueConstraint(1,3), + sizeSpec=constraint.ValueSizeConstraint(0,1) + ) == self.s1.clone( + tagSet=tag.TagSet(tag.Tag(tag.tagClassPrivate, + tag.tagFormatSimple,2)), + subtypeSpec=constraint.ConstraintsIntersection(constraint.SingleValueConstraint(1,3)), + sizeSpec=constraint.ValueSizeConstraint(0,1) + ) + +class Sequence(unittest.TestCase): + def setUp(self): + self.s1 = univ.Sequence(componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.OptionalNamedType('nick', univ.OctetString()), + namedtype.DefaultedNamedType('age', univ.Integer(34)) + )) + def testTag(self): + assert self.s1.getTagSet() == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) + ), 'wrong tagSet' + def testById(self): + self.s1.setComponentByName('name', univ.OctetString('abc')) + assert self.s1.getComponentByName('name') == 'abc', 'set by name fails' + def testGetNearPosition(self): + assert self.s1.getComponentTypeMapNearPosition(1) == { + univ.OctetString.tagSet: univ.OctetString(), + univ.Integer.tagSet: univ.Integer(34) + } + assert self.s1.getComponentPositionNearType( + univ.OctetString.tagSet, 1 + ) == 1 + def testGetDefaultComponentByPosition(self): + self.s1.clear() + assert self.s1.getDefaultComponentByPosition(0) == None + assert self.s1.getDefaultComponentByPosition(2) == univ.Integer(34) + + def testSetDefaultComponents(self): + self.s1.clear() + assert self.s1.getComponentByPosition(2) == None + self.s1.setComponentByPosition(0, univ.OctetString('Ping')) + self.s1.setComponentByPosition(1, univ.OctetString('Pong')) + self.s1.setDefaultComponents() + assert self.s1.getComponentByPosition(2) == 34 + +class SetOf(unittest.TestCase): + def setUp(self): + self.s1 = univ.SetOf(componentType=univ.OctetString()) + def testTag(self): + assert self.s1.getTagSet() == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) + ), 'wrong tagSet' + def testSeq(self): + self.s1.setComponentByPosition(0, univ.OctetString('abc')) + assert self.s1[0] == 'abc', 'set by idx fails' + self.s1.setComponentByPosition(0, self.s1[0].clone('cba')) + assert self.s1[0] == 'cba', 'set by idx fails' + +class Set(unittest.TestCase): + def setUp(self): + self.s1 = univ.Set(componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.OptionalNamedType('null', univ.Null()), + namedtype.DefaultedNamedType('age', univ.Integer(34)) + )) + self.s2 = self.s1.clone() + def testTag(self): + assert self.s1.getTagSet() == tag.TagSet( + (), + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) + ), 'wrong tagSet' + def testByTypeWithPythonValue(self): + self.s1.setComponentByType(univ.OctetString.tagSet, 'abc') + assert self.s1.getComponentByType( + univ.OctetString.tagSet + ) == 'abc', 'set by name fails' + def testByTypeWithInstance(self): + self.s1.setComponentByType(univ.OctetString.tagSet, univ.OctetString('abc')) + assert self.s1.getComponentByType( + univ.OctetString.tagSet + ) == 'abc', 'set by name fails' + def testGetTypeMap(self): + assert self.s1.getTypeMap() == { + univ.Set.tagSet: univ.Set() + } + def testGetComponentTypeMap(self): + assert self.s1.getComponentTypeMap() == { + univ.OctetString.tagSet: univ.OctetString(), + univ.Null.tagSet: univ.Null(), + univ.Integer.tagSet: univ.Integer(34) + } + def testGetPositionByType(self): + assert self.s1.getComponentPositionByType( + univ.Null().getTagSet() + ) == 1 + +class Choice(unittest.TestCase): + def setUp(self): + innerComp = univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('count', univ.Integer()), + namedtype.NamedType('flag', univ.Boolean()) + )) + self.s1 = univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('name', univ.OctetString()), + namedtype.NamedType('sex', innerComp) + )) + def testTag(self): + assert self.s1.getTagSet() == tag.TagSet(), 'wrong tagSet' + def testOuterByTypeWithPythonValue(self): + self.s1.setComponentByType(univ.OctetString.tagSet, 'abc') + assert self.s1.getComponentByType( + univ.OctetString.tagSet + ) == 'abc' + def testOuterByTypeWithInstanceValue(self): + self.s1.setComponentByType( + univ.OctetString.tagSet, univ.OctetString('abc') + ) + assert self.s1.getComponentByType( + univ.OctetString.tagSet + ) == 'abc' + def testInnerByTypeWithPythonValue(self): + self.s1.setComponentByType(univ.Integer.tagSet, 123, 1) + assert self.s1.getComponentByType( + univ.Integer.tagSet, 1 + ) == 123 + def testInnerByTypeWithInstanceValue(self): + self.s1.setComponentByType( + univ.Integer.tagSet, univ.Integer(123), 1 + ) + assert self.s1.getComponentByType( + univ.Integer.tagSet, 1 + ) == 123 + def testCmp(self): + self.s1.setComponentByName('name', univ.OctetString('abc')) + assert self.s1 == 'abc', '__cmp__() fails' + def testGetComponent(self): + self.s1.setComponentByType(univ.OctetString.tagSet, 'abc') + assert self.s1.getComponent() == 'abc', 'getComponent() fails' + def testSetComponentByPosition(self): + self.s1.setComponentByPosition(0, univ.OctetString('Jim')) + assert self.s1 == 'Jim' + +if __name__ == '__main__': unittest.main() |