• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# coding: utf-8
2
3"""
4ASN.1 type classes for universal types. Exports the following items:
5
6 - load()
7 - Any()
8 - Asn1Value()
9 - BitString()
10 - BMPString()
11 - Boolean()
12 - CharacterString()
13 - Choice()
14 - EmbeddedPdv()
15 - Enumerated()
16 - GeneralizedTime()
17 - GeneralString()
18 - GraphicString()
19 - IA5String()
20 - InstanceOf()
21 - Integer()
22 - IntegerBitString()
23 - IntegerOctetString()
24 - Null()
25 - NumericString()
26 - ObjectDescriptor()
27 - ObjectIdentifier()
28 - OctetBitString()
29 - OctetString()
30 - PrintableString()
31 - Real()
32 - RelativeOid()
33 - Sequence()
34 - SequenceOf()
35 - Set()
36 - SetOf()
37 - TeletexString()
38 - UniversalString()
39 - UTCTime()
40 - UTF8String()
41 - VideotexString()
42 - VisibleString()
43 - VOID
44 - Void()
45
46Other type classes are defined that help compose the types listed above.
47"""
48
49from __future__ import unicode_literals, division, absolute_import, print_function
50
51from datetime import datetime, timedelta
52from fractions import Fraction
53import binascii
54import copy
55import math
56import re
57import sys
58
59from . import _teletex_codec
60from ._errors import unwrap
61from ._ordereddict import OrderedDict
62from ._types import type_name, str_cls, byte_cls, int_types, chr_cls
63from .parser import _parse, _dump_header
64from .util import int_to_bytes, int_from_bytes, timezone, extended_datetime, create_timezone, utc_with_dst
65
66if sys.version_info <= (3,):
67    from cStringIO import StringIO as BytesIO
68
69    range = xrange  # noqa
70    _PY2 = True
71
72else:
73    from io import BytesIO
74
75    _PY2 = False
76
77
78_teletex_codec.register()
79
80
81CLASS_NUM_TO_NAME_MAP = {
82    0: 'universal',
83    1: 'application',
84    2: 'context',
85    3: 'private',
86}
87
88CLASS_NAME_TO_NUM_MAP = {
89    'universal': 0,
90    'application': 1,
91    'context': 2,
92    'private': 3,
93    0: 0,
94    1: 1,
95    2: 2,
96    3: 3,
97}
98
99METHOD_NUM_TO_NAME_MAP = {
100    0: 'primitive',
101    1: 'constructed',
102}
103
104
105_OID_RE = re.compile(r'^\d+(\.\d+)*$')
106
107
108# A global tracker to ensure that _setup() is called for every class, even
109# if is has been called for a parent class. This allows different _fields
110# definitions for child classes. Without such a construct, the child classes
111# would just see the parent class attributes and would use them.
112_SETUP_CLASSES = {}
113
114
115def load(encoded_data, strict=False):
116    """
117    Loads a BER/DER-encoded byte string and construct a universal object based
118    on the tag value:
119
120     - 1: Boolean
121     - 2: Integer
122     - 3: BitString
123     - 4: OctetString
124     - 5: Null
125     - 6: ObjectIdentifier
126     - 7: ObjectDescriptor
127     - 8: InstanceOf
128     - 9: Real
129     - 10: Enumerated
130     - 11: EmbeddedPdv
131     - 12: UTF8String
132     - 13: RelativeOid
133     - 16: Sequence,
134     - 17: Set
135     - 18: NumericString
136     - 19: PrintableString
137     - 20: TeletexString
138     - 21: VideotexString
139     - 22: IA5String
140     - 23: UTCTime
141     - 24: GeneralizedTime
142     - 25: GraphicString
143     - 26: VisibleString
144     - 27: GeneralString
145     - 28: UniversalString
146     - 29: CharacterString
147     - 30: BMPString
148
149    :param encoded_data:
150        A byte string of BER or DER-encoded data
151
152    :param strict:
153        A boolean indicating if trailing data should be forbidden - if so, a
154        ValueError will be raised when trailing data exists
155
156    :raises:
157        ValueError - when strict is True and trailing data is present
158        ValueError - when the encoded value tag a tag other than listed above
159        ValueError - when the ASN.1 header length is longer than the data
160        TypeError - when encoded_data is not a byte string
161
162    :return:
163        An instance of the one of the universal classes
164    """
165
166    return Asn1Value.load(encoded_data, strict=strict)
167
168
169class Asn1Value(object):
170    """
171    The basis of all ASN.1 values
172    """
173
174    # The integer 0 for primitive, 1 for constructed
175    method = None
176
177    # An integer 0 through 3 - see CLASS_NUM_TO_NAME_MAP for value
178    class_ = None
179
180    # An integer 1 or greater indicating the tag number
181    tag = None
182
183    # An alternate tag allowed for this type - used for handling broken
184    # structures where a string value is encoded using an incorrect tag
185    _bad_tag = None
186
187    # If the value has been implicitly tagged
188    implicit = False
189
190    # If explicitly tagged, a tuple of 2-element tuples containing the
191    # class int and tag int, from innermost to outermost
192    explicit = None
193
194    # The BER/DER header bytes
195    _header = None
196
197    # Raw encoded value bytes not including class, method, tag, length header
198    contents = None
199
200    # The BER/DER trailer bytes
201    _trailer = b''
202
203    # The native python representation of the value - this is not used by
204    # some classes since they utilize _bytes or _unicode
205    _native = None
206
207    @classmethod
208    def load(cls, encoded_data, strict=False, **kwargs):
209        """
210        Loads a BER/DER-encoded byte string using the current class as the spec
211
212        :param encoded_data:
213            A byte string of BER or DER-encoded data
214
215        :param strict:
216            A boolean indicating if trailing data should be forbidden - if so, a
217            ValueError will be raised when trailing data exists
218
219        :return:
220            An instance of the current class
221        """
222
223        if not isinstance(encoded_data, byte_cls):
224            raise TypeError('encoded_data must be a byte string, not %s' % type_name(encoded_data))
225
226        spec = None
227        if cls.tag is not None:
228            spec = cls
229
230        value, _ = _parse_build(encoded_data, spec=spec, spec_params=kwargs, strict=strict)
231        return value
232
233    def __init__(self, explicit=None, implicit=None, no_explicit=False, tag_type=None, class_=None, tag=None,
234                 optional=None, default=None, contents=None, method=None):
235        """
236        The optional parameter is not used, but rather included so we don't
237        have to delete it from the parameter dictionary when passing as keyword
238        args
239
240        :param explicit:
241            An int tag number for explicit tagging, or a 2-element tuple of
242            class and tag.
243
244        :param implicit:
245            An int tag number for implicit tagging, or a 2-element tuple of
246            class and tag.
247
248        :param no_explicit:
249            If explicit tagging info should be removed from this instance.
250            Used internally to allow contructing the underlying value that
251            has been wrapped in an explicit tag.
252
253        :param tag_type:
254            None for normal values, or one of "implicit", "explicit" for tagged
255            values. Deprecated in favor of explicit and implicit params.
256
257        :param class_:
258            The class for the value - defaults to "universal" if tag_type is
259            None, otherwise defaults to "context". Valid values include:
260             - "universal"
261             - "application"
262             - "context"
263             - "private"
264            Deprecated in favor of explicit and implicit params.
265
266        :param tag:
267            The integer tag to override - usually this is used with tag_type or
268            class_. Deprecated in favor of explicit and implicit params.
269
270        :param optional:
271            Dummy parameter that allows "optional" key in spec param dicts
272
273        :param default:
274            The default value to use if the value is currently None
275
276        :param contents:
277            A byte string of the encoded contents of the value
278
279        :param method:
280            The method for the value - no default value since this is
281            normally set on a class. Valid values include:
282             - "primitive" or 0
283             - "constructed" or 1
284
285        :raises:
286            ValueError - when implicit, explicit, tag_type, class_ or tag are invalid values
287        """
288
289        try:
290            if self.__class__ not in _SETUP_CLASSES:
291                cls = self.__class__
292                # Allow explicit to be specified as a simple 2-element tuple
293                # instead of requiring the user make a nested tuple
294                if cls.explicit is not None and isinstance(cls.explicit[0], int_types):
295                    cls.explicit = (cls.explicit, )
296                if hasattr(cls, '_setup'):
297                    self._setup()
298                _SETUP_CLASSES[cls] = True
299
300            # Normalize tagging values
301            if explicit is not None:
302                if isinstance(explicit, int_types):
303                    if class_ is None:
304                        class_ = 'context'
305                    explicit = (class_, explicit)
306                # Prevent both explicit and tag_type == 'explicit'
307                if tag_type == 'explicit':
308                    tag_type = None
309                    tag = None
310
311            if implicit is not None:
312                if isinstance(implicit, int_types):
313                    if class_ is None:
314                        class_ = 'context'
315                    implicit = (class_, implicit)
316                # Prevent both implicit and tag_type == 'implicit'
317                if tag_type == 'implicit':
318                    tag_type = None
319                    tag = None
320
321            # Convert old tag_type API to explicit/implicit params
322            if tag_type is not None:
323                if class_ is None:
324                    class_ = 'context'
325                if tag_type == 'explicit':
326                    explicit = (class_, tag)
327                elif tag_type == 'implicit':
328                    implicit = (class_, tag)
329                else:
330                    raise ValueError(unwrap(
331                        '''
332                        tag_type must be one of "implicit", "explicit", not %s
333                        ''',
334                        repr(tag_type)
335                    ))
336
337            if explicit is not None:
338                # Ensure we have a tuple of 2-element tuples
339                if len(explicit) == 2 and isinstance(explicit[1], int_types):
340                    explicit = (explicit, )
341                for class_, tag in explicit:
342                    invalid_class = None
343                    if isinstance(class_, int_types):
344                        if class_ not in CLASS_NUM_TO_NAME_MAP:
345                            invalid_class = class_
346                    else:
347                        if class_ not in CLASS_NAME_TO_NUM_MAP:
348                            invalid_class = class_
349                        class_ = CLASS_NAME_TO_NUM_MAP[class_]
350                    if invalid_class is not None:
351                        raise ValueError(unwrap(
352                            '''
353                            explicit class must be one of "universal", "application",
354                            "context", "private", not %s
355                            ''',
356                            repr(invalid_class)
357                        ))
358                    if tag is not None:
359                        if not isinstance(tag, int_types):
360                            raise TypeError(unwrap(
361                                '''
362                                explicit tag must be an integer, not %s
363                                ''',
364                                type_name(tag)
365                            ))
366                    if self.explicit is None:
367                        self.explicit = ((class_, tag), )
368                    else:
369                        self.explicit = self.explicit + ((class_, tag), )
370
371            elif implicit is not None:
372                class_, tag = implicit
373                if class_ not in CLASS_NAME_TO_NUM_MAP:
374                    raise ValueError(unwrap(
375                        '''
376                        implicit class must be one of "universal", "application",
377                        "context", "private", not %s
378                        ''',
379                        repr(class_)
380                    ))
381                if tag is not None:
382                    if not isinstance(tag, int_types):
383                        raise TypeError(unwrap(
384                            '''
385                            implicit tag must be an integer, not %s
386                            ''',
387                            type_name(tag)
388                        ))
389                self.class_ = CLASS_NAME_TO_NUM_MAP[class_]
390                self.tag = tag
391                self.implicit = True
392            else:
393                if class_ is not None:
394                    if class_ not in CLASS_NAME_TO_NUM_MAP:
395                        raise ValueError(unwrap(
396                            '''
397                            class_ must be one of "universal", "application",
398                            "context", "private", not %s
399                            ''',
400                            repr(class_)
401                        ))
402                    self.class_ = CLASS_NAME_TO_NUM_MAP[class_]
403
404                if self.class_ is None:
405                    self.class_ = 0
406
407                if tag is not None:
408                    self.tag = tag
409
410            if method is not None:
411                if method not in set(["primitive", 0, "constructed", 1]):
412                    raise ValueError(unwrap(
413                        '''
414                        method must be one of "primitive" or "constructed",
415                        not %s
416                        ''',
417                        repr(method)
418                    ))
419                if method == "primitive":
420                    method = 0
421                elif method == "constructed":
422                    method = 1
423                self.method = method
424
425            if no_explicit:
426                self.explicit = None
427
428            if contents is not None:
429                self.contents = contents
430
431            elif default is not None:
432                self.set(default)
433
434        except (ValueError, TypeError) as e:
435            args = e.args[1:]
436            e.args = (e.args[0] + '\n    while constructing %s' % type_name(self),) + args
437            raise e
438
439    def __str__(self):
440        """
441        Since str is different in Python 2 and 3, this calls the appropriate
442        method, __unicode__() or __bytes__()
443
444        :return:
445            A unicode string
446        """
447
448        if _PY2:
449            return self.__bytes__()
450        else:
451            return self.__unicode__()
452
453    def __repr__(self):
454        """
455        :return:
456            A unicode string
457        """
458
459        if _PY2:
460            return '<%s %s b%s>' % (type_name(self), id(self), repr(self.dump()))
461        else:
462            return '<%s %s %s>' % (type_name(self), id(self), repr(self.dump()))
463
464    def __bytes__(self):
465        """
466        A fall-back method for print() in Python 2
467
468        :return:
469            A byte string of the output of repr()
470        """
471
472        return self.__repr__().encode('utf-8')
473
474    def __unicode__(self):
475        """
476        A fall-back method for print() in Python 3
477
478        :return:
479            A unicode string of the output of repr()
480        """
481
482        return self.__repr__()
483
484    def _new_instance(self):
485        """
486        Constructs a new copy of the current object, preserving any tagging
487
488        :return:
489            An Asn1Value object
490        """
491
492        new_obj = self.__class__()
493        new_obj.class_ = self.class_
494        new_obj.tag = self.tag
495        new_obj.implicit = self.implicit
496        new_obj.explicit = self.explicit
497        return new_obj
498
499    def __copy__(self):
500        """
501        Implements the copy.copy() interface
502
503        :return:
504            A new shallow copy of the current Asn1Value object
505        """
506
507        new_obj = self._new_instance()
508        new_obj._copy(self, copy.copy)
509        return new_obj
510
511    def __deepcopy__(self, memo):
512        """
513        Implements the copy.deepcopy() interface
514
515        :param memo:
516            A dict for memoization
517
518        :return:
519            A new deep copy of the current Asn1Value object
520        """
521
522        new_obj = self._new_instance()
523        memo[id(self)] = new_obj
524        new_obj._copy(self, copy.deepcopy)
525        return new_obj
526
527    def copy(self):
528        """
529        Copies the object, preserving any special tagging from it
530
531        :return:
532            An Asn1Value object
533        """
534
535        return copy.deepcopy(self)
536
537    def retag(self, tagging, tag=None):
538        """
539        Copies the object, applying a new tagging to it
540
541        :param tagging:
542            A dict containing the keys "explicit" and "implicit". Legacy
543            API allows a unicode string of "implicit" or "explicit".
544
545        :param tag:
546            A integer tag number. Only used when tagging is a unicode string.
547
548        :return:
549            An Asn1Value object
550        """
551
552        # This is required to preserve the old API
553        if not isinstance(tagging, dict):
554            tagging = {tagging: tag}
555        new_obj = self.__class__(explicit=tagging.get('explicit'), implicit=tagging.get('implicit'))
556        new_obj._copy(self, copy.deepcopy)
557        return new_obj
558
559    def untag(self):
560        """
561        Copies the object, removing any special tagging from it
562
563        :return:
564            An Asn1Value object
565        """
566
567        new_obj = self.__class__()
568        new_obj._copy(self, copy.deepcopy)
569        return new_obj
570
571    def _copy(self, other, copy_func):
572        """
573        Copies the contents of another Asn1Value object to itself
574
575        :param object:
576            Another instance of the same class
577
578        :param copy_func:
579            An reference of copy.copy() or copy.deepcopy() to use when copying
580            lists, dicts and objects
581        """
582
583        if self.__class__ != other.__class__:
584            raise TypeError(unwrap(
585                '''
586                Can not copy values from %s object to %s object
587                ''',
588                type_name(other),
589                type_name(self)
590            ))
591
592        self.contents = other.contents
593        self._native = copy_func(other._native)
594
595    def debug(self, nest_level=1):
596        """
597        Show the binary data and parsed data in a tree structure
598        """
599
600        prefix = '  ' * nest_level
601
602        # This interacts with Any and moves the tag, implicit, explicit, _header,
603        # contents, _footer to the parsed value so duplicate data isn't present
604        has_parsed = hasattr(self, 'parsed')
605
606        _basic_debug(prefix, self)
607        if has_parsed:
608            self.parsed.debug(nest_level + 2)
609        elif hasattr(self, 'chosen'):
610            self.chosen.debug(nest_level + 2)
611        else:
612            if _PY2 and isinstance(self.native, byte_cls):
613                print('%s    Native: b%s' % (prefix, repr(self.native)))
614            else:
615                print('%s    Native: %s' % (prefix, self.native))
616
617    def dump(self, force=False):
618        """
619        Encodes the value using DER
620
621        :param force:
622            If the encoded contents already exist, clear them and regenerate
623            to ensure they are in DER format instead of BER format
624
625        :return:
626            A byte string of the DER-encoded value
627        """
628
629        contents = self.contents
630
631        # If the length is indefinite, force the re-encoding
632        if self._header is not None and self._header[-1:] == b'\x80':
633            force = True
634
635        if self._header is None or force:
636            if isinstance(self, Constructable) and self._indefinite:
637                self.method = 0
638
639            header = _dump_header(self.class_, self.method, self.tag, self.contents)
640
641            if self.explicit is not None:
642                for class_, tag in self.explicit:
643                    header = _dump_header(class_, 1, tag, header + self.contents) + header
644
645            self._header = header
646            self._trailer = b''
647
648        return self._header + contents + self._trailer
649
650
651class ValueMap():
652    """
653    Basic functionality that allows for mapping values from ints or OIDs to
654    python unicode strings
655    """
656
657    # A dict from primitive value (int or OID) to unicode string. This needs
658    # to be defined in the source code
659    _map = None
660
661    # A dict from unicode string to int/OID. This is automatically generated
662    # from _map the first time it is needed
663    _reverse_map = None
664
665    def _setup(self):
666        """
667        Generates _reverse_map from _map
668        """
669
670        cls = self.__class__
671        if cls._map is None or cls._reverse_map is not None:
672            return
673        cls._reverse_map = {}
674        for key, value in cls._map.items():
675            cls._reverse_map[value] = key
676
677
678class Castable(object):
679    """
680    A mixin to handle converting an object between different classes that
681    represent the same encoded value, but with different rules for converting
682    to and from native Python values
683    """
684
685    def cast(self, other_class):
686        """
687        Converts the current object into an object of a different class. The
688        new class must use the ASN.1 encoding for the value.
689
690        :param other_class:
691            The class to instantiate the new object from
692
693        :return:
694            An instance of the type other_class
695        """
696
697        if other_class.tag != self.__class__.tag:
698            raise TypeError(unwrap(
699                '''
700                Can not covert a value from %s object to %s object since they
701                use different tags: %d versus %d
702                ''',
703                type_name(other_class),
704                type_name(self),
705                other_class.tag,
706                self.__class__.tag
707            ))
708
709        new_obj = other_class()
710        new_obj.class_ = self.class_
711        new_obj.implicit = self.implicit
712        new_obj.explicit = self.explicit
713        new_obj._header = self._header
714        new_obj.contents = self.contents
715        new_obj._trailer = self._trailer
716        if isinstance(self, Constructable):
717            new_obj.method = self.method
718            new_obj._indefinite = self._indefinite
719        return new_obj
720
721
722class Constructable(object):
723    """
724    A mixin to handle string types that may be constructed from chunks
725    contained within an indefinite length BER-encoded container
726    """
727
728    # Instance attribute indicating if an object was indefinite
729    # length when parsed - affects parsing and dumping
730    _indefinite = False
731
732    def _merge_chunks(self):
733        """
734        :return:
735            A concatenation of the native values of the contained chunks
736        """
737
738        if not self._indefinite:
739            return self._as_chunk()
740
741        pointer = 0
742        contents_len = len(self.contents)
743        output = None
744
745        while pointer < contents_len:
746            # We pass the current class as the spec so content semantics are preserved
747            sub_value, pointer = _parse_build(self.contents, pointer, spec=self.__class__)
748            if output is None:
749                output = sub_value._merge_chunks()
750            else:
751                output += sub_value._merge_chunks()
752
753        if output is None:
754            return self._as_chunk()
755
756        return output
757
758    def _as_chunk(self):
759        """
760        A method to return a chunk of data that can be combined for
761        constructed method values
762
763        :return:
764            A native Python value that can be added together. Examples include
765            byte strings, unicode strings or tuples.
766        """
767
768        return self.contents
769
770    def _setable_native(self):
771        """
772        Returns a native value that can be round-tripped into .set(), to
773        result in a DER encoding. This differs from .native in that .native
774        is designed for the end use, and may account for the fact that the
775        merged value is further parsed as ASN.1, such as in the case of
776        ParsableOctetString() and ParsableOctetBitString().
777
778        :return:
779            A python value that is valid to pass to .set()
780        """
781
782        return self.native
783
784    def _copy(self, other, copy_func):
785        """
786        Copies the contents of another Constructable object to itself
787
788        :param object:
789            Another instance of the same class
790
791        :param copy_func:
792            An reference of copy.copy() or copy.deepcopy() to use when copying
793            lists, dicts and objects
794        """
795
796        super(Constructable, self)._copy(other, copy_func)
797        # We really don't want to dump BER encodings, so if we see an
798        # indefinite encoding, let's re-encode it
799        if other._indefinite:
800            self.set(other._setable_native())
801
802
803class Void(Asn1Value):
804    """
805    A representation of an optional value that is not present. Has .native
806    property and .dump() method to be compatible with other value classes.
807    """
808
809    contents = b''
810
811    def __eq__(self, other):
812        """
813        :param other:
814            The other Primitive to compare to
815
816        :return:
817            A boolean
818        """
819
820        return other.__class__ == self.__class__
821
822    def __nonzero__(self):
823        return False
824
825    def __len__(self):
826        return 0
827
828    def __iter__(self):
829        return iter(())
830
831    @property
832    def native(self):
833        """
834        The native Python datatype representation of this value
835
836        :return:
837            None
838        """
839
840        return None
841
842    def dump(self, force=False):
843        """
844        Encodes the value using DER
845
846        :param force:
847            If the encoded contents already exist, clear them and regenerate
848            to ensure they are in DER format instead of BER format
849
850        :return:
851            A byte string of the DER-encoded value
852        """
853
854        return b''
855
856
857VOID = Void()
858
859
860class Any(Asn1Value):
861    """
862    A value class that can contain any value, and allows for easy parsing of
863    the underlying encoded value using a spec. This is normally contained in
864    a Structure that has an ObjectIdentifier field and _oid_pair and _oid_specs
865    defined.
866    """
867
868    # The parsed value object
869    _parsed = None
870
871    def __init__(self, value=None, **kwargs):
872        """
873        Sets the value of the object before passing to Asn1Value.__init__()
874
875        :param value:
876            An Asn1Value object that will be set as the parsed value
877        """
878
879        Asn1Value.__init__(self, **kwargs)
880
881        try:
882            if value is not None:
883                if not isinstance(value, Asn1Value):
884                    raise TypeError(unwrap(
885                        '''
886                        value must be an instance of Asn1Value, not %s
887                        ''',
888                        type_name(value)
889                    ))
890
891                self._parsed = (value, value.__class__, None)
892                self.contents = value.dump()
893
894        except (ValueError, TypeError) as e:
895            args = e.args[1:]
896            e.args = (e.args[0] + '\n    while constructing %s' % type_name(self),) + args
897            raise e
898
899    @property
900    def native(self):
901        """
902        The native Python datatype representation of this value
903
904        :return:
905            The .native value from the parsed value object
906        """
907
908        if self._parsed is None:
909            self.parse()
910
911        return self._parsed[0].native
912
913    @property
914    def parsed(self):
915        """
916        Returns the parsed object from .parse()
917
918        :return:
919            The object returned by .parse()
920        """
921
922        if self._parsed is None:
923            self.parse()
924
925        return self._parsed[0]
926
927    def parse(self, spec=None, spec_params=None):
928        """
929        Parses the contents generically, or using a spec with optional params
930
931        :param spec:
932            A class derived from Asn1Value that defines what class_ and tag the
933            value should have, and the semantics of the encoded value. The
934            return value will be of this type. If omitted, the encoded value
935            will be decoded using the standard universal tag based on the
936            encoded tag number.
937
938        :param spec_params:
939            A dict of params to pass to the spec object
940
941        :return:
942            An object of the type spec, or if not present, a child of Asn1Value
943        """
944
945        if self._parsed is None or self._parsed[1:3] != (spec, spec_params):
946            try:
947                passed_params = spec_params or {}
948                _tag_type_to_explicit_implicit(passed_params)
949                if self.explicit is not None:
950                    if 'explicit' in passed_params:
951                        passed_params['explicit'] = self.explicit + passed_params['explicit']
952                    else:
953                        passed_params['explicit'] = self.explicit
954                contents = self._header + self.contents + self._trailer
955                parsed_value, _ = _parse_build(
956                    contents,
957                    spec=spec,
958                    spec_params=passed_params
959                )
960                self._parsed = (parsed_value, spec, spec_params)
961
962                # Once we've parsed the Any value, clear any attributes from this object
963                # since they are now duplicate
964                self.tag = None
965                self.explicit = None
966                self.implicit = False
967                self._header = b''
968                self.contents = contents
969                self._trailer = b''
970
971            except (ValueError, TypeError) as e:
972                args = e.args[1:]
973                e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
974                raise e
975        return self._parsed[0]
976
977    def _copy(self, other, copy_func):
978        """
979        Copies the contents of another Any object to itself
980
981        :param object:
982            Another instance of the same class
983
984        :param copy_func:
985            An reference of copy.copy() or copy.deepcopy() to use when copying
986            lists, dicts and objects
987        """
988
989        super(Any, self)._copy(other, copy_func)
990        self._parsed = copy_func(other._parsed)
991
992    def dump(self, force=False):
993        """
994        Encodes the value using DER
995
996        :param force:
997            If the encoded contents already exist, clear them and regenerate
998            to ensure they are in DER format instead of BER format
999
1000        :return:
1001            A byte string of the DER-encoded value
1002        """
1003
1004        if self._parsed is None:
1005            self.parse()
1006
1007        return self._parsed[0].dump(force=force)
1008
1009
1010class Choice(Asn1Value):
1011    """
1012    A class to handle when a value may be one of several options
1013    """
1014
1015    # The index in _alternatives of the validated alternative
1016    _choice = None
1017
1018    # The name of the chosen alternative
1019    _name = None
1020
1021    # The Asn1Value object for the chosen alternative
1022    _parsed = None
1023
1024    # Choice overrides .contents to be a property so that the code expecting
1025    # the .contents attribute will get the .contents of the chosen alternative
1026    _contents = None
1027
1028    # A list of tuples in one of the following forms.
1029    #
1030    # Option 1, a unicode string field name and a value class
1031    #
1032    # ("name", Asn1ValueClass)
1033    #
1034    # Option 2, same as Option 1, but with a dict of class params
1035    #
1036    # ("name", Asn1ValueClass, {'explicit': 5})
1037    _alternatives = None
1038
1039    # A dict that maps tuples of (class_, tag) to an index in _alternatives
1040    _id_map = None
1041
1042    # A dict that maps alternative names to an index in _alternatives
1043    _name_map = None
1044
1045    @classmethod
1046    def load(cls, encoded_data, strict=False, **kwargs):
1047        """
1048        Loads a BER/DER-encoded byte string using the current class as the spec
1049
1050        :param encoded_data:
1051            A byte string of BER or DER encoded data
1052
1053        :param strict:
1054            A boolean indicating if trailing data should be forbidden - if so, a
1055            ValueError will be raised when trailing data exists
1056
1057        :return:
1058            A instance of the current class
1059        """
1060
1061        if not isinstance(encoded_data, byte_cls):
1062            raise TypeError('encoded_data must be a byte string, not %s' % type_name(encoded_data))
1063
1064        value, _ = _parse_build(encoded_data, spec=cls, spec_params=kwargs, strict=strict)
1065        return value
1066
1067    def _setup(self):
1068        """
1069        Generates _id_map from _alternatives to allow validating contents
1070        """
1071
1072        cls = self.__class__
1073        cls._id_map = {}
1074        cls._name_map = {}
1075        for index, info in enumerate(cls._alternatives):
1076            if len(info) < 3:
1077                info = info + ({},)
1078                cls._alternatives[index] = info
1079            id_ = _build_id_tuple(info[2], info[1])
1080            cls._id_map[id_] = index
1081            cls._name_map[info[0]] = index
1082
1083    def __init__(self, name=None, value=None, **kwargs):
1084        """
1085        Checks to ensure implicit tagging is not being used since it is
1086        incompatible with Choice, then forwards on to Asn1Value.__init__()
1087
1088        :param name:
1089            The name of the alternative to be set - used with value.
1090            Alternatively this may be a dict with a single key being the name
1091            and the value being the value, or a two-element tuple of the name
1092            and the value.
1093
1094        :param value:
1095            The alternative value to set - used with name
1096
1097        :raises:
1098            ValueError - when implicit param is passed (or legacy tag_type param is "implicit")
1099        """
1100
1101        _tag_type_to_explicit_implicit(kwargs)
1102
1103        Asn1Value.__init__(self, **kwargs)
1104
1105        try:
1106            if kwargs.get('implicit') is not None:
1107                raise ValueError(unwrap(
1108                    '''
1109                    The Choice type can not be implicitly tagged even if in an
1110                    implicit module - due to its nature any tagging must be
1111                    explicit
1112                    '''
1113                ))
1114
1115            if name is not None:
1116                if isinstance(name, dict):
1117                    if len(name) != 1:
1118                        raise ValueError(unwrap(
1119                            '''
1120                            When passing a dict as the "name" argument to %s,
1121                            it must have a single key/value - however %d were
1122                            present
1123                            ''',
1124                            type_name(self),
1125                            len(name)
1126                        ))
1127                    name, value = list(name.items())[0]
1128
1129                if isinstance(name, tuple):
1130                    if len(name) != 2:
1131                        raise ValueError(unwrap(
1132                            '''
1133                            When passing a tuple as the "name" argument to %s,
1134                            it must have two elements, the name and value -
1135                            however %d were present
1136                            ''',
1137                            type_name(self),
1138                            len(name)
1139                        ))
1140                    value = name[1]
1141                    name = name[0]
1142
1143                if name not in self._name_map:
1144                    raise ValueError(unwrap(
1145                        '''
1146                        The name specified, "%s", is not a valid alternative
1147                        for %s
1148                        ''',
1149                        name,
1150                        type_name(self)
1151                    ))
1152
1153                self._choice = self._name_map[name]
1154                _, spec, params = self._alternatives[self._choice]
1155
1156                if not isinstance(value, spec):
1157                    value = spec(value, **params)
1158                else:
1159                    value = _fix_tagging(value, params)
1160                self._parsed = value
1161
1162        except (ValueError, TypeError) as e:
1163            args = e.args[1:]
1164            e.args = (e.args[0] + '\n    while constructing %s' % type_name(self),) + args
1165            raise e
1166
1167    @property
1168    def contents(self):
1169        """
1170        :return:
1171            A byte string of the DER-encoded contents of the chosen alternative
1172        """
1173
1174        if self._parsed is not None:
1175            return self._parsed.contents
1176
1177        return self._contents
1178
1179    @contents.setter
1180    def contents(self, value):
1181        """
1182        :param value:
1183            A byte string of the DER-encoded contents of the chosen alternative
1184        """
1185
1186        self._contents = value
1187
1188    @property
1189    def name(self):
1190        """
1191        :return:
1192            A unicode string of the field name of the chosen alternative
1193        """
1194        if not self._name:
1195            self._name = self._alternatives[self._choice][0]
1196        return self._name
1197
1198    def parse(self):
1199        """
1200        Parses the detected alternative
1201
1202        :return:
1203            An Asn1Value object of the chosen alternative
1204        """
1205
1206        if self._parsed is None:
1207            try:
1208                _, spec, params = self._alternatives[self._choice]
1209                self._parsed, _ = _parse_build(self._contents, spec=spec, spec_params=params)
1210            except (ValueError, TypeError) as e:
1211                args = e.args[1:]
1212                e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
1213                raise e
1214        return self._parsed
1215
1216    @property
1217    def chosen(self):
1218        """
1219        :return:
1220            An Asn1Value object of the chosen alternative
1221        """
1222
1223        return self.parse()
1224
1225    @property
1226    def native(self):
1227        """
1228        The native Python datatype representation of this value
1229
1230        :return:
1231            The .native value from the contained value object
1232        """
1233
1234        return self.chosen.native
1235
1236    def validate(self, class_, tag, contents):
1237        """
1238        Ensures that the class and tag specified exist as an alternative
1239
1240        :param class_:
1241            The integer class_ from the encoded value header
1242
1243        :param tag:
1244            The integer tag from the encoded value header
1245
1246        :param contents:
1247            A byte string of the contents of the value - used when the object
1248            is explicitly tagged
1249
1250        :raises:
1251            ValueError - when value is not a valid alternative
1252        """
1253
1254        id_ = (class_, tag)
1255
1256        if self.explicit is not None:
1257            if self.explicit[-1] != id_:
1258                raise ValueError(unwrap(
1259                    '''
1260                    %s was explicitly tagged, but the value provided does not
1261                    match the class and tag
1262                    ''',
1263                    type_name(self)
1264                ))
1265
1266            ((class_, _, tag, _, _, _), _) = _parse(contents, len(contents))
1267            id_ = (class_, tag)
1268
1269        if id_ in self._id_map:
1270            self._choice = self._id_map[id_]
1271            return
1272
1273        # This means the Choice was implicitly tagged
1274        if self.class_ is not None and self.tag is not None:
1275            if len(self._alternatives) > 1:
1276                raise ValueError(unwrap(
1277                    '''
1278                    %s was implicitly tagged, but more than one alternative
1279                    exists
1280                    ''',
1281                    type_name(self)
1282                ))
1283            if id_ == (self.class_, self.tag):
1284                self._choice = 0
1285                return
1286
1287        asn1 = self._format_class_tag(class_, tag)
1288        asn1s = [self._format_class_tag(pair[0], pair[1]) for pair in self._id_map]
1289
1290        raise ValueError(unwrap(
1291            '''
1292            Value %s did not match the class and tag of any of the alternatives
1293            in %s: %s
1294            ''',
1295            asn1,
1296            type_name(self),
1297            ', '.join(asn1s)
1298        ))
1299
1300    def _format_class_tag(self, class_, tag):
1301        """
1302        :return:
1303            A unicode string of a human-friendly representation of the class and tag
1304        """
1305
1306        return '[%s %s]' % (CLASS_NUM_TO_NAME_MAP[class_].upper(), tag)
1307
1308    def _copy(self, other, copy_func):
1309        """
1310        Copies the contents of another Choice object to itself
1311
1312        :param object:
1313            Another instance of the same class
1314
1315        :param copy_func:
1316            An reference of copy.copy() or copy.deepcopy() to use when copying
1317            lists, dicts and objects
1318        """
1319
1320        super(Choice, self)._copy(other, copy_func)
1321        self._choice = other._choice
1322        self._name = other._name
1323        self._parsed = copy_func(other._parsed)
1324
1325    def dump(self, force=False):
1326        """
1327        Encodes the value using DER
1328
1329        :param force:
1330            If the encoded contents already exist, clear them and regenerate
1331            to ensure they are in DER format instead of BER format
1332
1333        :return:
1334            A byte string of the DER-encoded value
1335        """
1336
1337        # If the length is indefinite, force the re-encoding
1338        if self._header is not None and self._header[-1:] == b'\x80':
1339            force = True
1340
1341        self._contents = self.chosen.dump(force=force)
1342        if self._header is None or force:
1343            self._header = b''
1344            if self.explicit is not None:
1345                for class_, tag in self.explicit:
1346                    self._header = _dump_header(class_, 1, tag, self._header + self._contents) + self._header
1347        return self._header + self._contents
1348
1349
1350class Concat(object):
1351    """
1352    A class that contains two or more encoded child values concatentated
1353    together. THIS IS NOT PART OF THE ASN.1 SPECIFICATION! This exists to handle
1354    the x509.TrustedCertificate() class for OpenSSL certificates containing
1355    extra information.
1356    """
1357
1358    # A list of the specs of the concatenated values
1359    _child_specs = None
1360
1361    _children = None
1362
1363    @classmethod
1364    def load(cls, encoded_data, strict=False):
1365        """
1366        Loads a BER/DER-encoded byte string using the current class as the spec
1367
1368        :param encoded_data:
1369            A byte string of BER or DER encoded data
1370
1371        :param strict:
1372            A boolean indicating if trailing data should be forbidden - if so, a
1373            ValueError will be raised when trailing data exists
1374
1375        :return:
1376            A Concat object
1377        """
1378
1379        return cls(contents=encoded_data, strict=strict)
1380
1381    def __init__(self, value=None, contents=None, strict=False):
1382        """
1383        :param value:
1384            A native Python datatype to initialize the object value with
1385
1386        :param contents:
1387            A byte string of the encoded contents of the value
1388
1389        :param strict:
1390            A boolean indicating if trailing data should be forbidden - if so, a
1391            ValueError will be raised when trailing data exists in contents
1392
1393        :raises:
1394            ValueError - when an error occurs with one of the children
1395            TypeError - when an error occurs with one of the children
1396        """
1397
1398        if contents is not None:
1399            try:
1400                contents_len = len(contents)
1401                self._children = []
1402
1403                offset = 0
1404                for spec in self._child_specs:
1405                    if offset < contents_len:
1406                        child_value, offset = _parse_build(contents, pointer=offset, spec=spec)
1407                    else:
1408                        child_value = spec()
1409                    self._children.append(child_value)
1410
1411                if strict and offset != contents_len:
1412                    extra_bytes = contents_len - offset
1413                    raise ValueError('Extra data - %d bytes of trailing data were provided' % extra_bytes)
1414
1415            except (ValueError, TypeError) as e:
1416                args = e.args[1:]
1417                e.args = (e.args[0] + '\n    while constructing %s' % type_name(self),) + args
1418                raise e
1419
1420        if value is not None:
1421            if self._children is None:
1422                self._children = [None] * len(self._child_specs)
1423            for index, data in enumerate(value):
1424                self.__setitem__(index, data)
1425
1426    def __str__(self):
1427        """
1428        Since str is different in Python 2 and 3, this calls the appropriate
1429        method, __unicode__() or __bytes__()
1430
1431        :return:
1432            A unicode string
1433        """
1434
1435        if _PY2:
1436            return self.__bytes__()
1437        else:
1438            return self.__unicode__()
1439
1440    def __bytes__(self):
1441        """
1442        A byte string of the DER-encoded contents
1443        """
1444
1445        return self.dump()
1446
1447    def __unicode__(self):
1448        """
1449        :return:
1450            A unicode string
1451        """
1452
1453        return repr(self)
1454
1455    def __repr__(self):
1456        """
1457        :return:
1458            A unicode string
1459        """
1460
1461        return '<%s %s %s>' % (type_name(self), id(self), repr(self.dump()))
1462
1463    def __copy__(self):
1464        """
1465        Implements the copy.copy() interface
1466
1467        :return:
1468            A new shallow copy of the Concat object
1469        """
1470
1471        new_obj = self.__class__()
1472        new_obj._copy(self, copy.copy)
1473        return new_obj
1474
1475    def __deepcopy__(self, memo):
1476        """
1477        Implements the copy.deepcopy() interface
1478
1479        :param memo:
1480            A dict for memoization
1481
1482        :return:
1483            A new deep copy of the Concat object and all child objects
1484        """
1485
1486        new_obj = self.__class__()
1487        memo[id(self)] = new_obj
1488        new_obj._copy(self, copy.deepcopy)
1489        return new_obj
1490
1491    def copy(self):
1492        """
1493        Copies the object
1494
1495        :return:
1496            A Concat object
1497        """
1498
1499        return copy.deepcopy(self)
1500
1501    def _copy(self, other, copy_func):
1502        """
1503        Copies the contents of another Concat object to itself
1504
1505        :param object:
1506            Another instance of the same class
1507
1508        :param copy_func:
1509            An reference of copy.copy() or copy.deepcopy() to use when copying
1510            lists, dicts and objects
1511        """
1512
1513        if self.__class__ != other.__class__:
1514            raise TypeError(unwrap(
1515                '''
1516                Can not copy values from %s object to %s object
1517                ''',
1518                type_name(other),
1519                type_name(self)
1520            ))
1521
1522        self._children = copy_func(other._children)
1523
1524    def debug(self, nest_level=1):
1525        """
1526        Show the binary data and parsed data in a tree structure
1527        """
1528
1529        prefix = '  ' * nest_level
1530        print('%s%s Object #%s' % (prefix, type_name(self), id(self)))
1531        print('%s  Children:' % (prefix,))
1532        for child in self._children:
1533            child.debug(nest_level + 2)
1534
1535    def dump(self, force=False):
1536        """
1537        Encodes the value using DER
1538
1539        :param force:
1540            If the encoded contents already exist, clear them and regenerate
1541            to ensure they are in DER format instead of BER format
1542
1543        :return:
1544            A byte string of the DER-encoded value
1545        """
1546
1547        contents = b''
1548        for child in self._children:
1549            contents += child.dump(force=force)
1550        return contents
1551
1552    @property
1553    def contents(self):
1554        """
1555        :return:
1556            A byte string of the DER-encoded contents of the children
1557        """
1558
1559        return self.dump()
1560
1561    def __len__(self):
1562        """
1563        :return:
1564            Integer
1565        """
1566
1567        return len(self._children)
1568
1569    def __getitem__(self, key):
1570        """
1571        Allows accessing children by index
1572
1573        :param key:
1574            An integer of the child index
1575
1576        :raises:
1577            KeyError - when an index is invalid
1578
1579        :return:
1580            The Asn1Value object of the child specified
1581        """
1582
1583        if key > len(self._child_specs) - 1 or key < 0:
1584            raise KeyError(unwrap(
1585                '''
1586                No child is definition for position %d of %s
1587                ''',
1588                key,
1589                type_name(self)
1590            ))
1591
1592        return self._children[key]
1593
1594    def __setitem__(self, key, value):
1595        """
1596        Allows settings children by index
1597
1598        :param key:
1599            An integer of the child index
1600
1601        :param value:
1602            An Asn1Value object to set the child to
1603
1604        :raises:
1605            KeyError - when an index is invalid
1606            ValueError - when the value is not an instance of Asn1Value
1607        """
1608
1609        if key > len(self._child_specs) - 1 or key < 0:
1610            raise KeyError(unwrap(
1611                '''
1612                No child is defined for position %d of %s
1613                ''',
1614                key,
1615                type_name(self)
1616            ))
1617
1618        if not isinstance(value, Asn1Value):
1619            raise ValueError(unwrap(
1620                '''
1621                Value for child %s of %s is not an instance of
1622                asn1crypto.core.Asn1Value
1623                ''',
1624                key,
1625                type_name(self)
1626            ))
1627
1628        self._children[key] = value
1629
1630    def __iter__(self):
1631        """
1632        :return:
1633            An iterator of child values
1634        """
1635
1636        return iter(self._children)
1637
1638
1639class Primitive(Asn1Value):
1640    """
1641    Sets the class_ and method attributes for primitive, universal values
1642    """
1643
1644    class_ = 0
1645
1646    method = 0
1647
1648    def __init__(self, value=None, default=None, contents=None, **kwargs):
1649        """
1650        Sets the value of the object before passing to Asn1Value.__init__()
1651
1652        :param value:
1653            A native Python datatype to initialize the object value with
1654
1655        :param default:
1656            The default value if no value is specified
1657
1658        :param contents:
1659            A byte string of the encoded contents of the value
1660        """
1661
1662        Asn1Value.__init__(self, **kwargs)
1663
1664        try:
1665            if contents is not None:
1666                self.contents = contents
1667
1668            elif value is not None:
1669                self.set(value)
1670
1671            elif default is not None:
1672                self.set(default)
1673
1674        except (ValueError, TypeError) as e:
1675            args = e.args[1:]
1676            e.args = (e.args[0] + '\n    while constructing %s' % type_name(self),) + args
1677            raise e
1678
1679    def set(self, value):
1680        """
1681        Sets the value of the object
1682
1683        :param value:
1684            A byte string
1685        """
1686
1687        if not isinstance(value, byte_cls):
1688            raise TypeError(unwrap(
1689                '''
1690                %s value must be a byte string, not %s
1691                ''',
1692                type_name(self),
1693                type_name(value)
1694            ))
1695
1696        self._native = value
1697        self.contents = value
1698        self._header = None
1699        if self._trailer != b'':
1700            self._trailer = b''
1701
1702    def dump(self, force=False):
1703        """
1704        Encodes the value using DER
1705
1706        :param force:
1707            If the encoded contents already exist, clear them and regenerate
1708            to ensure they are in DER format instead of BER format
1709
1710        :return:
1711            A byte string of the DER-encoded value
1712        """
1713
1714        # If the length is indefinite, force the re-encoding
1715        if self._header is not None and self._header[-1:] == b'\x80':
1716            force = True
1717
1718        if force:
1719            native = self.native
1720            self.contents = None
1721            self.set(native)
1722
1723        return Asn1Value.dump(self)
1724
1725    def __ne__(self, other):
1726        return not self == other
1727
1728    def __eq__(self, other):
1729        """
1730        :param other:
1731            The other Primitive to compare to
1732
1733        :return:
1734            A boolean
1735        """
1736
1737        if not isinstance(other, Primitive):
1738            return False
1739
1740        if self.contents != other.contents:
1741            return False
1742
1743        # We compare class tag numbers since object tag numbers could be
1744        # different due to implicit or explicit tagging
1745        if self.__class__.tag != other.__class__.tag:
1746            return False
1747
1748        if self.__class__ == other.__class__ and self.contents == other.contents:
1749            return True
1750
1751        # If the objects share a common base class that is not too low-level
1752        # then we can compare the contents
1753        self_bases = (set(self.__class__.__bases__) | set([self.__class__])) - set([Asn1Value, Primitive, ValueMap])
1754        other_bases = (set(other.__class__.__bases__) | set([other.__class__])) - set([Asn1Value, Primitive, ValueMap])
1755        if self_bases | other_bases:
1756            return self.contents == other.contents
1757
1758        # When tagging is going on, do the extra work of constructing new
1759        # objects to see if the dumped representation are the same
1760        if self.implicit or self.explicit or other.implicit or other.explicit:
1761            return self.untag().dump() == other.untag().dump()
1762
1763        return self.dump() == other.dump()
1764
1765
1766class AbstractString(Constructable, Primitive):
1767    """
1768    A base class for all strings that have a known encoding. In general, we do
1769    not worry ourselves with confirming that the decoded values match a specific
1770    set of characters, only that they are decoded into a Python unicode string
1771    """
1772
1773    # The Python encoding name to use when decoding or encoded the contents
1774    _encoding = 'latin1'
1775
1776    # Instance attribute of (possibly-merged) unicode string
1777    _unicode = None
1778
1779    def set(self, value):
1780        """
1781        Sets the value of the string
1782
1783        :param value:
1784            A unicode string
1785        """
1786
1787        if not isinstance(value, str_cls):
1788            raise TypeError(unwrap(
1789                '''
1790                %s value must be a unicode string, not %s
1791                ''',
1792                type_name(self),
1793                type_name(value)
1794            ))
1795
1796        self._unicode = value
1797        self.contents = value.encode(self._encoding)
1798        self._header = None
1799        if self._indefinite:
1800            self._indefinite = False
1801            self.method = 0
1802        if self._trailer != b'':
1803            self._trailer = b''
1804
1805    def __unicode__(self):
1806        """
1807        :return:
1808            A unicode string
1809        """
1810
1811        if self.contents is None:
1812            return ''
1813        if self._unicode is None:
1814            self._unicode = self._merge_chunks().decode(self._encoding)
1815        return self._unicode
1816
1817    def _copy(self, other, copy_func):
1818        """
1819        Copies the contents of another AbstractString object to itself
1820
1821        :param object:
1822            Another instance of the same class
1823
1824        :param copy_func:
1825            An reference of copy.copy() or copy.deepcopy() to use when copying
1826            lists, dicts and objects
1827        """
1828
1829        super(AbstractString, self)._copy(other, copy_func)
1830        self._unicode = other._unicode
1831
1832    @property
1833    def native(self):
1834        """
1835        The native Python datatype representation of this value
1836
1837        :return:
1838            A unicode string or None
1839        """
1840
1841        if self.contents is None:
1842            return None
1843
1844        return self.__unicode__()
1845
1846
1847class Boolean(Primitive):
1848    """
1849    Represents a boolean in both ASN.1 and Python
1850    """
1851
1852    tag = 1
1853
1854    def set(self, value):
1855        """
1856        Sets the value of the object
1857
1858        :param value:
1859            True, False or another value that works with bool()
1860        """
1861
1862        self._native = bool(value)
1863        self.contents = b'\x00' if not value else b'\xff'
1864        self._header = None
1865        if self._trailer != b'':
1866            self._trailer = b''
1867
1868    # Python 2
1869    def __nonzero__(self):
1870        """
1871        :return:
1872            True or False
1873        """
1874        return self.__bool__()
1875
1876    def __bool__(self):
1877        """
1878        :return:
1879            True or False
1880        """
1881        return self.contents != b'\x00'
1882
1883    @property
1884    def native(self):
1885        """
1886        The native Python datatype representation of this value
1887
1888        :return:
1889            True, False or None
1890        """
1891
1892        if self.contents is None:
1893            return None
1894
1895        if self._native is None:
1896            self._native = self.__bool__()
1897        return self._native
1898
1899
1900class Integer(Primitive, ValueMap):
1901    """
1902    Represents an integer in both ASN.1 and Python
1903    """
1904
1905    tag = 2
1906
1907    def set(self, value):
1908        """
1909        Sets the value of the object
1910
1911        :param value:
1912            An integer, or a unicode string if _map is set
1913
1914        :raises:
1915            ValueError - when an invalid value is passed
1916        """
1917
1918        if isinstance(value, str_cls):
1919            if self._map is None:
1920                raise ValueError(unwrap(
1921                    '''
1922                    %s value is a unicode string, but no _map provided
1923                    ''',
1924                    type_name(self)
1925                ))
1926
1927            if value not in self._reverse_map:
1928                raise ValueError(unwrap(
1929                    '''
1930                    %s value, %s, is not present in the _map
1931                    ''',
1932                    type_name(self),
1933                    value
1934                ))
1935
1936            value = self._reverse_map[value]
1937
1938        elif not isinstance(value, int_types):
1939            raise TypeError(unwrap(
1940                '''
1941                %s value must be an integer or unicode string when a name_map
1942                is provided, not %s
1943                ''',
1944                type_name(self),
1945                type_name(value)
1946            ))
1947
1948        self._native = self._map[value] if self._map and value in self._map else value
1949
1950        self.contents = int_to_bytes(value, signed=True)
1951        self._header = None
1952        if self._trailer != b'':
1953            self._trailer = b''
1954
1955    def __int__(self):
1956        """
1957        :return:
1958            An integer
1959        """
1960        return int_from_bytes(self.contents, signed=True)
1961
1962    @property
1963    def native(self):
1964        """
1965        The native Python datatype representation of this value
1966
1967        :return:
1968            An integer or None
1969        """
1970
1971        if self.contents is None:
1972            return None
1973
1974        if self._native is None:
1975            self._native = self.__int__()
1976            if self._map is not None and self._native in self._map:
1977                self._native = self._map[self._native]
1978        return self._native
1979
1980
1981class _IntegerBitString(object):
1982    """
1983    A mixin for IntegerBitString and BitString to parse the contents as an integer.
1984    """
1985
1986    # Tuple of 1s and 0s; set through native
1987    _unused_bits = ()
1988
1989    def _as_chunk(self):
1990        """
1991        Parse the contents of a primitive BitString encoding as an integer value.
1992        Allows reconstructing indefinite length values.
1993
1994        :raises:
1995            ValueError - when an invalid value is passed
1996
1997        :return:
1998            A list with one tuple (value, bits, unused_bits) where value is an integer
1999            with the value of the BitString, bits is the bit count of value and
2000            unused_bits is a tuple of 1s and 0s.
2001        """
2002
2003        if self._indefinite:
2004            # return an empty chunk, for cases like \x23\x80\x00\x00
2005            return []
2006
2007        unused_bits_len = ord(self.contents[0]) if _PY2 else self.contents[0]
2008        value = int_from_bytes(self.contents[1:])
2009        bits = (len(self.contents) - 1) * 8
2010
2011        if not unused_bits_len:
2012            return [(value, bits, ())]
2013
2014        if len(self.contents) == 1:
2015            # Disallowed by X.690 §8.6.2.3
2016            raise ValueError('Empty bit string has {0} unused bits'.format(unused_bits_len))
2017
2018        if unused_bits_len > 7:
2019            # Disallowed by X.690 §8.6.2.2
2020            raise ValueError('Bit string has {0} unused bits'.format(unused_bits_len))
2021
2022        unused_bits = _int_to_bit_tuple(value & ((1 << unused_bits_len) - 1), unused_bits_len)
2023        value >>= unused_bits_len
2024        bits -= unused_bits_len
2025
2026        return [(value, bits, unused_bits)]
2027
2028    def _chunks_to_int(self):
2029        """
2030        Combines the chunks into a single value.
2031
2032        :raises:
2033            ValueError - when an invalid value is passed
2034
2035        :return:
2036            A tuple (value, bits, unused_bits) where value is an integer with the
2037            value of the BitString, bits is the bit count of value and unused_bits
2038            is a tuple of 1s and 0s.
2039        """
2040
2041        if not self._indefinite:
2042            # Fast path
2043            return self._as_chunk()[0]
2044
2045        value = 0
2046        total_bits = 0
2047        unused_bits = ()
2048
2049        # X.690 §8.6.3 allows empty indefinite encodings
2050        for chunk, bits, unused_bits in self._merge_chunks():
2051            if total_bits & 7:
2052                # Disallowed by X.690 §8.6.4
2053                raise ValueError('Only last chunk in a bit string may have unused bits')
2054            total_bits += bits
2055            value = (value << bits) | chunk
2056
2057        return value, total_bits, unused_bits
2058
2059    def _copy(self, other, copy_func):
2060        """
2061        Copies the contents of another _IntegerBitString object to itself
2062
2063        :param object:
2064            Another instance of the same class
2065
2066        :param copy_func:
2067            An reference of copy.copy() or copy.deepcopy() to use when copying
2068            lists, dicts and objects
2069        """
2070
2071        super(_IntegerBitString, self)._copy(other, copy_func)
2072        self._unused_bits = other._unused_bits
2073
2074    @property
2075    def unused_bits(self):
2076        """
2077        The unused bits of the bit string encoding.
2078
2079        :return:
2080            A tuple of 1s and 0s
2081        """
2082
2083        # call native to set _unused_bits
2084        self.native
2085
2086        return self._unused_bits
2087
2088
2089class BitString(_IntegerBitString, Constructable, Castable, Primitive, ValueMap):
2090    """
2091    Represents a bit string from ASN.1 as a Python tuple of 1s and 0s
2092    """
2093
2094    tag = 3
2095
2096    _size = None
2097
2098    def _setup(self):
2099        """
2100        Generates _reverse_map from _map
2101        """
2102
2103        ValueMap._setup(self)
2104
2105        cls = self.__class__
2106        if cls._map is not None:
2107            cls._size = max(self._map.keys()) + 1
2108
2109    def set(self, value):
2110        """
2111        Sets the value of the object
2112
2113        :param value:
2114            An integer or a tuple of integers 0 and 1
2115
2116        :raises:
2117            ValueError - when an invalid value is passed
2118        """
2119
2120        if isinstance(value, set):
2121            if self._map is None:
2122                raise ValueError(unwrap(
2123                    '''
2124                    %s._map has not been defined
2125                    ''',
2126                    type_name(self)
2127                ))
2128
2129            bits = [0] * self._size
2130            self._native = value
2131            for index in range(0, self._size):
2132                key = self._map.get(index)
2133                if key is None:
2134                    continue
2135                if key in value:
2136                    bits[index] = 1
2137
2138            value = ''.join(map(str_cls, bits))
2139
2140        elif value.__class__ == tuple:
2141            if self._map is None:
2142                self._native = value
2143            else:
2144                self._native = set()
2145                for index, bit in enumerate(value):
2146                    if bit:
2147                        name = self._map.get(index, index)
2148                        self._native.add(name)
2149            value = ''.join(map(str_cls, value))
2150
2151        else:
2152            raise TypeError(unwrap(
2153                '''
2154                %s value must be a tuple of ones and zeros or a set of unicode
2155                strings, not %s
2156                ''',
2157                type_name(self),
2158                type_name(value)
2159            ))
2160
2161        if self._map is not None:
2162            if len(value) > self._size:
2163                raise ValueError(unwrap(
2164                    '''
2165                    %s value must be at most %s bits long, specified was %s long
2166                    ''',
2167                    type_name(self),
2168                    self._size,
2169                    len(value)
2170                ))
2171            # A NamedBitList must have trailing zero bit truncated. See
2172            # https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
2173            # section 11.2,
2174            # https://tools.ietf.org/html/rfc5280#page-134 and
2175            # https://www.ietf.org/mail-archive/web/pkix/current/msg10443.html
2176            value = value.rstrip('0')
2177        size = len(value)
2178
2179        size_mod = size % 8
2180        extra_bits = 0
2181        if size_mod != 0:
2182            extra_bits = 8 - size_mod
2183            value += '0' * extra_bits
2184
2185        size_in_bytes = int(math.ceil(size / 8))
2186
2187        if extra_bits:
2188            extra_bits_byte = int_to_bytes(extra_bits)
2189        else:
2190            extra_bits_byte = b'\x00'
2191
2192        if value == '':
2193            value_bytes = b''
2194        else:
2195            value_bytes = int_to_bytes(int(value, 2))
2196        if len(value_bytes) != size_in_bytes:
2197            value_bytes = (b'\x00' * (size_in_bytes - len(value_bytes))) + value_bytes
2198
2199        self.contents = extra_bits_byte + value_bytes
2200        self._unused_bits = (0,) * extra_bits
2201        self._header = None
2202        if self._indefinite:
2203            self._indefinite = False
2204            self.method = 0
2205        if self._trailer != b'':
2206            self._trailer = b''
2207
2208    def __getitem__(self, key):
2209        """
2210        Retrieves a boolean version of one of the bits based on a name from the
2211        _map
2212
2213        :param key:
2214            The unicode string of one of the bit names
2215
2216        :raises:
2217            ValueError - when _map is not set or the key name is invalid
2218
2219        :return:
2220            A boolean if the bit is set
2221        """
2222
2223        is_int = isinstance(key, int_types)
2224        if not is_int:
2225            if not isinstance(self._map, dict):
2226                raise ValueError(unwrap(
2227                    '''
2228                    %s._map has not been defined
2229                    ''',
2230                    type_name(self)
2231                ))
2232
2233            if key not in self._reverse_map:
2234                raise ValueError(unwrap(
2235                    '''
2236                    %s._map does not contain an entry for "%s"
2237                    ''',
2238                    type_name(self),
2239                    key
2240                ))
2241
2242        if self._native is None:
2243            self.native
2244
2245        if self._map is None:
2246            if len(self._native) >= key + 1:
2247                return bool(self._native[key])
2248            return False
2249
2250        if is_int:
2251            key = self._map.get(key, key)
2252
2253        return key in self._native
2254
2255    def __setitem__(self, key, value):
2256        """
2257        Sets one of the bits based on a name from the _map
2258
2259        :param key:
2260            The unicode string of one of the bit names
2261
2262        :param value:
2263            A boolean value
2264
2265        :raises:
2266            ValueError - when _map is not set or the key name is invalid
2267        """
2268
2269        is_int = isinstance(key, int_types)
2270        if not is_int:
2271            if self._map is None:
2272                raise ValueError(unwrap(
2273                    '''
2274                    %s._map has not been defined
2275                    ''',
2276                    type_name(self)
2277                ))
2278
2279            if key not in self._reverse_map:
2280                raise ValueError(unwrap(
2281                    '''
2282                    %s._map does not contain an entry for "%s"
2283                    ''',
2284                    type_name(self),
2285                    key
2286                ))
2287
2288        if self._native is None:
2289            self.native
2290
2291        if self._map is None:
2292            new_native = list(self._native)
2293            max_key = len(new_native) - 1
2294            if key > max_key:
2295                new_native.extend([0] * (key - max_key))
2296            new_native[key] = 1 if value else 0
2297            self._native = tuple(new_native)
2298
2299        else:
2300            if is_int:
2301                key = self._map.get(key, key)
2302
2303            if value:
2304                if key not in self._native:
2305                    self._native.add(key)
2306            else:
2307                if key in self._native:
2308                    self._native.remove(key)
2309
2310        self.set(self._native)
2311
2312    @property
2313    def native(self):
2314        """
2315        The native Python datatype representation of this value
2316
2317        :return:
2318            If a _map is set, a set of names, or if no _map is set, a tuple of
2319            integers 1 and 0. None if no value.
2320        """
2321
2322        # For BitString we default the value to be all zeros
2323        if self.contents is None:
2324            if self._map is None:
2325                self.set(())
2326            else:
2327                self.set(set())
2328
2329        if self._native is None:
2330            int_value, bit_count, self._unused_bits = self._chunks_to_int()
2331            bits = _int_to_bit_tuple(int_value, bit_count)
2332
2333            if self._map:
2334                self._native = set()
2335                for index, bit in enumerate(bits):
2336                    if bit:
2337                        name = self._map.get(index, index)
2338                        self._native.add(name)
2339            else:
2340                self._native = bits
2341        return self._native
2342
2343
2344class OctetBitString(Constructable, Castable, Primitive):
2345    """
2346    Represents a bit string in ASN.1 as a Python byte string
2347    """
2348
2349    tag = 3
2350
2351    # Instance attribute of (possibly-merged) byte string
2352    _bytes = None
2353
2354    # Tuple of 1s and 0s; set through native
2355    _unused_bits = ()
2356
2357    def set(self, value):
2358        """
2359        Sets the value of the object
2360
2361        :param value:
2362            A byte string
2363
2364        :raises:
2365            ValueError - when an invalid value is passed
2366        """
2367
2368        if not isinstance(value, byte_cls):
2369            raise TypeError(unwrap(
2370                '''
2371                %s value must be a byte string, not %s
2372                ''',
2373                type_name(self),
2374                type_name(value)
2375            ))
2376
2377        self._bytes = value
2378        # Set the unused bits to 0
2379        self.contents = b'\x00' + value
2380        self._unused_bits = ()
2381        self._header = None
2382        if self._indefinite:
2383            self._indefinite = False
2384            self.method = 0
2385        if self._trailer != b'':
2386            self._trailer = b''
2387
2388    def __bytes__(self):
2389        """
2390        :return:
2391            A byte string
2392        """
2393
2394        if self.contents is None:
2395            return b''
2396        if self._bytes is None:
2397            if not self._indefinite:
2398                self._bytes, self._unused_bits = self._as_chunk()[0]
2399            else:
2400                chunks = self._merge_chunks()
2401                self._unused_bits = ()
2402                for chunk in chunks:
2403                    if self._unused_bits:
2404                        # Disallowed by X.690 §8.6.4
2405                        raise ValueError('Only last chunk in a bit string may have unused bits')
2406                    self._unused_bits = chunk[1]
2407                self._bytes = b''.join(chunk[0] for chunk in chunks)
2408
2409        return self._bytes
2410
2411    def _copy(self, other, copy_func):
2412        """
2413        Copies the contents of another OctetBitString object to itself
2414
2415        :param object:
2416            Another instance of the same class
2417
2418        :param copy_func:
2419            An reference of copy.copy() or copy.deepcopy() to use when copying
2420            lists, dicts and objects
2421        """
2422
2423        super(OctetBitString, self)._copy(other, copy_func)
2424        self._bytes = other._bytes
2425        self._unused_bits = other._unused_bits
2426
2427    def _as_chunk(self):
2428        """
2429        Allows reconstructing indefinite length values
2430
2431        :raises:
2432            ValueError - when an invalid value is passed
2433
2434        :return:
2435            List with one tuple, consisting of a byte string and an integer (unused bits)
2436        """
2437
2438        unused_bits_len = ord(self.contents[0]) if _PY2 else self.contents[0]
2439        if not unused_bits_len:
2440            return [(self.contents[1:], ())]
2441
2442        if len(self.contents) == 1:
2443            # Disallowed by X.690 §8.6.2.3
2444            raise ValueError('Empty bit string has {0} unused bits'.format(unused_bits_len))
2445
2446        if unused_bits_len > 7:
2447            # Disallowed by X.690 §8.6.2.2
2448            raise ValueError('Bit string has {0} unused bits'.format(unused_bits_len))
2449
2450        mask = (1 << unused_bits_len) - 1
2451        last_byte = ord(self.contents[-1]) if _PY2 else self.contents[-1]
2452
2453        # zero out the unused bits in the last byte.
2454        zeroed_byte = last_byte & ~mask
2455        value = self.contents[1:-1] + (chr(zeroed_byte) if _PY2 else bytes((zeroed_byte,)))
2456
2457        unused_bits = _int_to_bit_tuple(last_byte & mask, unused_bits_len)
2458
2459        return [(value, unused_bits)]
2460
2461    @property
2462    def native(self):
2463        """
2464        The native Python datatype representation of this value
2465
2466        :return:
2467            A byte string or None
2468        """
2469
2470        if self.contents is None:
2471            return None
2472
2473        return self.__bytes__()
2474
2475    @property
2476    def unused_bits(self):
2477        """
2478        The unused bits of the bit string encoding.
2479
2480        :return:
2481            A tuple of 1s and 0s
2482        """
2483
2484        # call native to set _unused_bits
2485        self.native
2486
2487        return self._unused_bits
2488
2489
2490class IntegerBitString(_IntegerBitString, Constructable, Castable, Primitive):
2491    """
2492    Represents a bit string in ASN.1 as a Python integer
2493    """
2494
2495    tag = 3
2496
2497    def set(self, value):
2498        """
2499        Sets the value of the object
2500
2501        :param value:
2502            An integer
2503
2504        :raises:
2505            ValueError - when an invalid value is passed
2506        """
2507
2508        if not isinstance(value, int_types):
2509            raise TypeError(unwrap(
2510                '''
2511                %s value must be a positive integer, not %s
2512                ''',
2513                type_name(self),
2514                type_name(value)
2515            ))
2516
2517        if value < 0:
2518            raise ValueError(unwrap(
2519                '''
2520                %s value must be a positive integer, not %d
2521                ''',
2522                type_name(self),
2523                value
2524            ))
2525
2526        self._native = value
2527        # Set the unused bits to 0
2528        self.contents = b'\x00' + int_to_bytes(value, signed=True)
2529        self._unused_bits = ()
2530        self._header = None
2531        if self._indefinite:
2532            self._indefinite = False
2533            self.method = 0
2534        if self._trailer != b'':
2535            self._trailer = b''
2536
2537    @property
2538    def native(self):
2539        """
2540        The native Python datatype representation of this value
2541
2542        :return:
2543            An integer or None
2544        """
2545
2546        if self.contents is None:
2547            return None
2548
2549        if self._native is None:
2550            self._native, __, self._unused_bits = self._chunks_to_int()
2551
2552        return self._native
2553
2554
2555class OctetString(Constructable, Castable, Primitive):
2556    """
2557    Represents a byte string in both ASN.1 and Python
2558    """
2559
2560    tag = 4
2561
2562    # Instance attribute of (possibly-merged) byte string
2563    _bytes = None
2564
2565    def set(self, value):
2566        """
2567        Sets the value of the object
2568
2569        :param value:
2570            A byte string
2571        """
2572
2573        if not isinstance(value, byte_cls):
2574            raise TypeError(unwrap(
2575                '''
2576                %s value must be a byte string, not %s
2577                ''',
2578                type_name(self),
2579                type_name(value)
2580            ))
2581
2582        self._bytes = value
2583        self.contents = value
2584        self._header = None
2585        if self._indefinite:
2586            self._indefinite = False
2587            self.method = 0
2588        if self._trailer != b'':
2589            self._trailer = b''
2590
2591    def __bytes__(self):
2592        """
2593        :return:
2594            A byte string
2595        """
2596
2597        if self.contents is None:
2598            return b''
2599        if self._bytes is None:
2600            self._bytes = self._merge_chunks()
2601        return self._bytes
2602
2603    def _copy(self, other, copy_func):
2604        """
2605        Copies the contents of another OctetString object to itself
2606
2607        :param object:
2608            Another instance of the same class
2609
2610        :param copy_func:
2611            An reference of copy.copy() or copy.deepcopy() to use when copying
2612            lists, dicts and objects
2613        """
2614
2615        super(OctetString, self)._copy(other, copy_func)
2616        self._bytes = other._bytes
2617
2618    @property
2619    def native(self):
2620        """
2621        The native Python datatype representation of this value
2622
2623        :return:
2624            A byte string or None
2625        """
2626
2627        if self.contents is None:
2628            return None
2629
2630        return self.__bytes__()
2631
2632
2633class IntegerOctetString(Constructable, Castable, Primitive):
2634    """
2635    Represents a byte string in ASN.1 as a Python integer
2636    """
2637
2638    tag = 4
2639
2640    # An explicit length in bytes the integer should be encoded to. This should
2641    # generally not be used since DER defines a canonical encoding, however some
2642    # use of this, such as when storing elliptic curve private keys, requires an
2643    # exact number of bytes, even if the leading bytes are null.
2644    _encoded_width = None
2645
2646    def set(self, value):
2647        """
2648        Sets the value of the object
2649
2650        :param value:
2651            An integer
2652
2653        :raises:
2654            ValueError - when an invalid value is passed
2655        """
2656
2657        if not isinstance(value, int_types):
2658            raise TypeError(unwrap(
2659                '''
2660                %s value must be a positive integer, not %s
2661                ''',
2662                type_name(self),
2663                type_name(value)
2664            ))
2665
2666        if value < 0:
2667            raise ValueError(unwrap(
2668                '''
2669                %s value must be a positive integer, not %d
2670                ''',
2671                type_name(self),
2672                value
2673            ))
2674
2675        self._native = value
2676        self.contents = int_to_bytes(value, signed=False, width=self._encoded_width)
2677        self._header = None
2678        if self._indefinite:
2679            self._indefinite = False
2680            self.method = 0
2681        if self._trailer != b'':
2682            self._trailer = b''
2683
2684    @property
2685    def native(self):
2686        """
2687        The native Python datatype representation of this value
2688
2689        :return:
2690            An integer or None
2691        """
2692
2693        if self.contents is None:
2694            return None
2695
2696        if self._native is None:
2697            self._native = int_from_bytes(self._merge_chunks())
2698        return self._native
2699
2700    def set_encoded_width(self, width):
2701        """
2702        Set the explicit enoding width for the integer
2703
2704        :param width:
2705            An integer byte width to encode the integer to
2706        """
2707
2708        self._encoded_width = width
2709        # Make sure the encoded value is up-to-date with the proper width
2710        if self.contents is not None and len(self.contents) != width:
2711            self.set(self.native)
2712
2713
2714class ParsableOctetString(Constructable, Castable, Primitive):
2715
2716    tag = 4
2717
2718    _parsed = None
2719
2720    # Instance attribute of (possibly-merged) byte string
2721    _bytes = None
2722
2723    def __init__(self, value=None, parsed=None, **kwargs):
2724        """
2725        Allows providing a parsed object that will be serialized to get the
2726        byte string value
2727
2728        :param value:
2729            A native Python datatype to initialize the object value with
2730
2731        :param parsed:
2732            If value is None and this is an Asn1Value object, this will be
2733            set as the parsed value, and the value will be obtained by calling
2734            .dump() on this object.
2735        """
2736
2737        set_parsed = False
2738        if value is None and parsed is not None and isinstance(parsed, Asn1Value):
2739            value = parsed.dump()
2740            set_parsed = True
2741
2742        Primitive.__init__(self, value=value, **kwargs)
2743
2744        if set_parsed:
2745            self._parsed = (parsed, parsed.__class__, None)
2746
2747    def set(self, value):
2748        """
2749        Sets the value of the object
2750
2751        :param value:
2752            A byte string
2753        """
2754
2755        if not isinstance(value, byte_cls):
2756            raise TypeError(unwrap(
2757                '''
2758                %s value must be a byte string, not %s
2759                ''',
2760                type_name(self),
2761                type_name(value)
2762            ))
2763
2764        self._bytes = value
2765        self.contents = value
2766        self._header = None
2767        if self._indefinite:
2768            self._indefinite = False
2769            self.method = 0
2770        if self._trailer != b'':
2771            self._trailer = b''
2772
2773    def parse(self, spec=None, spec_params=None):
2774        """
2775        Parses the contents generically, or using a spec with optional params
2776
2777        :param spec:
2778            A class derived from Asn1Value that defines what class_ and tag the
2779            value should have, and the semantics of the encoded value. The
2780            return value will be of this type. If omitted, the encoded value
2781            will be decoded using the standard universal tag based on the
2782            encoded tag number.
2783
2784        :param spec_params:
2785            A dict of params to pass to the spec object
2786
2787        :return:
2788            An object of the type spec, or if not present, a child of Asn1Value
2789        """
2790
2791        if self._parsed is None or self._parsed[1:3] != (spec, spec_params):
2792            parsed_value, _ = _parse_build(self.__bytes__(), spec=spec, spec_params=spec_params)
2793            self._parsed = (parsed_value, spec, spec_params)
2794        return self._parsed[0]
2795
2796    def __bytes__(self):
2797        """
2798        :return:
2799            A byte string
2800        """
2801
2802        if self.contents is None:
2803            return b''
2804        if self._bytes is None:
2805            self._bytes = self._merge_chunks()
2806        return self._bytes
2807
2808    def _setable_native(self):
2809        """
2810        Returns a byte string that can be passed into .set()
2811
2812        :return:
2813            A python value that is valid to pass to .set()
2814        """
2815
2816        return self.__bytes__()
2817
2818    def _copy(self, other, copy_func):
2819        """
2820        Copies the contents of another ParsableOctetString object to itself
2821
2822        :param object:
2823            Another instance of the same class
2824
2825        :param copy_func:
2826            An reference of copy.copy() or copy.deepcopy() to use when copying
2827            lists, dicts and objects
2828        """
2829
2830        super(ParsableOctetString, self)._copy(other, copy_func)
2831        self._bytes = other._bytes
2832        self._parsed = copy_func(other._parsed)
2833
2834    @property
2835    def native(self):
2836        """
2837        The native Python datatype representation of this value
2838
2839        :return:
2840            A byte string or None
2841        """
2842
2843        if self.contents is None:
2844            return None
2845
2846        if self._parsed is not None:
2847            return self._parsed[0].native
2848        else:
2849            return self.__bytes__()
2850
2851    @property
2852    def parsed(self):
2853        """
2854        Returns the parsed object from .parse()
2855
2856        :return:
2857            The object returned by .parse()
2858        """
2859
2860        if self._parsed is None:
2861            self.parse()
2862
2863        return self._parsed[0]
2864
2865    def dump(self, force=False):
2866        """
2867        Encodes the value using DER
2868
2869        :param force:
2870            If the encoded contents already exist, clear them and regenerate
2871            to ensure they are in DER format instead of BER format
2872
2873        :return:
2874            A byte string of the DER-encoded value
2875        """
2876
2877        # If the length is indefinite, force the re-encoding
2878        if self._indefinite:
2879            force = True
2880
2881        if force:
2882            if self._parsed is not None:
2883                native = self.parsed.dump(force=force)
2884            else:
2885                native = self.native
2886            self.contents = None
2887            self.set(native)
2888
2889        return Asn1Value.dump(self)
2890
2891
2892class ParsableOctetBitString(ParsableOctetString):
2893
2894    tag = 3
2895
2896    def set(self, value):
2897        """
2898        Sets the value of the object
2899
2900        :param value:
2901            A byte string
2902
2903        :raises:
2904            ValueError - when an invalid value is passed
2905        """
2906
2907        if not isinstance(value, byte_cls):
2908            raise TypeError(unwrap(
2909                '''
2910                %s value must be a byte string, not %s
2911                ''',
2912                type_name(self),
2913                type_name(value)
2914            ))
2915
2916        self._bytes = value
2917        # Set the unused bits to 0
2918        self.contents = b'\x00' + value
2919        self._header = None
2920        if self._indefinite:
2921            self._indefinite = False
2922            self.method = 0
2923        if self._trailer != b'':
2924            self._trailer = b''
2925
2926    def _as_chunk(self):
2927        """
2928        Allows reconstructing indefinite length values
2929
2930        :raises:
2931            ValueError - when an invalid value is passed
2932
2933        :return:
2934            A byte string
2935        """
2936
2937        unused_bits_len = ord(self.contents[0]) if _PY2 else self.contents[0]
2938        if unused_bits_len:
2939            raise ValueError('ParsableOctetBitString should have no unused bits')
2940
2941        return self.contents[1:]
2942
2943
2944class Null(Primitive):
2945    """
2946    Represents a null value in ASN.1 as None in Python
2947    """
2948
2949    tag = 5
2950
2951    contents = b''
2952
2953    def set(self, value):
2954        """
2955        Sets the value of the object
2956
2957        :param value:
2958            None
2959        """
2960
2961        self.contents = b''
2962
2963    @property
2964    def native(self):
2965        """
2966        The native Python datatype representation of this value
2967
2968        :return:
2969            None
2970        """
2971
2972        return None
2973
2974
2975class ObjectIdentifier(Primitive, ValueMap):
2976    """
2977    Represents an object identifier in ASN.1 as a Python unicode dotted
2978    integer string
2979    """
2980
2981    tag = 6
2982
2983    # A unicode string of the dotted form of the object identifier
2984    _dotted = None
2985
2986    @classmethod
2987    def map(cls, value):
2988        """
2989        Converts a dotted unicode string OID into a mapped unicode string
2990
2991        :param value:
2992            A dotted unicode string OID
2993
2994        :raises:
2995            ValueError - when no _map dict has been defined on the class
2996            TypeError - when value is not a unicode string
2997
2998        :return:
2999            A mapped unicode string
3000        """
3001
3002        if cls._map is None:
3003            raise ValueError(unwrap(
3004                '''
3005                %s._map has not been defined
3006                ''',
3007                type_name(cls)
3008            ))
3009
3010        if not isinstance(value, str_cls):
3011            raise TypeError(unwrap(
3012                '''
3013                value must be a unicode string, not %s
3014                ''',
3015                type_name(value)
3016            ))
3017
3018        return cls._map.get(value, value)
3019
3020    @classmethod
3021    def unmap(cls, value):
3022        """
3023        Converts a mapped unicode string value into a dotted unicode string OID
3024
3025        :param value:
3026            A mapped unicode string OR dotted unicode string OID
3027
3028        :raises:
3029            ValueError - when no _map dict has been defined on the class or the value can't be unmapped
3030            TypeError - when value is not a unicode string
3031
3032        :return:
3033            A dotted unicode string OID
3034        """
3035
3036        if cls not in _SETUP_CLASSES:
3037            cls()._setup()
3038            _SETUP_CLASSES[cls] = True
3039
3040        if cls._map is None:
3041            raise ValueError(unwrap(
3042                '''
3043                %s._map has not been defined
3044                ''',
3045                type_name(cls)
3046            ))
3047
3048        if not isinstance(value, str_cls):
3049            raise TypeError(unwrap(
3050                '''
3051                value must be a unicode string, not %s
3052                ''',
3053                type_name(value)
3054            ))
3055
3056        if value in cls._reverse_map:
3057            return cls._reverse_map[value]
3058
3059        if not _OID_RE.match(value):
3060            raise ValueError(unwrap(
3061                '''
3062                %s._map does not contain an entry for "%s"
3063                ''',
3064                type_name(cls),
3065                value
3066            ))
3067
3068        return value
3069
3070    def set(self, value):
3071        """
3072        Sets the value of the object
3073
3074        :param value:
3075            A unicode string. May be a dotted integer string, or if _map is
3076            provided, one of the mapped values.
3077
3078        :raises:
3079            ValueError - when an invalid value is passed
3080        """
3081
3082        if not isinstance(value, str_cls):
3083            raise TypeError(unwrap(
3084                '''
3085                %s value must be a unicode string, not %s
3086                ''',
3087                type_name(self),
3088                type_name(value)
3089            ))
3090
3091        self._native = value
3092
3093        if self._map is not None:
3094            if value in self._reverse_map:
3095                value = self._reverse_map[value]
3096
3097        self.contents = b''
3098        first = None
3099        for index, part in enumerate(value.split('.')):
3100            part = int(part)
3101
3102            # The first two parts are merged into a single byte
3103            if index == 0:
3104                first = part
3105                continue
3106            elif index == 1:
3107                if first > 2:
3108                    raise ValueError(unwrap(
3109                        '''
3110                        First arc must be one of 0, 1 or 2, not %s
3111                        ''',
3112                        repr(first)
3113                    ))
3114                elif first < 2 and part >= 40:
3115                    raise ValueError(unwrap(
3116                        '''
3117                        Second arc must be less than 40 if first arc is 0 or
3118                        1, not %s
3119                        ''',
3120                        repr(part)
3121                    ))
3122                part = (first * 40) + part
3123
3124            encoded_part = chr_cls(0x7F & part)
3125            part = part >> 7
3126            while part > 0:
3127                encoded_part = chr_cls(0x80 | (0x7F & part)) + encoded_part
3128                part = part >> 7
3129            self.contents += encoded_part
3130
3131        self._header = None
3132        if self._trailer != b'':
3133            self._trailer = b''
3134
3135    def __unicode__(self):
3136        """
3137        :return:
3138            A unicode string
3139        """
3140
3141        return self.dotted
3142
3143    @property
3144    def dotted(self):
3145        """
3146        :return:
3147            A unicode string of the object identifier in dotted notation, thus
3148            ignoring any mapped value
3149        """
3150
3151        if self._dotted is None:
3152            output = []
3153
3154            part = 0
3155            for byte in self.contents:
3156                if _PY2:
3157                    byte = ord(byte)
3158                part = part * 128
3159                part += byte & 127
3160                # Last byte in subidentifier has the eighth bit set to 0
3161                if byte & 0x80 == 0:
3162                    if len(output) == 0:
3163                        if part >= 80:
3164                            output.append(str_cls(2))
3165                            output.append(str_cls(part - 80))
3166                        elif part >= 40:
3167                            output.append(str_cls(1))
3168                            output.append(str_cls(part - 40))
3169                        else:
3170                            output.append(str_cls(0))
3171                            output.append(str_cls(part))
3172                    else:
3173                        output.append(str_cls(part))
3174                    part = 0
3175
3176            self._dotted = '.'.join(output)
3177        return self._dotted
3178
3179    @property
3180    def native(self):
3181        """
3182        The native Python datatype representation of this value
3183
3184        :return:
3185            A unicode string or None. If _map is not defined, the unicode string
3186            is a string of dotted integers. If _map is defined and the dotted
3187            string is present in the _map, the mapped value is returned.
3188        """
3189
3190        if self.contents is None:
3191            return None
3192
3193        if self._native is None:
3194            self._native = self.dotted
3195        if self._map is not None and self._native in self._map:
3196            self._native = self._map[self._native]
3197        return self._native
3198
3199
3200class ObjectDescriptor(Primitive):
3201    """
3202    Represents an object descriptor from ASN.1 - no Python implementation
3203    """
3204
3205    tag = 7
3206
3207
3208class InstanceOf(Primitive):
3209    """
3210    Represents an instance from ASN.1 - no Python implementation
3211    """
3212
3213    tag = 8
3214
3215
3216class Real(Primitive):
3217    """
3218    Represents a real number from ASN.1 - no Python implementation
3219    """
3220
3221    tag = 9
3222
3223
3224class Enumerated(Integer):
3225    """
3226    Represents a enumerated list of integers from ASN.1 as a Python
3227    unicode string
3228    """
3229
3230    tag = 10
3231
3232    def set(self, value):
3233        """
3234        Sets the value of the object
3235
3236        :param value:
3237            An integer or a unicode string from _map
3238
3239        :raises:
3240            ValueError - when an invalid value is passed
3241        """
3242
3243        if not isinstance(value, int_types) and not isinstance(value, str_cls):
3244            raise TypeError(unwrap(
3245                '''
3246                %s value must be an integer or a unicode string, not %s
3247                ''',
3248                type_name(self),
3249                type_name(value)
3250            ))
3251
3252        if isinstance(value, str_cls):
3253            if value not in self._reverse_map:
3254                raise ValueError(unwrap(
3255                    '''
3256                    %s value "%s" is not a valid value
3257                    ''',
3258                    type_name(self),
3259                    value
3260                ))
3261
3262            value = self._reverse_map[value]
3263
3264        elif value not in self._map:
3265            raise ValueError(unwrap(
3266                '''
3267                %s value %s is not a valid value
3268                ''',
3269                type_name(self),
3270                value
3271            ))
3272
3273        Integer.set(self, value)
3274
3275    @property
3276    def native(self):
3277        """
3278        The native Python datatype representation of this value
3279
3280        :return:
3281            A unicode string or None
3282        """
3283
3284        if self.contents is None:
3285            return None
3286
3287        if self._native is None:
3288            self._native = self._map[self.__int__()]
3289        return self._native
3290
3291
3292class UTF8String(AbstractString):
3293    """
3294    Represents a UTF-8 string from ASN.1 as a Python unicode string
3295    """
3296
3297    tag = 12
3298    _encoding = 'utf-8'
3299
3300
3301class RelativeOid(ObjectIdentifier):
3302    """
3303    Represents an object identifier in ASN.1 as a Python unicode dotted
3304    integer string
3305    """
3306
3307    tag = 13
3308
3309
3310class Sequence(Asn1Value):
3311    """
3312    Represents a sequence of fields from ASN.1 as a Python object with a
3313    dict-like interface
3314    """
3315
3316    tag = 16
3317
3318    class_ = 0
3319    method = 1
3320
3321    # A list of child objects, in order of _fields
3322    children = None
3323
3324    # Sequence overrides .contents to be a property so that the mutated state
3325    # of child objects can be checked to ensure everything is up-to-date
3326    _contents = None
3327
3328    # Variable to track if the object has been mutated
3329    _mutated = False
3330
3331    # A list of tuples in one of the following forms.
3332    #
3333    # Option 1, a unicode string field name and a value class
3334    #
3335    # ("name", Asn1ValueClass)
3336    #
3337    # Option 2, same as Option 1, but with a dict of class params
3338    #
3339    # ("name", Asn1ValueClass, {'explicit': 5})
3340    _fields = []
3341
3342    # A dict with keys being the name of a field and the value being a unicode
3343    # string of the method name on self to call to get the spec for that field
3344    _spec_callbacks = None
3345
3346    # A dict that maps unicode string field names to an index in _fields
3347    _field_map = None
3348
3349    # A list in the same order as _fields that has tuples in the form (class_, tag)
3350    _field_ids = None
3351
3352    # An optional 2-element tuple that defines the field names of an OID field
3353    # and the field that the OID should be used to help decode. Works with the
3354    # _oid_specs attribute.
3355    _oid_pair = None
3356
3357    # A dict with keys that are unicode string OID values and values that are
3358    # Asn1Value classes to use for decoding a variable-type field.
3359    _oid_specs = None
3360
3361    # A 2-element tuple of the indexes in _fields of the OID and value fields
3362    _oid_nums = None
3363
3364    # Predetermined field specs to optimize away calls to _determine_spec()
3365    _precomputed_specs = None
3366
3367    def __init__(self, value=None, default=None, **kwargs):
3368        """
3369        Allows setting field values before passing everything else along to
3370        Asn1Value.__init__()
3371
3372        :param value:
3373            A native Python datatype to initialize the object value with
3374
3375        :param default:
3376            The default value if no value is specified
3377        """
3378
3379        Asn1Value.__init__(self, **kwargs)
3380
3381        check_existing = False
3382        if value is None and default is not None:
3383            check_existing = True
3384            if self.children is None:
3385                if self.contents is None:
3386                    check_existing = False
3387                else:
3388                    self._parse_children()
3389            value = default
3390
3391        if value is not None:
3392            try:
3393                # Fields are iterated in definition order to allow things like
3394                # OID-based specs. Otherwise sometimes the value would be processed
3395                # before the OID field, resulting in invalid value object creation.
3396                if self._fields:
3397                    keys = [info[0] for info in self._fields]
3398                    unused_keys = set(value.keys())
3399                else:
3400                    keys = value.keys()
3401                    unused_keys = set(keys)
3402
3403                for key in keys:
3404                    # If we are setting defaults, but a real value has already
3405                    # been set for the field, then skip it
3406                    if check_existing:
3407                        index = self._field_map[key]
3408                        if index < len(self.children) and self.children[index] is not VOID:
3409                            if key in unused_keys:
3410                                unused_keys.remove(key)
3411                            continue
3412
3413                    if key in value:
3414                        self.__setitem__(key, value[key])
3415                        unused_keys.remove(key)
3416
3417                if len(unused_keys):
3418                    raise ValueError(unwrap(
3419                        '''
3420                        One or more unknown fields was passed to the constructor
3421                        of %s: %s
3422                        ''',
3423                        type_name(self),
3424                        ', '.join(sorted(list(unused_keys)))
3425                    ))
3426
3427            except (ValueError, TypeError) as e:
3428                args = e.args[1:]
3429                e.args = (e.args[0] + '\n    while constructing %s' % type_name(self),) + args
3430                raise e
3431
3432    @property
3433    def contents(self):
3434        """
3435        :return:
3436            A byte string of the DER-encoded contents of the sequence
3437        """
3438
3439        if self.children is None:
3440            return self._contents
3441
3442        if self._is_mutated():
3443            self._set_contents()
3444
3445        return self._contents
3446
3447    @contents.setter
3448    def contents(self, value):
3449        """
3450        :param value:
3451            A byte string of the DER-encoded contents of the sequence
3452        """
3453
3454        self._contents = value
3455
3456    def _is_mutated(self):
3457        """
3458        :return:
3459            A boolean - if the sequence or any children (recursively) have been
3460            mutated
3461        """
3462
3463        mutated = self._mutated
3464        if self.children is not None:
3465            for child in self.children:
3466                if isinstance(child, Sequence) or isinstance(child, SequenceOf):
3467                    mutated = mutated or child._is_mutated()
3468
3469        return mutated
3470
3471    def _lazy_child(self, index):
3472        """
3473        Builds a child object if the child has only been parsed into a tuple so far
3474        """
3475
3476        child = self.children[index]
3477        if child.__class__ == tuple:
3478            child = self.children[index] = _build(*child)
3479        return child
3480
3481    def __len__(self):
3482        """
3483        :return:
3484            Integer
3485        """
3486        # We inline this check to prevent method invocation each time
3487        if self.children is None:
3488            self._parse_children()
3489
3490        return len(self.children)
3491
3492    def __getitem__(self, key):
3493        """
3494        Allows accessing fields by name or index
3495
3496        :param key:
3497            A unicode string of the field name, or an integer of the field index
3498
3499        :raises:
3500            KeyError - when a field name or index is invalid
3501
3502        :return:
3503            The Asn1Value object of the field specified
3504        """
3505
3506        # We inline this check to prevent method invocation each time
3507        if self.children is None:
3508            self._parse_children()
3509
3510        if not isinstance(key, int_types):
3511            if key not in self._field_map:
3512                raise KeyError(unwrap(
3513                    '''
3514                    No field named "%s" defined for %s
3515                    ''',
3516                    key,
3517                    type_name(self)
3518                ))
3519            key = self._field_map[key]
3520
3521        if key >= len(self.children):
3522            raise KeyError(unwrap(
3523                '''
3524                No field numbered %s is present in this %s
3525                ''',
3526                key,
3527                type_name(self)
3528            ))
3529
3530        try:
3531            return self._lazy_child(key)
3532
3533        except (ValueError, TypeError) as e:
3534            args = e.args[1:]
3535            e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
3536            raise e
3537
3538    def __setitem__(self, key, value):
3539        """
3540        Allows settings fields by name or index
3541
3542        :param key:
3543            A unicode string of the field name, or an integer of the field index
3544
3545        :param value:
3546            A native Python datatype to set the field value to. This method will
3547            construct the appropriate Asn1Value object from _fields.
3548
3549        :raises:
3550            ValueError - when a field name or index is invalid
3551        """
3552
3553        # We inline this check to prevent method invocation each time
3554        if self.children is None:
3555            self._parse_children()
3556
3557        if not isinstance(key, int_types):
3558            if key not in self._field_map:
3559                raise KeyError(unwrap(
3560                    '''
3561                    No field named "%s" defined for %s
3562                    ''',
3563                    key,
3564                    type_name(self)
3565                ))
3566            key = self._field_map[key]
3567
3568        field_name, field_spec, value_spec, field_params, _ = self._determine_spec(key)
3569
3570        new_value = self._make_value(field_name, field_spec, value_spec, field_params, value)
3571
3572        invalid_value = False
3573        if isinstance(new_value, Any):
3574            invalid_value = new_value.parsed is None
3575        else:
3576            invalid_value = new_value.contents is None
3577
3578        if invalid_value:
3579            raise ValueError(unwrap(
3580                '''
3581                Value for field "%s" of %s is not set
3582                ''',
3583                field_name,
3584                type_name(self)
3585            ))
3586
3587        self.children[key] = new_value
3588
3589        if self._native is not None:
3590            self._native[self._fields[key][0]] = self.children[key].native
3591        self._mutated = True
3592
3593    def __delitem__(self, key):
3594        """
3595        Allows deleting optional or default fields by name or index
3596
3597        :param key:
3598            A unicode string of the field name, or an integer of the field index
3599
3600        :raises:
3601            ValueError - when a field name or index is invalid, or the field is not optional or defaulted
3602        """
3603
3604        # We inline this check to prevent method invocation each time
3605        if self.children is None:
3606            self._parse_children()
3607
3608        if not isinstance(key, int_types):
3609            if key not in self._field_map:
3610                raise KeyError(unwrap(
3611                    '''
3612                    No field named "%s" defined for %s
3613                    ''',
3614                    key,
3615                    type_name(self)
3616                ))
3617            key = self._field_map[key]
3618
3619        name, _, params = self._fields[key]
3620        if not params or ('default' not in params and 'optional' not in params):
3621            raise ValueError(unwrap(
3622                '''
3623                Can not delete the value for the field "%s" of %s since it is
3624                not optional or defaulted
3625                ''',
3626                name,
3627                type_name(self)
3628            ))
3629
3630        if 'optional' in params:
3631            self.children[key] = VOID
3632            if self._native is not None:
3633                self._native[name] = None
3634        else:
3635            self.__setitem__(key, None)
3636        self._mutated = True
3637
3638    def __iter__(self):
3639        """
3640        :return:
3641            An iterator of field key names
3642        """
3643
3644        for info in self._fields:
3645            yield info[0]
3646
3647    def _set_contents(self, force=False):
3648        """
3649        Updates the .contents attribute of the value with the encoded value of
3650        all of the child objects
3651
3652        :param force:
3653            Ensure all contents are in DER format instead of possibly using
3654            cached BER-encoded data
3655        """
3656
3657        if self.children is None:
3658            self._parse_children()
3659
3660        contents = BytesIO()
3661        for index, info in enumerate(self._fields):
3662            child = self.children[index]
3663            if child is None:
3664                child_dump = b''
3665            elif child.__class__ == tuple:
3666                if force:
3667                    child_dump = self._lazy_child(index).dump(force=force)
3668                else:
3669                    child_dump = child[3] + child[4] + child[5]
3670            else:
3671                child_dump = child.dump(force=force)
3672            # Skip values that are the same as the default
3673            if info[2] and 'default' in info[2]:
3674                default_value = info[1](**info[2])
3675                if default_value.dump() == child_dump:
3676                    continue
3677            contents.write(child_dump)
3678        self._contents = contents.getvalue()
3679
3680        self._header = None
3681        if self._trailer != b'':
3682            self._trailer = b''
3683
3684    def _setup(self):
3685        """
3686        Generates _field_map, _field_ids and _oid_nums for use in parsing
3687        """
3688
3689        cls = self.__class__
3690        cls._field_map = {}
3691        cls._field_ids = []
3692        cls._precomputed_specs = []
3693        for index, field in enumerate(cls._fields):
3694            if len(field) < 3:
3695                field = field + ({},)
3696                cls._fields[index] = field
3697            cls._field_map[field[0]] = index
3698            cls._field_ids.append(_build_id_tuple(field[2], field[1]))
3699
3700        if cls._oid_pair is not None:
3701            cls._oid_nums = (cls._field_map[cls._oid_pair[0]], cls._field_map[cls._oid_pair[1]])
3702
3703        for index, field in enumerate(cls._fields):
3704            has_callback = cls._spec_callbacks is not None and field[0] in cls._spec_callbacks
3705            is_mapped_oid = cls._oid_nums is not None and cls._oid_nums[1] == index
3706            if has_callback or is_mapped_oid:
3707                cls._precomputed_specs.append(None)
3708            else:
3709                cls._precomputed_specs.append((field[0], field[1], field[1], field[2], None))
3710
3711    def _determine_spec(self, index):
3712        """
3713        Determine how a value for a field should be constructed
3714
3715        :param index:
3716            The field number
3717
3718        :return:
3719            A tuple containing the following elements:
3720             - unicode string of the field name
3721             - Asn1Value class of the field spec
3722             - Asn1Value class of the value spec
3723             - None or dict of params to pass to the field spec
3724             - None or Asn1Value class indicating the value spec was derived from an OID or a spec callback
3725        """
3726
3727        name, field_spec, field_params = self._fields[index]
3728        value_spec = field_spec
3729        spec_override = None
3730
3731        if self._spec_callbacks is not None and name in self._spec_callbacks:
3732            callback = self._spec_callbacks[name]
3733            spec_override = callback(self)
3734            if spec_override:
3735                # Allow a spec callback to specify both the base spec and
3736                # the override, for situations such as OctetString and parse_as
3737                if spec_override.__class__ == tuple and len(spec_override) == 2:
3738                    field_spec, value_spec = spec_override
3739                    if value_spec is None:
3740                        value_spec = field_spec
3741                        spec_override = None
3742                # When no field spec is specified, use a single return value as that
3743                elif field_spec is None:
3744                    field_spec = spec_override
3745                    value_spec = field_spec
3746                    spec_override = None
3747                else:
3748                    value_spec = spec_override
3749
3750        elif self._oid_nums is not None and self._oid_nums[1] == index:
3751            oid = self._lazy_child(self._oid_nums[0]).native
3752            if oid in self._oid_specs:
3753                spec_override = self._oid_specs[oid]
3754                value_spec = spec_override
3755
3756        return (name, field_spec, value_spec, field_params, spec_override)
3757
3758    def _make_value(self, field_name, field_spec, value_spec, field_params, value):
3759        """
3760        Contructs an appropriate Asn1Value object for a field
3761
3762        :param field_name:
3763            A unicode string of the field name
3764
3765        :param field_spec:
3766            An Asn1Value class that is the field spec
3767
3768        :param value_spec:
3769            An Asn1Value class that is the vaue spec
3770
3771        :param field_params:
3772            None or a dict of params for the field spec
3773
3774        :param value:
3775            The value to construct an Asn1Value object from
3776
3777        :return:
3778            An instance of a child class of Asn1Value
3779        """
3780
3781        if value is None and 'optional' in field_params:
3782            return VOID
3783
3784        specs_different = field_spec != value_spec
3785        is_any = issubclass(field_spec, Any)
3786
3787        if issubclass(value_spec, Choice):
3788            is_asn1value = isinstance(value, Asn1Value)
3789            is_tuple = isinstance(value, tuple) and len(value) == 2
3790            is_dict = isinstance(value, dict) and len(value) == 1
3791            if not is_asn1value and not is_tuple and not is_dict:
3792                raise ValueError(unwrap(
3793                    '''
3794                    Can not set a native python value to %s, which has the
3795                    choice type of %s - value must be an instance of Asn1Value
3796                    ''',
3797                    field_name,
3798                    type_name(value_spec)
3799                ))
3800            if is_tuple or is_dict:
3801                value = value_spec(value)
3802            if not isinstance(value, value_spec):
3803                wrapper = value_spec()
3804                wrapper.validate(value.class_, value.tag, value.contents)
3805                wrapper._parsed = value
3806                new_value = wrapper
3807            else:
3808                new_value = value
3809
3810        elif isinstance(value, field_spec):
3811            new_value = value
3812            if specs_different:
3813                new_value.parse(value_spec)
3814
3815        elif (not specs_different or is_any) and not isinstance(value, value_spec):
3816            if (not is_any or specs_different) and isinstance(value, Asn1Value):
3817                raise TypeError(unwrap(
3818                    '''
3819                    %s value must be %s, not %s
3820                    ''',
3821                    field_name,
3822                    type_name(value_spec),
3823                    type_name(value)
3824                ))
3825            new_value = value_spec(value, **field_params)
3826
3827        else:
3828            if isinstance(value, value_spec):
3829                new_value = value
3830            else:
3831                if isinstance(value, Asn1Value):
3832                    raise TypeError(unwrap(
3833                        '''
3834                        %s value must be %s, not %s
3835                        ''',
3836                        field_name,
3837                        type_name(value_spec),
3838                        type_name(value)
3839                    ))
3840                new_value = value_spec(value)
3841
3842            # For when the field is OctetString or OctetBitString with embedded
3843            # values we need to wrap the value in the field spec to get the
3844            # appropriate encoded value.
3845            if specs_different and not is_any:
3846                wrapper = field_spec(value=new_value.dump(), **field_params)
3847                wrapper._parsed = (new_value, new_value.__class__, None)
3848                new_value = wrapper
3849
3850        new_value = _fix_tagging(new_value, field_params)
3851
3852        return new_value
3853
3854    def _parse_children(self, recurse=False):
3855        """
3856        Parses the contents and generates Asn1Value objects based on the
3857        definitions from _fields.
3858
3859        :param recurse:
3860            If child objects that are Sequence or SequenceOf objects should
3861            be recursively parsed
3862
3863        :raises:
3864            ValueError - when an error occurs parsing child objects
3865        """
3866
3867        cls = self.__class__
3868        if self._contents is None:
3869            if self._fields:
3870                self.children = [VOID] * len(self._fields)
3871                for index, (_, _, params) in enumerate(self._fields):
3872                    if 'default' in params:
3873                        if cls._precomputed_specs[index]:
3874                            field_name, field_spec, value_spec, field_params, _ = cls._precomputed_specs[index]
3875                        else:
3876                            field_name, field_spec, value_spec, field_params, _ = self._determine_spec(index)
3877                        self.children[index] = self._make_value(field_name, field_spec, value_spec, field_params, None)
3878            return
3879
3880        try:
3881            self.children = []
3882            contents_length = len(self._contents)
3883            child_pointer = 0
3884            field = 0
3885            field_len = len(self._fields)
3886            parts = None
3887            again = child_pointer < contents_length
3888            while again:
3889                if parts is None:
3890                    parts, child_pointer = _parse(self._contents, contents_length, pointer=child_pointer)
3891                again = child_pointer < contents_length
3892
3893                if field < field_len:
3894                    _, field_spec, value_spec, field_params, spec_override = (
3895                        cls._precomputed_specs[field] or self._determine_spec(field))
3896
3897                    # If the next value is optional or default, allow it to be absent
3898                    if field_params and ('optional' in field_params or 'default' in field_params):
3899                        if self._field_ids[field] != (parts[0], parts[2]) and field_spec != Any:
3900
3901                            # See if the value is a valid choice before assuming
3902                            # that we have a missing optional or default value
3903                            choice_match = False
3904                            if issubclass(field_spec, Choice):
3905                                try:
3906                                    tester = field_spec(**field_params)
3907                                    tester.validate(parts[0], parts[2], parts[4])
3908                                    choice_match = True
3909                                except (ValueError):
3910                                    pass
3911
3912                            if not choice_match:
3913                                if 'optional' in field_params:
3914                                    self.children.append(VOID)
3915                                else:
3916                                    self.children.append(field_spec(**field_params))
3917                                field += 1
3918                                again = True
3919                                continue
3920
3921                    if field_spec is None or (spec_override and issubclass(field_spec, Any)):
3922                        field_spec = value_spec
3923                        spec_override = None
3924
3925                    if spec_override:
3926                        child = parts + (field_spec, field_params, value_spec)
3927                    else:
3928                        child = parts + (field_spec, field_params)
3929
3930                # Handle situations where an optional or defaulted field definition is incorrect
3931                elif field_len > 0 and field + 1 <= field_len:
3932                    missed_fields = []
3933                    prev_field = field - 1
3934                    while prev_field >= 0:
3935                        prev_field_info = self._fields[prev_field]
3936                        if len(prev_field_info) < 3:
3937                            break
3938                        if 'optional' in prev_field_info[2] or 'default' in prev_field_info[2]:
3939                            missed_fields.append(prev_field_info[0])
3940                        prev_field -= 1
3941                    plural = 's' if len(missed_fields) > 1 else ''
3942                    missed_field_names = ', '.join(missed_fields)
3943                    raise ValueError(unwrap(
3944                        '''
3945                        Data for field %s (%s class, %s method, tag %s) does
3946                        not match the field definition%s of %s
3947                        ''',
3948                        field + 1,
3949                        CLASS_NUM_TO_NAME_MAP.get(parts[0]),
3950                        METHOD_NUM_TO_NAME_MAP.get(parts[1]),
3951                        parts[2],
3952                        plural,
3953                        missed_field_names
3954                    ))
3955
3956                else:
3957                    child = parts
3958
3959                if recurse:
3960                    child = _build(*child)
3961                    if isinstance(child, (Sequence, SequenceOf)):
3962                        child._parse_children(recurse=True)
3963
3964                self.children.append(child)
3965                field += 1
3966                parts = None
3967
3968            index = len(self.children)
3969            while index < field_len:
3970                name, field_spec, field_params = self._fields[index]
3971                if 'default' in field_params:
3972                    self.children.append(field_spec(**field_params))
3973                elif 'optional' in field_params:
3974                    self.children.append(VOID)
3975                else:
3976                    raise ValueError(unwrap(
3977                        '''
3978                        Field "%s" is missing from structure
3979                        ''',
3980                        name
3981                    ))
3982                index += 1
3983
3984        except (ValueError, TypeError) as e:
3985            self.children = None
3986            args = e.args[1:]
3987            e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
3988            raise e
3989
3990    def spec(self, field_name):
3991        """
3992        Determines the spec to use for the field specified. Depending on how
3993        the spec is determined (_oid_pair or _spec_callbacks), it may be
3994        necessary to set preceding field values before calling this. Usually
3995        specs, if dynamic, are controlled by a preceding ObjectIdentifier
3996        field.
3997
3998        :param field_name:
3999            A unicode string of the field name to get the spec for
4000
4001        :return:
4002            A child class of asn1crypto.core.Asn1Value that the field must be
4003            encoded using
4004        """
4005
4006        if not isinstance(field_name, str_cls):
4007            raise TypeError(unwrap(
4008                '''
4009                field_name must be a unicode string, not %s
4010                ''',
4011                type_name(field_name)
4012            ))
4013
4014        if self._fields is None:
4015            raise ValueError(unwrap(
4016                '''
4017                Unable to retrieve spec for field %s in the class %s because
4018                _fields has not been set
4019                ''',
4020                repr(field_name),
4021                type_name(self)
4022            ))
4023
4024        index = self._field_map[field_name]
4025        info = self._determine_spec(index)
4026
4027        return info[2]
4028
4029    @property
4030    def native(self):
4031        """
4032        The native Python datatype representation of this value
4033
4034        :return:
4035            An OrderedDict or None. If an OrderedDict, all child values are
4036            recursively converted to native representation also.
4037        """
4038
4039        if self.contents is None:
4040            return None
4041
4042        if self._native is None:
4043            if self.children is None:
4044                self._parse_children(recurse=True)
4045            try:
4046                self._native = OrderedDict()
4047                for index, child in enumerate(self.children):
4048                    if child.__class__ == tuple:
4049                        child = _build(*child)
4050                        self.children[index] = child
4051                    try:
4052                        name = self._fields[index][0]
4053                    except (IndexError):
4054                        name = str_cls(index)
4055                    self._native[name] = child.native
4056            except (ValueError, TypeError) as e:
4057                self._native = None
4058                args = e.args[1:]
4059                e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
4060                raise e
4061        return self._native
4062
4063    def _copy(self, other, copy_func):
4064        """
4065        Copies the contents of another Sequence object to itself
4066
4067        :param object:
4068            Another instance of the same class
4069
4070        :param copy_func:
4071            An reference of copy.copy() or copy.deepcopy() to use when copying
4072            lists, dicts and objects
4073        """
4074
4075        super(Sequence, self)._copy(other, copy_func)
4076        if self.children is not None:
4077            self.children = []
4078            for child in other.children:
4079                if child.__class__ == tuple:
4080                    self.children.append(child)
4081                else:
4082                    self.children.append(child.copy())
4083
4084    def debug(self, nest_level=1):
4085        """
4086        Show the binary data and parsed data in a tree structure
4087        """
4088
4089        if self.children is None:
4090            self._parse_children()
4091
4092        prefix = '  ' * nest_level
4093        _basic_debug(prefix, self)
4094        for field_name in self:
4095            child = self._lazy_child(self._field_map[field_name])
4096            if child is not VOID:
4097                print('%s    Field "%s"' % (prefix, field_name))
4098                child.debug(nest_level + 3)
4099
4100    def dump(self, force=False):
4101        """
4102        Encodes the value using DER
4103
4104        :param force:
4105            If the encoded contents already exist, clear them and regenerate
4106            to ensure they are in DER format instead of BER format
4107
4108        :return:
4109            A byte string of the DER-encoded value
4110        """
4111
4112        # If the length is indefinite, force the re-encoding
4113        if self._header is not None and self._header[-1:] == b'\x80':
4114            force = True
4115
4116        # We can't force encoding if we don't have a spec
4117        if force and self._fields == [] and self.__class__ is Sequence:
4118            force = False
4119
4120        if force:
4121            self._set_contents(force=force)
4122
4123        if self._fields and self.children is not None:
4124            for index, (field_name, _, params) in enumerate(self._fields):
4125                if self.children[index] is not VOID:
4126                    continue
4127                if 'default' in params or 'optional' in params:
4128                    continue
4129                raise ValueError(unwrap(
4130                    '''
4131                    Field "%s" is missing from structure
4132                    ''',
4133                    field_name
4134                ))
4135
4136        return Asn1Value.dump(self)
4137
4138
4139class SequenceOf(Asn1Value):
4140    """
4141    Represents a sequence (ordered) of a single type of values from ASN.1 as a
4142    Python object with a list-like interface
4143    """
4144
4145    tag = 16
4146
4147    class_ = 0
4148    method = 1
4149
4150    # A list of child objects
4151    children = None
4152
4153    # SequenceOf overrides .contents to be a property so that the mutated state
4154    # of child objects can be checked to ensure everything is up-to-date
4155    _contents = None
4156
4157    # Variable to track if the object has been mutated
4158    _mutated = False
4159
4160    # An Asn1Value class to use when parsing children
4161    _child_spec = None
4162
4163    def __init__(self, value=None, default=None, contents=None, spec=None, **kwargs):
4164        """
4165        Allows setting child objects and the _child_spec via the spec parameter
4166        before passing everything else along to Asn1Value.__init__()
4167
4168        :param value:
4169            A native Python datatype to initialize the object value with
4170
4171        :param default:
4172            The default value if no value is specified
4173
4174        :param contents:
4175            A byte string of the encoded contents of the value
4176
4177        :param spec:
4178            A class derived from Asn1Value to use to parse children
4179        """
4180
4181        if spec:
4182            self._child_spec = spec
4183
4184        Asn1Value.__init__(self, **kwargs)
4185
4186        try:
4187            if contents is not None:
4188                self.contents = contents
4189            else:
4190                if value is None and default is not None:
4191                    value = default
4192
4193                if value is not None:
4194                    for index, child in enumerate(value):
4195                        self.__setitem__(index, child)
4196
4197                    # Make sure a blank list is serialized
4198                    if self.contents is None:
4199                        self._set_contents()
4200
4201        except (ValueError, TypeError) as e:
4202            args = e.args[1:]
4203            e.args = (e.args[0] + '\n    while constructing %s' % type_name(self),) + args
4204            raise e
4205
4206    @property
4207    def contents(self):
4208        """
4209        :return:
4210            A byte string of the DER-encoded contents of the sequence
4211        """
4212
4213        if self.children is None:
4214            return self._contents
4215
4216        if self._is_mutated():
4217            self._set_contents()
4218
4219        return self._contents
4220
4221    @contents.setter
4222    def contents(self, value):
4223        """
4224        :param value:
4225            A byte string of the DER-encoded contents of the sequence
4226        """
4227
4228        self._contents = value
4229
4230    def _is_mutated(self):
4231        """
4232        :return:
4233            A boolean - if the sequence or any children (recursively) have been
4234            mutated
4235        """
4236
4237        mutated = self._mutated
4238        if self.children is not None:
4239            for child in self.children:
4240                if isinstance(child, Sequence) or isinstance(child, SequenceOf):
4241                    mutated = mutated or child._is_mutated()
4242
4243        return mutated
4244
4245    def _lazy_child(self, index):
4246        """
4247        Builds a child object if the child has only been parsed into a tuple so far
4248        """
4249
4250        child = self.children[index]
4251        if child.__class__ == tuple:
4252            child = _build(*child)
4253            self.children[index] = child
4254        return child
4255
4256    def _make_value(self, value):
4257        """
4258        Constructs a _child_spec value from a native Python data type, or
4259        an appropriate Asn1Value object
4260
4261        :param value:
4262            A native Python value, or some child of Asn1Value
4263
4264        :return:
4265            An object of type _child_spec
4266        """
4267
4268        if isinstance(value, self._child_spec):
4269            new_value = value
4270
4271        elif issubclass(self._child_spec, Any):
4272            if isinstance(value, Asn1Value):
4273                new_value = value
4274            else:
4275                raise ValueError(unwrap(
4276                    '''
4277                    Can not set a native python value to %s where the
4278                    _child_spec is Any - value must be an instance of Asn1Value
4279                    ''',
4280                    type_name(self)
4281                ))
4282
4283        elif issubclass(self._child_spec, Choice):
4284            if not isinstance(value, Asn1Value):
4285                raise ValueError(unwrap(
4286                    '''
4287                    Can not set a native python value to %s where the
4288                    _child_spec is the choice type %s - value must be an
4289                    instance of Asn1Value
4290                    ''',
4291                    type_name(self),
4292                    self._child_spec.__name__
4293                ))
4294            if not isinstance(value, self._child_spec):
4295                wrapper = self._child_spec()
4296                wrapper.validate(value.class_, value.tag, value.contents)
4297                wrapper._parsed = value
4298                value = wrapper
4299            new_value = value
4300
4301        else:
4302            return self._child_spec(value=value)
4303
4304        params = {}
4305        if self._child_spec.explicit:
4306            params['explicit'] = self._child_spec.explicit
4307        if self._child_spec.implicit:
4308            params['implicit'] = (self._child_spec.class_, self._child_spec.tag)
4309        return _fix_tagging(new_value, params)
4310
4311    def __len__(self):
4312        """
4313        :return:
4314            An integer
4315        """
4316        # We inline this checks to prevent method invocation each time
4317        if self.children is None:
4318            self._parse_children()
4319
4320        return len(self.children)
4321
4322    def __getitem__(self, key):
4323        """
4324        Allows accessing children via index
4325
4326        :param key:
4327            Integer index of child
4328        """
4329
4330        # We inline this checks to prevent method invocation each time
4331        if self.children is None:
4332            self._parse_children()
4333
4334        return self._lazy_child(key)
4335
4336    def __setitem__(self, key, value):
4337        """
4338        Allows overriding a child via index
4339
4340        :param key:
4341            Integer index of child
4342
4343        :param value:
4344            Native python datatype that will be passed to _child_spec to create
4345            new child object
4346        """
4347
4348        # We inline this checks to prevent method invocation each time
4349        if self.children is None:
4350            self._parse_children()
4351
4352        new_value = self._make_value(value)
4353
4354        # If adding at the end, create a space for the new value
4355        if key == len(self.children):
4356            self.children.append(None)
4357            if self._native is not None:
4358                self._native.append(None)
4359
4360        self.children[key] = new_value
4361
4362        if self._native is not None:
4363            self._native[key] = self.children[key].native
4364
4365        self._mutated = True
4366
4367    def __delitem__(self, key):
4368        """
4369        Allows removing a child via index
4370
4371        :param key:
4372            Integer index of child
4373        """
4374
4375        # We inline this checks to prevent method invocation each time
4376        if self.children is None:
4377            self._parse_children()
4378
4379        self.children.pop(key)
4380        if self._native is not None:
4381            self._native.pop(key)
4382
4383        self._mutated = True
4384
4385    def __iter__(self):
4386        """
4387        :return:
4388            An iter() of child objects
4389        """
4390
4391        # We inline this checks to prevent method invocation each time
4392        if self.children is None:
4393            self._parse_children()
4394
4395        for index in range(0, len(self.children)):
4396            yield self._lazy_child(index)
4397
4398    def __contains__(self, item):
4399        """
4400        :param item:
4401            An object of the type cls._child_spec
4402
4403        :return:
4404            A boolean if the item is contained in this SequenceOf
4405        """
4406
4407        if item is None or item is VOID:
4408            return False
4409
4410        if not isinstance(item, self._child_spec):
4411            raise TypeError(unwrap(
4412                '''
4413                Checking membership in %s is only available for instances of
4414                %s, not %s
4415                ''',
4416                type_name(self),
4417                type_name(self._child_spec),
4418                type_name(item)
4419            ))
4420
4421        for child in self:
4422            if child == item:
4423                return True
4424
4425        return False
4426
4427    def append(self, value):
4428        """
4429        Allows adding a child to the end of the sequence
4430
4431        :param value:
4432            Native python datatype that will be passed to _child_spec to create
4433            new child object
4434        """
4435
4436        # We inline this checks to prevent method invocation each time
4437        if self.children is None:
4438            self._parse_children()
4439
4440        self.children.append(self._make_value(value))
4441
4442        if self._native is not None:
4443            self._native.append(self.children[-1].native)
4444
4445        self._mutated = True
4446
4447    def _set_contents(self, force=False):
4448        """
4449        Encodes all child objects into the contents for this object
4450
4451        :param force:
4452            Ensure all contents are in DER format instead of possibly using
4453            cached BER-encoded data
4454        """
4455
4456        if self.children is None:
4457            self._parse_children()
4458
4459        contents = BytesIO()
4460        for child in self:
4461            contents.write(child.dump(force=force))
4462        self._contents = contents.getvalue()
4463        self._header = None
4464        if self._trailer != b'':
4465            self._trailer = b''
4466
4467    def _parse_children(self, recurse=False):
4468        """
4469        Parses the contents and generates Asn1Value objects based on the
4470        definitions from _child_spec.
4471
4472        :param recurse:
4473            If child objects that are Sequence or SequenceOf objects should
4474            be recursively parsed
4475
4476        :raises:
4477            ValueError - when an error occurs parsing child objects
4478        """
4479
4480        try:
4481            self.children = []
4482            if self._contents is None:
4483                return
4484            contents_length = len(self._contents)
4485            child_pointer = 0
4486            while child_pointer < contents_length:
4487                parts, child_pointer = _parse(self._contents, contents_length, pointer=child_pointer)
4488                if self._child_spec:
4489                    child = parts + (self._child_spec,)
4490                else:
4491                    child = parts
4492                if recurse:
4493                    child = _build(*child)
4494                    if isinstance(child, (Sequence, SequenceOf)):
4495                        child._parse_children(recurse=True)
4496                self.children.append(child)
4497        except (ValueError, TypeError) as e:
4498            self.children = None
4499            args = e.args[1:]
4500            e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
4501            raise e
4502
4503    def spec(self):
4504        """
4505        Determines the spec to use for child values.
4506
4507        :return:
4508            A child class of asn1crypto.core.Asn1Value that child values must be
4509            encoded using
4510        """
4511
4512        return self._child_spec
4513
4514    @property
4515    def native(self):
4516        """
4517        The native Python datatype representation of this value
4518
4519        :return:
4520            A list or None. If a list, all child values are recursively
4521            converted to native representation also.
4522        """
4523
4524        if self.contents is None:
4525            return None
4526
4527        if self._native is None:
4528            if self.children is None:
4529                self._parse_children(recurse=True)
4530            try:
4531                self._native = [child.native for child in self]
4532            except (ValueError, TypeError) as e:
4533                args = e.args[1:]
4534                e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
4535                raise e
4536        return self._native
4537
4538    def _copy(self, other, copy_func):
4539        """
4540        Copies the contents of another SequenceOf object to itself
4541
4542        :param object:
4543            Another instance of the same class
4544
4545        :param copy_func:
4546            An reference of copy.copy() or copy.deepcopy() to use when copying
4547            lists, dicts and objects
4548        """
4549
4550        super(SequenceOf, self)._copy(other, copy_func)
4551        if self.children is not None:
4552            self.children = []
4553            for child in other.children:
4554                if child.__class__ == tuple:
4555                    self.children.append(child)
4556                else:
4557                    self.children.append(child.copy())
4558
4559    def debug(self, nest_level=1):
4560        """
4561        Show the binary data and parsed data in a tree structure
4562        """
4563
4564        if self.children is None:
4565            self._parse_children()
4566
4567        prefix = '  ' * nest_level
4568        _basic_debug(prefix, self)
4569        for child in self:
4570            child.debug(nest_level + 1)
4571
4572    def dump(self, force=False):
4573        """
4574        Encodes the value using DER
4575
4576        :param force:
4577            If the encoded contents already exist, clear them and regenerate
4578            to ensure they are in DER format instead of BER format
4579
4580        :return:
4581            A byte string of the DER-encoded value
4582        """
4583
4584        # If the length is indefinite, force the re-encoding
4585        if self._header is not None and self._header[-1:] == b'\x80':
4586            force = True
4587
4588        if force:
4589            self._set_contents(force=force)
4590
4591        return Asn1Value.dump(self)
4592
4593
4594class Set(Sequence):
4595    """
4596    Represents a set of fields (unordered) from ASN.1 as a Python object with a
4597    dict-like interface
4598    """
4599
4600    method = 1
4601    class_ = 0
4602    tag = 17
4603
4604    # A dict of 2-element tuples in the form (class_, tag) as keys and integers
4605    # as values that are the index of the field in _fields
4606    _field_ids = None
4607
4608    def _setup(self):
4609        """
4610        Generates _field_map, _field_ids and _oid_nums for use in parsing
4611        """
4612
4613        cls = self.__class__
4614        cls._field_map = {}
4615        cls._field_ids = {}
4616        cls._precomputed_specs = []
4617        for index, field in enumerate(cls._fields):
4618            if len(field) < 3:
4619                field = field + ({},)
4620                cls._fields[index] = field
4621            cls._field_map[field[0]] = index
4622            cls._field_ids[_build_id_tuple(field[2], field[1])] = index
4623
4624        if cls._oid_pair is not None:
4625            cls._oid_nums = (cls._field_map[cls._oid_pair[0]], cls._field_map[cls._oid_pair[1]])
4626
4627        for index, field in enumerate(cls._fields):
4628            has_callback = cls._spec_callbacks is not None and field[0] in cls._spec_callbacks
4629            is_mapped_oid = cls._oid_nums is not None and cls._oid_nums[1] == index
4630            if has_callback or is_mapped_oid:
4631                cls._precomputed_specs.append(None)
4632            else:
4633                cls._precomputed_specs.append((field[0], field[1], field[1], field[2], None))
4634
4635    def _parse_children(self, recurse=False):
4636        """
4637        Parses the contents and generates Asn1Value objects based on the
4638        definitions from _fields.
4639
4640        :param recurse:
4641            If child objects that are Sequence or SequenceOf objects should
4642            be recursively parsed
4643
4644        :raises:
4645            ValueError - when an error occurs parsing child objects
4646        """
4647
4648        cls = self.__class__
4649        if self._contents is None:
4650            if self._fields:
4651                self.children = [VOID] * len(self._fields)
4652                for index, (_, _, params) in enumerate(self._fields):
4653                    if 'default' in params:
4654                        if cls._precomputed_specs[index]:
4655                            field_name, field_spec, value_spec, field_params, _ = cls._precomputed_specs[index]
4656                        else:
4657                            field_name, field_spec, value_spec, field_params, _ = self._determine_spec(index)
4658                        self.children[index] = self._make_value(field_name, field_spec, value_spec, field_params, None)
4659            return
4660
4661        try:
4662            child_map = {}
4663            contents_length = len(self.contents)
4664            child_pointer = 0
4665            seen_field = 0
4666            while child_pointer < contents_length:
4667                parts, child_pointer = _parse(self.contents, contents_length, pointer=child_pointer)
4668
4669                id_ = (parts[0], parts[2])
4670
4671                field = self._field_ids.get(id_)
4672                if field is None:
4673                    raise ValueError(unwrap(
4674                        '''
4675                        Data for field %s (%s class, %s method, tag %s) does
4676                        not match any of the field definitions
4677                        ''',
4678                        seen_field,
4679                        CLASS_NUM_TO_NAME_MAP.get(parts[0]),
4680                        METHOD_NUM_TO_NAME_MAP.get(parts[1]),
4681                        parts[2],
4682                    ))
4683
4684                _, field_spec, value_spec, field_params, spec_override = (
4685                    cls._precomputed_specs[field] or self._determine_spec(field))
4686
4687                if field_spec is None or (spec_override and issubclass(field_spec, Any)):
4688                    field_spec = value_spec
4689                    spec_override = None
4690
4691                if spec_override:
4692                    child = parts + (field_spec, field_params, value_spec)
4693                else:
4694                    child = parts + (field_spec, field_params)
4695
4696                if recurse:
4697                    child = _build(*child)
4698                    if isinstance(child, (Sequence, SequenceOf)):
4699                        child._parse_children(recurse=True)
4700
4701                child_map[field] = child
4702                seen_field += 1
4703
4704            total_fields = len(self._fields)
4705
4706            for index in range(0, total_fields):
4707                if index in child_map:
4708                    continue
4709
4710                name, field_spec, value_spec, field_params, spec_override = (
4711                    cls._precomputed_specs[index] or self._determine_spec(index))
4712
4713                if field_spec is None or (spec_override and issubclass(field_spec, Any)):
4714                    field_spec = value_spec
4715                    spec_override = None
4716
4717                missing = False
4718
4719                if not field_params:
4720                    missing = True
4721                elif 'optional' not in field_params and 'default' not in field_params:
4722                    missing = True
4723                elif 'optional' in field_params:
4724                    child_map[index] = VOID
4725                elif 'default' in field_params:
4726                    child_map[index] = field_spec(**field_params)
4727
4728                if missing:
4729                    raise ValueError(unwrap(
4730                        '''
4731                        Missing required field "%s" from %s
4732                        ''',
4733                        name,
4734                        type_name(self)
4735                    ))
4736
4737            self.children = []
4738            for index in range(0, total_fields):
4739                self.children.append(child_map[index])
4740
4741        except (ValueError, TypeError) as e:
4742            args = e.args[1:]
4743            e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
4744            raise e
4745
4746    def _set_contents(self, force=False):
4747        """
4748        Encodes all child objects into the contents for this object.
4749
4750        This method is overridden because a Set needs to be encoded by
4751        removing defaulted fields and then sorting the fields by tag.
4752
4753        :param force:
4754            Ensure all contents are in DER format instead of possibly using
4755            cached BER-encoded data
4756        """
4757
4758        if self.children is None:
4759            self._parse_children()
4760
4761        child_tag_encodings = []
4762        for index, child in enumerate(self.children):
4763            child_encoding = child.dump(force=force)
4764
4765            # Skip encoding defaulted children
4766            name, spec, field_params = self._fields[index]
4767            if 'default' in field_params:
4768                if spec(**field_params).dump() == child_encoding:
4769                    continue
4770
4771            child_tag_encodings.append((child.tag, child_encoding))
4772        child_tag_encodings.sort(key=lambda ct: ct[0])
4773
4774        self._contents = b''.join([ct[1] for ct in child_tag_encodings])
4775        self._header = None
4776        if self._trailer != b'':
4777            self._trailer = b''
4778
4779
4780class SetOf(SequenceOf):
4781    """
4782    Represents a set (unordered) of a single type of values from ASN.1 as a
4783    Python object with a list-like interface
4784    """
4785
4786    tag = 17
4787
4788    def _set_contents(self, force=False):
4789        """
4790        Encodes all child objects into the contents for this object.
4791
4792        This method is overridden because a SetOf needs to be encoded by
4793        sorting the child encodings.
4794
4795        :param force:
4796            Ensure all contents are in DER format instead of possibly using
4797            cached BER-encoded data
4798        """
4799
4800        if self.children is None:
4801            self._parse_children()
4802
4803        child_encodings = []
4804        for child in self:
4805            child_encodings.append(child.dump(force=force))
4806
4807        self._contents = b''.join(sorted(child_encodings))
4808        self._header = None
4809        if self._trailer != b'':
4810            self._trailer = b''
4811
4812
4813class EmbeddedPdv(Sequence):
4814    """
4815    A sequence structure
4816    """
4817
4818    tag = 11
4819
4820
4821class NumericString(AbstractString):
4822    """
4823    Represents a numeric string from ASN.1 as a Python unicode string
4824    """
4825
4826    tag = 18
4827    _encoding = 'latin1'
4828
4829
4830class PrintableString(AbstractString):
4831    """
4832    Represents a printable string from ASN.1 as a Python unicode string
4833    """
4834
4835    tag = 19
4836    _encoding = 'latin1'
4837
4838
4839class TeletexString(AbstractString):
4840    """
4841    Represents a teletex string from ASN.1 as a Python unicode string
4842    """
4843
4844    tag = 20
4845    _encoding = 'teletex'
4846
4847
4848class VideotexString(OctetString):
4849    """
4850    Represents a videotex string from ASN.1 as a Python byte string
4851    """
4852
4853    tag = 21
4854
4855
4856class IA5String(AbstractString):
4857    """
4858    Represents an IA5 string from ASN.1 as a Python unicode string
4859    """
4860
4861    tag = 22
4862    _encoding = 'ascii'
4863
4864
4865class AbstractTime(AbstractString):
4866    """
4867    Represents a time from ASN.1 as a Python datetime.datetime object
4868    """
4869
4870    @property
4871    def _parsed_time(self):
4872        """
4873        The parsed datetime string.
4874
4875        :raises:
4876            ValueError - when an invalid value is passed
4877
4878        :return:
4879            A dict with the parsed values
4880        """
4881
4882        string = str_cls(self)
4883
4884        m = self._TIMESTRING_RE.match(string)
4885        if not m:
4886            raise ValueError(unwrap(
4887                '''
4888                Error parsing %s to a %s
4889                ''',
4890                string,
4891                type_name(self),
4892            ))
4893
4894        groups = m.groupdict()
4895
4896        tz = None
4897        if groups['zulu']:
4898            tz = timezone.utc
4899        elif groups['dsign']:
4900            sign = 1 if groups['dsign'] == '+' else -1
4901            tz = create_timezone(sign * timedelta(
4902                hours=int(groups['dhour']),
4903                minutes=int(groups['dminute'] or 0)
4904            ))
4905
4906        if groups['fraction']:
4907            # Compute fraction in microseconds
4908            fract = Fraction(
4909                int(groups['fraction']),
4910                10 ** len(groups['fraction'])
4911            ) * 1000000
4912
4913            if groups['minute'] is None:
4914                fract *= 3600
4915            elif groups['second'] is None:
4916                fract *= 60
4917
4918            fract_usec = int(fract.limit_denominator(1))
4919
4920        else:
4921            fract_usec = 0
4922
4923        return {
4924            'year': int(groups['year']),
4925            'month': int(groups['month']),
4926            'day': int(groups['day']),
4927            'hour': int(groups['hour']),
4928            'minute': int(groups['minute'] or 0),
4929            'second': int(groups['second'] or 0),
4930            'tzinfo': tz,
4931            'fraction': fract_usec,
4932        }
4933
4934    @property
4935    def native(self):
4936        """
4937        The native Python datatype representation of this value
4938
4939        :return:
4940            A datetime.datetime object, asn1crypto.util.extended_datetime object or
4941            None. The datetime object is usually timezone aware. If it's naive, then
4942            it's in the sender's local time; see X.680 sect. 42.3
4943        """
4944
4945        if self.contents is None:
4946            return None
4947
4948        if self._native is None:
4949            parsed = self._parsed_time
4950
4951            fraction = parsed.pop('fraction', 0)
4952
4953            value = self._get_datetime(parsed)
4954
4955            if fraction:
4956                value += timedelta(microseconds=fraction)
4957
4958            self._native = value
4959
4960        return self._native
4961
4962
4963class UTCTime(AbstractTime):
4964    """
4965    Represents a UTC time from ASN.1 as a timezone aware Python datetime.datetime object
4966    """
4967
4968    tag = 23
4969
4970    # Regular expression for UTCTime as described in X.680 sect. 43 and ISO 8601
4971    _TIMESTRING_RE = re.compile(r'''
4972        ^
4973        # YYMMDD
4974        (?P<year>\d{2})
4975        (?P<month>\d{2})
4976        (?P<day>\d{2})
4977
4978        # hhmm or hhmmss
4979        (?P<hour>\d{2})
4980        (?P<minute>\d{2})
4981        (?P<second>\d{2})?
4982
4983        # Matches nothing, needed because GeneralizedTime uses this.
4984        (?P<fraction>)
4985
4986        # Z or [-+]hhmm
4987        (?:
4988            (?P<zulu>Z)
4989            |
4990            (?:
4991                (?P<dsign>[-+])
4992                (?P<dhour>\d{2})
4993                (?P<dminute>\d{2})
4994            )
4995        )
4996        $
4997    ''', re.X)
4998
4999    def set(self, value):
5000        """
5001        Sets the value of the object
5002
5003        :param value:
5004            A unicode string or a datetime.datetime object
5005
5006        :raises:
5007            ValueError - when an invalid value is passed
5008        """
5009
5010        if isinstance(value, datetime):
5011            if not value.tzinfo:
5012                raise ValueError('Must be timezone aware')
5013
5014            # Convert value to UTC.
5015            value = value.astimezone(utc_with_dst)
5016
5017            if not 1950 <= value.year <= 2049:
5018                raise ValueError('Year of the UTCTime is not in range [1950, 2049], use GeneralizedTime instead')
5019
5020            value = value.strftime('%y%m%d%H%M%SZ')
5021            if _PY2:
5022                value = value.decode('ascii')
5023
5024        AbstractString.set(self, value)
5025        # Set it to None and let the class take care of converting the next
5026        # time that .native is called
5027        self._native = None
5028
5029    def _get_datetime(self, parsed):
5030        """
5031        Create a datetime object from the parsed time.
5032
5033        :return:
5034            An aware datetime.datetime object
5035        """
5036
5037        # X.680 only specifies that UTCTime is not using a century.
5038        # So "18" could as well mean 2118 or 1318.
5039        # X.509 and CMS specify to use UTCTime for years earlier than 2050.
5040        # Assume that UTCTime is only used for years [1950, 2049].
5041        if parsed['year'] < 50:
5042            parsed['year'] += 2000
5043        else:
5044            parsed['year'] += 1900
5045
5046        return datetime(**parsed)
5047
5048
5049class GeneralizedTime(AbstractTime):
5050    """
5051    Represents a generalized time from ASN.1 as a Python datetime.datetime
5052    object or asn1crypto.util.extended_datetime object in UTC
5053    """
5054
5055    tag = 24
5056
5057    # Regular expression for GeneralizedTime as described in X.680 sect. 42 and ISO 8601
5058    _TIMESTRING_RE = re.compile(r'''
5059        ^
5060        # YYYYMMDD
5061        (?P<year>\d{4})
5062        (?P<month>\d{2})
5063        (?P<day>\d{2})
5064
5065        # hh or hhmm or hhmmss
5066        (?P<hour>\d{2})
5067        (?:
5068            (?P<minute>\d{2})
5069            (?P<second>\d{2})?
5070        )?
5071
5072        # Optional fraction; [.,]dddd (one or more decimals)
5073        # If Seconds are given, it's fractions of Seconds.
5074        # Else if Minutes are given, it's fractions of Minutes.
5075        # Else it's fractions of Hours.
5076        (?:
5077            [,.]
5078            (?P<fraction>\d+)
5079        )?
5080
5081        # Optional timezone. If left out, the time is in local time.
5082        # Z or [-+]hh or [-+]hhmm
5083        (?:
5084            (?P<zulu>Z)
5085            |
5086            (?:
5087                (?P<dsign>[-+])
5088                (?P<dhour>\d{2})
5089                (?P<dminute>\d{2})?
5090            )
5091        )?
5092        $
5093    ''', re.X)
5094
5095    def set(self, value):
5096        """
5097        Sets the value of the object
5098
5099        :param value:
5100            A unicode string, a datetime.datetime object or an
5101            asn1crypto.util.extended_datetime object
5102
5103        :raises:
5104            ValueError - when an invalid value is passed
5105        """
5106
5107        if isinstance(value, (datetime, extended_datetime)):
5108            if not value.tzinfo:
5109                raise ValueError('Must be timezone aware')
5110
5111            # Convert value to UTC.
5112            value = value.astimezone(utc_with_dst)
5113
5114            if value.microsecond:
5115                fraction = '.' + str(value.microsecond).zfill(6).rstrip('0')
5116            else:
5117                fraction = ''
5118
5119            value = value.strftime('%Y%m%d%H%M%S') + fraction + 'Z'
5120            if _PY2:
5121                value = value.decode('ascii')
5122
5123        AbstractString.set(self, value)
5124        # Set it to None and let the class take care of converting the next
5125        # time that .native is called
5126        self._native = None
5127
5128    def _get_datetime(self, parsed):
5129        """
5130        Create a datetime object from the parsed time.
5131
5132        :return:
5133            A datetime.datetime object or asn1crypto.util.extended_datetime object.
5134            It may or may not be aware.
5135        """
5136
5137        if parsed['year'] == 0:
5138            # datetime does not support year 0. Use extended_datetime instead.
5139            return extended_datetime(**parsed)
5140        else:
5141            return datetime(**parsed)
5142
5143
5144class GraphicString(AbstractString):
5145    """
5146    Represents a graphic string from ASN.1 as a Python unicode string
5147    """
5148
5149    tag = 25
5150    # This is technically not correct since this type can contain any charset
5151    _encoding = 'latin1'
5152
5153
5154class VisibleString(AbstractString):
5155    """
5156    Represents a visible string from ASN.1 as a Python unicode string
5157    """
5158
5159    tag = 26
5160    _encoding = 'latin1'
5161
5162
5163class GeneralString(AbstractString):
5164    """
5165    Represents a general string from ASN.1 as a Python unicode string
5166    """
5167
5168    tag = 27
5169    # This is technically not correct since this type can contain any charset
5170    _encoding = 'latin1'
5171
5172
5173class UniversalString(AbstractString):
5174    """
5175    Represents a universal string from ASN.1 as a Python unicode string
5176    """
5177
5178    tag = 28
5179    _encoding = 'utf-32-be'
5180
5181
5182class CharacterString(AbstractString):
5183    """
5184    Represents a character string from ASN.1 as a Python unicode string
5185    """
5186
5187    tag = 29
5188    # This is technically not correct since this type can contain any charset
5189    _encoding = 'latin1'
5190
5191
5192class BMPString(AbstractString):
5193    """
5194    Represents a BMP string from ASN.1 as a Python unicode string
5195    """
5196
5197    tag = 30
5198    _encoding = 'utf-16-be'
5199
5200
5201def _basic_debug(prefix, self):
5202    """
5203    Prints out basic information about an Asn1Value object. Extracted for reuse
5204    among different classes that customize the debug information.
5205
5206    :param prefix:
5207        A unicode string of spaces to prefix output line with
5208
5209    :param self:
5210        The object to print the debugging information about
5211    """
5212
5213    print('%s%s Object #%s' % (prefix, type_name(self), id(self)))
5214    if self._header:
5215        print('%s  Header: 0x%s' % (prefix, binascii.hexlify(self._header or b'').decode('utf-8')))
5216
5217    has_header = self.method is not None and self.class_ is not None and self.tag is not None
5218    if has_header:
5219        method_name = METHOD_NUM_TO_NAME_MAP.get(self.method)
5220        class_name = CLASS_NUM_TO_NAME_MAP.get(self.class_)
5221
5222    if self.explicit is not None:
5223        for class_, tag in self.explicit:
5224            print(
5225                '%s    %s tag %s (explicitly tagged)' %
5226                (
5227                    prefix,
5228                    CLASS_NUM_TO_NAME_MAP.get(class_),
5229                    tag
5230                )
5231            )
5232        if has_header:
5233            print('%s      %s %s %s' % (prefix, method_name, class_name, self.tag))
5234
5235    elif self.implicit:
5236        if has_header:
5237            print('%s    %s %s tag %s (implicitly tagged)' % (prefix, method_name, class_name, self.tag))
5238
5239    elif has_header:
5240        print('%s    %s %s tag %s' % (prefix, method_name, class_name, self.tag))
5241
5242    if self._trailer:
5243        print('%s  Trailer: 0x%s' % (prefix, binascii.hexlify(self._trailer or b'').decode('utf-8')))
5244
5245    print('%s  Data: 0x%s' % (prefix, binascii.hexlify(self.contents or b'').decode('utf-8')))
5246
5247
5248def _tag_type_to_explicit_implicit(params):
5249    """
5250    Converts old-style "tag_type" and "tag" params to "explicit" and "implicit"
5251
5252    :param params:
5253        A dict of parameters to convert from tag_type/tag to explicit/implicit
5254    """
5255
5256    if 'tag_type' in params:
5257        if params['tag_type'] == 'explicit':
5258            params['explicit'] = (params.get('class', 2), params['tag'])
5259        elif params['tag_type'] == 'implicit':
5260            params['implicit'] = (params.get('class', 2), params['tag'])
5261        del params['tag_type']
5262        del params['tag']
5263        if 'class' in params:
5264            del params['class']
5265
5266
5267def _fix_tagging(value, params):
5268    """
5269    Checks if a value is properly tagged based on the spec, and re/untags as
5270    necessary
5271
5272    :param value:
5273        An Asn1Value object
5274
5275    :param params:
5276        A dict of spec params
5277
5278    :return:
5279        An Asn1Value that is properly tagged
5280    """
5281
5282    _tag_type_to_explicit_implicit(params)
5283
5284    retag = False
5285    if 'implicit' not in params:
5286        if value.implicit is not False:
5287            retag = True
5288    else:
5289        if isinstance(params['implicit'], tuple):
5290            class_, tag = params['implicit']
5291        else:
5292            tag = params['implicit']
5293            class_ = 'context'
5294        if value.implicit is False:
5295            retag = True
5296        elif value.class_ != CLASS_NAME_TO_NUM_MAP[class_] or value.tag != tag:
5297            retag = True
5298
5299    if params.get('explicit') != value.explicit:
5300        retag = True
5301
5302    if retag:
5303        return value.retag(params)
5304    return value
5305
5306
5307def _build_id_tuple(params, spec):
5308    """
5309    Builds a 2-element tuple used to identify fields by grabbing the class_
5310    and tag from an Asn1Value class and the params dict being passed to it
5311
5312    :param params:
5313        A dict of params to pass to spec
5314
5315    :param spec:
5316        An Asn1Value class
5317
5318    :return:
5319        A 2-element integer tuple in the form (class_, tag)
5320    """
5321
5322    # Handle situations where the spec is not known at setup time
5323    if spec is None:
5324        return (None, None)
5325
5326    required_class = spec.class_
5327    required_tag = spec.tag
5328
5329    _tag_type_to_explicit_implicit(params)
5330
5331    if 'explicit' in params:
5332        if isinstance(params['explicit'], tuple):
5333            required_class, required_tag = params['explicit']
5334        else:
5335            required_class = 2
5336            required_tag = params['explicit']
5337    elif 'implicit' in params:
5338        if isinstance(params['implicit'], tuple):
5339            required_class, required_tag = params['implicit']
5340        else:
5341            required_class = 2
5342            required_tag = params['implicit']
5343    if required_class is not None and not isinstance(required_class, int_types):
5344        required_class = CLASS_NAME_TO_NUM_MAP[required_class]
5345
5346    required_class = params.get('class_', required_class)
5347    required_tag = params.get('tag', required_tag)
5348
5349    return (required_class, required_tag)
5350
5351
5352def _int_to_bit_tuple(value, bits):
5353    """
5354    Format value as a tuple of 1s and 0s.
5355
5356    :param value:
5357        A non-negative integer to format
5358
5359    :param bits:
5360        Number of bits in the output
5361
5362    :return:
5363        A tuple of 1s and 0s with bits members.
5364    """
5365
5366    if not value and not bits:
5367        return ()
5368
5369    result = tuple(map(int, format(value, '0{0}b'.format(bits))))
5370    if len(result) != bits:
5371        raise ValueError('Result too large: {0} > {1}'.format(len(result), bits))
5372
5373    return result
5374
5375
5376_UNIVERSAL_SPECS = {
5377    1: Boolean,
5378    2: Integer,
5379    3: BitString,
5380    4: OctetString,
5381    5: Null,
5382    6: ObjectIdentifier,
5383    7: ObjectDescriptor,
5384    8: InstanceOf,
5385    9: Real,
5386    10: Enumerated,
5387    11: EmbeddedPdv,
5388    12: UTF8String,
5389    13: RelativeOid,
5390    16: Sequence,
5391    17: Set,
5392    18: NumericString,
5393    19: PrintableString,
5394    20: TeletexString,
5395    21: VideotexString,
5396    22: IA5String,
5397    23: UTCTime,
5398    24: GeneralizedTime,
5399    25: GraphicString,
5400    26: VisibleString,
5401    27: GeneralString,
5402    28: UniversalString,
5403    29: CharacterString,
5404    30: BMPString
5405}
5406
5407
5408def _build(class_, method, tag, header, contents, trailer, spec=None, spec_params=None, nested_spec=None):
5409    """
5410    Builds an Asn1Value object generically, or using a spec with optional params
5411
5412    :param class_:
5413        An integer representing the ASN.1 class
5414
5415    :param method:
5416        An integer representing the ASN.1 method
5417
5418    :param tag:
5419        An integer representing the ASN.1 tag
5420
5421    :param header:
5422        A byte string of the ASN.1 header (class, method, tag, length)
5423
5424    :param contents:
5425        A byte string of the ASN.1 value
5426
5427    :param trailer:
5428        A byte string of any ASN.1 trailer (only used by indefinite length encodings)
5429
5430    :param spec:
5431        A class derived from Asn1Value that defines what class_ and tag the
5432        value should have, and the semantics of the encoded value. The
5433        return value will be of this type. If omitted, the encoded value
5434        will be decoded using the standard universal tag based on the
5435        encoded tag number.
5436
5437    :param spec_params:
5438        A dict of params to pass to the spec object
5439
5440    :param nested_spec:
5441        For certain Asn1Value classes (such as OctetString and BitString), the
5442        contents can be further parsed and interpreted as another Asn1Value.
5443        This parameter controls the spec for that sub-parsing.
5444
5445    :return:
5446        An object of the type spec, or if not specified, a child of Asn1Value
5447    """
5448
5449    if spec_params is not None:
5450        _tag_type_to_explicit_implicit(spec_params)
5451
5452    if header is None:
5453        return VOID
5454
5455    header_set = False
5456
5457    # If an explicit specification was passed in, make sure it matches
5458    if spec is not None:
5459        # If there is explicit tagging and contents, we have to split
5460        # the header and trailer off before we do the parsing
5461        no_explicit = spec_params and 'no_explicit' in spec_params
5462        if not no_explicit and (spec.explicit or (spec_params and 'explicit' in spec_params)):
5463            if spec_params:
5464                value = spec(**spec_params)
5465            else:
5466                value = spec()
5467            original_explicit = value.explicit
5468            explicit_info = reversed(original_explicit)
5469            parsed_class = class_
5470            parsed_method = method
5471            parsed_tag = tag
5472            to_parse = contents
5473            explicit_header = header
5474            explicit_trailer = trailer or b''
5475            for expected_class, expected_tag in explicit_info:
5476                if parsed_class != expected_class:
5477                    raise ValueError(unwrap(
5478                        '''
5479                        Error parsing %s - explicitly-tagged class should have been
5480                        %s, but %s was found
5481                        ''',
5482                        type_name(value),
5483                        CLASS_NUM_TO_NAME_MAP.get(expected_class),
5484                        CLASS_NUM_TO_NAME_MAP.get(parsed_class, parsed_class)
5485                    ))
5486                if parsed_method != 1:
5487                    raise ValueError(unwrap(
5488                        '''
5489                        Error parsing %s - explicitly-tagged method should have
5490                        been %s, but %s was found
5491                        ''',
5492                        type_name(value),
5493                        METHOD_NUM_TO_NAME_MAP.get(1),
5494                        METHOD_NUM_TO_NAME_MAP.get(parsed_method, parsed_method)
5495                    ))
5496                if parsed_tag != expected_tag:
5497                    raise ValueError(unwrap(
5498                        '''
5499                        Error parsing %s - explicitly-tagged tag should have been
5500                        %s, but %s was found
5501                        ''',
5502                        type_name(value),
5503                        expected_tag,
5504                        parsed_tag
5505                    ))
5506                info, _ = _parse(to_parse, len(to_parse))
5507                parsed_class, parsed_method, parsed_tag, parsed_header, to_parse, parsed_trailer = info
5508
5509                if not isinstance(value, Choice):
5510                    explicit_header += parsed_header
5511                    explicit_trailer = parsed_trailer + explicit_trailer
5512
5513            value = _build(*info, spec=spec, spec_params={'no_explicit': True})
5514            value._header = explicit_header
5515            value._trailer = explicit_trailer
5516            value.explicit = original_explicit
5517            header_set = True
5518        else:
5519            if spec_params:
5520                value = spec(contents=contents, **spec_params)
5521            else:
5522                value = spec(contents=contents)
5523
5524            if spec is Any:
5525                pass
5526
5527            elif isinstance(value, Choice):
5528                value.validate(class_, tag, contents)
5529                try:
5530                    # Force parsing the Choice now
5531                    value.contents = header + value.contents
5532                    header = b''
5533                    value.parse()
5534                except (ValueError, TypeError) as e:
5535                    args = e.args[1:]
5536                    e.args = (e.args[0] + '\n    while parsing %s' % type_name(value),) + args
5537                    raise e
5538
5539            else:
5540                if class_ != value.class_:
5541                    raise ValueError(unwrap(
5542                        '''
5543                        Error parsing %s - class should have been %s, but %s was
5544                        found
5545                        ''',
5546                        type_name(value),
5547                        CLASS_NUM_TO_NAME_MAP.get(value.class_),
5548                        CLASS_NUM_TO_NAME_MAP.get(class_, class_)
5549                    ))
5550                if method != value.method:
5551                    # Allow parsing a primitive method as constructed if the value
5552                    # is indefinite length. This is to allow parsing BER.
5553                    ber_indef = method == 1 and value.method == 0 and trailer == b'\x00\x00'
5554                    if not ber_indef or not isinstance(value, Constructable):
5555                        raise ValueError(unwrap(
5556                            '''
5557                            Error parsing %s - method should have been %s, but %s was found
5558                            ''',
5559                            type_name(value),
5560                            METHOD_NUM_TO_NAME_MAP.get(value.method),
5561                            METHOD_NUM_TO_NAME_MAP.get(method, method)
5562                        ))
5563                    else:
5564                        value.method = method
5565                        value._indefinite = True
5566                if tag != value.tag:
5567                    if isinstance(value._bad_tag, tuple):
5568                        is_bad_tag = tag in value._bad_tag
5569                    else:
5570                        is_bad_tag = tag == value._bad_tag
5571                    if not is_bad_tag:
5572                        raise ValueError(unwrap(
5573                            '''
5574                            Error parsing %s - tag should have been %s, but %s was found
5575                            ''',
5576                            type_name(value),
5577                            value.tag,
5578                            tag
5579                        ))
5580
5581    # For explicitly tagged, un-speced parsings, we use a generic container
5582    # since we will be parsing the contents and discarding the outer object
5583    # anyway a little further on
5584    elif spec_params and 'explicit' in spec_params:
5585        original_value = Asn1Value(contents=contents, **spec_params)
5586        original_explicit = original_value.explicit
5587
5588        to_parse = contents
5589        explicit_header = header
5590        explicit_trailer = trailer or b''
5591        for expected_class, expected_tag in reversed(original_explicit):
5592            info, _ = _parse(to_parse, len(to_parse))
5593            _, _, _, parsed_header, to_parse, parsed_trailer = info
5594            explicit_header += parsed_header
5595            explicit_trailer = parsed_trailer + explicit_trailer
5596        value = _build(*info, spec=spec, spec_params={'no_explicit': True})
5597        value._header = header + value._header
5598        value._trailer += trailer or b''
5599        value.explicit = original_explicit
5600        header_set = True
5601
5602    # If no spec was specified, allow anything and just process what
5603    # is in the input data
5604    else:
5605        if tag not in _UNIVERSAL_SPECS:
5606            raise ValueError(unwrap(
5607                '''
5608                Unknown element - %s class, %s method, tag %s
5609                ''',
5610                CLASS_NUM_TO_NAME_MAP.get(class_),
5611                METHOD_NUM_TO_NAME_MAP.get(method),
5612                tag
5613            ))
5614
5615        spec = _UNIVERSAL_SPECS[tag]
5616
5617        value = spec(contents=contents, class_=class_)
5618        ber_indef = method == 1 and value.method == 0 and trailer == b'\x00\x00'
5619        if ber_indef and isinstance(value, Constructable):
5620            value._indefinite = True
5621        value.method = method
5622
5623    if not header_set:
5624        value._header = header
5625        value._trailer = trailer or b''
5626
5627    # Destroy any default value that our contents have overwritten
5628    value._native = None
5629
5630    if nested_spec:
5631        try:
5632            value.parse(nested_spec)
5633        except (ValueError, TypeError) as e:
5634            args = e.args[1:]
5635            e.args = (e.args[0] + '\n    while parsing %s' % type_name(value),) + args
5636            raise e
5637
5638    return value
5639
5640
5641def _parse_build(encoded_data, pointer=0, spec=None, spec_params=None, strict=False):
5642    """
5643    Parses a byte string generically, or using a spec with optional params
5644
5645    :param encoded_data:
5646        A byte string that contains BER-encoded data
5647
5648    :param pointer:
5649        The index in the byte string to parse from
5650
5651    :param spec:
5652        A class derived from Asn1Value that defines what class_ and tag the
5653        value should have, and the semantics of the encoded value. The
5654        return value will be of this type. If omitted, the encoded value
5655        will be decoded using the standard universal tag based on the
5656        encoded tag number.
5657
5658    :param spec_params:
5659        A dict of params to pass to the spec object
5660
5661    :param strict:
5662        A boolean indicating if trailing data should be forbidden - if so, a
5663        ValueError will be raised when trailing data exists
5664
5665    :return:
5666        A 2-element tuple:
5667         - 0: An object of the type spec, or if not specified, a child of Asn1Value
5668         - 1: An integer indicating how many bytes were consumed
5669    """
5670
5671    encoded_len = len(encoded_data)
5672    info, new_pointer = _parse(encoded_data, encoded_len, pointer)
5673    if strict and new_pointer != pointer + encoded_len:
5674        extra_bytes = pointer + encoded_len - new_pointer
5675        raise ValueError('Extra data - %d bytes of trailing data were provided' % extra_bytes)
5676    return (_build(*info, spec=spec, spec_params=spec_params), new_pointer)
5677