3 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
4 # Copyright (C) 2017-2020 Sergey Matveev <stargrave@stargrave.org>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as
8 # published by the Free Software Foundation, version 3 of the License.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this program. If not, see <http://www.gnu.org/licenses/>.
17 """Python ASN.1 DER/BER codec with abstract structures
19 This library allows you to marshal various structures in ASN.1 DER
20 format, unmarshal them in BER/CER/DER ones.
24 >>> Integer().decode(raw) == i
27 There are primitive types, holding single values
28 (:py:class:`pyderasn.BitString`,
29 :py:class:`pyderasn.Boolean`,
30 :py:class:`pyderasn.Enumerated`,
31 :py:class:`pyderasn.GeneralizedTime`,
32 :py:class:`pyderasn.Integer`,
33 :py:class:`pyderasn.Null`,
34 :py:class:`pyderasn.ObjectIdentifier`,
35 :py:class:`pyderasn.OctetString`,
36 :py:class:`pyderasn.UTCTime`,
37 :py:class:`various strings <pyderasn.CommonString>`
38 (:py:class:`pyderasn.BMPString`,
39 :py:class:`pyderasn.GeneralString`,
40 :py:class:`pyderasn.GraphicString`,
41 :py:class:`pyderasn.IA5String`,
42 :py:class:`pyderasn.ISO646String`,
43 :py:class:`pyderasn.NumericString`,
44 :py:class:`pyderasn.PrintableString`,
45 :py:class:`pyderasn.T61String`,
46 :py:class:`pyderasn.TeletexString`,
47 :py:class:`pyderasn.UniversalString`,
48 :py:class:`pyderasn.UTF8String`,
49 :py:class:`pyderasn.VideotexString`,
50 :py:class:`pyderasn.VisibleString`)),
51 constructed types, holding multiple primitive types
52 (:py:class:`pyderasn.Sequence`,
53 :py:class:`pyderasn.SequenceOf`,
54 :py:class:`pyderasn.Set`,
55 :py:class:`pyderasn.SetOf`),
56 and special types like
57 :py:class:`pyderasn.Any` and
58 :py:class:`pyderasn.Choice`.
66 Most types in ASN.1 has specific tag for them. ``Obj.tag_default`` is
67 the default tag used during coding process. You can override it with
68 either ``IMPLICIT`` (using ``impl`` keyword argument), or
69 ``EXPLICIT`` one (using ``expl`` keyword argument). Both arguments take
70 raw binary string, containing that tag. You can **not** set implicit and
71 explicit tags simultaneously.
73 There are :py:func:`pyderasn.tag_ctxp` and :py:func:`pyderasn.tag_ctxc`
74 functions, allowing you to easily create ``CONTEXT``
75 ``PRIMITIVE``/``CONSTRUCTED`` tags, by specifying only the required tag
76 number. Pay attention that explicit tags always have *constructed* tag
77 (``tag_ctxc``), but implicit tags for primitive types are primitive
82 >>> Integer(impl=tag_ctxp(1))
84 >>> Integer(expl=tag_ctxc(2))
87 Implicit tag is not explicitly shown.
89 Two objects of the same type, but with different implicit/explicit tags
92 You can get object's effective tag (either default or implicited) through
93 ``tag`` property. You can decode it using :py:func:`pyderasn.tag_decode`
96 >>> tag_decode(tag_ctxc(123))
98 >>> klass, form, num = tag_decode(tag_ctxc(123))
99 >>> klass == TagClassContext
101 >>> form == TagFormConstructed
104 To determine if object has explicit tag, use ``expled`` boolean property
105 and ``expl_tag`` property, returning explicit tag's value.
110 Many objects in sequences could be ``OPTIONAL`` and could have
111 ``DEFAULT`` value. You can specify that object's property using
112 corresponding keyword arguments.
114 >>> Integer(optional=True, default=123)
115 INTEGER 123 OPTIONAL DEFAULT
117 Those specifications do not play any role in primitive value encoding,
118 but are taken into account when dealing with sequences holding them. For
119 example ``TBSCertificate`` sequence holds defaulted, explicitly tagged
122 class Version(Integer):
128 class TBSCertificate(Sequence):
130 ("version", Version(expl=tag_ctxc(0), default="v1")),
133 When default argument is used and value is not specified, then it equals
141 Some objects give ability to set value size constraints. This is either
142 possible integer value, or allowed length of various strings and
143 sequences. Constraints are set in the following way::
148 And values satisfaction is checked as: ``MIN <= X <= MAX``.
150 For simplicity you can also set bounds the following way::
152 bounded_x = X(bounds=(MIN, MAX))
154 If bounds are not satisfied, then :py:exc:`pyderasn.BoundsError` is
160 All objects have ``ready`` boolean property, that tells if object is
161 ready to be encoded. If that kind of action is performed on unready
162 object, then :py:exc:`pyderasn.ObjNotReady` exception will be raised.
164 All objects have ``copy()`` method, that returns their copy, that can be
172 Decoding is performed using ``decode()`` method. ``offset`` optional
173 argument could be used to set initial object's offset in the binary
174 data, for convenience. It returns decoded object and remaining
175 unmarshalled data (tail). Internally all work is done on
176 ``memoryview(data)``, and you can leave returning tail as a memoryview,
177 by specifying ``leavemm=True`` argument.
179 When object is decoded, ``decoded`` property is true and you can safely
180 use following properties:
182 * ``offset`` -- position including initial offset where object's tag starts
183 * ``tlen`` -- length of object's tag
184 * ``llen`` -- length of object's length value
185 * ``vlen`` -- length of object's value
186 * ``tlvlen`` -- length of the whole object
188 Pay attention that those values do **not** include anything related to
189 explicit tag. If you want to know information about it, then use:
191 * ``expled`` -- to know if explicit tag is set
192 * ``expl_offset`` (it is lesser than ``offset``)
195 * ``expl_vlen`` (that actually equals to ordinary ``tlvlen``)
196 * ``fulloffset`` -- it equals to ``expl_offset`` if explicit tag is set,
198 * ``fulllen`` -- it equals to ``expl_len`` if explicit tag is set,
201 When error occurs, :py:exc:`pyderasn.DecodeError` is raised.
208 You can specify so called context keyword argument during ``decode()``
209 invocation. It is dictionary containing various options governing
212 Currently available context options:
214 * :ref:`allow_default_values <allow_default_values_ctx>`
215 * :ref:`allow_expl_oob <allow_expl_oob_ctx>`
216 * :ref:`allow_unordered_set <allow_unordered_set_ctx>`
217 * :ref:`bered <bered_ctx>`
218 * :ref:`defines_by_path <defines_by_path_ctx>`
225 All objects have ``pps()`` method, that is a generator of
226 :py:class:`pyderasn.PP` namedtuple, holding various raw information
227 about the object. If ``pps`` is called on sequences, then all underlying
228 ``PP`` will be yielded.
230 You can use :py:func:`pyderasn.pp_console_row` function, converting
231 those ``PP`` to human readable string. Actually exactly it is used for
232 all object ``repr``. But it is easy to write custom formatters.
234 >>> from pyderasn import pprint
235 >>> encoded = Integer(-12345).encode()
236 >>> obj, tail = Integer().decode(encoded)
237 >>> print(pprint(obj))
238 0 [1,1, 2] INTEGER -12345
242 Example certificate::
244 >>> print(pprint(crt))
245 0 [1,3,1604] Certificate SEQUENCE
246 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE
247 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
248 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595
249 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
250 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
251 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
253 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
254 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF
255 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
256 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
257 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
258 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
259 . . . . . . . 13:02:45:53
261 1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
262 1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
263 1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
265 1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits
266 . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
267 . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
272 Let's parse that output, human::
274 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
275 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
276 0 1 2 3 4 5 6 7 8 9 10 11
280 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
286 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
292 52-2∞ B [1,1,1054]∞ . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
297 Offset of the object, where its DER/BER encoding begins.
298 Pay attention that it does **not** include explicit tag.
300 If explicit tag exists, then this is its length (tag + encoded length).
302 Length of object's tag. For example CHOICE does not have its own tag,
305 Length of encoded length.
307 Length of encoded value.
309 Visual indentation to show the depth of object in the hierarchy.
311 Object's name inside SEQUENCE/CHOICE.
313 If either IMPLICIT or EXPLICIT tag is set, then it will be shown
314 here. "IMPLICIT" is omitted.
316 Object's class name, if set. Omitted if it is just an ordinary simple
317 value (like with ``algorithm`` in example above).
321 Object's value, if set. Can consist of multiple words (like OCTET/BIT
322 STRINGs above). We see ``v3`` value in Version, because it is named.
323 ``rdnSequence`` is the choice of CHOICE type.
325 Possible other flags like OPTIONAL and DEFAULT, if value equals to the
326 default one, specified in the schema.
328 Shows does object contains any kind of BER encoded data (possibly
329 Sequence holding BER-encoded underlying value).
331 Only applicable to BER encoded data. Indefinite length encoding mark.
333 Only applicable to BER encoded data. If object has BER-specific
334 encoding, then ``BER`` will be shown. It does not depend on indefinite
335 length encoding. ``EOC``, ``BOOLEAN``, ``BIT STRING``, ``OCTET STRING``
336 (and its derivatives), ``SET``, ``SET OF`` could be BERed.
344 ASN.1 structures often have ANY and OCTET STRING fields, that are
345 DEFINED BY some previously met ObjectIdentifier. This library provides
346 ability to specify mapping between some OID and field that must be
347 decoded with specific specification.
354 :py:class:`pyderasn.ObjectIdentifier` field inside
355 :py:class:`pyderasn.Sequence` can hold mapping between OIDs and
356 necessary for decoding structures. For example, CMS (:rfc:`5652`)
359 class ContentInfo(Sequence):
361 ("contentType", ContentType(defines=((("content",), {
362 id_digestedData: DigestedData(),
363 id_signedData: SignedData(),
365 ("content", Any(expl=tag_ctxc(0))),
368 ``contentType`` field tells that it defines that ``content`` must be
369 decoded with ``SignedData`` specification, if ``contentType`` equals to
370 ``id-signedData``. The same applies to ``DigestedData``. If
371 ``contentType`` contains unknown OID, then no automatic decoding is
374 You can specify multiple fields, that will be autodecoded -- that is why
375 ``defines`` kwarg is a sequence. You can specify defined field
376 relatively or absolutely to current decode path. For example ``defines``
377 for AlgorithmIdentifier of X.509's
378 ``tbsCertificate:subjectPublicKeyInfo:algorithm:algorithm``::
382 id_ecPublicKey: ECParameters(),
383 id_GostR3410_2001: GostR34102001PublicKeyParameters(),
385 (("..", "subjectPublicKey"), {
386 id_rsaEncryption: RSAPublicKey(),
387 id_GostR3410_2001: OctetString(),
391 tells that if certificate's SPKI algorithm is GOST R 34.10-2001, then
392 autodecode its parameters inside SPKI's algorithm and its public key
395 Following types can be automatically decoded (DEFINED BY):
397 * :py:class:`pyderasn.Any`
398 * :py:class:`pyderasn.BitString` (that is multiple of 8 bits)
399 * :py:class:`pyderasn.OctetString`
400 * :py:class:`pyderasn.SequenceOf`/:py:class:`pyderasn.SetOf`
401 ``Any``/``BitString``/``OctetString``-s
403 When any of those fields is automatically decoded, then ``.defined``
404 attribute contains ``(OID, value)`` tuple. ``OID`` tells by which OID it
405 was defined, ``value`` contains corresponding decoded value. For example
406 above, ``content_info["content"].defined == (id_signedData, signed_data)``.
408 .. _defines_by_path_ctx:
410 defines_by_path context option
411 ______________________________
413 Sometimes you either can not or do not want to explicitly set *defines*
414 in the scheme. You can dynamically apply those definitions when calling
415 ``.decode()`` method.
417 Specify ``defines_by_path`` key in the :ref:`decode context <ctx>`. Its
418 value must be sequence of following tuples::
420 (decode_path, defines)
422 where ``decode_path`` is a tuple holding so-called decode path to the
423 exact :py:class:`pyderasn.ObjectIdentifier` field you want to apply
424 ``defines``, holding exactly the same value as accepted in its
425 :ref:`keyword argument <defines>`.
427 For example, again for CMS, you want to automatically decode
428 ``SignedData`` and CMC's (:rfc:`5272`) ``PKIData`` and ``PKIResponse``
429 structures it may hold. Also, automatically decode ``controlSequence``
432 content_info, tail = ContentInfo().decode(data, ctx={"defines_by_path": (
435 ((("content",), {id_signedData: SignedData()}),),
440 DecodePathDefBy(id_signedData),
445 id_cct_PKIData: PKIData(),
446 id_cct_PKIResponse: PKIResponse(),
452 DecodePathDefBy(id_signedData),
455 DecodePathDefBy(id_cct_PKIResponse),
461 id_cmc_recipientNonce: RecipientNonce(),
462 id_cmc_senderNonce: SenderNonce(),
463 id_cmc_statusInfoV2: CMCStatusInfoV2(),
464 id_cmc_transactionId: TransactionId(),
469 Pay attention for :py:class:`pyderasn.DecodePathDefBy` and ``any``.
470 First function is useful for path construction when some automatic
471 decoding is already done. ``any`` means literally any value it meet --
472 useful for SEQUENCE/SET OF-s.
479 By default PyDERASN accepts only DER encoded data. It always encodes to
480 DER. But you can optionally enable BER decoding with setting ``bered``
481 :ref:`context <ctx>` argument to True. Indefinite lengths and
482 constructed primitive types should be parsed successfully.
484 * If object is encoded in BER form (not the DER one), then ``ber_encoded``
485 attribute is set to True. Only ``BOOLEAN``, ``BIT STRING``, ``OCTET
486 STRING``, ``OBJECT IDENTIFIER``, ``SEQUENCE``, ``SET``, ``SET OF``
488 * If object has an indefinite length encoding, then its ``lenindef``
489 attribute is set to True. Only ``BIT STRING``, ``OCTET STRING``,
490 ``SEQUENCE``, ``SET``, ``SEQUENCE OF``, ``SET OF``, ``ANY`` can
492 * If object has an indefinite length encoded explicit tag, then
493 ``expl_lenindef`` is set to True.
494 * If object has either any of BER-related encoding (explicit tag
495 indefinite length, object's indefinite length, BER-encoding) or any
496 underlying component has that kind of encoding, then ``bered``
497 attribute is set to True. For example SignedData CMS can have
498 ``ContentInfo:content:signerInfos:*`` ``bered`` value set to True, but
499 ``ContentInfo:content:signerInfos:*:signedAttrs`` won't.
501 EOC (end-of-contents) token's length is taken in advance in object's
504 .. _allow_expl_oob_ctx:
506 Allow explicit tag out-of-bound
507 -------------------------------
509 Invalid BER encoding could contain ``EXPLICIT`` tag containing more than
510 one value, more than one object. If you set ``allow_expl_oob`` context
511 option to True, then no error will be raised and that invalid encoding
512 will be silently further processed. But pay attention that offsets and
513 lengths will be invalid in that case.
517 This option should be used only for skipping some decode errors, just
518 to see the decoded structure somehow.
522 .. autoclass:: pyderasn.Obj
530 .. autoclass:: pyderasn.Boolean
535 .. autoclass:: pyderasn.Integer
540 .. autoclass:: pyderasn.BitString
545 .. autoclass:: pyderasn.OctetString
550 .. autoclass:: pyderasn.Null
555 .. autoclass:: pyderasn.ObjectIdentifier
560 .. autoclass:: pyderasn.Enumerated
564 .. autoclass:: pyderasn.CommonString
568 .. autoclass:: pyderasn.NumericString
572 .. autoclass:: pyderasn.PrintableString
576 .. autoclass:: pyderasn.UTCTime
577 :members: __init__, todatetime
581 .. autoclass:: pyderasn.GeneralizedTime
588 .. autoclass:: pyderasn.Choice
593 .. autoclass:: PrimitiveTypes
597 .. autoclass:: pyderasn.Any
605 .. autoclass:: pyderasn.Sequence
610 .. autoclass:: pyderasn.Set
615 .. autoclass:: pyderasn.SequenceOf
620 .. autoclass:: pyderasn.SetOf
626 .. autofunction:: pyderasn.abs_decode_path
627 .. autofunction:: pyderasn.colonize_hex
628 .. autofunction:: pyderasn.hexenc
629 .. autofunction:: pyderasn.hexdec
630 .. autofunction:: pyderasn.tag_encode
631 .. autofunction:: pyderasn.tag_decode
632 .. autofunction:: pyderasn.tag_ctxp
633 .. autofunction:: pyderasn.tag_ctxc
634 .. autoclass:: pyderasn.DecodeError
636 .. autoclass:: pyderasn.NotEnoughData
637 .. autoclass:: pyderasn.LenIndefForm
638 .. autoclass:: pyderasn.TagMismatch
639 .. autoclass:: pyderasn.InvalidLength
640 .. autoclass:: pyderasn.InvalidOID
641 .. autoclass:: pyderasn.ObjUnknown
642 .. autoclass:: pyderasn.ObjNotReady
643 .. autoclass:: pyderasn.InvalidValueType
644 .. autoclass:: pyderasn.BoundsError
647 from codecs import getdecoder
648 from codecs import getencoder
649 from collections import namedtuple
650 from collections import OrderedDict
651 from copy import copy
652 from datetime import datetime
653 from math import ceil
654 from os import environ
655 from string import ascii_letters
656 from string import digits
658 from six import add_metaclass
659 from six import binary_type
660 from six import byte2int
661 from six import indexbytes
662 from six import int2byte
663 from six import integer_types
664 from six import iterbytes
665 from six import iteritems
666 from six import itervalues
668 from six import string_types
669 from six import text_type
670 from six import unichr as six_unichr
671 from six.moves import xrange as six_xrange
675 from termcolor import colored
676 except ImportError: # pragma: no cover
677 def colored(what, *args, **kwargs):
722 "TagClassApplication",
726 "TagFormConstructed",
737 TagClassUniversal = 0
738 TagClassApplication = 1 << 6
739 TagClassContext = 1 << 7
740 TagClassPrivate = 1 << 6 | 1 << 7
742 TagFormConstructed = 1 << 5
745 TagClassApplication: "APPLICATION ",
746 TagClassPrivate: "PRIVATE ",
747 TagClassUniversal: "UNIV ",
751 LENINDEF = b"\x80" # length indefinite mark
752 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
755 ########################################################################
757 ########################################################################
759 class ASN1Error(ValueError):
763 class DecodeError(ASN1Error):
764 def __init__(self, msg="", klass=None, decode_path=(), offset=0):
766 :param str msg: reason of decode failing
767 :param klass: optional exact DecodeError inherited class (like
768 :py:exc:`NotEnoughData`, :py:exc:`TagMismatch`,
769 :py:exc:`InvalidLength`)
770 :param decode_path: tuple of strings. It contains human
771 readable names of the fields through which
772 decoding process has passed
773 :param int offset: binary offset where failure happened
775 super(DecodeError, self).__init__()
778 self.decode_path = decode_path
784 "" if self.klass is None else self.klass.__name__,
786 ("(%s)" % ":".join(str(dp) for dp in self.decode_path))
787 if len(self.decode_path) > 0 else ""
789 ("(at %d)" % self.offset) if self.offset > 0 else "",
795 return "%s(%s)" % (self.__class__.__name__, self)
798 class NotEnoughData(DecodeError):
802 class LenIndefForm(DecodeError):
806 class TagMismatch(DecodeError):
810 class InvalidLength(DecodeError):
814 class InvalidOID(DecodeError):
818 class ObjUnknown(ASN1Error):
819 def __init__(self, name):
820 super(ObjUnknown, self).__init__()
824 return "object is unknown: %s" % self.name
827 return "%s(%s)" % (self.__class__.__name__, self)
830 class ObjNotReady(ASN1Error):
831 def __init__(self, name):
832 super(ObjNotReady, self).__init__()
836 return "object is not ready: %s" % self.name
839 return "%s(%s)" % (self.__class__.__name__, self)
842 class InvalidValueType(ASN1Error):
843 def __init__(self, expected_types):
844 super(InvalidValueType, self).__init__()
845 self.expected_types = expected_types
848 return "invalid value type, expected: %s" % ", ".join(
849 [repr(t) for t in self.expected_types]
853 return "%s(%s)" % (self.__class__.__name__, self)
856 class BoundsError(ASN1Error):
857 def __init__(self, bound_min, value, bound_max):
858 super(BoundsError, self).__init__()
859 self.bound_min = bound_min
861 self.bound_max = bound_max
864 return "unsatisfied bounds: %s <= %s <= %s" % (
871 return "%s(%s)" % (self.__class__.__name__, self)
874 ########################################################################
876 ########################################################################
878 _hexdecoder = getdecoder("hex")
879 _hexencoder = getencoder("hex")
883 """Binary data to hexadecimal string convert
885 return _hexdecoder(data)[0]
889 """Hexadecimal string to binary data convert
891 return _hexencoder(data)[0].decode("ascii")
894 def int_bytes_len(num, byte_len=8):
897 return int(ceil(float(num.bit_length()) / byte_len))
900 def zero_ended_encode(num):
901 octets = bytearray(int_bytes_len(num, 7))
903 octets[i] = num & 0x7F
907 octets[i] = 0x80 | (num & 0x7F)
913 def tag_encode(num, klass=TagClassUniversal, form=TagFormPrimitive):
914 """Encode tag to binary form
916 :param int num: tag's number
917 :param int klass: tag's class (:py:data:`pyderasn.TagClassUniversal`,
918 :py:data:`pyderasn.TagClassContext`,
919 :py:data:`pyderasn.TagClassApplication`,
920 :py:data:`pyderasn.TagClassPrivate`)
921 :param int form: tag's form (:py:data:`pyderasn.TagFormPrimitive`,
922 :py:data:`pyderasn.TagFormConstructed`)
926 return int2byte(klass | form | num)
927 # [XX|X|11111][1.......][1.......] ... [0.......]
928 return int2byte(klass | form | 31) + zero_ended_encode(num)
932 """Decode tag from binary form
936 No validation is performed, assuming that it has already passed.
938 It returns tuple with three integers, as
939 :py:func:`pyderasn.tag_encode` accepts.
941 first_octet = byte2int(tag)
942 klass = first_octet & 0xC0
943 form = first_octet & 0x20
944 if first_octet & 0x1F < 0x1F:
945 return (klass, form, first_octet & 0x1F)
947 for octet in iterbytes(tag[1:]):
950 return (klass, form, num)
954 """Create CONTEXT PRIMITIVE tag
956 return tag_encode(num=num, klass=TagClassContext, form=TagFormPrimitive)
960 """Create CONTEXT CONSTRUCTED tag
962 return tag_encode(num=num, klass=TagClassContext, form=TagFormConstructed)
966 """Take off tag from the data
968 :returns: (encoded tag, tag length, remaining data)
971 raise NotEnoughData("no data at all")
972 if byte2int(data) & 0x1F < 31:
973 return data[:1], 1, data[1:]
978 raise DecodeError("unfinished tag")
979 if indexbytes(data, i) & 0x80 == 0:
982 return data[:i], i, data[i:]
988 octets = bytearray(int_bytes_len(l) + 1)
989 octets[0] = 0x80 | (len(octets) - 1)
990 for i in six_xrange(len(octets) - 1, 0, -1):
996 def len_decode(data):
999 :returns: (decoded length, length's length, remaining data)
1000 :raises LenIndefForm: if indefinite form encoding is met
1003 raise NotEnoughData("no data at all")
1004 first_octet = byte2int(data)
1005 if first_octet & 0x80 == 0:
1006 return first_octet, 1, data[1:]
1007 octets_num = first_octet & 0x7F
1008 if octets_num + 1 > len(data):
1009 raise NotEnoughData("encoded length is longer than data")
1011 raise LenIndefForm()
1012 if byte2int(data[1:]) == 0:
1013 raise DecodeError("leading zeros")
1015 for v in iterbytes(data[1:1 + octets_num]):
1018 raise DecodeError("long form instead of short one")
1019 return l, 1 + octets_num, data[1 + octets_num:]
1022 ########################################################################
1024 ########################################################################
1026 class AutoAddSlots(type):
1027 def __new__(cls, name, bases, _dict):
1028 _dict["__slots__"] = _dict.get("__slots__", ())
1029 return type.__new__(cls, name, bases, _dict)
1032 @add_metaclass(AutoAddSlots)
1034 """Common ASN.1 object class
1036 All ASN.1 types are inherited from it. It has metaclass that
1037 automatically adds ``__slots__`` to all inherited classes.
1061 self.tag = getattr(self, "impl", self.tag_default) if impl is None else impl
1062 self._expl = getattr(self, "expl", None) if expl is None else expl
1063 if self.tag != self.tag_default and self._expl is not None:
1064 raise ValueError("implicit and explicit tags can not be set simultaneously")
1065 if default is not None:
1067 self.optional = optional
1068 self.offset, self.llen, self.vlen = _decoded
1070 self.expl_lenindef = False
1071 self.lenindef = False
1072 self.ber_encoded = False
1075 def ready(self): # pragma: no cover
1076 """Is object ready to be encoded?
1078 raise NotImplementedError()
1080 def _assert_ready(self):
1082 raise ObjNotReady(self.__class__.__name__)
1086 """Is either object or any elements inside is BER encoded?
1088 return self.expl_lenindef or self.lenindef or self.ber_encoded
1092 """Is object decoded?
1094 return (self.llen + self.vlen) > 0
1096 def copy(self): # pragma: no cover
1097 """Make a copy of object, safe to be mutated
1099 raise NotImplementedError()
1103 """See :ref:`decoding`
1105 return len(self.tag)
1109 """See :ref:`decoding`
1111 return self.tlen + self.llen + self.vlen
1113 def __str__(self): # pragma: no cover
1114 return self.__bytes__() if PY2 else self.__unicode__()
1116 def __ne__(self, their):
1117 return not(self == their)
1119 def __gt__(self, their): # pragma: no cover
1120 return not(self < their)
1122 def __le__(self, their): # pragma: no cover
1123 return (self == their) or (self < their)
1125 def __ge__(self, their): # pragma: no cover
1126 return (self == their) or (self > their)
1128 def _encode(self): # pragma: no cover
1129 raise NotImplementedError()
1131 def _decode(self, tlv, offset, decode_path, ctx, tag_only): # pragma: no cover
1132 raise NotImplementedError()
1135 """Encode the structure
1137 :returns: DER representation
1139 raw = self._encode()
1140 if self._expl is None:
1142 return b"".join((self._expl, len_encode(len(raw)), raw))
1152 _ctx_immutable=True,
1156 :param data: either binary or memoryview
1157 :param int offset: initial data's offset
1158 :param bool leavemm: do we need to leave memoryview of remaining
1159 data as is, or convert it to bytes otherwise
1160 :param ctx: optional :ref:`context <ctx>` governing decoding process
1161 :param tag_only: decode only the tag, without length and contents
1162 (used only in Choice and Set structures, trying to
1163 determine if tag satisfies the scheme)
1164 :param _ctx_immutable: do we need to copy ``ctx`` before using it
1165 :returns: (Obj, remaining data)
1167 .. seealso:: :ref:`decoding`
1171 elif _ctx_immutable:
1173 tlv = memoryview(data)
1174 if self._expl is None:
1175 result = self._decode(
1178 decode_path=decode_path,
1187 t, tlen, lv = tag_strip(tlv)
1188 except DecodeError as err:
1189 raise err.__class__(
1191 klass=self.__class__,
1192 decode_path=decode_path,
1197 klass=self.__class__,
1198 decode_path=decode_path,
1202 l, llen, v = len_decode(lv)
1203 except LenIndefForm as err:
1204 if not ctx.get("bered", False):
1205 raise err.__class__(
1207 klass=self.__class__,
1208 decode_path=decode_path,
1212 offset += tlen + llen
1213 result = self._decode(
1216 decode_path=decode_path,
1220 if tag_only: # pragma: no cover
1223 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
1224 if eoc_expected.tobytes() != EOC:
1227 klass=self.__class__,
1228 decode_path=decode_path,
1232 obj.expl_lenindef = True
1233 except DecodeError as err:
1234 raise err.__class__(
1236 klass=self.__class__,
1237 decode_path=decode_path,
1242 raise NotEnoughData(
1243 "encoded length is longer than data",
1244 klass=self.__class__,
1245 decode_path=decode_path,
1248 result = self._decode(
1250 offset=offset + tlen + llen,
1251 decode_path=decode_path,
1255 if tag_only: # pragma: no cover
1258 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
1260 "explicit tag out-of-bound, longer than data",
1261 klass=self.__class__,
1262 decode_path=decode_path,
1265 return obj, (tail if leavemm else tail.tobytes())
1269 """See :ref:`decoding`
1271 return self._expl is not None
1275 """See :ref:`decoding`
1280 def expl_tlen(self):
1281 """See :ref:`decoding`
1283 return len(self._expl)
1286 def expl_llen(self):
1287 """See :ref:`decoding`
1289 if self.expl_lenindef:
1291 return len(len_encode(self.tlvlen))
1294 def expl_offset(self):
1295 """See :ref:`decoding`
1297 return self.offset - self.expl_tlen - self.expl_llen
1300 def expl_vlen(self):
1301 """See :ref:`decoding`
1306 def expl_tlvlen(self):
1307 """See :ref:`decoding`
1309 return self.expl_tlen + self.expl_llen + self.expl_vlen
1312 def fulloffset(self):
1313 """See :ref:`decoding`
1315 return self.expl_offset if self.expled else self.offset
1319 """See :ref:`decoding`
1321 return self.expl_tlvlen if self.expled else self.tlvlen
1323 def pps_lenindef(self, decode_path):
1324 if self.lenindef and not (
1325 getattr(self, "defined", None) is not None and
1326 self.defined[1].lenindef
1329 asn1_type_name="EOC",
1331 decode_path=decode_path,
1333 self.offset + self.tlvlen -
1334 (EOC_LEN * 2 if self.expl_lenindef else EOC_LEN)
1342 if self.expl_lenindef:
1344 asn1_type_name="EOC",
1345 obj_name="EXPLICIT",
1346 decode_path=decode_path,
1347 offset=self.expl_offset + self.expl_tlvlen - EOC_LEN,
1356 class DecodePathDefBy(object):
1357 """DEFINED BY representation inside decode path
1359 __slots__ = ("defined_by",)
1361 def __init__(self, defined_by):
1362 self.defined_by = defined_by
1364 def __ne__(self, their):
1365 return not(self == their)
1367 def __eq__(self, their):
1368 if not isinstance(their, self.__class__):
1370 return self.defined_by == their.defined_by
1373 return "DEFINED BY " + str(self.defined_by)
1376 return "<%s: %s>" % (self.__class__.__name__, self.defined_by)
1379 ########################################################################
1381 ########################################################################
1383 PP = namedtuple("PP", (
1411 asn1_type_name="unknown",
1428 expl_lenindef=False,
1459 def _colourize(what, colour, with_colours, attrs=("bold",)):
1460 return colored(what, colour, attrs=attrs) if with_colours else what
1463 def colonize_hex(hexed):
1464 """Separate hexadecimal string with colons
1466 return ":".join(hexed[i:i + 2] for i in six_xrange(0, len(hexed), 2))
1475 with_decode_path=False,
1476 decode_path_len_decrease=0,
1483 " " if pp.expl_offset is None else
1484 ("-%d" % (pp.offset - pp.expl_offset))
1486 LENINDEF_PP_CHAR if pp.expl_lenindef else " ",
1488 col = _colourize(col, "red", with_colours, ())
1489 col += _colourize("B", "red", with_colours) if pp.bered else " "
1491 col = "[%d,%d,%4d]%s" % (
1495 LENINDEF_PP_CHAR if pp.lenindef else " "
1497 col = _colourize(col, "green", with_colours, ())
1499 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1500 if decode_path_len > 0:
1501 cols.append(" ." * decode_path_len)
1502 ent = pp.decode_path[-1]
1503 if isinstance(ent, DecodePathDefBy):
1504 cols.append(_colourize("DEFINED BY", "red", with_colours, ("reverse",)))
1505 value = str(ent.defined_by)
1508 len(oid_maps) > 0 and
1509 ent.defined_by.asn1_type_name ==
1510 ObjectIdentifier.asn1_type_name
1512 for oid_map in oid_maps:
1513 oid_name = oid_map.get(value)
1514 if oid_name is not None:
1515 cols.append(_colourize("%s:" % oid_name, "green", with_colours))
1517 if oid_name is None:
1518 cols.append(_colourize("%s:" % value, "white", with_colours, ("reverse",)))
1520 cols.append(_colourize("%s:" % ent, "yellow", with_colours, ("reverse",)))
1521 if pp.expl is not None:
1522 klass, _, num = pp.expl
1523 col = "[%s%d] EXPLICIT" % (TagClassReprs[klass], num)
1524 cols.append(_colourize(col, "blue", with_colours))
1525 if pp.impl is not None:
1526 klass, _, num = pp.impl
1527 col = "[%s%d]" % (TagClassReprs[klass], num)
1528 cols.append(_colourize(col, "blue", with_colours))
1529 if pp.asn1_type_name.replace(" ", "") != pp.obj_name.upper():
1530 cols.append(_colourize(pp.obj_name, "magenta", with_colours))
1532 cols.append(_colourize("BER", "red", with_colours))
1533 cols.append(_colourize(pp.asn1_type_name, "cyan", with_colours))
1534 if pp.value is not None:
1536 cols.append(_colourize(value, "white", with_colours, ("reverse",)))
1538 len(oid_maps) > 0 and
1539 pp.asn1_type_name == ObjectIdentifier.asn1_type_name
1541 for oid_map in oid_maps:
1542 oid_name = oid_map.get(value)
1543 if oid_name is not None:
1544 cols.append(_colourize("(%s)" % oid_name, "green", with_colours))
1546 if pp.asn1_type_name == Integer.asn1_type_name:
1547 hex_repr = hex(int(pp.obj._value))[2:].upper()
1548 if len(hex_repr) % 2 != 0:
1549 hex_repr = "0" + hex_repr
1550 cols.append(_colourize(
1551 "(%s)" % colonize_hex(hex_repr),
1556 if isinstance(pp.blob, binary_type):
1557 cols.append(hexenc(pp.blob))
1558 elif isinstance(pp.blob, tuple):
1559 cols.append(", ".join(pp.blob))
1561 cols.append(_colourize("OPTIONAL", "red", with_colours))
1563 cols.append(_colourize("DEFAULT", "red", with_colours))
1564 if with_decode_path:
1565 cols.append(_colourize(
1566 "[%s]" % ":".join(str(p) for p in pp.decode_path),
1570 return " ".join(cols)
1573 def pp_console_blob(pp, decode_path_len_decrease=0):
1574 cols = [" " * len("XXXXXYYZZ [X,X,XXXX]Z")]
1575 decode_path_len = len(pp.decode_path) - decode_path_len_decrease
1576 if decode_path_len > 0:
1577 cols.append(" ." * (decode_path_len + 1))
1578 if isinstance(pp.blob, binary_type):
1579 blob = hexenc(pp.blob).upper()
1580 for i in six_xrange(0, len(blob), 32):
1581 chunk = blob[i:i + 32]
1582 yield " ".join(cols + [colonize_hex(chunk)])
1583 elif isinstance(pp.blob, tuple):
1584 yield " ".join(cols + [", ".join(pp.blob)])
1592 with_decode_path=False,
1593 decode_path_only=(),
1595 """Pretty print object
1597 :param Obj obj: object you want to pretty print
1598 :param oid_maps: list of ``OID <-> humand readable string`` dictionary.
1599 When OID from it is met, then its humand readable form
1601 :param big_blobs: if large binary objects are met (like OctetString
1602 values), do we need to print them too, on separate
1604 :param with_colours: colourize output, if ``termcolor`` library
1606 :param with_decode_path: print decode path
1607 :param decode_path_only: print only that specified decode path
1609 def _pprint_pps(pps):
1611 if hasattr(pp, "_fields"):
1613 decode_path_only != () and
1615 str(p) for p in pp.decode_path[:len(decode_path_only)]
1616 ) != decode_path_only
1620 yield pp_console_row(
1625 with_colours=with_colours,
1626 with_decode_path=with_decode_path,
1627 decode_path_len_decrease=len(decode_path_only),
1629 for row in pp_console_blob(
1631 decode_path_len_decrease=len(decode_path_only),
1635 yield pp_console_row(
1640 with_colours=with_colours,
1641 with_decode_path=with_decode_path,
1642 decode_path_len_decrease=len(decode_path_only),
1645 for row in _pprint_pps(pp):
1647 return "\n".join(_pprint_pps(obj.pps()))
1650 ########################################################################
1651 # ASN.1 primitive types
1652 ########################################################################
1655 """``BOOLEAN`` boolean type
1657 >>> b = Boolean(True)
1659 >>> b == Boolean(True)
1665 tag_default = tag_encode(1)
1666 asn1_type_name = "BOOLEAN"
1678 :param value: set the value. Either boolean type, or
1679 :py:class:`pyderasn.Boolean` object
1680 :param bytes impl: override default tag with ``IMPLICIT`` one
1681 :param bytes expl: override default tag with ``EXPLICIT`` one
1682 :param default: set default value. Type same as in ``value``
1683 :param bool optional: is object ``OPTIONAL`` in sequence
1685 super(Boolean, self).__init__(impl, expl, default, optional, _decoded)
1686 self._value = None if value is None else self._value_sanitize(value)
1687 if default is not None:
1688 default = self._value_sanitize(default)
1689 self.default = self.__class__(
1695 self._value = default
1697 def _value_sanitize(self, value):
1698 if isinstance(value, bool):
1700 if issubclass(value.__class__, Boolean):
1702 raise InvalidValueType((self.__class__, bool))
1706 return self._value is not None
1709 obj = self.__class__()
1710 obj._value = self._value
1712 obj._expl = self._expl
1713 obj.default = self.default
1714 obj.optional = self.optional
1715 obj.offset = self.offset
1716 obj.llen = self.llen
1717 obj.vlen = self.vlen
1718 obj.expl_lenindef = self.expl_lenindef
1719 obj.lenindef = self.lenindef
1720 obj.ber_encoded = self.ber_encoded
1723 def __nonzero__(self):
1724 self._assert_ready()
1728 self._assert_ready()
1731 def __eq__(self, their):
1732 if isinstance(their, bool):
1733 return self._value == their
1734 if not issubclass(their.__class__, Boolean):
1737 self._value == their._value and
1738 self.tag == their.tag and
1739 self._expl == their._expl
1750 return self.__class__(
1752 impl=self.tag if impl is None else impl,
1753 expl=self._expl if expl is None else expl,
1754 default=self.default if default is None else default,
1755 optional=self.optional if optional is None else optional,
1759 self._assert_ready()
1763 (b"\xFF" if self._value else b"\x00"),
1766 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
1768 t, _, lv = tag_strip(tlv)
1769 except DecodeError as err:
1770 raise err.__class__(
1772 klass=self.__class__,
1773 decode_path=decode_path,
1778 klass=self.__class__,
1779 decode_path=decode_path,
1785 l, _, v = len_decode(lv)
1786 except DecodeError as err:
1787 raise err.__class__(
1789 klass=self.__class__,
1790 decode_path=decode_path,
1794 raise InvalidLength(
1795 "Boolean's length must be equal to 1",
1796 klass=self.__class__,
1797 decode_path=decode_path,
1801 raise NotEnoughData(
1802 "encoded length is longer than data",
1803 klass=self.__class__,
1804 decode_path=decode_path,
1807 first_octet = byte2int(v)
1809 if first_octet == 0:
1811 elif first_octet == 0xFF:
1813 elif ctx.get("bered", False):
1818 "unacceptable Boolean value",
1819 klass=self.__class__,
1820 decode_path=decode_path,
1823 obj = self.__class__(
1827 default=self.default,
1828 optional=self.optional,
1829 _decoded=(offset, 1, 1),
1831 obj.ber_encoded = ber_encoded
1835 return pp_console_row(next(self.pps()))
1837 def pps(self, decode_path=()):
1840 asn1_type_name=self.asn1_type_name,
1841 obj_name=self.__class__.__name__,
1842 decode_path=decode_path,
1843 value=str(self._value) if self.ready else None,
1844 optional=self.optional,
1845 default=self == self.default,
1846 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
1847 expl=None if self._expl is None else tag_decode(self._expl),
1852 expl_offset=self.expl_offset if self.expled else None,
1853 expl_tlen=self.expl_tlen if self.expled else None,
1854 expl_llen=self.expl_llen if self.expled else None,
1855 expl_vlen=self.expl_vlen if self.expled else None,
1856 expl_lenindef=self.expl_lenindef,
1857 ber_encoded=self.ber_encoded,
1860 for pp in self.pps_lenindef(decode_path):
1865 """``INTEGER`` integer type
1867 >>> b = Integer(-123)
1869 >>> b == Integer(-123)
1874 >>> Integer(2, bounds=(1, 3))
1876 >>> Integer(5, bounds=(1, 3))
1877 Traceback (most recent call last):
1878 pyderasn.BoundsError: unsatisfied bounds: 1 <= 5 <= 3
1882 class Version(Integer):
1889 >>> v = Version("v1")
1896 {'v3': 2, 'v1': 0, 'v2': 1}
1898 __slots__ = ("specs", "_bound_min", "_bound_max")
1899 tag_default = tag_encode(2)
1900 asn1_type_name = "INTEGER"
1914 :param value: set the value. Either integer type, named value
1915 (if ``schema`` is specified in the class), or
1916 :py:class:`pyderasn.Integer` object
1917 :param bounds: set ``(MIN, MAX)`` value constraint.
1918 (-inf, +inf) by default
1919 :param bytes impl: override default tag with ``IMPLICIT`` one
1920 :param bytes expl: override default tag with ``EXPLICIT`` one
1921 :param default: set default value. Type same as in ``value``
1922 :param bool optional: is object ``OPTIONAL`` in sequence
1924 super(Integer, self).__init__(impl, expl, default, optional, _decoded)
1926 specs = getattr(self, "schema", {}) if _specs is None else _specs
1927 self.specs = specs if isinstance(specs, dict) else dict(specs)
1928 self._bound_min, self._bound_max = getattr(
1931 (float("-inf"), float("+inf")),
1932 ) if bounds is None else bounds
1933 if value is not None:
1934 self._value = self._value_sanitize(value)
1935 if default is not None:
1936 default = self._value_sanitize(default)
1937 self.default = self.__class__(
1943 if self._value is None:
1944 self._value = default
1946 def _value_sanitize(self, value):
1947 if isinstance(value, integer_types):
1949 elif issubclass(value.__class__, Integer):
1950 value = value._value
1951 elif isinstance(value, str):
1952 value = self.specs.get(value)
1954 raise ObjUnknown("integer value: %s" % value)
1956 raise InvalidValueType((self.__class__, int, str))
1957 if not self._bound_min <= value <= self._bound_max:
1958 raise BoundsError(self._bound_min, value, self._bound_max)
1963 return self._value is not None
1966 obj = self.__class__(_specs=self.specs)
1967 obj._value = self._value
1968 obj._bound_min = self._bound_min
1969 obj._bound_max = self._bound_max
1971 obj._expl = self._expl
1972 obj.default = self.default
1973 obj.optional = self.optional
1974 obj.offset = self.offset
1975 obj.llen = self.llen
1976 obj.vlen = self.vlen
1977 obj.expl_lenindef = self.expl_lenindef
1978 obj.lenindef = self.lenindef
1979 obj.ber_encoded = self.ber_encoded
1983 self._assert_ready()
1984 return int(self._value)
1987 self._assert_ready()
1990 bytes(self._expl or b"") +
1991 str(self._value).encode("ascii"),
1994 def __eq__(self, their):
1995 if isinstance(their, integer_types):
1996 return self._value == their
1997 if not issubclass(their.__class__, Integer):
2000 self._value == their._value and
2001 self.tag == their.tag and
2002 self._expl == their._expl
2005 def __lt__(self, their):
2006 return self._value < their._value
2010 for name, value in iteritems(self.specs):
2011 if value == self._value:
2024 return self.__class__(
2027 (self._bound_min, self._bound_max)
2028 if bounds is None else bounds
2030 impl=self.tag if impl is None else impl,
2031 expl=self._expl if expl is None else expl,
2032 default=self.default if default is None else default,
2033 optional=self.optional if optional is None else optional,
2038 self._assert_ready()
2042 octets = bytearray([0])
2046 octets = bytearray()
2048 octets.append((value & 0xFF) ^ 0xFF)
2050 if len(octets) == 0 or octets[-1] & 0x80 == 0:
2053 octets = bytearray()
2055 octets.append(value & 0xFF)
2057 if octets[-1] & 0x80 > 0:
2060 octets = bytes(octets)
2062 bytes_len = ceil(value.bit_length() / 8) or 1
2065 octets = value.to_bytes(
2070 except OverflowError:
2074 return b"".join((self.tag, len_encode(len(octets)), octets))
2076 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2078 t, _, lv = tag_strip(tlv)
2079 except DecodeError as err:
2080 raise err.__class__(
2082 klass=self.__class__,
2083 decode_path=decode_path,
2088 klass=self.__class__,
2089 decode_path=decode_path,
2095 l, llen, v = len_decode(lv)
2096 except DecodeError as err:
2097 raise err.__class__(
2099 klass=self.__class__,
2100 decode_path=decode_path,
2104 raise NotEnoughData(
2105 "encoded length is longer than data",
2106 klass=self.__class__,
2107 decode_path=decode_path,
2111 raise NotEnoughData(
2113 klass=self.__class__,
2114 decode_path=decode_path,
2117 v, tail = v[:l], v[l:]
2118 first_octet = byte2int(v)
2120 second_octet = byte2int(v[1:])
2122 ((first_octet == 0x00) and (second_octet & 0x80 == 0)) or
2123 ((first_octet == 0xFF) and (second_octet & 0x80 != 0))
2126 "non normalized integer",
2127 klass=self.__class__,
2128 decode_path=decode_path,
2133 if first_octet & 0x80 > 0:
2134 octets = bytearray()
2135 for octet in bytearray(v):
2136 octets.append(octet ^ 0xFF)
2137 for octet in octets:
2138 value = (value << 8) | octet
2142 for octet in bytearray(v):
2143 value = (value << 8) | octet
2145 value = int.from_bytes(v, byteorder="big", signed=True)
2147 obj = self.__class__(
2149 bounds=(self._bound_min, self._bound_max),
2152 default=self.default,
2153 optional=self.optional,
2155 _decoded=(offset, llen, l),
2157 except BoundsError as err:
2160 klass=self.__class__,
2161 decode_path=decode_path,
2167 return pp_console_row(next(self.pps()))
2169 def pps(self, decode_path=()):
2172 asn1_type_name=self.asn1_type_name,
2173 obj_name=self.__class__.__name__,
2174 decode_path=decode_path,
2175 value=(self.named or str(self._value)) if self.ready else None,
2176 optional=self.optional,
2177 default=self == self.default,
2178 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2179 expl=None if self._expl is None else tag_decode(self._expl),
2184 expl_offset=self.expl_offset if self.expled else None,
2185 expl_tlen=self.expl_tlen if self.expled else None,
2186 expl_llen=self.expl_llen if self.expled else None,
2187 expl_vlen=self.expl_vlen if self.expled else None,
2188 expl_lenindef=self.expl_lenindef,
2191 for pp in self.pps_lenindef(decode_path):
2195 SET01 = frozenset(("0", "1"))
2198 class BitString(Obj):
2199 """``BIT STRING`` bit string type
2201 >>> BitString(b"hello world")
2202 BIT STRING 88 bits 68656c6c6f20776f726c64
2205 >>> b == b"hello world"
2210 >>> BitString("'0A3B5F291CD'H")
2211 BIT STRING 44 bits 0a3b5f291cd0
2212 >>> b = BitString("'010110000000'B")
2213 BIT STRING 12 bits 5800
2216 >>> b[0], b[1], b[2], b[3]
2217 (False, True, False, True)
2221 [False, True, False, True, True, False, False, False, False, False, False, False]
2225 class KeyUsage(BitString):
2227 ("digitalSignature", 0),
2228 ("nonRepudiation", 1),
2229 ("keyEncipherment", 2),
2232 >>> b = KeyUsage(("keyEncipherment", "nonRepudiation"))
2233 KeyUsage BIT STRING 3 bits nonRepudiation, keyEncipherment
2235 ['nonRepudiation', 'keyEncipherment']
2237 {'nonRepudiation': 1, 'digitalSignature': 0, 'keyEncipherment': 2}
2241 Pay attention that BIT STRING can be encoded both in primitive
2242 and constructed forms. Decoder always checks constructed form tag
2243 additionally to specified primitive one. If BER decoding is
2244 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2245 of DER restrictions.
2247 __slots__ = ("tag_constructed", "specs", "defined")
2248 tag_default = tag_encode(3)
2249 asn1_type_name = "BIT STRING"
2262 :param value: set the value. Either binary type, tuple of named
2263 values (if ``schema`` is specified in the class),
2264 string in ``'XXX...'B`` form, or
2265 :py:class:`pyderasn.BitString` object
2266 :param bytes impl: override default tag with ``IMPLICIT`` one
2267 :param bytes expl: override default tag with ``EXPLICIT`` one
2268 :param default: set default value. Type same as in ``value``
2269 :param bool optional: is object ``OPTIONAL`` in sequence
2271 super(BitString, self).__init__(impl, expl, default, optional, _decoded)
2272 specs = getattr(self, "schema", {}) if _specs is None else _specs
2273 self.specs = specs if isinstance(specs, dict) else dict(specs)
2274 self._value = None if value is None else self._value_sanitize(value)
2275 if default is not None:
2276 default = self._value_sanitize(default)
2277 self.default = self.__class__(
2283 self._value = default
2285 tag_klass, _, tag_num = tag_decode(self.tag)
2286 self.tag_constructed = tag_encode(
2288 form=TagFormConstructed,
2292 def _bits2octets(self, bits):
2293 if len(self.specs) > 0:
2294 bits = bits.rstrip("0")
2296 bits += "0" * ((8 - (bit_len % 8)) % 8)
2297 octets = bytearray(len(bits) // 8)
2298 for i in six_xrange(len(octets)):
2299 octets[i] = int(bits[i * 8:(i * 8) + 8], 2)
2300 return bit_len, bytes(octets)
2302 def _value_sanitize(self, value):
2303 if isinstance(value, (string_types, binary_type)):
2305 isinstance(value, string_types) and
2306 value.startswith("'")
2308 if value.endswith("'B"):
2310 if not frozenset(value) <= SET01:
2311 raise ValueError("B's coding contains unacceptable chars")
2312 return self._bits2octets(value)
2313 if value.endswith("'H"):
2317 hexdec(value + ("" if len(value) % 2 == 0 else "0")),
2319 if isinstance(value, binary_type):
2320 return (len(value) * 8, value)
2321 raise InvalidValueType((self.__class__, string_types, binary_type))
2322 if isinstance(value, tuple):
2325 isinstance(value[0], integer_types) and
2326 isinstance(value[1], binary_type)
2331 bit = self.specs.get(name)
2333 raise ObjUnknown("BitString value: %s" % name)
2336 return self._bits2octets("")
2337 bits = frozenset(bits)
2338 return self._bits2octets("".join(
2339 ("1" if bit in bits else "0")
2340 for bit in six_xrange(max(bits) + 1)
2342 if issubclass(value.__class__, BitString):
2344 raise InvalidValueType((self.__class__, binary_type, string_types))
2348 return self._value is not None
2351 obj = self.__class__(_specs=self.specs)
2353 if value is not None:
2354 value = (value[0], value[1])
2357 obj._expl = self._expl
2358 obj.default = self.default
2359 obj.optional = self.optional
2360 obj.offset = self.offset
2361 obj.llen = self.llen
2362 obj.vlen = self.vlen
2363 obj.expl_lenindef = self.expl_lenindef
2364 obj.lenindef = self.lenindef
2365 obj.ber_encoded = self.ber_encoded
2369 self._assert_ready()
2370 for i in six_xrange(self._value[0]):
2375 self._assert_ready()
2376 return self._value[0]
2378 def __bytes__(self):
2379 self._assert_ready()
2380 return self._value[1]
2382 def __eq__(self, their):
2383 if isinstance(their, bytes):
2384 return self._value[1] == their
2385 if not issubclass(their.__class__, BitString):
2388 self._value == their._value and
2389 self.tag == their.tag and
2390 self._expl == their._expl
2395 return [name for name, bit in iteritems(self.specs) if self[bit]]
2405 return self.__class__(
2407 impl=self.tag if impl is None else impl,
2408 expl=self._expl if expl is None else expl,
2409 default=self.default if default is None else default,
2410 optional=self.optional if optional is None else optional,
2414 def __getitem__(self, key):
2415 if isinstance(key, int):
2416 bit_len, octets = self._value
2420 byte2int(memoryview(octets)[key // 8:]) >>
2423 if isinstance(key, string_types):
2424 value = self.specs.get(key)
2426 raise ObjUnknown("BitString value: %s" % key)
2428 raise InvalidValueType((int, str))
2431 self._assert_ready()
2432 bit_len, octets = self._value
2435 len_encode(len(octets) + 1),
2436 int2byte((8 - bit_len % 8) % 8),
2440 def _decode_chunk(self, lv, offset, decode_path):
2442 l, llen, v = len_decode(lv)
2443 except DecodeError as err:
2444 raise err.__class__(
2446 klass=self.__class__,
2447 decode_path=decode_path,
2451 raise NotEnoughData(
2452 "encoded length is longer than data",
2453 klass=self.__class__,
2454 decode_path=decode_path,
2458 raise NotEnoughData(
2460 klass=self.__class__,
2461 decode_path=decode_path,
2464 pad_size = byte2int(v)
2465 if l == 1 and pad_size != 0:
2467 "invalid empty value",
2468 klass=self.__class__,
2469 decode_path=decode_path,
2475 klass=self.__class__,
2476 decode_path=decode_path,
2479 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2482 klass=self.__class__,
2483 decode_path=decode_path,
2486 v, tail = v[:l], v[l:]
2487 obj = self.__class__(
2488 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2491 default=self.default,
2492 optional=self.optional,
2494 _decoded=(offset, llen, l),
2498 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2500 t, tlen, lv = tag_strip(tlv)
2501 except DecodeError as err:
2502 raise err.__class__(
2504 klass=self.__class__,
2505 decode_path=decode_path,
2509 if tag_only: # pragma: no cover
2511 return self._decode_chunk(lv, offset, decode_path)
2512 if t == self.tag_constructed:
2513 if not ctx.get("bered", False):
2515 "unallowed BER constructed encoding",
2516 klass=self.__class__,
2517 decode_path=decode_path,
2520 if tag_only: # pragma: no cover
2524 l, llen, v = len_decode(lv)
2525 except LenIndefForm:
2526 llen, l, v = 1, 0, lv[1:]
2528 except DecodeError as err:
2529 raise err.__class__(
2531 klass=self.__class__,
2532 decode_path=decode_path,
2536 raise NotEnoughData(
2537 "encoded length is longer than data",
2538 klass=self.__class__,
2539 decode_path=decode_path,
2542 if not lenindef and l == 0:
2543 raise NotEnoughData(
2545 klass=self.__class__,
2546 decode_path=decode_path,
2550 sub_offset = offset + tlen + llen
2554 if v[:EOC_LEN].tobytes() == EOC:
2561 "chunk out of bounds",
2562 klass=self.__class__,
2563 decode_path=decode_path + (str(len(chunks) - 1),),
2564 offset=chunks[-1].offset,
2566 sub_decode_path = decode_path + (str(len(chunks)),)
2568 chunk, v_tail = BitString().decode(
2571 decode_path=sub_decode_path,
2574 _ctx_immutable=False,
2578 "expected BitString encoded chunk",
2579 klass=self.__class__,
2580 decode_path=sub_decode_path,
2583 chunks.append(chunk)
2584 sub_offset += chunk.tlvlen
2585 vlen += chunk.tlvlen
2587 if len(chunks) == 0:
2590 klass=self.__class__,
2591 decode_path=decode_path,
2596 for chunk_i, chunk in enumerate(chunks[:-1]):
2597 if chunk.bit_len % 8 != 0:
2599 "BitString chunk is not multiple of 8 bits",
2600 klass=self.__class__,
2601 decode_path=decode_path + (str(chunk_i),),
2602 offset=chunk.offset,
2604 values.append(bytes(chunk))
2605 bit_len += chunk.bit_len
2606 chunk_last = chunks[-1]
2607 values.append(bytes(chunk_last))
2608 bit_len += chunk_last.bit_len
2609 obj = self.__class__(
2610 value=(bit_len, b"".join(values)),
2613 default=self.default,
2614 optional=self.optional,
2616 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2618 obj.lenindef = lenindef
2619 obj.ber_encoded = True
2620 return obj, (v[EOC_LEN:] if lenindef else v)
2622 klass=self.__class__,
2623 decode_path=decode_path,
2628 return pp_console_row(next(self.pps()))
2630 def pps(self, decode_path=()):
2634 bit_len, blob = self._value
2635 value = "%d bits" % bit_len
2636 if len(self.specs) > 0:
2637 blob = tuple(self.named)
2640 asn1_type_name=self.asn1_type_name,
2641 obj_name=self.__class__.__name__,
2642 decode_path=decode_path,
2645 optional=self.optional,
2646 default=self == self.default,
2647 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2648 expl=None if self._expl is None else tag_decode(self._expl),
2653 expl_offset=self.expl_offset if self.expled else None,
2654 expl_tlen=self.expl_tlen if self.expled else None,
2655 expl_llen=self.expl_llen if self.expled else None,
2656 expl_vlen=self.expl_vlen if self.expled else None,
2657 expl_lenindef=self.expl_lenindef,
2658 lenindef=self.lenindef,
2659 ber_encoded=self.ber_encoded,
2662 defined_by, defined = self.defined or (None, None)
2663 if defined_by is not None:
2665 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2667 for pp in self.pps_lenindef(decode_path):
2671 class OctetString(Obj):
2672 """``OCTET STRING`` binary string type
2674 >>> s = OctetString(b"hello world")
2675 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2676 >>> s == OctetString(b"hello world")
2681 >>> OctetString(b"hello", bounds=(4, 4))
2682 Traceback (most recent call last):
2683 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2684 >>> OctetString(b"hell", bounds=(4, 4))
2685 OCTET STRING 4 bytes 68656c6c
2689 Pay attention that OCTET STRING can be encoded both in primitive
2690 and constructed forms. Decoder always checks constructed form tag
2691 additionally to specified primitive one. If BER decoding is
2692 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2693 of DER restrictions.
2695 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2696 tag_default = tag_encode(4)
2697 asn1_type_name = "OCTET STRING"
2710 :param value: set the value. Either binary type, or
2711 :py:class:`pyderasn.OctetString` object
2712 :param bounds: set ``(MIN, MAX)`` value size constraint.
2713 (-inf, +inf) by default
2714 :param bytes impl: override default tag with ``IMPLICIT`` one
2715 :param bytes expl: override default tag with ``EXPLICIT`` one
2716 :param default: set default value. Type same as in ``value``
2717 :param bool optional: is object ``OPTIONAL`` in sequence
2719 super(OctetString, self).__init__(
2727 self._bound_min, self._bound_max = getattr(
2731 ) if bounds is None else bounds
2732 if value is not None:
2733 self._value = self._value_sanitize(value)
2734 if default is not None:
2735 default = self._value_sanitize(default)
2736 self.default = self.__class__(
2741 if self._value is None:
2742 self._value = default
2744 tag_klass, _, tag_num = tag_decode(self.tag)
2745 self.tag_constructed = tag_encode(
2747 form=TagFormConstructed,
2751 def _value_sanitize(self, value):
2752 if isinstance(value, binary_type):
2754 elif issubclass(value.__class__, OctetString):
2755 value = value._value
2757 raise InvalidValueType((self.__class__, bytes))
2758 if not self._bound_min <= len(value) <= self._bound_max:
2759 raise BoundsError(self._bound_min, len(value), self._bound_max)
2764 return self._value is not None
2767 obj = self.__class__()
2768 obj._value = self._value
2769 obj._bound_min = self._bound_min
2770 obj._bound_max = self._bound_max
2772 obj._expl = self._expl
2773 obj.default = self.default
2774 obj.optional = self.optional
2775 obj.offset = self.offset
2776 obj.llen = self.llen
2777 obj.vlen = self.vlen
2778 obj.expl_lenindef = self.expl_lenindef
2779 obj.lenindef = self.lenindef
2780 obj.ber_encoded = self.ber_encoded
2783 def __bytes__(self):
2784 self._assert_ready()
2787 def __eq__(self, their):
2788 if isinstance(their, binary_type):
2789 return self._value == their
2790 if not issubclass(their.__class__, OctetString):
2793 self._value == their._value and
2794 self.tag == their.tag and
2795 self._expl == their._expl
2798 def __lt__(self, their):
2799 return self._value < their._value
2810 return self.__class__(
2813 (self._bound_min, self._bound_max)
2814 if bounds is None else bounds
2816 impl=self.tag if impl is None else impl,
2817 expl=self._expl if expl is None else expl,
2818 default=self.default if default is None else default,
2819 optional=self.optional if optional is None else optional,
2823 self._assert_ready()
2826 len_encode(len(self._value)),
2830 def _decode_chunk(self, lv, offset, decode_path):
2832 l, llen, v = len_decode(lv)
2833 except DecodeError as err:
2834 raise err.__class__(
2836 klass=self.__class__,
2837 decode_path=decode_path,
2841 raise NotEnoughData(
2842 "encoded length is longer than data",
2843 klass=self.__class__,
2844 decode_path=decode_path,
2847 v, tail = v[:l], v[l:]
2849 obj = self.__class__(
2851 bounds=(self._bound_min, self._bound_max),
2854 default=self.default,
2855 optional=self.optional,
2856 _decoded=(offset, llen, l),
2858 except DecodeError as err:
2861 klass=self.__class__,
2862 decode_path=decode_path,
2865 except BoundsError as err:
2868 klass=self.__class__,
2869 decode_path=decode_path,
2874 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2876 t, tlen, lv = tag_strip(tlv)
2877 except DecodeError as err:
2878 raise err.__class__(
2880 klass=self.__class__,
2881 decode_path=decode_path,
2887 return self._decode_chunk(lv, offset, decode_path)
2888 if t == self.tag_constructed:
2889 if not ctx.get("bered", False):
2891 "unallowed BER constructed encoding",
2892 klass=self.__class__,
2893 decode_path=decode_path,
2900 l, llen, v = len_decode(lv)
2901 except LenIndefForm:
2902 llen, l, v = 1, 0, lv[1:]
2904 except DecodeError as err:
2905 raise err.__class__(
2907 klass=self.__class__,
2908 decode_path=decode_path,
2912 raise NotEnoughData(
2913 "encoded length is longer than data",
2914 klass=self.__class__,
2915 decode_path=decode_path,
2919 sub_offset = offset + tlen + llen
2923 if v[:EOC_LEN].tobytes() == EOC:
2930 "chunk out of bounds",
2931 klass=self.__class__,
2932 decode_path=decode_path + (str(len(chunks) - 1),),
2933 offset=chunks[-1].offset,
2935 sub_decode_path = decode_path + (str(len(chunks)),)
2937 chunk, v_tail = OctetString().decode(
2940 decode_path=sub_decode_path,
2943 _ctx_immutable=False,
2947 "expected OctetString encoded chunk",
2948 klass=self.__class__,
2949 decode_path=sub_decode_path,
2952 chunks.append(chunk)
2953 sub_offset += chunk.tlvlen
2954 vlen += chunk.tlvlen
2957 obj = self.__class__(
2958 value=b"".join(bytes(chunk) for chunk in chunks),
2959 bounds=(self._bound_min, self._bound_max),
2962 default=self.default,
2963 optional=self.optional,
2964 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2966 except DecodeError as err:
2969 klass=self.__class__,
2970 decode_path=decode_path,
2973 except BoundsError as err:
2976 klass=self.__class__,
2977 decode_path=decode_path,
2980 obj.lenindef = lenindef
2981 obj.ber_encoded = True
2982 return obj, (v[EOC_LEN:] if lenindef else v)
2984 klass=self.__class__,
2985 decode_path=decode_path,
2990 return pp_console_row(next(self.pps()))
2992 def pps(self, decode_path=()):
2995 asn1_type_name=self.asn1_type_name,
2996 obj_name=self.__class__.__name__,
2997 decode_path=decode_path,
2998 value=("%d bytes" % len(self._value)) if self.ready else None,
2999 blob=self._value if self.ready else None,
3000 optional=self.optional,
3001 default=self == self.default,
3002 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3003 expl=None if self._expl is None else tag_decode(self._expl),
3008 expl_offset=self.expl_offset if self.expled else None,
3009 expl_tlen=self.expl_tlen if self.expled else None,
3010 expl_llen=self.expl_llen if self.expled else None,
3011 expl_vlen=self.expl_vlen if self.expled else None,
3012 expl_lenindef=self.expl_lenindef,
3013 lenindef=self.lenindef,
3014 ber_encoded=self.ber_encoded,
3017 defined_by, defined = self.defined or (None, None)
3018 if defined_by is not None:
3020 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3022 for pp in self.pps_lenindef(decode_path):
3027 """``NULL`` null object
3035 tag_default = tag_encode(5)
3036 asn1_type_name = "NULL"
3040 value=None, # unused, but Sequence passes it
3047 :param bytes impl: override default tag with ``IMPLICIT`` one
3048 :param bytes expl: override default tag with ``EXPLICIT`` one
3049 :param bool optional: is object ``OPTIONAL`` in sequence
3051 super(Null, self).__init__(impl, expl, None, optional, _decoded)
3059 obj = self.__class__()
3061 obj._expl = self._expl
3062 obj.default = self.default
3063 obj.optional = self.optional
3064 obj.offset = self.offset
3065 obj.llen = self.llen
3066 obj.vlen = self.vlen
3067 obj.expl_lenindef = self.expl_lenindef
3068 obj.lenindef = self.lenindef
3069 obj.ber_encoded = self.ber_encoded
3072 def __eq__(self, their):
3073 if not issubclass(their.__class__, Null):
3076 self.tag == their.tag and
3077 self._expl == their._expl
3087 return self.__class__(
3088 impl=self.tag if impl is None else impl,
3089 expl=self._expl if expl is None else expl,
3090 optional=self.optional if optional is None else optional,
3094 return self.tag + len_encode(0)
3096 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3098 t, _, lv = tag_strip(tlv)
3099 except DecodeError as err:
3100 raise err.__class__(
3102 klass=self.__class__,
3103 decode_path=decode_path,
3108 klass=self.__class__,
3109 decode_path=decode_path,
3112 if tag_only: # pragma: no cover
3115 l, _, v = len_decode(lv)
3116 except DecodeError as err:
3117 raise err.__class__(
3119 klass=self.__class__,
3120 decode_path=decode_path,
3124 raise InvalidLength(
3125 "Null must have zero length",
3126 klass=self.__class__,
3127 decode_path=decode_path,
3130 obj = self.__class__(
3133 optional=self.optional,
3134 _decoded=(offset, 1, 0),
3139 return pp_console_row(next(self.pps()))
3141 def pps(self, decode_path=()):
3144 asn1_type_name=self.asn1_type_name,
3145 obj_name=self.__class__.__name__,
3146 decode_path=decode_path,
3147 optional=self.optional,
3148 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3149 expl=None if self._expl is None else tag_decode(self._expl),
3154 expl_offset=self.expl_offset if self.expled else None,
3155 expl_tlen=self.expl_tlen if self.expled else None,
3156 expl_llen=self.expl_llen if self.expled else None,
3157 expl_vlen=self.expl_vlen if self.expled else None,
3158 expl_lenindef=self.expl_lenindef,
3161 for pp in self.pps_lenindef(decode_path):
3165 class ObjectIdentifier(Obj):
3166 """``OBJECT IDENTIFIER`` OID type
3168 >>> oid = ObjectIdentifier((1, 2, 3))
3169 OBJECT IDENTIFIER 1.2.3
3170 >>> oid == ObjectIdentifier("1.2.3")
3176 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3177 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3179 >>> str(ObjectIdentifier((3, 1)))
3180 Traceback (most recent call last):
3181 pyderasn.InvalidOID: unacceptable first arc value
3183 __slots__ = ("defines",)
3184 tag_default = tag_encode(6)
3185 asn1_type_name = "OBJECT IDENTIFIER"
3198 :param value: set the value. Either tuples of integers,
3199 string of "."-concatenated integers, or
3200 :py:class:`pyderasn.ObjectIdentifier` object
3201 :param defines: sequence of tuples. Each tuple has two elements.
3202 First one is relative to current one decode
3203 path, aiming to the field defined by that OID.
3204 Read about relative path in
3205 :py:func:`pyderasn.abs_decode_path`. Second
3206 tuple element is ``{OID: pyderasn.Obj()}``
3207 dictionary, mapping between current OID value
3208 and structure applied to defined field.
3209 :ref:`Read about DEFINED BY <definedby>`
3210 :param bytes impl: override default tag with ``IMPLICIT`` one
3211 :param bytes expl: override default tag with ``EXPLICIT`` one
3212 :param default: set default value. Type same as in ``value``
3213 :param bool optional: is object ``OPTIONAL`` in sequence
3215 super(ObjectIdentifier, self).__init__(
3223 if value is not None:
3224 self._value = self._value_sanitize(value)
3225 if default is not None:
3226 default = self._value_sanitize(default)
3227 self.default = self.__class__(
3232 if self._value is None:
3233 self._value = default
3234 self.defines = defines
3236 def __add__(self, their):
3237 if isinstance(their, self.__class__):
3238 return self.__class__(self._value + their._value)
3239 if isinstance(their, tuple):
3240 return self.__class__(self._value + their)
3241 raise InvalidValueType((self.__class__, tuple))
3243 def _value_sanitize(self, value):
3244 if issubclass(value.__class__, ObjectIdentifier):
3246 if isinstance(value, string_types):
3248 value = tuple(int(arc) for arc in value.split("."))
3250 raise InvalidOID("unacceptable arcs values")
3251 if isinstance(value, tuple):
3253 raise InvalidOID("less than 2 arcs")
3254 first_arc = value[0]
3255 if first_arc in (0, 1):
3256 if not (0 <= value[1] <= 39):
3257 raise InvalidOID("second arc is too wide")
3258 elif first_arc == 2:
3261 raise InvalidOID("unacceptable first arc value")
3263 raise InvalidValueType((self.__class__, str, tuple))
3267 return self._value is not None
3270 obj = self.__class__()
3271 obj._value = self._value
3272 obj.defines = self.defines
3274 obj._expl = self._expl
3275 obj.default = self.default
3276 obj.optional = self.optional
3277 obj.offset = self.offset
3278 obj.llen = self.llen
3279 obj.vlen = self.vlen
3280 obj.expl_lenindef = self.expl_lenindef
3281 obj.lenindef = self.lenindef
3282 obj.ber_encoded = self.ber_encoded
3286 self._assert_ready()
3287 return iter(self._value)
3290 return ".".join(str(arc) for arc in self._value or ())
3293 self._assert_ready()
3296 bytes(self._expl or b"") +
3297 str(self._value).encode("ascii"),
3300 def __eq__(self, their):
3301 if isinstance(their, tuple):
3302 return self._value == their
3303 if not issubclass(their.__class__, ObjectIdentifier):
3306 self.tag == their.tag and
3307 self._expl == their._expl and
3308 self._value == their._value
3311 def __lt__(self, their):
3312 return self._value < their._value
3323 return self.__class__(
3325 defines=self.defines if defines is None else defines,
3326 impl=self.tag if impl is None else impl,
3327 expl=self._expl if expl is None else expl,
3328 default=self.default if default is None else default,
3329 optional=self.optional if optional is None else optional,
3333 self._assert_ready()
3335 first_value = value[1]
3336 first_arc = value[0]
3339 elif first_arc == 1:
3341 elif first_arc == 2:
3343 else: # pragma: no cover
3344 raise RuntimeError("invalid arc is stored")
3345 octets = [zero_ended_encode(first_value)]
3346 for arc in value[2:]:
3347 octets.append(zero_ended_encode(arc))
3348 v = b"".join(octets)
3349 return b"".join((self.tag, len_encode(len(v)), v))
3351 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3353 t, _, lv = tag_strip(tlv)
3354 except DecodeError as err:
3355 raise err.__class__(
3357 klass=self.__class__,
3358 decode_path=decode_path,
3363 klass=self.__class__,
3364 decode_path=decode_path,
3367 if tag_only: # pragma: no cover
3370 l, llen, v = len_decode(lv)
3371 except DecodeError as err:
3372 raise err.__class__(
3374 klass=self.__class__,
3375 decode_path=decode_path,
3379 raise NotEnoughData(
3380 "encoded length is longer than data",
3381 klass=self.__class__,
3382 decode_path=decode_path,
3386 raise NotEnoughData(
3388 klass=self.__class__,
3389 decode_path=decode_path,
3392 v, tail = v[:l], v[l:]
3399 octet = indexbytes(v, i)
3400 if i == 0 and octet == 0x80:
3401 if ctx.get("bered", False):
3404 raise DecodeError("non normalized arc encoding")
3405 arc = (arc << 7) | (octet & 0x7F)
3406 if octet & 0x80 == 0:
3414 klass=self.__class__,
3415 decode_path=decode_path,
3419 second_arc = arcs[0]
3420 if 0 <= second_arc <= 39:
3422 elif 40 <= second_arc <= 79:
3428 obj = self.__class__(
3429 value=tuple([first_arc, second_arc] + arcs[1:]),
3432 default=self.default,
3433 optional=self.optional,
3434 _decoded=(offset, llen, l),
3437 obj.ber_encoded = True
3441 return pp_console_row(next(self.pps()))
3443 def pps(self, decode_path=()):
3446 asn1_type_name=self.asn1_type_name,
3447 obj_name=self.__class__.__name__,
3448 decode_path=decode_path,
3449 value=str(self) if self.ready else None,
3450 optional=self.optional,
3451 default=self == self.default,
3452 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3453 expl=None if self._expl is None else tag_decode(self._expl),
3458 expl_offset=self.expl_offset if self.expled else None,
3459 expl_tlen=self.expl_tlen if self.expled else None,
3460 expl_llen=self.expl_llen if self.expled else None,
3461 expl_vlen=self.expl_vlen if self.expled else None,
3462 expl_lenindef=self.expl_lenindef,
3463 ber_encoded=self.ber_encoded,
3466 for pp in self.pps_lenindef(decode_path):
3470 class Enumerated(Integer):
3471 """``ENUMERATED`` integer type
3473 This type is identical to :py:class:`pyderasn.Integer`, but requires
3474 schema to be specified and does not accept values missing from it.
3477 tag_default = tag_encode(10)
3478 asn1_type_name = "ENUMERATED"
3489 bounds=None, # dummy argument, workability for Integer.decode
3491 super(Enumerated, self).__init__(
3500 if len(self.specs) == 0:
3501 raise ValueError("schema must be specified")
3503 def _value_sanitize(self, value):
3504 if isinstance(value, self.__class__):
3505 value = value._value
3506 elif isinstance(value, integer_types):
3507 for _value in itervalues(self.specs):
3512 "unknown integer value: %s" % value,
3513 klass=self.__class__,
3515 elif isinstance(value, string_types):
3516 value = self.specs.get(value)
3518 raise ObjUnknown("integer value: %s" % value)
3520 raise InvalidValueType((self.__class__, int, str))
3524 obj = self.__class__(_specs=self.specs)
3525 obj._value = self._value
3526 obj._bound_min = self._bound_min
3527 obj._bound_max = self._bound_max
3529 obj._expl = self._expl
3530 obj.default = self.default
3531 obj.optional = self.optional
3532 obj.offset = self.offset
3533 obj.llen = self.llen
3534 obj.vlen = self.vlen
3535 obj.expl_lenindef = self.expl_lenindef
3536 obj.lenindef = self.lenindef
3537 obj.ber_encoded = self.ber_encoded
3549 return self.__class__(
3551 impl=self.tag if impl is None else impl,
3552 expl=self._expl if expl is None else expl,
3553 default=self.default if default is None else default,
3554 optional=self.optional if optional is None else optional,
3559 class CommonString(OctetString):
3560 """Common class for all strings
3562 Everything resembles :py:class:`pyderasn.OctetString`, except
3563 ability to deal with unicode text strings.
3565 >>> hexenc("привет мир".encode("utf-8"))
3566 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3567 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3569 >>> s = UTF8String("привет мир")
3570 UTF8String UTF8String привет мир
3572 'привет мир'
3573 >>> hexenc(bytes(s))
3574 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3576 >>> PrintableString("привет мир")
3577 Traceback (most recent call last):
3578 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3580 >>> BMPString("ада", bounds=(2, 2))
3581 Traceback (most recent call last):
3582 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3583 >>> s = BMPString("ад", bounds=(2, 2))
3586 >>> hexenc(bytes(s))
3594 * - :py:class:`pyderasn.UTF8String`
3596 * - :py:class:`pyderasn.NumericString`
3598 * - :py:class:`pyderasn.PrintableString`
3600 * - :py:class:`pyderasn.TeletexString`
3602 * - :py:class:`pyderasn.T61String`
3604 * - :py:class:`pyderasn.VideotexString`
3606 * - :py:class:`pyderasn.IA5String`
3608 * - :py:class:`pyderasn.GraphicString`
3610 * - :py:class:`pyderasn.VisibleString`
3612 * - :py:class:`pyderasn.ISO646String`
3614 * - :py:class:`pyderasn.GeneralString`
3616 * - :py:class:`pyderasn.UniversalString`
3618 * - :py:class:`pyderasn.BMPString`
3621 __slots__ = ("encoding",)
3623 def _value_sanitize(self, value):
3625 value_decoded = None
3626 if isinstance(value, self.__class__):
3627 value_raw = value._value
3628 elif isinstance(value, text_type):
3629 value_decoded = value
3630 elif isinstance(value, binary_type):
3633 raise InvalidValueType((self.__class__, text_type, binary_type))
3636 value_decoded.encode(self.encoding)
3637 if value_raw is None else value_raw
3640 value_raw.decode(self.encoding)
3641 if value_decoded is None else value_decoded
3643 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3644 raise DecodeError(str(err))
3645 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3653 def __eq__(self, their):
3654 if isinstance(their, binary_type):
3655 return self._value == their
3656 if isinstance(their, text_type):
3657 return self._value == their.encode(self.encoding)
3658 if not isinstance(their, self.__class__):
3661 self._value == their._value and
3662 self.tag == their.tag and
3663 self._expl == their._expl
3666 def __unicode__(self):
3668 return self._value.decode(self.encoding)
3669 return text_type(self._value)
3672 return pp_console_row(next(self.pps(no_unicode=PY2)))
3674 def pps(self, decode_path=(), no_unicode=False):
3677 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3680 asn1_type_name=self.asn1_type_name,
3681 obj_name=self.__class__.__name__,
3682 decode_path=decode_path,
3684 optional=self.optional,
3685 default=self == self.default,
3686 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3687 expl=None if self._expl is None else tag_decode(self._expl),
3692 expl_offset=self.expl_offset if self.expled else None,
3693 expl_tlen=self.expl_tlen if self.expled else None,
3694 expl_llen=self.expl_llen if self.expled else None,
3695 expl_vlen=self.expl_vlen if self.expled else None,
3696 expl_lenindef=self.expl_lenindef,
3697 ber_encoded=self.ber_encoded,
3700 for pp in self.pps_lenindef(decode_path):
3704 class UTF8String(CommonString):
3706 tag_default = tag_encode(12)
3708 asn1_type_name = "UTF8String"
3711 class AllowableCharsMixin(object):
3713 def allowable_chars(self):
3715 return self._allowable_chars
3716 return frozenset(six_unichr(c) for c in self._allowable_chars)
3719 class NumericString(AllowableCharsMixin, CommonString):
3722 Its value is properly sanitized: only ASCII digits with spaces can
3725 >>> NumericString().allowable_chars
3726 frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
3729 tag_default = tag_encode(18)
3731 asn1_type_name = "NumericString"
3732 _allowable_chars = frozenset(digits.encode("ascii") + b" ")
3734 def _value_sanitize(self, value):
3735 value = super(NumericString, self)._value_sanitize(value)
3736 if not frozenset(value) <= self._allowable_chars:
3737 raise DecodeError("non-numeric value")
3741 class PrintableString(AllowableCharsMixin, CommonString):
3744 Its value is properly sanitized: see X.680 41.4 table 10.
3746 >>> PrintableString().allowable_chars
3747 frozenset([' ', "'", ..., 'z'])
3750 tag_default = tag_encode(19)
3752 asn1_type_name = "PrintableString"
3753 _allowable_chars = frozenset(
3754 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3757 def _value_sanitize(self, value):
3758 value = super(PrintableString, self)._value_sanitize(value)
3759 if not frozenset(value) <= self._allowable_chars:
3760 raise DecodeError("non-printable value")
3764 class TeletexString(CommonString):
3766 tag_default = tag_encode(20)
3768 asn1_type_name = "TeletexString"
3771 class T61String(TeletexString):
3773 asn1_type_name = "T61String"
3776 class VideotexString(CommonString):
3778 tag_default = tag_encode(21)
3779 encoding = "iso-8859-1"
3780 asn1_type_name = "VideotexString"
3783 class IA5String(CommonString):
3785 tag_default = tag_encode(22)
3787 asn1_type_name = "IA5"
3790 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3791 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3792 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3795 class UTCTime(CommonString):
3796 """``UTCTime`` datetime type
3798 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3799 UTCTime UTCTime 2017-09-30T22:07:50
3805 datetime.datetime(2017, 9, 30, 22, 7, 50)
3806 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3807 datetime.datetime(1957, 9, 30, 22, 7, 50)
3811 BER encoding is unsupported.
3814 tag_default = tag_encode(23)
3816 asn1_type_name = "UTCTime"
3826 bounds=None, # dummy argument, workability for OctetString.decode
3829 :param value: set the value. Either datetime type, or
3830 :py:class:`pyderasn.UTCTime` object
3831 :param bytes impl: override default tag with ``IMPLICIT`` one
3832 :param bytes expl: override default tag with ``EXPLICIT`` one
3833 :param default: set default value. Type same as in ``value``
3834 :param bool optional: is object ``OPTIONAL`` in sequence
3836 super(UTCTime, self).__init__(
3844 if value is not None:
3845 self._value = self._value_sanitize(value)
3846 if default is not None:
3847 default = self._value_sanitize(default)
3848 self.default = self.__class__(
3853 if self._value is None:
3854 self._value = default
3856 def _strptime(self, value):
3857 # datetime.strptime's format: %y%m%d%H%M%SZ
3858 if len(value) != LEN_YYMMDDHHMMSSZ:
3859 raise ValueError("invalid UTCTime length")
3860 if value[-1] != "Z":
3861 raise ValueError("non UTC timezone")
3863 2000 + int(value[:2]), # %y
3864 int(value[2:4]), # %m
3865 int(value[4:6]), # %d
3866 int(value[6:8]), # %H
3867 int(value[8:10]), # %M
3868 int(value[10:12]), # %S
3871 def _value_sanitize(self, value):
3872 if isinstance(value, binary_type):
3874 value_decoded = value.decode("ascii")
3875 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3876 raise DecodeError("invalid UTCTime encoding: %r" % err)
3878 self._strptime(value_decoded)
3879 except (TypeError, ValueError) as err:
3880 raise DecodeError("invalid UTCTime format: %r" % err)
3882 if isinstance(value, self.__class__):
3884 if isinstance(value, datetime):
3885 return value.strftime("%y%m%d%H%M%SZ").encode("ascii")
3886 raise InvalidValueType((self.__class__, datetime))
3888 def __eq__(self, their):
3889 if isinstance(their, binary_type):
3890 return self._value == their
3891 if isinstance(their, datetime):
3892 return self.todatetime() == their
3893 if not isinstance(their, self.__class__):
3896 self._value == their._value and
3897 self.tag == their.tag and
3898 self._expl == their._expl
3901 def todatetime(self):
3902 """Convert to datetime
3906 Pay attention that UTCTime can not hold full year, so all years
3907 having < 50 years are treated as 20xx, 19xx otherwise, according
3908 to X.509 recomendation.
3910 value = self._strptime(self._value.decode("ascii"))
3911 year = value.year % 100
3913 year=(2000 + year) if year < 50 else (1900 + year),
3917 minute=value.minute,
3918 second=value.second,
3922 return pp_console_row(next(self.pps()))
3924 def pps(self, decode_path=()):
3927 asn1_type_name=self.asn1_type_name,
3928 obj_name=self.__class__.__name__,
3929 decode_path=decode_path,
3930 value=self.todatetime().isoformat() if self.ready else None,
3931 optional=self.optional,
3932 default=self == self.default,
3933 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3934 expl=None if self._expl is None else tag_decode(self._expl),
3939 expl_offset=self.expl_offset if self.expled else None,
3940 expl_tlen=self.expl_tlen if self.expled else None,
3941 expl_llen=self.expl_llen if self.expled else None,
3942 expl_vlen=self.expl_vlen if self.expled else None,
3943 expl_lenindef=self.expl_lenindef,
3944 ber_encoded=self.ber_encoded,
3947 for pp in self.pps_lenindef(decode_path):
3951 class GeneralizedTime(UTCTime):
3952 """``GeneralizedTime`` datetime type
3954 This type is similar to :py:class:`pyderasn.UTCTime`.
3956 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3957 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3959 '20170930220750.000123Z'
3960 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3961 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3965 BER encoding is unsupported.
3969 Only microsecond fractions are supported.
3970 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
3971 higher precision values.
3974 tag_default = tag_encode(24)
3975 asn1_type_name = "GeneralizedTime"
3977 def _strptime(self, value):
3979 if l == LEN_YYYYMMDDHHMMSSZ:
3980 # datetime.strptime's format: %y%m%d%H%M%SZ
3981 if value[-1] != "Z":
3982 raise ValueError("non UTC timezone")
3984 int(value[:4]), # %Y
3985 int(value[4:6]), # %m
3986 int(value[6:8]), # %d
3987 int(value[8:10]), # %H
3988 int(value[10:12]), # %M
3989 int(value[12:14]), # %S
3991 if l >= LEN_YYYYMMDDHHMMSSDMZ:
3992 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
3993 if value[-1] != "Z":
3994 raise ValueError("non UTC timezone")
3995 if value[14] != ".":
3996 raise ValueError("no fractions separator")
3999 raise ValueError("trailing zero")
4002 raise ValueError("only microsecond fractions are supported")
4003 us = int(us + ("0" * (6 - us_len)))
4005 int(value[:4]), # %Y
4006 int(value[4:6]), # %m
4007 int(value[6:8]), # %d
4008 int(value[8:10]), # %H
4009 int(value[10:12]), # %M
4010 int(value[12:14]), # %S
4014 raise ValueError("invalid GeneralizedTime length")
4016 def _value_sanitize(self, value):
4017 if isinstance(value, binary_type):
4019 value_decoded = value.decode("ascii")
4020 except (UnicodeEncodeError, UnicodeDecodeError) as err:
4021 raise DecodeError("invalid GeneralizedTime encoding: %r" % err)
4023 self._strptime(value_decoded)
4024 except (TypeError, ValueError) as err:
4026 "invalid GeneralizedTime format: %r" % err,
4027 klass=self.__class__,
4030 if isinstance(value, self.__class__):
4032 if isinstance(value, datetime):
4033 encoded = value.strftime("%Y%m%d%H%M%S")
4034 if value.microsecond > 0:
4035 encoded = encoded + (".%06d" % value.microsecond).rstrip("0")
4036 return (encoded + "Z").encode("ascii")
4037 raise InvalidValueType((self.__class__, datetime))
4039 def todatetime(self):
4040 return self._strptime(self._value.decode("ascii"))
4043 class GraphicString(CommonString):
4045 tag_default = tag_encode(25)
4046 encoding = "iso-8859-1"
4047 asn1_type_name = "GraphicString"
4050 class VisibleString(CommonString):
4052 tag_default = tag_encode(26)
4054 asn1_type_name = "VisibleString"
4057 class ISO646String(VisibleString):
4059 asn1_type_name = "ISO646String"
4062 class GeneralString(CommonString):
4064 tag_default = tag_encode(27)
4065 encoding = "iso-8859-1"
4066 asn1_type_name = "GeneralString"
4069 class UniversalString(CommonString):
4071 tag_default = tag_encode(28)
4072 encoding = "utf-32-be"
4073 asn1_type_name = "UniversalString"
4076 class BMPString(CommonString):
4078 tag_default = tag_encode(30)
4079 encoding = "utf-16-be"
4080 asn1_type_name = "BMPString"
4084 """``CHOICE`` special type
4088 class GeneralName(Choice):
4090 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4091 ("dNSName", IA5String(impl=tag_ctxp(2))),
4094 >>> gn = GeneralName()
4096 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4097 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4098 >>> gn["dNSName"] = IA5String("bar.baz")
4099 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4100 >>> gn["rfc822Name"]
4103 [2] IA5String IA5 bar.baz
4106 >>> gn.value == gn["dNSName"]
4109 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4111 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4112 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4114 __slots__ = ("specs",)
4116 asn1_type_name = "CHOICE"
4129 :param value: set the value. Either ``(choice, value)`` tuple, or
4130 :py:class:`pyderasn.Choice` object
4131 :param bytes impl: can not be set, do **not** use it
4132 :param bytes expl: override default tag with ``EXPLICIT`` one
4133 :param default: set default value. Type same as in ``value``
4134 :param bool optional: is object ``OPTIONAL`` in sequence
4136 if impl is not None:
4137 raise ValueError("no implicit tag allowed for CHOICE")
4138 super(Choice, self).__init__(None, expl, default, optional, _decoded)
4140 schema = getattr(self, "schema", ())
4141 if len(schema) == 0:
4142 raise ValueError("schema must be specified")
4144 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4147 if value is not None:
4148 self._value = self._value_sanitize(value)
4149 if default is not None:
4150 default_value = self._value_sanitize(default)
4151 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4152 default_obj.specs = self.specs
4153 default_obj._value = default_value
4154 self.default = default_obj
4156 self._value = default_obj.copy()._value
4158 def _value_sanitize(self, value):
4159 if isinstance(value, tuple) and len(value) == 2:
4161 spec = self.specs.get(choice)
4163 raise ObjUnknown(choice)
4164 if not isinstance(obj, spec.__class__):
4165 raise InvalidValueType((spec,))
4166 return (choice, spec(obj))
4167 if isinstance(value, self.__class__):
4169 raise InvalidValueType((self.__class__, tuple))
4173 return self._value is not None and self._value[1].ready
4177 return self.expl_lenindef or (
4178 (self._value is not None) and
4179 self._value[1].bered
4183 obj = self.__class__(schema=self.specs)
4184 obj._expl = self._expl
4185 obj.default = self.default
4186 obj.optional = self.optional
4187 obj.offset = self.offset
4188 obj.llen = self.llen
4189 obj.vlen = self.vlen
4190 obj.expl_lenindef = self.expl_lenindef
4191 obj.lenindef = self.lenindef
4192 obj.ber_encoded = self.ber_encoded
4194 if value is not None:
4195 obj._value = (value[0], value[1].copy())
4198 def __eq__(self, their):
4199 if isinstance(their, tuple) and len(their) == 2:
4200 return self._value == their
4201 if not isinstance(their, self.__class__):
4204 self.specs == their.specs and
4205 self._value == their._value
4215 return self.__class__(
4218 expl=self._expl if expl is None else expl,
4219 default=self.default if default is None else default,
4220 optional=self.optional if optional is None else optional,
4225 self._assert_ready()
4226 return self._value[0]
4230 self._assert_ready()
4231 return self._value[1]
4233 def __getitem__(self, key):
4234 if key not in self.specs:
4235 raise ObjUnknown(key)
4236 if self._value is None:
4238 choice, value = self._value
4243 def __setitem__(self, key, value):
4244 spec = self.specs.get(key)
4246 raise ObjUnknown(key)
4247 if not isinstance(value, spec.__class__):
4248 raise InvalidValueType((spec.__class__,))
4249 self._value = (key, spec(value))
4257 return self._value[1].decoded if self.ready else False
4260 self._assert_ready()
4261 return self._value[1].encode()
4263 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4264 for choice, spec in iteritems(self.specs):
4265 sub_decode_path = decode_path + (choice,)
4271 decode_path=sub_decode_path,
4274 _ctx_immutable=False,
4281 klass=self.__class__,
4282 decode_path=decode_path,
4285 if tag_only: # pragma: no cover
4287 value, tail = spec.decode(
4291 decode_path=sub_decode_path,
4293 _ctx_immutable=False,
4295 obj = self.__class__(
4298 default=self.default,
4299 optional=self.optional,
4300 _decoded=(offset, 0, value.fulllen),
4302 obj._value = (choice, value)
4306 value = pp_console_row(next(self.pps()))
4308 value = "%s[%r]" % (value, self.value)
4311 def pps(self, decode_path=()):
4314 asn1_type_name=self.asn1_type_name,
4315 obj_name=self.__class__.__name__,
4316 decode_path=decode_path,
4317 value=self.choice if self.ready else None,
4318 optional=self.optional,
4319 default=self == self.default,
4320 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4321 expl=None if self._expl is None else tag_decode(self._expl),
4326 expl_lenindef=self.expl_lenindef,
4330 yield self.value.pps(decode_path=decode_path + (self.choice,))
4331 for pp in self.pps_lenindef(decode_path):
4335 class PrimitiveTypes(Choice):
4336 """Predefined ``CHOICE`` for all generic primitive types
4338 It could be useful for general decoding of some unspecified values:
4340 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4341 OCTET STRING 3 bytes 666f6f
4342 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4346 schema = tuple((klass.__name__, klass()) for klass in (
4371 """``ANY`` special type
4373 >>> Any(Integer(-123))
4375 >>> a = Any(OctetString(b"hello world").encode())
4376 ANY 040b68656c6c6f20776f726c64
4377 >>> hexenc(bytes(a))
4378 b'0x040x0bhello world'
4380 __slots__ = ("defined",)
4381 tag_default = tag_encode(0)
4382 asn1_type_name = "ANY"
4392 :param value: set the value. Either any kind of pyderasn's
4393 **ready** object, or bytes. Pay attention that
4394 **no** validation is performed is raw binary value
4396 :param bytes expl: override default tag with ``EXPLICIT`` one
4397 :param bool optional: is object ``OPTIONAL`` in sequence
4399 super(Any, self).__init__(None, expl, None, optional, _decoded)
4400 self._value = None if value is None else self._value_sanitize(value)
4403 def _value_sanitize(self, value):
4404 if isinstance(value, binary_type):
4406 if isinstance(value, self.__class__):
4408 if isinstance(value, Obj):
4409 return value.encode()
4410 raise InvalidValueType((self.__class__, Obj, binary_type))
4414 return self._value is not None
4418 if self.expl_lenindef or self.lenindef:
4420 if self.defined is None:
4422 return self.defined[1].bered
4425 obj = self.__class__()
4426 obj._value = self._value
4428 obj._expl = self._expl
4429 obj.optional = self.optional
4430 obj.offset = self.offset
4431 obj.llen = self.llen
4432 obj.vlen = self.vlen
4433 obj.expl_lenindef = self.expl_lenindef
4434 obj.lenindef = self.lenindef
4435 obj.ber_encoded = self.ber_encoded
4438 def __eq__(self, their):
4439 if isinstance(their, binary_type):
4440 return self._value == their
4441 if issubclass(their.__class__, Any):
4442 return self._value == their._value
4451 return self.__class__(
4453 expl=self._expl if expl is None else expl,
4454 optional=self.optional if optional is None else optional,
4457 def __bytes__(self):
4458 self._assert_ready()
4466 self._assert_ready()
4469 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4471 t, tlen, lv = tag_strip(tlv)
4472 except DecodeError as err:
4473 raise err.__class__(
4475 klass=self.__class__,
4476 decode_path=decode_path,
4480 l, llen, v = len_decode(lv)
4481 except LenIndefForm as err:
4482 if not ctx.get("bered", False):
4483 raise err.__class__(
4485 klass=self.__class__,
4486 decode_path=decode_path,
4489 llen, vlen, v = 1, 0, lv[1:]
4490 sub_offset = offset + tlen + llen
4492 while v[:EOC_LEN].tobytes() != EOC:
4493 chunk, v = Any().decode(
4496 decode_path=decode_path + (str(chunk_i),),
4499 _ctx_immutable=False,
4501 vlen += chunk.tlvlen
4502 sub_offset += chunk.tlvlen
4504 tlvlen = tlen + llen + vlen + EOC_LEN
4505 obj = self.__class__(
4506 value=tlv[:tlvlen].tobytes(),
4508 optional=self.optional,
4509 _decoded=(offset, 0, tlvlen),
4513 return obj, v[EOC_LEN:]
4514 except DecodeError as err:
4515 raise err.__class__(
4517 klass=self.__class__,
4518 decode_path=decode_path,
4522 raise NotEnoughData(
4523 "encoded length is longer than data",
4524 klass=self.__class__,
4525 decode_path=decode_path,
4528 tlvlen = tlen + llen + l
4529 v, tail = tlv[:tlvlen], v[l:]
4530 obj = self.__class__(
4533 optional=self.optional,
4534 _decoded=(offset, 0, tlvlen),
4540 return pp_console_row(next(self.pps()))
4542 def pps(self, decode_path=()):
4545 asn1_type_name=self.asn1_type_name,
4546 obj_name=self.__class__.__name__,
4547 decode_path=decode_path,
4548 blob=self._value if self.ready else None,
4549 optional=self.optional,
4550 default=self == self.default,
4551 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4552 expl=None if self._expl is None else tag_decode(self._expl),
4557 expl_offset=self.expl_offset if self.expled else None,
4558 expl_tlen=self.expl_tlen if self.expled else None,
4559 expl_llen=self.expl_llen if self.expled else None,
4560 expl_vlen=self.expl_vlen if self.expled else None,
4561 expl_lenindef=self.expl_lenindef,
4562 lenindef=self.lenindef,
4565 defined_by, defined = self.defined or (None, None)
4566 if defined_by is not None:
4568 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4570 for pp in self.pps_lenindef(decode_path):
4574 ########################################################################
4575 # ASN.1 constructed types
4576 ########################################################################
4578 def get_def_by_path(defines_by_path, sub_decode_path):
4579 """Get define by decode path
4581 for path, define in defines_by_path:
4582 if len(path) != len(sub_decode_path):
4584 for p1, p2 in zip(path, sub_decode_path):
4585 if (p1 != any) and (p1 != p2):
4591 def abs_decode_path(decode_path, rel_path):
4592 """Create an absolute decode path from current and relative ones
4594 :param decode_path: current decode path, starting point. Tuple of strings
4595 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4596 If first tuple's element is "/", then treat it as
4597 an absolute path, ignoring ``decode_path`` as
4598 starting point. Also this tuple can contain ".."
4599 elements, stripping the leading element from
4602 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4603 ("foo", "bar", "baz", "whatever")
4604 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4606 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4609 if rel_path[0] == "/":
4611 if rel_path[0] == "..":
4612 return abs_decode_path(decode_path[:-1], rel_path[1:])
4613 return decode_path + rel_path
4616 class Sequence(Obj):
4617 """``SEQUENCE`` structure type
4619 You have to make specification of sequence::
4621 class Extension(Sequence):
4623 ("extnID", ObjectIdentifier()),
4624 ("critical", Boolean(default=False)),
4625 ("extnValue", OctetString()),
4628 Then, you can work with it as with dictionary.
4630 >>> ext = Extension()
4631 >>> Extension().specs
4633 ('extnID', OBJECT IDENTIFIER),
4634 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4635 ('extnValue', OCTET STRING),
4637 >>> ext["extnID"] = "1.2.3"
4638 Traceback (most recent call last):
4639 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4640 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4642 You can determine if sequence is ready to be encoded:
4647 Traceback (most recent call last):
4648 pyderasn.ObjNotReady: object is not ready: extnValue
4649 >>> ext["extnValue"] = OctetString(b"foobar")
4653 Value you want to assign, must have the same **type** as in
4654 corresponding specification, but it can have different tags,
4655 optional/default attributes -- they will be taken from specification
4658 class TBSCertificate(Sequence):
4660 ("version", Version(expl=tag_ctxc(0), default="v1")),
4663 >>> tbs = TBSCertificate()
4664 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4666 Assign ``None`` to remove value from sequence.
4668 You can set values in Sequence during its initialization:
4670 >>> AlgorithmIdentifier((
4671 ("algorithm", ObjectIdentifier("1.2.3")),
4672 ("parameters", Any(Null()))
4674 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4676 You can determine if value exists/set in the sequence and take its value:
4678 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4681 OBJECT IDENTIFIER 1.2.3
4683 But pay attention that if value has default, then it won't be (not
4684 in) in the sequence (because ``DEFAULT`` must not be encoded in
4685 DER), but you can read its value:
4687 >>> "critical" in ext, ext["critical"]
4688 (False, BOOLEAN False)
4689 >>> ext["critical"] = Boolean(True)
4690 >>> "critical" in ext, ext["critical"]
4691 (True, BOOLEAN True)
4693 All defaulted values are always optional.
4695 .. _allow_default_values_ctx:
4697 DER prohibits default value encoding and will raise an error if
4698 default value is unexpectedly met during decode.
4699 If :ref:`bered <bered_ctx>` context option is set, then no error
4700 will be raised, but ``bered`` attribute set. You can disable strict
4701 defaulted values existence validation by setting
4702 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4704 Two sequences are equal if they have equal specification (schema),
4705 implicit/explicit tagging and the same values.
4707 __slots__ = ("specs",)
4708 tag_default = tag_encode(form=TagFormConstructed, num=16)
4709 asn1_type_name = "SEQUENCE"
4721 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4723 schema = getattr(self, "schema", ())
4725 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4728 if value is not None:
4729 if issubclass(value.__class__, Sequence):
4730 self._value = value._value
4731 elif hasattr(value, "__iter__"):
4732 for seq_key, seq_value in value:
4733 self[seq_key] = seq_value
4735 raise InvalidValueType((Sequence,))
4736 if default is not None:
4737 if not issubclass(default.__class__, Sequence):
4738 raise InvalidValueType((Sequence,))
4739 default_value = default._value
4740 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4741 default_obj.specs = self.specs
4742 default_obj._value = default_value
4743 self.default = default_obj
4745 self._value = default_obj.copy()._value
4749 for name, spec in iteritems(self.specs):
4750 value = self._value.get(name)
4761 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4763 return any(value.bered for value in itervalues(self._value))
4766 obj = self.__class__(schema=self.specs)
4768 obj._expl = self._expl
4769 obj.default = self.default
4770 obj.optional = self.optional
4771 obj.offset = self.offset
4772 obj.llen = self.llen
4773 obj.vlen = self.vlen
4774 obj.expl_lenindef = self.expl_lenindef
4775 obj.lenindef = self.lenindef
4776 obj.ber_encoded = self.ber_encoded
4777 obj._value = {k: v.copy() for k, v in iteritems(self._value)}
4780 def __eq__(self, their):
4781 if not isinstance(their, self.__class__):
4784 self.specs == their.specs and
4785 self.tag == their.tag and
4786 self._expl == their._expl and
4787 self._value == their._value
4798 return self.__class__(
4801 impl=self.tag if impl is None else impl,
4802 expl=self._expl if expl is None else expl,
4803 default=self.default if default is None else default,
4804 optional=self.optional if optional is None else optional,
4807 def __contains__(self, key):
4808 return key in self._value
4810 def __setitem__(self, key, value):
4811 spec = self.specs.get(key)
4813 raise ObjUnknown(key)
4815 self._value.pop(key, None)
4817 if not isinstance(value, spec.__class__):
4818 raise InvalidValueType((spec.__class__,))
4819 value = spec(value=value)
4820 if spec.default is not None and value == spec.default:
4821 self._value.pop(key, None)
4823 self._value[key] = value
4825 def __getitem__(self, key):
4826 value = self._value.get(key)
4827 if value is not None:
4829 spec = self.specs.get(key)
4831 raise ObjUnknown(key)
4832 if spec.default is not None:
4836 def _encoded_values(self):
4838 for name, spec in iteritems(self.specs):
4839 value = self._value.get(name)
4843 raise ObjNotReady(name)
4844 raws.append(value.encode())
4848 v = b"".join(self._encoded_values())
4849 return b"".join((self.tag, len_encode(len(v)), v))
4851 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4853 t, tlen, lv = tag_strip(tlv)
4854 except DecodeError as err:
4855 raise err.__class__(
4857 klass=self.__class__,
4858 decode_path=decode_path,
4863 klass=self.__class__,
4864 decode_path=decode_path,
4867 if tag_only: # pragma: no cover
4870 ctx_bered = ctx.get("bered", False)
4872 l, llen, v = len_decode(lv)
4873 except LenIndefForm as err:
4875 raise err.__class__(
4877 klass=self.__class__,
4878 decode_path=decode_path,
4881 l, llen, v = 0, 1, lv[1:]
4883 except DecodeError as err:
4884 raise err.__class__(
4886 klass=self.__class__,
4887 decode_path=decode_path,
4891 raise NotEnoughData(
4892 "encoded length is longer than data",
4893 klass=self.__class__,
4894 decode_path=decode_path,
4898 v, tail = v[:l], v[l:]
4900 sub_offset = offset + tlen + llen
4903 ctx_allow_default_values = ctx.get("allow_default_values", False)
4904 for name, spec in iteritems(self.specs):
4905 if spec.optional and (
4906 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4910 sub_decode_path = decode_path + (name,)
4912 value, v_tail = spec.decode(
4916 decode_path=sub_decode_path,
4918 _ctx_immutable=False,
4920 except TagMismatch as err:
4921 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
4925 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4926 if defined is not None:
4927 defined_by, defined_spec = defined
4928 if issubclass(value.__class__, SequenceOf):
4929 for i, _value in enumerate(value):
4930 sub_sub_decode_path = sub_decode_path + (
4932 DecodePathDefBy(defined_by),
4934 defined_value, defined_tail = defined_spec.decode(
4935 memoryview(bytes(_value)),
4937 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4938 if value.expled else (value.tlen + value.llen)
4941 decode_path=sub_sub_decode_path,
4943 _ctx_immutable=False,
4945 if len(defined_tail) > 0:
4948 klass=self.__class__,
4949 decode_path=sub_sub_decode_path,
4952 _value.defined = (defined_by, defined_value)
4954 defined_value, defined_tail = defined_spec.decode(
4955 memoryview(bytes(value)),
4957 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4958 if value.expled else (value.tlen + value.llen)
4961 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4963 _ctx_immutable=False,
4965 if len(defined_tail) > 0:
4968 klass=self.__class__,
4969 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4972 value.defined = (defined_by, defined_value)
4974 value_len = value.fulllen
4976 sub_offset += value_len
4978 if spec.default is not None and value == spec.default:
4979 if ctx_bered or ctx_allow_default_values:
4983 "DEFAULT value met",
4984 klass=self.__class__,
4985 decode_path=sub_decode_path,
4988 values[name] = value
4990 spec_defines = getattr(spec, "defines", ())
4991 if len(spec_defines) == 0:
4992 defines_by_path = ctx.get("defines_by_path", ())
4993 if len(defines_by_path) > 0:
4994 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4995 if spec_defines is not None and len(spec_defines) > 0:
4996 for rel_path, schema in spec_defines:
4997 defined = schema.get(value, None)
4998 if defined is not None:
4999 ctx.setdefault("_defines", []).append((
5000 abs_decode_path(sub_decode_path[:-1], rel_path),
5004 if v[:EOC_LEN].tobytes() != EOC:
5007 klass=self.__class__,
5008 decode_path=decode_path,
5016 klass=self.__class__,
5017 decode_path=decode_path,
5020 obj = self.__class__(
5024 default=self.default,
5025 optional=self.optional,
5026 _decoded=(offset, llen, vlen),
5029 obj.lenindef = lenindef
5030 obj.ber_encoded = ber_encoded
5034 value = pp_console_row(next(self.pps()))
5036 for name in self.specs:
5037 _value = self._value.get(name)
5040 cols.append("%s: %s" % (name, repr(_value)))
5041 return "%s[%s]" % (value, "; ".join(cols))
5043 def pps(self, decode_path=()):
5046 asn1_type_name=self.asn1_type_name,
5047 obj_name=self.__class__.__name__,
5048 decode_path=decode_path,
5049 optional=self.optional,
5050 default=self == self.default,
5051 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5052 expl=None if self._expl is None else tag_decode(self._expl),
5057 expl_offset=self.expl_offset if self.expled else None,
5058 expl_tlen=self.expl_tlen if self.expled else None,
5059 expl_llen=self.expl_llen if self.expled else None,
5060 expl_vlen=self.expl_vlen if self.expled else None,
5061 expl_lenindef=self.expl_lenindef,
5062 lenindef=self.lenindef,
5063 ber_encoded=self.ber_encoded,
5066 for name in self.specs:
5067 value = self._value.get(name)
5070 yield value.pps(decode_path=decode_path + (name,))
5071 for pp in self.pps_lenindef(decode_path):
5075 class Set(Sequence):
5076 """``SET`` structure type
5078 Its usage is identical to :py:class:`pyderasn.Sequence`.
5080 .. _allow_unordered_set_ctx:
5082 DER prohibits unordered values encoding and will raise an error
5083 during decode. If If :ref:`bered <bered_ctx>` context option is set,
5084 then no error will occure. Also you can disable strict values
5085 ordering check by setting ``"allow_unordered_set": True``
5086 :ref:`context <ctx>` option.
5089 tag_default = tag_encode(form=TagFormConstructed, num=17)
5090 asn1_type_name = "SET"
5093 raws = self._encoded_values()
5096 return b"".join((self.tag, len_encode(len(v)), v))
5098 def _specs_items(self):
5099 return iteritems(self.specs)
5101 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5103 t, tlen, lv = tag_strip(tlv)
5104 except DecodeError as err:
5105 raise err.__class__(
5107 klass=self.__class__,
5108 decode_path=decode_path,
5113 klass=self.__class__,
5114 decode_path=decode_path,
5120 ctx_bered = ctx.get("bered", False)
5122 l, llen, v = len_decode(lv)
5123 except LenIndefForm as err:
5125 raise err.__class__(
5127 klass=self.__class__,
5128 decode_path=decode_path,
5131 l, llen, v = 0, 1, lv[1:]
5133 except DecodeError as err:
5134 raise err.__class__(
5136 klass=self.__class__,
5137 decode_path=decode_path,
5141 raise NotEnoughData(
5142 "encoded length is longer than data",
5143 klass=self.__class__,
5147 v, tail = v[:l], v[l:]
5149 sub_offset = offset + tlen + llen
5152 ctx_allow_default_values = ctx.get("allow_default_values", False)
5153 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5154 value_prev = memoryview(v[:0])
5157 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5159 for name, spec in self._specs_items():
5160 sub_decode_path = decode_path + (name,)
5166 decode_path=sub_decode_path,
5169 _ctx_immutable=False,
5176 klass=self.__class__,
5177 decode_path=decode_path,
5180 value, v_tail = spec.decode(
5184 decode_path=sub_decode_path,
5186 _ctx_immutable=False,
5188 value_len = value.fulllen
5189 if value_prev.tobytes() > v[:value_len].tobytes():
5190 if ctx_bered or ctx_allow_unordered_set:
5194 "unordered " + self.asn1_type_name,
5195 klass=self.__class__,
5196 decode_path=sub_decode_path,
5199 if spec.default is None or value != spec.default:
5201 elif ctx_bered or ctx_allow_default_values:
5205 "DEFAULT value met",
5206 klass=self.__class__,
5207 decode_path=sub_decode_path,
5210 values[name] = value
5211 value_prev = v[:value_len]
5212 sub_offset += value_len
5215 obj = self.__class__(
5219 default=self.default,
5220 optional=self.optional,
5221 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5224 if v[:EOC_LEN].tobytes() != EOC:
5227 klass=self.__class__,
5228 decode_path=decode_path,
5236 "not all values are ready",
5237 klass=self.__class__,
5238 decode_path=decode_path,
5241 obj.ber_encoded = ber_encoded
5245 class SequenceOf(Obj):
5246 """``SEQUENCE OF`` sequence type
5248 For that kind of type you must specify the object it will carry on
5249 (bounds are for example here, not required)::
5251 class Ints(SequenceOf):
5256 >>> ints.append(Integer(123))
5257 >>> ints.append(Integer(234))
5259 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5260 >>> [int(i) for i in ints]
5262 >>> ints.append(Integer(345))
5263 Traceback (most recent call last):
5264 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5267 >>> ints[1] = Integer(345)
5269 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5271 Also you can initialize sequence with preinitialized values:
5273 >>> ints = Ints([Integer(123), Integer(234)])
5275 __slots__ = ("spec", "_bound_min", "_bound_max")
5276 tag_default = tag_encode(form=TagFormConstructed, num=16)
5277 asn1_type_name = "SEQUENCE OF"
5290 super(SequenceOf, self).__init__(
5298 schema = getattr(self, "schema", None)
5300 raise ValueError("schema must be specified")
5302 self._bound_min, self._bound_max = getattr(
5306 ) if bounds is None else bounds
5308 if value is not None:
5309 self._value = self._value_sanitize(value)
5310 if default is not None:
5311 default_value = self._value_sanitize(default)
5312 default_obj = self.__class__(
5317 default_obj._value = default_value
5318 self.default = default_obj
5320 self._value = default_obj.copy()._value
5322 def _value_sanitize(self, value):
5323 if issubclass(value.__class__, SequenceOf):
5324 value = value._value
5325 elif hasattr(value, "__iter__"):
5328 raise InvalidValueType((self.__class__, iter))
5329 if not self._bound_min <= len(value) <= self._bound_max:
5330 raise BoundsError(self._bound_min, len(value), self._bound_max)
5332 if not isinstance(v, self.spec.__class__):
5333 raise InvalidValueType((self.spec.__class__,))
5338 return all(v.ready for v in self._value)
5342 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5344 return any(v.bered for v in self._value)
5347 obj = self.__class__(schema=self.spec)
5348 obj._bound_min = self._bound_min
5349 obj._bound_max = self._bound_max
5351 obj._expl = self._expl
5352 obj.default = self.default
5353 obj.optional = self.optional
5354 obj.offset = self.offset
5355 obj.llen = self.llen
5356 obj.vlen = self.vlen
5357 obj.expl_lenindef = self.expl_lenindef
5358 obj.lenindef = self.lenindef
5359 obj.ber_encoded = self.ber_encoded
5360 obj._value = [v.copy() for v in self._value]
5363 def __eq__(self, their):
5364 if isinstance(their, self.__class__):
5366 self.spec == their.spec and
5367 self.tag == their.tag and
5368 self._expl == their._expl and
5369 self._value == their._value
5371 if hasattr(their, "__iter__"):
5372 return self._value == list(their)
5384 return self.__class__(
5388 (self._bound_min, self._bound_max)
5389 if bounds is None else bounds
5391 impl=self.tag if impl is None else impl,
5392 expl=self._expl if expl is None else expl,
5393 default=self.default if default is None else default,
5394 optional=self.optional if optional is None else optional,
5397 def __contains__(self, key):
5398 return key in self._value
5400 def append(self, value):
5401 if not isinstance(value, self.spec.__class__):
5402 raise InvalidValueType((self.spec.__class__,))
5403 if len(self._value) + 1 > self._bound_max:
5406 len(self._value) + 1,
5409 self._value.append(value)
5412 self._assert_ready()
5413 return iter(self._value)
5416 self._assert_ready()
5417 return len(self._value)
5419 def __setitem__(self, key, value):
5420 if not isinstance(value, self.spec.__class__):
5421 raise InvalidValueType((self.spec.__class__,))
5422 self._value[key] = self.spec(value=value)
5424 def __getitem__(self, key):
5425 return self._value[key]
5427 def _encoded_values(self):
5428 return [v.encode() for v in self._value]
5431 v = b"".join(self._encoded_values())
5432 return b"".join((self.tag, len_encode(len(v)), v))
5434 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5436 t, tlen, lv = tag_strip(tlv)
5437 except DecodeError as err:
5438 raise err.__class__(
5440 klass=self.__class__,
5441 decode_path=decode_path,
5446 klass=self.__class__,
5447 decode_path=decode_path,
5453 ctx_bered = ctx.get("bered", False)
5455 l, llen, v = len_decode(lv)
5456 except LenIndefForm as err:
5458 raise err.__class__(
5460 klass=self.__class__,
5461 decode_path=decode_path,
5464 l, llen, v = 0, 1, lv[1:]
5466 except DecodeError as err:
5467 raise err.__class__(
5469 klass=self.__class__,
5470 decode_path=decode_path,
5474 raise NotEnoughData(
5475 "encoded length is longer than data",
5476 klass=self.__class__,
5477 decode_path=decode_path,
5481 v, tail = v[:l], v[l:]
5483 sub_offset = offset + tlen + llen
5485 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5486 value_prev = memoryview(v[:0])
5490 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5492 sub_decode_path = decode_path + (str(len(_value)),)
5493 value, v_tail = spec.decode(
5497 decode_path=sub_decode_path,
5499 _ctx_immutable=False,
5501 value_len = value.fulllen
5503 if value_prev.tobytes() > v[:value_len].tobytes():
5504 if ctx_bered or ctx_allow_unordered_set:
5508 "unordered " + self.asn1_type_name,
5509 klass=self.__class__,
5510 decode_path=sub_decode_path,
5513 value_prev = v[:value_len]
5514 _value.append(value)
5515 sub_offset += value_len
5519 obj = self.__class__(
5522 bounds=(self._bound_min, self._bound_max),
5525 default=self.default,
5526 optional=self.optional,
5527 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5529 except BoundsError as err:
5532 klass=self.__class__,
5533 decode_path=decode_path,
5537 if v[:EOC_LEN].tobytes() != EOC:
5540 klass=self.__class__,
5541 decode_path=decode_path,
5546 obj.ber_encoded = ber_encoded
5551 pp_console_row(next(self.pps())),
5552 ", ".join(repr(v) for v in self._value),
5555 def pps(self, decode_path=()):
5558 asn1_type_name=self.asn1_type_name,
5559 obj_name=self.__class__.__name__,
5560 decode_path=decode_path,
5561 optional=self.optional,
5562 default=self == self.default,
5563 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5564 expl=None if self._expl is None else tag_decode(self._expl),
5569 expl_offset=self.expl_offset if self.expled else None,
5570 expl_tlen=self.expl_tlen if self.expled else None,
5571 expl_llen=self.expl_llen if self.expled else None,
5572 expl_vlen=self.expl_vlen if self.expled else None,
5573 expl_lenindef=self.expl_lenindef,
5574 lenindef=self.lenindef,
5575 ber_encoded=self.ber_encoded,
5578 for i, value in enumerate(self._value):
5579 yield value.pps(decode_path=decode_path + (str(i),))
5580 for pp in self.pps_lenindef(decode_path):
5584 class SetOf(SequenceOf):
5585 """``SET OF`` sequence type
5587 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5590 tag_default = tag_encode(form=TagFormConstructed, num=17)
5591 asn1_type_name = "SET OF"
5594 raws = self._encoded_values()
5597 return b"".join((self.tag, len_encode(len(v)), v))
5599 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5600 return super(SetOf, self)._decode(
5606 ordering_check=True,
5610 def obj_by_path(pypath): # pragma: no cover
5611 """Import object specified as string Python path
5613 Modules must be separated from classes/functions with ``:``.
5615 >>> obj_by_path("foo.bar:Baz")
5616 <class 'foo.bar.Baz'>
5617 >>> obj_by_path("foo.bar:Baz.boo")
5618 <classmethod 'foo.bar.Baz.boo'>
5620 mod, objs = pypath.rsplit(":", 1)
5621 from importlib import import_module
5622 obj = import_module(mod)
5623 for obj_name in objs.split("."):
5624 obj = getattr(obj, obj_name)
5628 def generic_decoder(): # pragma: no cover
5629 # All of this below is a big hack with self references
5630 choice = PrimitiveTypes()
5631 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5632 choice.specs["SetOf"] = SetOf(schema=choice)
5633 for i in six_xrange(31):
5634 choice.specs["SequenceOf%d" % i] = SequenceOf(
5638 choice.specs["Any"] = Any()
5640 # Class name equals to type name, to omit it from output
5641 class SEQUENCEOF(SequenceOf):
5649 with_decode_path=False,
5650 decode_path_only=(),
5652 def _pprint_pps(pps):
5654 if hasattr(pp, "_fields"):
5656 decode_path_only != () and
5657 pp.decode_path[:len(decode_path_only)] != decode_path_only
5660 if pp.asn1_type_name == Choice.asn1_type_name:
5662 pp_kwargs = pp._asdict()
5663 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5664 pp = _pp(**pp_kwargs)
5665 yield pp_console_row(
5670 with_colours=with_colours,
5671 with_decode_path=with_decode_path,
5672 decode_path_len_decrease=len(decode_path_only),
5674 for row in pp_console_blob(
5676 decode_path_len_decrease=len(decode_path_only),
5680 for row in _pprint_pps(pp):
5682 return "\n".join(_pprint_pps(obj.pps()))
5683 return SEQUENCEOF(), pprint_any
5686 def main(): # pragma: no cover
5688 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5689 parser.add_argument(
5693 help="Skip that number of bytes from the beginning",
5695 parser.add_argument(
5697 help="Python paths to dictionary with OIDs, comma separated",
5699 parser.add_argument(
5701 help="Python path to schema definition to use",
5703 parser.add_argument(
5704 "--defines-by-path",
5705 help="Python path to decoder's defines_by_path",
5707 parser.add_argument(
5709 action="store_true",
5710 help="Disallow BER encoding",
5712 parser.add_argument(
5713 "--print-decode-path",
5714 action="store_true",
5715 help="Print decode paths",
5717 parser.add_argument(
5718 "--decode-path-only",
5719 help="Print only specified decode path",
5721 parser.add_argument(
5723 action="store_true",
5724 help="Allow explicit tag out-of-bound",
5726 parser.add_argument(
5728 type=argparse.FileType("rb"),
5729 help="Path to DER file you want to decode",
5731 args = parser.parse_args()
5732 args.DERFile.seek(args.skip)
5733 der = memoryview(args.DERFile.read())
5734 args.DERFile.close()
5736 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
5737 if args.oids else ()
5740 schema = obj_by_path(args.schema)
5741 from functools import partial
5742 pprinter = partial(pprint, big_blobs=True)
5744 schema, pprinter = generic_decoder()
5746 "bered": not args.nobered,
5747 "allow_expl_oob": args.allow_expl_oob,
5749 if args.defines_by_path is not None:
5750 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5751 obj, tail = schema().decode(der, ctx=ctx)
5755 with_colours=environ.get("NO_COLOR") is None,
5756 with_decode_path=args.print_decode_path,
5758 () if args.decode_path_only is None else
5759 tuple(args.decode_path_only.split(":"))
5763 print("\nTrailing data: %s" % hexenc(tail))
5766 if __name__ == "__main__":