| <html> | |
| <title> | |
| PyASN1 programmer's manual | |
| </title> | |
| <head> | |
| </head> | |
| <body> | |
| <center> | |
| <table width=60%> | |
| <tr> | |
| <td> | |
| <h3> | |
| PyASN1 programmer's manual | |
| </h3> | |
| <p align=right> | |
| <i>written by <a href=mailto:ilya@glas.net>Ilya Etingof</a>, 2011-2012</i> | |
| </p> | |
| <p> | |
| Free and open-source pyasn1 library makes it easier for programmers and | |
| network engineers to develop, debug and experiment with ASN.1-based protocols | |
| using Python programming language as a tool. | |
| </p> | |
| <p> | |
| Abstract Syntax Notation One | |
| (<a href=http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_1x>ASN.1</a>) | |
| is a set of | |
| <a href=http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-X.693-0207w.zip> | |
| ITU standards</a> concered with provisioning instrumentation for developing | |
| data exchange protocols in a robust, clear and interoperabable way for | |
| various IT systems and applications. Most of the efforts are targeting the | |
| following areas: | |
| <ul> | |
| <li>Data structures: the standard introduces a collection of basic data types | |
| (similar to integers, bits, strings, arrays and records in a programming | |
| language) that can be used for defining complex, possibly nested data | |
| structures representing domain-specific data units. | |
| <li>Serialization protocols: domain-specific data units expressed in ASN.1 | |
| types could be converted into a series of octets for storage or transmission | |
| over the wire and then recovered back into their structured form on the | |
| receiving end. This process is immune to various hardware and software | |
| related dependencies. | |
| <li>Data description language: could be used to describe particular set of | |
| domain-specific data structures and their relationships. Such a description | |
| could be passed to an ASN.1 compiler for automated generation of program | |
| code that represents ASN.1 data structures in language-native environment | |
| and handles data serialization issues. | |
| </ul> | |
| </p> | |
| <p> | |
| This tutorial and algorithms, implemented by pyasn1 library, are | |
| largely based on the information read in the book | |
| <a href="http://www.oss.com/asn1/dubuisson.html"> | |
| ASN.1 - Communication between heterogeneous systems</a> | |
| by Olivier Dubuisson. Another relevant resource is | |
| <a href=ftp://ftp.rsasecurity.com/pub/pkcs/ascii/layman.asc> | |
| A Layman's Guide to a Subset of ASN.1, BER, and DER</a> by Burton S. Kaliski. | |
| It's advised to refer to these books for more in-depth knowledge on the | |
| subject of ASN.1. | |
| </p> | |
| <p> | |
| As of this writing, pyasn1 library implements most of standard ASN.1 data | |
| structures in a rather detailed and feature-rich manner. Another highly | |
| important capability of the library is its data serialization facilities. | |
| The last component of the standard - ASN.1 compiler is planned for | |
| implementation in the future. | |
| </p> | |
| </p> | |
| The pyasn1 library was designed to follow the pre-1995 ASN.1 specification | |
| (also known as X.208). Later, post 1995, revision (X.680) introduced | |
| significant changes most of which have not yet been supported by pyasn1. | |
| </p> | |
| <h3> | |
| Table of contents | |
| </h3> | |
| <p> | |
| <ul> | |
| <li><a href="#1">1. Data model for ASN.1 types</a> | |
| <li><a href="#1.1">1.1 Scalar types</a> | |
| <li><a href="#1.1.1">1.1.1 Boolean type</a> | |
| <li><a href="#1.1.2">1.1.2 Null type</a> | |
| <li><a href="#1.1.3">1.1.3 Integer type</a> | |
| <li><a href="#1.1.4">1.1.4 Enumerated type</a> | |
| <li><a href="#1.1.5">1.1.5 Real type</a> | |
| <li><a href="#1.1.6">1.1.6 Bit string type</a> | |
| <li><a href="#1.1.7">1.1.7 OctetString type</a> | |
| <li><a href="#1.1.8">1.1.8 ObjectIdentifier type</a> | |
| <li><a href="#1.1.9">1.1.9 Character string types</a> | |
| <li><a href="#1.1.10">1.1.10 Useful types</a> | |
| <li><a href="#1.2">1.2 Tagging</a> | |
| <li><a href="#1.3">1.3 Constructed types</a> | |
| <li><a href="#1.3.1">1.3.1 Sequence and Set types</a> | |
| <li><a href="#1.3.2">1.3.2 SequenceOf and SetOf types</a> | |
| <li><a href="#1.3.3">1.3.3 Choice type</a> | |
| <li><a href="#1.3.4">1.3.4 Any type</a> | |
| <li><a href="#1.4">1.4 Subtype constraints</a> | |
| <li><a href="#1.4.1">1.4.1 Single value constraint</a> | |
| <li><a href="#1.4.2">1.4.2 Value range constraint</a> | |
| <li><a href="#1.4.3">1.4.3 Size constraint</a> | |
| <li><a href="#1.4.4">1.4.4 Alphabet constraint</a> | |
| <li><a href="#1.4.5">1.4.5 Constraint combinations</a> | |
| <li><a href="#1.5">1.5 Types relationships</a> | |
| <li><a href="#2">2. Codecs</a> | |
| <li><a href="#2.1">2.1 Encoders</a> | |
| <li><a href="#2.2">2.2 Decoders</a> | |
| <li><a href="#2.2.1">2.2.1 Decoding untagged types</a> | |
| <li><a href="#2.2.2">2.2.2 Ignoring unknown types</a> | |
| <li><a href="#3">3. Feedback and getting help</a> | |
| </ul> | |
| <a name="1"></a> | |
| <h3> | |
| 1. Data model for ASN.1 types | |
| </h3> | |
| <p> | |
| All ASN.1 types could be categorized into two groups: scalar (also called | |
| simple or primitive) and constructed. The first group is populated by | |
| well-known types like Integer or String. Members of constructed group | |
| hold other types (simple or constructed) as their inner components, thus | |
| they are semantically close to a programming language records or lists. | |
| </p> | |
| <p> | |
| In pyasn1, all ASN.1 types and values are implemented as Python objects. | |
| The same pyasn1 object can represent either ASN.1 type and/or value | |
| depending of the presense of value initializer on object instantiation. | |
| We will further refer to these as <i>pyasn1 type object</i> versus <i>pyasn1 | |
| value object</i>. | |
| </p> | |
| <p> | |
| Primitive ASN.1 types are implemented as immutable scalar objects. There values | |
| could be used just like corresponding native Python values (integers, | |
| strings/bytes etc) and freely mixed with them in expressions. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> asn1IntegerValue = univ.Integer(12) | |
| >>> asn1IntegerValue - 2 | |
| 10 | |
| >>> univ.OctetString('abc') == 'abc' | |
| True # Python 2 | |
| >>> univ.OctetString(b'abc') == b'abc' | |
| True # Python 3 | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| It would be an error to perform an operation on a pyasn1 type object | |
| as it holds no value to deal with: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> asn1IntegerType = univ.Integer() | |
| >>> asn1IntegerType - 2 | |
| ... | |
| pyasn1.error.PyAsn1Error: No value for __coerce__() | |
| </pre> | |
| </td></tr></table> | |
| <a name="1.1"></a> | |
| <h4> | |
| 1.1 Scalar types | |
| </h4> | |
| <p> | |
| In the sub-sections that follow we will explain pyasn1 mapping to those | |
| primitive ASN.1 types. Both, ASN.1 notation and corresponding pyasn1 | |
| syntax will be given in each case. | |
| </p> | |
| <a name="1.1.1"></a> | |
| <h4> | |
| 1.1.1 Boolean type | |
| </h4> | |
| <p> | |
| This is the simplest type those values could be either True or False. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| ;; type specification | |
| FunFactorPresent ::= BOOLEAN | |
| ;; values declaration and assignment | |
| pythonFunFactor FunFactorPresent ::= TRUE | |
| cobolFunFactor FunFactorPresent :: FALSE | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| And here's pyasn1 version of it: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> class FunFactorPresent(univ.Boolean): pass | |
| ... | |
| >>> pythonFunFactor = FunFactorPresent(True) | |
| >>> cobolFunFactor = FunFactorPresent(False) | |
| >>> pythonFunFactor | |
| FunFactorPresent('True(1)') | |
| >>> cobolFunFactor | |
| FunFactorPresent('False(0)') | |
| >>> pythonFunFactor == cobolFunFactor | |
| False | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <a name="1.1.2"></a> | |
| <h4> | |
| 1.1.2 Null type | |
| </h4> | |
| <p> | |
| The NULL type is sometimes used to express the absense of any information. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| ;; type specification | |
| Vote ::= CHOICE { | |
| agreed BOOLEAN, | |
| skip NULL | |
| } | |
| </td></tr></table> | |
| ;; value declaration and assignment | |
| myVote Vote ::= skip:NULL | |
| </pre> | |
| <p> | |
| We will explain the CHOICE type later in this paper, meanwhile the NULL | |
| type: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> skip = univ.Null() | |
| >>> skip | |
| Null('') | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <a name="1.1.3"></a> | |
| <h4> | |
| 1.1.3 Integer type | |
| </h4> | |
| <p> | |
| ASN.1 defines the values of Integer type as negative or positive of whatever | |
| length. This definition plays nicely with Python as the latter places no | |
| limit on Integers. However, some ASN.1 implementations may impose certain | |
| limits of integer value ranges. Keep that in mind when designing new | |
| data structures. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| ;; values specification | |
| age-of-universe INTEGER ::= 13750000000 | |
| mean-martian-surface-temperature INTEGER ::= -63 | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| A rather strigntforward mapping into pyasn1: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> ageOfUniverse = univ.Integer(13750000000) | |
| >>> ageOfUniverse | |
| Integer(13750000000) | |
| >>> | |
| >>> meanMartianSurfaceTemperature = univ.Integer(-63) | |
| >>> meanMartianSurfaceTemperature | |
| Integer(-63) | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| ASN.1 allows to assign human-friendly names to particular values of | |
| an INTEGER type. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| Temperature ::= INTEGER { | |
| freezing(0), | |
| boiling(100) | |
| } | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| The Temperature type expressed in pyasn1: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, namedval | |
| >>> class Temperature(univ.Integer): | |
| ... namedValues = namedval.NamedValues(('freezing', 0), ('boiling', 100)) | |
| ... | |
| >>> t = Temperature(0) | |
| >>> t | |
| Temperature('freezing(0)') | |
| >>> t + 1 | |
| Temperature(1) | |
| >>> t + 100 | |
| Temperature('boiling(100)') | |
| >>> t = Temperature('boiling') | |
| >>> t | |
| Temperature('boiling(100)') | |
| >>> Temperature('boiling') / 2 | |
| Temperature(50) | |
| >>> -1 < Temperature('freezing') | |
| True | |
| >>> 47 > Temperature('boiling') | |
| False | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| These values labels have no effect on Integer type operations, any value | |
| still could be assigned to a type (information on value constraints will | |
| follow further in this paper). | |
| </p> | |
| <a name="1.1.4"></a> | |
| <h4> | |
| 1.1.4 Enumerated type | |
| </h4> | |
| <p> | |
| ASN.1 Enumerated type differs from an Integer type in a number of ways. | |
| Most important is that its instance can only hold a value that belongs | |
| to a set of values specified on type declaration. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| error-status ::= ENUMERATED { | |
| no-error(0), | |
| authentication-error(10), | |
| authorization-error(20), | |
| general-failure(51) | |
| } | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| When constructing Enumerated type we will use two pyasn1 features: values | |
| labels (as mentioned above) and value constraint (will be described in | |
| more details later on). | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, namedval, constraint | |
| >>> class ErrorStatus(univ.Enumerated): | |
| ... namedValues = namedval.NamedValues( | |
| ... ('no-error', 0), | |
| ... ('authentication-error', 10), | |
| ... ('authorization-error', 20), | |
| ... ('general-failure', 51) | |
| ... ) | |
| ... subtypeSpec = univ.Enumerated.subtypeSpec + \ | |
| ... constraint.SingleValueConstraint(0, 10, 20, 51) | |
| ... | |
| >>> errorStatus = univ.ErrorStatus('no-error') | |
| >>> errorStatus | |
| ErrorStatus('no-error(0)') | |
| >>> errorStatus == univ.ErrorStatus('general-failure') | |
| False | |
| >>> univ.ErrorStatus('non-existing-state') | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.error.PyAsn1Error: Can't coerce non-existing-state into integer | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Particular integer values associated with Enumerated value states | |
| have no meaning. They should not be used as such or in any kind of | |
| math operation. Those integer values are only used by codecs to | |
| transfer state from one entity to another. | |
| </p> | |
| <a name="1.1.5"></a> | |
| <h4> | |
| 1.1.5 Real type | |
| </h4> | |
| <p> | |
| Values of the Real type are a three-component tuple of mantissa, base and | |
| exponent. All three are integers. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| pi ::= REAL { mantissa 314159, base 10, exponent -5 } | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Corresponding pyasn1 objects can be initialized with either a three-component | |
| tuple or a Python float. Infinite values could be expressed in a way, | |
| compatible with Python float type. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> pi = univ.Real((314159, 10, -5)) | |
| >>> pi | |
| Real((314159, 10,-5)) | |
| >>> float(pi) | |
| 3.14159 | |
| >>> pi == univ.Real(3.14159) | |
| True | |
| >>> univ.Real('inf') | |
| Real('inf') | |
| >>> univ.Real('-inf') == float('-inf') | |
| True | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| If a Real object is initialized from a Python float or yielded by a math | |
| operation, the base is set to decimal 10 (what affects encoding). | |
| </p> | |
| <a name="1.1.6"></a> | |
| <h4> | |
| 1.1.6 Bit string type | |
| </h4> | |
| <p> | |
| ASN.1 BIT STRING type holds opaque binary data of an arbitrarily length. | |
| A BIT STRING value could be initialized by either a binary (base 2) or | |
| hex (base 16) value. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| public-key BIT STRING ::= '1010111011110001010110101101101 | |
| 1011000101010000010110101100010 | |
| 0110101010000111101010111111110'B | |
| signature BIT STRING ::= 'AF01330CD932093392100B39FF00DE0'H | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| The pyasn1 BitString objects can initialize from native ASN.1 notation | |
| (base 2 or base 16 strings) or from a Python tuple of binary components. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> publicKey = univ.BitString( | |
| ... "'1010111011110001010110101101101" | |
| ... "1011000101010000010110101100010" | |
| ... "0110101010000111101010111111110'B" | |
| ) | |
| >>> publicKey | |
| BitString("'10101110111100010101101011011011011000101010000010110101100010\ | |
| 0110101010000111101010111111110'B") | |
| >>> signature = univ.BitString( | |
| ... "'AF01330CD932093392100B39FF00DE0'H" | |
| ... ) | |
| >>> signature | |
| BitString("'101011110000000100110011000011001101100100110010000010010011001\ | |
| 1100100100001000000001011001110011111111100000000110111100000'B") | |
| >>> fingerprint = univ.BitString( | |
| ... (1, 0, 1, 1 ,0, 1, 1, 1, 0, 1, 0, 1) | |
| ... ) | |
| >>> fingerprint | |
| BitString("'101101110101'B") | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Another BIT STRING initialization method supported by ASN.1 notation | |
| is to specify only 1-th bits along with their human-friendly label | |
| and bit offset relative to the beginning of the bit string. With this | |
| method, all not explicitly mentioned bits are doomed to be zeros. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| bit-mask BIT STRING ::= { | |
| read-flag(0), | |
| write-flag(2), | |
| run-flag(4) | |
| } | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| To express this in pyasn1, we will employ the named values feature (as with | |
| Enumeration type). | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, namedval | |
| >>> class BitMask(univ.BitString): | |
| ... namedValues = namedval.NamedValues( | |
| ... ('read-flag', 0), | |
| ... ('write-flag', 2), | |
| ... ('run-flag', 4) | |
| ... ) | |
| >>> bitMask = BitMask('read-flag,run-flag') | |
| >>> bitMask | |
| BitMask("'10001'B") | |
| >>> tuple(bitMask) | |
| (1, 0, 0, 0, 1) | |
| >>> bitMask[4] | |
| 1 | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| The BitString objects mimic the properties of Python tuple type in part | |
| of immutable sequence object protocol support. | |
| </p> | |
| <a name="1.1.7"></a> | |
| <h4> | |
| 1.1.7 OctetString type | |
| </h4> | |
| <p> | |
| The OCTET STRING type is a confusing subject. According to ASN.1 | |
| specification, this type is similar to BIT STRING, the major difference | |
| is that the former operates in 8-bit chunks of data. What is important | |
| to note, is that OCTET STRING was NOT designed to handle text strings - the | |
| standard provides many other types specialized for text content. For that | |
| reason, ASN.1 forbids to initialize OCTET STRING values with "quoted text | |
| strings", only binary or hex initializers, similar to BIT STRING ones, | |
| are allowed. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| thumbnail OCTET STRING ::= '1000010111101110101111000000111011'B | |
| thumbnail OCTET STRING ::= 'FA9823C43E43510DE3422'H | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| However, ASN.1 users (e.g. protocols designers) seem to ignore the original | |
| purpose of the OCTET STRING type - they used it for handling all kinds of | |
| data, including text strings. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| welcome-message OCTET STRING ::= "Welcome to ASN.1 wilderness!" | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| In pyasn1, we have taken a liberal approach and allowed both BIT STRING | |
| style and quoted text initializers for the OctetString objects. To avoid | |
| possible collisions, quoted text is the default initialization syntax. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> thumbnail = univ.OctetString( | |
| ... binValue='1000010111101110101111000000111011' | |
| ... ) | |
| >>> thumbnail | |
| OctetString(hexValue='85eebcec0') | |
| >>> thumbnail = univ.OctetString( | |
| ... hexValue='FA9823C43E43510DE3422' | |
| ... ) | |
| >>> thumbnail | |
| OctetString(hexValue='fa9823c43e4351de34220') | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Most frequent usage of the OctetString class is to instantiate it with | |
| a text string. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> welcomeMessage = univ.OctetString('Welcome to ASN.1 wilderness!') | |
| >>> welcomeMessage | |
| OctetString(b'Welcome to ASN.1 wilderness!') | |
| >>> print('%s' % welcomeMessage) | |
| Welcome to ASN.1 wilderness! | |
| >>> welcomeMessage[11:16] | |
| OctetString(b'ASN.1') | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| OctetString objects support the immutable sequence object protocol. | |
| In other words, they behave like Python 3 bytes (or Python 2 strings). | |
| </p> | |
| <p> | |
| When running pyasn1 on Python 3, it's better to use the bytes objects for | |
| OctetString instantiation, as it's more reliable and efficient. | |
| </p> | |
| <p> | |
| Additionally, OctetString's can also be instantiated with a sequence of | |
| 8-bit integers (ASCII codes). | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> univ.OctetString((77, 101, 101, 103, 111)) | |
| OctetString(b'Meego') | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| It is sometimes convenient to express OctetString instances as 8-bit | |
| characters (Python 3 bytes or Python 2 strings) or 8-bit integers. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> octetString = univ.OctetString('ABCDEF') | |
| >>> octetString.asNumbers() | |
| (65, 66, 67, 68, 69, 70) | |
| >>> octetString.asOctets() | |
| b'ABCDEF' | |
| </pre> | |
| </td></tr></table> | |
| <a name="1.1.8"></a> | |
| <h4> | |
| 1.1.8 ObjectIdentifier type | |
| </h4> | |
| <p> | |
| Values of the OBJECT IDENTIFIER type are sequences of integers that could | |
| be used to identify virtually anything in the world. Various ASN.1-based | |
| protocols employ OBJECT IDENTIFIERs for their own identification needs. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| internet-id OBJECT IDENTIFIER ::= { | |
| iso(1) identified-organization(3) dod(6) internet(1) | |
| } | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| One of the natural ways to map OBJECT IDENTIFIER type into a Python | |
| one is to use Python tuples of integers. So this approach is taken by | |
| pyasn1. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> internetId = univ.ObjectIdentifier((1, 3, 6, 1)) | |
| >>> internetId | |
| ObjectIdentifier('1.3.6.1') | |
| >>> internetId[2] | |
| 6 | |
| >>> internetId[1:3] | |
| ObjectIdentifier('3.6') | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| A more human-friendly "dotted" notation is also supported. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> univ.ObjectIdentifier('1.3.6.1') | |
| ObjectIdentifier('1.3.6.1') | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Symbolic names of the arcs of object identifier, sometimes present in | |
| ASN.1 specifications, are not preserved and used in pyasn1 objects. | |
| </p> | |
| <p> | |
| The ObjectIdentifier objects mimic the properties of Python tuple type in | |
| part of immutable sequence object protocol support. | |
| </p> | |
| <a name="1.1.9"></a> | |
| <h4> | |
| 1.1.9 Character string types | |
| </h4> | |
| <p> | |
| ASN.1 standard introduces a diverse set of text-specific types. All of them | |
| were designed to handle various types of characters. Some of these types seem | |
| be obsolete nowdays, as their target technologies are gone. Another issue | |
| to be aware of is that raw OCTET STRING type is sometimes used in practice | |
| by ASN.1 users instead of specialized character string types, despite | |
| explicit prohibition imposed by ASN.1 specification. | |
| </p> | |
| <p> | |
| The two types are specific to ASN.1 are NumericString and PrintableString. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| welcome-message ::= PrintableString { | |
| "Welcome to ASN.1 text types" | |
| } | |
| dial-pad-numbers ::= NumericString { | |
| "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" | |
| } | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Their pyasn1 implementations are: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import char | |
| >>> '%s' % char.PrintableString("Welcome to ASN.1 text types") | |
| 'Welcome to ASN.1 text types' | |
| >>> dialPadNumbers = char.NumericString( | |
| "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" | |
| ) | |
| >>> dialPadNumbers | |
| NumericString(b'0123456789') | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| The following types came to ASN.1 from ISO standards on character sets. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import char | |
| >>> char.VisibleString("abc") | |
| VisibleString(b'abc') | |
| >>> char.IA5String('abc') | |
| IA5String(b'abc') | |
| >>> char.TeletexString('abc') | |
| TeletexString(b'abc') | |
| >>> char.VideotexString('abc') | |
| VideotexString(b'abc') | |
| >>> char.GraphicString('abc') | |
| GraphicString(b'abc') | |
| >>> char.GeneralString('abc') | |
| GeneralString(b'abc') | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| The last three types are relatively recent addition to the family of | |
| character string types: UniversalString, BMPString, UTF8String. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import char | |
| >>> char.UniversalString("abc") | |
| UniversalString(b'abc') | |
| >>> char.BMPString('abc') | |
| BMPString(b'abc') | |
| >>> char.UTF8String('abc') | |
| UTF8String(b'abc') | |
| >>> utf8String = char.UTF8String('У попа была собака') | |
| >>> utf8String | |
| UTF8String(b'\xd0\xa3 \xd0\xbf\xd0\xbe\xd0\xbf\xd0\xb0 \xd0\xb1\xd1\x8b\xd0\xbb\xd0\xb0 \ | |
| \xd1\x81\xd0\xbe\xd0\xb1\xd0\xb0\xd0\xba\xd0\xb0') | |
| >>> print(utf8String) | |
| У попа была собака | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| In pyasn1, all character type objects behave like Python strings. None of | |
| them is currently constrained in terms of valid alphabet so it's up to | |
| the data source to keep an eye on data validation for these types. | |
| </p> | |
| <a name="1.1.10"></a> | |
| <h4> | |
| 1.1.10 Useful types | |
| </h4> | |
| <p> | |
| There are three so-called useful types defined in the standard: | |
| ObjectDescriptor, GeneralizedTime, UTCTime. They all are subtypes | |
| of GraphicString or VisibleString types therefore useful types are | |
| character string types. | |
| </p> | |
| <p> | |
| It's advised by the ASN.1 standard to have an instance of ObjectDescriptor | |
| type holding a human-readable description of corresponding instance of | |
| OBJECT IDENTIFIER type. There are no formal linkage between these instances | |
| and provision for ObjectDescriptor uniqueness in the standard. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import useful | |
| >>> descrBER = useful.ObjectDescriptor( | |
| "Basic encoding of a single ASN.1 type" | |
| ) | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| GeneralizedTime and UTCTime types are designed to hold a human-readable | |
| timestamp in a universal and unambiguous form. The former provides | |
| more flexibility in notation while the latter is more strict but has | |
| Y2K issues. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| ;; Mar 8 2010 12:00:00 MSK | |
| moscow-time GeneralizedTime ::= "20110308120000.0" | |
| ;; Mar 8 2010 12:00:00 UTC | |
| utc-time GeneralizedTime ::= "201103081200Z" | |
| ;; Mar 8 1999 12:00:00 UTC | |
| utc-time UTCTime ::= "9803081200Z" | |
| </pre> | |
| </td></tr></table> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import useful | |
| >>> moscowTime = useful.GeneralizedTime("20110308120000.0") | |
| >>> utcTime = useful.UTCTime("9803081200Z") | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Despite their intended use, these types possess no special, time-related, | |
| handling in pyasn1. They are just printable strings. | |
| </p> | |
| <a name="1.2"></a> | |
| <h4> | |
| 1.2 Tagging | |
| </h4> | |
| <p> | |
| In order to continue with the Constructed ASN.1 types, we will first have | |
| to introduce the concept of tagging (and its pyasn1 implementation), as | |
| some of the Constructed types rely upon the tagging feature. | |
| </p> | |
| <p> | |
| When a value is coming into an ASN.1-based system (received from a network | |
| or read from some storage), the receiving entity has to determine the | |
| type of the value to interpret and verify it accordingly. | |
| </p> | |
| <p> | |
| Historically, the first data serialization protocol introduced in | |
| ASN.1 was BER (Basic Encoding Rules). According to BER, any serialized | |
| value is packed into a triplet of (Type, Length, Value) where Type is a | |
| code that identifies the value (which is called <i>tag</i> in ASN.1), | |
| length is the number of bytes occupied by the value in its serialized form | |
| and value is ASN.1 value in a form suitable for serial transmission or storage. | |
| </p> | |
| <p> | |
| For that reason almost every ASN.1 type has a tag (which is actually a | |
| BER type) associated with it by default. | |
| </p> | |
| <p> | |
| An ASN.1 tag could be viewed as a tuple of three numbers: | |
| (Class, Format, Number). While Number identifies a tag, Class component | |
| is used to create scopes for Numbers. Four scopes are currently defined: | |
| UNIVERSAL, context-specific, APPLICATION and PRIVATE. The Format component | |
| is actually a one-bit flag - zero for tags associated with scalar types, | |
| and one for constructed types (will be discussed later on). | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| MyIntegerType ::= [12] INTEGER | |
| MyOctetString ::= [APPLICATION 0] OCTET STRING | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| In pyasn1, tags are implemented as immutable, tuple-like objects: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import tag | |
| >>> myTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10) | |
| >>> myTag | |
| Tag(tagClass=128, tagFormat=0, tagId=10) | |
| >>> tuple(myTag) | |
| (128, 0, 10) | |
| >>> myTag[2] | |
| 10 | |
| >>> myTag == tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 10) | |
| False | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Default tag, associated with any ASN.1 type, could be extended or replaced | |
| to make new type distinguishable from its ancestor. The standard provides | |
| two modes of tag mangling - IMPLICIT and EXPLICIT. | |
| </p> | |
| <p> | |
| EXPLICIT mode works by appending new tag to the existing ones thus creating | |
| an ordered set of tags. This set will be considered as a whole for type | |
| identification and encoding purposes. Important property of EXPLICIT tagging | |
| mode is that it preserves base type information in encoding what makes it | |
| possible to completely recover type information from encoding. | |
| </p> | |
| <p> | |
| When tagging in IMPLICIT mode, the outermost existing tag is dropped and | |
| replaced with a new one. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| MyIntegerType ::= [12] IMPLICIT INTEGER | |
| MyOctetString ::= [APPLICATION 0] EXPLICIT OCTET STRING | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| To model both modes of tagging, a specialized container TagSet object (holding | |
| zero, one or more Tag objects) is used in pyasn1. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import tag | |
| >>> tagSet = tag.TagSet( | |
| ... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10), # base tag | |
| ... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10) # effective tag | |
| ... ) | |
| >>> tagSet | |
| TagSet(Tag(tagClass=128, tagFormat=0, tagId=10)) | |
| >>> tagSet.getBaseTag() | |
| Tag(tagClass=128, tagFormat=0, tagId=10) | |
| >>> tagSet = tagSet.tagExplicitly( | |
| ... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 20) | |
| ... ) | |
| >>> tagSet | |
| TagSet(Tag(tagClass=128, tagFormat=0, tagId=10), | |
| Tag(tagClass=128, tagFormat=32, tagId=20)) | |
| >>> tagSet = tagSet.tagExplicitly( | |
| ... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 30) | |
| ... ) | |
| >>> tagSet | |
| TagSet(Tag(tagClass=128, tagFormat=0, tagId=10), | |
| Tag(tagClass=128, tagFormat=32, tagId=20), | |
| Tag(tagClass=128, tagFormat=32, tagId=30)) | |
| >>> tagSet = tagSet.tagImplicitly( | |
| ... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 40) | |
| ... ) | |
| >>> tagSet | |
| TagSet(Tag(tagClass=128, tagFormat=0, tagId=10), | |
| Tag(tagClass=128, tagFormat=32, tagId=20), | |
| Tag(tagClass=128, tagFormat=32, tagId=40)) | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| As a side note: the "base tag" concept (accessible through the getBaseTag() | |
| method) is specific to pyasn1 -- the base tag is used to identify the original | |
| ASN.1 type of an object in question. Base tag is never occurs in encoding | |
| and is mostly used internally by pyasn1 for choosing type-specific data | |
| processing algorithms. The "effective tag" is the one that always appears in | |
| encoding and is used on tagSets comparation. | |
| </p> | |
| <p> | |
| Any two TagSet objects could be compared to see if one is a derivative | |
| of the other. Figuring this out is also useful in cases when a type-specific | |
| data processing algorithms are to be chosen. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import tag | |
| >>> tagSet1 = tag.TagSet( | |
| ... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10) # base tag | |
| ... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10) # effective tag | |
| ... ) | |
| >>> tagSet2 = tagSet1.tagExplicitly( | |
| ... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 20) | |
| ... ) | |
| >>> tagSet1.isSuperTagSetOf(tagSet2) | |
| True | |
| >>> tagSet2.isSuperTagSetOf(tagSet1) | |
| False | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| We will complete this discussion on tagging with a real-world example. The | |
| following ASN.1 tagged type: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| MyIntegerType ::= [12] EXPLICIT INTEGER | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| could be expressed in pyasn1 like this: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, tag | |
| >>> class MyIntegerType(univ.Integer): | |
| ... tagSet = univ.Integer.tagSet.tagExplicitly( | |
| ... tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 12) | |
| ... ) | |
| >>> myInteger = MyIntegerType(12345) | |
| >>> myInteger.getTagSet() | |
| TagSet(Tag(tagClass=0, tagFormat=0, tagId=2), | |
| Tag(tagClass=128, tagFormat=32, tagId=12)) | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Referring to the above code, the tagSet class attribute is a property of any | |
| pyasn1 type object that assigns default tagSet to a pyasn1 value object. This | |
| default tagSet specification can be ignored and effectively replaced by some | |
| other tagSet value passed on object instantiation. | |
| </p> | |
| <p> | |
| It's important to understand that the tag set property of pyasn1 type/value | |
| object can never be modifed in place. In other words, a pyasn1 type/value | |
| object can never change its tags. The only way is to create a new pyasn1 | |
| type/value object and associate different tag set with it. | |
| </p> | |
| <a name="1.3"></a> | |
| <h4> | |
| 1.3 Constructed types | |
| </h4> | |
| <p> | |
| Besides scalar types, ASN.1 specifies so-called constructed ones - these | |
| are capable of holding one or more values of other types, both scalar | |
| and constructed. | |
| </p> | |
| <p> | |
| In pyasn1 implementation, constructed ASN.1 types behave like | |
| Python sequences, and also support additional component addressing methods, | |
| specific to particular constructed type. | |
| </p> | |
| <a name="1.3.1"></a> | |
| <h4> | |
| 1.3.1 Sequence and Set types | |
| </h4> | |
| <p> | |
| The Sequence and Set types have many similar properties: | |
| </p> | |
| <ul> | |
| <li>they can hold any number of inner components of different types | |
| <li>every component has a human-friendly identifier | |
| <li>any component can have a default value | |
| <li>some components can be absent. | |
| </ul> | |
| <p> | |
| However, Sequence type guarantees the ordering of Sequence value components | |
| to match their declaration order. By contrast, components of the | |
| Set type can be ordered to best suite application's needs. | |
| <p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| Record ::= SEQUENCE { | |
| id INTEGER, | |
| room [0] INTEGER OPTIONAL, | |
| house [1] INTEGER DEFAULT 0 | |
| } | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Up to this moment, the only method we used for creating new pyasn1 types | |
| is Python sub-classing. With this method, a new, named Python class is created | |
| what mimics type derivation in ASN.1 grammar. However, ASN.1 also allows for | |
| defining anonymous subtypes (room and house components in the example above). | |
| To support anonymous subtyping in pyasn1, a cloning operation on an existing | |
| pyasn1 type object can be invoked what creates a new instance of original | |
| object with possibly modified properties. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, namedtype, tag | |
| >>> class Record(univ.Sequence): | |
| ... componentType = namedtype.NamedTypes( | |
| ... namedtype.NamedType('id', univ.Integer()), | |
| ... namedtype.OptionalNamedType( | |
| ... 'room', | |
| ... univ.Integer().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)) | |
| ... ), | |
| ... namedtype.DefaultedNamedType( | |
| ... 'house', | |
| ... univ.Integer(0).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)) | |
| ... ) | |
| ... ) | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| All pyasn1 constructed type classes have a class attribute <b>componentType</b> | |
| that represent default type specification. Its value is a NamedTypes object. | |
| </p> | |
| <p> | |
| The NamedTypes class instance holds a sequence of NameType, OptionalNamedType | |
| or DefaultedNamedType objects which, in turn, refer to pyasn1 type objects that | |
| represent inner SEQUENCE components specification. | |
| </p> | |
| <p> | |
| Finally, invocation of a subtype() method of pyasn1 type objects in the code | |
| above returns an implicitly tagged copy of original object. | |
| </p> | |
| <p> | |
| Once a SEQUENCE or SET type is decleared with pyasn1, it can be instantiated | |
| and initialized (continuing the above code): | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> record = Record() | |
| >>> record.setComponentByName('id', 123) | |
| >>> print(record.prettyPrint()) | |
| Record: | |
| id=123 | |
| >>> | |
| >>> record.setComponentByPosition(1, 321) | |
| >>> print(record.prettyPrint()) | |
| Record: | |
| id=123 | |
| room=321 | |
| >>> | |
| >>> record.setDefaultComponents() | |
| >>> print(record.prettyPrint()) | |
| Record: | |
| id=123 | |
| room=321 | |
| house=0 | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Inner components of pyasn1 Sequence/Set objects could be accessed using the | |
| following methods: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> record.getComponentByName('id') | |
| Integer(123) | |
| >>> record.getComponentByPosition(1) | |
| Integer(321) | |
| >>> record[2] | |
| Integer(0) | |
| >>> for idx in range(len(record)): | |
| ... print(record.getNameByPosition(idx), record.getComponentByPosition(idx)) | |
| id 123 | |
| room 321 | |
| house 0 | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| The Set type share all the properties of Sequence type, and additionally | |
| support by-tag component addressing (as all Set components have distinct | |
| types). | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, namedtype, tag | |
| >>> class Gamer(univ.Set): | |
| ... componentType = namedtype.NamedTypes( | |
| ... namedtype.NamedType('score', univ.Integer()), | |
| ... namedtype.NamedType('player', univ.OctetString()), | |
| ... namedtype.NamedType('id', univ.ObjectIdentifier()) | |
| ... ) | |
| >>> gamer = Gamer() | |
| >>> gamer.setComponentByType(univ.Integer().getTagSet(), 121343) | |
| >>> gamer.setComponentByType(univ.OctetString().getTagSet(), 'Pascal') | |
| >>> gamer.setComponentByType(univ.ObjectIdentifier().getTagSet(), (1,3,7,2)) | |
| >>> print(gamer.prettyPrint()) | |
| Gamer: | |
| score=121343 | |
| player=b'Pascal' | |
| id=1.3.7.2 | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <a name="1.3.2"></a> | |
| <h4> | |
| 1.3.2 SequenceOf and SetOf types | |
| </h4> | |
| <p> | |
| Both, SequenceOf and SetOf types resemble an unlimited size list of components. | |
| All the components must be of the same type. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| Progression ::= SEQUENCE OF INTEGER | |
| arithmeticProgression Progression ::= { 1, 3, 5, 7 } | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| SequenceOf and SetOf types are expressed by the very similar pyasn1 type | |
| objects. Their components can only be addressed by position and they | |
| both have a property of automatic resize. | |
| </p> | |
| <p> | |
| To specify inner component type, the <b>componentType</b> class attribute | |
| should refer to another pyasn1 type object. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> class Progression(univ.SequenceOf): | |
| ... componentType = univ.Integer() | |
| >>> arithmeticProgression = Progression() | |
| >>> arithmeticProgression.setComponentByPosition(1, 111) | |
| >>> print(arithmeticProgression.prettyPrint()) | |
| Progression: | |
| -empty- 111 | |
| >>> arithmeticProgression.setComponentByPosition(0, 100) | |
| >>> print(arithmeticProgression.prettyPrint()) | |
| Progression: | |
| 100 111 | |
| >>> | |
| >>> for idx in range(len(arithmeticProgression)): | |
| ... arithmeticProgression.getComponentByPosition(idx) | |
| Integer(100) | |
| Integer(111) | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Any scalar or constructed pyasn1 type object can serve as an inner component. | |
| Missing components are prohibited in SequenceOf/SetOf value objects. | |
| </p> | |
| <a name="1.3.3"></a> | |
| <h4> | |
| 1.3.3 Choice type | |
| </h4> | |
| <p> | |
| Values of ASN.1 CHOICE type can contain only a single value of a type from a | |
| list of possible alternatives. Alternatives must be ASN.1 types with | |
| distinct tags for the whole structure to remain unambiguous. Unlike most | |
| other types, CHOICE is an untagged one, e.g. it has no base tag of its own. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| CodeOrMessage ::= CHOICE { | |
| code INTEGER, | |
| message OCTET STRING | |
| } | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| In pyasn1 implementation, Choice object behaves like Set but accepts only | |
| a single inner component at a time. It also offers a few additional methods | |
| specific to its behaviour. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, namedtype | |
| >>> class CodeOrMessage(univ.Choice): | |
| ... componentType = namedtype.NamedTypes( | |
| ... namedtype.NamedType('code', univ.Integer()), | |
| ... namedtype.NamedType('message', univ.OctetString()) | |
| ... ) | |
| >>> | |
| >>> codeOrMessage = CodeOrMessage() | |
| >>> print(codeOrMessage.prettyPrint()) | |
| CodeOrMessage: | |
| >>> codeOrMessage.setComponentByName('code', 123) | |
| >>> print(codeOrMessage.prettyPrint()) | |
| CodeOrMessage: | |
| code=123 | |
| >>> codeOrMessage.setComponentByName('message', 'my string value') | |
| >>> print(codeOrMessage.prettyPrint()) | |
| CodeOrMessage: | |
| message=b'my string value' | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Since there could be only a single inner component value in the pyasn1 Choice | |
| value object, either of the following methods could be used for fetching it | |
| (continuing previous code): | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> codeOrMessage.getName() | |
| 'message' | |
| >>> codeOrMessage.getComponent() | |
| OctetString(b'my string value') | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <a name="1.3.4"></a> | |
| <h4> | |
| 1.3.4 Any type | |
| </h4> | |
| <p> | |
| The ASN.1 ANY type is a kind of wildcard or placeholder that matches | |
| any other type without knowing it in advance. Like CHOICE type, ANY | |
| has no base tag. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| Error ::= SEQUENCE { | |
| code INTEGER, | |
| parameter ANY DEFINED BY code | |
| } | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| The ANY type is frequently used in specifications, where exact type is not | |
| yet agreed upon between communicating parties or the number of possible | |
| alternatives of a type is infinite. | |
| Sometimes an auxiliary selector is kept around to help parties indicate | |
| the kind of ANY payload in effect ("code" in the example above). | |
| </p> | |
| <p> | |
| Values of the ANY type contain serialized ASN.1 value(s) in form of | |
| an octet string. Therefore pyasn1 Any value object share the properties of | |
| pyasn1 OctetString object. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> someValue = univ.Any(b'\x02\x01\x01') | |
| >>> someValue | |
| Any(b'\x02\x01\x01') | |
| >>> str(someValue) | |
| '\x02\x01\x01' | |
| >>> bytes(someValue) | |
| b'\x02\x01\x01' | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Receiving application is supposed to explicitly deserialize the content of Any | |
| value object, possibly using auxiliary selector for figuring out its ASN.1 | |
| type to pick appropriate decoder. | |
| </p> | |
| <p> | |
| There will be some more talk and code snippets covering Any type in the codecs | |
| chapters that follow. | |
| </p> | |
| <a name="1.4"></a> | |
| <h4> | |
| 1.4 Subtype constraints | |
| </h4> | |
| <p> | |
| Most ASN.1 types can correspond to an infinite set of values. To adapt to | |
| particular application's data model and needs, ASN.1 provides a mechanism | |
| for limiting the infinite set to values, that make sense in particular case. | |
| </p> | |
| <p> | |
| Imposing value constraints on an ASN.1 type can also be seen as creating | |
| a subtype from its base type. | |
| </p> | |
| <p> | |
| In pyasn1, constraints take shape of immutable objects capable | |
| of evaluating given value against constraint-specific requirements. | |
| Constraint object is a property of pyasn1 type. Like TagSet property, | |
| associated with every pyasn1 type, constraints can never be modified | |
| in place. The only way to modify pyasn1 type constraint is to associate | |
| new constraint object to a new pyasn1 type object. | |
| </p> | |
| <p> | |
| A handful of different flavors of <i>constraints</i> are defined in ASN.1. | |
| We will discuss them one by one in the following chapters and also explain | |
| how to combine and apply them to types. | |
| </p> | |
| <a name="1.4.1"></a> | |
| <h4> | |
| 1.4.1 Single value constraint | |
| </h4> | |
| <p> | |
| This kind of constraint allows for limiting type to a finite, specified set | |
| of values. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| DialButton ::= OCTET STRING ( | |
| "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | |
| ) | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Its pyasn1 implementation would look like: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import constraint | |
| >>> c = constraint.SingleValueConstraint( | |
| '0','1','2','3','4','5','6','7','8','9' | |
| ) | |
| >>> c | |
| SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) | |
| >>> c('0') | |
| >>> c('A') | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.type.error.ValueConstraintError: | |
| SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) failed at: A | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| As can be seen in the snippet above, if a value violates the constraint, an | |
| exception will be thrown. A constrainted pyasn1 type object holds a | |
| reference to a constraint object (or their combination, as will be explained | |
| later) and calls it for value verification. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, constraint | |
| >>> class DialButton(univ.OctetString): | |
| ... subtypeSpec = constraint.SingleValueConstraint( | |
| ... '0','1','2','3','4','5','6','7','8','9' | |
| ... ) | |
| >>> DialButton('0') | |
| DialButton(b'0') | |
| >>> DialButton('A') | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.type.error.ValueConstraintError: | |
| SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) failed at: A | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Constrained pyasn1 value object can never hold a violating value. | |
| </p> | |
| <a name="1.4.2"></a> | |
| <h4> | |
| 1.4.2 Value range constraint | |
| </h4> | |
| <p> | |
| A pair of values, compliant to a type to be constrained, denote low and upper | |
| bounds of allowed range of values of a type. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| Teenagers ::= INTEGER (13..19) | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| And in pyasn1 terms: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, constraint | |
| >>> class Teenagers(univ.Integer): | |
| ... subtypeSpec = constraint.ValueRangeConstraint(13, 19) | |
| >>> Teenagers(14) | |
| Teenagers(14) | |
| >>> Teenagers(20) | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.type.error.ValueConstraintError: | |
| ValueRangeConstraint(13, 19) failed at: 20 | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Value range constraint usually applies numeric types. | |
| </p> | |
| <a name="1.4.3"></a> | |
| <h4> | |
| 1.4.3 Size constraint | |
| </h4> | |
| <p> | |
| It is sometimes convenient to set or limit the allowed size of a data item | |
| to be sent from one application to another to manage bandwidth and memory | |
| consumption issues. Size constraint specifies the lower and upper bounds | |
| of the size of a valid value. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| TwoBits ::= BIT STRING (SIZE (2)) | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Express the same grammar in pyasn1: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, constraint | |
| >>> class TwoBits(univ.BitString): | |
| ... subtypeSpec = constraint.ValueSizeConstraint(2, 2) | |
| >>> TwoBits((1,1)) | |
| TwoBits("'11'B") | |
| >>> TwoBits((1,1,0)) | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.type.error.ValueConstraintError: | |
| ValueSizeConstraint(2, 2) failed at: (1, 1, 0) | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Size constraint can be applied to potentially massive values - bit or octet | |
| strings, SEQUENCE OF/SET OF values. | |
| </p> | |
| <a name="1.4.4"></a> | |
| <h4> | |
| 1.4.4 Alphabet constraint | |
| </h4> | |
| <p> | |
| The permitted alphabet constraint is similar to Single value constraint | |
| but constraint applies to individual characters of a value. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| MorseCode ::= PrintableString (FROM ("."|"-"|" ")) | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| And in pyasn1: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import char, constraint | |
| >>> class MorseCode(char.PrintableString): | |
| ... subtypeSpec = constraint.PermittedAlphabetConstraint(".", "-", " ") | |
| >>> MorseCode("...---...") | |
| MorseCode('...---...') | |
| >>> MorseCode("?") | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.type.error.ValueConstraintError: | |
| PermittedAlphabetConstraint(".", "-", " ") failed at: "?" | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Current implementation does not handle ranges of characters in constraint | |
| (FROM "A".."Z" syntax), one has to list the whole set in a range. | |
| </p> | |
| <a name="1.4.5"></a> | |
| <h4> | |
| 1.4.5 Constraint combinations | |
| </h4> | |
| <p> | |
| Up to this moment, we used a single constraint per ASN.1 type. The standard, | |
| however, allows for combining multiple individual constraints into | |
| intersections, unions and exclusions. | |
| </p> | |
| <p> | |
| In pyasn1 data model, all of these methods of constraint combinations are | |
| implemented as constraint-like objects holding individual constraint (or | |
| combination) objects. Like terminal constraint objects, combination objects | |
| are capable to perform value verification at its set of enclosed constraints | |
| according to the logic of particular combination. | |
| </p> | |
| <p> | |
| Constraints intersection verification succeeds only if a value is | |
| compliant to each constraint in a set. To begin with, the following | |
| specification will constitute a valid telephone number: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| PhoneNumber ::= NumericString (FROM ("0".."9")) (SIZE 11) | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Constraint intersection object serves the logic above: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import char, constraint | |
| >>> class PhoneNumber(char.NumericString): | |
| ... subtypeSpec = constraint.ConstraintsIntersection( | |
| ... constraint.PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'), | |
| ... constraint.ValueSizeConstraint(11, 11) | |
| ... ) | |
| >>> PhoneNumber('79039343212') | |
| PhoneNumber('79039343212') | |
| >>> PhoneNumber('?9039343212') | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.type.error.ValueConstraintError: | |
| ConstraintsIntersection( | |
| PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'), | |
| ValueSizeConstraint(11, 11)) failed at: | |
| PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9') failed at: "?039343212" | |
| >>> PhoneNumber('9343212') | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.type.error.ValueConstraintError: | |
| ConstraintsIntersection( | |
| PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'), | |
| ValueSizeConstraint(11, 11)) failed at: | |
| ValueSizeConstraint(10, 10) failed at: "9343212" | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Union of constraints works by making sure that a value is compliant | |
| to any of the constraint in a set. For instance: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| CapitalOrSmall ::= IA5String (FROM ('A','B','C') | FROM ('a','b','c')) | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| It's important to note, that a value must fully comply to any single | |
| constraint in a set. In the specification above, a value of all small or | |
| all capital letters is compliant, but a mix of small&capitals is not. | |
| Here's its pyasn1 analogue: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import char, constraint | |
| >>> class CapitalOrSmall(char.IA5String): | |
| ... subtypeSpec = constraint.ConstraintsUnion( | |
| ... constraint.PermittedAlphabetConstraint('A','B','C'), | |
| ... constraint.PermittedAlphabetConstraint('a','b','c') | |
| ... ) | |
| >>> CapitalOrSmall('ABBA') | |
| CapitalOrSmall('ABBA') | |
| >>> CapitalOrSmall('abba') | |
| CapitalOrSmall('abba') | |
| >>> CapitalOrSmall('Abba') | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.type.error.ValueConstraintError: | |
| ConstraintsUnion(PermittedAlphabetConstraint('A', 'B', 'C'), | |
| PermittedAlphabetConstraint('a', 'b', 'c')) failed at: failed for "Abba" | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Finally, the exclusion constraint simply negates the logic of value | |
| verification at a constraint. In the following example, any integer value | |
| is allowed in a type but not zero. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| NoZero ::= INTEGER (ALL EXCEPT 0) | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| In pyasn1 the above definition would read: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, constraint | |
| >>> class NoZero(univ.Integer): | |
| ... subtypeSpec = constraint.ConstraintsExclusion( | |
| ... constraint.SingleValueConstraint(0) | |
| ... ) | |
| >>> NoZero(1) | |
| NoZero(1) | |
| >>> NoZero(0) | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.type.error.ValueConstraintError: | |
| ConstraintsExclusion(SingleValueConstraint(0)) failed at: 0 | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| The depth of such a constraints tree, built with constraint combination objects | |
| at its nodes, has not explicit limit. Value verification is performed in a | |
| recursive manner till a definite solution is found. | |
| </p> | |
| <a name="1.5"></a> | |
| <h4> | |
| 1.5 Types relationships | |
| </h4> | |
| <p> | |
| In the course of data processing in an application, it is sometimes | |
| convenient to figure out the type relationships between pyasn1 type or | |
| value objects. Formally, two things influence pyasn1 types relationship: | |
| <i>tag set</i> and <i>subtype constraints</i>. One pyasn1 type is considered | |
| to be a derivative of another if their TagSet and Constraint objects are | |
| a derivation of one another. | |
| </p> | |
| <p> | |
| The following example illustrates the concept (we use the same tagset but | |
| different constraints for simplicity): | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, constraint | |
| >>> i1 = univ.Integer(subtypeSpec=constraint.ValueRangeConstraint(3,8)) | |
| >>> i2 = univ.Integer(subtypeSpec=constraint.ConstraintsIntersection( | |
| ... constraint.ValueRangeConstraint(3,8), | |
| ... constraint.ValueRangeConstraint(4,7) | |
| ... ) ) | |
| >>> i1.isSameTypeWith(i2) | |
| False | |
| >>> i1.isSuperTypeOf(i2) | |
| True | |
| >>> i1.isSuperTypeOf(i1) | |
| True | |
| >>> i2.isSuperTypeOf(i1) | |
| False | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| As can be seen in the above code snippet, there are two methods of any pyasn1 | |
| type/value object that test types for their relationship: | |
| <b>isSameTypeWith</b>() and <b>isSuperTypeOf</b>(). The former is | |
| self-descriptive while the latter yields true if the argument appears | |
| to be a pyasn1 object which has tagset and constraints derived from those | |
| of the object being called. | |
| </p> | |
| <a name="2"></a> | |
| <h3> | |
| 2. Codecs | |
| </h3> | |
| <p> | |
| In ASN.1 context, | |
| <a href=http://en.wikipedia.org/wiki/Codec>codec</a> | |
| is a program that transforms between concrete data structures and a stream | |
| of octets, suitable for transmission over the wire. This serialized form of | |
| data is sometimes called <i>substrate</i> or <i>essence</i>. | |
| </p> | |
| <p> | |
| In pyasn1 implementation, substrate takes shape of Python 3 bytes or | |
| Python 2 string objects. | |
| </p> | |
| <p> | |
| One of the properties of a codec is its ability to cope with incomplete | |
| data and/or substrate what implies codec to be stateful. In other words, | |
| when decoder runs out of substrate and data item being recovered is still | |
| incomplete, stateful codec would suspend and complete data item recovery | |
| whenever the rest of substrate becomes available. Similarly, stateful encoder | |
| would encode data items in multiple steps waiting for source data to | |
| arrive. Codec restartability is especially important when application deals | |
| with large volumes of data and/or runs on low RAM. For an interesting | |
| discussion on codecs options and design choices, refer to | |
| <a href=http://directory.apache.org/subprojects/asn1/>Apache ASN.1 project</a> | |
| . | |
| </p> | |
| <p> | |
| As of this writing, codecs implemented in pyasn1 are all stateless, mostly | |
| to keep the code simple. | |
| </p> | |
| <p> | |
| The pyasn1 package currently supports | |
| <a href=http://en.wikipedia.org/wiki/Basic_encoding_rules>BER</a> codec and | |
| its variations -- | |
| <a href=http://en.wikipedia.org/wiki/Canonical_encoding_rules>CER</a> and | |
| <a href=http://en.wikipedia.org/wiki/Distinguished_encoding_rules>DER</a>. | |
| More ASN.1 codecs are planned for implementation in the future. | |
| </p> | |
| <a name="2.1"></a> | |
| <h4> | |
| 2.1 Encoders | |
| </h4> | |
| <p> | |
| Encoder is used for transforming pyasn1 value objects into substrate. Only | |
| pyasn1 value objects could be serialized, attempts to process pyasn1 type | |
| objects will cause encoder failure. | |
| </p> | |
| <p> | |
| The following code will create a pyasn1 Integer object and serialize it with | |
| BER encoder: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> from pyasn1.codec.ber import encoder | |
| >>> encoder.encode(univ.Integer(123456)) | |
| b'\x02\x03\x01\xe2@' | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| BER standard also defines a so-called <i>indefinite length</i> encoding form | |
| which makes large data items processing more memory efficient. It is mostly | |
| useful when encoder does not have the whole value all at once and the | |
| length of the value can not be determined at the beginning of encoding. | |
| </p> | |
| <p> | |
| <i>Constructed encoding</i> is another feature of BER closely related to the | |
| indefinite length form. In essence, a large scalar value (such as ASN.1 | |
| character BitString type) could be chopped into smaller chunks by encoder | |
| and transmitted incrementally to limit memory consumption. Unlike indefinite | |
| length case, the length of the whole value must be known in advance when | |
| using constructed, definite length encoding form. | |
| </p> | |
| <p> | |
| Since pyasn1 codecs are not restartable, pyasn1 encoder may only encode data | |
| item all at once. However, even in this case, generating indefinite length | |
| encoding may help a low-memory receiver, running a restartable decoder, | |
| to process a large data item. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> from pyasn1.codec.ber import encoder | |
| >>> encoder.encode( | |
| ... univ.OctetString('The quick brown fox jumps over the lazy dog'), | |
| ... defMode=False, | |
| ... maxChunkSize=8 | |
| ... ) | |
| b'$\x80\x04\x08The quic\x04\x08k brown \x04\x08fox jump\x04\x08s over \ | |
| t\x04\x08he lazy \x04\x03dog\x00\x00' | |
| >>> | |
| >>> encoder.encode( | |
| ... univ.OctetString('The quick brown fox jumps over the lazy dog'), | |
| ... maxChunkSize=8 | |
| ... ) | |
| b'$7\x04\x08The quic\x04\x08k brown \x04\x08fox jump\x04\x08s over \ | |
| t\x04\x08he lazy \x04\x03dog' | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| The <b>defMode</b> encoder parameter disables definite length encoding mode, | |
| while the optional <b>maxChunkSize</b> parameter specifies desired | |
| substrate chunk size that influences memory requirements at the decoder's end. | |
| </p> | |
| <p> | |
| To use CER or DER encoders one needs to explicitly import and call them - the | |
| APIs are all compatible. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> from pyasn1.codec.ber import encoder as ber_encoder | |
| >>> from pyasn1.codec.cer import encoder as cer_encoder | |
| >>> from pyasn1.codec.der import encoder as der_encoder | |
| >>> ber_encoder.encode(univ.Boolean(True)) | |
| b'\x01\x01\x01' | |
| >>> cer_encoder.encode(univ.Boolean(True)) | |
| b'\x01\x01\xff' | |
| >>> der_encoder.encode(univ.Boolean(True)) | |
| b'\x01\x01\xff' | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <a name="2.2"></a> | |
| <h4> | |
| 2.2 Decoders | |
| </h4> | |
| <p> | |
| In the process of decoding, pyasn1 value objects are created and linked to | |
| each other, based on the information containted in the substrate. Thus, | |
| the original pyasn1 value object(s) are recovered. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> from pyasn1.codec.ber import encoder, decoder | |
| >>> substrate = encoder.encode(univ.Boolean(True)) | |
| >>> decoder.decode(substrate) | |
| (Boolean('True(1)'), b'') | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Commenting on the code snippet above, pyasn1 decoder accepts substrate | |
| as an argument and returns a tuple of pyasn1 value object (possibly | |
| a top-level one in case of constructed object) and unprocessed part | |
| of input substrate. | |
| </p> | |
| <p> | |
| All pyasn1 decoders can handle both definite and indefinite length | |
| encoding modes automatically, explicit switching into one mode | |
| to another is not required. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> from pyasn1.codec.ber import encoder, decoder | |
| >>> substrate = encoder.encode( | |
| ... univ.OctetString('The quick brown fox jumps over the lazy dog'), | |
| ... defMode=False, | |
| ... maxChunkSize=8 | |
| ... ) | |
| >>> decoder.decode(substrate) | |
| (OctetString(b'The quick brown fox jumps over the lazy dog'), b'') | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Speaking of BER/CER/DER encoding, in many situations substrate may not contain | |
| all necessary information needed for complete and accurate ASN.1 values | |
| recovery. The most obvious cases include implicitly tagged ASN.1 types | |
| and constrained types. | |
| </p> | |
| <p> | |
| As discussed earlier in this handbook, when an ASN.1 type is implicitly | |
| tagged, previous outermost tag is lost and never appears in substrate. | |
| If it is the base tag that gets lost, decoder is unable to pick type-specific | |
| value decoder at its table of built-in types, and therefore recover | |
| the value part, based only on the information contained in substrate. The | |
| approach taken by pyasn1 decoder is to use a prototype pyasn1 type object (or | |
| a set of them) to <i>guide</i> the decoding process by matching [possibly | |
| incomplete] tags recovered from substrate with those found in prototype pyasn1 | |
| type objects (also called pyasn1 specification object further in this paper). | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.codec.ber import decoder | |
| >>> decoder.decode(b'\x02\x01\x0c', asn1Spec=univ.Integer()) | |
| Integer(12), b'' | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Decoder would neither modify pyasn1 specification object nor use | |
| its current values (if it's a pyasn1 value object), but rather use it as | |
| a hint for choosing proper decoder and as a pattern for creating new objects: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, tag | |
| >>> from pyasn1.codec.ber import encoder, decoder | |
| >>> i = univ.Integer(12345).subtype( | |
| ... implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 40) | |
| ... ) | |
| >>> substrate = encoder.encode(i) | |
| >>> substrate | |
| b'\x9f(\x0209' | |
| >>> decoder.decode(substrate) | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.error.PyAsn1Error: | |
| TagSet(Tag(tagClass=128, tagFormat=0, tagId=40)) not in asn1Spec | |
| >>> decoder.decode(substrate, asn1Spec=i) | |
| (Integer(12345), b'') | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Notice in the example above, that an attempt to run decoder without passing | |
| pyasn1 specification object fails because recovered tag does not belong | |
| to any of the built-in types. | |
| </p> | |
| <p> | |
| Another important feature of guided decoder operation is the use of | |
| values constraints possibly present in pyasn1 specification object. | |
| To explain this, we will decode a random integer object into generic Integer | |
| and the constrained one. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, constraint | |
| >>> from pyasn1.codec.ber import encoder, decoder | |
| >>> class DialDigit(univ.Integer): | |
| ... subtypeSpec = constraint.ValueRangeConstraint(0,9) | |
| >>> substrate = encoder.encode(univ.Integer(13)) | |
| >>> decoder.decode(substrate) | |
| (Integer(13), b'') | |
| >>> decoder.decode(substrate, asn1Spec=DialDigit()) | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.type.error.ValueConstraintError: | |
| ValueRangeConstraint(0, 9) failed at: 13 | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Similarily to encoders, to use CER or DER decoders application has to | |
| explicitly import and call them - all APIs are compatible. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> from pyasn1.codec.ber import encoder as ber_encoder | |
| >>> substrate = ber_encoder.encode(univ.OctetString('http://pyasn1.sf.net')) | |
| >>> | |
| >>> from pyasn1.codec.ber import decoder as ber_decoder | |
| >>> from pyasn1.codec.cer import decoder as cer_decoder | |
| >>> from pyasn1.codec.der import decoder as der_decoder | |
| >>> | |
| >>> ber_decoder.decode(substrate) | |
| (OctetString(b'http://pyasn1.sf.net'), b'') | |
| >>> cer_decoder.decode(substrate) | |
| (OctetString(b'http://pyasn1.sf.net'), b'') | |
| >>> der_decoder.decode(substrate) | |
| (OctetString(b'http://pyasn1.sf.net'), b'') | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <a name="2.2.1"></a> | |
| <h4> | |
| 2.2.1 Decoding untagged types | |
| </h4> | |
| <p> | |
| It has already been mentioned, that ASN.1 has two "special case" types: | |
| CHOICE and ANY. They are different from other types in part of | |
| tagging - unless these two are additionally tagged, neither of them will | |
| have their own tag. Therefore these types become invisible in substrate | |
| and can not be recovered without passing pyasn1 specification object to | |
| decoder. | |
| </p> | |
| <p> | |
| To explain the issue, we will first prepare a Choice object to deal with: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, namedtype | |
| >>> class CodeOrMessage(univ.Choice): | |
| ... componentType = namedtype.NamedTypes( | |
| ... namedtype.NamedType('code', univ.Integer()), | |
| ... namedtype.NamedType('message', univ.OctetString()) | |
| ... ) | |
| >>> | |
| >>> codeOrMessage = CodeOrMessage() | |
| >>> codeOrMessage.setComponentByName('message', 'my string value') | |
| >>> print(codeOrMessage.prettyPrint()) | |
| CodeOrMessage: | |
| message=b'my string value' | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Let's now encode this Choice object and then decode its substrate | |
| with and without pyasn1 specification object: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.codec.ber import encoder, decoder | |
| >>> substrate = encoder.encode(codeOrMessage) | |
| >>> substrate | |
| b'\x04\x0fmy string value' | |
| >>> encoder.encode(univ.OctetString('my string value')) | |
| b'\x04\x0fmy string value' | |
| >>> | |
| >>> decoder.decode(substrate) | |
| (OctetString(b'my string value'), b'') | |
| >>> codeOrMessage, substrate = decoder.decode(substrate, asn1Spec=CodeOrMessage()) | |
| >>> print(codeOrMessage.prettyPrint()) | |
| CodeOrMessage: | |
| message=b'my string value' | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| First thing to notice in the listing above is that the substrate produced | |
| for our Choice value object is equivalent to the substrate for an OctetString | |
| object initialized to the same value. In other words, any information about | |
| the Choice component is absent in encoding. | |
| </p> | |
| <p> | |
| Sure enough, that kind of substrate will decode into an OctetString object, | |
| unless original Choice type object is passed to decoder to guide the decoding | |
| process. | |
| </p> | |
| <p> | |
| Similarily untagged ANY type behaves differently on decoding phase - when | |
| decoder bumps into an Any object in pyasn1 specification, it stops decoding | |
| and puts all the substrate into a new Any value object in form of an octet | |
| string. Concerned application could then re-run decoder with an additional, | |
| more exact pyasn1 specification object to recover the contents of Any | |
| object. | |
| </p> | |
| <p> | |
| As it was mentioned elsewhere in this paper, Any type allows for incomplete | |
| or changing ASN.1 specification to be handled gracefully by decoder and | |
| applications. | |
| </p> | |
| <p> | |
| To illustrate the working of Any type, we'll have to make the stage | |
| by encoding a pyasn1 object and then putting its substrate into an any | |
| object. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> from pyasn1.codec.ber import encoder, decoder | |
| >>> innerSubstrate = encoder.encode(univ.Integer(1234)) | |
| >>> innerSubstrate | |
| b'\x02\x02\x04\xd2' | |
| >>> any = univ.Any(innerSubstrate) | |
| >>> any | |
| Any(b'\x02\x02\x04\xd2') | |
| >>> substrate = encoder.encode(any) | |
| >>> substrate | |
| b'\x02\x02\x04\xd2' | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| As with Choice type encoding, there is no traces of Any type in substrate. | |
| Obviously, the substrate we are dealing with, will decode into the inner | |
| [Integer] component, unless pyasn1 specification is given to guide the | |
| decoder. Continuing previous code: | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ | |
| >>> from pyasn1.codec.ber import encoder, decoder | |
| >>> decoder.decode(substrate) | |
| (Integer(1234), b'') | |
| >>> any, substrate = decoder.decode(substrate, asn1Spec=univ.Any()) | |
| >>> any | |
| Any(b'\x02\x02\x04\xd2') | |
| >>> decoder.decode(str(any)) | |
| (Integer(1234), b'') | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| Both CHOICE and ANY types are widely used in practice. Reader is welcome to | |
| take a look at | |
| <a href=http://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt> | |
| ASN.1 specifications of X.509 applications</a> for more information. | |
| </p> | |
| <a name="2.2.2"></a> | |
| <h4> | |
| 2.2.2 Ignoring unknown types | |
| </h4> | |
| <p> | |
| When dealing with a loosely specified ASN.1 structure, the receiving | |
| end may not be aware of some types present in the substrate. It may be | |
| convenient then to turn decoder into a recovery mode. Whilst there, decoder | |
| will not bail out when hit an unknown tag but rather treat it as an Any | |
| type. | |
| </p> | |
| <table bgcolor="lightgray" border=0 width=100%><TR><TD> | |
| <pre> | |
| >>> from pyasn1.type import univ, tag | |
| >>> from pyasn1.codec.ber import encoder, decoder | |
| >>> taggedInt = univ.Integer(12345).subtype( | |
| ... implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 40) | |
| ... ) | |
| >>> substrate = encoder.encode(taggedInt) | |
| >>> decoder.decode(substrate) | |
| Traceback (most recent call last): | |
| ... | |
| pyasn1.error.PyAsn1Error: TagSet(Tag(tagClass=128, tagFormat=0, tagId=40)) not in asn1Spec | |
| >>> | |
| >>> decoder.decode.defaultErrorState = decoder.stDumpRawValue | |
| >>> decoder.decode(substrate) | |
| (Any(b'\x9f(\x0209'), '') | |
| >>> | |
| </pre> | |
| </td></tr></table> | |
| <p> | |
| It's also possible to configure a custom decoder, to handle unknown tags | |
| found in substrate. This can be done by means of <b>defaultRawDecoder</b> | |
| attribute holding a reference to type decoder object. Refer to the source | |
| for API details. | |
| </p> | |
| <a name="3"></a> | |
| <h3> | |
| 3. Feedback and getting help | |
| </h3> | |
| <p> | |
| Although pyasn1 software is almost a decade old and used in many production | |
| environments, it still may have bugs and non-implemented pieces. Anyone | |
| who happens to run into such defect is welcome to complain to | |
| <a href=mailto:pyasn1-users@lists.sourceforge.net>pyasn1 mailing list</a> | |
| or better yet fix the issue and send | |
| <a href=mailto:ilya@glas.net>me</a> the patch. | |
| </p> | |
| <p> | |
| Typically, pyasn1 is used for building arbitrary protocol support into | |
| various applications. This involves manual translation of ASN.1 data | |
| structures into their pyasn1 implementations. To save time and effort, | |
| data structures for some of the popular protocols are pre-programmed | |
| and kept for further re-use in form of the | |
| <a href=http://sourceforge.net/projects/pyasn1/files/pyasn1-modules/> | |
| pyasn1-modules package</a>. For instance, many structures for PKI (X.509, | |
| PKCS#*, CRMF, OCSP), LDAP and SNMP are present. | |
| Applications authors are advised to import and use relevant modules | |
| from that package whenever needed protocol structures are already | |
| there. New protocol modules contributions are welcome. | |
| </p> | |
| <p> | |
| And finally, the latest pyasn1 package revision is available for free | |
| download from | |
| <a href=http://sourceforge.net/projects/pyasn1/>project home</a> and | |
| also from the | |
| <a href=http://pypi.python.org/pypi>Python package repository</a>. | |
| </p> | |
| <hr> | |
| </td> | |
| </tr> | |
| </table> | |
| </center> | |
| </body> | |
| </html> |