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 elif 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)
2322 raise InvalidValueType((self.__class__, string_types, binary_type))
2323 if isinstance(value, tuple):
2326 isinstance(value[0], integer_types) and
2327 isinstance(value[1], binary_type)
2332 bit = self.specs.get(name)
2334 raise ObjUnknown("BitString value: %s" % name)
2337 return self._bits2octets("")
2338 bits = frozenset(bits)
2339 return self._bits2octets("".join(
2340 ("1" if bit in bits else "0")
2341 for bit in six_xrange(max(bits) + 1)
2343 if issubclass(value.__class__, BitString):
2345 raise InvalidValueType((self.__class__, binary_type, string_types))
2349 return self._value is not None
2352 obj = self.__class__(_specs=self.specs)
2354 if value is not None:
2355 value = (value[0], value[1])
2358 obj._expl = self._expl
2359 obj.default = self.default
2360 obj.optional = self.optional
2361 obj.offset = self.offset
2362 obj.llen = self.llen
2363 obj.vlen = self.vlen
2364 obj.expl_lenindef = self.expl_lenindef
2365 obj.lenindef = self.lenindef
2366 obj.ber_encoded = self.ber_encoded
2370 self._assert_ready()
2371 for i in six_xrange(self._value[0]):
2376 self._assert_ready()
2377 return self._value[0]
2379 def __bytes__(self):
2380 self._assert_ready()
2381 return self._value[1]
2383 def __eq__(self, their):
2384 if isinstance(their, bytes):
2385 return self._value[1] == their
2386 if not issubclass(their.__class__, BitString):
2389 self._value == their._value and
2390 self.tag == their.tag and
2391 self._expl == their._expl
2396 return [name for name, bit in iteritems(self.specs) if self[bit]]
2406 return self.__class__(
2408 impl=self.tag if impl is None else impl,
2409 expl=self._expl if expl is None else expl,
2410 default=self.default if default is None else default,
2411 optional=self.optional if optional is None else optional,
2415 def __getitem__(self, key):
2416 if isinstance(key, int):
2417 bit_len, octets = self._value
2421 byte2int(memoryview(octets)[key // 8:]) >>
2424 if isinstance(key, string_types):
2425 value = self.specs.get(key)
2427 raise ObjUnknown("BitString value: %s" % key)
2429 raise InvalidValueType((int, str))
2432 self._assert_ready()
2433 bit_len, octets = self._value
2436 len_encode(len(octets) + 1),
2437 int2byte((8 - bit_len % 8) % 8),
2441 def _decode_chunk(self, lv, offset, decode_path, ctx):
2443 l, llen, v = len_decode(lv)
2444 except DecodeError as err:
2445 raise err.__class__(
2447 klass=self.__class__,
2448 decode_path=decode_path,
2452 raise NotEnoughData(
2453 "encoded length is longer than data",
2454 klass=self.__class__,
2455 decode_path=decode_path,
2459 raise NotEnoughData(
2461 klass=self.__class__,
2462 decode_path=decode_path,
2465 pad_size = byte2int(v)
2466 if l == 1 and pad_size != 0:
2468 "invalid empty value",
2469 klass=self.__class__,
2470 decode_path=decode_path,
2476 klass=self.__class__,
2477 decode_path=decode_path,
2480 if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
2483 klass=self.__class__,
2484 decode_path=decode_path,
2487 v, tail = v[:l], v[l:]
2488 obj = self.__class__(
2489 value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
2492 default=self.default,
2493 optional=self.optional,
2495 _decoded=(offset, llen, l),
2499 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2501 t, tlen, lv = tag_strip(tlv)
2502 except DecodeError as err:
2503 raise err.__class__(
2505 klass=self.__class__,
2506 decode_path=decode_path,
2510 if tag_only: # pragma: no cover
2512 return self._decode_chunk(lv, offset, decode_path, ctx)
2513 if t == self.tag_constructed:
2514 if not ctx.get("bered", False):
2516 "unallowed BER constructed encoding",
2517 klass=self.__class__,
2518 decode_path=decode_path,
2521 if tag_only: # pragma: no cover
2525 l, llen, v = len_decode(lv)
2526 except LenIndefForm:
2527 llen, l, v = 1, 0, lv[1:]
2529 except DecodeError as err:
2530 raise err.__class__(
2532 klass=self.__class__,
2533 decode_path=decode_path,
2537 raise NotEnoughData(
2538 "encoded length is longer than data",
2539 klass=self.__class__,
2540 decode_path=decode_path,
2543 if not lenindef and l == 0:
2544 raise NotEnoughData(
2546 klass=self.__class__,
2547 decode_path=decode_path,
2551 sub_offset = offset + tlen + llen
2555 if v[:EOC_LEN].tobytes() == EOC:
2562 "chunk out of bounds",
2563 klass=self.__class__,
2564 decode_path=decode_path + (str(len(chunks) - 1),),
2565 offset=chunks[-1].offset,
2567 sub_decode_path = decode_path + (str(len(chunks)),)
2569 chunk, v_tail = BitString().decode(
2572 decode_path=sub_decode_path,
2575 _ctx_immutable=False,
2579 "expected BitString encoded chunk",
2580 klass=self.__class__,
2581 decode_path=sub_decode_path,
2584 chunks.append(chunk)
2585 sub_offset += chunk.tlvlen
2586 vlen += chunk.tlvlen
2588 if len(chunks) == 0:
2591 klass=self.__class__,
2592 decode_path=decode_path,
2597 for chunk_i, chunk in enumerate(chunks[:-1]):
2598 if chunk.bit_len % 8 != 0:
2600 "BitString chunk is not multiple of 8 bits",
2601 klass=self.__class__,
2602 decode_path=decode_path + (str(chunk_i),),
2603 offset=chunk.offset,
2605 values.append(bytes(chunk))
2606 bit_len += chunk.bit_len
2607 chunk_last = chunks[-1]
2608 values.append(bytes(chunk_last))
2609 bit_len += chunk_last.bit_len
2610 obj = self.__class__(
2611 value=(bit_len, b"".join(values)),
2614 default=self.default,
2615 optional=self.optional,
2617 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2619 obj.lenindef = lenindef
2620 obj.ber_encoded = True
2621 return obj, (v[EOC_LEN:] if lenindef else v)
2623 klass=self.__class__,
2624 decode_path=decode_path,
2629 return pp_console_row(next(self.pps()))
2631 def pps(self, decode_path=()):
2635 bit_len, blob = self._value
2636 value = "%d bits" % bit_len
2637 if len(self.specs) > 0:
2638 blob = tuple(self.named)
2641 asn1_type_name=self.asn1_type_name,
2642 obj_name=self.__class__.__name__,
2643 decode_path=decode_path,
2646 optional=self.optional,
2647 default=self == self.default,
2648 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
2649 expl=None if self._expl is None else tag_decode(self._expl),
2654 expl_offset=self.expl_offset if self.expled else None,
2655 expl_tlen=self.expl_tlen if self.expled else None,
2656 expl_llen=self.expl_llen if self.expled else None,
2657 expl_vlen=self.expl_vlen if self.expled else None,
2658 expl_lenindef=self.expl_lenindef,
2659 lenindef=self.lenindef,
2660 ber_encoded=self.ber_encoded,
2663 defined_by, defined = self.defined or (None, None)
2664 if defined_by is not None:
2666 decode_path=decode_path + (DecodePathDefBy(defined_by),)
2668 for pp in self.pps_lenindef(decode_path):
2672 class OctetString(Obj):
2673 """``OCTET STRING`` binary string type
2675 >>> s = OctetString(b"hello world")
2676 OCTET STRING 11 bytes 68656c6c6f20776f726c64
2677 >>> s == OctetString(b"hello world")
2682 >>> OctetString(b"hello", bounds=(4, 4))
2683 Traceback (most recent call last):
2684 pyderasn.BoundsError: unsatisfied bounds: 4 <= 5 <= 4
2685 >>> OctetString(b"hell", bounds=(4, 4))
2686 OCTET STRING 4 bytes 68656c6c
2690 Pay attention that OCTET STRING can be encoded both in primitive
2691 and constructed forms. Decoder always checks constructed form tag
2692 additionally to specified primitive one. If BER decoding is
2693 :ref:`not enabled <bered_ctx>`, then decoder will fail, because
2694 of DER restrictions.
2696 __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
2697 tag_default = tag_encode(4)
2698 asn1_type_name = "OCTET STRING"
2711 :param value: set the value. Either binary type, or
2712 :py:class:`pyderasn.OctetString` object
2713 :param bounds: set ``(MIN, MAX)`` value size constraint.
2714 (-inf, +inf) by default
2715 :param bytes impl: override default tag with ``IMPLICIT`` one
2716 :param bytes expl: override default tag with ``EXPLICIT`` one
2717 :param default: set default value. Type same as in ``value``
2718 :param bool optional: is object ``OPTIONAL`` in sequence
2720 super(OctetString, self).__init__(
2728 self._bound_min, self._bound_max = getattr(
2732 ) if bounds is None else bounds
2733 if value is not None:
2734 self._value = self._value_sanitize(value)
2735 if default is not None:
2736 default = self._value_sanitize(default)
2737 self.default = self.__class__(
2742 if self._value is None:
2743 self._value = default
2745 tag_klass, _, tag_num = tag_decode(self.tag)
2746 self.tag_constructed = tag_encode(
2748 form=TagFormConstructed,
2752 def _value_sanitize(self, value):
2753 if isinstance(value, binary_type):
2755 elif issubclass(value.__class__, OctetString):
2756 value = value._value
2758 raise InvalidValueType((self.__class__, bytes))
2759 if not self._bound_min <= len(value) <= self._bound_max:
2760 raise BoundsError(self._bound_min, len(value), self._bound_max)
2765 return self._value is not None
2768 obj = self.__class__()
2769 obj._value = self._value
2770 obj._bound_min = self._bound_min
2771 obj._bound_max = self._bound_max
2773 obj._expl = self._expl
2774 obj.default = self.default
2775 obj.optional = self.optional
2776 obj.offset = self.offset
2777 obj.llen = self.llen
2778 obj.vlen = self.vlen
2779 obj.expl_lenindef = self.expl_lenindef
2780 obj.lenindef = self.lenindef
2781 obj.ber_encoded = self.ber_encoded
2784 def __bytes__(self):
2785 self._assert_ready()
2788 def __eq__(self, their):
2789 if isinstance(their, binary_type):
2790 return self._value == their
2791 if not issubclass(their.__class__, OctetString):
2794 self._value == their._value and
2795 self.tag == their.tag and
2796 self._expl == their._expl
2799 def __lt__(self, their):
2800 return self._value < their._value
2811 return self.__class__(
2814 (self._bound_min, self._bound_max)
2815 if bounds is None else bounds
2817 impl=self.tag if impl is None else impl,
2818 expl=self._expl if expl is None else expl,
2819 default=self.default if default is None else default,
2820 optional=self.optional if optional is None else optional,
2824 self._assert_ready()
2827 len_encode(len(self._value)),
2831 def _decode_chunk(self, lv, offset, decode_path, ctx):
2833 l, llen, v = len_decode(lv)
2834 except DecodeError as err:
2835 raise err.__class__(
2837 klass=self.__class__,
2838 decode_path=decode_path,
2842 raise NotEnoughData(
2843 "encoded length is longer than data",
2844 klass=self.__class__,
2845 decode_path=decode_path,
2848 v, tail = v[:l], v[l:]
2850 obj = self.__class__(
2852 bounds=(self._bound_min, self._bound_max),
2855 default=self.default,
2856 optional=self.optional,
2857 _decoded=(offset, llen, l),
2859 except DecodeError as err:
2862 klass=self.__class__,
2863 decode_path=decode_path,
2866 except BoundsError as err:
2869 klass=self.__class__,
2870 decode_path=decode_path,
2875 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
2877 t, tlen, lv = tag_strip(tlv)
2878 except DecodeError as err:
2879 raise err.__class__(
2881 klass=self.__class__,
2882 decode_path=decode_path,
2888 return self._decode_chunk(lv, offset, decode_path, ctx)
2889 if t == self.tag_constructed:
2890 if not ctx.get("bered", False):
2892 "unallowed BER constructed encoding",
2893 klass=self.__class__,
2894 decode_path=decode_path,
2901 l, llen, v = len_decode(lv)
2902 except LenIndefForm:
2903 llen, l, v = 1, 0, lv[1:]
2905 except DecodeError as err:
2906 raise err.__class__(
2908 klass=self.__class__,
2909 decode_path=decode_path,
2913 raise NotEnoughData(
2914 "encoded length is longer than data",
2915 klass=self.__class__,
2916 decode_path=decode_path,
2920 sub_offset = offset + tlen + llen
2924 if v[:EOC_LEN].tobytes() == EOC:
2931 "chunk out of bounds",
2932 klass=self.__class__,
2933 decode_path=decode_path + (str(len(chunks) - 1),),
2934 offset=chunks[-1].offset,
2936 sub_decode_path = decode_path + (str(len(chunks)),)
2938 chunk, v_tail = OctetString().decode(
2941 decode_path=sub_decode_path,
2944 _ctx_immutable=False,
2948 "expected OctetString encoded chunk",
2949 klass=self.__class__,
2950 decode_path=sub_decode_path,
2953 chunks.append(chunk)
2954 sub_offset += chunk.tlvlen
2955 vlen += chunk.tlvlen
2958 obj = self.__class__(
2959 value=b"".join(bytes(chunk) for chunk in chunks),
2960 bounds=(self._bound_min, self._bound_max),
2963 default=self.default,
2964 optional=self.optional,
2965 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
2967 except DecodeError as err:
2970 klass=self.__class__,
2971 decode_path=decode_path,
2974 except BoundsError as err:
2977 klass=self.__class__,
2978 decode_path=decode_path,
2981 obj.lenindef = lenindef
2982 obj.ber_encoded = True
2983 return obj, (v[EOC_LEN:] if lenindef else v)
2985 klass=self.__class__,
2986 decode_path=decode_path,
2991 return pp_console_row(next(self.pps()))
2993 def pps(self, decode_path=()):
2996 asn1_type_name=self.asn1_type_name,
2997 obj_name=self.__class__.__name__,
2998 decode_path=decode_path,
2999 value=("%d bytes" % len(self._value)) if self.ready else None,
3000 blob=self._value if self.ready else None,
3001 optional=self.optional,
3002 default=self == self.default,
3003 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3004 expl=None if self._expl is None else tag_decode(self._expl),
3009 expl_offset=self.expl_offset if self.expled else None,
3010 expl_tlen=self.expl_tlen if self.expled else None,
3011 expl_llen=self.expl_llen if self.expled else None,
3012 expl_vlen=self.expl_vlen if self.expled else None,
3013 expl_lenindef=self.expl_lenindef,
3014 lenindef=self.lenindef,
3015 ber_encoded=self.ber_encoded,
3018 defined_by, defined = self.defined or (None, None)
3019 if defined_by is not None:
3021 decode_path=decode_path + (DecodePathDefBy(defined_by),)
3023 for pp in self.pps_lenindef(decode_path):
3028 """``NULL`` null object
3036 tag_default = tag_encode(5)
3037 asn1_type_name = "NULL"
3041 value=None, # unused, but Sequence passes it
3048 :param bytes impl: override default tag with ``IMPLICIT`` one
3049 :param bytes expl: override default tag with ``EXPLICIT`` one
3050 :param bool optional: is object ``OPTIONAL`` in sequence
3052 super(Null, self).__init__(impl, expl, None, optional, _decoded)
3060 obj = self.__class__()
3062 obj._expl = self._expl
3063 obj.default = self.default
3064 obj.optional = self.optional
3065 obj.offset = self.offset
3066 obj.llen = self.llen
3067 obj.vlen = self.vlen
3068 obj.expl_lenindef = self.expl_lenindef
3069 obj.lenindef = self.lenindef
3070 obj.ber_encoded = self.ber_encoded
3073 def __eq__(self, their):
3074 if not issubclass(their.__class__, Null):
3077 self.tag == their.tag and
3078 self._expl == their._expl
3088 return self.__class__(
3089 impl=self.tag if impl is None else impl,
3090 expl=self._expl if expl is None else expl,
3091 optional=self.optional if optional is None else optional,
3095 return self.tag + len_encode(0)
3097 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3099 t, _, lv = tag_strip(tlv)
3100 except DecodeError as err:
3101 raise err.__class__(
3103 klass=self.__class__,
3104 decode_path=decode_path,
3109 klass=self.__class__,
3110 decode_path=decode_path,
3113 if tag_only: # pragma: no cover
3116 l, _, v = len_decode(lv)
3117 except DecodeError as err:
3118 raise err.__class__(
3120 klass=self.__class__,
3121 decode_path=decode_path,
3125 raise InvalidLength(
3126 "Null must have zero length",
3127 klass=self.__class__,
3128 decode_path=decode_path,
3131 obj = self.__class__(
3134 optional=self.optional,
3135 _decoded=(offset, 1, 0),
3140 return pp_console_row(next(self.pps()))
3142 def pps(self, decode_path=()):
3145 asn1_type_name=self.asn1_type_name,
3146 obj_name=self.__class__.__name__,
3147 decode_path=decode_path,
3148 optional=self.optional,
3149 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3150 expl=None if self._expl is None else tag_decode(self._expl),
3155 expl_offset=self.expl_offset if self.expled else None,
3156 expl_tlen=self.expl_tlen if self.expled else None,
3157 expl_llen=self.expl_llen if self.expled else None,
3158 expl_vlen=self.expl_vlen if self.expled else None,
3159 expl_lenindef=self.expl_lenindef,
3162 for pp in self.pps_lenindef(decode_path):
3166 class ObjectIdentifier(Obj):
3167 """``OBJECT IDENTIFIER`` OID type
3169 >>> oid = ObjectIdentifier((1, 2, 3))
3170 OBJECT IDENTIFIER 1.2.3
3171 >>> oid == ObjectIdentifier("1.2.3")
3177 >>> oid + (4, 5) + ObjectIdentifier("1.7")
3178 OBJECT IDENTIFIER 1.2.3.4.5.1.7
3180 >>> str(ObjectIdentifier((3, 1)))
3181 Traceback (most recent call last):
3182 pyderasn.InvalidOID: unacceptable first arc value
3184 __slots__ = ("defines",)
3185 tag_default = tag_encode(6)
3186 asn1_type_name = "OBJECT IDENTIFIER"
3199 :param value: set the value. Either tuples of integers,
3200 string of "."-concatenated integers, or
3201 :py:class:`pyderasn.ObjectIdentifier` object
3202 :param defines: sequence of tuples. Each tuple has two elements.
3203 First one is relative to current one decode
3204 path, aiming to the field defined by that OID.
3205 Read about relative path in
3206 :py:func:`pyderasn.abs_decode_path`. Second
3207 tuple element is ``{OID: pyderasn.Obj()}``
3208 dictionary, mapping between current OID value
3209 and structure applied to defined field.
3210 :ref:`Read about DEFINED BY <definedby>`
3211 :param bytes impl: override default tag with ``IMPLICIT`` one
3212 :param bytes expl: override default tag with ``EXPLICIT`` one
3213 :param default: set default value. Type same as in ``value``
3214 :param bool optional: is object ``OPTIONAL`` in sequence
3216 super(ObjectIdentifier, self).__init__(
3224 if value is not None:
3225 self._value = self._value_sanitize(value)
3226 if default is not None:
3227 default = self._value_sanitize(default)
3228 self.default = self.__class__(
3233 if self._value is None:
3234 self._value = default
3235 self.defines = defines
3237 def __add__(self, their):
3238 if isinstance(their, self.__class__):
3239 return self.__class__(self._value + their._value)
3240 if isinstance(their, tuple):
3241 return self.__class__(self._value + their)
3242 raise InvalidValueType((self.__class__, tuple))
3244 def _value_sanitize(self, value):
3245 if issubclass(value.__class__, ObjectIdentifier):
3247 if isinstance(value, string_types):
3249 value = tuple(int(arc) for arc in value.split("."))
3251 raise InvalidOID("unacceptable arcs values")
3252 if isinstance(value, tuple):
3254 raise InvalidOID("less than 2 arcs")
3255 first_arc = value[0]
3256 if first_arc in (0, 1):
3257 if not (0 <= value[1] <= 39):
3258 raise InvalidOID("second arc is too wide")
3259 elif first_arc == 2:
3262 raise InvalidOID("unacceptable first arc value")
3264 raise InvalidValueType((self.__class__, str, tuple))
3268 return self._value is not None
3271 obj = self.__class__()
3272 obj._value = self._value
3273 obj.defines = self.defines
3275 obj._expl = self._expl
3276 obj.default = self.default
3277 obj.optional = self.optional
3278 obj.offset = self.offset
3279 obj.llen = self.llen
3280 obj.vlen = self.vlen
3281 obj.expl_lenindef = self.expl_lenindef
3282 obj.lenindef = self.lenindef
3283 obj.ber_encoded = self.ber_encoded
3287 self._assert_ready()
3288 return iter(self._value)
3291 return ".".join(str(arc) for arc in self._value or ())
3294 self._assert_ready()
3297 bytes(self._expl or b"") +
3298 str(self._value).encode("ascii"),
3301 def __eq__(self, their):
3302 if isinstance(their, tuple):
3303 return self._value == their
3304 if not issubclass(their.__class__, ObjectIdentifier):
3307 self.tag == their.tag and
3308 self._expl == their._expl and
3309 self._value == their._value
3312 def __lt__(self, their):
3313 return self._value < their._value
3324 return self.__class__(
3326 defines=self.defines if defines is None else defines,
3327 impl=self.tag if impl is None else impl,
3328 expl=self._expl if expl is None else expl,
3329 default=self.default if default is None else default,
3330 optional=self.optional if optional is None else optional,
3334 self._assert_ready()
3336 first_value = value[1]
3337 first_arc = value[0]
3340 elif first_arc == 1:
3342 elif first_arc == 2:
3344 else: # pragma: no cover
3345 raise RuntimeError("invalid arc is stored")
3346 octets = [zero_ended_encode(first_value)]
3347 for arc in value[2:]:
3348 octets.append(zero_ended_encode(arc))
3349 v = b"".join(octets)
3350 return b"".join((self.tag, len_encode(len(v)), v))
3352 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
3354 t, _, lv = tag_strip(tlv)
3355 except DecodeError as err:
3356 raise err.__class__(
3358 klass=self.__class__,
3359 decode_path=decode_path,
3364 klass=self.__class__,
3365 decode_path=decode_path,
3368 if tag_only: # pragma: no cover
3371 l, llen, v = len_decode(lv)
3372 except DecodeError as err:
3373 raise err.__class__(
3375 klass=self.__class__,
3376 decode_path=decode_path,
3380 raise NotEnoughData(
3381 "encoded length is longer than data",
3382 klass=self.__class__,
3383 decode_path=decode_path,
3387 raise NotEnoughData(
3389 klass=self.__class__,
3390 decode_path=decode_path,
3393 v, tail = v[:l], v[l:]
3400 octet = indexbytes(v, i)
3401 if i == 0 and octet == 0x80:
3402 if ctx.get("bered", False):
3405 raise DecodeError("non normalized arc encoding")
3406 arc = (arc << 7) | (octet & 0x7F)
3407 if octet & 0x80 == 0:
3415 klass=self.__class__,
3416 decode_path=decode_path,
3420 second_arc = arcs[0]
3421 if 0 <= second_arc <= 39:
3423 elif 40 <= second_arc <= 79:
3429 obj = self.__class__(
3430 value=tuple([first_arc, second_arc] + arcs[1:]),
3433 default=self.default,
3434 optional=self.optional,
3435 _decoded=(offset, llen, l),
3438 obj.ber_encoded = True
3442 return pp_console_row(next(self.pps()))
3444 def pps(self, decode_path=()):
3447 asn1_type_name=self.asn1_type_name,
3448 obj_name=self.__class__.__name__,
3449 decode_path=decode_path,
3450 value=str(self) if self.ready else None,
3451 optional=self.optional,
3452 default=self == self.default,
3453 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3454 expl=None if self._expl is None else tag_decode(self._expl),
3459 expl_offset=self.expl_offset if self.expled else None,
3460 expl_tlen=self.expl_tlen if self.expled else None,
3461 expl_llen=self.expl_llen if self.expled else None,
3462 expl_vlen=self.expl_vlen if self.expled else None,
3463 expl_lenindef=self.expl_lenindef,
3464 ber_encoded=self.ber_encoded,
3467 for pp in self.pps_lenindef(decode_path):
3471 class Enumerated(Integer):
3472 """``ENUMERATED`` integer type
3474 This type is identical to :py:class:`pyderasn.Integer`, but requires
3475 schema to be specified and does not accept values missing from it.
3478 tag_default = tag_encode(10)
3479 asn1_type_name = "ENUMERATED"
3490 bounds=None, # dummy argument, workability for Integer.decode
3492 super(Enumerated, self).__init__(
3501 if len(self.specs) == 0:
3502 raise ValueError("schema must be specified")
3504 def _value_sanitize(self, value):
3505 if isinstance(value, self.__class__):
3506 value = value._value
3507 elif isinstance(value, integer_types):
3508 for _value in itervalues(self.specs):
3513 "unknown integer value: %s" % value,
3514 klass=self.__class__,
3516 elif isinstance(value, string_types):
3517 value = self.specs.get(value)
3519 raise ObjUnknown("integer value: %s" % value)
3521 raise InvalidValueType((self.__class__, int, str))
3525 obj = self.__class__(_specs=self.specs)
3526 obj._value = self._value
3527 obj._bound_min = self._bound_min
3528 obj._bound_max = self._bound_max
3530 obj._expl = self._expl
3531 obj.default = self.default
3532 obj.optional = self.optional
3533 obj.offset = self.offset
3534 obj.llen = self.llen
3535 obj.vlen = self.vlen
3536 obj.expl_lenindef = self.expl_lenindef
3537 obj.lenindef = self.lenindef
3538 obj.ber_encoded = self.ber_encoded
3550 return self.__class__(
3552 impl=self.tag if impl is None else impl,
3553 expl=self._expl if expl is None else expl,
3554 default=self.default if default is None else default,
3555 optional=self.optional if optional is None else optional,
3560 class CommonString(OctetString):
3561 """Common class for all strings
3563 Everything resembles :py:class:`pyderasn.OctetString`, except
3564 ability to deal with unicode text strings.
3566 >>> hexenc("привет мир".encode("utf-8"))
3567 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3568 >>> UTF8String("привет мир") == UTF8String(hexdec("d0...80"))
3570 >>> s = UTF8String("привет мир")
3571 UTF8String UTF8String привет мир
3573 'привет мир'
3574 >>> hexenc(bytes(s))
3575 'd0bfd180d0b8d0b2d0b5d18220d0bcd0b8d180'
3577 >>> PrintableString("привет мир")
3578 Traceback (most recent call last):
3579 pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
3581 >>> BMPString("ада", bounds=(2, 2))
3582 Traceback (most recent call last):
3583 pyderasn.BoundsError: unsatisfied bounds: 2 <= 3 <= 2
3584 >>> s = BMPString("ад", bounds=(2, 2))
3587 >>> hexenc(bytes(s))
3595 * - :py:class:`pyderasn.UTF8String`
3597 * - :py:class:`pyderasn.NumericString`
3599 * - :py:class:`pyderasn.PrintableString`
3601 * - :py:class:`pyderasn.TeletexString`
3603 * - :py:class:`pyderasn.T61String`
3605 * - :py:class:`pyderasn.VideotexString`
3607 * - :py:class:`pyderasn.IA5String`
3609 * - :py:class:`pyderasn.GraphicString`
3611 * - :py:class:`pyderasn.VisibleString`
3613 * - :py:class:`pyderasn.ISO646String`
3615 * - :py:class:`pyderasn.GeneralString`
3617 * - :py:class:`pyderasn.UniversalString`
3619 * - :py:class:`pyderasn.BMPString`
3622 __slots__ = ("encoding",)
3624 def _value_sanitize(self, value):
3626 value_decoded = None
3627 if isinstance(value, self.__class__):
3628 value_raw = value._value
3629 elif isinstance(value, text_type):
3630 value_decoded = value
3631 elif isinstance(value, binary_type):
3634 raise InvalidValueType((self.__class__, text_type, binary_type))
3637 value_decoded.encode(self.encoding)
3638 if value_raw is None else value_raw
3641 value_raw.decode(self.encoding)
3642 if value_decoded is None else value_decoded
3644 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3645 raise DecodeError(str(err))
3646 if not self._bound_min <= len(value_decoded) <= self._bound_max:
3654 def __eq__(self, their):
3655 if isinstance(their, binary_type):
3656 return self._value == their
3657 if isinstance(their, text_type):
3658 return self._value == their.encode(self.encoding)
3659 if not isinstance(their, self.__class__):
3662 self._value == their._value and
3663 self.tag == their.tag and
3664 self._expl == their._expl
3667 def __unicode__(self):
3669 return self._value.decode(self.encoding)
3670 return text_type(self._value)
3673 return pp_console_row(next(self.pps(no_unicode=PY2)))
3675 def pps(self, decode_path=(), no_unicode=False):
3678 value = hexenc(bytes(self)) if no_unicode else self.__unicode__()
3681 asn1_type_name=self.asn1_type_name,
3682 obj_name=self.__class__.__name__,
3683 decode_path=decode_path,
3685 optional=self.optional,
3686 default=self == self.default,
3687 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3688 expl=None if self._expl is None else tag_decode(self._expl),
3693 expl_offset=self.expl_offset if self.expled else None,
3694 expl_tlen=self.expl_tlen if self.expled else None,
3695 expl_llen=self.expl_llen if self.expled else None,
3696 expl_vlen=self.expl_vlen if self.expled else None,
3697 expl_lenindef=self.expl_lenindef,
3698 ber_encoded=self.ber_encoded,
3701 for pp in self.pps_lenindef(decode_path):
3705 class UTF8String(CommonString):
3707 tag_default = tag_encode(12)
3709 asn1_type_name = "UTF8String"
3712 class AllowableCharsMixin(object):
3714 def allowable_chars(self):
3716 return self._allowable_chars
3717 return frozenset(six_unichr(c) for c in self._allowable_chars)
3720 class NumericString(AllowableCharsMixin, CommonString):
3723 Its value is properly sanitized: only ASCII digits with spaces can
3726 >>> NumericString().allowable_chars
3727 frozenset(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '])
3730 tag_default = tag_encode(18)
3732 asn1_type_name = "NumericString"
3733 _allowable_chars = frozenset(digits.encode("ascii") + b" ")
3735 def _value_sanitize(self, value):
3736 value = super(NumericString, self)._value_sanitize(value)
3737 if not frozenset(value) <= self._allowable_chars:
3738 raise DecodeError("non-numeric value")
3742 class PrintableString(AllowableCharsMixin, CommonString):
3745 Its value is properly sanitized: see X.680 41.4 table 10.
3747 >>> PrintableString().allowable_chars
3748 frozenset([' ', "'", ..., 'z'])
3751 tag_default = tag_encode(19)
3753 asn1_type_name = "PrintableString"
3754 _allowable_chars = frozenset(
3755 (ascii_letters + digits + " '()+,-./:=?").encode("ascii")
3758 def _value_sanitize(self, value):
3759 value = super(PrintableString, self)._value_sanitize(value)
3760 if not frozenset(value) <= self._allowable_chars:
3761 raise DecodeError("non-printable value")
3765 class TeletexString(CommonString):
3767 tag_default = tag_encode(20)
3769 asn1_type_name = "TeletexString"
3772 class T61String(TeletexString):
3774 asn1_type_name = "T61String"
3777 class VideotexString(CommonString):
3779 tag_default = tag_encode(21)
3780 encoding = "iso-8859-1"
3781 asn1_type_name = "VideotexString"
3784 class IA5String(CommonString):
3786 tag_default = tag_encode(22)
3788 asn1_type_name = "IA5"
3791 LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ")
3792 LEN_YYYYMMDDHHMMSSDMZ = len("YYYYMMDDHHMMSSDMZ")
3793 LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ")
3796 class UTCTime(CommonString):
3797 """``UTCTime`` datetime type
3799 >>> t = UTCTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3800 UTCTime UTCTime 2017-09-30T22:07:50
3806 datetime.datetime(2017, 9, 30, 22, 7, 50)
3807 >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
3808 datetime.datetime(1957, 9, 30, 22, 7, 50)
3812 BER encoding is unsupported.
3815 tag_default = tag_encode(23)
3817 asn1_type_name = "UTCTime"
3827 bounds=None, # dummy argument, workability for OctetString.decode
3830 :param value: set the value. Either datetime type, or
3831 :py:class:`pyderasn.UTCTime` object
3832 :param bytes impl: override default tag with ``IMPLICIT`` one
3833 :param bytes expl: override default tag with ``EXPLICIT`` one
3834 :param default: set default value. Type same as in ``value``
3835 :param bool optional: is object ``OPTIONAL`` in sequence
3837 super(UTCTime, self).__init__(
3845 if value is not None:
3846 self._value = self._value_sanitize(value)
3847 if default is not None:
3848 default = self._value_sanitize(default)
3849 self.default = self.__class__(
3854 if self._value is None:
3855 self._value = default
3857 def _strptime(self, value):
3858 # datetime.strptime's format: %y%m%d%H%M%SZ
3859 if len(value) != LEN_YYMMDDHHMMSSZ:
3860 raise ValueError("invalid UTCTime length")
3861 if value[-1] != "Z":
3862 raise ValueError("non UTC timezone")
3864 2000 + int(value[:2]), # %y
3865 int(value[2:4]), # %m
3866 int(value[4:6]), # %d
3867 int(value[6:8]), # %H
3868 int(value[8:10]), # %M
3869 int(value[10:12]), # %S
3872 def _value_sanitize(self, value):
3873 if isinstance(value, binary_type):
3875 value_decoded = value.decode("ascii")
3876 except (UnicodeEncodeError, UnicodeDecodeError) as err:
3877 raise DecodeError("invalid UTCTime encoding: %r" % err)
3879 self._strptime(value_decoded)
3880 except (TypeError, ValueError) as err:
3881 raise DecodeError("invalid UTCTime format: %r" % err)
3883 if isinstance(value, self.__class__):
3885 if isinstance(value, datetime):
3886 return value.strftime("%y%m%d%H%M%SZ").encode("ascii")
3887 raise InvalidValueType((self.__class__, datetime))
3889 def __eq__(self, their):
3890 if isinstance(their, binary_type):
3891 return self._value == their
3892 if isinstance(their, datetime):
3893 return self.todatetime() == their
3894 if not isinstance(their, self.__class__):
3897 self._value == their._value and
3898 self.tag == their.tag and
3899 self._expl == their._expl
3902 def todatetime(self):
3903 """Convert to datetime
3907 Pay attention that UTCTime can not hold full year, so all years
3908 having < 50 years are treated as 20xx, 19xx otherwise, according
3909 to X.509 recomendation.
3911 value = self._strptime(self._value.decode("ascii"))
3912 year = value.year % 100
3914 year=(2000 + year) if year < 50 else (1900 + year),
3918 minute=value.minute,
3919 second=value.second,
3923 return pp_console_row(next(self.pps()))
3925 def pps(self, decode_path=()):
3928 asn1_type_name=self.asn1_type_name,
3929 obj_name=self.__class__.__name__,
3930 decode_path=decode_path,
3931 value=self.todatetime().isoformat() if self.ready else None,
3932 optional=self.optional,
3933 default=self == self.default,
3934 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
3935 expl=None if self._expl is None else tag_decode(self._expl),
3940 expl_offset=self.expl_offset if self.expled else None,
3941 expl_tlen=self.expl_tlen if self.expled else None,
3942 expl_llen=self.expl_llen if self.expled else None,
3943 expl_vlen=self.expl_vlen if self.expled else None,
3944 expl_lenindef=self.expl_lenindef,
3945 ber_encoded=self.ber_encoded,
3948 for pp in self.pps_lenindef(decode_path):
3952 class GeneralizedTime(UTCTime):
3953 """``GeneralizedTime`` datetime type
3955 This type is similar to :py:class:`pyderasn.UTCTime`.
3957 >>> t = GeneralizedTime(datetime(2017, 9, 30, 22, 7, 50, 123))
3958 GeneralizedTime GeneralizedTime 2017-09-30T22:07:50.000123
3960 '20170930220750.000123Z'
3961 >>> t = GeneralizedTime(datetime(2057, 9, 30, 22, 7, 50))
3962 GeneralizedTime GeneralizedTime 2057-09-30T22:07:50
3966 BER encoding is unsupported.
3970 Only microsecond fractions are supported.
3971 :py:exc:`pyderasn.DecodeError` will be raised during decoding of
3972 higher precision values.
3975 tag_default = tag_encode(24)
3976 asn1_type_name = "GeneralizedTime"
3978 def _strptime(self, value):
3980 if l == LEN_YYYYMMDDHHMMSSZ:
3981 # datetime.strptime's format: %y%m%d%H%M%SZ
3982 if value[-1] != "Z":
3983 raise ValueError("non UTC timezone")
3985 int(value[:4]), # %Y
3986 int(value[4:6]), # %m
3987 int(value[6:8]), # %d
3988 int(value[8:10]), # %H
3989 int(value[10:12]), # %M
3990 int(value[12:14]), # %S
3992 if l >= LEN_YYYYMMDDHHMMSSDMZ:
3993 # datetime.strptime's format: %Y%m%d%H%M%S.%fZ
3994 if value[-1] != "Z":
3995 raise ValueError("non UTC timezone")
3996 if value[14] != ".":
3997 raise ValueError("no fractions separator")
4000 raise ValueError("trailing zero")
4003 raise ValueError("only microsecond fractions are supported")
4004 us = int(us + ("0" * (6 - us_len)))
4006 int(value[:4]), # %Y
4007 int(value[4:6]), # %m
4008 int(value[6:8]), # %d
4009 int(value[8:10]), # %H
4010 int(value[10:12]), # %M
4011 int(value[12:14]), # %S
4015 raise ValueError("invalid GeneralizedTime length")
4017 def _value_sanitize(self, value):
4018 if isinstance(value, binary_type):
4020 value_decoded = value.decode("ascii")
4021 except (UnicodeEncodeError, UnicodeDecodeError) as err:
4022 raise DecodeError("invalid GeneralizedTime encoding: %r" % err)
4024 self._strptime(value_decoded)
4025 except (TypeError, ValueError) as err:
4027 "invalid GeneralizedTime format: %r" % err,
4028 klass=self.__class__,
4031 if isinstance(value, self.__class__):
4033 if isinstance(value, datetime):
4034 encoded = value.strftime("%Y%m%d%H%M%S")
4035 if value.microsecond > 0:
4036 encoded = encoded + (".%06d" % value.microsecond).rstrip("0")
4037 return (encoded + "Z").encode("ascii")
4038 raise InvalidValueType((self.__class__, datetime))
4040 def todatetime(self):
4041 return self._strptime(self._value.decode("ascii"))
4044 class GraphicString(CommonString):
4046 tag_default = tag_encode(25)
4047 encoding = "iso-8859-1"
4048 asn1_type_name = "GraphicString"
4051 class VisibleString(CommonString):
4053 tag_default = tag_encode(26)
4055 asn1_type_name = "VisibleString"
4058 class ISO646String(VisibleString):
4060 asn1_type_name = "ISO646String"
4063 class GeneralString(CommonString):
4065 tag_default = tag_encode(27)
4066 encoding = "iso-8859-1"
4067 asn1_type_name = "GeneralString"
4070 class UniversalString(CommonString):
4072 tag_default = tag_encode(28)
4073 encoding = "utf-32-be"
4074 asn1_type_name = "UniversalString"
4077 class BMPString(CommonString):
4079 tag_default = tag_encode(30)
4080 encoding = "utf-16-be"
4081 asn1_type_name = "BMPString"
4085 """``CHOICE`` special type
4089 class GeneralName(Choice):
4091 ("rfc822Name", IA5String(impl=tag_ctxp(1))),
4092 ("dNSName", IA5String(impl=tag_ctxp(2))),
4095 >>> gn = GeneralName()
4097 >>> gn["rfc822Name"] = IA5String("foo@bar.baz")
4098 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4099 >>> gn["dNSName"] = IA5String("bar.baz")
4100 GeneralName CHOICE dNSName[[2] IA5String IA5 bar.baz]
4101 >>> gn["rfc822Name"]
4104 [2] IA5String IA5 bar.baz
4107 >>> gn.value == gn["dNSName"]
4110 OrderedDict([('rfc822Name', [1] IA5String IA5), ('dNSName', [2] IA5String IA5)])
4112 >>> GeneralName(("rfc822Name", IA5String("foo@bar.baz")))
4113 GeneralName CHOICE rfc822Name[[1] IA5String IA5 foo@bar.baz]
4115 __slots__ = ("specs",)
4117 asn1_type_name = "CHOICE"
4130 :param value: set the value. Either ``(choice, value)`` tuple, or
4131 :py:class:`pyderasn.Choice` object
4132 :param bytes impl: can not be set, do **not** use it
4133 :param bytes expl: override default tag with ``EXPLICIT`` one
4134 :param default: set default value. Type same as in ``value``
4135 :param bool optional: is object ``OPTIONAL`` in sequence
4137 if impl is not None:
4138 raise ValueError("no implicit tag allowed for CHOICE")
4139 super(Choice, self).__init__(None, expl, default, optional, _decoded)
4141 schema = getattr(self, "schema", ())
4142 if len(schema) == 0:
4143 raise ValueError("schema must be specified")
4145 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4148 if value is not None:
4149 self._value = self._value_sanitize(value)
4150 if default is not None:
4151 default_value = self._value_sanitize(default)
4152 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4153 default_obj.specs = self.specs
4154 default_obj._value = default_value
4155 self.default = default_obj
4157 self._value = default_obj.copy()._value
4159 def _value_sanitize(self, value):
4160 if isinstance(value, tuple) and len(value) == 2:
4162 spec = self.specs.get(choice)
4164 raise ObjUnknown(choice)
4165 if not isinstance(obj, spec.__class__):
4166 raise InvalidValueType((spec,))
4167 return (choice, spec(obj))
4168 if isinstance(value, self.__class__):
4170 raise InvalidValueType((self.__class__, tuple))
4174 return self._value is not None and self._value[1].ready
4178 return self.expl_lenindef or (
4179 (self._value is not None) and
4180 self._value[1].bered
4184 obj = self.__class__(schema=self.specs)
4185 obj._expl = self._expl
4186 obj.default = self.default
4187 obj.optional = self.optional
4188 obj.offset = self.offset
4189 obj.llen = self.llen
4190 obj.vlen = self.vlen
4191 obj.expl_lenindef = self.expl_lenindef
4192 obj.lenindef = self.lenindef
4193 obj.ber_encoded = self.ber_encoded
4195 if value is not None:
4196 obj._value = (value[0], value[1].copy())
4199 def __eq__(self, their):
4200 if isinstance(their, tuple) and len(their) == 2:
4201 return self._value == their
4202 if not isinstance(their, self.__class__):
4205 self.specs == their.specs and
4206 self._value == their._value
4216 return self.__class__(
4219 expl=self._expl if expl is None else expl,
4220 default=self.default if default is None else default,
4221 optional=self.optional if optional is None else optional,
4226 self._assert_ready()
4227 return self._value[0]
4231 self._assert_ready()
4232 return self._value[1]
4234 def __getitem__(self, key):
4235 if key not in self.specs:
4236 raise ObjUnknown(key)
4237 if self._value is None:
4239 choice, value = self._value
4244 def __setitem__(self, key, value):
4245 spec = self.specs.get(key)
4247 raise ObjUnknown(key)
4248 if not isinstance(value, spec.__class__):
4249 raise InvalidValueType((spec.__class__,))
4250 self._value = (key, spec(value))
4258 return self._value[1].decoded if self.ready else False
4261 self._assert_ready()
4262 return self._value[1].encode()
4264 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4265 for choice, spec in iteritems(self.specs):
4266 sub_decode_path = decode_path + (choice,)
4272 decode_path=sub_decode_path,
4275 _ctx_immutable=False,
4282 klass=self.__class__,
4283 decode_path=decode_path,
4286 if tag_only: # pragma: no cover
4288 value, tail = spec.decode(
4292 decode_path=sub_decode_path,
4294 _ctx_immutable=False,
4296 obj = self.__class__(
4299 default=self.default,
4300 optional=self.optional,
4301 _decoded=(offset, 0, value.fulllen),
4303 obj._value = (choice, value)
4307 value = pp_console_row(next(self.pps()))
4309 value = "%s[%r]" % (value, self.value)
4312 def pps(self, decode_path=()):
4315 asn1_type_name=self.asn1_type_name,
4316 obj_name=self.__class__.__name__,
4317 decode_path=decode_path,
4318 value=self.choice if self.ready else None,
4319 optional=self.optional,
4320 default=self == self.default,
4321 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4322 expl=None if self._expl is None else tag_decode(self._expl),
4327 expl_lenindef=self.expl_lenindef,
4331 yield self.value.pps(decode_path=decode_path + (self.choice,))
4332 for pp in self.pps_lenindef(decode_path):
4336 class PrimitiveTypes(Choice):
4337 """Predefined ``CHOICE`` for all generic primitive types
4339 It could be useful for general decoding of some unspecified values:
4341 >>> PrimitiveTypes().decode(hexdec("0403666f6f"))[0].value
4342 OCTET STRING 3 bytes 666f6f
4343 >>> PrimitiveTypes().decode(hexdec("0203123456"))[0].value
4347 schema = tuple((klass.__name__, klass()) for klass in (
4372 """``ANY`` special type
4374 >>> Any(Integer(-123))
4376 >>> a = Any(OctetString(b"hello world").encode())
4377 ANY 040b68656c6c6f20776f726c64
4378 >>> hexenc(bytes(a))
4379 b'0x040x0bhello world'
4381 __slots__ = ("defined",)
4382 tag_default = tag_encode(0)
4383 asn1_type_name = "ANY"
4393 :param value: set the value. Either any kind of pyderasn's
4394 **ready** object, or bytes. Pay attention that
4395 **no** validation is performed is raw binary value
4397 :param bytes expl: override default tag with ``EXPLICIT`` one
4398 :param bool optional: is object ``OPTIONAL`` in sequence
4400 super(Any, self).__init__(None, expl, None, optional, _decoded)
4401 self._value = None if value is None else self._value_sanitize(value)
4404 def _value_sanitize(self, value):
4405 if isinstance(value, binary_type):
4407 if isinstance(value, self.__class__):
4409 if isinstance(value, Obj):
4410 return value.encode()
4411 raise InvalidValueType((self.__class__, Obj, binary_type))
4415 return self._value is not None
4419 if self.expl_lenindef or self.lenindef:
4421 if self.defined is None:
4423 return self.defined[1].bered
4426 obj = self.__class__()
4427 obj._value = self._value
4429 obj._expl = self._expl
4430 obj.optional = self.optional
4431 obj.offset = self.offset
4432 obj.llen = self.llen
4433 obj.vlen = self.vlen
4434 obj.expl_lenindef = self.expl_lenindef
4435 obj.lenindef = self.lenindef
4436 obj.ber_encoded = self.ber_encoded
4439 def __eq__(self, their):
4440 if isinstance(their, binary_type):
4441 return self._value == their
4442 if issubclass(their.__class__, Any):
4443 return self._value == their._value
4452 return self.__class__(
4454 expl=self._expl if expl is None else expl,
4455 optional=self.optional if optional is None else optional,
4458 def __bytes__(self):
4459 self._assert_ready()
4467 self._assert_ready()
4470 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4472 t, tlen, lv = tag_strip(tlv)
4473 except DecodeError as err:
4474 raise err.__class__(
4476 klass=self.__class__,
4477 decode_path=decode_path,
4481 l, llen, v = len_decode(lv)
4482 except LenIndefForm as err:
4483 if not ctx.get("bered", False):
4484 raise err.__class__(
4486 klass=self.__class__,
4487 decode_path=decode_path,
4490 llen, vlen, v = 1, 0, lv[1:]
4491 sub_offset = offset + tlen + llen
4493 while v[:EOC_LEN].tobytes() != EOC:
4494 chunk, v = Any().decode(
4497 decode_path=decode_path + (str(chunk_i),),
4500 _ctx_immutable=False,
4502 vlen += chunk.tlvlen
4503 sub_offset += chunk.tlvlen
4505 tlvlen = tlen + llen + vlen + EOC_LEN
4506 obj = self.__class__(
4507 value=tlv[:tlvlen].tobytes(),
4509 optional=self.optional,
4510 _decoded=(offset, 0, tlvlen),
4514 return obj, v[EOC_LEN:]
4515 except DecodeError as err:
4516 raise err.__class__(
4518 klass=self.__class__,
4519 decode_path=decode_path,
4523 raise NotEnoughData(
4524 "encoded length is longer than data",
4525 klass=self.__class__,
4526 decode_path=decode_path,
4529 tlvlen = tlen + llen + l
4530 v, tail = tlv[:tlvlen], v[l:]
4531 obj = self.__class__(
4534 optional=self.optional,
4535 _decoded=(offset, 0, tlvlen),
4541 return pp_console_row(next(self.pps()))
4543 def pps(self, decode_path=()):
4546 asn1_type_name=self.asn1_type_name,
4547 obj_name=self.__class__.__name__,
4548 decode_path=decode_path,
4549 blob=self._value if self.ready else None,
4550 optional=self.optional,
4551 default=self == self.default,
4552 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
4553 expl=None if self._expl is None else tag_decode(self._expl),
4558 expl_offset=self.expl_offset if self.expled else None,
4559 expl_tlen=self.expl_tlen if self.expled else None,
4560 expl_llen=self.expl_llen if self.expled else None,
4561 expl_vlen=self.expl_vlen if self.expled else None,
4562 expl_lenindef=self.expl_lenindef,
4563 lenindef=self.lenindef,
4566 defined_by, defined = self.defined or (None, None)
4567 if defined_by is not None:
4569 decode_path=decode_path + (DecodePathDefBy(defined_by),)
4571 for pp in self.pps_lenindef(decode_path):
4575 ########################################################################
4576 # ASN.1 constructed types
4577 ########################################################################
4579 def get_def_by_path(defines_by_path, sub_decode_path):
4580 """Get define by decode path
4582 for path, define in defines_by_path:
4583 if len(path) != len(sub_decode_path):
4585 for p1, p2 in zip(path, sub_decode_path):
4586 if (p1 != any) and (p1 != p2):
4592 def abs_decode_path(decode_path, rel_path):
4593 """Create an absolute decode path from current and relative ones
4595 :param decode_path: current decode path, starting point. Tuple of strings
4596 :param rel_path: relative path to ``decode_path``. Tuple of strings.
4597 If first tuple's element is "/", then treat it as
4598 an absolute path, ignoring ``decode_path`` as
4599 starting point. Also this tuple can contain ".."
4600 elements, stripping the leading element from
4603 >>> abs_decode_path(("foo", "bar"), ("baz", "whatever"))
4604 ("foo", "bar", "baz", "whatever")
4605 >>> abs_decode_path(("foo", "bar", "baz"), ("..", "..", "whatever"))
4607 >>> abs_decode_path(("foo", "bar"), ("/", "baz", "whatever"))
4610 if rel_path[0] == "/":
4612 if rel_path[0] == "..":
4613 return abs_decode_path(decode_path[:-1], rel_path[1:])
4614 return decode_path + rel_path
4617 class Sequence(Obj):
4618 """``SEQUENCE`` structure type
4620 You have to make specification of sequence::
4622 class Extension(Sequence):
4624 ("extnID", ObjectIdentifier()),
4625 ("critical", Boolean(default=False)),
4626 ("extnValue", OctetString()),
4629 Then, you can work with it as with dictionary.
4631 >>> ext = Extension()
4632 >>> Extension().specs
4634 ('extnID', OBJECT IDENTIFIER),
4635 ('critical', BOOLEAN False OPTIONAL DEFAULT),
4636 ('extnValue', OCTET STRING),
4638 >>> ext["extnID"] = "1.2.3"
4639 Traceback (most recent call last):
4640 pyderasn.InvalidValueType: invalid value type, expected: <class 'pyderasn.ObjectIdentifier'>
4641 >>> ext["extnID"] = ObjectIdentifier("1.2.3")
4643 You can determine if sequence is ready to be encoded:
4648 Traceback (most recent call last):
4649 pyderasn.ObjNotReady: object is not ready: extnValue
4650 >>> ext["extnValue"] = OctetString(b"foobar")
4654 Value you want to assign, must have the same **type** as in
4655 corresponding specification, but it can have different tags,
4656 optional/default attributes -- they will be taken from specification
4659 class TBSCertificate(Sequence):
4661 ("version", Version(expl=tag_ctxc(0), default="v1")),
4664 >>> tbs = TBSCertificate()
4665 >>> tbs["version"] = Version("v2") # no need to explicitly add ``expl``
4667 Assign ``None`` to remove value from sequence.
4669 You can set values in Sequence during its initialization:
4671 >>> AlgorithmIdentifier((
4672 ("algorithm", ObjectIdentifier("1.2.3")),
4673 ("parameters", Any(Null()))
4675 AlgorithmIdentifier SEQUENCE[algorithm: OBJECT IDENTIFIER 1.2.3; parameters: ANY 0500 OPTIONAL]
4677 You can determine if value exists/set in the sequence and take its value:
4679 >>> "extnID" in ext, "extnValue" in ext, "critical" in ext
4682 OBJECT IDENTIFIER 1.2.3
4684 But pay attention that if value has default, then it won't be (not
4685 in) in the sequence (because ``DEFAULT`` must not be encoded in
4686 DER), but you can read its value:
4688 >>> "critical" in ext, ext["critical"]
4689 (False, BOOLEAN False)
4690 >>> ext["critical"] = Boolean(True)
4691 >>> "critical" in ext, ext["critical"]
4692 (True, BOOLEAN True)
4694 All defaulted values are always optional.
4696 .. _allow_default_values_ctx:
4698 DER prohibits default value encoding and will raise an error if
4699 default value is unexpectedly met during decode.
4700 If :ref:`bered <bered_ctx>` context option is set, then no error
4701 will be raised, but ``bered`` attribute set. You can disable strict
4702 defaulted values existence validation by setting
4703 ``"allow_default_values": True`` :ref:`context <ctx>` option.
4705 Two sequences are equal if they have equal specification (schema),
4706 implicit/explicit tagging and the same values.
4708 __slots__ = ("specs",)
4709 tag_default = tag_encode(form=TagFormConstructed, num=16)
4710 asn1_type_name = "SEQUENCE"
4722 super(Sequence, self).__init__(impl, expl, default, optional, _decoded)
4724 schema = getattr(self, "schema", ())
4726 schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
4729 if value is not None:
4730 if issubclass(value.__class__, Sequence):
4731 self._value = value._value
4732 elif hasattr(value, "__iter__"):
4733 for seq_key, seq_value in value:
4734 self[seq_key] = seq_value
4736 raise InvalidValueType((Sequence,))
4737 if default is not None:
4738 if not issubclass(default.__class__, Sequence):
4739 raise InvalidValueType((Sequence,))
4740 default_value = default._value
4741 default_obj = self.__class__(impl=self.tag, expl=self._expl)
4742 default_obj.specs = self.specs
4743 default_obj._value = default_value
4744 self.default = default_obj
4746 self._value = default_obj.copy()._value
4750 for name, spec in iteritems(self.specs):
4751 value = self._value.get(name)
4763 if self.expl_lenindef or self.lenindef or self.ber_encoded:
4765 return any(value.bered for value in itervalues(self._value))
4768 obj = self.__class__(schema=self.specs)
4770 obj._expl = self._expl
4771 obj.default = self.default
4772 obj.optional = self.optional
4773 obj.offset = self.offset
4774 obj.llen = self.llen
4775 obj.vlen = self.vlen
4776 obj.expl_lenindef = self.expl_lenindef
4777 obj.lenindef = self.lenindef
4778 obj.ber_encoded = self.ber_encoded
4779 obj._value = {k: v.copy() for k, v in iteritems(self._value)}
4782 def __eq__(self, their):
4783 if not isinstance(their, self.__class__):
4786 self.specs == their.specs and
4787 self.tag == their.tag and
4788 self._expl == their._expl and
4789 self._value == their._value
4800 return self.__class__(
4803 impl=self.tag if impl is None else impl,
4804 expl=self._expl if expl is None else expl,
4805 default=self.default if default is None else default,
4806 optional=self.optional if optional is None else optional,
4809 def __contains__(self, key):
4810 return key in self._value
4812 def __setitem__(self, key, value):
4813 spec = self.specs.get(key)
4815 raise ObjUnknown(key)
4817 self._value.pop(key, None)
4819 if not isinstance(value, spec.__class__):
4820 raise InvalidValueType((spec.__class__,))
4821 value = spec(value=value)
4822 if spec.default is not None and value == spec.default:
4823 self._value.pop(key, None)
4825 self._value[key] = value
4827 def __getitem__(self, key):
4828 value = self._value.get(key)
4829 if value is not None:
4831 spec = self.specs.get(key)
4833 raise ObjUnknown(key)
4834 if spec.default is not None:
4838 def _encoded_values(self):
4840 for name, spec in iteritems(self.specs):
4841 value = self._value.get(name)
4845 raise ObjNotReady(name)
4846 raws.append(value.encode())
4850 v = b"".join(self._encoded_values())
4851 return b"".join((self.tag, len_encode(len(v)), v))
4853 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
4855 t, tlen, lv = tag_strip(tlv)
4856 except DecodeError as err:
4857 raise err.__class__(
4859 klass=self.__class__,
4860 decode_path=decode_path,
4865 klass=self.__class__,
4866 decode_path=decode_path,
4869 if tag_only: # pragma: no cover
4872 ctx_bered = ctx.get("bered", False)
4874 l, llen, v = len_decode(lv)
4875 except LenIndefForm as err:
4877 raise err.__class__(
4879 klass=self.__class__,
4880 decode_path=decode_path,
4883 l, llen, v = 0, 1, lv[1:]
4885 except DecodeError as err:
4886 raise err.__class__(
4888 klass=self.__class__,
4889 decode_path=decode_path,
4893 raise NotEnoughData(
4894 "encoded length is longer than data",
4895 klass=self.__class__,
4896 decode_path=decode_path,
4900 v, tail = v[:l], v[l:]
4902 sub_offset = offset + tlen + llen
4905 ctx_allow_default_values = ctx.get("allow_default_values", False)
4906 for name, spec in iteritems(self.specs):
4907 if spec.optional and (
4908 (lenindef and v[:EOC_LEN].tobytes() == EOC) or
4912 sub_decode_path = decode_path + (name,)
4914 value, v_tail = spec.decode(
4918 decode_path=sub_decode_path,
4920 _ctx_immutable=False,
4922 except TagMismatch as err:
4923 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
4927 defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
4928 if defined is not None:
4929 defined_by, defined_spec = defined
4930 if issubclass(value.__class__, SequenceOf):
4931 for i, _value in enumerate(value):
4932 sub_sub_decode_path = sub_decode_path + (
4934 DecodePathDefBy(defined_by),
4936 defined_value, defined_tail = defined_spec.decode(
4937 memoryview(bytes(_value)),
4939 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4940 if value.expled else (value.tlen + value.llen)
4943 decode_path=sub_sub_decode_path,
4945 _ctx_immutable=False,
4947 if len(defined_tail) > 0:
4950 klass=self.__class__,
4951 decode_path=sub_sub_decode_path,
4954 _value.defined = (defined_by, defined_value)
4956 defined_value, defined_tail = defined_spec.decode(
4957 memoryview(bytes(value)),
4959 (value.tlen + value.llen + value.expl_tlen + value.expl_llen)
4960 if value.expled else (value.tlen + value.llen)
4963 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4965 _ctx_immutable=False,
4967 if len(defined_tail) > 0:
4970 klass=self.__class__,
4971 decode_path=sub_decode_path + (DecodePathDefBy(defined_by),),
4974 value.defined = (defined_by, defined_value)
4976 value_len = value.fulllen
4978 sub_offset += value_len
4980 if spec.default is not None and value == spec.default:
4981 if ctx_bered or ctx_allow_default_values:
4985 "DEFAULT value met",
4986 klass=self.__class__,
4987 decode_path=sub_decode_path,
4990 values[name] = value
4992 spec_defines = getattr(spec, "defines", ())
4993 if len(spec_defines) == 0:
4994 defines_by_path = ctx.get("defines_by_path", ())
4995 if len(defines_by_path) > 0:
4996 spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
4997 if spec_defines is not None and len(spec_defines) > 0:
4998 for rel_path, schema in spec_defines:
4999 defined = schema.get(value, None)
5000 if defined is not None:
5001 ctx.setdefault("_defines", []).append((
5002 abs_decode_path(sub_decode_path[:-1], rel_path),
5006 if v[:EOC_LEN].tobytes() != EOC:
5009 klass=self.__class__,
5010 decode_path=decode_path,
5018 klass=self.__class__,
5019 decode_path=decode_path,
5022 obj = self.__class__(
5026 default=self.default,
5027 optional=self.optional,
5028 _decoded=(offset, llen, vlen),
5031 obj.lenindef = lenindef
5032 obj.ber_encoded = ber_encoded
5036 value = pp_console_row(next(self.pps()))
5038 for name in self.specs:
5039 _value = self._value.get(name)
5042 cols.append("%s: %s" % (name, repr(_value)))
5043 return "%s[%s]" % (value, "; ".join(cols))
5045 def pps(self, decode_path=()):
5048 asn1_type_name=self.asn1_type_name,
5049 obj_name=self.__class__.__name__,
5050 decode_path=decode_path,
5051 optional=self.optional,
5052 default=self == self.default,
5053 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5054 expl=None if self._expl is None else tag_decode(self._expl),
5059 expl_offset=self.expl_offset if self.expled else None,
5060 expl_tlen=self.expl_tlen if self.expled else None,
5061 expl_llen=self.expl_llen if self.expled else None,
5062 expl_vlen=self.expl_vlen if self.expled else None,
5063 expl_lenindef=self.expl_lenindef,
5064 lenindef=self.lenindef,
5065 ber_encoded=self.ber_encoded,
5068 for name in self.specs:
5069 value = self._value.get(name)
5072 yield value.pps(decode_path=decode_path + (name,))
5073 for pp in self.pps_lenindef(decode_path):
5077 class Set(Sequence):
5078 """``SET`` structure type
5080 Its usage is identical to :py:class:`pyderasn.Sequence`.
5082 .. _allow_unordered_set_ctx:
5084 DER prohibits unordered values encoding and will raise an error
5085 during decode. If If :ref:`bered <bered_ctx>` context option is set,
5086 then no error will occure. Also you can disable strict values
5087 ordering check by setting ``"allow_unordered_set": True``
5088 :ref:`context <ctx>` option.
5091 tag_default = tag_encode(form=TagFormConstructed, num=17)
5092 asn1_type_name = "SET"
5095 raws = self._encoded_values()
5098 return b"".join((self.tag, len_encode(len(v)), v))
5100 def _specs_items(self):
5101 return iteritems(self.specs)
5103 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5105 t, tlen, lv = tag_strip(tlv)
5106 except DecodeError as err:
5107 raise err.__class__(
5109 klass=self.__class__,
5110 decode_path=decode_path,
5115 klass=self.__class__,
5116 decode_path=decode_path,
5122 ctx_bered = ctx.get("bered", False)
5124 l, llen, v = len_decode(lv)
5125 except LenIndefForm as err:
5127 raise err.__class__(
5129 klass=self.__class__,
5130 decode_path=decode_path,
5133 l, llen, v = 0, 1, lv[1:]
5135 except DecodeError as err:
5136 raise err.__class__(
5138 klass=self.__class__,
5139 decode_path=decode_path,
5143 raise NotEnoughData(
5144 "encoded length is longer than data",
5145 klass=self.__class__,
5149 v, tail = v[:l], v[l:]
5151 sub_offset = offset + tlen + llen
5154 ctx_allow_default_values = ctx.get("allow_default_values", False)
5155 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5156 value_prev = memoryview(v[:0])
5159 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5161 for name, spec in self._specs_items():
5162 sub_decode_path = decode_path + (name,)
5168 decode_path=sub_decode_path,
5171 _ctx_immutable=False,
5178 klass=self.__class__,
5179 decode_path=decode_path,
5182 value, v_tail = spec.decode(
5186 decode_path=sub_decode_path,
5188 _ctx_immutable=False,
5190 value_len = value.fulllen
5191 if value_prev.tobytes() > v[:value_len].tobytes():
5192 if ctx_bered or ctx_allow_unordered_set:
5196 "unordered " + self.asn1_type_name,
5197 klass=self.__class__,
5198 decode_path=sub_decode_path,
5201 if spec.default is None or value != spec.default:
5203 elif ctx_bered or ctx_allow_default_values:
5207 "DEFAULT value met",
5208 klass=self.__class__,
5209 decode_path=sub_decode_path,
5212 values[name] = value
5213 value_prev = v[:value_len]
5214 sub_offset += value_len
5217 obj = self.__class__(
5221 default=self.default,
5222 optional=self.optional,
5223 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5226 if v[:EOC_LEN].tobytes() != EOC:
5229 klass=self.__class__,
5230 decode_path=decode_path,
5238 "not all values are ready",
5239 klass=self.__class__,
5240 decode_path=decode_path,
5243 obj.ber_encoded = ber_encoded
5247 class SequenceOf(Obj):
5248 """``SEQUENCE OF`` sequence type
5250 For that kind of type you must specify the object it will carry on
5251 (bounds are for example here, not required)::
5253 class Ints(SequenceOf):
5258 >>> ints.append(Integer(123))
5259 >>> ints.append(Integer(234))
5261 Ints SEQUENCE OF[INTEGER 123, INTEGER 234]
5262 >>> [int(i) for i in ints]
5264 >>> ints.append(Integer(345))
5265 Traceback (most recent call last):
5266 pyderasn.BoundsError: unsatisfied bounds: 0 <= 3 <= 2
5269 >>> ints[1] = Integer(345)
5271 Ints SEQUENCE OF[INTEGER 123, INTEGER 345]
5273 Also you can initialize sequence with preinitialized values:
5275 >>> ints = Ints([Integer(123), Integer(234)])
5277 __slots__ = ("spec", "_bound_min", "_bound_max")
5278 tag_default = tag_encode(form=TagFormConstructed, num=16)
5279 asn1_type_name = "SEQUENCE OF"
5292 super(SequenceOf, self).__init__(
5300 schema = getattr(self, "schema", None)
5302 raise ValueError("schema must be specified")
5304 self._bound_min, self._bound_max = getattr(
5308 ) if bounds is None else bounds
5310 if value is not None:
5311 self._value = self._value_sanitize(value)
5312 if default is not None:
5313 default_value = self._value_sanitize(default)
5314 default_obj = self.__class__(
5319 default_obj._value = default_value
5320 self.default = default_obj
5322 self._value = default_obj.copy()._value
5324 def _value_sanitize(self, value):
5325 if issubclass(value.__class__, SequenceOf):
5326 value = value._value
5327 elif hasattr(value, "__iter__"):
5330 raise InvalidValueType((self.__class__, iter))
5331 if not self._bound_min <= len(value) <= self._bound_max:
5332 raise BoundsError(self._bound_min, len(value), self._bound_max)
5334 if not isinstance(v, self.spec.__class__):
5335 raise InvalidValueType((self.spec.__class__,))
5340 return all(v.ready for v in self._value)
5344 if self.expl_lenindef or self.lenindef or self.ber_encoded:
5346 return any(v.bered for v in self._value)
5349 obj = self.__class__(schema=self.spec)
5350 obj._bound_min = self._bound_min
5351 obj._bound_max = self._bound_max
5353 obj._expl = self._expl
5354 obj.default = self.default
5355 obj.optional = self.optional
5356 obj.offset = self.offset
5357 obj.llen = self.llen
5358 obj.vlen = self.vlen
5359 obj.expl_lenindef = self.expl_lenindef
5360 obj.lenindef = self.lenindef
5361 obj.ber_encoded = self.ber_encoded
5362 obj._value = [v.copy() for v in self._value]
5365 def __eq__(self, their):
5366 if isinstance(their, self.__class__):
5368 self.spec == their.spec and
5369 self.tag == their.tag and
5370 self._expl == their._expl and
5371 self._value == their._value
5373 if hasattr(their, "__iter__"):
5374 return self._value == list(their)
5386 return self.__class__(
5390 (self._bound_min, self._bound_max)
5391 if bounds is None else bounds
5393 impl=self.tag if impl is None else impl,
5394 expl=self._expl if expl is None else expl,
5395 default=self.default if default is None else default,
5396 optional=self.optional if optional is None else optional,
5399 def __contains__(self, key):
5400 return key in self._value
5402 def append(self, value):
5403 if not isinstance(value, self.spec.__class__):
5404 raise InvalidValueType((self.spec.__class__,))
5405 if len(self._value) + 1 > self._bound_max:
5408 len(self._value) + 1,
5411 self._value.append(value)
5414 self._assert_ready()
5415 return iter(self._value)
5418 self._assert_ready()
5419 return len(self._value)
5421 def __setitem__(self, key, value):
5422 if not isinstance(value, self.spec.__class__):
5423 raise InvalidValueType((self.spec.__class__,))
5424 self._value[key] = self.spec(value=value)
5426 def __getitem__(self, key):
5427 return self._value[key]
5429 def _encoded_values(self):
5430 return [v.encode() for v in self._value]
5433 v = b"".join(self._encoded_values())
5434 return b"".join((self.tag, len_encode(len(v)), v))
5436 def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
5438 t, tlen, lv = tag_strip(tlv)
5439 except DecodeError as err:
5440 raise err.__class__(
5442 klass=self.__class__,
5443 decode_path=decode_path,
5448 klass=self.__class__,
5449 decode_path=decode_path,
5455 ctx_bered = ctx.get("bered", False)
5457 l, llen, v = len_decode(lv)
5458 except LenIndefForm as err:
5460 raise err.__class__(
5462 klass=self.__class__,
5463 decode_path=decode_path,
5466 l, llen, v = 0, 1, lv[1:]
5468 except DecodeError as err:
5469 raise err.__class__(
5471 klass=self.__class__,
5472 decode_path=decode_path,
5476 raise NotEnoughData(
5477 "encoded length is longer than data",
5478 klass=self.__class__,
5479 decode_path=decode_path,
5483 v, tail = v[:l], v[l:]
5485 sub_offset = offset + tlen + llen
5487 ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
5488 value_prev = memoryview(v[:0])
5492 if lenindef and v[:EOC_LEN].tobytes() == EOC:
5494 sub_decode_path = decode_path + (str(len(_value)),)
5495 value, v_tail = spec.decode(
5499 decode_path=sub_decode_path,
5501 _ctx_immutable=False,
5503 value_len = value.fulllen
5505 if value_prev.tobytes() > v[:value_len].tobytes():
5506 if ctx_bered or ctx_allow_unordered_set:
5510 "unordered " + self.asn1_type_name,
5511 klass=self.__class__,
5512 decode_path=sub_decode_path,
5515 value_prev = v[:value_len]
5516 _value.append(value)
5517 sub_offset += value_len
5521 obj = self.__class__(
5524 bounds=(self._bound_min, self._bound_max),
5527 default=self.default,
5528 optional=self.optional,
5529 _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
5531 except BoundsError as err:
5534 klass=self.__class__,
5535 decode_path=decode_path,
5539 if v[:EOC_LEN].tobytes() != EOC:
5542 klass=self.__class__,
5543 decode_path=decode_path,
5548 obj.ber_encoded = ber_encoded
5553 pp_console_row(next(self.pps())),
5554 ", ".join(repr(v) for v in self._value),
5557 def pps(self, decode_path=()):
5560 asn1_type_name=self.asn1_type_name,
5561 obj_name=self.__class__.__name__,
5562 decode_path=decode_path,
5563 optional=self.optional,
5564 default=self == self.default,
5565 impl=None if self.tag == self.tag_default else tag_decode(self.tag),
5566 expl=None if self._expl is None else tag_decode(self._expl),
5571 expl_offset=self.expl_offset if self.expled else None,
5572 expl_tlen=self.expl_tlen if self.expled else None,
5573 expl_llen=self.expl_llen if self.expled else None,
5574 expl_vlen=self.expl_vlen if self.expled else None,
5575 expl_lenindef=self.expl_lenindef,
5576 lenindef=self.lenindef,
5577 ber_encoded=self.ber_encoded,
5580 for i, value in enumerate(self._value):
5581 yield value.pps(decode_path=decode_path + (str(i),))
5582 for pp in self.pps_lenindef(decode_path):
5586 class SetOf(SequenceOf):
5587 """``SET OF`` sequence type
5589 Its usage is identical to :py:class:`pyderasn.SequenceOf`.
5592 tag_default = tag_encode(form=TagFormConstructed, num=17)
5593 asn1_type_name = "SET OF"
5596 raws = self._encoded_values()
5599 return b"".join((self.tag, len_encode(len(v)), v))
5601 def _decode(self, tlv, offset, decode_path, ctx, tag_only):
5602 return super(SetOf, self)._decode(
5608 ordering_check=True,
5612 def obj_by_path(pypath): # pragma: no cover
5613 """Import object specified as string Python path
5615 Modules must be separated from classes/functions with ``:``.
5617 >>> obj_by_path("foo.bar:Baz")
5618 <class 'foo.bar.Baz'>
5619 >>> obj_by_path("foo.bar:Baz.boo")
5620 <classmethod 'foo.bar.Baz.boo'>
5622 mod, objs = pypath.rsplit(":", 1)
5623 from importlib import import_module
5624 obj = import_module(mod)
5625 for obj_name in objs.split("."):
5626 obj = getattr(obj, obj_name)
5630 def generic_decoder(): # pragma: no cover
5631 # All of this below is a big hack with self references
5632 choice = PrimitiveTypes()
5633 choice.specs["SequenceOf"] = SequenceOf(schema=choice)
5634 choice.specs["SetOf"] = SetOf(schema=choice)
5635 for i in six_xrange(31):
5636 choice.specs["SequenceOf%d" % i] = SequenceOf(
5640 choice.specs["Any"] = Any()
5642 # Class name equals to type name, to omit it from output
5643 class SEQUENCEOF(SequenceOf):
5651 with_decode_path=False,
5652 decode_path_only=(),
5654 def _pprint_pps(pps):
5656 if hasattr(pp, "_fields"):
5658 decode_path_only != () and
5659 pp.decode_path[:len(decode_path_only)] != decode_path_only
5662 if pp.asn1_type_name == Choice.asn1_type_name:
5664 pp_kwargs = pp._asdict()
5665 pp_kwargs["decode_path"] = pp.decode_path[:-1] + (">",)
5666 pp = _pp(**pp_kwargs)
5667 yield pp_console_row(
5672 with_colours=with_colours,
5673 with_decode_path=with_decode_path,
5674 decode_path_len_decrease=len(decode_path_only),
5676 for row in pp_console_blob(
5678 decode_path_len_decrease=len(decode_path_only),
5682 for row in _pprint_pps(pp):
5684 return "\n".join(_pprint_pps(obj.pps()))
5685 return SEQUENCEOF(), pprint_any
5688 def main(): # pragma: no cover
5690 parser = argparse.ArgumentParser(description="PyDERASN ASN.1 BER/DER decoder")
5691 parser.add_argument(
5695 help="Skip that number of bytes from the beginning",
5697 parser.add_argument(
5699 help="Python paths to dictionary with OIDs, comma separated",
5701 parser.add_argument(
5703 help="Python path to schema definition to use",
5705 parser.add_argument(
5706 "--defines-by-path",
5707 help="Python path to decoder's defines_by_path",
5709 parser.add_argument(
5711 action="store_true",
5712 help="Disallow BER encoding",
5714 parser.add_argument(
5715 "--print-decode-path",
5716 action="store_true",
5717 help="Print decode paths",
5719 parser.add_argument(
5720 "--decode-path-only",
5721 help="Print only specified decode path",
5723 parser.add_argument(
5725 action="store_true",
5726 help="Allow explicit tag out-of-bound",
5728 parser.add_argument(
5730 type=argparse.FileType("rb"),
5731 help="Path to DER file you want to decode",
5733 args = parser.parse_args()
5734 args.DERFile.seek(args.skip)
5735 der = memoryview(args.DERFile.read())
5736 args.DERFile.close()
5738 [obj_by_path(_path) for _path in (args.oids or "").split(",")]
5739 if args.oids else ()
5742 schema = obj_by_path(args.schema)
5743 from functools import partial
5744 pprinter = partial(pprint, big_blobs=True)
5746 schema, pprinter = generic_decoder()
5748 "bered": not args.nobered,
5749 "allow_expl_oob": args.allow_expl_oob,
5751 if args.defines_by_path is not None:
5752 ctx["defines_by_path"] = obj_by_path(args.defines_by_path)
5753 obj, tail = schema().decode(der, ctx=ctx)
5757 with_colours=True if environ.get("NO_COLOR") is None else False,
5758 with_decode_path=args.print_decode_path,
5760 () if args.decode_path_only is None else
5761 tuple(args.decode_path_only.split(":"))
5765 print("\nTrailing data: %s" % hexenc(tail))
5768 if __name__ == "__main__":