3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2020 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, version 3 of the License.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this program. If not, see <http://www.gnu.org/licenses/>.
17 """Python ASN.1 DER/BER codec with abstract structures
19 This library allows you to marshal various structures in ASN.1 DER
20 format, unmarshal them in BER/CER/DER ones.
24 >>> Integer().decode(raw) == i
27 There are primitive types, holding single values
28 (:py:class:`pyderasn.BitString`,
29 :py:class:`pyderasn.Boolean`,
30 :py:class:`pyderasn.Enumerated`,
31 :py:class:`pyderasn.GeneralizedTime`,
32 :py:class:`pyderasn.Integer`,
33 :py:class:`pyderasn.Null`,
34 :py:class:`pyderasn.ObjectIdentifier`,
35 :py:class:`pyderasn.OctetString`,
36 :py:class:`pyderasn.UTCTime`,
37 :py:class:`various strings <pyderasn.CommonString>`
38 (:py:class:`pyderasn.BMPString`,
39 :py:class:`pyderasn.GeneralString`,
40 :py:class:`pyderasn.GraphicString`,
41 :py:class:`pyderasn.IA5String`,
42 :py:class:`pyderasn.ISO646String`,
43 :py:class:`pyderasn.NumericString`,
44 :py:class:`pyderasn.PrintableString`,
45 :py:class:`pyderasn.T61String`,
46 :py:class:`pyderasn.TeletexString`,
47 :py:class:`pyderasn.UniversalString`,
48 :py:class:`pyderasn.UTF8String`,
49 :py:class:`pyderasn.VideotexString`,
50 :py:class:`pyderasn.VisibleString`)),
51 constructed types, holding multiple primitive types
52 (:py:class:`pyderasn.Sequence`,
53 :py:class:`pyderasn.SequenceOf`,
54 :py:class:`pyderasn.Set`,
55 :py:class:`pyderasn.SetOf`),
56 and special types like
57 :py:class:`pyderasn.Any` and
58 :py:class:`pyderasn.Choice`.
66 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
67 the default tag used during coding process. You can override it with
68 either ``IMPLICIT`` (using ``impl`` keyword argument), or
69 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
70 raw binary string, containing that tag. You can **not** set implicit and
71 explicit tags simultaneously.
73 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
74 functions, allowing you to easily create ``CONTEXT``
75 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
76 number. Pay attention that explicit tags always have *constructed* tag
77 (``tag_ctxc``), but implicit tags for primitive types are primitive
82 >>> Integer(impl=tag_ctxp(1))
84 >>> Integer(expl=tag_ctxc(2))
87 Implicit tag is not explicitly shown.
89 Two objects of the same type, but with different implicit/explicit tags
92 You can get object's effective tag (either default or implicited) through
93 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
96 >>> tag_decode(tag_ctxc(123))
98 >>> klass, form, num = tag_decode(tag_ctxc(123))
99 >>> klass == TagClassContext
101 >>> form == TagFormConstructed
104 To determine if object has explicit tag, use ``expled`` boolean property
105 and ``expl_tag`` property, returning explicit tag's value.
110 Many objects in sequences could be ``OPTIONAL`` and could have
111 ``DEFAULT`` value. You can specify that object's property using
112 corresponding keyword arguments.
114 >>> Integer(optional=True, default=123)
115 INTEGER 123 OPTIONAL DEFAULT
117 Those specifications do not play any role in primitive value encoding,
118 but are taken into account when dealing with sequences holding them. For
119 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
122 class Version(Integer):
128 class TBSCertificate(Sequence):
130 ("version", Version(expl=tag_ctxc(0), default="v1")),
133 When default argument is used and value is not specified, then it equals
141 Some objects give ability to set value size constraints. This is either
142 possible integer value, or allowed length of various strings and
143 sequences. Constraints are set in the following way::
148 And values satisfaction is checked as: ``MIN <= X <= MAX``.
150 For simplicity you can also set bounds the following way::
152 bounded_x = X(bounds=(MIN, MAX))
154 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
160 All objects have ``ready`` boolean property, that tells if object is
161 ready to be encoded. If that kind of action is performed on unready
162 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
164 All objects have ``copy()`` method, that returns their copy, that can be
172 Decoding is performed using ``decode()`` method. ``offset`` optional
173 argument could be used to set initial object's offset in the binary
174 data, for convenience. It returns decoded object and remaining
175 unmarshalled data (tail). Internally all work is done on
176 ``memoryview(data)``, and you can leave returning tail as a memoryview,
177 by specifying ``leavemm=True`` argument.
179 When object is decoded, ``decoded`` property is true and you can safely
180 use following properties:
182 * ``offset`` -- position including initial offset where object's tag starts
183 * ``tlen`` -- length of object's tag
184 * ``llen`` -- length of object's length value
185 * ``vlen`` -- length of object's value
186 * ``tlvlen`` -- length of the whole object
188 Pay attention that those values do **not** include anything related to
189 explicit tag. If you want to know information about it, then use:
191 * ``expled`` -- to know if explicit tag is set
192 * ``expl_offset`` (it is lesser than ``offset``)
195 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
196 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
198 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
201 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
208 You can specify so called context keyword argument during ``decode()``
209 invocation. It is dictionary containing various options governing
212 Currently available context options:
214 * :ref:`allow_default_values <allow_default_values_ctx>`
215 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
216 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
217 * :ref:`bered <bered_ctx>`
218 * :ref:`defines_by_path <defines_by_path_ctx>`
225 All objects have ``pps()`` method, that is a generator of
226 :py:class:`pyderasn.PP` namedtuple, holding various raw information
227 about the object. If ``pps`` is called on sequences, then all underlying
228 ``PP`` will be yielded.
230 You can use :py:func:`pyderasn.pp_console_row` function, converting
231 those ``PP`` to human readable string. Actually exactly it is used for
232 all object ``repr``. But it is easy to write custom formatters.
234 >>> from pyderasn import pprint
235 >>> encoded = Integer(-12345).encode()
236 >>> obj, tail = Integer().decode(encoded)
237 >>> print(pprint(obj))
238 0 [1,1, 2] INTEGER -12345
242 Example certificate::
244 >>> print(pprint(crt))
245 0 [1,3,1604] Certificate SEQUENCE
246 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE
247 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
248 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595
249 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
250 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
251 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
253 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
254 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF
255 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
256 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
257 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
258 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
259 . . . . . . . 13:02:45:53
261 1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
262 1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
263 1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
265 1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits
266 . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
267 . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
272 Let's parse that output, human::
274 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
275 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
276 0 1 2 3 4 5 6 7 8 9 10 11
280 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
286 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
292 52-2∞ B [1,1,1054]∞ . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
297 Offset of the object, where its DER/BER encoding begins.
298 Pay attention that it does **not** include explicit tag.
300 If explicit tag exists, then this is its length (tag + encoded length).
302 Length of object's tag. For example CHOICE does not have its own tag,
305 Length of encoded length.
307 Length of encoded value.
309 Visual indentation to show the depth of object in the hierarchy.
311 Object's name inside SEQUENCE/CHOICE.
313 If either IMPLICIT or EXPLICIT tag is set, then it will be shown
314 here. "IMPLICIT" is omitted.
316 Object's class name, if set. Omitted if it is just an ordinary simple
317 value (like with ``algorithm`` in example above).
321 Object's value, if set. Can consist of multiple words (like OCTET/BIT
322 STRINGs above). We see ``v3`` value in Version, because it is named.
323 ``rdnSequence`` is the choice of CHOICE type.
325 Possible other flags like OPTIONAL and DEFAULT, if value equals to the
326 default one, specified in the schema.
328 Shows does object contains any kind of BER encoded data (possibly
329 Sequence holding BER-encoded underlying value).
331 Only applicable to BER encoded data. Indefinite length encoding mark.
333 Only applicable to BER encoded data. If object has BER-specific
334 encoding, then ``BER`` will be shown. It does not depend on indefinite
335 length encoding. ``EOC``, ``BOOLEAN``, ``BIT STRING``, ``OCTET STRING``
336 (and its derivatives), ``SET``, ``SET OF`` could be BERed.
344 ASN.1 structures often have ANY and OCTET STRING fields, that are
345 DEFINED BY some previously met ObjectIdentifier. This library provides
346 ability to specify mapping between some OID and field that must be
347 decoded with specific specification.
354 :py:class:`pyderasn.ObjectIdentifier` field inside
355 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
356 necessary for decoding structures. For example, CMS (:rfc:`5652`)
359 class ContentInfo(Sequence):
361 ("contentType", ContentType(defines=((("content",), {
362 id_digestedData: DigestedData(),
363 id_signedData: SignedData(),
365 ("content", Any(expl=tag_ctxc(0))),
368 ``contentType`` field tells that it defines that ``content`` must be
369 decoded with ``SignedData`` specification, if ``contentType`` equals to
370 ``id-signedData``. The same applies to ``DigestedData``. If
371 ``contentType`` contains unknown OID, then no automatic decoding is
374 You can specify multiple fields, that will be autodecoded -- that is why
375 ``defines`` kwarg is a sequence. You can specify defined field
376 relatively or absolutely to current decode path. For example ``defines``
377 for AlgorithmIdentifier of X.509's
378 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
382 id_ecPublicKey: ECParameters(),
383 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
385 (("..", "subjectPublicKey"), {
386 id_rsaEncryption: RSAPublicKey(),
387 id_GostR3410_2001: OctetString(),
391 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
392 autodecode its parameters inside SPKI's algorithm and its public key
395 Following types can be automatically decoded (DEFINED BY):
397 * :py:class:`pyderasn.Any`
398 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
399 * :py:class:`pyderasn.OctetString`
400 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
401 ``Any``/``BitString``/``OctetString``-s
403 When any of those fields is automatically decoded, then ``.defined``
404 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
405 was defined, ``value`` contains corresponding decoded value. For example
406 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
408 .. _defines_by_path_ctx:
410 defines_by_path context option
411 ______________________________
413 Sometimes you either can not or do not want to explicitly set *defines*
414 in the scheme. You can dynamically apply those definitions when calling
415 ``.decode()`` method.
417 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
418 value must be sequence of following tuples::
420 (decode_path, defines)
422 where ``decode_path`` is a tuple holding so-called decode path to the
423 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
424 ``defines``, holding exactly the same value as accepted in its
425 :ref:`keyword argument <defines>`.
427 For example, again for CMS, you want to automatically decode
428 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
429 structures it may hold. Also, automatically decode ``controlSequence``
432 content_info, tail = ContentInfo().decode(data, ctx={"defines_by_path": (
435 ((("content",), {id_signedData: SignedData()}),),
440 DecodePathDefBy(id_signedData),
445 id_cct_PKIData: PKIData(),
446 id_cct_PKIResponse: PKIResponse(),
452 DecodePathDefBy(id_signedData),
455 DecodePathDefBy(id_cct_PKIResponse),
461 id_cmc_recipientNonce: RecipientNonce(),
462 id_cmc_senderNonce: SenderNonce(),
463 id_cmc_statusInfoV2: CMCStatusInfoV2(),
464 id_cmc_transactionId: TransactionId(),
469 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
470 First function is useful for path construction when some automatic
471 decoding is already done. ``any`` means literally any value it meet --
472 useful for SEQUENCE/SET OF-s.
479 By default PyDERASN accepts only DER encoded data. It always encodes to
480 DER. But you can optionally enable BER decoding with setting ``bered``
481 :ref:`context <ctx>` argument to True. Indefinite lengths and
482 constructed primitive types should be parsed successfully.
484 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
485 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
486 STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``
488 * If object has an indefinite length encoding, then its ``lenindef``
489 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
490 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
492 * If object has an indefinite length encoded explicit tag, then
493 ``expl_lenindef`` is set to True.
494 * If object has either any of BER-related encoding (explicit tag
495 indefinite length, object's indefinite length, BER-encoding) or any
496 underlying component has that kind of encoding, then ``bered``
497 attribute is set to True. For example SignedData CMS can have
498 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
499 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
501 EOC (end-of-contents) token's length is taken in advance in object's
504 .. _allow_expl_oob_ctx:
506 Allow explicit tag out-of-bound
507 -------------------------------
509 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
510 one value, more than one object. If you set ``allow_expl_oob`` context
511 option to True, then no error will be raised and that invalid encoding
512 will be silently further processed. But pay attention that offsets and
513 lengths will be invalid in that case.
517 This option should be used only for skipping some decode errors, just
518 to see the decoded structure somehow.
525 .. autoclass:: pyderasn.Boolean
530 .. autoclass:: pyderasn.Integer
535 .. autoclass:: pyderasn.BitString
540 .. autoclass:: pyderasn.OctetString
545 .. autoclass:: pyderasn.Null
550 .. autoclass:: pyderasn.ObjectIdentifier
555 .. autoclass:: pyderasn.Enumerated
559 .. autoclass:: pyderasn.CommonString
563 .. autoclass:: pyderasn.NumericString
567 .. autoclass:: pyderasn.PrintableString
571 .. autoclass:: pyderasn.UTCTime
572 :members: __init__, todatetime
576 .. autoclass:: pyderasn.GeneralizedTime
583 .. autoclass:: pyderasn.Choice
588 .. autoclass:: PrimitiveTypes
592 .. autoclass:: pyderasn.Any
600 .. autoclass:: pyderasn.Sequence
605 .. autoclass:: pyderasn.Set
610 .. autoclass:: pyderasn.SequenceOf
615 .. autoclass:: pyderasn.SetOf
621 .. autofunction:: pyderasn.abs_decode_path
622 .. autofunction:: pyderasn.colonize_hex
623 .. autofunction:: pyderasn.hexenc
624 .. autofunction:: pyderasn.hexdec
625 .. autofunction:: pyderasn.tag_encode
626 .. autofunction:: pyderasn.tag_decode
627 .. autofunction:: pyderasn.tag_ctxp
628 .. autofunction:: pyderasn.tag_ctxc
629 .. autoclass:: pyderasn.Obj
630 .. autoclass:: pyderasn.DecodeError
632 .. autoclass:: pyderasn.NotEnoughData
633 .. autoclass:: pyderasn.LenIndefForm
634 .. autoclass:: pyderasn.TagMismatch
635 .. autoclass:: pyderasn.InvalidLength
636 .. autoclass:: pyderasn.InvalidOID
637 .. autoclass:: pyderasn.ObjUnknown
638 .. autoclass:: pyderasn.ObjNotReady
639 .. autoclass:: pyderasn.InvalidValueType
640 .. autoclass:: pyderasn.BoundsError
643 from codecs import getdecoder
644 from codecs import getencoder
645 from collections import namedtuple
646 from collections import OrderedDict
647 from copy import copy
648 from datetime import datetime
649 from math import ceil
650 from os import environ
651 from string import ascii_letters
652 from string import digits
654 from six import add_metaclass
655 from six import binary_type
656 from six import byte2int
657 from six import indexbytes
658 from six import int2byte
659 from six import integer_types
660 from six import iterbytes
661 from six import iteritems
662 from six import itervalues
664 from six import string_types
665 from six import text_type
666 from six import unichr as six_unichr
667 from six.moves import xrange as six_xrange
671 from termcolor import colored
672 except ImportError: # pragma: no cover
673 def colored(what, *args, **kwargs):
718 "TagClassApplication",
722 "TagFormConstructed",
733 TagClassUniversal = 0
734 TagClassApplication = 1 << 6
735 TagClassContext = 1 << 7
736 TagClassPrivate = 1 << 6 | 1 << 7
738 TagFormConstructed = 1 << 5
741 TagClassApplication: "APPLICATION ",
742 TagClassPrivate: "PRIVATE ",
743 TagClassUniversal: "UNIV ",
747 LENINDEF = b"\x80" # length indefinite mark
748 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
751 ########################################################################
753 ########################################################################
755 class ASN1Error(ValueError):
759 class DecodeError(ASN1Error):
760 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
762 :param str msg: reason of decode failing
763 :param klass: optional exact DecodeError inherited class (like
764 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
765 :py:exc:`InvalidLength`)
766 :param decode_path: tuple of strings. It contains human
767 readable names of the fields through which
768 decoding process has passed
769 :param int offset: binary offset where failure happened
771 super(DecodeError, self).__init__()
774 self.decode_path = decode_path
780 "" if self.klass is None else self.klass.__name__,
782 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
783 if len(self.decode_path) > 0 else ""
785 ("(at %d)" % self.offset) if self.offset > 0 else "",
791 return "%s(%s)" % (self.__class__.__name__, self)
794 class NotEnoughData(DecodeError):
798 class LenIndefForm(DecodeError):
802 class TagMismatch(DecodeError):
806 class InvalidLength(DecodeError):
810 class InvalidOID(DecodeError):
814 class ObjUnknown(ASN1Error):
815 def __init__(self, name):
816 super(ObjUnknown, self).__init__()
820 return "object is unknown: %s" % self.name
823 return "%s(%s)" % (self.__class__.__name__, self)
826 class ObjNotReady(ASN1Error):
827 def __init__(self, name):
828 super(ObjNotReady, self).__init__()
832 return "object is not ready: %s" % self.name
835 return "%s(%s)" % (self.__class__.__name__, self)
838 class InvalidValueType(ASN1Error):
839 def __init__(self, expected_types):
840 super(InvalidValueType, self).__init__()
841 self.expected_types = expected_types
844 return "invalid value type, expected: %s" % ", ".join(
845 [repr(t) for t in self.expected_types]
849 return "%s(%s)" % (self.__class__.__name__, self)
852 class BoundsError(ASN1Error):
853 def __init__(self, bound_min, value, bound_max):
854 super(BoundsError, self).__init__()
855 self.bound_min = bound_min
857 self.bound_max = bound_max
860 return "unsatisfied bounds: %s <= %s <= %s" % (
867 return "%s(%s)" % (self.__class__.__name__, self)
870 ########################################################################
872 ########################################################################
874 _hexdecoder = getdecoder("hex")
875 _hexencoder = getencoder("hex")
879 """Binary data to hexadecimal string convert
881 return _hexdecoder(data)[0]
885 """Hexadecimal string to binary data convert
887 return _hexencoder(data)[0].decode("ascii")
890 def int_bytes_len(num, byte_len=8):
893 return int(ceil(float(num.bit_length()) / byte_len))
896 def zero_ended_encode(num):
897 octets = bytearray(int_bytes_len(num, 7))
899 octets[i] = num & 0x7F
903 octets[i] = 0x80 | (num & 0x7F)
909 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
910 """Encode tag to binary form
912 :param int num: tag's number
913 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
914 :py:data:`pyderasn.TagClassContext`,
915 :py:data:`pyderasn.TagClassApplication`,
916 :py:data:`pyderasn.TagClassPrivate`)
917 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
918 :py:data:`pyderasn.TagFormConstructed`)
922 return int2byte(klass | form | num)
923 # [XX|X|11111][1.......][1.......] ... [0.......]
924 return int2byte(klass | form | 31) + zero_ended_encode(num)
928 """Decode tag from binary form
932 No validation is performed, assuming that it has already passed.
934 It returns tuple with three integers, as
935 :py:func:`pyderasn.tag_encode` accepts.
937 first_octet = byte2int(tag)
938 klass = first_octet & 0xC0
939 form = first_octet & 0x20
940 if first_octet & 0x1F < 0x1F:
941 return (klass, form, first_octet & 0x1F)
943 for octet in iterbytes(tag[1:]):
946 return (klass, form, num)
950 """Create CONTEXT PRIMITIVE tag
952 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
956 """Create CONTEXT CONSTRUCTED tag
958 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
962 """Take off tag from the data
964 :returns: (encoded tag, tag length, remaining data)
967 raise NotEnoughData("no data at all")
968 if byte2int(data) & 0x1F < 31:
969 return data[:1], 1, data[1:]
974 raise DecodeError("unfinished tag")
975 if indexbytes(data, i) & 0x80 == 0:
978 return data[:i], i, data[i:]
984 octets = bytearray(int_bytes_len(l) + 1)
985 octets[0] = 0x80 | (len(octets) - 1)
986 for i in six_xrange(len(octets) - 1, 0, -1):
992 def len_decode(data):
995 :returns: (decoded length, length's length, remaining data)
996 :raises LenIndefForm: if indefinite form encoding is met
999 raise NotEnoughData("no data at all")
1000 first_octet = byte2int(data)
1001 if first_octet & 0x80 == 0:
1002 return first_octet, 1, data[1:]
1003 octets_num = first_octet & 0x7F
1004 if octets_num + 1 > len(data):
1005 raise NotEnoughData("encoded length is longer than data")
1007 raise LenIndefForm()
1008 if byte2int(data[1:]) == 0:
1009 raise DecodeError("leading zeros")
1011 for v in iterbytes(data[1:1 + octets_num]):
1014 raise DecodeError("long form instead of short one")
1015 return l, 1 + octets_num, data[1 + octets_num:]
1018 ########################################################################
1020 ########################################################################
1022 class AutoAddSlots(type):
1023 def __new__(cls, name, bases, _dict):
1024 _dict["__slots__"] = _dict.get("__slots__", ())
1025 return type.__new__(cls, name, bases, _dict)
1028 @add_metaclass(AutoAddSlots)
1030 """Common ASN.1 object class
1032 All ASN.1 types are inherited from it. It has metaclass that
1033 automatically adds ``__slots__`` to all inherited classes.
1057 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1058 self._expl = getattr(self, "expl", None) if expl is None else expl
1059 if self.tag != self.tag_default and self._expl is not None:
1060 raise ValueError("implicit and explicit tags can not be set simultaneously")
1061 if default is not None:
1063 self.optional = optional
1064 self.offset, self.llen, self.vlen = _decoded
1066 self.expl_lenindef = False
1067 self.lenindef = False
1068 self.ber_encoded = False
1071 def ready(self): # pragma: no cover
1072 """Is object ready to be encoded?
1074 raise NotImplementedError()
1076 def _assert_ready(self):
1078 raise ObjNotReady(self.__class__.__name__)
1082 """Is either object or any elements inside is BER encoded?
1084 return self.expl_lenindef or self.lenindef or self.ber_encoded
1088 """Is object decoded?
1090 return (self.llen + self.vlen) > 0
1092 def copy(self): # pragma: no cover
1093 """Make a copy of object, safe to be mutated
1095 raise NotImplementedError()
1099 return len(self.tag)
1103 return self.tlen + self.llen + self.vlen
1105 def __str__(self): # pragma: no cover
1106 return self.__bytes__() if PY2 else self.__unicode__()
1108 def __ne__(self, their):
1109 return not(self == their)
1111 def __gt__(self, their): # pragma: no cover
1112 return not(self < their)
1114 def __le__(self, their): # pragma: no cover
1115 return (self == their) or (self < their)
1117 def __ge__(self, their): # pragma: no cover
1118 return (self == their) or (self > their)
1120 def _encode(self): # pragma: no cover
1121 raise NotImplementedError()
1123 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1124 raise NotImplementedError()
1127 raw = self._encode()
1128 if self._expl is None:
1130 return b"".join((self._expl, len_encode(len(raw)), raw))
1140 _ctx_immutable=True,
1144 :param data: either binary or memoryview
1145 :param int offset: initial data's offset
1146 :param bool leavemm: do we need to leave memoryview of remaining
1147 data as is, or convert it to bytes otherwise
1148 :param ctx: optional :ref:`context <ctx>` governing decoding process
1149 :param tag_only: decode only the tag, without length and contents
1150 (used only in Choice and Set structures, trying to
1151 determine if tag satisfies the scheme)
1152 :param _ctx_immutable: do we need to copy ``ctx`` before using it
1153 :returns: (Obj, remaining data)
1157 elif _ctx_immutable:
1159 tlv = memoryview(data)
1160 if self._expl is None:
1161 result = self._decode(
1164 decode_path=decode_path,
1173 t, tlen, lv = tag_strip(tlv)
1174 except DecodeError as err:
1175 raise err.__class__(
1177 klass=self.__class__,
1178 decode_path=decode_path,
1183 klass=self.__class__,
1184 decode_path=decode_path,
1188 l, llen, v = len_decode(lv)
1189 except LenIndefForm as err:
1190 if not ctx.get("bered", False):
1191 raise err.__class__(
1193 klass=self.__class__,
1194 decode_path=decode_path,
1198 offset += tlen + llen
1199 result = self._decode(
1202 decode_path=decode_path,
1206 if tag_only: # pragma: no cover
1209 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1210 if eoc_expected.tobytes() != EOC:
1213 klass=self.__class__,
1214 decode_path=decode_path,
1218 obj.expl_lenindef = True
1219 except DecodeError as err:
1220 raise err.__class__(
1222 klass=self.__class__,
1223 decode_path=decode_path,
1228 raise NotEnoughData(
1229 "encoded length is longer than data",
1230 klass=self.__class__,
1231 decode_path=decode_path,
1234 result = self._decode(
1236 offset=offset + tlen + llen,
1237 decode_path=decode_path,
1241 if tag_only: # pragma: no cover
1244 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1246 "explicit tag out-of-bound, longer than data",
1247 klass=self.__class__,
1248 decode_path=decode_path,
1251 return obj, (tail if leavemm else tail.tobytes())
1255 return self._expl is not None
1262 def expl_tlen(self):
1263 return len(self._expl)
1266 def expl_llen(self):
1267 if self.expl_lenindef:
1269 return len(len_encode(self.tlvlen))
1272 def expl_offset(self):
1273 return self.offset - self.expl_tlen - self.expl_llen
1276 def expl_vlen(self):
1280 def expl_tlvlen(self):
1281 return self.expl_tlen + self.expl_llen + self.expl_vlen
1284 def fulloffset(self):
1285 return self.expl_offset if self.expled else self.offset
1289 return self.expl_tlvlen if self.expled else self.tlvlen
1291 def pps_lenindef(self, decode_path):
1292 if self.lenindef and not (
1293 getattr(self, "defined", None) is not None and
1294 self.defined[1].lenindef
1297 asn1_type_name="EOC",
1299 decode_path=decode_path,
1301 self.offset + self.tlvlen -
1302 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1310 if self.expl_lenindef:
1312 asn1_type_name="EOC",
1313 obj_name="EXPLICIT",
1314 decode_path=decode_path,
1315 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1324 class DecodePathDefBy(object):
1325 """DEFINED BY representation inside decode path
1327 __slots__ = ("defined_by",)
1329 def __init__(self, defined_by):
1330 self.defined_by = defined_by
1332 def __ne__(self, their):
1333 return not(self == their)
1335 def __eq__(self, their):
1336 if not isinstance(their, self.__class__):
1338 return self.defined_by == their.defined_by
1341 return "DEFINED BY " + str(self.defined_by)
1344 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1347 ########################################################################
1349 ########################################################################
1351 PP = namedtuple("PP", (
1379 asn1_type_name="unknown",
1396 expl_lenindef=False,
1427 def _colourize(what, colour, with_colours, attrs=("bold",)):
1428 return colored(what, colour, attrs=attrs) if with_colours else what
1431 def colonize_hex(hexed):
1432 """Separate hexadecimal string with colons
1434 return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
1443 with_decode_path=False,
1444 decode_path_len_decrease=0,
1451 " " if pp.expl_offset is None else
1452 ("-%d" % (pp.offset - pp.expl_offset))
1454 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1456 col = _colourize(col, "red", with_colours, ())
1457 col += _colourize("B", "red", with_colours) if pp.bered else " "
1459 col = "[%d,%d,%4d]%s" % (
1463 LENINDEF_PP_CHAR if pp.lenindef else " "
1465 col = _colourize(col, "green", with_colours, ())
1467 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1468 if decode_path_len > 0:
1469 cols.append(" ." * decode_path_len)
1470 ent = pp.decode_path[-1]
1471 if isinstance(ent, DecodePathDefBy):
1472 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1473 value = str(ent.defined_by)
1476 len(oid_maps) > 0 and
1477 ent.defined_by.asn1_type_name ==
1478 ObjectIdentifier.asn1_type_name
1480 for oid_map in oid_maps:
1481 oid_name = oid_map.get(value)
1482 if oid_name is not None:
1483 cols.append(_colourize("%s:" % oid_name, "green", with_colours))
1485 if oid_name is None:
1486 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1488 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1489 if pp.expl is not None:
1490 klass, _, num = pp.expl
1491 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1492 cols.append(_colourize(col, "blue", with_colours))
1493 if pp.impl is not None:
1494 klass, _, num = pp.impl
1495 col = "[%s%d]" % (TagClassReprs[klass], num)
1496 cols.append(_colourize(col, "blue", with_colours))
1497 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1498 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1500 cols.append(_colourize("BER", "red", with_colours))
1501 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1502 if pp.value is not None:
1504 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1506 len(oid_maps) > 0 and
1507 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
1509 for oid_map in oid_maps:
1510 oid_name = oid_map.get(value)
1511 if oid_name is not None:
1512 cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
1514 if pp.asn1_type_name == Integer.asn1_type_name:
1515 hex_repr = hex(int(pp.obj._value))[2:].upper()
1516 if len(hex_repr) % 2 != 0:
1517 hex_repr = "0" + hex_repr
1518 cols.append(_colourize(
1519 "(%s)" % colonize_hex(hex_repr),
1524 if isinstance(pp.blob, binary_type):
1525 cols.append(hexenc(pp.blob))
1526 elif isinstance(pp.blob, tuple):
1527 cols.append(", ".join(pp.blob))
1529 cols.append(_colourize("OPTIONAL", "red", with_colours))
1531 cols.append(_colourize("DEFAULT", "red", with_colours))
1532 if with_decode_path:
1533 cols.append(_colourize(
1534 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1538 return " ".join(cols)
1541 def pp_console_blob(pp, decode_path_len_decrease=0):
1542 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1543 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1544 if decode_path_len > 0:
1545 cols.append(" ." * (decode_path_len + 1))
1546 if isinstance(pp.blob, binary_type):
1547 blob = hexenc(pp.blob).upper()
1548 for i in six_xrange(0, len(blob), 32):
1549 chunk = blob[i:i + 32]
1550 yield " ".join(cols + [colonize_hex(chunk)])
1551 elif isinstance(pp.blob, tuple):
1552 yield " ".join(cols + [", ".join(pp.blob)])
1560 with_decode_path=False,
1561 decode_path_only=(),
1563 """Pretty print object
1565 :param Obj obj: object you want to pretty print
1566 :param oid_maps: list of ``OID <-> humand readable string`` dictionary.
1567 When OID from it is met, then its humand readable form
1569 :param big_blobs: if large binary objects are met (like OctetString
1570 values), do we need to print them too, on separate
1572 :param with_colours: colourize output, if ``termcolor`` library
1574 :param with_decode_path: print decode path
1575 :param decode_path_only: print only that specified decode path
1577 def _pprint_pps(pps):
1579 if hasattr(pp, "_fields"):
1581 decode_path_only != () and
1583 str(p) for p in pp.decode_path[:len(decode_path_only)]
1584 ) != decode_path_only
1588 yield pp_console_row(
1593 with_colours=with_colours,
1594 with_decode_path=with_decode_path,
1595 decode_path_len_decrease=len(decode_path_only),
1597 for row in pp_console_blob(
1599 decode_path_len_decrease=len(decode_path_only),
1603 yield pp_console_row(
1608 with_colours=with_colours,
1609 with_decode_path=with_decode_path,
1610 decode_path_len_decrease=len(decode_path_only),
1613 for row in _pprint_pps(pp):
1615 return "\n".join(_pprint_pps(obj.pps()))
1618 ########################################################################
1619 # ASN.1 primitive types
1620 ########################################################################
1623 """``BOOLEAN`` boolean type
1625 >>> b = Boolean(True)
1627 >>> b == Boolean(True)
1633 tag_default = tag_encode(1)
1634 asn1_type_name = "BOOLEAN"
1646 :param value: set the value. Either boolean type, or
1647 :py:class:`pyderasn.Boolean` object
1648 :param bytes impl: override default tag with ``IMPLICIT`` one
1649 :param bytes expl: override default tag with ``EXPLICIT`` one
1650 :param default: set default value. Type same as in ``value``
1651 :param bool optional: is object ``OPTIONAL`` in sequence
1653 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1654 self._value = None if value is None else self._value_sanitize(value)
1655 if default is not None:
1656 default = self._value_sanitize(default)
1657 self.default = self.__class__(
1663 self._value = default
1665 def _value_sanitize(self, value):
1666 if isinstance(value, bool):
1668 if issubclass(value.__class__, Boolean):
1670 raise InvalidValueType((self.__class__, bool))
1674 return self._value is not None
1677 obj = self.__class__()
1678 obj._value = self._value
1680 obj._expl = self._expl
1681 obj.default = self.default
1682 obj.optional = self.optional
1683 obj.offset = self.offset
1684 obj.llen = self.llen
1685 obj.vlen = self.vlen
1686 obj.expl_lenindef = self.expl_lenindef
1687 obj.lenindef = self.lenindef
1688 obj.ber_encoded = self.ber_encoded
1691 def __nonzero__(self):
1692 self._assert_ready()
1696 self._assert_ready()
1699 def __eq__(self, their):
1700 if isinstance(their, bool):
1701 return self._value == their
1702 if not issubclass(their.__class__, Boolean):
1705 self._value == their._value and
1706 self.tag == their.tag and
1707 self._expl == their._expl
1718 return self.__class__(
1720 impl=self.tag if impl is None else impl,
1721 expl=self._expl if expl is None else expl,
1722 default=self.default if default is None else default,
1723 optional=self.optional if optional is None else optional,
1727 self._assert_ready()
1731 (b"\xFF" if self._value else b"\x00"),
1734 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1736 t, _, lv = tag_strip(tlv)
1737 except DecodeError as err:
1738 raise err.__class__(
1740 klass=self.__class__,
1741 decode_path=decode_path,
1746 klass=self.__class__,
1747 decode_path=decode_path,
1753 l, _, v = len_decode(lv)
1754 except DecodeError as err:
1755 raise err.__class__(
1757 klass=self.__class__,
1758 decode_path=decode_path,
1762 raise InvalidLength(
1763 "Boolean's length must be equal to 1",
1764 klass=self.__class__,
1765 decode_path=decode_path,
1769 raise NotEnoughData(
1770 "encoded length is longer than data",
1771 klass=self.__class__,
1772 decode_path=decode_path,
1775 first_octet = byte2int(v)
1777 if first_octet == 0:
1779 elif first_octet == 0xFF:
1781 elif ctx.get("bered", False):
1786 "unacceptable Boolean value",
1787 klass=self.__class__,
1788 decode_path=decode_path,
1791 obj = self.__class__(
1795 default=self.default,
1796 optional=self.optional,
1797 _decoded=(offset, 1, 1),
1799 obj.ber_encoded = ber_encoded
1803 return pp_console_row(next(self.pps()))
1805 def pps(self, decode_path=()):
1808 asn1_type_name=self.asn1_type_name,
1809 obj_name=self.__class__.__name__,
1810 decode_path=decode_path,
1811 value=str(self._value) if self.ready else None,
1812 optional=self.optional,
1813 default=self == self.default,
1814 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1815 expl=None if self._expl is None else tag_decode(self._expl),
1820 expl_offset=self.expl_offset if self.expled else None,
1821 expl_tlen=self.expl_tlen if self.expled else None,
1822 expl_llen=self.expl_llen if self.expled else None,
1823 expl_vlen=self.expl_vlen if self.expled else None,
1824 expl_lenindef=self.expl_lenindef,
1825 ber_encoded=self.ber_encoded,
1828 for pp in self.pps_lenindef(decode_path):
1833 """``INTEGER`` integer type
1835 >>> b = Integer(-123)
1837 >>> b == Integer(-123)
1842 >>> Integer(2, bounds=(1, 3))
1844 >>> Integer(5, bounds=(1, 3))
1845 Traceback (most recent call last):
1846 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1850 class Version(Integer):
1857 >>> v = Version("v1")
1864 {'v3': 2, 'v1': 0, 'v2': 1}
1866 __slots__ = ("specs", "_bound_min", "_bound_max")
1867 tag_default = tag_encode(2)
1868 asn1_type_name = "INTEGER"
1882 :param value: set the value. Either integer type, named value
1883 (if ``schema`` is specified in the class), or
1884 :py:class:`pyderasn.Integer` object
1885 :param bounds: set ``(MIN, MAX)`` value constraint.
1886 (-inf, +inf) by default
1887 :param bytes impl: override default tag with ``IMPLICIT`` one
1888 :param bytes expl: override default tag with ``EXPLICIT`` one
1889 :param default: set default value. Type same as in ``value``
1890 :param bool optional: is object ``OPTIONAL`` in sequence
1892 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1894 specs = getattr(self, "schema", {}) if _specs is None else _specs
1895 self.specs = specs if isinstance(specs, dict) else dict(specs)
1896 self._bound_min, self._bound_max = getattr(
1899 (float("-inf"), float("+inf")),
1900 ) if bounds is None else bounds
1901 if value is not None:
1902 self._value = self._value_sanitize(value)
1903 if default is not None:
1904 default = self._value_sanitize(default)
1905 self.default = self.__class__(
1911 if self._value is None:
1912 self._value = default
1914 def _value_sanitize(self, value):
1915 if isinstance(value, integer_types):
1917 elif issubclass(value.__class__, Integer):
1918 value = value._value
1919 elif isinstance(value, str):
1920 value = self.specs.get(value)
1922 raise ObjUnknown("integer value: %s" % value)
1924 raise InvalidValueType((self.__class__, int, str))
1925 if not self._bound_min <= value <= self._bound_max:
1926 raise BoundsError(self._bound_min, value, self._bound_max)
1931 return self._value is not None
1934 obj = self.__class__(_specs=self.specs)
1935 obj._value = self._value
1936 obj._bound_min = self._bound_min
1937 obj._bound_max = self._bound_max
1939 obj._expl = self._expl
1940 obj.default = self.default
1941 obj.optional = self.optional
1942 obj.offset = self.offset
1943 obj.llen = self.llen
1944 obj.vlen = self.vlen
1945 obj.expl_lenindef = self.expl_lenindef
1946 obj.lenindef = self.lenindef
1947 obj.ber_encoded = self.ber_encoded
1951 self._assert_ready()
1952 return int(self._value)
1955 self._assert_ready()
1958 bytes(self._expl or b"") +
1959 str(self._value).encode("ascii"),
1962 def __eq__(self, their):
1963 if isinstance(their, integer_types):
1964 return self._value == their
1965 if not issubclass(their.__class__, Integer):
1968 self._value == their._value and
1969 self.tag == their.tag and
1970 self._expl == their._expl
1973 def __lt__(self, their):
1974 return self._value < their._value
1978 for name, value in iteritems(self.specs):
1979 if value == self._value:
1992 return self.__class__(
1995 (self._bound_min, self._bound_max)
1996 if bounds is None else bounds
1998 impl=self.tag if impl is None else impl,
1999 expl=self._expl if expl is None else expl,
2000 default=self.default if default is None else default,
2001 optional=self.optional if optional is None else optional,
2006 self._assert_ready()
2010 octets = bytearray([0])
2014 octets = bytearray()
2016 octets.append((value & 0xFF) ^ 0xFF)
2018 if len(octets) == 0 or octets[-1] & 0x80 == 0:
2021 octets = bytearray()
2023 octets.append(value & 0xFF)
2025 if octets[-1] & 0x80 > 0:
2028 octets = bytes(octets)
2030 bytes_len = ceil(value.bit_length() / 8) or 1
2033 octets = value.to_bytes(
2038 except OverflowError:
2042 return b"".join((self.tag, len_encode(len(octets)), octets))
2044 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2046 t, _, lv = tag_strip(tlv)
2047 except DecodeError as err:
2048 raise err.__class__(
2050 klass=self.__class__,
2051 decode_path=decode_path,
2056 klass=self.__class__,
2057 decode_path=decode_path,
2063 l, llen, v = len_decode(lv)
2064 except DecodeError as err:
2065 raise err.__class__(
2067 klass=self.__class__,
2068 decode_path=decode_path,
2072 raise NotEnoughData(
2073 "encoded length is longer than data",
2074 klass=self.__class__,
2075 decode_path=decode_path,
2079 raise NotEnoughData(
2081 klass=self.__class__,
2082 decode_path=decode_path,
2085 v, tail = v[:l], v[l:]
2086 first_octet = byte2int(v)
2088 second_octet = byte2int(v[1:])
2090 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2091 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2094 "non normalized integer",
2095 klass=self.__class__,
2096 decode_path=decode_path,
2101 if first_octet & 0x80 > 0:
2102 octets = bytearray()
2103 for octet in bytearray(v):
2104 octets.append(octet ^ 0xFF)
2105 for octet in octets:
2106 value = (value << 8) | octet
2110 for octet in bytearray(v):
2111 value = (value << 8) | octet
2113 value = int.from_bytes(v, byteorder="big", signed=True)
2115 obj = self.__class__(
2117 bounds=(self._bound_min, self._bound_max),
2120 default=self.default,
2121 optional=self.optional,
2123 _decoded=(offset, llen, l),
2125 except BoundsError as err:
2128 klass=self.__class__,
2129 decode_path=decode_path,
2135 return pp_console_row(next(self.pps()))
2137 def pps(self, decode_path=()):
2140 asn1_type_name=self.asn1_type_name,
2141 obj_name=self.__class__.__name__,
2142 decode_path=decode_path,
2143 value=(self.named or str(self._value)) if self.ready else None,
2144 optional=self.optional,
2145 default=self == self.default,
2146 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2147 expl=None if self._expl is None else tag_decode(self._expl),
2152 expl_offset=self.expl_offset if self.expled else None,
2153 expl_tlen=self.expl_tlen if self.expled else None,
2154 expl_llen=self.expl_llen if self.expled else None,
2155 expl_vlen=self.expl_vlen if self.expled else None,
2156 expl_lenindef=self.expl_lenindef,
2159 for pp in self.pps_lenindef(decode_path):
2163 SET01 = frozenset(("0", "1"))
2166 class BitString(Obj):
2167 """``BIT STRING`` bit string type
2169 >>> BitString(b"hello world")
2170 BIT STRING 88 bits 68656c6c6f20776f726c64
2173 >>> b == b"hello world"
2178 >>> BitString("'0A3B5F291CD'H")
2179 BIT STRING 44 bits 0a3b5f291cd0
2180 >>> b = BitString("'010110000000'B")
2181 BIT STRING 12 bits 5800
2184 >>> b[0], b[1], b[2], b[3]
2185 (False, True, False, True)
2189 [False, True, False, True, True, False, False, False, False, False, False, False]
2193 class KeyUsage(BitString):
2195 ("digitalSignature", 0),
2196 ("nonRepudiation", 1),
2197 ("keyEncipherment", 2),
2200 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2201 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2203 ['nonRepudiation', 'keyEncipherment']
2205 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2209 Pay attention that BIT STRING can be encoded both in primitive
2210 and constructed forms. Decoder always checks constructed form tag
2211 additionally to specified primitive one. If BER decoding is
2212 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2213 of DER restrictions.
2215 __slots__ = ("tag_constructed", "specs", "defined")
2216 tag_default = tag_encode(3)
2217 asn1_type_name = "BIT STRING"
2230 :param value: set the value. Either binary type, tuple of named
2231 values (if ``schema`` is specified in the class),
2232 string in ``'XXX...'B`` form, or
2233 :py:class:`pyderasn.BitString` object
2234 :param bytes impl: override default tag with ``IMPLICIT`` one
2235 :param bytes expl: override default tag with ``EXPLICIT`` one
2236 :param default: set default value. Type same as in ``value``
2237 :param bool optional: is object ``OPTIONAL`` in sequence
2239 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2240 specs = getattr(self, "schema", {}) if _specs is None else _specs
2241 self.specs = specs if isinstance(specs, dict) else dict(specs)
2242 self._value = None if value is None else self._value_sanitize(value)
2243 if default is not None:
2244 default = self._value_sanitize(default)
2245 self.default = self.__class__(
2251 self._value = default
2253 tag_klass, _, tag_num = tag_decode(self.tag)
2254 self.tag_constructed = tag_encode(
2256 form=TagFormConstructed,
2260 def _bits2octets(self, bits):
2261 if len(self.specs) > 0:
2262 bits = bits.rstrip("0")
2264 bits += "0" * ((8 - (bit_len % 8)) % 8)
2265 octets = bytearray(len(bits) // 8)
2266 for i in six_xrange(len(octets)):
2267 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2268 return bit_len, bytes(octets)
2270 def _value_sanitize(self, value):
2271 if isinstance(value, (string_types, binary_type)):
2273 isinstance(value, string_types) and
2274 value.startswith("'")
2276 if value.endswith("'B"):
2278 if not frozenset(value) <= SET01:
2279 raise ValueError("B's coding contains unacceptable chars")
2280 return self._bits2octets(value)
2281 elif value.endswith("'H"):
2285 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2287 if isinstance(value, binary_type):
2288 return (len(value) * 8, value)
2290 raise InvalidValueType((self.__class__, string_types, binary_type))
2291 if isinstance(value, tuple):
2294 isinstance(value[0], integer_types) and
2295 isinstance(value[1], binary_type)
2300 bit = self.specs.get(name)
2302 raise ObjUnknown("BitString value: %s" % name)
2305 return self._bits2octets("")
2306 bits = frozenset(bits)
2307 return self._bits2octets("".join(
2308 ("1" if bit in bits else "0")
2309 for bit in six_xrange(max(bits) + 1)
2311 if issubclass(value.__class__, BitString):
2313 raise InvalidValueType((self.__class__, binary_type, string_types))
2317 return self._value is not None
2320 obj = self.__class__(_specs=self.specs)
2322 if value is not None:
2323 value = (value[0], value[1])
2326 obj._expl = self._expl
2327 obj.default = self.default
2328 obj.optional = self.optional
2329 obj.offset = self.offset
2330 obj.llen = self.llen
2331 obj.vlen = self.vlen
2332 obj.expl_lenindef = self.expl_lenindef
2333 obj.lenindef = self.lenindef
2334 obj.ber_encoded = self.ber_encoded
2338 self._assert_ready()
2339 for i in six_xrange(self._value[0]):
2344 self._assert_ready()
2345 return self._value[0]
2347 def __bytes__(self):
2348 self._assert_ready()
2349 return self._value[1]
2351 def __eq__(self, their):
2352 if isinstance(their, bytes):
2353 return self._value[1] == their
2354 if not issubclass(their.__class__, BitString):
2357 self._value == their._value and
2358 self.tag == their.tag and
2359 self._expl == their._expl
2364 return [name for name, bit in iteritems(self.specs) if self[bit]]
2374 return self.__class__(
2376 impl=self.tag if impl is None else impl,
2377 expl=self._expl if expl is None else expl,
2378 default=self.default if default is None else default,
2379 optional=self.optional if optional is None else optional,
2383 def __getitem__(self, key):
2384 if isinstance(key, int):
2385 bit_len, octets = self._value
2389 byte2int(memoryview(octets)[key // 8:]) >>
2392 if isinstance(key, string_types):
2393 value = self.specs.get(key)
2395 raise ObjUnknown("BitString value: %s" % key)
2397 raise InvalidValueType((int, str))
2400 self._assert_ready()
2401 bit_len, octets = self._value
2404 len_encode(len(octets) + 1),
2405 int2byte((8 - bit_len % 8) % 8),
2409 def _decode_chunk(self, lv, offset, decode_path, ctx):
2411 l, llen, v = len_decode(lv)
2412 except DecodeError as err:
2413 raise err.__class__(
2415 klass=self.__class__,
2416 decode_path=decode_path,
2420 raise NotEnoughData(
2421 "encoded length is longer than data",
2422 klass=self.__class__,
2423 decode_path=decode_path,
2427 raise NotEnoughData(
2429 klass=self.__class__,
2430 decode_path=decode_path,
2433 pad_size = byte2int(v)
2434 if l == 1 and pad_size != 0:
2436 "invalid empty value",
2437 klass=self.__class__,
2438 decode_path=decode_path,
2444 klass=self.__class__,
2445 decode_path=decode_path,
2448 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2451 klass=self.__class__,
2452 decode_path=decode_path,
2455 v, tail = v[:l], v[l:]
2456 obj = self.__class__(
2457 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2460 default=self.default,
2461 optional=self.optional,
2463 _decoded=(offset, llen, l),
2467 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2469 t, tlen, lv = tag_strip(tlv)
2470 except DecodeError as err:
2471 raise err.__class__(
2473 klass=self.__class__,
2474 decode_path=decode_path,
2478 if tag_only: # pragma: no cover
2480 return self._decode_chunk(lv, offset, decode_path, ctx)
2481 if t == self.tag_constructed:
2482 if not ctx.get("bered", False):
2484 "unallowed BER constructed encoding",
2485 klass=self.__class__,
2486 decode_path=decode_path,
2489 if tag_only: # pragma: no cover
2493 l, llen, v = len_decode(lv)
2494 except LenIndefForm:
2495 llen, l, v = 1, 0, lv[1:]
2497 except DecodeError as err:
2498 raise err.__class__(
2500 klass=self.__class__,
2501 decode_path=decode_path,
2505 raise NotEnoughData(
2506 "encoded length is longer than data",
2507 klass=self.__class__,
2508 decode_path=decode_path,
2511 if not lenindef and l == 0:
2512 raise NotEnoughData(
2514 klass=self.__class__,
2515 decode_path=decode_path,
2519 sub_offset = offset + tlen + llen
2523 if v[:EOC_LEN].tobytes() == EOC:
2530 "chunk out of bounds",
2531 klass=self.__class__,
2532 decode_path=decode_path + (str(len(chunks) - 1),),
2533 offset=chunks[-1].offset,
2535 sub_decode_path = decode_path + (str(len(chunks)),)
2537 chunk, v_tail = BitString().decode(
2540 decode_path=sub_decode_path,
2543 _ctx_immutable=False,
2547 "expected BitString encoded chunk",
2548 klass=self.__class__,
2549 decode_path=sub_decode_path,
2552 chunks.append(chunk)
2553 sub_offset += chunk.tlvlen
2554 vlen += chunk.tlvlen
2556 if len(chunks) == 0:
2559 klass=self.__class__,
2560 decode_path=decode_path,
2565 for chunk_i, chunk in enumerate(chunks[:-1]):
2566 if chunk.bit_len % 8 != 0:
2568 "BitString chunk is not multiple of 8 bits",
2569 klass=self.__class__,
2570 decode_path=decode_path + (str(chunk_i),),
2571 offset=chunk.offset,
2573 values.append(bytes(chunk))
2574 bit_len += chunk.bit_len
2575 chunk_last = chunks[-1]
2576 values.append(bytes(chunk_last))
2577 bit_len += chunk_last.bit_len
2578 obj = self.__class__(
2579 value=(bit_len, b"".join(values)),
2582 default=self.default,
2583 optional=self.optional,
2585 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2587 obj.lenindef = lenindef
2588 obj.ber_encoded = True
2589 return obj, (v[EOC_LEN:] if lenindef else v)
2591 klass=self.__class__,
2592 decode_path=decode_path,
2597 return pp_console_row(next(self.pps()))
2599 def pps(self, decode_path=()):
2603 bit_len, blob = self._value
2604 value = "%d bits" % bit_len
2605 if len(self.specs) > 0:
2606 blob = tuple(self.named)
2609 asn1_type_name=self.asn1_type_name,
2610 obj_name=self.__class__.__name__,
2611 decode_path=decode_path,
2614 optional=self.optional,
2615 default=self == self.default,
2616 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2617 expl=None if self._expl is None else tag_decode(self._expl),
2622 expl_offset=self.expl_offset if self.expled else None,
2623 expl_tlen=self.expl_tlen if self.expled else None,
2624 expl_llen=self.expl_llen if self.expled else None,
2625 expl_vlen=self.expl_vlen if self.expled else None,
2626 expl_lenindef=self.expl_lenindef,
2627 lenindef=self.lenindef,
2628 ber_encoded=self.ber_encoded,
2631 defined_by, defined = self.defined or (None, None)
2632 if defined_by is not None:
2634 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2636 for pp in self.pps_lenindef(decode_path):
2640 class OctetString(Obj):
2641 """``OCTET STRING`` binary string type
2643 >>> s = OctetString(b"hello world")
2644 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2645 >>> s == OctetString(b"hello world")
2650 >>> OctetString(b"hello", bounds=(4, 4))
2651 Traceback (most recent call last):
2652 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2653 >>> OctetString(b"hell", bounds=(4, 4))
2654 OCTET STRING 4 bytes 68656c6c
2658 Pay attention that OCTET STRING can be encoded both in primitive
2659 and constructed forms. Decoder always checks constructed form tag
2660 additionally to specified primitive one. If BER decoding is
2661 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2662 of DER restrictions.
2664 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2665 tag_default = tag_encode(4)
2666 asn1_type_name = "OCTET STRING"
2679 :param value: set the value. Either binary type, or
2680 :py:class:`pyderasn.OctetString` object
2681 :param bounds: set ``(MIN, MAX)`` value size constraint.
2682 (-inf, +inf) by default
2683 :param bytes impl: override default tag with ``IMPLICIT`` one
2684 :param bytes expl: override default tag with ``EXPLICIT`` one
2685 :param default: set default value. Type same as in ``value``
2686 :param bool optional: is object ``OPTIONAL`` in sequence
2688 super(OctetString, self).__init__(
2696 self._bound_min, self._bound_max = getattr(
2700 ) if bounds is None else bounds
2701 if value is not None:
2702 self._value = self._value_sanitize(value)
2703 if default is not None:
2704 default = self._value_sanitize(default)
2705 self.default = self.__class__(
2710 if self._value is None:
2711 self._value = default
2713 tag_klass, _, tag_num = tag_decode(self.tag)
2714 self.tag_constructed = tag_encode(
2716 form=TagFormConstructed,
2720 def _value_sanitize(self, value):
2721 if isinstance(value, binary_type):
2723 elif issubclass(value.__class__, OctetString):
2724 value = value._value
2726 raise InvalidValueType((self.__class__, bytes))
2727 if not self._bound_min <= len(value) <= self._bound_max:
2728 raise BoundsError(self._bound_min, len(value), self._bound_max)
2733 return self._value is not None
2736 obj = self.__class__()
2737 obj._value = self._value
2738 obj._bound_min = self._bound_min
2739 obj._bound_max = self._bound_max
2741 obj._expl = self._expl
2742 obj.default = self.default
2743 obj.optional = self.optional
2744 obj.offset = self.offset
2745 obj.llen = self.llen
2746 obj.vlen = self.vlen
2747 obj.expl_lenindef = self.expl_lenindef
2748 obj.lenindef = self.lenindef
2749 obj.ber_encoded = self.ber_encoded
2752 def __bytes__(self):
2753 self._assert_ready()
2756 def __eq__(self, their):
2757 if isinstance(their, binary_type):
2758 return self._value == their
2759 if not issubclass(their.__class__, OctetString):
2762 self._value == their._value and
2763 self.tag == their.tag and
2764 self._expl == their._expl
2767 def __lt__(self, their):
2768 return self._value < their._value
2779 return self.__class__(
2782 (self._bound_min, self._bound_max)
2783 if bounds is None else bounds
2785 impl=self.tag if impl is None else impl,
2786 expl=self._expl if expl is None else expl,
2787 default=self.default if default is None else default,
2788 optional=self.optional if optional is None else optional,
2792 self._assert_ready()
2795 len_encode(len(self._value)),
2799 def _decode_chunk(self, lv, offset, decode_path, ctx):
2801 l, llen, v = len_decode(lv)
2802 except DecodeError as err:
2803 raise err.__class__(
2805 klass=self.__class__,
2806 decode_path=decode_path,
2810 raise NotEnoughData(
2811 "encoded length is longer than data",
2812 klass=self.__class__,
2813 decode_path=decode_path,
2816 v, tail = v[:l], v[l:]
2818 obj = self.__class__(
2820 bounds=(self._bound_min, self._bound_max),
2823 default=self.default,
2824 optional=self.optional,
2825 _decoded=(offset, llen, l),
2827 except DecodeError as err:
2830 klass=self.__class__,
2831 decode_path=decode_path,
2834 except BoundsError as err:
2837 klass=self.__class__,
2838 decode_path=decode_path,
2843 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2845 t, tlen, lv = tag_strip(tlv)
2846 except DecodeError as err:
2847 raise err.__class__(
2849 klass=self.__class__,
2850 decode_path=decode_path,
2856 return self._decode_chunk(lv, offset, decode_path, ctx)
2857 if t == self.tag_constructed:
2858 if not ctx.get("bered", False):
2860 "unallowed BER constructed encoding",
2861 klass=self.__class__,
2862 decode_path=decode_path,
2869 l, llen, v = len_decode(lv)
2870 except LenIndefForm:
2871 llen, l, v = 1, 0, lv[1:]
2873 except DecodeError as err:
2874 raise err.__class__(
2876 klass=self.__class__,
2877 decode_path=decode_path,
2881 raise NotEnoughData(
2882 "encoded length is longer than data",
2883 klass=self.__class__,
2884 decode_path=decode_path,
2888 sub_offset = offset + tlen + llen
2892 if v[:EOC_LEN].tobytes() == EOC:
2899 "chunk out of bounds",
2900 klass=self.__class__,
2901 decode_path=decode_path + (str(len(chunks) - 1),),
2902 offset=chunks[-1].offset,
2904 sub_decode_path = decode_path + (str(len(chunks)),)
2906 chunk, v_tail = OctetString().decode(
2909 decode_path=sub_decode_path,
2912 _ctx_immutable=False,
2916 "expected OctetString encoded chunk",
2917 klass=self.__class__,
2918 decode_path=sub_decode_path,
2921 chunks.append(chunk)
2922 sub_offset += chunk.tlvlen
2923 vlen += chunk.tlvlen
2926 obj = self.__class__(
2927 value=b"".join(bytes(chunk) for chunk in chunks),
2928 bounds=(self._bound_min, self._bound_max),
2931 default=self.default,
2932 optional=self.optional,
2933 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2935 except DecodeError as err:
2938 klass=self.__class__,
2939 decode_path=decode_path,
2942 except BoundsError as err:
2945 klass=self.__class__,
2946 decode_path=decode_path,
2949 obj.lenindef = lenindef
2950 obj.ber_encoded = True
2951 return obj, (v[EOC_LEN:] if lenindef else v)
2953 klass=self.__class__,
2954 decode_path=decode_path,
2959 return pp_console_row(next(self.pps()))
2961 def pps(self, decode_path=()):
2964 asn1_type_name=self.asn1_type_name,
2965 obj_name=self.__class__.__name__,
2966 decode_path=decode_path,
2967 value=("%d bytes" % len(self._value)) if self.ready else None,
2968 blob=self._value if self.ready else None,
2969 optional=self.optional,
2970 default=self == self.default,
2971 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2972 expl=None if self._expl is None else tag_decode(self._expl),
2977 expl_offset=self.expl_offset if self.expled else None,
2978 expl_tlen=self.expl_tlen if self.expled else None,
2979 expl_llen=self.expl_llen if self.expled else None,
2980 expl_vlen=self.expl_vlen if self.expled else None,
2981 expl_lenindef=self.expl_lenindef,
2982 lenindef=self.lenindef,
2983 ber_encoded=self.ber_encoded,
2986 defined_by, defined = self.defined or (None, None)
2987 if defined_by is not None:
2989 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2991 for pp in self.pps_lenindef(decode_path):
2996 """``NULL`` null object
3004 tag_default = tag_encode(5)
3005 asn1_type_name = "NULL"
3009 value=None, # unused, but Sequence passes it
3016 :param bytes impl: override default tag with ``IMPLICIT`` one
3017 :param bytes expl: override default tag with ``EXPLICIT`` one
3018 :param bool optional: is object ``OPTIONAL`` in sequence
3020 super(Null, self).__init__(impl, expl, None, optional, _decoded)
3028 obj = self.__class__()
3030 obj._expl = self._expl
3031 obj.default = self.default
3032 obj.optional = self.optional
3033 obj.offset = self.offset
3034 obj.llen = self.llen
3035 obj.vlen = self.vlen
3036 obj.expl_lenindef = self.expl_lenindef
3037 obj.lenindef = self.lenindef
3038 obj.ber_encoded = self.ber_encoded
3041 def __eq__(self, their):
3042 if not issubclass(their.__class__, Null):
3045 self.tag == their.tag and
3046 self._expl == their._expl
3056 return self.__class__(
3057 impl=self.tag if impl is None else impl,
3058 expl=self._expl if expl is None else expl,
3059 optional=self.optional if optional is None else optional,
3063 return self.tag + len_encode(0)
3065 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3067 t, _, lv = tag_strip(tlv)
3068 except DecodeError as err:
3069 raise err.__class__(
3071 klass=self.__class__,
3072 decode_path=decode_path,
3077 klass=self.__class__,
3078 decode_path=decode_path,
3081 if tag_only: # pragma: no cover
3084 l, _, v = len_decode(lv)
3085 except DecodeError as err:
3086 raise err.__class__(
3088 klass=self.__class__,
3089 decode_path=decode_path,
3093 raise InvalidLength(
3094 "Null must have zero length",
3095 klass=self.__class__,
3096 decode_path=decode_path,
3099 obj = self.__class__(
3102 optional=self.optional,
3103 _decoded=(offset, 1, 0),
3108 return pp_console_row(next(self.pps()))
3110 def pps(self, decode_path=()):
3113 asn1_type_name=self.asn1_type_name,
3114 obj_name=self.__class__.__name__,
3115 decode_path=decode_path,
3116 optional=self.optional,
3117 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3118 expl=None if self._expl is None else tag_decode(self._expl),
3123 expl_offset=self.expl_offset if self.expled else None,
3124 expl_tlen=self.expl_tlen if self.expled else None,
3125 expl_llen=self.expl_llen if self.expled else None,
3126 expl_vlen=self.expl_vlen if self.expled else None,
3127 expl_lenindef=self.expl_lenindef,
3130 for pp in self.pps_lenindef(decode_path):
3134 class ObjectIdentifier(Obj):
3135 """``OBJECT IDENTIFIER`` OID type
3137 >>> oid = ObjectIdentifier((1, 2, 3))
3138 OBJECT IDENTIFIER 1.2.3
3139 >>> oid == ObjectIdentifier("1.2.3")
3145 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3146 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3148 >>> str(ObjectIdentifier((3, 1)))
3149 Traceback (most recent call last):
3150 pyderasn.InvalidOID: unacceptable first arc value
3152 __slots__ = ("defines",)
3153 tag_default = tag_encode(6)
3154 asn1_type_name = "OBJECT IDENTIFIER"
3167 :param value: set the value. Either tuples of integers,
3168 string of "."-concatenated integers, or
3169 :py:class:`pyderasn.ObjectIdentifier` object
3170 :param defines: sequence of tuples. Each tuple has two elements.
3171 First one is relative to current one decode
3172 path, aiming to the field defined by that OID.
3173 Read about relative path in
3174 :py:func:`pyderasn.abs_decode_path`. Second
3175 tuple element is ``{OID: pyderasn.Obj()}``
3176 dictionary, mapping between current OID value
3177 and structure applied to defined field.
3178 :ref:`Read about DEFINED BY <definedby>`
3179 :param bytes impl: override default tag with ``IMPLICIT`` one
3180 :param bytes expl: override default tag with ``EXPLICIT`` one
3181 :param default: set default value. Type same as in ``value``
3182 :param bool optional: is object ``OPTIONAL`` in sequence
3184 super(ObjectIdentifier, self).__init__(
3192 if value is not None:
3193 self._value = self._value_sanitize(value)
3194 if default is not None:
3195 default = self._value_sanitize(default)
3196 self.default = self.__class__(
3201 if self._value is None:
3202 self._value = default
3203 self.defines = defines
3205 def __add__(self, their):
3206 if isinstance(their, self.__class__):
3207 return self.__class__(self._value + their._value)
3208 if isinstance(their, tuple):
3209 return self.__class__(self._value + their)
3210 raise InvalidValueType((self.__class__, tuple))
3212 def _value_sanitize(self, value):
3213 if issubclass(value.__class__, ObjectIdentifier):
3215 if isinstance(value, string_types):
3217 value = tuple(int(arc) for arc in value.split("."))
3219 raise InvalidOID("unacceptable arcs values")
3220 if isinstance(value, tuple):
3222 raise InvalidOID("less than 2 arcs")
3223 first_arc = value[0]
3224 if first_arc in (0, 1):
3225 if not (0 <= value[1] <= 39):
3226 raise InvalidOID("second arc is too wide")
3227 elif first_arc == 2:
3230 raise InvalidOID("unacceptable first arc value")
3232 raise InvalidValueType((self.__class__, str, tuple))
3236 return self._value is not None
3239 obj = self.__class__()
3240 obj._value = self._value
3241 obj.defines = self.defines
3243 obj._expl = self._expl
3244 obj.default = self.default
3245 obj.optional = self.optional
3246 obj.offset = self.offset
3247 obj.llen = self.llen
3248 obj.vlen = self.vlen
3249 obj.expl_lenindef = self.expl_lenindef
3250 obj.lenindef = self.lenindef
3251 obj.ber_encoded = self.ber_encoded
3255 self._assert_ready()
3256 return iter(self._value)
3259 return ".".join(str(arc) for arc in self._value or ())
3262 self._assert_ready()
3265 bytes(self._expl or b"") +
3266 str(self._value).encode("ascii"),
3269 def __eq__(self, their):
3270 if isinstance(their, tuple):
3271 return self._value == their
3272 if not issubclass(their.__class__, ObjectIdentifier):
3275 self.tag == their.tag and
3276 self._expl == their._expl and
3277 self._value == their._value
3280 def __lt__(self, their):
3281 return self._value < their._value
3292 return self.__class__(
3294 defines=self.defines if defines is None else defines,
3295 impl=self.tag if impl is None else impl,
3296 expl=self._expl if expl is None else expl,
3297 default=self.default if default is None else default,
3298 optional=self.optional if optional is None else optional,
3302 self._assert_ready()
3304 first_value = value[1]
3305 first_arc = value[0]
3308 elif first_arc == 1:
3310 elif first_arc == 2:
3312 else: # pragma: no cover
3313 raise RuntimeError("invalid arc is stored")
3314 octets = [zero_ended_encode(first_value)]
3315 for arc in value[2:]:
3316 octets.append(zero_ended_encode(arc))
3317 v = b"".join(octets)
3318 return b"".join((self.tag, len_encode(len(v)), v))
3320 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3322 t, _, lv = tag_strip(tlv)
3323 except DecodeError as err:
3324 raise err.__class__(
3326 klass=self.__class__,
3327 decode_path=decode_path,
3332 klass=self.__class__,
3333 decode_path=decode_path,
3336 if tag_only: # pragma: no cover
3339 l, llen, v = len_decode(lv)
3340 except DecodeError as err:
3341 raise err.__class__(
3343 klass=self.__class__,
3344 decode_path=decode_path,
3348 raise NotEnoughData(
3349 "encoded length is longer than data",
3350 klass=self.__class__,
3351 decode_path=decode_path,
3355 raise NotEnoughData(
3357 klass=self.__class__,
3358 decode_path=decode_path,
3361 v, tail = v[:l], v[l:]
3368 octet = indexbytes(v, i)
3369 if i == 0 and octet == 0x80:
3370 if ctx.get("bered", False):
3373 raise DecodeError("non normalized arc encoding")
3374 arc = (arc << 7) | (octet & 0x7F)
3375 if octet & 0x80 == 0:
3383 klass=self.__class__,
3384 decode_path=decode_path,
3388 second_arc = arcs[0]
3389 if 0 <= second_arc <= 39:
3391 elif 40 <= second_arc <= 79:
3397 obj = self.__class__(
3398 value=tuple([first_arc, second_arc] + arcs[1:]),
3401 default=self.default,
3402 optional=self.optional,
3403 _decoded=(offset, llen, l),
3406 obj.ber_encoded = True
3410 return pp_console_row(next(self.pps()))
3412 def pps(self, decode_path=()):
3415 asn1_type_name=self.asn1_type_name,
3416 obj_name=self.__class__.__name__,
3417 decode_path=decode_path,
3418 value=str(self) if self.ready else None,
3419 optional=self.optional,
3420 default=self == self.default,
3421 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3422 expl=None if self._expl is None else tag_decode(self._expl),
3427 expl_offset=self.expl_offset if self.expled else None,
3428 expl_tlen=self.expl_tlen if self.expled else None,
3429 expl_llen=self.expl_llen if self.expled else None,
3430 expl_vlen=self.expl_vlen if self.expled else None,
3431 expl_lenindef=self.expl_lenindef,
3432 ber_encoded=self.ber_encoded,
3435 for pp in self.pps_lenindef(decode_path):
3439 class Enumerated(Integer):
3440 """``ENUMERATED`` integer type
3442 This type is identical to :py:class:`pyderasn.Integer`, but requires
3443 schema to be specified and does not accept values missing from it.
3446 tag_default = tag_encode(10)
3447 asn1_type_name = "ENUMERATED"
3458 bounds=None, # dummy argument, workability for Integer.decode
3460 super(Enumerated, self).__init__(
3469 if len(self.specs) == 0:
3470 raise ValueError("schema must be specified")
3472 def _value_sanitize(self, value):
3473 if isinstance(value, self.__class__):
3474 value = value._value
3475 elif isinstance(value, integer_types):
3476 for _value in itervalues(self.specs):
3481 "unknown integer value: %s" % value,
3482 klass=self.__class__,
3484 elif isinstance(value, string_types):
3485 value = self.specs.get(value)
3487 raise ObjUnknown("integer value: %s" % value)
3489 raise InvalidValueType((self.__class__, int, str))
3493 obj = self.__class__(_specs=self.specs)
3494 obj._value = self._value
3495 obj._bound_min = self._bound_min
3496 obj._bound_max = self._bound_max
3498 obj._expl = self._expl
3499 obj.default = self.default
3500 obj.optional = self.optional
3501 obj.offset = self.offset
3502 obj.llen = self.llen
3503 obj.vlen = self.vlen
3504 obj.expl_lenindef = self.expl_lenindef
3505 obj.lenindef = self.lenindef
3506 obj.ber_encoded = self.ber_encoded
3518 return self.__class__(
3520 impl=self.tag if impl is None else impl,
3521 expl=self._expl if expl is None else expl,
3522 default=self.default if default is None else default,
3523 optional=self.optional if optional is None else optional,
3528 class CommonString(OctetString):
3529 """Common class for all strings
3531 Everything resembles :py:class:`pyderasn.OctetString`, except
3532 ability to deal with unicode text strings.
3534 >>> hexenc("привет мир".encode("utf-8"))
3535 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3536 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3538 >>> s = UTF8String("привет мир")
3539 UTF8String UTF8String привет мир
3542 >>> hexenc(bytes(s))
3543 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3545 >>> PrintableString("привет мир")
3546 Traceback (most recent call last):
3547 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3549 >>> BMPString("ада", bounds=(2, 2))
3550 Traceback (most recent call last):
3551 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3552 >>> s = BMPString("ад", bounds=(2, 2))
3555 >>> hexenc(bytes(s))
3563 * - :py:class:`pyderasn.UTF8String`
3565 * - :py:class:`pyderasn.NumericString`
3567 * - :py:class:`pyderasn.PrintableString`
3569 * - :py:class:`pyderasn.TeletexString`
3571 * - :py:class:`pyderasn.T61String`
3573 * - :py:class:`pyderasn.VideotexString`
3575 * - :py:class:`pyderasn.IA5String`
3577 * - :py:class:`pyderasn.GraphicString`
3579 * - :py:class:`pyderasn.VisibleString`
3581 * - :py:class:`pyderasn.ISO646String`
3583 * - :py:class:`pyderasn.GeneralString`
3585 * - :py:class:`pyderasn.UniversalString`
3587 * - :py:class:`pyderasn.BMPString`
3590 __slots__ = ("encoding",)
3592 def _value_sanitize(self, value):
3594 value_decoded = None
3595 if isinstance(value, self.__class__):
3596 value_raw = value._value
3597 elif isinstance(value, text_type):
3598 value_decoded = value
3599 elif isinstance(value, binary_type):
3602 raise InvalidValueType((self.__class__, text_type, binary_type))
3605 value_decoded.encode(self.encoding)
3606 if value_raw is None else value_raw
3609 value_raw.decode(self.encoding)
3610 if value_decoded is None else value_decoded
3612 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3613 raise DecodeError(str(err))
3614 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3622 def __eq__(self, their):
3623 if isinstance(their, binary_type):
3624 return self._value == their
3625 if isinstance(their, text_type):
3626 return self._value == their.encode(self.encoding)
3627 if not isinstance(their, self.__class__):
3630 self._value == their._value and
3631 self.tag == their.tag and
3632 self._expl == their._expl
3635 def __unicode__(self):
3637 return self._value.decode(self.encoding)
3638 return text_type(self._value)
3641 return pp_console_row(next(self.pps(no_unicode=PY2)))
3643 def pps(self, decode_path=(), no_unicode=False):
3646 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3649 asn1_type_name=self.asn1_type_name,
3650 obj_name=self.__class__.__name__,
3651 decode_path=decode_path,
3653 optional=self.optional,
3654 default=self == self.default,
3655 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3656 expl=None if self._expl is None else tag_decode(self._expl),
3661 expl_offset=self.expl_offset if self.expled else None,
3662 expl_tlen=self.expl_tlen if self.expled else None,
3663 expl_llen=self.expl_llen if self.expled else None,
3664 expl_vlen=self.expl_vlen if self.expled else None,
3665 expl_lenindef=self.expl_lenindef,
3666 ber_encoded=self.ber_encoded,
3669 for pp in self.pps_lenindef(decode_path):
3673 class UTF8String(CommonString):
3675 tag_default = tag_encode(12)
3677 asn1_type_name = "UTF8String"
3680 class AllowableCharsMixin(object):
3682 def allowable_chars(self):
3684 return self._allowable_chars
3685 return frozenset(six_unichr(c) for c in self._allowable_chars)
3688 class NumericString(AllowableCharsMixin, CommonString):
3691 Its value is properly sanitized: only ASCII digits with spaces can
3694 >>> NumericString().allowable_chars
3695 frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
3698 tag_default = tag_encode(18)
3700 asn1_type_name = "NumericString"
3701 _allowable_chars = frozenset(digits.encode("ascii") + b" ")
3703 def _value_sanitize(self, value):
3704 value = super(NumericString, self)._value_sanitize(value)
3705 if not frozenset(value) <= self._allowable_chars:
3706 raise DecodeError("non-numeric value")
3710 class PrintableString(AllowableCharsMixin, CommonString):
3713 Its value is properly sanitized: see X.680 41.4 table 10.
3715 >>> PrintableString().allowable_chars
3716 frozenset([' ', "'", ..., 'z'])
3719 tag_default = tag_encode(19)
3721 asn1_type_name = "PrintableString"
3722 _allowable_chars = frozenset(
3723 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3726 def _value_sanitize(self, value):
3727 value = super(PrintableString, self)._value_sanitize(value)
3728 if not frozenset(value) <= self._allowable_chars:
3729 raise DecodeError("non-printable value")
3733 class TeletexString(CommonString):
3735 tag_default = tag_encode(20)
3737 asn1_type_name = "TeletexString"
3740 class T61String(TeletexString):
3742 asn1_type_name = "T61String"
3745 class VideotexString(CommonString):
3747 tag_default = tag_encode(21)
3748 encoding = "iso-8859-1"
3749 asn1_type_name = "VideotexString"
3752 class IA5String(CommonString):
3754 tag_default = tag_encode(22)
3756 asn1_type_name = "IA5"
3759 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3760 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3761 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3764 class UTCTime(CommonString):
3765 """``UTCTime`` datetime type
3767 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3768 UTCTime UTCTime 2017-09-30T22:07:50
3774 datetime.datetime(2017, 9, 30, 22, 7, 50)
3775 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3776 datetime.datetime(1957, 9, 30, 22, 7, 50)
3780 BER encoding is unsupported.
3783 tag_default = tag_encode(23)
3785 asn1_type_name = "UTCTime"
3795 bounds=None, # dummy argument, workability for OctetString.decode
3798 :param value: set the value. Either datetime type, or
3799 :py:class:`pyderasn.UTCTime` object
3800 :param bytes impl: override default tag with ``IMPLICIT`` one
3801 :param bytes expl: override default tag with ``EXPLICIT`` one
3802 :param default: set default value. Type same as in ``value``
3803 :param bool optional: is object ``OPTIONAL`` in sequence
3805 super(UTCTime, self).__init__(
3813 if value is not None:
3814 self._value = self._value_sanitize(value)
3815 if default is not None:
3816 default = self._value_sanitize(default)
3817 self.default = self.__class__(
3822 if self._value is None:
3823 self._value = default
3825 def _strptime(self, value):
3826 # datetime.strptime's format: %y%m%d%H%M%SZ
3827 if len(value) != LEN_YYMMDDHHMMSSZ:
3828 raise ValueError("invalid UTCTime length")
3829 if value[-1] != "Z":
3830 raise ValueError("non UTC timezone")
3832 2000 + int(value[:2]), # %y
3833 int(value[2:4]), # %m
3834 int(value[4:6]), # %d
3835 int(value[6:8]), # %H
3836 int(value[8:10]), # %M
3837 int(value[10:12]), # %S
3840 def _value_sanitize(self, value):
3841 if isinstance(value, binary_type):
3843 value_decoded = value.decode("ascii")
3844 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3845 raise DecodeError("invalid UTCTime encoding: %r" % err)
3847 self._strptime(value_decoded)
3848 except (TypeError, ValueError) as err:
3849 raise DecodeError("invalid UTCTime format: %r" % err)
3851 if isinstance(value, self.__class__):
3853 if isinstance(value, datetime):
3854 return value.strftime("%y%m%d%H%M%SZ").encode("ascii")
3855 raise InvalidValueType((self.__class__, datetime))
3857 def __eq__(self, their):
3858 if isinstance(their, binary_type):
3859 return self._value == their
3860 if isinstance(their, datetime):
3861 return self.todatetime() == their
3862 if not isinstance(their, self.__class__):
3865 self._value == their._value and
3866 self.tag == their.tag and
3867 self._expl == their._expl
3870 def todatetime(self):
3871 """Convert to datetime
3875 Pay attention that UTCTime can not hold full year, so all years
3876 having < 50 years are treated as 20xx, 19xx otherwise, according
3877 to X.509 recomendation.
3879 value = self._strptime(self._value.decode("ascii"))
3880 year = value.year % 100
3882 year=(2000 + year) if year < 50 else (1900 + year),
3886 minute=value.minute,
3887 second=value.second,
3891 return pp_console_row(next(self.pps()))
3893 def pps(self, decode_path=()):
3896 asn1_type_name=self.asn1_type_name,
3897 obj_name=self.__class__.__name__,
3898 decode_path=decode_path,
3899 value=self.todatetime().isoformat() if self.ready else None,
3900 optional=self.optional,
3901 default=self == self.default,
3902 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3903 expl=None if self._expl is None else tag_decode(self._expl),
3908 expl_offset=self.expl_offset if self.expled else None,
3909 expl_tlen=self.expl_tlen if self.expled else None,
3910 expl_llen=self.expl_llen if self.expled else None,
3911 expl_vlen=self.expl_vlen if self.expled else None,
3912 expl_lenindef=self.expl_lenindef,
3913 ber_encoded=self.ber_encoded,
3916 for pp in self.pps_lenindef(decode_path):
3920 class GeneralizedTime(UTCTime):
3921 """``GeneralizedTime`` datetime type
3923 This type is similar to :py:class:`pyderasn.UTCTime`.
3925 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3926 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3928 '20170930220750.000123Z'
3929 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3930 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3934 BER encoding is unsupported.
3938 Only microsecond fractions are supported.
3939 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
3940 higher precision values.
3943 tag_default = tag_encode(24)
3944 asn1_type_name = "GeneralizedTime"
3946 def _strptime(self, value):
3948 if l == LEN_YYYYMMDDHHMMSSZ:
3949 # datetime.strptime's format: %y%m%d%H%M%SZ
3950 if value[-1] != "Z":
3951 raise ValueError("non UTC timezone")
3953 int(value[:4]), # %Y
3954 int(value[4:6]), # %m
3955 int(value[6:8]), # %d
3956 int(value[8:10]), # %H
3957 int(value[10:12]), # %M
3958 int(value[12:14]), # %S
3960 if l >= LEN_YYYYMMDDHHMMSSDMZ:
3961 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
3962 if value[-1] != "Z":
3963 raise ValueError("non UTC timezone")
3964 if value[14] != ".":
3965 raise ValueError("no fractions separator")
3968 raise ValueError("trailing zero")
3971 raise ValueError("only microsecond fractions are supported")
3972 us = int(us + ("0" * (6 - us_len)))
3974 int(value[:4]), # %Y
3975 int(value[4:6]), # %m
3976 int(value[6:8]), # %d
3977 int(value[8:10]), # %H
3978 int(value[10:12]), # %M
3979 int(value[12:14]), # %S
3983 raise ValueError("invalid GeneralizedTime length")
3985 def _value_sanitize(self, value):
3986 if isinstance(value, binary_type):
3988 value_decoded = value.decode("ascii")
3989 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3990 raise DecodeError("invalid GeneralizedTime encoding: %r" % err)
3992 self._strptime(value_decoded)
3993 except (TypeError, ValueError) as err:
3995 "invalid GeneralizedTime format: %r" % err,
3996 klass=self.__class__,
3999 if isinstance(value, self.__class__):
4001 if isinstance(value, datetime):
4002 encoded = value.strftime("%Y%m%d%H%M%S")
4003 if value.microsecond > 0:
4004 encoded = encoded + (".%06d" % value.microsecond).rstrip("0")
4005 return (encoded + "Z").encode("ascii")
4006 raise InvalidValueType((self.__class__, datetime))
4008 def todatetime(self):
4009 return self._strptime(self._value.decode("ascii"))
4012 class GraphicString(CommonString):
4014 tag_default = tag_encode(25)
4015 encoding = "iso-8859-1"
4016 asn1_type_name = "GraphicString"
4019 class VisibleString(CommonString):
4021 tag_default = tag_encode(26)
4023 asn1_type_name = "VisibleString"
4026 class ISO646String(VisibleString):
4028 asn1_type_name = "ISO646String"
4031 class GeneralString(CommonString):
4033 tag_default = tag_encode(27)
4034 encoding = "iso-8859-1"
4035 asn1_type_name = "GeneralString"
4038 class UniversalString(CommonString):
4040 tag_default = tag_encode(28)
4041 encoding = "utf-32-be"
4042 asn1_type_name = "UniversalString"
4045 class BMPString(CommonString):
4047 tag_default = tag_encode(30)
4048 encoding = "utf-16-be"
4049 asn1_type_name = "BMPString"
4053 """``CHOICE`` special type
4057 class GeneralName(Choice):
4059 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4060 ("dNSName", IA5String(impl=tag_ctxp(2))),
4063 >>> gn = GeneralName()
4065 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4066 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4067 >>> gn["dNSName"] = IA5String("bar.baz")
4068 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4069 >>> gn["rfc822Name"]
4072 [2] IA5String IA5 bar.baz
4075 >>> gn.value == gn["dNSName"]
4078 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4080 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4081 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4083 __slots__ = ("specs",)
4085 asn1_type_name = "CHOICE"
4098 :param value: set the value. Either ``(choice, value)`` tuple, or
4099 :py:class:`pyderasn.Choice` object
4100 :param bytes impl: can not be set, do **not** use it
4101 :param bytes expl: override default tag with ``EXPLICIT`` one
4102 :param default: set default value. Type same as in ``value``
4103 :param bool optional: is object ``OPTIONAL`` in sequence
4105 if impl is not None:
4106 raise ValueError("no implicit tag allowed for CHOICE")
4107 super(Choice, self).__init__(None, expl, default, optional, _decoded)
4109 schema = getattr(self, "schema", ())
4110 if len(schema) == 0:
4111 raise ValueError("schema must be specified")
4113 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4116 if value is not None:
4117 self._value = self._value_sanitize(value)
4118 if default is not None:
4119 default_value = self._value_sanitize(default)
4120 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4121 default_obj.specs = self.specs
4122 default_obj._value = default_value
4123 self.default = default_obj
4125 self._value = default_obj.copy()._value
4127 def _value_sanitize(self, value):
4128 if isinstance(value, tuple) and len(value) == 2:
4130 spec = self.specs.get(choice)
4132 raise ObjUnknown(choice)
4133 if not isinstance(obj, spec.__class__):
4134 raise InvalidValueType((spec,))
4135 return (choice, spec(obj))
4136 if isinstance(value, self.__class__):
4138 raise InvalidValueType((self.__class__, tuple))
4142 return self._value is not None and self._value[1].ready
4146 return self.expl_lenindef or (
4147 (self._value is not None) and
4148 self._value[1].bered
4152 obj = self.__class__(schema=self.specs)
4153 obj._expl = self._expl
4154 obj.default = self.default
4155 obj.optional = self.optional
4156 obj.offset = self.offset
4157 obj.llen = self.llen
4158 obj.vlen = self.vlen
4159 obj.expl_lenindef = self.expl_lenindef
4160 obj.lenindef = self.lenindef
4161 obj.ber_encoded = self.ber_encoded
4163 if value is not None:
4164 obj._value = (value[0], value[1].copy())
4167 def __eq__(self, their):
4168 if isinstance(their, tuple) and len(their) == 2:
4169 return self._value == their
4170 if not isinstance(their, self.__class__):
4173 self.specs == their.specs and
4174 self._value == their._value
4184 return self.__class__(
4187 expl=self._expl if expl is None else expl,
4188 default=self.default if default is None else default,
4189 optional=self.optional if optional is None else optional,
4194 self._assert_ready()
4195 return self._value[0]
4199 self._assert_ready()
4200 return self._value[1]
4202 def __getitem__(self, key):
4203 if key not in self.specs:
4204 raise ObjUnknown(key)
4205 if self._value is None:
4207 choice, value = self._value
4212 def __setitem__(self, key, value):
4213 spec = self.specs.get(key)
4215 raise ObjUnknown(key)
4216 if not isinstance(value, spec.__class__):
4217 raise InvalidValueType((spec.__class__,))
4218 self._value = (key, spec(value))
4226 return self._value[1].decoded if self.ready else False
4229 self._assert_ready()
4230 return self._value[1].encode()
4232 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4233 for choice, spec in iteritems(self.specs):
4234 sub_decode_path = decode_path + (choice,)
4240 decode_path=sub_decode_path,
4243 _ctx_immutable=False,
4250 klass=self.__class__,
4251 decode_path=decode_path,
4254 if tag_only: # pragma: no cover
4256 value, tail = spec.decode(
4260 decode_path=sub_decode_path,
4262 _ctx_immutable=False,
4264 obj = self.__class__(
4267 default=self.default,
4268 optional=self.optional,
4269 _decoded=(offset, 0, value.fulllen),
4271 obj._value = (choice, value)
4275 value = pp_console_row(next(self.pps()))
4277 value = "%s[%r]" % (value, self.value)
4280 def pps(self, decode_path=()):
4283 asn1_type_name=self.asn1_type_name,
4284 obj_name=self.__class__.__name__,
4285 decode_path=decode_path,
4286 value=self.choice if self.ready else None,
4287 optional=self.optional,
4288 default=self == self.default,
4289 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4290 expl=None if self._expl is None else tag_decode(self._expl),
4295 expl_lenindef=self.expl_lenindef,
4299 yield self.value.pps(decode_path=decode_path + (self.choice,))
4300 for pp in self.pps_lenindef(decode_path):
4304 class PrimitiveTypes(Choice):
4305 """Predefined ``CHOICE`` for all generic primitive types
4307 It could be useful for general decoding of some unspecified values:
4309 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4310 OCTET STRING 3 bytes 666f6f
4311 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4315 schema = tuple((klass.__name__, klass()) for klass in (
4340 """``ANY`` special type
4342 >>> Any(Integer(-123))
4344 >>> a = Any(OctetString(b"hello world").encode())
4345 ANY 040b68656c6c6f20776f726c64
4346 >>> hexenc(bytes(a))
4347 b'0x040x0bhello world'
4349 __slots__ = ("defined",)
4350 tag_default = tag_encode(0)
4351 asn1_type_name = "ANY"
4361 :param value: set the value. Either any kind of pyderasn's
4362 **ready** object, or bytes. Pay attention that
4363 **no** validation is performed is raw binary value
4365 :param bytes expl: override default tag with ``EXPLICIT`` one
4366 :param bool optional: is object ``OPTIONAL`` in sequence
4368 super(Any, self).__init__(None, expl, None, optional, _decoded)
4369 self._value = None if value is None else self._value_sanitize(value)
4372 def _value_sanitize(self, value):
4373 if isinstance(value, binary_type):
4375 if isinstance(value, self.__class__):
4377 if isinstance(value, Obj):
4378 return value.encode()
4379 raise InvalidValueType((self.__class__, Obj, binary_type))
4383 return self._value is not None
4387 if self.expl_lenindef or self.lenindef:
4389 if self.defined is None:
4391 return self.defined[1].bered
4394 obj = self.__class__()
4395 obj._value = self._value
4397 obj._expl = self._expl
4398 obj.optional = self.optional
4399 obj.offset = self.offset
4400 obj.llen = self.llen
4401 obj.vlen = self.vlen
4402 obj.expl_lenindef = self.expl_lenindef
4403 obj.lenindef = self.lenindef
4404 obj.ber_encoded = self.ber_encoded
4407 def __eq__(self, their):
4408 if isinstance(their, binary_type):
4409 return self._value == their
4410 if issubclass(their.__class__, Any):
4411 return self._value == their._value
4420 return self.__class__(
4422 expl=self._expl if expl is None else expl,
4423 optional=self.optional if optional is None else optional,
4426 def __bytes__(self):
4427 self._assert_ready()
4435 self._assert_ready()
4438 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4440 t, tlen, lv = tag_strip(tlv)
4441 except DecodeError as err:
4442 raise err.__class__(
4444 klass=self.__class__,
4445 decode_path=decode_path,
4449 l, llen, v = len_decode(lv)
4450 except LenIndefForm as err:
4451 if not ctx.get("bered", False):
4452 raise err.__class__(
4454 klass=self.__class__,
4455 decode_path=decode_path,
4458 llen, vlen, v = 1, 0, lv[1:]
4459 sub_offset = offset + tlen + llen
4461 while v[:EOC_LEN].tobytes() != EOC:
4462 chunk, v = Any().decode(
4465 decode_path=decode_path + (str(chunk_i),),
4468 _ctx_immutable=False,
4470 vlen += chunk.tlvlen
4471 sub_offset += chunk.tlvlen
4473 tlvlen = tlen + llen + vlen + EOC_LEN
4474 obj = self.__class__(
4475 value=tlv[:tlvlen].tobytes(),
4477 optional=self.optional,
4478 _decoded=(offset, 0, tlvlen),
4482 return obj, v[EOC_LEN:]
4483 except DecodeError as err:
4484 raise err.__class__(
4486 klass=self.__class__,
4487 decode_path=decode_path,
4491 raise NotEnoughData(
4492 "encoded length is longer than data",
4493 klass=self.__class__,
4494 decode_path=decode_path,
4497 tlvlen = tlen + llen + l
4498 v, tail = tlv[:tlvlen], v[l:]
4499 obj = self.__class__(
4502 optional=self.optional,
4503 _decoded=(offset, 0, tlvlen),
4509 return pp_console_row(next(self.pps()))
4511 def pps(self, decode_path=()):
4514 asn1_type_name=self.asn1_type_name,
4515 obj_name=self.__class__.__name__,
4516 decode_path=decode_path,
4517 blob=self._value if self.ready else None,
4518 optional=self.optional,
4519 default=self == self.default,
4520 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4521 expl=None if self._expl is None else tag_decode(self._expl),
4526 expl_offset=self.expl_offset if self.expled else None,
4527 expl_tlen=self.expl_tlen if self.expled else None,
4528 expl_llen=self.expl_llen if self.expled else None,
4529 expl_vlen=self.expl_vlen if self.expled else None,
4530 expl_lenindef=self.expl_lenindef,
4531 lenindef=self.lenindef,
4534 defined_by, defined = self.defined or (None, None)
4535 if defined_by is not None:
4537 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4539 for pp in self.pps_lenindef(decode_path):
4543 ########################################################################
4544 # ASN.1 constructed types
4545 ########################################################################
4547 def get_def_by_path(defines_by_path, sub_decode_path):
4548 """Get define by decode path
4550 for path, define in defines_by_path:
4551 if len(path) != len(sub_decode_path):
4553 for p1, p2 in zip(path, sub_decode_path):
4554 if (p1 != any) and (p1 != p2):
4560 def abs_decode_path(decode_path, rel_path):
4561 """Create an absolute decode path from current and relative ones
4563 :param decode_path: current decode path, starting point. Tuple of strings
4564 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4565 If first tuple's element is "/", then treat it as
4566 an absolute path, ignoring ``decode_path`` as
4567 starting point. Also this tuple can contain ".."
4568 elements, stripping the leading element from
4571 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4572 ("foo", "bar", "baz", "whatever")
4573 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4575 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4578 if rel_path[0] == "/":
4580 if rel_path[0] == "..":
4581 return abs_decode_path(decode_path[:-1], rel_path[1:])
4582 return decode_path + rel_path
4585 class Sequence(Obj):
4586 """``SEQUENCE`` structure type
4588 You have to make specification of sequence::
4590 class Extension(Sequence):
4592 ("extnID", ObjectIdentifier()),
4593 ("critical", Boolean(default=False)),
4594 ("extnValue", OctetString()),
4597 Then, you can work with it as with dictionary.
4599 >>> ext = Extension()
4600 >>> Extension().specs
4602 ('extnID', OBJECT IDENTIFIER),
4603 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4604 ('extnValue', OCTET STRING),
4606 >>> ext["extnID"] = "1.2.3"
4607 Traceback (most recent call last):
4608 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4609 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4611 You can determine if sequence is ready to be encoded:
4616 Traceback (most recent call last):
4617 pyderasn.ObjNotReady: object is not ready: extnValue
4618 >>> ext["extnValue"] = OctetString(b"foobar")
4622 Value you want to assign, must have the same **type** as in
4623 corresponding specification, but it can have different tags,
4624 optional/default attributes -- they will be taken from specification
4627 class TBSCertificate(Sequence):
4629 ("version", Version(expl=tag_ctxc(0), default="v1")),
4632 >>> tbs = TBSCertificate()
4633 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4635 Assign ``None`` to remove value from sequence.
4637 You can set values in Sequence during its initialization:
4639 >>> AlgorithmIdentifier((
4640 ("algorithm", ObjectIdentifier("1.2.3")),
4641 ("parameters", Any(Null()))
4643 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4645 You can determine if value exists/set in the sequence and take its value:
4647 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4650 OBJECT IDENTIFIER 1.2.3
4652 But pay attention that if value has default, then it won't be (not
4653 in) in the sequence (because ``DEFAULT`` must not be encoded in
4654 DER), but you can read its value:
4656 >>> "critical" in ext, ext["critical"]
4657 (False, BOOLEAN False)
4658 >>> ext["critical"] = Boolean(True)
4659 >>> "critical" in ext, ext["critical"]
4660 (True, BOOLEAN True)
4662 All defaulted values are always optional.
4664 .. _allow_default_values_ctx:
4666 DER prohibits default value encoding and will raise an error if
4667 default value is unexpectedly met during decode.
4668 If :ref:`bered <bered_ctx>` context option is set, then no error
4669 will be raised, but ``bered`` attribute set. You can disable strict
4670 defaulted values existence validation by setting
4671 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4673 Two sequences are equal if they have equal specification (schema),
4674 implicit/explicit tagging and the same values.
4676 __slots__ = ("specs",)
4677 tag_default = tag_encode(form=TagFormConstructed, num=16)
4678 asn1_type_name = "SEQUENCE"
4690 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4692 schema = getattr(self, "schema", ())
4694 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4697 if value is not None:
4698 if issubclass(value.__class__, Sequence):
4699 self._value = value._value
4700 elif hasattr(value, "__iter__"):
4701 for seq_key, seq_value in value:
4702 self[seq_key] = seq_value
4704 raise InvalidValueType((Sequence,))
4705 if default is not None:
4706 if not issubclass(default.__class__, Sequence):
4707 raise InvalidValueType((Sequence,))
4708 default_value = default._value
4709 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4710 default_obj.specs = self.specs
4711 default_obj._value = default_value
4712 self.default = default_obj
4714 self._value = default_obj.copy()._value
4718 for name, spec in iteritems(self.specs):
4719 value = self._value.get(name)
4731 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4733 return any(value.bered for value in itervalues(self._value))
4736 obj = self.__class__(schema=self.specs)
4738 obj._expl = self._expl
4739 obj.default = self.default
4740 obj.optional = self.optional
4741 obj.offset = self.offset
4742 obj.llen = self.llen
4743 obj.vlen = self.vlen
4744 obj.expl_lenindef = self.expl_lenindef
4745 obj.lenindef = self.lenindef
4746 obj.ber_encoded = self.ber_encoded
4747 obj._value = {k: v.copy() for k, v in iteritems(self._value)}
4750 def __eq__(self, their):
4751 if not isinstance(their, self.__class__):
4754 self.specs == their.specs and
4755 self.tag == their.tag and
4756 self._expl == their._expl and
4757 self._value == their._value
4768 return self.__class__(
4771 impl=self.tag if impl is None else impl,
4772 expl=self._expl if expl is None else expl,
4773 default=self.default if default is None else default,
4774 optional=self.optional if optional is None else optional,
4777 def __contains__(self, key):
4778 return key in self._value
4780 def __setitem__(self, key, value):
4781 spec = self.specs.get(key)
4783 raise ObjUnknown(key)
4785 self._value.pop(key, None)
4787 if not isinstance(value, spec.__class__):
4788 raise InvalidValueType((spec.__class__,))
4789 value = spec(value=value)
4790 if spec.default is not None and value == spec.default:
4791 self._value.pop(key, None)
4793 self._value[key] = value
4795 def __getitem__(self, key):
4796 value = self._value.get(key)
4797 if value is not None:
4799 spec = self.specs.get(key)
4801 raise ObjUnknown(key)
4802 if spec.default is not None:
4806 def _encoded_values(self):
4808 for name, spec in iteritems(self.specs):
4809 value = self._value.get(name)
4813 raise ObjNotReady(name)
4814 raws.append(value.encode())
4818 v = b"".join(self._encoded_values())
4819 return b"".join((self.tag, len_encode(len(v)), v))
4821 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4823 t, tlen, lv = tag_strip(tlv)
4824 except DecodeError as err:
4825 raise err.__class__(
4827 klass=self.__class__,
4828 decode_path=decode_path,
4833 klass=self.__class__,
4834 decode_path=decode_path,
4837 if tag_only: # pragma: no cover
4840 ctx_bered = ctx.get("bered", False)
4842 l, llen, v = len_decode(lv)
4843 except LenIndefForm as err:
4845 raise err.__class__(
4847 klass=self.__class__,
4848 decode_path=decode_path,
4851 l, llen, v = 0, 1, lv[1:]
4853 except DecodeError as err:
4854 raise err.__class__(
4856 klass=self.__class__,
4857 decode_path=decode_path,
4861 raise NotEnoughData(
4862 "encoded length is longer than data",
4863 klass=self.__class__,
4864 decode_path=decode_path,
4868 v, tail = v[:l], v[l:]
4870 sub_offset = offset + tlen + llen
4873 ctx_allow_default_values = ctx.get("allow_default_values", False)
4874 for name, spec in iteritems(self.specs):
4875 if spec.optional and (
4876 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4880 sub_decode_path = decode_path + (name,)
4882 value, v_tail = spec.decode(
4886 decode_path=sub_decode_path,
4888 _ctx_immutable=False,
4890 except TagMismatch as err:
4891 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
4895 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4896 if defined is not None:
4897 defined_by, defined_spec = defined
4898 if issubclass(value.__class__, SequenceOf):
4899 for i, _value in enumerate(value):
4900 sub_sub_decode_path = sub_decode_path + (
4902 DecodePathDefBy(defined_by),
4904 defined_value, defined_tail = defined_spec.decode(
4905 memoryview(bytes(_value)),
4907 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4908 if value.expled else (value.tlen + value.llen)
4911 decode_path=sub_sub_decode_path,
4913 _ctx_immutable=False,
4915 if len(defined_tail) > 0:
4918 klass=self.__class__,
4919 decode_path=sub_sub_decode_path,
4922 _value.defined = (defined_by, defined_value)
4924 defined_value, defined_tail = defined_spec.decode(
4925 memoryview(bytes(value)),
4927 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4928 if value.expled else (value.tlen + value.llen)
4931 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4933 _ctx_immutable=False,
4935 if len(defined_tail) > 0:
4938 klass=self.__class__,
4939 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4942 value.defined = (defined_by, defined_value)
4944 value_len = value.fulllen
4946 sub_offset += value_len
4948 if spec.default is not None and value == spec.default:
4949 if ctx_bered or ctx_allow_default_values:
4953 "DEFAULT value met",
4954 klass=self.__class__,
4955 decode_path=sub_decode_path,
4958 values[name] = value
4960 spec_defines = getattr(spec, "defines", ())
4961 if len(spec_defines) == 0:
4962 defines_by_path = ctx.get("defines_by_path", ())
4963 if len(defines_by_path) > 0:
4964 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4965 if spec_defines is not None and len(spec_defines) > 0:
4966 for rel_path, schema in spec_defines:
4967 defined = schema.get(value, None)
4968 if defined is not None:
4969 ctx.setdefault("_defines", []).append((
4970 abs_decode_path(sub_decode_path[:-1], rel_path),
4974 if v[:EOC_LEN].tobytes() != EOC:
4977 klass=self.__class__,
4978 decode_path=decode_path,
4986 klass=self.__class__,
4987 decode_path=decode_path,
4990 obj = self.__class__(
4994 default=self.default,
4995 optional=self.optional,
4996 _decoded=(offset, llen, vlen),
4999 obj.lenindef = lenindef
5000 obj.ber_encoded = ber_encoded
5004 value = pp_console_row(next(self.pps()))
5006 for name in self.specs:
5007 _value = self._value.get(name)
5010 cols.append("%s: %s" % (name, repr(_value)))
5011 return "%s[%s]" % (value, "; ".join(cols))
5013 def pps(self, decode_path=()):
5016 asn1_type_name=self.asn1_type_name,
5017 obj_name=self.__class__.__name__,
5018 decode_path=decode_path,
5019 optional=self.optional,
5020 default=self == self.default,
5021 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5022 expl=None if self._expl is None else tag_decode(self._expl),
5027 expl_offset=self.expl_offset if self.expled else None,
5028 expl_tlen=self.expl_tlen if self.expled else None,
5029 expl_llen=self.expl_llen if self.expled else None,
5030 expl_vlen=self.expl_vlen if self.expled else None,
5031 expl_lenindef=self.expl_lenindef,
5032 lenindef=self.lenindef,
5033 ber_encoded=self.ber_encoded,
5036 for name in self.specs:
5037 value = self._value.get(name)
5040 yield value.pps(decode_path=decode_path + (name,))
5041 for pp in self.pps_lenindef(decode_path):
5045 class Set(Sequence):
5046 """``SET`` structure type
5048 Its usage is identical to :py:class:`pyderasn.Sequence`.
5050 .. _allow_unordered_set_ctx:
5052 DER prohibits unordered values encoding and will raise an error
5053 during decode. If If :ref:`bered <bered_ctx>` context option is set,
5054 then no error will occure. Also you can disable strict values
5055 ordering check by setting ``"allow_unordered_set": True``
5056 :ref:`context <ctx>` option.
5059 tag_default = tag_encode(form=TagFormConstructed, num=17)
5060 asn1_type_name = "SET"
5063 raws = self._encoded_values()
5066 return b"".join((self.tag, len_encode(len(v)), v))
5068 def _specs_items(self):
5069 return iteritems(self.specs)
5071 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5073 t, tlen, lv = tag_strip(tlv)
5074 except DecodeError as err:
5075 raise err.__class__(
5077 klass=self.__class__,
5078 decode_path=decode_path,
5083 klass=self.__class__,
5084 decode_path=decode_path,
5090 ctx_bered = ctx.get("bered", False)
5092 l, llen, v = len_decode(lv)
5093 except LenIndefForm as err:
5095 raise err.__class__(
5097 klass=self.__class__,
5098 decode_path=decode_path,
5101 l, llen, v = 0, 1, lv[1:]
5103 except DecodeError as err:
5104 raise err.__class__(
5106 klass=self.__class__,
5107 decode_path=decode_path,
5111 raise NotEnoughData(
5112 "encoded length is longer than data",
5113 klass=self.__class__,
5117 v, tail = v[:l], v[l:]
5119 sub_offset = offset + tlen + llen
5122 ctx_allow_default_values = ctx.get("allow_default_values", False)
5123 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5124 value_prev = memoryview(v[:0])
5127 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5129 for name, spec in self._specs_items():
5130 sub_decode_path = decode_path + (name,)
5136 decode_path=sub_decode_path,
5139 _ctx_immutable=False,
5146 klass=self.__class__,
5147 decode_path=decode_path,
5150 value, v_tail = spec.decode(
5154 decode_path=sub_decode_path,
5156 _ctx_immutable=False,
5158 value_len = value.fulllen
5159 if value_prev.tobytes() > v[:value_len].tobytes():
5160 if ctx_bered or ctx_allow_unordered_set:
5164 "unordered " + self.asn1_type_name,
5165 klass=self.__class__,
5166 decode_path=sub_decode_path,
5169 if spec.default is None or value != spec.default:
5171 elif ctx_bered or ctx_allow_default_values:
5175 "DEFAULT value met",
5176 klass=self.__class__,
5177 decode_path=sub_decode_path,
5180 values[name] = value
5181 value_prev = v[:value_len]
5182 sub_offset += value_len
5185 obj = self.__class__(
5189 default=self.default,
5190 optional=self.optional,
5191 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5194 if v[:EOC_LEN].tobytes() != EOC:
5197 klass=self.__class__,
5198 decode_path=decode_path,
5206 "not all values are ready",
5207 klass=self.__class__,
5208 decode_path=decode_path,
5211 obj.ber_encoded = ber_encoded
5215 class SequenceOf(Obj):
5216 """``SEQUENCE OF`` sequence type
5218 For that kind of type you must specify the object it will carry on
5219 (bounds are for example here, not required)::
5221 class Ints(SequenceOf):
5226 >>> ints.append(Integer(123))
5227 >>> ints.append(Integer(234))
5229 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5230 >>> [int(i) for i in ints]
5232 >>> ints.append(Integer(345))
5233 Traceback (most recent call last):
5234 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5237 >>> ints[1] = Integer(345)
5239 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5241 Also you can initialize sequence with preinitialized values:
5243 >>> ints = Ints([Integer(123), Integer(234)])
5245 __slots__ = ("spec", "_bound_min", "_bound_max")
5246 tag_default = tag_encode(form=TagFormConstructed, num=16)
5247 asn1_type_name = "SEQUENCE OF"
5260 super(SequenceOf, self).__init__(
5268 schema = getattr(self, "schema", None)
5270 raise ValueError("schema must be specified")
5272 self._bound_min, self._bound_max = getattr(
5276 ) if bounds is None else bounds
5278 if value is not None:
5279 self._value = self._value_sanitize(value)
5280 if default is not None:
5281 default_value = self._value_sanitize(default)
5282 default_obj = self.__class__(
5287 default_obj._value = default_value
5288 self.default = default_obj
5290 self._value = default_obj.copy()._value
5292 def _value_sanitize(self, value):
5293 if issubclass(value.__class__, SequenceOf):
5294 value = value._value
5295 elif hasattr(value, "__iter__"):
5298 raise InvalidValueType((self.__class__, iter))
5299 if not self._bound_min <= len(value) <= self._bound_max:
5300 raise BoundsError(self._bound_min, len(value), self._bound_max)
5302 if not isinstance(v, self.spec.__class__):
5303 raise InvalidValueType((self.spec.__class__,))
5308 return all(v.ready for v in self._value)
5312 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5314 return any(v.bered for v in self._value)
5317 obj = self.__class__(schema=self.spec)
5318 obj._bound_min = self._bound_min
5319 obj._bound_max = self._bound_max
5321 obj._expl = self._expl
5322 obj.default = self.default
5323 obj.optional = self.optional
5324 obj.offset = self.offset
5325 obj.llen = self.llen
5326 obj.vlen = self.vlen
5327 obj.expl_lenindef = self.expl_lenindef
5328 obj.lenindef = self.lenindef
5329 obj.ber_encoded = self.ber_encoded
5330 obj._value = [v.copy() for v in self._value]
5333 def __eq__(self, their):
5334 if isinstance(their, self.__class__):
5336 self.spec == their.spec and
5337 self.tag == their.tag and
5338 self._expl == their._expl and
5339 self._value == their._value
5341 if hasattr(their, "__iter__"):
5342 return self._value == list(their)
5354 return self.__class__(
5358 (self._bound_min, self._bound_max)
5359 if bounds is None else bounds
5361 impl=self.tag if impl is None else impl,
5362 expl=self._expl if expl is None else expl,
5363 default=self.default if default is None else default,
5364 optional=self.optional if optional is None else optional,
5367 def __contains__(self, key):
5368 return key in self._value
5370 def append(self, value):
5371 if not isinstance(value, self.spec.__class__):
5372 raise InvalidValueType((self.spec.__class__,))
5373 if len(self._value) + 1 > self._bound_max:
5376 len(self._value) + 1,
5379 self._value.append(value)
5382 self._assert_ready()
5383 return iter(self._value)
5386 self._assert_ready()
5387 return len(self._value)
5389 def __setitem__(self, key, value):
5390 if not isinstance(value, self.spec.__class__):
5391 raise InvalidValueType((self.spec.__class__,))
5392 self._value[key] = self.spec(value=value)
5394 def __getitem__(self, key):
5395 return self._value[key]
5397 def _encoded_values(self):
5398 return [v.encode() for v in self._value]
5401 v = b"".join(self._encoded_values())
5402 return b"".join((self.tag, len_encode(len(v)), v))
5404 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5406 t, tlen, lv = tag_strip(tlv)
5407 except DecodeError as err:
5408 raise err.__class__(
5410 klass=self.__class__,
5411 decode_path=decode_path,
5416 klass=self.__class__,
5417 decode_path=decode_path,
5423 ctx_bered = ctx.get("bered", False)
5425 l, llen, v = len_decode(lv)
5426 except LenIndefForm as err:
5428 raise err.__class__(
5430 klass=self.__class__,
5431 decode_path=decode_path,
5434 l, llen, v = 0, 1, lv[1:]
5436 except DecodeError as err:
5437 raise err.__class__(
5439 klass=self.__class__,
5440 decode_path=decode_path,
5444 raise NotEnoughData(
5445 "encoded length is longer than data",
5446 klass=self.__class__,
5447 decode_path=decode_path,
5451 v, tail = v[:l], v[l:]
5453 sub_offset = offset + tlen + llen
5455 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5456 value_prev = memoryview(v[:0])
5460 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5462 sub_decode_path = decode_path + (str(len(_value)),)
5463 value, v_tail = spec.decode(
5467 decode_path=sub_decode_path,
5469 _ctx_immutable=False,
5471 value_len = value.fulllen
5473 if value_prev.tobytes() > v[:value_len].tobytes():
5474 if ctx_bered or ctx_allow_unordered_set:
5478 "unordered " + self.asn1_type_name,
5479 klass=self.__class__,
5480 decode_path=sub_decode_path,
5483 value_prev = v[:value_len]
5484 _value.append(value)
5485 sub_offset += value_len
5489 obj = self.__class__(
5492 bounds=(self._bound_min, self._bound_max),
5495 default=self.default,
5496 optional=self.optional,
5497 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5499 except BoundsError as err:
5502 klass=self.__class__,
5503 decode_path=decode_path,
5507 if v[:EOC_LEN].tobytes() != EOC:
5510 klass=self.__class__,
5511 decode_path=decode_path,
5516 obj.ber_encoded = ber_encoded
5521 pp_console_row(next(self.pps())),
5522 ", ".join(repr(v) for v in self._value),
5525 def pps(self, decode_path=()):
5528 asn1_type_name=self.asn1_type_name,
5529 obj_name=self.__class__.__name__,
5530 decode_path=decode_path,
5531 optional=self.optional,
5532 default=self == self.default,
5533 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5534 expl=None if self._expl is None else tag_decode(self._expl),
5539 expl_offset=self.expl_offset if self.expled else None,
5540 expl_tlen=self.expl_tlen if self.expled else None,
5541 expl_llen=self.expl_llen if self.expled else None,
5542 expl_vlen=self.expl_vlen if self.expled else None,
5543 expl_lenindef=self.expl_lenindef,
5544 lenindef=self.lenindef,
5545 ber_encoded=self.ber_encoded,
5548 for i, value in enumerate(self._value):
5549 yield value.pps(decode_path=decode_path + (str(i),))
5550 for pp in self.pps_lenindef(decode_path):
5554 class SetOf(SequenceOf):
5555 """``SET OF`` sequence type
5557 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5560 tag_default = tag_encode(form=TagFormConstructed, num=17)
5561 asn1_type_name = "SET OF"
5564 raws = self._encoded_values()
5567 return b"".join((self.tag, len_encode(len(v)), v))
5569 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5570 return super(SetOf, self)._decode(
5576 ordering_check=True,
5580 def obj_by_path(pypath): # pragma: no cover
5581 """Import object specified as string Python path
5583 Modules must be separated from classes/functions with ``:``.
5585 >>> obj_by_path("foo.bar:Baz")
5586 <class 'foo.bar.Baz'>
5587 >>> obj_by_path("foo.bar:Baz.boo")
5588 <classmethod 'foo.bar.Baz.boo'>
5590 mod, objs = pypath.rsplit(":", 1)
5591 from importlib import import_module
5592 obj = import_module(mod)
5593 for obj_name in objs.split("."):
5594 obj = getattr(obj, obj_name)
5598 def generic_decoder(): # pragma: no cover
5599 # All of this below is a big hack with self references
5600 choice = PrimitiveTypes()
5601 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5602 choice.specs["SetOf"] = SetOf(schema=choice)
5603 for i in six_xrange(31):
5604 choice.specs["SequenceOf%d" % i] = SequenceOf(
5608 choice.specs["Any"] = Any()
5610 # Class name equals to type name, to omit it from output
5611 class SEQUENCEOF(SequenceOf):
5619 with_decode_path=False,
5620 decode_path_only=(),
5622 def _pprint_pps(pps):
5624 if hasattr(pp, "_fields"):
5626 decode_path_only != () and
5627 pp.decode_path[:len(decode_path_only)] != decode_path_only
5630 if pp.asn1_type_name == Choice.asn1_type_name:
5632 pp_kwargs = pp._asdict()
5633 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5634 pp = _pp(**pp_kwargs)
5635 yield pp_console_row(
5640 with_colours=with_colours,
5641 with_decode_path=with_decode_path,
5642 decode_path_len_decrease=len(decode_path_only),
5644 for row in pp_console_blob(
5646 decode_path_len_decrease=len(decode_path_only),
5650 for row in _pprint_pps(pp):
5652 return "\n".join(_pprint_pps(obj.pps()))
5653 return SEQUENCEOF(), pprint_any
5656 def main(): # pragma: no cover
5658 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5659 parser.add_argument(
5663 help="Skip that number of bytes from the beginning",
5665 parser.add_argument(
5667 help="Python paths to dictionary with OIDs, comma separated",
5669 parser.add_argument(
5671 help="Python path to schema definition to use",
5673 parser.add_argument(
5674 "--defines-by-path",
5675 help="Python path to decoder's defines_by_path",
5677 parser.add_argument(
5679 action="store_true",
5680 help="Disallow BER encoding",
5682 parser.add_argument(
5683 "--print-decode-path",
5684 action="store_true",
5685 help="Print decode paths",
5687 parser.add_argument(
5688 "--decode-path-only",
5689 help="Print only specified decode path",
5691 parser.add_argument(
5693 action="store_true",
5694 help="Allow explicit tag out-of-bound",
5696 parser.add_argument(
5698 type=argparse.FileType("rb"),
5699 help="Path to DER file you want to decode",
5701 args = parser.parse_args()
5702 args.DERFile.seek(args.skip)
5703 der = memoryview(args.DERFile.read())
5704 args.DERFile.close()
5706 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
5707 if args.oids else ()
5710 schema = obj_by_path(args.schema)
5711 from functools import partial
5712 pprinter = partial(pprint, big_blobs=True)
5714 schema, pprinter = generic_decoder()
5716 "bered": not args.nobered,
5717 "allow_expl_oob": args.allow_expl_oob,
5719 if args.defines_by_path is not None:
5720 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5721 obj, tail = schema().decode(der, ctx=ctx)
5725 with_colours=True if environ.get("NO_COLOR") is None else False,
5726 with_decode_path=args.print_decode_path,
5728 () if args.decode_path_only is None else
5729 tuple(args.decode_path_only.split(":"))
5733 print("\nTrailing data: %s" % hexenc(tail))
5736 if __name__ == "__main__":