3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2019 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
17 # <http://www.gnu.org/licenses/>.
18 """Python ASN.1 DER/BER codec with abstract structures
20 This library allows you to marshal various structures in ASN.1 DER
21 format, unmarshal them in BER/CER/DER ones.
25 >>> Integer().decode(raw) == i
28 There are primitive types, holding single values
29 (:py:class:`pyderasn.BitString`,
30 :py:class:`pyderasn.Boolean`,
31 :py:class:`pyderasn.Enumerated`,
32 :py:class:`pyderasn.GeneralizedTime`,
33 :py:class:`pyderasn.Integer`,
34 :py:class:`pyderasn.Null`,
35 :py:class:`pyderasn.ObjectIdentifier`,
36 :py:class:`pyderasn.OctetString`,
37 :py:class:`pyderasn.UTCTime`,
38 :py:class:`various strings <pyderasn.CommonString>`
39 (:py:class:`pyderasn.BMPString`,
40 :py:class:`pyderasn.GeneralString`,
41 :py:class:`pyderasn.GraphicString`,
42 :py:class:`pyderasn.IA5String`,
43 :py:class:`pyderasn.ISO646String`,
44 :py:class:`pyderasn.NumericString`,
45 :py:class:`pyderasn.PrintableString`,
46 :py:class:`pyderasn.T61String`,
47 :py:class:`pyderasn.TeletexString`,
48 :py:class:`pyderasn.UniversalString`,
49 :py:class:`pyderasn.UTF8String`,
50 :py:class:`pyderasn.VideotexString`,
51 :py:class:`pyderasn.VisibleString`)),
52 constructed types, holding multiple primitive types
53 (:py:class:`pyderasn.Sequence`,
54 :py:class:`pyderasn.SequenceOf`,
55 :py:class:`pyderasn.Set`,
56 :py:class:`pyderasn.SetOf`),
57 and special types like
58 :py:class:`pyderasn.Any` and
59 :py:class:`pyderasn.Choice`.
67 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
68 the default tag used during coding process. You can override it with
69 either ``IMPLICIT`` (using ``impl`` keyword argument), or
70 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
71 raw binary string, containing that tag. You can **not** set implicit and
72 explicit tags simultaneously.
74 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
75 functions, allowing you to easily create ``CONTEXT``
76 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
77 number. Pay attention that explicit tags always have *constructed* tag
78 (``tag_ctxc``), but implicit tags for primitive types are primitive
83 >>> Integer(impl=tag_ctxp(1))
85 >>> Integer(expl=tag_ctxc(2))
88 Implicit tag is not explicitly shown.
90 Two objects of the same type, but with different implicit/explicit tags
93 You can get object's effective tag (either default or implicited) through
94 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
97 >>> tag_decode(tag_ctxc(123))
99 >>> klass, form, num = tag_decode(tag_ctxc(123))
100 >>> klass == TagClassContext
102 >>> form == TagFormConstructed
105 To determine if object has explicit tag, use ``expled`` boolean property
106 and ``expl_tag`` property, returning explicit tag's value.
111 Many objects in sequences could be ``OPTIONAL`` and could have
112 ``DEFAULT`` value. You can specify that object's property using
113 corresponding keyword arguments.
115 >>> Integer(optional=True, default=123)
116 INTEGER 123 OPTIONAL DEFAULT
118 Those specifications do not play any role in primitive value encoding,
119 but are taken into account when dealing with sequences holding them. For
120 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
123 class Version(Integer):
129 class TBSCertificate(Sequence):
131 ("version", Version(expl=tag_ctxc(0), default="v1")),
134 When default argument is used and value is not specified, then it equals
142 Some objects give ability to set value size constraints. This is either
143 possible integer value, or allowed length of various strings and
144 sequences. Constraints are set in the following way::
149 And values satisfaction is checked as: ``MIN <= X <= MAX``.
151 For simplicity you can also set bounds the following way::
153 bounded_x = X(bounds=(MIN, MAX))
155 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
161 All objects have ``ready`` boolean property, that tells if object is
162 ready to be encoded. If that kind of action is performed on unready
163 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
165 All objects have ``copy()`` method, that returns their copy, that can be
173 Decoding is performed using ``decode()`` method. ``offset`` optional
174 argument could be used to set initial object's offset in the binary
175 data, for convenience. It returns decoded object and remaining
176 unmarshalled data (tail). Internally all work is done on
177 ``memoryview(data)``, and you can leave returning tail as a memoryview,
178 by specifying ``leavemm=True`` argument.
180 When object is decoded, ``decoded`` property is true and you can safely
181 use following properties:
183 * ``offset`` -- position including initial offset where object's tag starts
184 * ``tlen`` -- length of object's tag
185 * ``llen`` -- length of object's length value
186 * ``vlen`` -- length of object's value
187 * ``tlvlen`` -- length of the whole object
189 Pay attention that those values do **not** include anything related to
190 explicit tag. If you want to know information about it, then use:
192 * ``expled`` -- to know if explicit tag is set
193 * ``expl_offset`` (it is lesser than ``offset``)
196 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
197 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
199 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
202 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
209 You can specify so called context keyword argument during ``decode()``
210 invocation. It is dictionary containing various options governing
213 Currently available context options:
215 * :ref:`allow_default_values <allow_default_values_ctx>`
216 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
217 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
218 * :ref:`bered <bered_ctx>`
219 * :ref:`defines_by_path <defines_by_path_ctx>`
226 All objects have ``pps()`` method, that is a generator of
227 :py:class:`pyderasn.PP` namedtuple, holding various raw information
228 about the object. If ``pps`` is called on sequences, then all underlying
229 ``PP`` will be yielded.
231 You can use :py:func:`pyderasn.pp_console_row` function, converting
232 those ``PP`` to human readable string. Actually exactly it is used for
233 all object ``repr``. But it is easy to write custom formatters.
235 >>> from pyderasn import pprint
236 >>> encoded = Integer(-12345).encode()
237 >>> obj, tail = Integer().decode(encoded)
238 >>> print(pprint(obj))
239 0 [1,1, 2] INTEGER -12345
243 Example certificate::
245 >>> print(pprint(crt))
246 0 [1,3,1604] Certificate SEQUENCE
247 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE
248 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
249 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595
250 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
251 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
252 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
254 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
255 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF
256 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
257 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
258 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
259 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
260 . . . . . . . 13:02:45:53
262 1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
263 1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
264 1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
266 1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits
267 . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
268 . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
273 Let's parse that output, human::
275 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
276 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
277 0 1 2 3 4 5 6 7 8 9 10 11
281 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
287 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
293 52-2∞ B [1,1,1054]∞ . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
298 Offset of the object, where its DER/BER encoding begins.
299 Pay attention that it does **not** include explicit tag.
301 If explicit tag exists, then this is its length (tag + encoded length).
303 Length of object's tag. For example CHOICE does not have its own tag,
306 Length of encoded length.
308 Length of encoded value.
310 Visual indentation to show the depth of object in the hierarchy.
312 Object's name inside SEQUENCE/CHOICE.
314 If either IMPLICIT or EXPLICIT tag is set, then it will be shown
315 here. "IMPLICIT" is omitted.
317 Object's class name, if set. Omitted if it is just an ordinary simple
318 value (like with ``algorithm`` in example above).
322 Object's value, if set. Can consist of multiple words (like OCTET/BIT
323 STRINGs above). We see ``v3`` value in Version, because it is named.
324 ``rdnSequence`` is the choice of CHOICE type.
326 Possible other flags like OPTIONAL and DEFAULT, if value equals to the
327 default one, specified in the schema.
329 Shows does object contains any kind of BER encoded data (possibly
330 Sequence holding BER-encoded underlying value).
332 Only applicable to BER encoded data. Indefinite length encoding mark.
334 Only applicable to BER encoded data. If object has BER-specific
335 encoding, then ``BER`` will be shown. It does not depend on indefinite
336 length encoding. ``EOC``, ``BOOLEAN``, ``BIT STRING``, ``OCTET STRING``
337 (and its derivatives), ``SET``, ``SET OF`` could be BERed.
345 ASN.1 structures often have ANY and OCTET STRING fields, that are
346 DEFINED BY some previously met ObjectIdentifier. This library provides
347 ability to specify mapping between some OID and field that must be
348 decoded with specific specification.
353 :py:class:`pyderasn.ObjectIdentifier` field inside
354 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
355 necessary for decoding structures. For example, CMS (:rfc:`5652`)
358 class ContentInfo(Sequence):
360 ("contentType", ContentType(defines=((("content",), {
361 id_digestedData: DigestedData(),
362 id_signedData: SignedData(),
364 ("content", Any(expl=tag_ctxc(0))),
367 ``contentType`` field tells that it defines that ``content`` must be
368 decoded with ``SignedData`` specification, if ``contentType`` equals to
369 ``id-signedData``. The same applies to ``DigestedData``. If
370 ``contentType`` contains unknown OID, then no automatic decoding is
373 You can specify multiple fields, that will be autodecoded -- that is why
374 ``defines`` kwarg is a sequence. You can specify defined field
375 relatively or absolutely to current decode path. For example ``defines``
376 for AlgorithmIdentifier of X.509's
377 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
381 id_ecPublicKey: ECParameters(),
382 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
384 (("..", "subjectPublicKey"), {
385 id_rsaEncryption: RSAPublicKey(),
386 id_GostR3410_2001: OctetString(),
390 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
391 autodecode its parameters inside SPKI's algorithm and its public key
394 Following types can be automatically decoded (DEFINED BY):
396 * :py:class:`pyderasn.Any`
397 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
398 * :py:class:`pyderasn.OctetString`
399 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
400 ``Any``/``BitString``/``OctetString``-s
402 When any of those fields is automatically decoded, then ``.defined``
403 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
404 was defined, ``value`` contains corresponding decoded value. For example
405 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
407 .. _defines_by_path_ctx:
409 defines_by_path context option
410 ______________________________
412 Sometimes you either can not or do not want to explicitly set *defines*
413 in the scheme. You can dynamically apply those definitions when calling
414 ``.decode()`` method.
416 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
417 value must be sequence of following tuples::
419 (decode_path, defines)
421 where ``decode_path`` is a tuple holding so-called decode path to the
422 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
423 ``defines``, holding exactly the same value as accepted in its keyword
426 For example, again for CMS, you want to automatically decode
427 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
428 structures it may hold. Also, automatically decode ``controlSequence``
431 content_info, tail = ContentInfo().decode(data, ctx={"defines_by_path": (
434 ((("content",), {id_signedData: SignedData()}),),
439 DecodePathDefBy(id_signedData),
444 id_cct_PKIData: PKIData(),
445 id_cct_PKIResponse: PKIResponse(),
451 DecodePathDefBy(id_signedData),
454 DecodePathDefBy(id_cct_PKIResponse),
460 id_cmc_recipientNonce: RecipientNonce(),
461 id_cmc_senderNonce: SenderNonce(),
462 id_cmc_statusInfoV2: CMCStatusInfoV2(),
463 id_cmc_transactionId: TransactionId(),
468 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
469 First function is useful for path construction when some automatic
470 decoding is already done. ``any`` means literally any value it meet --
471 useful for SEQUENCE/SET OF-s.
478 By default PyDERASN accepts only DER encoded data. It always encodes to
479 DER. But you can optionally enable BER decoding with setting ``bered``
480 :ref:`context <ctx>` argument to True. Indefinite lengths and
481 constructed primitive types should be parsed successfully.
483 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
484 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
485 STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``
487 * If object has an indefinite length encoding, then its ``lenindef``
488 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
489 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
491 * If object has an indefinite length encoded explicit tag, then
492 ``expl_lenindef`` is set to True.
493 * If object has either any of BER-related encoding (explicit tag
494 indefinite length, object's indefinite length, BER-encoding) or any
495 underlying component has that kind of encoding, then ``bered``
496 attribute is set to True. For example SignedData CMS can have
497 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
498 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
500 EOC (end-of-contents) token's length is taken in advance in object's
503 .. _allow_expl_oob_ctx:
505 Allow explicit tag out-of-bound
506 -------------------------------
508 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
509 one value, more than one object. If you set ``allow_expl_oob`` context
510 option to True, then no error will be raised and that invalid encoding
511 will be silently further processed. But pay attention that offsets and
512 lengths will be invalid in that case.
516 This option should be used only for skipping some decode errors, just
517 to see the decoded structure somehow.
524 .. autoclass:: pyderasn.Boolean
529 .. autoclass:: pyderasn.Integer
534 .. autoclass:: pyderasn.BitString
539 .. autoclass:: pyderasn.OctetString
544 .. autoclass:: pyderasn.Null
549 .. autoclass:: pyderasn.ObjectIdentifier
554 .. autoclass:: pyderasn.Enumerated
558 .. autoclass:: pyderasn.CommonString
562 .. autoclass:: pyderasn.NumericString
566 .. autoclass:: pyderasn.PrintableString
570 .. autoclass:: pyderasn.UTCTime
571 :members: __init__, todatetime
575 .. autoclass:: pyderasn.GeneralizedTime
582 .. autoclass:: pyderasn.Choice
587 .. autoclass:: PrimitiveTypes
591 .. autoclass:: pyderasn.Any
599 .. autoclass:: pyderasn.Sequence
604 .. autoclass:: pyderasn.Set
609 .. autoclass:: pyderasn.SequenceOf
614 .. autoclass:: pyderasn.SetOf
620 .. autofunction:: pyderasn.abs_decode_path
621 .. autofunction:: pyderasn.colonize_hex
622 .. autofunction:: pyderasn.hexenc
623 .. autofunction:: pyderasn.hexdec
624 .. autofunction:: pyderasn.tag_encode
625 .. autofunction:: pyderasn.tag_decode
626 .. autofunction:: pyderasn.tag_ctxp
627 .. autofunction:: pyderasn.tag_ctxc
628 .. autoclass:: pyderasn.Obj
629 .. autoclass:: pyderasn.DecodeError
631 .. autoclass:: pyderasn.NotEnoughData
632 .. autoclass:: pyderasn.LenIndefForm
633 .. autoclass:: pyderasn.TagMismatch
634 .. autoclass:: pyderasn.InvalidLength
635 .. autoclass:: pyderasn.InvalidOID
636 .. autoclass:: pyderasn.ObjUnknown
637 .. autoclass:: pyderasn.ObjNotReady
638 .. autoclass:: pyderasn.InvalidValueType
639 .. autoclass:: pyderasn.BoundsError
642 from codecs import getdecoder
643 from codecs import getencoder
644 from collections import namedtuple
645 from collections import OrderedDict
646 from copy import copy
647 from datetime import datetime
648 from math import ceil
649 from os import environ
650 from string import ascii_letters
651 from string import digits
653 from six import add_metaclass
654 from six import binary_type
655 from six import byte2int
656 from six import indexbytes
657 from six import int2byte
658 from six import integer_types
659 from six import iterbytes
660 from six import iteritems
661 from six import itervalues
663 from six import string_types
664 from six import text_type
665 from six import unichr as six_unichr
666 from six.moves import xrange as six_xrange
670 from termcolor import colored
671 except ImportError: # pragma: no cover
672 def colored(what, *args, **kwargs):
716 "TagClassApplication",
720 "TagFormConstructed",
731 TagClassUniversal = 0
732 TagClassApplication = 1 << 6
733 TagClassContext = 1 << 7
734 TagClassPrivate = 1 << 6 | 1 << 7
736 TagFormConstructed = 1 << 5
739 TagClassApplication: "APPLICATION ",
740 TagClassPrivate: "PRIVATE ",
741 TagClassUniversal: "UNIV ",
745 LENINDEF = b"\x80" # length indefinite mark
746 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
749 ########################################################################
751 ########################################################################
753 class ASN1Error(ValueError):
757 class DecodeError(ASN1Error):
758 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
760 :param str msg: reason of decode failing
761 :param klass: optional exact DecodeError inherited class (like
762 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
763 :py:exc:`InvalidLength`)
764 :param decode_path: tuple of strings. It contains human
765 readable names of the fields through which
766 decoding process has passed
767 :param int offset: binary offset where failure happened
769 super(DecodeError, self).__init__()
772 self.decode_path = decode_path
778 "" if self.klass is None else self.klass.__name__,
780 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
781 if len(self.decode_path) > 0 else ""
783 ("(at %d)" % self.offset) if self.offset > 0 else "",
789 return "%s(%s)" % (self.__class__.__name__, self)
792 class NotEnoughData(DecodeError):
796 class LenIndefForm(DecodeError):
800 class TagMismatch(DecodeError):
804 class InvalidLength(DecodeError):
808 class InvalidOID(DecodeError):
812 class ObjUnknown(ASN1Error):
813 def __init__(self, name):
814 super(ObjUnknown, self).__init__()
818 return "object is unknown: %s" % self.name
821 return "%s(%s)" % (self.__class__.__name__, self)
824 class ObjNotReady(ASN1Error):
825 def __init__(self, name):
826 super(ObjNotReady, self).__init__()
830 return "object is not ready: %s" % self.name
833 return "%s(%s)" % (self.__class__.__name__, self)
836 class InvalidValueType(ASN1Error):
837 def __init__(self, expected_types):
838 super(InvalidValueType, self).__init__()
839 self.expected_types = expected_types
842 return "invalid value type, expected: %s" % ", ".join(
843 [repr(t) for t in self.expected_types]
847 return "%s(%s)" % (self.__class__.__name__, self)
850 class BoundsError(ASN1Error):
851 def __init__(self, bound_min, value, bound_max):
852 super(BoundsError, self).__init__()
853 self.bound_min = bound_min
855 self.bound_max = bound_max
858 return "unsatisfied bounds: %s <= %s <= %s" % (
865 return "%s(%s)" % (self.__class__.__name__, self)
868 ########################################################################
870 ########################################################################
872 _hexdecoder = getdecoder("hex")
873 _hexencoder = getencoder("hex")
877 """Binary data to hexadecimal string convert
879 return _hexdecoder(data)[0]
883 """Hexadecimal string to binary data convert
885 return _hexencoder(data)[0].decode("ascii")
888 def int_bytes_len(num, byte_len=8):
891 return int(ceil(float(num.bit_length()) / byte_len))
894 def zero_ended_encode(num):
895 octets = bytearray(int_bytes_len(num, 7))
897 octets[i] = num & 0x7F
901 octets[i] = 0x80 | (num & 0x7F)
907 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
908 """Encode tag to binary form
910 :param int num: tag's number
911 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
912 :py:data:`pyderasn.TagClassContext`,
913 :py:data:`pyderasn.TagClassApplication`,
914 :py:data:`pyderasn.TagClassPrivate`)
915 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
916 :py:data:`pyderasn.TagFormConstructed`)
920 return int2byte(klass | form | num)
921 # [XX|X|11111][1.......][1.......] ... [0.......]
922 return int2byte(klass | form | 31) + zero_ended_encode(num)
926 """Decode tag from binary form
930 No validation is performed, assuming that it has already passed.
932 It returns tuple with three integers, as
933 :py:func:`pyderasn.tag_encode` accepts.
935 first_octet = byte2int(tag)
936 klass = first_octet & 0xC0
937 form = first_octet & 0x20
938 if first_octet & 0x1F < 0x1F:
939 return (klass, form, first_octet & 0x1F)
941 for octet in iterbytes(tag[1:]):
944 return (klass, form, num)
948 """Create CONTEXT PRIMITIVE tag
950 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
954 """Create CONTEXT CONSTRUCTED tag
956 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
960 """Take off tag from the data
962 :returns: (encoded tag, tag length, remaining data)
965 raise NotEnoughData("no data at all")
966 if byte2int(data) & 0x1F < 31:
967 return data[:1], 1, data[1:]
972 raise DecodeError("unfinished tag")
973 if indexbytes(data, i) & 0x80 == 0:
976 return data[:i], i, data[i:]
982 octets = bytearray(int_bytes_len(l) + 1)
983 octets[0] = 0x80 | (len(octets) - 1)
984 for i in six_xrange(len(octets) - 1, 0, -1):
990 def len_decode(data):
993 :returns: (decoded length, length's length, remaining data)
994 :raises LenIndefForm: if indefinite form encoding is met
997 raise NotEnoughData("no data at all")
998 first_octet = byte2int(data)
999 if first_octet & 0x80 == 0:
1000 return first_octet, 1, data[1:]
1001 octets_num = first_octet & 0x7F
1002 if octets_num + 1 > len(data):
1003 raise NotEnoughData("encoded length is longer than data")
1005 raise LenIndefForm()
1006 if byte2int(data[1:]) == 0:
1007 raise DecodeError("leading zeros")
1009 for v in iterbytes(data[1:1 + octets_num]):
1012 raise DecodeError("long form instead of short one")
1013 return l, 1 + octets_num, data[1 + octets_num:]
1016 ########################################################################
1018 ########################################################################
1020 class AutoAddSlots(type):
1021 def __new__(mcs, name, bases, _dict):
1022 _dict["__slots__"] = _dict.get("__slots__", ())
1023 return type.__new__(mcs, name, bases, _dict)
1026 @add_metaclass(AutoAddSlots)
1028 """Common ASN.1 object class
1030 All ASN.1 types are inherited from it. It has metaclass that
1031 automatically adds ``__slots__`` to all inherited classes.
1055 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1056 self._expl = getattr(self, "expl", None) if expl is None else expl
1057 if self.tag != self.tag_default and self._expl is not None:
1058 raise ValueError("implicit and explicit tags can not be set simultaneously")
1059 if default is not None:
1061 self.optional = optional
1062 self.offset, self.llen, self.vlen = _decoded
1064 self.expl_lenindef = False
1065 self.lenindef = False
1066 self.ber_encoded = False
1069 def ready(self): # pragma: no cover
1070 """Is object ready to be encoded?
1072 raise NotImplementedError()
1074 def _assert_ready(self):
1076 raise ObjNotReady(self.__class__.__name__)
1080 """Is either object or any elements inside is BER encoded?
1082 return self.expl_lenindef or self.lenindef or self.ber_encoded
1086 """Is object decoded?
1088 return (self.llen + self.vlen) > 0
1090 def copy(self): # pragma: no cover
1091 """Make a copy of object, safe to be mutated
1093 raise NotImplementedError()
1097 return len(self.tag)
1101 return self.tlen + self.llen + self.vlen
1103 def __str__(self): # pragma: no cover
1104 return self.__bytes__() if PY2 else self.__unicode__()
1106 def __ne__(self, their):
1107 return not(self == their)
1109 def __gt__(self, their): # pragma: no cover
1110 return not(self < their)
1112 def __le__(self, their): # pragma: no cover
1113 return (self == their) or (self < their)
1115 def __ge__(self, their): # pragma: no cover
1116 return (self == their) or (self > their)
1118 def _encode(self): # pragma: no cover
1119 raise NotImplementedError()
1121 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1122 raise NotImplementedError()
1125 raw = self._encode()
1126 if self._expl is None:
1128 return b"".join((self._expl, len_encode(len(raw)), raw))
1138 _ctx_immutable=True,
1142 :param data: either binary or memoryview
1143 :param int offset: initial data's offset
1144 :param bool leavemm: do we need to leave memoryview of remaining
1145 data as is, or convert it to bytes otherwise
1146 :param ctx: optional :ref:`context <ctx>` governing decoding process
1147 :param tag_only: decode only the tag, without length and contents
1148 (used only in Choice and Set structures, trying to
1149 determine if tag satisfies the scheme)
1150 :param _ctx_immutable: do we need to copy ``ctx`` before using it
1151 :returns: (Obj, remaining data)
1155 elif _ctx_immutable:
1157 tlv = memoryview(data)
1158 if self._expl is None:
1159 result = self._decode(
1162 decode_path=decode_path,
1171 t, tlen, lv = tag_strip(tlv)
1172 except DecodeError as err:
1173 raise err.__class__(
1175 klass=self.__class__,
1176 decode_path=decode_path,
1181 klass=self.__class__,
1182 decode_path=decode_path,
1186 l, llen, v = len_decode(lv)
1187 except LenIndefForm as err:
1188 if not ctx.get("bered", False):
1189 raise err.__class__(
1191 klass=self.__class__,
1192 decode_path=decode_path,
1196 offset += tlen + llen
1197 result = self._decode(
1200 decode_path=decode_path,
1204 if tag_only: # pragma: no cover
1207 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1208 if eoc_expected.tobytes() != EOC:
1211 klass=self.__class__,
1212 decode_path=decode_path,
1216 obj.expl_lenindef = True
1217 except DecodeError as err:
1218 raise err.__class__(
1220 klass=self.__class__,
1221 decode_path=decode_path,
1226 raise NotEnoughData(
1227 "encoded length is longer than data",
1228 klass=self.__class__,
1229 decode_path=decode_path,
1232 result = self._decode(
1234 offset=offset + tlen + llen,
1235 decode_path=decode_path,
1239 if tag_only: # pragma: no cover
1242 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1244 "explicit tag out-of-bound, longer than data",
1245 klass=self.__class__,
1246 decode_path=decode_path,
1249 return obj, (tail if leavemm else tail.tobytes())
1253 return self._expl is not None
1260 def expl_tlen(self):
1261 return len(self._expl)
1264 def expl_llen(self):
1265 if self.expl_lenindef:
1267 return len(len_encode(self.tlvlen))
1270 def expl_offset(self):
1271 return self.offset - self.expl_tlen - self.expl_llen
1274 def expl_vlen(self):
1278 def expl_tlvlen(self):
1279 return self.expl_tlen + self.expl_llen + self.expl_vlen
1282 def fulloffset(self):
1283 return self.expl_offset if self.expled else self.offset
1287 return self.expl_tlvlen if self.expled else self.tlvlen
1289 def pps_lenindef(self, decode_path):
1290 if self.lenindef and not (
1291 getattr(self, "defined", None) is not None and
1292 self.defined[1].lenindef
1295 asn1_type_name="EOC",
1297 decode_path=decode_path,
1299 self.offset + self.tlvlen -
1300 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1308 if self.expl_lenindef:
1310 asn1_type_name="EOC",
1311 obj_name="EXPLICIT",
1312 decode_path=decode_path,
1313 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1322 class DecodePathDefBy(object):
1323 """DEFINED BY representation inside decode path
1325 __slots__ = ("defined_by",)
1327 def __init__(self, defined_by):
1328 self.defined_by = defined_by
1330 def __ne__(self, their):
1331 return not(self == their)
1333 def __eq__(self, their):
1334 if not isinstance(their, self.__class__):
1336 return self.defined_by == their.defined_by
1339 return "DEFINED BY " + str(self.defined_by)
1342 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1345 ########################################################################
1347 ########################################################################
1349 PP = namedtuple("PP", (
1377 asn1_type_name="unknown",
1394 expl_lenindef=False,
1425 def _colourize(what, colour, with_colours, attrs=("bold",)):
1426 return colored(what, colour, attrs=attrs) if with_colours else what
1429 def colonize_hex(hexed):
1430 """Separate hexadecimal string with colons
1432 return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
1441 with_decode_path=False,
1442 decode_path_len_decrease=0,
1449 " " if pp.expl_offset is None else
1450 ("-%d" % (pp.offset - pp.expl_offset))
1452 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1454 col = _colourize(col, "red", with_colours, ())
1455 col += _colourize("B", "red", with_colours) if pp.bered else " "
1457 col = "[%d,%d,%4d]%s" % (
1461 LENINDEF_PP_CHAR if pp.lenindef else " "
1463 col = _colourize(col, "green", with_colours, ())
1465 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1466 if decode_path_len > 0:
1467 cols.append(" ." * decode_path_len)
1468 ent = pp.decode_path[-1]
1469 if isinstance(ent, DecodePathDefBy):
1470 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1471 value = str(ent.defined_by)
1474 len(oid_maps) > 0 and
1475 ent.defined_by.asn1_type_name ==
1476 ObjectIdentifier.asn1_type_name
1478 for oid_map in oid_maps:
1479 oid_name = oid_map.get(value)
1480 if oid_name is not None:
1481 cols.append(_colourize("%s:" % oid_name, "green", with_colours))
1483 if oid_name is None:
1484 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1486 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1487 if pp.expl is not None:
1488 klass, _, num = pp.expl
1489 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1490 cols.append(_colourize(col, "blue", with_colours))
1491 if pp.impl is not None:
1492 klass, _, num = pp.impl
1493 col = "[%s%d]" % (TagClassReprs[klass], num)
1494 cols.append(_colourize(col, "blue", with_colours))
1495 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1496 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1498 cols.append(_colourize("BER", "red", with_colours))
1499 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1500 if pp.value is not None:
1502 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1504 len(oid_maps) > 0 and
1505 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
1507 for oid_map in oid_maps:
1508 oid_name = oid_map.get(value)
1509 if oid_name is not None:
1510 cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
1512 if pp.asn1_type_name == Integer.asn1_type_name:
1513 hex_repr = hex(int(pp.obj._value))[2:].upper()
1514 if len(hex_repr) % 2 != 0:
1515 hex_repr = "0" + hex_repr
1516 cols.append(_colourize(
1517 "(%s)" % colonize_hex(hex_repr),
1522 if isinstance(pp.blob, binary_type):
1523 cols.append(hexenc(pp.blob))
1524 elif isinstance(pp.blob, tuple):
1525 cols.append(", ".join(pp.blob))
1527 cols.append(_colourize("OPTIONAL", "red", with_colours))
1529 cols.append(_colourize("DEFAULT", "red", with_colours))
1530 if with_decode_path:
1531 cols.append(_colourize(
1532 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1536 return " ".join(cols)
1539 def pp_console_blob(pp, decode_path_len_decrease=0):
1540 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1541 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1542 if decode_path_len > 0:
1543 cols.append(" ." * (decode_path_len + 1))
1544 if isinstance(pp.blob, binary_type):
1545 blob = hexenc(pp.blob).upper()
1546 for i in six_xrange(0, len(blob), 32):
1547 chunk = blob[i:i + 32]
1548 yield " ".join(cols + [colonize_hex(chunk)])
1549 elif isinstance(pp.blob, tuple):
1550 yield " ".join(cols + [", ".join(pp.blob)])
1558 with_decode_path=False,
1559 decode_path_only=(),
1561 """Pretty print object
1563 :param Obj obj: object you want to pretty print
1564 :param oid_maps: list of ``OID <-> humand readable string`` dictionary.
1565 When OID from it is met, then its humand readable form
1567 :param big_blobs: if large binary objects are met (like OctetString
1568 values), do we need to print them too, on separate
1570 :param with_colours: colourize output, if ``termcolor`` library
1572 :param with_decode_path: print decode path
1573 :param decode_path_only: print only that specified decode path
1575 def _pprint_pps(pps):
1577 if hasattr(pp, "_fields"):
1579 decode_path_only != () and
1581 str(p) for p in pp.decode_path[:len(decode_path_only)]
1582 ) != decode_path_only
1586 yield pp_console_row(
1591 with_colours=with_colours,
1592 with_decode_path=with_decode_path,
1593 decode_path_len_decrease=len(decode_path_only),
1595 for row in pp_console_blob(
1597 decode_path_len_decrease=len(decode_path_only),
1601 yield pp_console_row(
1606 with_colours=with_colours,
1607 with_decode_path=with_decode_path,
1608 decode_path_len_decrease=len(decode_path_only),
1611 for row in _pprint_pps(pp):
1613 return "\n".join(_pprint_pps(obj.pps()))
1616 ########################################################################
1617 # ASN.1 primitive types
1618 ########################################################################
1621 """``BOOLEAN`` boolean type
1623 >>> b = Boolean(True)
1625 >>> b == Boolean(True)
1631 tag_default = tag_encode(1)
1632 asn1_type_name = "BOOLEAN"
1644 :param value: set the value. Either boolean type, or
1645 :py:class:`pyderasn.Boolean` object
1646 :param bytes impl: override default tag with ``IMPLICIT`` one
1647 :param bytes expl: override default tag with ``EXPLICIT`` one
1648 :param default: set default value. Type same as in ``value``
1649 :param bool optional: is object ``OPTIONAL`` in sequence
1651 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1652 self._value = None if value is None else self._value_sanitize(value)
1653 if default is not None:
1654 default = self._value_sanitize(default)
1655 self.default = self.__class__(
1661 self._value = default
1663 def _value_sanitize(self, value):
1664 if isinstance(value, bool):
1666 if issubclass(value.__class__, Boolean):
1668 raise InvalidValueType((self.__class__, bool))
1672 return self._value is not None
1675 obj = self.__class__()
1676 obj._value = self._value
1678 obj._expl = self._expl
1679 obj.default = self.default
1680 obj.optional = self.optional
1681 obj.offset = self.offset
1682 obj.llen = self.llen
1683 obj.vlen = self.vlen
1684 obj.expl_lenindef = self.expl_lenindef
1685 obj.lenindef = self.lenindef
1686 obj.ber_encoded = self.ber_encoded
1689 def __nonzero__(self):
1690 self._assert_ready()
1694 self._assert_ready()
1697 def __eq__(self, their):
1698 if isinstance(their, bool):
1699 return self._value == their
1700 if not issubclass(their.__class__, Boolean):
1703 self._value == their._value and
1704 self.tag == their.tag and
1705 self._expl == their._expl
1716 return self.__class__(
1718 impl=self.tag if impl is None else impl,
1719 expl=self._expl if expl is None else expl,
1720 default=self.default if default is None else default,
1721 optional=self.optional if optional is None else optional,
1725 self._assert_ready()
1729 (b"\xFF" if self._value else b"\x00"),
1732 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1734 t, _, lv = tag_strip(tlv)
1735 except DecodeError as err:
1736 raise err.__class__(
1738 klass=self.__class__,
1739 decode_path=decode_path,
1744 klass=self.__class__,
1745 decode_path=decode_path,
1751 l, _, v = len_decode(lv)
1752 except DecodeError as err:
1753 raise err.__class__(
1755 klass=self.__class__,
1756 decode_path=decode_path,
1760 raise InvalidLength(
1761 "Boolean's length must be equal to 1",
1762 klass=self.__class__,
1763 decode_path=decode_path,
1767 raise NotEnoughData(
1768 "encoded length is longer than data",
1769 klass=self.__class__,
1770 decode_path=decode_path,
1773 first_octet = byte2int(v)
1775 if first_octet == 0:
1777 elif first_octet == 0xFF:
1779 elif ctx.get("bered", False):
1784 "unacceptable Boolean value",
1785 klass=self.__class__,
1786 decode_path=decode_path,
1789 obj = self.__class__(
1793 default=self.default,
1794 optional=self.optional,
1795 _decoded=(offset, 1, 1),
1797 obj.ber_encoded = ber_encoded
1801 return pp_console_row(next(self.pps()))
1803 def pps(self, decode_path=()):
1806 asn1_type_name=self.asn1_type_name,
1807 obj_name=self.__class__.__name__,
1808 decode_path=decode_path,
1809 value=str(self._value) if self.ready else None,
1810 optional=self.optional,
1811 default=self == self.default,
1812 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1813 expl=None if self._expl is None else tag_decode(self._expl),
1818 expl_offset=self.expl_offset if self.expled else None,
1819 expl_tlen=self.expl_tlen if self.expled else None,
1820 expl_llen=self.expl_llen if self.expled else None,
1821 expl_vlen=self.expl_vlen if self.expled else None,
1822 expl_lenindef=self.expl_lenindef,
1823 ber_encoded=self.ber_encoded,
1826 for pp in self.pps_lenindef(decode_path):
1831 """``INTEGER`` integer type
1833 >>> b = Integer(-123)
1835 >>> b == Integer(-123)
1840 >>> Integer(2, bounds=(1, 3))
1842 >>> Integer(5, bounds=(1, 3))
1843 Traceback (most recent call last):
1844 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1848 class Version(Integer):
1855 >>> v = Version("v1")
1862 {'v3': 2, 'v1': 0, 'v2': 1}
1864 __slots__ = ("specs", "_bound_min", "_bound_max")
1865 tag_default = tag_encode(2)
1866 asn1_type_name = "INTEGER"
1880 :param value: set the value. Either integer type, named value
1881 (if ``schema`` is specified in the class), or
1882 :py:class:`pyderasn.Integer` object
1883 :param bounds: set ``(MIN, MAX)`` value constraint.
1884 (-inf, +inf) by default
1885 :param bytes impl: override default tag with ``IMPLICIT`` one
1886 :param bytes expl: override default tag with ``EXPLICIT`` one
1887 :param default: set default value. Type same as in ``value``
1888 :param bool optional: is object ``OPTIONAL`` in sequence
1890 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1892 specs = getattr(self, "schema", {}) if _specs is None else _specs
1893 self.specs = specs if isinstance(specs, dict) else dict(specs)
1894 self._bound_min, self._bound_max = getattr(
1897 (float("-inf"), float("+inf")),
1898 ) if bounds is None else bounds
1899 if value is not None:
1900 self._value = self._value_sanitize(value)
1901 if default is not None:
1902 default = self._value_sanitize(default)
1903 self.default = self.__class__(
1909 if self._value is None:
1910 self._value = default
1912 def _value_sanitize(self, value):
1913 if isinstance(value, integer_types):
1915 elif issubclass(value.__class__, Integer):
1916 value = value._value
1917 elif isinstance(value, str):
1918 value = self.specs.get(value)
1920 raise ObjUnknown("integer value: %s" % value)
1922 raise InvalidValueType((self.__class__, int, str))
1923 if not self._bound_min <= value <= self._bound_max:
1924 raise BoundsError(self._bound_min, value, self._bound_max)
1929 return self._value is not None
1932 obj = self.__class__(_specs=self.specs)
1933 obj._value = self._value
1934 obj._bound_min = self._bound_min
1935 obj._bound_max = self._bound_max
1937 obj._expl = self._expl
1938 obj.default = self.default
1939 obj.optional = self.optional
1940 obj.offset = self.offset
1941 obj.llen = self.llen
1942 obj.vlen = self.vlen
1943 obj.expl_lenindef = self.expl_lenindef
1944 obj.lenindef = self.lenindef
1945 obj.ber_encoded = self.ber_encoded
1949 self._assert_ready()
1950 return int(self._value)
1953 self._assert_ready()
1956 bytes(self._expl or b"") +
1957 str(self._value).encode("ascii"),
1960 def __eq__(self, their):
1961 if isinstance(their, integer_types):
1962 return self._value == their
1963 if not issubclass(their.__class__, Integer):
1966 self._value == their._value and
1967 self.tag == their.tag and
1968 self._expl == their._expl
1971 def __lt__(self, their):
1972 return self._value < their._value
1976 for name, value in iteritems(self.specs):
1977 if value == self._value:
1989 return self.__class__(
1992 (self._bound_min, self._bound_max)
1993 if bounds is None else bounds
1995 impl=self.tag if impl is None else impl,
1996 expl=self._expl if expl is None else expl,
1997 default=self.default if default is None else default,
1998 optional=self.optional if optional is None else optional,
2003 self._assert_ready()
2007 octets = bytearray([0])
2011 octets = bytearray()
2013 octets.append((value & 0xFF) ^ 0xFF)
2015 if len(octets) == 0 or octets[-1] & 0x80 == 0:
2018 octets = bytearray()
2020 octets.append(value & 0xFF)
2022 if octets[-1] & 0x80 > 0:
2025 octets = bytes(octets)
2027 bytes_len = ceil(value.bit_length() / 8) or 1
2030 octets = value.to_bytes(
2035 except OverflowError:
2039 return b"".join((self.tag, len_encode(len(octets)), octets))
2041 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2043 t, _, lv = tag_strip(tlv)
2044 except DecodeError as err:
2045 raise err.__class__(
2047 klass=self.__class__,
2048 decode_path=decode_path,
2053 klass=self.__class__,
2054 decode_path=decode_path,
2060 l, llen, v = len_decode(lv)
2061 except DecodeError as err:
2062 raise err.__class__(
2064 klass=self.__class__,
2065 decode_path=decode_path,
2069 raise NotEnoughData(
2070 "encoded length is longer than data",
2071 klass=self.__class__,
2072 decode_path=decode_path,
2076 raise NotEnoughData(
2078 klass=self.__class__,
2079 decode_path=decode_path,
2082 v, tail = v[:l], v[l:]
2083 first_octet = byte2int(v)
2085 second_octet = byte2int(v[1:])
2087 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2088 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2091 "non normalized integer",
2092 klass=self.__class__,
2093 decode_path=decode_path,
2098 if first_octet & 0x80 > 0:
2099 octets = bytearray()
2100 for octet in bytearray(v):
2101 octets.append(octet ^ 0xFF)
2102 for octet in octets:
2103 value = (value << 8) | octet
2107 for octet in bytearray(v):
2108 value = (value << 8) | octet
2110 value = int.from_bytes(v, byteorder="big", signed=True)
2112 obj = self.__class__(
2114 bounds=(self._bound_min, self._bound_max),
2117 default=self.default,
2118 optional=self.optional,
2120 _decoded=(offset, llen, l),
2122 except BoundsError as err:
2125 klass=self.__class__,
2126 decode_path=decode_path,
2132 return pp_console_row(next(self.pps()))
2134 def pps(self, decode_path=()):
2137 asn1_type_name=self.asn1_type_name,
2138 obj_name=self.__class__.__name__,
2139 decode_path=decode_path,
2140 value=(self.named or str(self._value)) if self.ready else None,
2141 optional=self.optional,
2142 default=self == self.default,
2143 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2144 expl=None if self._expl is None else tag_decode(self._expl),
2149 expl_offset=self.expl_offset if self.expled else None,
2150 expl_tlen=self.expl_tlen if self.expled else None,
2151 expl_llen=self.expl_llen if self.expled else None,
2152 expl_vlen=self.expl_vlen if self.expled else None,
2153 expl_lenindef=self.expl_lenindef,
2156 for pp in self.pps_lenindef(decode_path):
2160 SET01 = frozenset(("0", "1"))
2163 class BitString(Obj):
2164 """``BIT STRING`` bit string type
2166 >>> BitString(b"hello world")
2167 BIT STRING 88 bits 68656c6c6f20776f726c64
2170 >>> b == b"hello world"
2175 >>> BitString("'0A3B5F291CD'H")
2176 BIT STRING 44 bits 0a3b5f291cd0
2177 >>> b = BitString("'010110000000'B")
2178 BIT STRING 12 bits 5800
2181 >>> b[0], b[1], b[2], b[3]
2182 (False, True, False, True)
2186 [False, True, False, True, True, False, False, False, False, False, False, False]
2190 class KeyUsage(BitString):
2192 ("digitalSignature", 0),
2193 ("nonRepudiation", 1),
2194 ("keyEncipherment", 2),
2197 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2198 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2200 ['nonRepudiation', 'keyEncipherment']
2202 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2206 Pay attention that BIT STRING can be encoded both in primitive
2207 and constructed forms. Decoder always checks constructed form tag
2208 additionally to specified primitive one. If BER decoding is
2209 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2210 of DER restrictions.
2212 __slots__ = ("tag_constructed", "specs", "defined")
2213 tag_default = tag_encode(3)
2214 asn1_type_name = "BIT STRING"
2227 :param value: set the value. Either binary type, tuple of named
2228 values (if ``schema`` is specified in the class),
2229 string in ``'XXX...'B`` form, or
2230 :py:class:`pyderasn.BitString` object
2231 :param bytes impl: override default tag with ``IMPLICIT`` one
2232 :param bytes expl: override default tag with ``EXPLICIT`` one
2233 :param default: set default value. Type same as in ``value``
2234 :param bool optional: is object ``OPTIONAL`` in sequence
2236 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2237 specs = getattr(self, "schema", {}) if _specs is None else _specs
2238 self.specs = specs if isinstance(specs, dict) else dict(specs)
2239 self._value = None if value is None else self._value_sanitize(value)
2240 if default is not None:
2241 default = self._value_sanitize(default)
2242 self.default = self.__class__(
2248 self._value = default
2250 tag_klass, _, tag_num = tag_decode(self.tag)
2251 self.tag_constructed = tag_encode(
2253 form=TagFormConstructed,
2257 def _bits2octets(self, bits):
2258 if len(self.specs) > 0:
2259 bits = bits.rstrip("0")
2261 bits += "0" * ((8 - (bit_len % 8)) % 8)
2262 octets = bytearray(len(bits) // 8)
2263 for i in six_xrange(len(octets)):
2264 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2265 return bit_len, bytes(octets)
2267 def _value_sanitize(self, value):
2268 if isinstance(value, (string_types, binary_type)):
2270 isinstance(value, string_types) and
2271 value.startswith("'")
2273 if value.endswith("'B"):
2275 if not frozenset(value) <= SET01:
2276 raise ValueError("B's coding contains unacceptable chars")
2277 return self._bits2octets(value)
2278 elif value.endswith("'H"):
2282 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2284 if isinstance(value, binary_type):
2285 return (len(value) * 8, value)
2287 raise InvalidValueType((self.__class__, string_types, binary_type))
2288 if isinstance(value, tuple):
2291 isinstance(value[0], integer_types) and
2292 isinstance(value[1], binary_type)
2297 bit = self.specs.get(name)
2299 raise ObjUnknown("BitString value: %s" % name)
2302 return self._bits2octets("")
2303 bits = frozenset(bits)
2304 return self._bits2octets("".join(
2305 ("1" if bit in bits else "0")
2306 for bit in six_xrange(max(bits) + 1)
2308 if issubclass(value.__class__, BitString):
2310 raise InvalidValueType((self.__class__, binary_type, string_types))
2314 return self._value is not None
2317 obj = self.__class__(_specs=self.specs)
2319 if value is not None:
2320 value = (value[0], value[1])
2323 obj._expl = self._expl
2324 obj.default = self.default
2325 obj.optional = self.optional
2326 obj.offset = self.offset
2327 obj.llen = self.llen
2328 obj.vlen = self.vlen
2329 obj.expl_lenindef = self.expl_lenindef
2330 obj.lenindef = self.lenindef
2331 obj.ber_encoded = self.ber_encoded
2335 self._assert_ready()
2336 for i in six_xrange(self._value[0]):
2341 self._assert_ready()
2342 return self._value[0]
2344 def __bytes__(self):
2345 self._assert_ready()
2346 return self._value[1]
2348 def __eq__(self, their):
2349 if isinstance(their, bytes):
2350 return self._value[1] == their
2351 if not issubclass(their.__class__, BitString):
2354 self._value == their._value and
2355 self.tag == their.tag and
2356 self._expl == their._expl
2361 return [name for name, bit in iteritems(self.specs) if self[bit]]
2371 return self.__class__(
2373 impl=self.tag if impl is None else impl,
2374 expl=self._expl if expl is None else expl,
2375 default=self.default if default is None else default,
2376 optional=self.optional if optional is None else optional,
2380 def __getitem__(self, key):
2381 if isinstance(key, int):
2382 bit_len, octets = self._value
2386 byte2int(memoryview(octets)[key // 8:]) >>
2389 if isinstance(key, string_types):
2390 value = self.specs.get(key)
2392 raise ObjUnknown("BitString value: %s" % key)
2394 raise InvalidValueType((int, str))
2397 self._assert_ready()
2398 bit_len, octets = self._value
2401 len_encode(len(octets) + 1),
2402 int2byte((8 - bit_len % 8) % 8),
2406 def _decode_chunk(self, lv, offset, decode_path, ctx):
2408 l, llen, v = len_decode(lv)
2409 except DecodeError as err:
2410 raise err.__class__(
2412 klass=self.__class__,
2413 decode_path=decode_path,
2417 raise NotEnoughData(
2418 "encoded length is longer than data",
2419 klass=self.__class__,
2420 decode_path=decode_path,
2424 raise NotEnoughData(
2426 klass=self.__class__,
2427 decode_path=decode_path,
2430 pad_size = byte2int(v)
2431 if l == 1 and pad_size != 0:
2433 "invalid empty value",
2434 klass=self.__class__,
2435 decode_path=decode_path,
2441 klass=self.__class__,
2442 decode_path=decode_path,
2445 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2448 klass=self.__class__,
2449 decode_path=decode_path,
2452 v, tail = v[:l], v[l:]
2453 obj = self.__class__(
2454 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2457 default=self.default,
2458 optional=self.optional,
2460 _decoded=(offset, llen, l),
2464 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2466 t, tlen, lv = tag_strip(tlv)
2467 except DecodeError as err:
2468 raise err.__class__(
2470 klass=self.__class__,
2471 decode_path=decode_path,
2475 if tag_only: # pragma: no cover
2477 return self._decode_chunk(lv, offset, decode_path, ctx)
2478 if t == self.tag_constructed:
2479 if not ctx.get("bered", False):
2481 "unallowed BER constructed encoding",
2482 klass=self.__class__,
2483 decode_path=decode_path,
2486 if tag_only: # pragma: no cover
2490 l, llen, v = len_decode(lv)
2491 except LenIndefForm:
2492 llen, l, v = 1, 0, lv[1:]
2494 except DecodeError as err:
2495 raise err.__class__(
2497 klass=self.__class__,
2498 decode_path=decode_path,
2502 raise NotEnoughData(
2503 "encoded length is longer than data",
2504 klass=self.__class__,
2505 decode_path=decode_path,
2508 if not lenindef and l == 0:
2509 raise NotEnoughData(
2511 klass=self.__class__,
2512 decode_path=decode_path,
2516 sub_offset = offset + tlen + llen
2520 if v[:EOC_LEN].tobytes() == EOC:
2527 "chunk out of bounds",
2528 klass=self.__class__,
2529 decode_path=decode_path + (str(len(chunks) - 1),),
2530 offset=chunks[-1].offset,
2532 sub_decode_path = decode_path + (str(len(chunks)),)
2534 chunk, v_tail = BitString().decode(
2537 decode_path=sub_decode_path,
2540 _ctx_immutable=False,
2544 "expected BitString encoded chunk",
2545 klass=self.__class__,
2546 decode_path=sub_decode_path,
2549 chunks.append(chunk)
2550 sub_offset += chunk.tlvlen
2551 vlen += chunk.tlvlen
2553 if len(chunks) == 0:
2556 klass=self.__class__,
2557 decode_path=decode_path,
2562 for chunk_i, chunk in enumerate(chunks[:-1]):
2563 if chunk.bit_len % 8 != 0:
2565 "BitString chunk is not multiple of 8 bits",
2566 klass=self.__class__,
2567 decode_path=decode_path + (str(chunk_i),),
2568 offset=chunk.offset,
2570 values.append(bytes(chunk))
2571 bit_len += chunk.bit_len
2572 chunk_last = chunks[-1]
2573 values.append(bytes(chunk_last))
2574 bit_len += chunk_last.bit_len
2575 obj = self.__class__(
2576 value=(bit_len, b"".join(values)),
2579 default=self.default,
2580 optional=self.optional,
2582 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2584 obj.lenindef = lenindef
2585 obj.ber_encoded = True
2586 return obj, (v[EOC_LEN:] if lenindef else v)
2588 klass=self.__class__,
2589 decode_path=decode_path,
2594 return pp_console_row(next(self.pps()))
2596 def pps(self, decode_path=()):
2600 bit_len, blob = self._value
2601 value = "%d bits" % bit_len
2602 if len(self.specs) > 0:
2603 blob = tuple(self.named)
2606 asn1_type_name=self.asn1_type_name,
2607 obj_name=self.__class__.__name__,
2608 decode_path=decode_path,
2611 optional=self.optional,
2612 default=self == self.default,
2613 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2614 expl=None if self._expl is None else tag_decode(self._expl),
2619 expl_offset=self.expl_offset if self.expled else None,
2620 expl_tlen=self.expl_tlen if self.expled else None,
2621 expl_llen=self.expl_llen if self.expled else None,
2622 expl_vlen=self.expl_vlen if self.expled else None,
2623 expl_lenindef=self.expl_lenindef,
2624 lenindef=self.lenindef,
2625 ber_encoded=self.ber_encoded,
2628 defined_by, defined = self.defined or (None, None)
2629 if defined_by is not None:
2631 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2633 for pp in self.pps_lenindef(decode_path):
2637 class OctetString(Obj):
2638 """``OCTET STRING`` binary string type
2640 >>> s = OctetString(b"hello world")
2641 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2642 >>> s == OctetString(b"hello world")
2647 >>> OctetString(b"hello", bounds=(4, 4))
2648 Traceback (most recent call last):
2649 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2650 >>> OctetString(b"hell", bounds=(4, 4))
2651 OCTET STRING 4 bytes 68656c6c
2655 Pay attention that OCTET STRING can be encoded both in primitive
2656 and constructed forms. Decoder always checks constructed form tag
2657 additionally to specified primitive one. If BER decoding is
2658 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2659 of DER restrictions.
2661 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2662 tag_default = tag_encode(4)
2663 asn1_type_name = "OCTET STRING"
2676 :param value: set the value. Either binary type, or
2677 :py:class:`pyderasn.OctetString` object
2678 :param bounds: set ``(MIN, MAX)`` value size constraint.
2679 (-inf, +inf) by default
2680 :param bytes impl: override default tag with ``IMPLICIT`` one
2681 :param bytes expl: override default tag with ``EXPLICIT`` one
2682 :param default: set default value. Type same as in ``value``
2683 :param bool optional: is object ``OPTIONAL`` in sequence
2685 super(OctetString, self).__init__(
2693 self._bound_min, self._bound_max = getattr(
2697 ) if bounds is None else bounds
2698 if value is not None:
2699 self._value = self._value_sanitize(value)
2700 if default is not None:
2701 default = self._value_sanitize(default)
2702 self.default = self.__class__(
2707 if self._value is None:
2708 self._value = default
2710 tag_klass, _, tag_num = tag_decode(self.tag)
2711 self.tag_constructed = tag_encode(
2713 form=TagFormConstructed,
2717 def _value_sanitize(self, value):
2718 if isinstance(value, binary_type):
2720 elif issubclass(value.__class__, OctetString):
2721 value = value._value
2723 raise InvalidValueType((self.__class__, bytes))
2724 if not self._bound_min <= len(value) <= self._bound_max:
2725 raise BoundsError(self._bound_min, len(value), self._bound_max)
2730 return self._value is not None
2733 obj = self.__class__()
2734 obj._value = self._value
2735 obj._bound_min = self._bound_min
2736 obj._bound_max = self._bound_max
2738 obj._expl = self._expl
2739 obj.default = self.default
2740 obj.optional = self.optional
2741 obj.offset = self.offset
2742 obj.llen = self.llen
2743 obj.vlen = self.vlen
2744 obj.expl_lenindef = self.expl_lenindef
2745 obj.lenindef = self.lenindef
2746 obj.ber_encoded = self.ber_encoded
2749 def __bytes__(self):
2750 self._assert_ready()
2753 def __eq__(self, their):
2754 if isinstance(their, binary_type):
2755 return self._value == their
2756 if not issubclass(their.__class__, OctetString):
2759 self._value == their._value and
2760 self.tag == their.tag and
2761 self._expl == their._expl
2764 def __lt__(self, their):
2765 return self._value < their._value
2776 return self.__class__(
2779 (self._bound_min, self._bound_max)
2780 if bounds is None else bounds
2782 impl=self.tag if impl is None else impl,
2783 expl=self._expl if expl is None else expl,
2784 default=self.default if default is None else default,
2785 optional=self.optional if optional is None else optional,
2789 self._assert_ready()
2792 len_encode(len(self._value)),
2796 def _decode_chunk(self, lv, offset, decode_path, ctx):
2798 l, llen, v = len_decode(lv)
2799 except DecodeError as err:
2800 raise err.__class__(
2802 klass=self.__class__,
2803 decode_path=decode_path,
2807 raise NotEnoughData(
2808 "encoded length is longer than data",
2809 klass=self.__class__,
2810 decode_path=decode_path,
2813 v, tail = v[:l], v[l:]
2815 obj = self.__class__(
2817 bounds=(self._bound_min, self._bound_max),
2820 default=self.default,
2821 optional=self.optional,
2822 _decoded=(offset, llen, l),
2824 except DecodeError as err:
2827 klass=self.__class__,
2828 decode_path=decode_path,
2831 except BoundsError as err:
2834 klass=self.__class__,
2835 decode_path=decode_path,
2840 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2842 t, tlen, lv = tag_strip(tlv)
2843 except DecodeError as err:
2844 raise err.__class__(
2846 klass=self.__class__,
2847 decode_path=decode_path,
2853 return self._decode_chunk(lv, offset, decode_path, ctx)
2854 if t == self.tag_constructed:
2855 if not ctx.get("bered", False):
2857 "unallowed BER constructed encoding",
2858 klass=self.__class__,
2859 decode_path=decode_path,
2866 l, llen, v = len_decode(lv)
2867 except LenIndefForm:
2868 llen, l, v = 1, 0, lv[1:]
2870 except DecodeError as err:
2871 raise err.__class__(
2873 klass=self.__class__,
2874 decode_path=decode_path,
2878 raise NotEnoughData(
2879 "encoded length is longer than data",
2880 klass=self.__class__,
2881 decode_path=decode_path,
2885 sub_offset = offset + tlen + llen
2889 if v[:EOC_LEN].tobytes() == EOC:
2896 "chunk out of bounds",
2897 klass=self.__class__,
2898 decode_path=decode_path + (str(len(chunks) - 1),),
2899 offset=chunks[-1].offset,
2901 sub_decode_path = decode_path + (str(len(chunks)),)
2903 chunk, v_tail = OctetString().decode(
2906 decode_path=sub_decode_path,
2909 _ctx_immutable=False,
2913 "expected OctetString encoded chunk",
2914 klass=self.__class__,
2915 decode_path=sub_decode_path,
2918 chunks.append(chunk)
2919 sub_offset += chunk.tlvlen
2920 vlen += chunk.tlvlen
2923 obj = self.__class__(
2924 value=b"".join(bytes(chunk) for chunk in chunks),
2925 bounds=(self._bound_min, self._bound_max),
2928 default=self.default,
2929 optional=self.optional,
2930 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2932 except DecodeError as err:
2935 klass=self.__class__,
2936 decode_path=decode_path,
2939 except BoundsError as err:
2942 klass=self.__class__,
2943 decode_path=decode_path,
2946 obj.lenindef = lenindef
2947 obj.ber_encoded = True
2948 return obj, (v[EOC_LEN:] if lenindef else v)
2950 klass=self.__class__,
2951 decode_path=decode_path,
2956 return pp_console_row(next(self.pps()))
2958 def pps(self, decode_path=()):
2961 asn1_type_name=self.asn1_type_name,
2962 obj_name=self.__class__.__name__,
2963 decode_path=decode_path,
2964 value=("%d bytes" % len(self._value)) if self.ready else None,
2965 blob=self._value if self.ready else None,
2966 optional=self.optional,
2967 default=self == self.default,
2968 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2969 expl=None if self._expl is None else tag_decode(self._expl),
2974 expl_offset=self.expl_offset if self.expled else None,
2975 expl_tlen=self.expl_tlen if self.expled else None,
2976 expl_llen=self.expl_llen if self.expled else None,
2977 expl_vlen=self.expl_vlen if self.expled else None,
2978 expl_lenindef=self.expl_lenindef,
2979 lenindef=self.lenindef,
2980 ber_encoded=self.ber_encoded,
2983 defined_by, defined = self.defined or (None, None)
2984 if defined_by is not None:
2986 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2988 for pp in self.pps_lenindef(decode_path):
2993 """``NULL`` null object
3001 tag_default = tag_encode(5)
3002 asn1_type_name = "NULL"
3006 value=None, # unused, but Sequence passes it
3013 :param bytes impl: override default tag with ``IMPLICIT`` one
3014 :param bytes expl: override default tag with ``EXPLICIT`` one
3015 :param bool optional: is object ``OPTIONAL`` in sequence
3017 super(Null, self).__init__(impl, expl, None, optional, _decoded)
3025 obj = self.__class__()
3027 obj._expl = self._expl
3028 obj.default = self.default
3029 obj.optional = self.optional
3030 obj.offset = self.offset
3031 obj.llen = self.llen
3032 obj.vlen = self.vlen
3033 obj.expl_lenindef = self.expl_lenindef
3034 obj.lenindef = self.lenindef
3035 obj.ber_encoded = self.ber_encoded
3038 def __eq__(self, their):
3039 if not issubclass(their.__class__, Null):
3042 self.tag == their.tag and
3043 self._expl == their._expl
3053 return self.__class__(
3054 impl=self.tag if impl is None else impl,
3055 expl=self._expl if expl is None else expl,
3056 optional=self.optional if optional is None else optional,
3060 return self.tag + len_encode(0)
3062 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3064 t, _, lv = tag_strip(tlv)
3065 except DecodeError as err:
3066 raise err.__class__(
3068 klass=self.__class__,
3069 decode_path=decode_path,
3074 klass=self.__class__,
3075 decode_path=decode_path,
3078 if tag_only: # pragma: no cover
3081 l, _, v = len_decode(lv)
3082 except DecodeError as err:
3083 raise err.__class__(
3085 klass=self.__class__,
3086 decode_path=decode_path,
3090 raise InvalidLength(
3091 "Null must have zero length",
3092 klass=self.__class__,
3093 decode_path=decode_path,
3096 obj = self.__class__(
3099 optional=self.optional,
3100 _decoded=(offset, 1, 0),
3105 return pp_console_row(next(self.pps()))
3107 def pps(self, decode_path=()):
3110 asn1_type_name=self.asn1_type_name,
3111 obj_name=self.__class__.__name__,
3112 decode_path=decode_path,
3113 optional=self.optional,
3114 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3115 expl=None if self._expl is None else tag_decode(self._expl),
3120 expl_offset=self.expl_offset if self.expled else None,
3121 expl_tlen=self.expl_tlen if self.expled else None,
3122 expl_llen=self.expl_llen if self.expled else None,
3123 expl_vlen=self.expl_vlen if self.expled else None,
3124 expl_lenindef=self.expl_lenindef,
3127 for pp in self.pps_lenindef(decode_path):
3131 class ObjectIdentifier(Obj):
3132 """``OBJECT IDENTIFIER`` OID type
3134 >>> oid = ObjectIdentifier((1, 2, 3))
3135 OBJECT IDENTIFIER 1.2.3
3136 >>> oid == ObjectIdentifier("1.2.3")
3142 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3143 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3145 >>> str(ObjectIdentifier((3, 1)))
3146 Traceback (most recent call last):
3147 pyderasn.InvalidOID: unacceptable first arc value
3149 __slots__ = ("defines",)
3150 tag_default = tag_encode(6)
3151 asn1_type_name = "OBJECT IDENTIFIER"
3164 :param value: set the value. Either tuples of integers,
3165 string of "."-concatenated integers, or
3166 :py:class:`pyderasn.ObjectIdentifier` object
3167 :param defines: sequence of tuples. Each tuple has two elements.
3168 First one is relative to current one decode
3169 path, aiming to the field defined by that OID.
3170 Read about relative path in
3171 :py:func:`pyderasn.abs_decode_path`. Second
3172 tuple element is ``{OID: pyderasn.Obj()}``
3173 dictionary, mapping between current OID value
3174 and structure applied to defined field.
3175 :ref:`Read about DEFINED BY <definedby>`
3176 :param bytes impl: override default tag with ``IMPLICIT`` one
3177 :param bytes expl: override default tag with ``EXPLICIT`` one
3178 :param default: set default value. Type same as in ``value``
3179 :param bool optional: is object ``OPTIONAL`` in sequence
3181 super(ObjectIdentifier, self).__init__(
3189 if value is not None:
3190 self._value = self._value_sanitize(value)
3191 if default is not None:
3192 default = self._value_sanitize(default)
3193 self.default = self.__class__(
3198 if self._value is None:
3199 self._value = default
3200 self.defines = defines
3202 def __add__(self, their):
3203 if isinstance(their, self.__class__):
3204 return self.__class__(self._value + their._value)
3205 if isinstance(their, tuple):
3206 return self.__class__(self._value + their)
3207 raise InvalidValueType((self.__class__, tuple))
3209 def _value_sanitize(self, value):
3210 if issubclass(value.__class__, ObjectIdentifier):
3212 if isinstance(value, string_types):
3214 value = tuple(int(arc) for arc in value.split("."))
3216 raise InvalidOID("unacceptable arcs values")
3217 if isinstance(value, tuple):
3219 raise InvalidOID("less than 2 arcs")
3220 first_arc = value[0]
3221 if first_arc in (0, 1):
3222 if not (0 <= value[1] <= 39):
3223 raise InvalidOID("second arc is too wide")
3224 elif first_arc == 2:
3227 raise InvalidOID("unacceptable first arc value")
3229 raise InvalidValueType((self.__class__, str, tuple))
3233 return self._value is not None
3236 obj = self.__class__()
3237 obj._value = self._value
3238 obj.defines = self.defines
3240 obj._expl = self._expl
3241 obj.default = self.default
3242 obj.optional = self.optional
3243 obj.offset = self.offset
3244 obj.llen = self.llen
3245 obj.vlen = self.vlen
3246 obj.expl_lenindef = self.expl_lenindef
3247 obj.lenindef = self.lenindef
3248 obj.ber_encoded = self.ber_encoded
3252 self._assert_ready()
3253 return iter(self._value)
3256 return ".".join(str(arc) for arc in self._value or ())
3259 self._assert_ready()
3262 bytes(self._expl or b"") +
3263 str(self._value).encode("ascii"),
3266 def __eq__(self, their):
3267 if isinstance(their, tuple):
3268 return self._value == their
3269 if not issubclass(their.__class__, ObjectIdentifier):
3272 self.tag == their.tag and
3273 self._expl == their._expl and
3274 self._value == their._value
3277 def __lt__(self, their):
3278 return self._value < their._value
3289 return self.__class__(
3291 defines=self.defines if defines is None else defines,
3292 impl=self.tag if impl is None else impl,
3293 expl=self._expl if expl is None else expl,
3294 default=self.default if default is None else default,
3295 optional=self.optional if optional is None else optional,
3299 self._assert_ready()
3301 first_value = value[1]
3302 first_arc = value[0]
3305 elif first_arc == 1:
3307 elif first_arc == 2:
3309 else: # pragma: no cover
3310 raise RuntimeError("invalid arc is stored")
3311 octets = [zero_ended_encode(first_value)]
3312 for arc in value[2:]:
3313 octets.append(zero_ended_encode(arc))
3314 v = b"".join(octets)
3315 return b"".join((self.tag, len_encode(len(v)), v))
3317 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3319 t, _, lv = tag_strip(tlv)
3320 except DecodeError as err:
3321 raise err.__class__(
3323 klass=self.__class__,
3324 decode_path=decode_path,
3329 klass=self.__class__,
3330 decode_path=decode_path,
3333 if tag_only: # pragma: no cover
3336 l, llen, v = len_decode(lv)
3337 except DecodeError as err:
3338 raise err.__class__(
3340 klass=self.__class__,
3341 decode_path=decode_path,
3345 raise NotEnoughData(
3346 "encoded length is longer than data",
3347 klass=self.__class__,
3348 decode_path=decode_path,
3352 raise NotEnoughData(
3354 klass=self.__class__,
3355 decode_path=decode_path,
3358 v, tail = v[:l], v[l:]
3365 octet = indexbytes(v, i)
3366 if i == 0 and octet == 0x80:
3367 if ctx.get("bered", False):
3370 raise DecodeError("non normalized arc encoding")
3371 arc = (arc << 7) | (octet & 0x7F)
3372 if octet & 0x80 == 0:
3380 klass=self.__class__,
3381 decode_path=decode_path,
3385 second_arc = arcs[0]
3386 if 0 <= second_arc <= 39:
3388 elif 40 <= second_arc <= 79:
3394 obj = self.__class__(
3395 value=tuple([first_arc, second_arc] + arcs[1:]),
3398 default=self.default,
3399 optional=self.optional,
3400 _decoded=(offset, llen, l),
3403 obj.ber_encoded = True
3407 return pp_console_row(next(self.pps()))
3409 def pps(self, decode_path=()):
3412 asn1_type_name=self.asn1_type_name,
3413 obj_name=self.__class__.__name__,
3414 decode_path=decode_path,
3415 value=str(self) if self.ready else None,
3416 optional=self.optional,
3417 default=self == self.default,
3418 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3419 expl=None if self._expl is None else tag_decode(self._expl),
3424 expl_offset=self.expl_offset if self.expled else None,
3425 expl_tlen=self.expl_tlen if self.expled else None,
3426 expl_llen=self.expl_llen if self.expled else None,
3427 expl_vlen=self.expl_vlen if self.expled else None,
3428 expl_lenindef=self.expl_lenindef,
3429 ber_encoded=self.ber_encoded,
3432 for pp in self.pps_lenindef(decode_path):
3436 class Enumerated(Integer):
3437 """``ENUMERATED`` integer type
3439 This type is identical to :py:class:`pyderasn.Integer`, but requires
3440 schema to be specified and does not accept values missing from it.
3443 tag_default = tag_encode(10)
3444 asn1_type_name = "ENUMERATED"
3455 bounds=None, # dummy argument, workability for Integer.decode
3457 super(Enumerated, self).__init__(
3466 if len(self.specs) == 0:
3467 raise ValueError("schema must be specified")
3469 def _value_sanitize(self, value):
3470 if isinstance(value, self.__class__):
3471 value = value._value
3472 elif isinstance(value, integer_types):
3473 for _value in itervalues(self.specs):
3478 "unknown integer value: %s" % value,
3479 klass=self.__class__,
3481 elif isinstance(value, string_types):
3482 value = self.specs.get(value)
3484 raise ObjUnknown("integer value: %s" % value)
3486 raise InvalidValueType((self.__class__, int, str))
3490 obj = self.__class__(_specs=self.specs)
3491 obj._value = self._value
3492 obj._bound_min = self._bound_min
3493 obj._bound_max = self._bound_max
3495 obj._expl = self._expl
3496 obj.default = self.default
3497 obj.optional = self.optional
3498 obj.offset = self.offset
3499 obj.llen = self.llen
3500 obj.vlen = self.vlen
3501 obj.expl_lenindef = self.expl_lenindef
3502 obj.lenindef = self.lenindef
3503 obj.ber_encoded = self.ber_encoded
3515 return self.__class__(
3517 impl=self.tag if impl is None else impl,
3518 expl=self._expl if expl is None else expl,
3519 default=self.default if default is None else default,
3520 optional=self.optional if optional is None else optional,
3525 class CommonString(OctetString):
3526 """Common class for all strings
3528 Everything resembles :py:class:`pyderasn.OctetString`, except
3529 ability to deal with unicode text strings.
3531 >>> hexenc("привет мир".encode("utf-8"))
3532 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3533 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3535 >>> s = UTF8String("привет мир")
3536 UTF8String UTF8String привет мир
3538 'привет мир'
3539 >>> hexenc(bytes(s))
3540 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3542 >>> PrintableString("привет мир")
3543 Traceback (most recent call last):
3544 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3546 >>> BMPString("ада", bounds=(2, 2))
3547 Traceback (most recent call last):
3548 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3549 >>> s = BMPString("ад", bounds=(2, 2))
3552 >>> hexenc(bytes(s))
3560 * - :py:class:`pyderasn.UTF8String`
3562 * - :py:class:`pyderasn.NumericString`
3564 * - :py:class:`pyderasn.PrintableString`
3566 * - :py:class:`pyderasn.TeletexString`
3568 * - :py:class:`pyderasn.T61String`
3570 * - :py:class:`pyderasn.VideotexString`
3572 * - :py:class:`pyderasn.IA5String`
3574 * - :py:class:`pyderasn.GraphicString`
3576 * - :py:class:`pyderasn.VisibleString`
3578 * - :py:class:`pyderasn.ISO646String`
3580 * - :py:class:`pyderasn.GeneralString`
3582 * - :py:class:`pyderasn.UniversalString`
3584 * - :py:class:`pyderasn.BMPString`
3587 __slots__ = ("encoding",)
3589 def _value_sanitize(self, value):
3591 value_decoded = None
3592 if isinstance(value, self.__class__):
3593 value_raw = value._value
3594 elif isinstance(value, text_type):
3595 value_decoded = value
3596 elif isinstance(value, binary_type):
3599 raise InvalidValueType((self.__class__, text_type, binary_type))
3602 value_decoded.encode(self.encoding)
3603 if value_raw is None else value_raw
3606 value_raw.decode(self.encoding)
3607 if value_decoded is None else value_decoded
3609 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3610 raise DecodeError(str(err))
3611 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3619 def __eq__(self, their):
3620 if isinstance(their, binary_type):
3621 return self._value == their
3622 if isinstance(their, text_type):
3623 return self._value == their.encode(self.encoding)
3624 if not isinstance(their, self.__class__):
3627 self._value == their._value and
3628 self.tag == their.tag and
3629 self._expl == their._expl
3632 def __unicode__(self):
3634 return self._value.decode(self.encoding)
3635 return text_type(self._value)
3638 return pp_console_row(next(self.pps(no_unicode=PY2)))
3640 def pps(self, decode_path=(), no_unicode=False):
3643 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3646 asn1_type_name=self.asn1_type_name,
3647 obj_name=self.__class__.__name__,
3648 decode_path=decode_path,
3650 optional=self.optional,
3651 default=self == self.default,
3652 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3653 expl=None if self._expl is None else tag_decode(self._expl),
3658 expl_offset=self.expl_offset if self.expled else None,
3659 expl_tlen=self.expl_tlen if self.expled else None,
3660 expl_llen=self.expl_llen if self.expled else None,
3661 expl_vlen=self.expl_vlen if self.expled else None,
3662 expl_lenindef=self.expl_lenindef,
3663 ber_encoded=self.ber_encoded,
3666 for pp in self.pps_lenindef(decode_path):
3670 class UTF8String(CommonString):
3672 tag_default = tag_encode(12)
3674 asn1_type_name = "UTF8String"
3677 class AllowableCharsMixin(object):
3679 def allowable_chars(self):
3681 return self._allowable_chars
3682 return frozenset(six_unichr(c) for c in self._allowable_chars)
3685 class NumericString(AllowableCharsMixin, CommonString):
3688 Its value is properly sanitized: only ASCII digits with spaces can
3691 >>> NumericString().allowable_chars
3692 frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
3695 tag_default = tag_encode(18)
3697 asn1_type_name = "NumericString"
3698 _allowable_chars = frozenset(digits.encode("ascii") + b" ")
3700 def _value_sanitize(self, value):
3701 value = super(NumericString, self)._value_sanitize(value)
3702 if not frozenset(value) <= self._allowable_chars:
3703 raise DecodeError("non-numeric value")
3707 class PrintableString(AllowableCharsMixin, CommonString):
3710 Its value is properly sanitized: see X.680 41.4 table 10.
3712 >>> PrintableString().allowable_chars
3713 frozenset([' ', "'", ..., 'z'])
3716 tag_default = tag_encode(19)
3718 asn1_type_name = "PrintableString"
3719 _allowable_chars = frozenset(
3720 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3723 def _value_sanitize(self, value):
3724 value = super(PrintableString, self)._value_sanitize(value)
3725 if not frozenset(value) <= self._allowable_chars:
3726 raise DecodeError("non-printable value")
3730 class TeletexString(CommonString):
3732 tag_default = tag_encode(20)
3734 asn1_type_name = "TeletexString"
3737 class T61String(TeletexString):
3739 asn1_type_name = "T61String"
3742 class VideotexString(CommonString):
3744 tag_default = tag_encode(21)
3745 encoding = "iso-8859-1"
3746 asn1_type_name = "VideotexString"
3749 class IA5String(CommonString):
3751 tag_default = tag_encode(22)
3753 asn1_type_name = "IA5"
3756 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3757 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3758 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3761 class UTCTime(CommonString):
3762 """``UTCTime`` datetime type
3764 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3765 UTCTime UTCTime 2017-09-30T22:07:50
3771 datetime.datetime(2017, 9, 30, 22, 7, 50)
3772 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3773 datetime.datetime(1957, 9, 30, 22, 7, 50)
3777 BER encoding is unsupported.
3780 tag_default = tag_encode(23)
3782 asn1_type_name = "UTCTime"
3792 bounds=None, # dummy argument, workability for OctetString.decode
3795 :param value: set the value. Either datetime type, or
3796 :py:class:`pyderasn.UTCTime` object
3797 :param bytes impl: override default tag with ``IMPLICIT`` one
3798 :param bytes expl: override default tag with ``EXPLICIT`` one
3799 :param default: set default value. Type same as in ``value``
3800 :param bool optional: is object ``OPTIONAL`` in sequence
3802 super(UTCTime, self).__init__(
3810 if value is not None:
3811 self._value = self._value_sanitize(value)
3812 if default is not None:
3813 default = self._value_sanitize(default)
3814 self.default = self.__class__(
3819 if self._value is None:
3820 self._value = default
3822 def _strptime(self, value):
3823 # datetime.strptime's format: %y%m%d%H%M%SZ
3824 if len(value) != LEN_YYMMDDHHMMSSZ:
3825 raise ValueError("invalid UTCTime length")
3826 if value[-1] != "Z":
3827 raise ValueError("non UTC timezone")
3829 2000 + int(value[:2]), # %y
3830 int(value[2:4]), # %m
3831 int(value[4:6]), # %d
3832 int(value[6:8]), # %H
3833 int(value[8:10]), # %M
3834 int(value[10:12]), # %S
3837 def _value_sanitize(self, value):
3838 if isinstance(value, binary_type):
3840 value_decoded = value.decode("ascii")
3841 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3842 raise DecodeError("invalid UTCTime encoding: %r" % err)
3844 self._strptime(value_decoded)
3845 except (TypeError, ValueError) as err:
3846 raise DecodeError("invalid UTCTime format: %r" % err)
3848 if isinstance(value, self.__class__):
3850 if isinstance(value, datetime):
3851 return value.strftime("%y%m%d%H%M%SZ").encode("ascii")
3852 raise InvalidValueType((self.__class__, datetime))
3854 def __eq__(self, their):
3855 if isinstance(their, binary_type):
3856 return self._value == their
3857 if isinstance(their, datetime):
3858 return self.todatetime() == their
3859 if not isinstance(their, self.__class__):
3862 self._value == their._value and
3863 self.tag == their.tag and
3864 self._expl == their._expl
3867 def todatetime(self):
3868 """Convert to datetime
3872 Pay attention that UTCTime can not hold full year, so all years
3873 having < 50 years are treated as 20xx, 19xx otherwise, according
3874 to X.509 recomendation.
3876 value = self._strptime(self._value.decode("ascii"))
3877 year = value.year % 100
3879 year=(2000 + year) if year < 50 else (1900 + year),
3883 minute=value.minute,
3884 second=value.second,
3888 return pp_console_row(next(self.pps()))
3890 def pps(self, decode_path=()):
3893 asn1_type_name=self.asn1_type_name,
3894 obj_name=self.__class__.__name__,
3895 decode_path=decode_path,
3896 value=self.todatetime().isoformat() if self.ready else None,
3897 optional=self.optional,
3898 default=self == self.default,
3899 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3900 expl=None if self._expl is None else tag_decode(self._expl),
3905 expl_offset=self.expl_offset if self.expled else None,
3906 expl_tlen=self.expl_tlen if self.expled else None,
3907 expl_llen=self.expl_llen if self.expled else None,
3908 expl_vlen=self.expl_vlen if self.expled else None,
3909 expl_lenindef=self.expl_lenindef,
3910 ber_encoded=self.ber_encoded,
3913 for pp in self.pps_lenindef(decode_path):
3917 class GeneralizedTime(UTCTime):
3918 """``GeneralizedTime`` datetime type
3920 This type is similar to :py:class:`pyderasn.UTCTime`.
3922 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3923 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3925 '20170930220750.000123Z'
3926 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3927 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3931 BER encoding is unsupported.
3935 Only microsecond fractions are supported.
3936 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
3937 higher precision values.
3940 tag_default = tag_encode(24)
3941 asn1_type_name = "GeneralizedTime"
3943 def _strptime(self, value):
3945 if l == LEN_YYYYMMDDHHMMSSZ:
3946 # datetime.strptime's format: %y%m%d%H%M%SZ
3947 if value[-1] != "Z":
3948 raise ValueError("non UTC timezone")
3950 int(value[:4]), # %Y
3951 int(value[4:6]), # %m
3952 int(value[6:8]), # %d
3953 int(value[8:10]), # %H
3954 int(value[10:12]), # %M
3955 int(value[12:14]), # %S
3957 if l >= LEN_YYYYMMDDHHMMSSDMZ:
3958 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
3959 if value[-1] != "Z":
3960 raise ValueError("non UTC timezone")
3961 if value[14] != ".":
3962 raise ValueError("no fractions separator")
3965 raise ValueError("trailing zero")
3968 raise ValueError("only microsecond fractions are supported")
3969 us = int(us + ("0" * (6 - us_len)))
3971 int(value[:4]), # %Y
3972 int(value[4:6]), # %m
3973 int(value[6:8]), # %d
3974 int(value[8:10]), # %H
3975 int(value[10:12]), # %M
3976 int(value[12:14]), # %S
3980 raise ValueError("invalid GeneralizedTime length")
3982 def _value_sanitize(self, value):
3983 if isinstance(value, binary_type):
3985 value_decoded = value.decode("ascii")
3986 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3987 raise DecodeError("invalid GeneralizedTime encoding: %r" % err)
3989 self._strptime(value_decoded)
3990 except (TypeError, ValueError) as err:
3992 "invalid GeneralizedTime format: %r" % err,
3993 klass=self.__class__,
3996 if isinstance(value, self.__class__):
3998 if isinstance(value, datetime):
3999 encoded = value.strftime("%Y%m%d%H%M%S")
4000 if value.microsecond > 0:
4001 encoded = encoded + (".%06d" % value.microsecond).rstrip("0")
4002 return (encoded + "Z").encode("ascii")
4003 raise InvalidValueType((self.__class__, datetime))
4005 def todatetime(self):
4006 return self._strptime(self._value.decode("ascii"))
4009 class GraphicString(CommonString):
4011 tag_default = tag_encode(25)
4012 encoding = "iso-8859-1"
4013 asn1_type_name = "GraphicString"
4016 class VisibleString(CommonString):
4018 tag_default = tag_encode(26)
4020 asn1_type_name = "VisibleString"
4023 class ISO646String(VisibleString):
4025 asn1_type_name = "ISO646String"
4028 class GeneralString(CommonString):
4030 tag_default = tag_encode(27)
4031 encoding = "iso-8859-1"
4032 asn1_type_name = "GeneralString"
4035 class UniversalString(CommonString):
4037 tag_default = tag_encode(28)
4038 encoding = "utf-32-be"
4039 asn1_type_name = "UniversalString"
4042 class BMPString(CommonString):
4044 tag_default = tag_encode(30)
4045 encoding = "utf-16-be"
4046 asn1_type_name = "BMPString"
4050 """``CHOICE`` special type
4054 class GeneralName(Choice):
4056 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4057 ("dNSName", IA5String(impl=tag_ctxp(2))),
4060 >>> gn = GeneralName()
4062 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4063 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4064 >>> gn["dNSName"] = IA5String("bar.baz")
4065 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4066 >>> gn["rfc822Name"]
4069 [2] IA5String IA5 bar.baz
4072 >>> gn.value == gn["dNSName"]
4075 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4077 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4078 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4080 __slots__ = ("specs",)
4082 asn1_type_name = "CHOICE"
4095 :param value: set the value. Either ``(choice, value)`` tuple, or
4096 :py:class:`pyderasn.Choice` object
4097 :param bytes impl: can not be set, do **not** use it
4098 :param bytes expl: override default tag with ``EXPLICIT`` one
4099 :param default: set default value. Type same as in ``value``
4100 :param bool optional: is object ``OPTIONAL`` in sequence
4102 if impl is not None:
4103 raise ValueError("no implicit tag allowed for CHOICE")
4104 super(Choice, self).__init__(None, expl, default, optional, _decoded)
4106 schema = getattr(self, "schema", ())
4107 if len(schema) == 0:
4108 raise ValueError("schema must be specified")
4110 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4113 if value is not None:
4114 self._value = self._value_sanitize(value)
4115 if default is not None:
4116 default_value = self._value_sanitize(default)
4117 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4118 default_obj.specs = self.specs
4119 default_obj._value = default_value
4120 self.default = default_obj
4122 self._value = default_obj.copy()._value
4124 def _value_sanitize(self, value):
4125 if isinstance(value, tuple) and len(value) == 2:
4127 spec = self.specs.get(choice)
4129 raise ObjUnknown(choice)
4130 if not isinstance(obj, spec.__class__):
4131 raise InvalidValueType((spec,))
4132 return (choice, spec(obj))
4133 if isinstance(value, self.__class__):
4135 raise InvalidValueType((self.__class__, tuple))
4139 return self._value is not None and self._value[1].ready
4143 return self.expl_lenindef or (
4144 (self._value is not None) and
4145 self._value[1].bered
4149 obj = self.__class__(schema=self.specs)
4150 obj._expl = self._expl
4151 obj.default = self.default
4152 obj.optional = self.optional
4153 obj.offset = self.offset
4154 obj.llen = self.llen
4155 obj.vlen = self.vlen
4156 obj.expl_lenindef = self.expl_lenindef
4157 obj.lenindef = self.lenindef
4158 obj.ber_encoded = self.ber_encoded
4160 if value is not None:
4161 obj._value = (value[0], value[1].copy())
4164 def __eq__(self, their):
4165 if isinstance(their, tuple) and len(their) == 2:
4166 return self._value == their
4167 if not isinstance(their, self.__class__):
4170 self.specs == their.specs and
4171 self._value == their._value
4181 return self.__class__(
4184 expl=self._expl if expl is None else expl,
4185 default=self.default if default is None else default,
4186 optional=self.optional if optional is None else optional,
4191 self._assert_ready()
4192 return self._value[0]
4196 self._assert_ready()
4197 return self._value[1]
4199 def __getitem__(self, key):
4200 if key not in self.specs:
4201 raise ObjUnknown(key)
4202 if self._value is None:
4204 choice, value = self._value
4209 def __setitem__(self, key, value):
4210 spec = self.specs.get(key)
4212 raise ObjUnknown(key)
4213 if not isinstance(value, spec.__class__):
4214 raise InvalidValueType((spec.__class__,))
4215 self._value = (key, spec(value))
4223 return self._value[1].decoded if self.ready else False
4226 self._assert_ready()
4227 return self._value[1].encode()
4229 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4230 for choice, spec in iteritems(self.specs):
4231 sub_decode_path = decode_path + (choice,)
4237 decode_path=sub_decode_path,
4240 _ctx_immutable=False,
4247 klass=self.__class__,
4248 decode_path=decode_path,
4251 if tag_only: # pragma: no cover
4253 value, tail = spec.decode(
4257 decode_path=sub_decode_path,
4259 _ctx_immutable=False,
4261 obj = self.__class__(
4264 default=self.default,
4265 optional=self.optional,
4266 _decoded=(offset, 0, value.fulllen),
4268 obj._value = (choice, value)
4272 value = pp_console_row(next(self.pps()))
4274 value = "%s[%r]" % (value, self.value)
4277 def pps(self, decode_path=()):
4280 asn1_type_name=self.asn1_type_name,
4281 obj_name=self.__class__.__name__,
4282 decode_path=decode_path,
4283 value=self.choice if self.ready else None,
4284 optional=self.optional,
4285 default=self == self.default,
4286 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4287 expl=None if self._expl is None else tag_decode(self._expl),
4292 expl_lenindef=self.expl_lenindef,
4296 yield self.value.pps(decode_path=decode_path + (self.choice,))
4297 for pp in self.pps_lenindef(decode_path):
4301 class PrimitiveTypes(Choice):
4302 """Predefined ``CHOICE`` for all generic primitive types
4304 It could be useful for general decoding of some unspecified values:
4306 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4307 OCTET STRING 3 bytes 666f6f
4308 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4312 schema = tuple((klass.__name__, klass()) for klass in (
4337 """``ANY`` special type
4339 >>> Any(Integer(-123))
4341 >>> a = Any(OctetString(b"hello world").encode())
4342 ANY 040b68656c6c6f20776f726c64
4343 >>> hexenc(bytes(a))
4344 b'0x040x0bhello world'
4346 __slots__ = ("defined",)
4347 tag_default = tag_encode(0)
4348 asn1_type_name = "ANY"
4358 :param value: set the value. Either any kind of pyderasn's
4359 **ready** object, or bytes. Pay attention that
4360 **no** validation is performed is raw binary value
4362 :param bytes expl: override default tag with ``EXPLICIT`` one
4363 :param bool optional: is object ``OPTIONAL`` in sequence
4365 super(Any, self).__init__(None, expl, None, optional, _decoded)
4366 self._value = None if value is None else self._value_sanitize(value)
4369 def _value_sanitize(self, value):
4370 if isinstance(value, binary_type):
4372 if isinstance(value, self.__class__):
4374 if isinstance(value, Obj):
4375 return value.encode()
4376 raise InvalidValueType((self.__class__, Obj, binary_type))
4380 return self._value is not None
4384 if self.expl_lenindef or self.lenindef:
4386 if self.defined is None:
4388 return self.defined[1].bered
4391 obj = self.__class__()
4392 obj._value = self._value
4394 obj._expl = self._expl
4395 obj.optional = self.optional
4396 obj.offset = self.offset
4397 obj.llen = self.llen
4398 obj.vlen = self.vlen
4399 obj.expl_lenindef = self.expl_lenindef
4400 obj.lenindef = self.lenindef
4401 obj.ber_encoded = self.ber_encoded
4404 def __eq__(self, their):
4405 if isinstance(their, binary_type):
4406 return self._value == their
4407 if issubclass(their.__class__, Any):
4408 return self._value == their._value
4417 return self.__class__(
4419 expl=self._expl if expl is None else expl,
4420 optional=self.optional if optional is None else optional,
4423 def __bytes__(self):
4424 self._assert_ready()
4432 self._assert_ready()
4435 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4437 t, tlen, lv = tag_strip(tlv)
4438 except DecodeError as err:
4439 raise err.__class__(
4441 klass=self.__class__,
4442 decode_path=decode_path,
4446 l, llen, v = len_decode(lv)
4447 except LenIndefForm as err:
4448 if not ctx.get("bered", False):
4449 raise err.__class__(
4451 klass=self.__class__,
4452 decode_path=decode_path,
4455 llen, vlen, v = 1, 0, lv[1:]
4456 sub_offset = offset + tlen + llen
4458 while v[:EOC_LEN].tobytes() != EOC:
4459 chunk, v = Any().decode(
4462 decode_path=decode_path + (str(chunk_i),),
4465 _ctx_immutable=False,
4467 vlen += chunk.tlvlen
4468 sub_offset += chunk.tlvlen
4470 tlvlen = tlen + llen + vlen + EOC_LEN
4471 obj = self.__class__(
4472 value=tlv[:tlvlen].tobytes(),
4474 optional=self.optional,
4475 _decoded=(offset, 0, tlvlen),
4479 return obj, v[EOC_LEN:]
4480 except DecodeError as err:
4481 raise err.__class__(
4483 klass=self.__class__,
4484 decode_path=decode_path,
4488 raise NotEnoughData(
4489 "encoded length is longer than data",
4490 klass=self.__class__,
4491 decode_path=decode_path,
4494 tlvlen = tlen + llen + l
4495 v, tail = tlv[:tlvlen], v[l:]
4496 obj = self.__class__(
4499 optional=self.optional,
4500 _decoded=(offset, 0, tlvlen),
4506 return pp_console_row(next(self.pps()))
4508 def pps(self, decode_path=()):
4511 asn1_type_name=self.asn1_type_name,
4512 obj_name=self.__class__.__name__,
4513 decode_path=decode_path,
4514 blob=self._value if self.ready else None,
4515 optional=self.optional,
4516 default=self == self.default,
4517 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4518 expl=None if self._expl is None else tag_decode(self._expl),
4523 expl_offset=self.expl_offset if self.expled else None,
4524 expl_tlen=self.expl_tlen if self.expled else None,
4525 expl_llen=self.expl_llen if self.expled else None,
4526 expl_vlen=self.expl_vlen if self.expled else None,
4527 expl_lenindef=self.expl_lenindef,
4528 lenindef=self.lenindef,
4531 defined_by, defined = self.defined or (None, None)
4532 if defined_by is not None:
4534 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4536 for pp in self.pps_lenindef(decode_path):
4540 ########################################################################
4541 # ASN.1 constructed types
4542 ########################################################################
4544 def get_def_by_path(defines_by_path, sub_decode_path):
4545 """Get define by decode path
4547 for path, define in defines_by_path:
4548 if len(path) != len(sub_decode_path):
4550 for p1, p2 in zip(path, sub_decode_path):
4551 if (p1 != any) and (p1 != p2):
4557 def abs_decode_path(decode_path, rel_path):
4558 """Create an absolute decode path from current and relative ones
4560 :param decode_path: current decode path, starting point. Tuple of strings
4561 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4562 If first tuple's element is "/", then treat it as
4563 an absolute path, ignoring ``decode_path`` as
4564 starting point. Also this tuple can contain ".."
4565 elements, stripping the leading element from
4568 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4569 ("foo", "bar", "baz", "whatever")
4570 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4572 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4575 if rel_path[0] == "/":
4577 if rel_path[0] == "..":
4578 return abs_decode_path(decode_path[:-1], rel_path[1:])
4579 return decode_path + rel_path
4582 class Sequence(Obj):
4583 """``SEQUENCE`` structure type
4585 You have to make specification of sequence::
4587 class Extension(Sequence):
4589 ("extnID", ObjectIdentifier()),
4590 ("critical", Boolean(default=False)),
4591 ("extnValue", OctetString()),
4594 Then, you can work with it as with dictionary.
4596 >>> ext = Extension()
4597 >>> Extension().specs
4599 ('extnID', OBJECT IDENTIFIER),
4600 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4601 ('extnValue', OCTET STRING),
4603 >>> ext["extnID"] = "1.2.3"
4604 Traceback (most recent call last):
4605 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4606 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4608 You can determine if sequence is ready to be encoded:
4613 Traceback (most recent call last):
4614 pyderasn.ObjNotReady: object is not ready: extnValue
4615 >>> ext["extnValue"] = OctetString(b"foobar")
4619 Value you want to assign, must have the same **type** as in
4620 corresponding specification, but it can have different tags,
4621 optional/default attributes -- they will be taken from specification
4624 class TBSCertificate(Sequence):
4626 ("version", Version(expl=tag_ctxc(0), default="v1")),
4629 >>> tbs = TBSCertificate()
4630 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4632 Assign ``None`` to remove value from sequence.
4634 You can set values in Sequence during its initialization:
4636 >>> AlgorithmIdentifier((
4637 ("algorithm", ObjectIdentifier("1.2.3")),
4638 ("parameters", Any(Null()))
4640 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4642 You can determine if value exists/set in the sequence and take its value:
4644 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4647 OBJECT IDENTIFIER 1.2.3
4649 But pay attention that if value has default, then it won't be (not
4650 in) in the sequence (because ``DEFAULT`` must not be encoded in
4651 DER), but you can read its value:
4653 >>> "critical" in ext, ext["critical"]
4654 (False, BOOLEAN False)
4655 >>> ext["critical"] = Boolean(True)
4656 >>> "critical" in ext, ext["critical"]
4657 (True, BOOLEAN True)
4659 All defaulted values are always optional.
4661 .. _allow_default_values_ctx:
4663 DER prohibits default value encoding and will raise an error if
4664 default value is unexpectedly met during decode.
4665 If :ref:`bered <bered_ctx>` context option is set, then no error
4666 will be raised, but ``bered`` attribute set. You can disable strict
4667 defaulted values existence validation by setting
4668 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4670 Two sequences are equal if they have equal specification (schema),
4671 implicit/explicit tagging and the same values.
4673 __slots__ = ("specs",)
4674 tag_default = tag_encode(form=TagFormConstructed, num=16)
4675 asn1_type_name = "SEQUENCE"
4687 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4689 schema = getattr(self, "schema", ())
4691 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4694 if value is not None:
4695 if issubclass(value.__class__, Sequence):
4696 self._value = value._value
4697 elif hasattr(value, "__iter__"):
4698 for seq_key, seq_value in value:
4699 self[seq_key] = seq_value
4701 raise InvalidValueType((Sequence,))
4702 if default is not None:
4703 if not issubclass(default.__class__, Sequence):
4704 raise InvalidValueType((Sequence,))
4705 default_value = default._value
4706 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4707 default_obj.specs = self.specs
4708 default_obj._value = default_value
4709 self.default = default_obj
4711 self._value = default_obj.copy()._value
4715 for name, spec in iteritems(self.specs):
4716 value = self._value.get(name)
4728 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4730 return any(value.bered for value in itervalues(self._value))
4733 obj = self.__class__(schema=self.specs)
4735 obj._expl = self._expl
4736 obj.default = self.default
4737 obj.optional = self.optional
4738 obj.offset = self.offset
4739 obj.llen = self.llen
4740 obj.vlen = self.vlen
4741 obj.expl_lenindef = self.expl_lenindef
4742 obj.lenindef = self.lenindef
4743 obj.ber_encoded = self.ber_encoded
4744 obj._value = {k: v.copy() for k, v in iteritems(self._value)}
4747 def __eq__(self, their):
4748 if not isinstance(their, self.__class__):
4751 self.specs == their.specs and
4752 self.tag == their.tag and
4753 self._expl == their._expl and
4754 self._value == their._value
4765 return self.__class__(
4768 impl=self.tag if impl is None else impl,
4769 expl=self._expl if expl is None else expl,
4770 default=self.default if default is None else default,
4771 optional=self.optional if optional is None else optional,
4774 def __contains__(self, key):
4775 return key in self._value
4777 def __setitem__(self, key, value):
4778 spec = self.specs.get(key)
4780 raise ObjUnknown(key)
4782 self._value.pop(key, None)
4784 if not isinstance(value, spec.__class__):
4785 raise InvalidValueType((spec.__class__,))
4786 value = spec(value=value)
4787 if spec.default is not None and value == spec.default:
4788 self._value.pop(key, None)
4790 self._value[key] = value
4792 def __getitem__(self, key):
4793 value = self._value.get(key)
4794 if value is not None:
4796 spec = self.specs.get(key)
4798 raise ObjUnknown(key)
4799 if spec.default is not None:
4803 def _encoded_values(self):
4805 for name, spec in iteritems(self.specs):
4806 value = self._value.get(name)
4810 raise ObjNotReady(name)
4811 raws.append(value.encode())
4815 v = b"".join(self._encoded_values())
4816 return b"".join((self.tag, len_encode(len(v)), v))
4818 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4820 t, tlen, lv = tag_strip(tlv)
4821 except DecodeError as err:
4822 raise err.__class__(
4824 klass=self.__class__,
4825 decode_path=decode_path,
4830 klass=self.__class__,
4831 decode_path=decode_path,
4834 if tag_only: # pragma: no cover
4837 ctx_bered = ctx.get("bered", False)
4839 l, llen, v = len_decode(lv)
4840 except LenIndefForm as err:
4842 raise err.__class__(
4844 klass=self.__class__,
4845 decode_path=decode_path,
4848 l, llen, v = 0, 1, lv[1:]
4850 except DecodeError as err:
4851 raise err.__class__(
4853 klass=self.__class__,
4854 decode_path=decode_path,
4858 raise NotEnoughData(
4859 "encoded length is longer than data",
4860 klass=self.__class__,
4861 decode_path=decode_path,
4865 v, tail = v[:l], v[l:]
4867 sub_offset = offset + tlen + llen
4870 ctx_allow_default_values = ctx.get("allow_default_values", False)
4871 for name, spec in iteritems(self.specs):
4872 if spec.optional and (
4873 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4877 sub_decode_path = decode_path + (name,)
4879 value, v_tail = spec.decode(
4883 decode_path=sub_decode_path,
4885 _ctx_immutable=False,
4892 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4893 if defined is not None:
4894 defined_by, defined_spec = defined
4895 if issubclass(value.__class__, SequenceOf):
4896 for i, _value in enumerate(value):
4897 sub_sub_decode_path = sub_decode_path + (
4899 DecodePathDefBy(defined_by),
4901 defined_value, defined_tail = defined_spec.decode(
4902 memoryview(bytes(_value)),
4904 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4905 if value.expled else (value.tlen + value.llen)
4908 decode_path=sub_sub_decode_path,
4910 _ctx_immutable=False,
4912 if len(defined_tail) > 0:
4915 klass=self.__class__,
4916 decode_path=sub_sub_decode_path,
4919 _value.defined = (defined_by, defined_value)
4921 defined_value, defined_tail = defined_spec.decode(
4922 memoryview(bytes(value)),
4924 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4925 if value.expled else (value.tlen + value.llen)
4928 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4930 _ctx_immutable=False,
4932 if len(defined_tail) > 0:
4935 klass=self.__class__,
4936 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4939 value.defined = (defined_by, defined_value)
4941 value_len = value.fulllen
4943 sub_offset += value_len
4945 if spec.default is not None and value == spec.default:
4946 if ctx_bered or ctx_allow_default_values:
4950 "DEFAULT value met",
4951 klass=self.__class__,
4952 decode_path=sub_decode_path,
4955 values[name] = value
4957 spec_defines = getattr(spec, "defines", ())
4958 if len(spec_defines) == 0:
4959 defines_by_path = ctx.get("defines_by_path", ())
4960 if len(defines_by_path) > 0:
4961 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4962 if spec_defines is not None and len(spec_defines) > 0:
4963 for rel_path, schema in spec_defines:
4964 defined = schema.get(value, None)
4965 if defined is not None:
4966 ctx.setdefault("_defines", []).append((
4967 abs_decode_path(sub_decode_path[:-1], rel_path),
4971 if v[:EOC_LEN].tobytes() != EOC:
4974 klass=self.__class__,
4975 decode_path=decode_path,
4983 klass=self.__class__,
4984 decode_path=decode_path,
4987 obj = self.__class__(
4991 default=self.default,
4992 optional=self.optional,
4993 _decoded=(offset, llen, vlen),
4996 obj.lenindef = lenindef
4997 obj.ber_encoded = ber_encoded
5001 value = pp_console_row(next(self.pps()))
5003 for name in self.specs:
5004 _value = self._value.get(name)
5007 cols.append("%s: %s" % (name, repr(_value)))
5008 return "%s[%s]" % (value, "; ".join(cols))
5010 def pps(self, decode_path=()):
5013 asn1_type_name=self.asn1_type_name,
5014 obj_name=self.__class__.__name__,
5015 decode_path=decode_path,
5016 optional=self.optional,
5017 default=self == self.default,
5018 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5019 expl=None if self._expl is None else tag_decode(self._expl),
5024 expl_offset=self.expl_offset if self.expled else None,
5025 expl_tlen=self.expl_tlen if self.expled else None,
5026 expl_llen=self.expl_llen if self.expled else None,
5027 expl_vlen=self.expl_vlen if self.expled else None,
5028 expl_lenindef=self.expl_lenindef,
5029 lenindef=self.lenindef,
5030 ber_encoded=self.ber_encoded,
5033 for name in self.specs:
5034 value = self._value.get(name)
5037 yield value.pps(decode_path=decode_path + (name,))
5038 for pp in self.pps_lenindef(decode_path):
5042 class Set(Sequence):
5043 """``SET`` structure type
5045 Its usage is identical to :py:class:`pyderasn.Sequence`.
5047 .. _allow_unordered_set_ctx:
5049 DER prohibits unordered values encoding and will raise an error
5050 during decode. If If :ref:`bered <bered_ctx>` context option is set,
5051 then no error will occure. Also you can disable strict values
5052 ordering check by setting ``"allow_unordered_set": True``
5053 :ref:`context <ctx>` option.
5056 tag_default = tag_encode(form=TagFormConstructed, num=17)
5057 asn1_type_name = "SET"
5060 raws = self._encoded_values()
5063 return b"".join((self.tag, len_encode(len(v)), v))
5065 def _specs_items(self):
5066 return iteritems(self.specs)
5068 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5070 t, tlen, lv = tag_strip(tlv)
5071 except DecodeError as err:
5072 raise err.__class__(
5074 klass=self.__class__,
5075 decode_path=decode_path,
5080 klass=self.__class__,
5081 decode_path=decode_path,
5087 ctx_bered = ctx.get("bered", False)
5089 l, llen, v = len_decode(lv)
5090 except LenIndefForm as err:
5092 raise err.__class__(
5094 klass=self.__class__,
5095 decode_path=decode_path,
5098 l, llen, v = 0, 1, lv[1:]
5100 except DecodeError as err:
5101 raise err.__class__(
5103 klass=self.__class__,
5104 decode_path=decode_path,
5108 raise NotEnoughData(
5109 "encoded length is longer than data",
5110 klass=self.__class__,
5114 v, tail = v[:l], v[l:]
5116 sub_offset = offset + tlen + llen
5119 ctx_allow_default_values = ctx.get("allow_default_values", False)
5120 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5121 value_prev = memoryview(v[:0])
5124 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5126 for name, spec in self._specs_items():
5127 sub_decode_path = decode_path + (name,)
5133 decode_path=sub_decode_path,
5136 _ctx_immutable=False,
5143 klass=self.__class__,
5144 decode_path=decode_path,
5147 value, v_tail = spec.decode(
5151 decode_path=sub_decode_path,
5153 _ctx_immutable=False,
5155 value_len = value.fulllen
5156 if value_prev.tobytes() > v[:value_len].tobytes():
5157 if ctx_bered or ctx_allow_unordered_set:
5161 "unordered " + self.asn1_type_name,
5162 klass=self.__class__,
5163 decode_path=sub_decode_path,
5166 if spec.default is None or value != spec.default:
5168 elif ctx_bered or ctx_allow_default_values:
5172 "DEFAULT value met",
5173 klass=self.__class__,
5174 decode_path=sub_decode_path,
5177 values[name] = value
5178 value_prev = v[:value_len]
5179 sub_offset += value_len
5182 obj = self.__class__(
5186 default=self.default,
5187 optional=self.optional,
5188 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5191 if v[:EOC_LEN].tobytes() != EOC:
5194 klass=self.__class__,
5195 decode_path=decode_path,
5203 "not all values are ready",
5204 klass=self.__class__,
5205 decode_path=decode_path,
5208 obj.ber_encoded = ber_encoded
5212 class SequenceOf(Obj):
5213 """``SEQUENCE OF`` sequence type
5215 For that kind of type you must specify the object it will carry on
5216 (bounds are for example here, not required)::
5218 class Ints(SequenceOf):
5223 >>> ints.append(Integer(123))
5224 >>> ints.append(Integer(234))
5226 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5227 >>> [int(i) for i in ints]
5229 >>> ints.append(Integer(345))
5230 Traceback (most recent call last):
5231 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5234 >>> ints[1] = Integer(345)
5236 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5238 Also you can initialize sequence with preinitialized values:
5240 >>> ints = Ints([Integer(123), Integer(234)])
5242 __slots__ = ("spec", "_bound_min", "_bound_max")
5243 tag_default = tag_encode(form=TagFormConstructed, num=16)
5244 asn1_type_name = "SEQUENCE OF"
5257 super(SequenceOf, self).__init__(
5265 schema = getattr(self, "schema", None)
5267 raise ValueError("schema must be specified")
5269 self._bound_min, self._bound_max = getattr(
5273 ) if bounds is None else bounds
5275 if value is not None:
5276 self._value = self._value_sanitize(value)
5277 if default is not None:
5278 default_value = self._value_sanitize(default)
5279 default_obj = self.__class__(
5284 default_obj._value = default_value
5285 self.default = default_obj
5287 self._value = default_obj.copy()._value
5289 def _value_sanitize(self, value):
5290 if issubclass(value.__class__, SequenceOf):
5291 value = value._value
5292 elif hasattr(value, "__iter__"):
5295 raise InvalidValueType((self.__class__, iter))
5296 if not self._bound_min <= len(value) <= self._bound_max:
5297 raise BoundsError(self._bound_min, len(value), self._bound_max)
5299 if not isinstance(v, self.spec.__class__):
5300 raise InvalidValueType((self.spec.__class__,))
5305 return all(v.ready for v in self._value)
5309 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5311 return any(v.bered for v in self._value)
5314 obj = self.__class__(schema=self.spec)
5315 obj._bound_min = self._bound_min
5316 obj._bound_max = self._bound_max
5318 obj._expl = self._expl
5319 obj.default = self.default
5320 obj.optional = self.optional
5321 obj.offset = self.offset
5322 obj.llen = self.llen
5323 obj.vlen = self.vlen
5324 obj.expl_lenindef = self.expl_lenindef
5325 obj.lenindef = self.lenindef
5326 obj.ber_encoded = self.ber_encoded
5327 obj._value = [v.copy() for v in self._value]
5330 def __eq__(self, their):
5331 if isinstance(their, self.__class__):
5333 self.spec == their.spec and
5334 self.tag == their.tag and
5335 self._expl == their._expl and
5336 self._value == their._value
5338 if hasattr(their, "__iter__"):
5339 return self._value == list(their)
5351 return self.__class__(
5355 (self._bound_min, self._bound_max)
5356 if bounds is None else bounds
5358 impl=self.tag if impl is None else impl,
5359 expl=self._expl if expl is None else expl,
5360 default=self.default if default is None else default,
5361 optional=self.optional if optional is None else optional,
5364 def __contains__(self, key):
5365 return key in self._value
5367 def append(self, value):
5368 if not isinstance(value, self.spec.__class__):
5369 raise InvalidValueType((self.spec.__class__,))
5370 if len(self._value) + 1 > self._bound_max:
5373 len(self._value) + 1,
5376 self._value.append(value)
5379 self._assert_ready()
5380 return iter(self._value)
5383 self._assert_ready()
5384 return len(self._value)
5386 def __setitem__(self, key, value):
5387 if not isinstance(value, self.spec.__class__):
5388 raise InvalidValueType((self.spec.__class__,))
5389 self._value[key] = self.spec(value=value)
5391 def __getitem__(self, key):
5392 return self._value[key]
5394 def _encoded_values(self):
5395 return [v.encode() for v in self._value]
5398 v = b"".join(self._encoded_values())
5399 return b"".join((self.tag, len_encode(len(v)), v))
5401 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5403 t, tlen, lv = tag_strip(tlv)
5404 except DecodeError as err:
5405 raise err.__class__(
5407 klass=self.__class__,
5408 decode_path=decode_path,
5413 klass=self.__class__,
5414 decode_path=decode_path,
5420 ctx_bered = ctx.get("bered", False)
5422 l, llen, v = len_decode(lv)
5423 except LenIndefForm as err:
5425 raise err.__class__(
5427 klass=self.__class__,
5428 decode_path=decode_path,
5431 l, llen, v = 0, 1, lv[1:]
5433 except DecodeError as err:
5434 raise err.__class__(
5436 klass=self.__class__,
5437 decode_path=decode_path,
5441 raise NotEnoughData(
5442 "encoded length is longer than data",
5443 klass=self.__class__,
5444 decode_path=decode_path,
5448 v, tail = v[:l], v[l:]
5450 sub_offset = offset + tlen + llen
5452 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5453 value_prev = memoryview(v[:0])
5457 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5459 sub_decode_path = decode_path + (str(len(_value)),)
5460 value, v_tail = spec.decode(
5464 decode_path=sub_decode_path,
5466 _ctx_immutable=False,
5468 value_len = value.fulllen
5470 if value_prev.tobytes() > v[:value_len].tobytes():
5471 if ctx_bered or ctx_allow_unordered_set:
5475 "unordered " + self.asn1_type_name,
5476 klass=self.__class__,
5477 decode_path=sub_decode_path,
5480 value_prev = v[:value_len]
5481 _value.append(value)
5482 sub_offset += value_len
5486 obj = self.__class__(
5489 bounds=(self._bound_min, self._bound_max),
5492 default=self.default,
5493 optional=self.optional,
5494 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5496 except BoundsError as err:
5499 klass=self.__class__,
5500 decode_path=decode_path,
5504 if v[:EOC_LEN].tobytes() != EOC:
5507 klass=self.__class__,
5508 decode_path=decode_path,
5513 obj.ber_encoded = ber_encoded
5518 pp_console_row(next(self.pps())),
5519 ", ".join(repr(v) for v in self._value),
5522 def pps(self, decode_path=()):
5525 asn1_type_name=self.asn1_type_name,
5526 obj_name=self.__class__.__name__,
5527 decode_path=decode_path,
5528 optional=self.optional,
5529 default=self == self.default,
5530 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5531 expl=None if self._expl is None else tag_decode(self._expl),
5536 expl_offset=self.expl_offset if self.expled else None,
5537 expl_tlen=self.expl_tlen if self.expled else None,
5538 expl_llen=self.expl_llen if self.expled else None,
5539 expl_vlen=self.expl_vlen if self.expled else None,
5540 expl_lenindef=self.expl_lenindef,
5541 lenindef=self.lenindef,
5542 ber_encoded=self.ber_encoded,
5545 for i, value in enumerate(self._value):
5546 yield value.pps(decode_path=decode_path + (str(i),))
5547 for pp in self.pps_lenindef(decode_path):
5551 class SetOf(SequenceOf):
5552 """``SET OF`` sequence type
5554 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5557 tag_default = tag_encode(form=TagFormConstructed, num=17)
5558 asn1_type_name = "SET OF"
5561 raws = self._encoded_values()
5564 return b"".join((self.tag, len_encode(len(v)), v))
5566 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5567 return super(SetOf, self)._decode(
5573 ordering_check=True,
5577 def obj_by_path(pypath): # pragma: no cover
5578 """Import object specified as string Python path
5580 Modules must be separated from classes/functions with ``:``.
5582 >>> obj_by_path("foo.bar:Baz")
5583 <class 'foo.bar.Baz'>
5584 >>> obj_by_path("foo.bar:Baz.boo")
5585 <classmethod 'foo.bar.Baz.boo'>
5587 mod, objs = pypath.rsplit(":", 1)
5588 from importlib import import_module
5589 obj = import_module(mod)
5590 for obj_name in objs.split("."):
5591 obj = getattr(obj, obj_name)
5595 def generic_decoder(): # pragma: no cover
5596 # All of this below is a big hack with self references
5597 choice = PrimitiveTypes()
5598 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5599 choice.specs["SetOf"] = SetOf(schema=choice)
5600 for i in six_xrange(31):
5601 choice.specs["SequenceOf%d" % i] = SequenceOf(
5605 choice.specs["Any"] = Any()
5607 # Class name equals to type name, to omit it from output
5608 class SEQUENCEOF(SequenceOf):
5616 with_decode_path=False,
5617 decode_path_only=(),
5619 def _pprint_pps(pps):
5621 if hasattr(pp, "_fields"):
5623 decode_path_only != () and
5624 pp.decode_path[:len(decode_path_only)] != decode_path_only
5627 if pp.asn1_type_name == Choice.asn1_type_name:
5629 pp_kwargs = pp._asdict()
5630 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5631 pp = _pp(**pp_kwargs)
5632 yield pp_console_row(
5637 with_colours=with_colours,
5638 with_decode_path=with_decode_path,
5639 decode_path_len_decrease=len(decode_path_only),
5641 for row in pp_console_blob(
5643 decode_path_len_decrease=len(decode_path_only),
5647 for row in _pprint_pps(pp):
5649 return "\n".join(_pprint_pps(obj.pps()))
5650 return SEQUENCEOF(), pprint_any
5653 def main(): # pragma: no cover
5655 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5656 parser.add_argument(
5660 help="Skip that number of bytes from the beginning",
5662 parser.add_argument(
5664 help="Python paths to dictionary with OIDs, comma separated",
5666 parser.add_argument(
5668 help="Python path to schema definition to use",
5670 parser.add_argument(
5671 "--defines-by-path",
5672 help="Python path to decoder's defines_by_path",
5674 parser.add_argument(
5676 action="store_true",
5677 help="Disallow BER encoding",
5679 parser.add_argument(
5680 "--print-decode-path",
5681 action="store_true",
5682 help="Print decode paths",
5684 parser.add_argument(
5685 "--decode-path-only",
5686 help="Print only specified decode path",
5688 parser.add_argument(
5690 action="store_true",
5691 help="Allow explicit tag out-of-bound",
5693 parser.add_argument(
5695 type=argparse.FileType("rb"),
5696 help="Path to DER file you want to decode",
5698 args = parser.parse_args()
5699 args.DERFile.seek(args.skip)
5700 der = memoryview(args.DERFile.read())
5701 args.DERFile.close()
5703 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
5704 if args.oids else ()
5707 schema = obj_by_path(args.schema)
5708 from functools import partial
5709 pprinter = partial(pprint, big_blobs=True)
5711 schema, pprinter = generic_decoder()
5713 "bered": not args.nobered,
5714 "allow_expl_oob": args.allow_expl_oob,
5716 if args.defines_by_path is not None:
5717 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5718 obj, tail = schema().decode(der, ctx=ctx)
5722 with_colours=True if environ.get("NO_COLOR") is None else False,
5723 with_decode_path=args.print_decode_path,
5725 () if args.decode_path_only is None else
5726 tuple(args.decode_path_only.split(":"))
5730 print("\nTrailing data: %s" % hexenc(tail))
5733 if __name__ == "__main__":