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        if force:
4117            self._set_contents(force=force)
4118
4119        if self._fields and self.children is not None:
4120            for index, (field_name, _, params) in enumerate(self._fields):
4121                if self.children[index] is not VOID:
4122                    continue
4123                if 'default' in params or 'optional' in params:
4124                    continue
4125                raise ValueError(unwrap(
4126                    '''
4127                    Field "%s" is missing from structure
4128                    ''',
4129                    field_name
4130                ))
4131
4132        return Asn1Value.dump(self)
4133
4134
4135class SequenceOf(Asn1Value):
4136    """
4137    Represents a sequence (ordered) of a single type of values from ASN.1 as a
4138    Python object with a list-like interface
4139    """
4140
4141    tag = 16
4142
4143    class_ = 0
4144    method = 1
4145
4146    # A list of child objects
4147    children = None
4148
4149    # SequenceOf overrides .contents to be a property so that the mutated state
4150    # of child objects can be checked to ensure everything is up-to-date
4151    _contents = None
4152
4153    # Variable to track if the object has been mutated
4154    _mutated = False
4155
4156    # An Asn1Value class to use when parsing children
4157    _child_spec = None
4158
4159    def __init__(self, value=None, default=None, contents=None, spec=None, **kwargs):
4160        """
4161        Allows setting child objects and the _child_spec via the spec parameter
4162        before passing everything else along to Asn1Value.__init__()
4163
4164        :param value:
4165            A native Python datatype to initialize the object value with
4166
4167        :param default:
4168            The default value if no value is specified
4169
4170        :param contents:
4171            A byte string of the encoded contents of the value
4172
4173        :param spec:
4174            A class derived from Asn1Value to use to parse children
4175        """
4176
4177        if spec:
4178            self._child_spec = spec
4179
4180        Asn1Value.__init__(self, **kwargs)
4181
4182        try:
4183            if contents is not None:
4184                self.contents = contents
4185            else:
4186                if value is None and default is not None:
4187                    value = default
4188
4189                if value is not None:
4190                    for index, child in enumerate(value):
4191                        self.__setitem__(index, child)
4192
4193                    # Make sure a blank list is serialized
4194                    if self.contents is None:
4195                        self._set_contents()
4196
4197        except (ValueError, TypeError) as e:
4198            args = e.args[1:]
4199            e.args = (e.args[0] + '\n    while constructing %s' % type_name(self),) + args
4200            raise e
4201
4202    @property
4203    def contents(self):
4204        """
4205        :return:
4206            A byte string of the DER-encoded contents of the sequence
4207        """
4208
4209        if self.children is None:
4210            return self._contents
4211
4212        if self._is_mutated():
4213            self._set_contents()
4214
4215        return self._contents
4216
4217    @contents.setter
4218    def contents(self, value):
4219        """
4220        :param value:
4221            A byte string of the DER-encoded contents of the sequence
4222        """
4223
4224        self._contents = value
4225
4226    def _is_mutated(self):
4227        """
4228        :return:
4229            A boolean - if the sequence or any children (recursively) have been
4230            mutated
4231        """
4232
4233        mutated = self._mutated
4234        if self.children is not None:
4235            for child in self.children:
4236                if isinstance(child, Sequence) or isinstance(child, SequenceOf):
4237                    mutated = mutated or child._is_mutated()
4238
4239        return mutated
4240
4241    def _lazy_child(self, index):
4242        """
4243        Builds a child object if the child has only been parsed into a tuple so far
4244        """
4245
4246        child = self.children[index]
4247        if child.__class__ == tuple:
4248            child = _build(*child)
4249            self.children[index] = child
4250        return child
4251
4252    def _make_value(self, value):
4253        """
4254        Constructs a _child_spec value from a native Python data type, or
4255        an appropriate Asn1Value object
4256
4257        :param value:
4258            A native Python value, or some child of Asn1Value
4259
4260        :return:
4261            An object of type _child_spec
4262        """
4263
4264        if isinstance(value, self._child_spec):
4265            new_value = value
4266
4267        elif issubclass(self._child_spec, Any):
4268            if isinstance(value, Asn1Value):
4269                new_value = value
4270            else:
4271                raise ValueError(unwrap(
4272                    '''
4273                    Can not set a native python value to %s where the
4274                    _child_spec is Any - value must be an instance of Asn1Value
4275                    ''',
4276                    type_name(self)
4277                ))
4278
4279        elif issubclass(self._child_spec, Choice):
4280            if not isinstance(value, Asn1Value):
4281                raise ValueError(unwrap(
4282                    '''
4283                    Can not set a native python value to %s where the
4284                    _child_spec is the choice type %s - value must be an
4285                    instance of Asn1Value
4286                    ''',
4287                    type_name(self),
4288                    self._child_spec.__name__
4289                ))
4290            if not isinstance(value, self._child_spec):
4291                wrapper = self._child_spec()
4292                wrapper.validate(value.class_, value.tag, value.contents)
4293                wrapper._parsed = value
4294                value = wrapper
4295            new_value = value
4296
4297        else:
4298            return self._child_spec(value=value)
4299
4300        params = {}
4301        if self._child_spec.explicit:
4302            params['explicit'] = self._child_spec.explicit
4303        if self._child_spec.implicit:
4304            params['implicit'] = (self._child_spec.class_, self._child_spec.tag)
4305        return _fix_tagging(new_value, params)
4306
4307    def __len__(self):
4308        """
4309        :return:
4310            An integer
4311        """
4312        # We inline this checks to prevent method invocation each time
4313        if self.children is None:
4314            self._parse_children()
4315
4316        return len(self.children)
4317
4318    def __getitem__(self, key):
4319        """
4320        Allows accessing children via index
4321
4322        :param key:
4323            Integer index of child
4324        """
4325
4326        # We inline this checks to prevent method invocation each time
4327        if self.children is None:
4328            self._parse_children()
4329
4330        return self._lazy_child(key)
4331
4332    def __setitem__(self, key, value):
4333        """
4334        Allows overriding a child via index
4335
4336        :param key:
4337            Integer index of child
4338
4339        :param value:
4340            Native python datatype that will be passed to _child_spec to create
4341            new child object
4342        """
4343
4344        # We inline this checks to prevent method invocation each time
4345        if self.children is None:
4346            self._parse_children()
4347
4348        new_value = self._make_value(value)
4349
4350        # If adding at the end, create a space for the new value
4351        if key == len(self.children):
4352            self.children.append(None)
4353            if self._native is not None:
4354                self._native.append(None)
4355
4356        self.children[key] = new_value
4357
4358        if self._native is not None:
4359            self._native[key] = self.children[key].native
4360
4361        self._mutated = True
4362
4363    def __delitem__(self, key):
4364        """
4365        Allows removing a child via index
4366
4367        :param key:
4368            Integer index of child
4369        """
4370
4371        # We inline this checks to prevent method invocation each time
4372        if self.children is None:
4373            self._parse_children()
4374
4375        self.children.pop(key)
4376        if self._native is not None:
4377            self._native.pop(key)
4378
4379        self._mutated = True
4380
4381    def __iter__(self):
4382        """
4383        :return:
4384            An iter() of child objects
4385        """
4386
4387        # We inline this checks to prevent method invocation each time
4388        if self.children is None:
4389            self._parse_children()
4390
4391        for index in range(0, len(self.children)):
4392            yield self._lazy_child(index)
4393
4394    def __contains__(self, item):
4395        """
4396        :param item:
4397            An object of the type cls._child_spec
4398
4399        :return:
4400            A boolean if the item is contained in this SequenceOf
4401        """
4402
4403        if item is None or item is VOID:
4404            return False
4405
4406        if not isinstance(item, self._child_spec):
4407            raise TypeError(unwrap(
4408                '''
4409                Checking membership in %s is only available for instances of
4410                %s, not %s
4411                ''',
4412                type_name(self),
4413                type_name(self._child_spec),
4414                type_name(item)
4415            ))
4416
4417        for child in self:
4418            if child == item:
4419                return True
4420
4421        return False
4422
4423    def append(self, value):
4424        """
4425        Allows adding a child to the end of the sequence
4426
4427        :param value:
4428            Native python datatype that will be passed to _child_spec to create
4429            new child object
4430        """
4431
4432        # We inline this checks to prevent method invocation each time
4433        if self.children is None:
4434            self._parse_children()
4435
4436        self.children.append(self._make_value(value))
4437
4438        if self._native is not None:
4439            self._native.append(self.children[-1].native)
4440
4441        self._mutated = True
4442
4443    def _set_contents(self, force=False):
4444        """
4445        Encodes all child objects into the contents for this object
4446
4447        :param force:
4448            Ensure all contents are in DER format instead of possibly using
4449            cached BER-encoded data
4450        """
4451
4452        if self.children is None:
4453            self._parse_children()
4454
4455        contents = BytesIO()
4456        for child in self:
4457            contents.write(child.dump(force=force))
4458        self._contents = contents.getvalue()
4459        self._header = None
4460        if self._trailer != b'':
4461            self._trailer = b''
4462
4463    def _parse_children(self, recurse=False):
4464        """
4465        Parses the contents and generates Asn1Value objects based on the
4466        definitions from _child_spec.
4467
4468        :param recurse:
4469            If child objects that are Sequence or SequenceOf objects should
4470            be recursively parsed
4471
4472        :raises:
4473            ValueError - when an error occurs parsing child objects
4474        """
4475
4476        try:
4477            self.children = []
4478            if self._contents is None:
4479                return
4480            contents_length = len(self._contents)
4481            child_pointer = 0
4482            while child_pointer < contents_length:
4483                parts, child_pointer = _parse(self._contents, contents_length, pointer=child_pointer)
4484                if self._child_spec:
4485                    child = parts + (self._child_spec,)
4486                else:
4487                    child = parts
4488                if recurse:
4489                    child = _build(*child)
4490                    if isinstance(child, (Sequence, SequenceOf)):
4491                        child._parse_children(recurse=True)
4492                self.children.append(child)
4493        except (ValueError, TypeError) as e:
4494            self.children = None
4495            args = e.args[1:]
4496            e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
4497            raise e
4498
4499    def spec(self):
4500        """
4501        Determines the spec to use for child values.
4502
4503        :return:
4504            A child class of asn1crypto.core.Asn1Value that child values must be
4505            encoded using
4506        """
4507
4508        return self._child_spec
4509
4510    @property
4511    def native(self):
4512        """
4513        The native Python datatype representation of this value
4514
4515        :return:
4516            A list or None. If a list, all child values are recursively
4517            converted to native representation also.
4518        """
4519
4520        if self.contents is None:
4521            return None
4522
4523        if self._native is None:
4524            if self.children is None:
4525                self._parse_children(recurse=True)
4526            try:
4527                self._native = [child.native for child in self]
4528            except (ValueError, TypeError) as e:
4529                args = e.args[1:]
4530                e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
4531                raise e
4532        return self._native
4533
4534    def _copy(self, other, copy_func):
4535        """
4536        Copies the contents of another SequenceOf object to itself
4537
4538        :param object:
4539            Another instance of the same class
4540
4541        :param copy_func:
4542            An reference of copy.copy() or copy.deepcopy() to use when copying
4543            lists, dicts and objects
4544        """
4545
4546        super(SequenceOf, self)._copy(other, copy_func)
4547        if self.children is not None:
4548            self.children = []
4549            for child in other.children:
4550                if child.__class__ == tuple:
4551                    self.children.append(child)
4552                else:
4553                    self.children.append(child.copy())
4554
4555    def debug(self, nest_level=1):
4556        """
4557        Show the binary data and parsed data in a tree structure
4558        """
4559
4560        if self.children is None:
4561            self._parse_children()
4562
4563        prefix = '  ' * nest_level
4564        _basic_debug(prefix, self)
4565        for child in self:
4566            child.debug(nest_level + 1)
4567
4568    def dump(self, force=False):
4569        """
4570        Encodes the value using DER
4571
4572        :param force:
4573            If the encoded contents already exist, clear them and regenerate
4574            to ensure they are in DER format instead of BER format
4575
4576        :return:
4577            A byte string of the DER-encoded value
4578        """
4579
4580        # If the length is indefinite, force the re-encoding
4581        if self._header is not None and self._header[-1:] == b'\x80':
4582            force = True
4583
4584        if force:
4585            self._set_contents(force=force)
4586
4587        return Asn1Value.dump(self)
4588
4589
4590class Set(Sequence):
4591    """
4592    Represents a set of fields (unordered) from ASN.1 as a Python object with a
4593    dict-like interface
4594    """
4595
4596    method = 1
4597    class_ = 0
4598    tag = 17
4599
4600    # A dict of 2-element tuples in the form (class_, tag) as keys and integers
4601    # as values that are the index of the field in _fields
4602    _field_ids = None
4603
4604    def _setup(self):
4605        """
4606        Generates _field_map, _field_ids and _oid_nums for use in parsing
4607        """
4608
4609        cls = self.__class__
4610        cls._field_map = {}
4611        cls._field_ids = {}
4612        cls._precomputed_specs = []
4613        for index, field in enumerate(cls._fields):
4614            if len(field) < 3:
4615                field = field + ({},)
4616                cls._fields[index] = field
4617            cls._field_map[field[0]] = index
4618            cls._field_ids[_build_id_tuple(field[2], field[1])] = index
4619
4620        if cls._oid_pair is not None:
4621            cls._oid_nums = (cls._field_map[cls._oid_pair[0]], cls._field_map[cls._oid_pair[1]])
4622
4623        for index, field in enumerate(cls._fields):
4624            has_callback = cls._spec_callbacks is not None and field[0] in cls._spec_callbacks
4625            is_mapped_oid = cls._oid_nums is not None and cls._oid_nums[1] == index
4626            if has_callback or is_mapped_oid:
4627                cls._precomputed_specs.append(None)
4628            else:
4629                cls._precomputed_specs.append((field[0], field[1], field[1], field[2], None))
4630
4631    def _parse_children(self, recurse=False):
4632        """
4633        Parses the contents and generates Asn1Value objects based on the
4634        definitions from _fields.
4635
4636        :param recurse:
4637            If child objects that are Sequence or SequenceOf objects should
4638            be recursively parsed
4639
4640        :raises:
4641            ValueError - when an error occurs parsing child objects
4642        """
4643
4644        cls = self.__class__
4645        if self._contents is None:
4646            if self._fields:
4647                self.children = [VOID] * len(self._fields)
4648                for index, (_, _, params) in enumerate(self._fields):
4649                    if 'default' in params:
4650                        if cls._precomputed_specs[index]:
4651                            field_name, field_spec, value_spec, field_params, _ = cls._precomputed_specs[index]
4652                        else:
4653                            field_name, field_spec, value_spec, field_params, _ = self._determine_spec(index)
4654                        self.children[index] = self._make_value(field_name, field_spec, value_spec, field_params, None)
4655            return
4656
4657        try:
4658            child_map = {}
4659            contents_length = len(self.contents)
4660            child_pointer = 0
4661            seen_field = 0
4662            while child_pointer < contents_length:
4663                parts, child_pointer = _parse(self.contents, contents_length, pointer=child_pointer)
4664
4665                id_ = (parts[0], parts[2])
4666
4667                field = self._field_ids.get(id_)
4668                if field is None:
4669                    raise ValueError(unwrap(
4670                        '''
4671                        Data for field %s (%s class, %s method, tag %s) does
4672                        not match any of the field definitions
4673                        ''',
4674                        seen_field,
4675                        CLASS_NUM_TO_NAME_MAP.get(parts[0]),
4676                        METHOD_NUM_TO_NAME_MAP.get(parts[1]),
4677                        parts[2],
4678                    ))
4679
4680                _, field_spec, value_spec, field_params, spec_override = (
4681                    cls._precomputed_specs[field] or self._determine_spec(field))
4682
4683                if field_spec is None or (spec_override and issubclass(field_spec, Any)):
4684                    field_spec = value_spec
4685                    spec_override = None
4686
4687                if spec_override:
4688                    child = parts + (field_spec, field_params, value_spec)
4689                else:
4690                    child = parts + (field_spec, field_params)
4691
4692                if recurse:
4693                    child = _build(*child)
4694                    if isinstance(child, (Sequence, SequenceOf)):
4695                        child._parse_children(recurse=True)
4696
4697                child_map[field] = child
4698                seen_field += 1
4699
4700            total_fields = len(self._fields)
4701
4702            for index in range(0, total_fields):
4703                if index in child_map:
4704                    continue
4705
4706                name, field_spec, value_spec, field_params, spec_override = (
4707                    cls._precomputed_specs[index] or self._determine_spec(index))
4708
4709                if field_spec is None or (spec_override and issubclass(field_spec, Any)):
4710                    field_spec = value_spec
4711                    spec_override = None
4712
4713                missing = False
4714
4715                if not field_params:
4716                    missing = True
4717                elif 'optional' not in field_params and 'default' not in field_params:
4718                    missing = True
4719                elif 'optional' in field_params:
4720                    child_map[index] = VOID
4721                elif 'default' in field_params:
4722                    child_map[index] = field_spec(**field_params)
4723
4724                if missing:
4725                    raise ValueError(unwrap(
4726                        '''
4727                        Missing required field "%s" from %s
4728                        ''',
4729                        name,
4730                        type_name(self)
4731                    ))
4732
4733            self.children = []
4734            for index in range(0, total_fields):
4735                self.children.append(child_map[index])
4736
4737        except (ValueError, TypeError) as e:
4738            args = e.args[1:]
4739            e.args = (e.args[0] + '\n    while parsing %s' % type_name(self),) + args
4740            raise e
4741
4742    def _set_contents(self, force=False):
4743        """
4744        Encodes all child objects into the contents for this object.
4745
4746        This method is overridden because a Set needs to be encoded by
4747        removing defaulted fields and then sorting the fields by tag.
4748
4749        :param force:
4750            Ensure all contents are in DER format instead of possibly using
4751            cached BER-encoded data
4752        """
4753
4754        if self.children is None:
4755            self._parse_children()
4756
4757        child_tag_encodings = []
4758        for index, child in enumerate(self.children):
4759            child_encoding = child.dump(force=force)
4760
4761            # Skip encoding defaulted children
4762            name, spec, field_params = self._fields[index]
4763            if 'default' in field_params:
4764                if spec(**field_params).dump() == child_encoding:
4765                    continue
4766
4767            child_tag_encodings.append((child.tag, child_encoding))
4768        child_tag_encodings.sort(key=lambda ct: ct[0])
4769
4770        self._contents = b''.join([ct[1] for ct in child_tag_encodings])
4771        self._header = None
4772        if self._trailer != b'':
4773            self._trailer = b''
4774
4775
4776class SetOf(SequenceOf):
4777    """
4778    Represents a set (unordered) of a single type of values from ASN.1 as a
4779    Python object with a list-like interface
4780    """
4781
4782    tag = 17
4783
4784    def _set_contents(self, force=False):
4785        """
4786        Encodes all child objects into the contents for this object.
4787
4788        This method is overridden because a SetOf needs to be encoded by
4789        sorting the child encodings.
4790
4791        :param force:
4792            Ensure all contents are in DER format instead of possibly using
4793            cached BER-encoded data
4794        """
4795
4796        if self.children is None:
4797            self._parse_children()
4798
4799        child_encodings = []
4800        for child in self:
4801            child_encodings.append(child.dump(force=force))
4802
4803        self._contents = b''.join(sorted(child_encodings))
4804        self._header = None
4805        if self._trailer != b'':
4806            self._trailer = b''
4807
4808
4809class EmbeddedPdv(Sequence):
4810    """
4811    A sequence structure
4812    """
4813
4814    tag = 11
4815
4816
4817class NumericString(AbstractString):
4818    """
4819    Represents a numeric string from ASN.1 as a Python unicode string
4820    """
4821
4822    tag = 18
4823    _encoding = 'latin1'
4824
4825
4826class PrintableString(AbstractString):
4827    """
4828    Represents a printable string from ASN.1 as a Python unicode string
4829    """
4830
4831    tag = 19
4832    _encoding = 'latin1'
4833
4834
4835class TeletexString(AbstractString):
4836    """
4837    Represents a teletex string from ASN.1 as a Python unicode string
4838    """
4839
4840    tag = 20
4841    _encoding = 'teletex'
4842
4843
4844class VideotexString(OctetString):
4845    """
4846    Represents a videotex string from ASN.1 as a Python byte string
4847    """
4848
4849    tag = 21
4850
4851
4852class IA5String(AbstractString):
4853    """
4854    Represents an IA5 string from ASN.1 as a Python unicode string
4855    """
4856
4857    tag = 22
4858    _encoding = 'ascii'
4859
4860
4861class AbstractTime(AbstractString):
4862    """
4863    Represents a time from ASN.1 as a Python datetime.datetime object
4864    """
4865
4866    @property
4867    def _parsed_time(self):
4868        """
4869        The parsed datetime string.
4870
4871        :raises:
4872            ValueError - when an invalid value is passed
4873
4874        :return:
4875            A dict with the parsed values
4876        """
4877
4878        string = str_cls(self)
4879
4880        m = self._TIMESTRING_RE.match(string)
4881        if not m:
4882            raise ValueError(unwrap(
4883                '''
4884                Error parsing %s to a %s
4885                ''',
4886                string,
4887                type_name(self),
4888            ))
4889
4890        groups = m.groupdict()
4891
4892        tz = None
4893        if groups['zulu']:
4894            tz = timezone.utc
4895        elif groups['dsign']:
4896            sign = 1 if groups['dsign'] == '+' else -1
4897            tz = create_timezone(sign * timedelta(
4898                hours=int(groups['dhour']),
4899                minutes=int(groups['dminute'] or 0)
4900            ))
4901
4902        if groups['fraction']:
4903            # Compute fraction in microseconds
4904            fract = Fraction(
4905                int(groups['fraction']),
4906                10 ** len(groups['fraction'])
4907            ) * 1000000
4908
4909            if groups['minute'] is None:
4910                fract *= 3600
4911            elif groups['second'] is None:
4912                fract *= 60
4913
4914            fract_usec = int(fract.limit_denominator(1))
4915
4916        else:
4917            fract_usec = 0
4918
4919        return {
4920            'year': int(groups['year']),
4921            'month': int(groups['month']),
4922            'day': int(groups['day']),
4923            'hour': int(groups['hour']),
4924            'minute': int(groups['minute'] or 0),
4925            'second': int(groups['second'] or 0),
4926            'tzinfo': tz,
4927            'fraction': fract_usec,
4928        }
4929
4930    @property
4931    def native(self):
4932        """
4933        The native Python datatype representation of this value
4934
4935        :return:
4936            A datetime.datetime object, asn1crypto.util.extended_datetime object or
4937            None. The datetime object is usually timezone aware. If it's naive, then
4938            it's in the sender's local time; see X.680 sect. 42.3
4939        """
4940
4941        if self.contents is None:
4942            return None
4943
4944        if self._native is None:
4945            parsed = self._parsed_time
4946
4947            fraction = parsed.pop('fraction', 0)
4948
4949            value = self._get_datetime(parsed)
4950
4951            if fraction:
4952                value += timedelta(microseconds=fraction)
4953
4954            self._native = value
4955
4956        return self._native
4957
4958
4959class UTCTime(AbstractTime):
4960    """
4961    Represents a UTC time from ASN.1 as a timezone aware Python datetime.datetime object
4962    """
4963
4964    tag = 23
4965
4966    # Regular expression for UTCTime as described in X.680 sect. 43 and ISO 8601
4967    _TIMESTRING_RE = re.compile(r'''
4968        ^
4969        # YYMMDD
4970        (?P<year>\d{2})
4971        (?P<month>\d{2})
4972        (?P<day>\d{2})
4973
4974        # hhmm or hhmmss
4975        (?P<hour>\d{2})
4976        (?P<minute>\d{2})
4977        (?P<second>\d{2})?
4978
4979        # Matches nothing, needed because GeneralizedTime uses this.
4980        (?P<fraction>)
4981
4982        # Z or [-+]hhmm
4983        (?:
4984            (?P<zulu>Z)
4985            |
4986            (?:
4987                (?P<dsign>[-+])
4988                (?P<dhour>\d{2})
4989                (?P<dminute>\d{2})
4990            )
4991        )
4992        $
4993    ''', re.X)
4994
4995    def set(self, value):
4996        """
4997        Sets the value of the object
4998
4999        :param value:
5000            A unicode string or a datetime.datetime object
5001
5002        :raises:
5003            ValueError - when an invalid value is passed
5004        """
5005
5006        if isinstance(value, datetime):
5007            if not value.tzinfo:
5008                raise ValueError('Must be timezone aware')
5009
5010            # Convert value to UTC.
5011            value = value.astimezone(utc_with_dst)
5012
5013            if not 1950 <= value.year <= 2049:
5014                raise ValueError('Year of the UTCTime is not in range [1950, 2049], use GeneralizedTime instead')
5015
5016            value = value.strftime('%y%m%d%H%M%SZ')
5017            if _PY2:
5018                value = value.decode('ascii')
5019
5020        AbstractString.set(self, value)
5021        # Set it to None and let the class take care of converting the next
5022        # time that .native is called
5023        self._native = None
5024
5025    def _get_datetime(self, parsed):
5026        """
5027        Create a datetime object from the parsed time.
5028
5029        :return:
5030            An aware datetime.datetime object
5031        """
5032
5033        # X.680 only specifies that UTCTime is not using a century.
5034        # So "18" could as well mean 2118 or 1318.
5035        # X.509 and CMS specify to use UTCTime for years earlier than 2050.
5036        # Assume that UTCTime is only used for years [1950, 2049].
5037        if parsed['year'] < 50:
5038            parsed['year'] += 2000
5039        else:
5040            parsed['year'] += 1900
5041
5042        return datetime(**parsed)
5043
5044
5045class GeneralizedTime(AbstractTime):
5046    """
5047    Represents a generalized time from ASN.1 as a Python datetime.datetime
5048    object or asn1crypto.util.extended_datetime object in UTC
5049    """
5050
5051    tag = 24
5052
5053    # Regular expression for GeneralizedTime as described in X.680 sect. 42 and ISO 8601
5054    _TIMESTRING_RE = re.compile(r'''
5055        ^
5056        # YYYYMMDD
5057        (?P<year>\d{4})
5058        (?P<month>\d{2})
5059        (?P<day>\d{2})
5060
5061        # hh or hhmm or hhmmss
5062        (?P<hour>\d{2})
5063        (?:
5064            (?P<minute>\d{2})
5065            (?P<second>\d{2})?
5066        )?
5067
5068        # Optional fraction; [.,]dddd (one or more decimals)
5069        # If Seconds are given, it's fractions of Seconds.
5070        # Else if Minutes are given, it's fractions of Minutes.
5071        # Else it's fractions of Hours.
5072        (?:
5073            [,.]
5074            (?P<fraction>\d+)
5075        )?
5076
5077        # Optional timezone. If left out, the time is in local time.
5078        # Z or [-+]hh or [-+]hhmm
5079        (?:
5080            (?P<zulu>Z)
5081            |
5082            (?:
5083                (?P<dsign>[-+])
5084                (?P<dhour>\d{2})
5085                (?P<dminute>\d{2})?
5086            )
5087        )?
5088        $
5089    ''', re.X)
5090
5091    def set(self, value):
5092        """
5093        Sets the value of the object
5094
5095        :param value:
5096            A unicode string, a datetime.datetime object or an
5097            asn1crypto.util.extended_datetime object
5098
5099        :raises:
5100            ValueError - when an invalid value is passed
5101        """
5102
5103        if isinstance(value, (datetime, extended_datetime)):
5104            if not value.tzinfo:
5105                raise ValueError('Must be timezone aware')
5106
5107            # Convert value to UTC.
5108            value = value.astimezone(utc_with_dst)
5109
5110            if value.microsecond:
5111                fraction = '.' + str(value.microsecond).zfill(6).rstrip('0')
5112            else:
5113                fraction = ''
5114
5115            value = value.strftime('%Y%m%d%H%M%S') + fraction + 'Z'
5116            if _PY2:
5117                value = value.decode('ascii')
5118
5119        AbstractString.set(self, value)
5120        # Set it to None and let the class take care of converting the next
5121        # time that .native is called
5122        self._native = None
5123
5124    def _get_datetime(self, parsed):
5125        """
5126        Create a datetime object from the parsed time.
5127
5128        :return:
5129            A datetime.datetime object or asn1crypto.util.extended_datetime object.
5130            It may or may not be aware.
5131        """
5132
5133        if parsed['year'] == 0:
5134            # datetime does not support year 0. Use extended_datetime instead.
5135            return extended_datetime(**parsed)
5136        else:
5137            return datetime(**parsed)
5138
5139
5140class GraphicString(AbstractString):
5141    """
5142    Represents a graphic string from ASN.1 as a Python unicode string
5143    """
5144
5145    tag = 25
5146    # This is technically not correct since this type can contain any charset
5147    _encoding = 'latin1'
5148
5149
5150class VisibleString(AbstractString):
5151    """
5152    Represents a visible string from ASN.1 as a Python unicode string
5153    """
5154
5155    tag = 26
5156    _encoding = 'latin1'
5157
5158
5159class GeneralString(AbstractString):
5160    """
5161    Represents a general string from ASN.1 as a Python unicode string
5162    """
5163
5164    tag = 27
5165    # This is technically not correct since this type can contain any charset
5166    _encoding = 'latin1'
5167
5168
5169class UniversalString(AbstractString):
5170    """
5171    Represents a universal string from ASN.1 as a Python unicode string
5172    """
5173
5174    tag = 28
5175    _encoding = 'utf-32-be'
5176
5177
5178class CharacterString(AbstractString):
5179    """
5180    Represents a character string from ASN.1 as a Python unicode string
5181    """
5182
5183    tag = 29
5184    # This is technically not correct since this type can contain any charset
5185    _encoding = 'latin1'
5186
5187
5188class BMPString(AbstractString):
5189    """
5190    Represents a BMP string from ASN.1 as a Python unicode string
5191    """
5192
5193    tag = 30
5194    _encoding = 'utf-16-be'
5195
5196
5197def _basic_debug(prefix, self):
5198    """
5199    Prints out basic information about an Asn1Value object. Extracted for reuse
5200    among different classes that customize the debug information.
5201
5202    :param prefix:
5203        A unicode string of spaces to prefix output line with
5204
5205    :param self:
5206        The object to print the debugging information about
5207    """
5208
5209    print('%s%s Object #%s' % (prefix, type_name(self), id(self)))
5210    if self._header:
5211        print('%s  Header: 0x%s' % (prefix, binascii.hexlify(self._header or b'').decode('utf-8')))
5212
5213    has_header = self.method is not None and self.class_ is not None and self.tag is not None
5214    if has_header:
5215        method_name = METHOD_NUM_TO_NAME_MAP.get(self.method)
5216        class_name = CLASS_NUM_TO_NAME_MAP.get(self.class_)
5217
5218    if self.explicit is not None:
5219        for class_, tag in self.explicit:
5220            print(
5221                '%s    %s tag %s (explicitly tagged)' %
5222                (
5223                    prefix,
5224                    CLASS_NUM_TO_NAME_MAP.get(class_),
5225                    tag
5226                )
5227            )
5228        if has_header:
5229            print('%s      %s %s %s' % (prefix, method_name, class_name, self.tag))
5230
5231    elif self.implicit:
5232        if has_header:
5233            print('%s    %s %s tag %s (implicitly tagged)' % (prefix, method_name, class_name, self.tag))
5234
5235    elif has_header:
5236        print('%s    %s %s tag %s' % (prefix, method_name, class_name, self.tag))
5237
5238    if self._trailer:
5239        print('%s  Trailer: 0x%s' % (prefix, binascii.hexlify(self._trailer or b'').decode('utf-8')))
5240
5241    print('%s  Data: 0x%s' % (prefix, binascii.hexlify(self.contents or b'').decode('utf-8')))
5242
5243
5244def _tag_type_to_explicit_implicit(params):
5245    """
5246    Converts old-style "tag_type" and "tag" params to "explicit" and "implicit"
5247
5248    :param params:
5249        A dict of parameters to convert from tag_type/tag to explicit/implicit
5250    """
5251
5252    if 'tag_type' in params:
5253        if params['tag_type'] == 'explicit':
5254            params['explicit'] = (params.get('class', 2), params['tag'])
5255        elif params['tag_type'] == 'implicit':
5256            params['implicit'] = (params.get('class', 2), params['tag'])
5257        del params['tag_type']
5258        del params['tag']
5259        if 'class' in params:
5260            del params['class']
5261
5262
5263def _fix_tagging(value, params):
5264    """
5265    Checks if a value is properly tagged based on the spec, and re/untags as
5266    necessary
5267
5268    :param value:
5269        An Asn1Value object
5270
5271    :param params:
5272        A dict of spec params
5273
5274    :return:
5275        An Asn1Value that is properly tagged
5276    """
5277
5278    _tag_type_to_explicit_implicit(params)
5279
5280    retag = False
5281    if 'implicit' not in params:
5282        if value.implicit is not False:
5283            retag = True
5284    else:
5285        if isinstance(params['implicit'], tuple):
5286            class_, tag = params['implicit']
5287        else:
5288            tag = params['implicit']
5289            class_ = 'context'
5290        if value.implicit is False:
5291            retag = True
5292        elif value.class_ != CLASS_NAME_TO_NUM_MAP[class_] or value.tag != tag:
5293            retag = True
5294
5295    if params.get('explicit') != value.explicit:
5296        retag = True
5297
5298    if retag:
5299        return value.retag(params)
5300    return value
5301
5302
5303def _build_id_tuple(params, spec):
5304    """
5305    Builds a 2-element tuple used to identify fields by grabbing the class_
5306    and tag from an Asn1Value class and the params dict being passed to it
5307
5308    :param params:
5309        A dict of params to pass to spec
5310
5311    :param spec:
5312        An Asn1Value class
5313
5314    :return:
5315        A 2-element integer tuple in the form (class_, tag)
5316    """
5317
5318    # Handle situations where the spec is not known at setup time
5319    if spec is None:
5320        return (None, None)
5321
5322    required_class = spec.class_
5323    required_tag = spec.tag
5324
5325    _tag_type_to_explicit_implicit(params)
5326
5327    if 'explicit' in params:
5328        if isinstance(params['explicit'], tuple):
5329            required_class, required_tag = params['explicit']
5330        else:
5331            required_class = 2
5332            required_tag = params['explicit']
5333    elif 'implicit' in params:
5334        if isinstance(params['implicit'], tuple):
5335            required_class, required_tag = params['implicit']
5336        else:
5337            required_class = 2
5338            required_tag = params['implicit']
5339    if required_class is not None and not isinstance(required_class, int_types):
5340        required_class = CLASS_NAME_TO_NUM_MAP[required_class]
5341
5342    required_class = params.get('class_', required_class)
5343    required_tag = params.get('tag', required_tag)
5344
5345    return (required_class, required_tag)
5346
5347
5348def _int_to_bit_tuple(value, bits):
5349    """
5350    Format value as a tuple of 1s and 0s.
5351
5352    :param value:
5353        A non-negative integer to format
5354
5355    :param bits:
5356        Number of bits in the output
5357
5358    :return:
5359        A tuple of 1s and 0s with bits members.
5360    """
5361
5362    if not value and not bits:
5363        return ()
5364
5365    result = tuple(map(int, format(value, '0{0}b'.format(bits))))
5366    if len(result) != bits:
5367        raise ValueError('Result too large: {0} > {1}'.format(len(result), bits))
5368
5369    return result
5370
5371
5372_UNIVERSAL_SPECS = {
5373    1: Boolean,
5374    2: Integer,
5375    3: BitString,
5376    4: OctetString,
5377    5: Null,
5378    6: ObjectIdentifier,
5379    7: ObjectDescriptor,
5380    8: InstanceOf,
5381    9: Real,
5382    10: Enumerated,
5383    11: EmbeddedPdv,
5384    12: UTF8String,
5385    13: RelativeOid,
5386    16: Sequence,
5387    17: Set,
5388    18: NumericString,
5389    19: PrintableString,
5390    20: TeletexString,
5391    21: VideotexString,
5392    22: IA5String,
5393    23: UTCTime,
5394    24: GeneralizedTime,
5395    25: GraphicString,
5396    26: VisibleString,
5397    27: GeneralString,
5398    28: UniversalString,
5399    29: CharacterString,
5400    30: BMPString
5401}
5402
5403
5404def _build(class_, method, tag, header, contents, trailer, spec=None, spec_params=None, nested_spec=None):
5405    """
5406    Builds an Asn1Value object generically, or using a spec with optional params
5407
5408    :param class_:
5409        An integer representing the ASN.1 class
5410
5411    :param method:
5412        An integer representing the ASN.1 method
5413
5414    :param tag:
5415        An integer representing the ASN.1 tag
5416
5417    :param header:
5418        A byte string of the ASN.1 header (class, method, tag, length)
5419
5420    :param contents:
5421        A byte string of the ASN.1 value
5422
5423    :param trailer:
5424        A byte string of any ASN.1 trailer (only used by indefinite length encodings)
5425
5426    :param spec:
5427        A class derived from Asn1Value that defines what class_ and tag the
5428        value should have, and the semantics of the encoded value. The
5429        return value will be of this type. If omitted, the encoded value
5430        will be decoded using the standard universal tag based on the
5431        encoded tag number.
5432
5433    :param spec_params:
5434        A dict of params to pass to the spec object
5435
5436    :param nested_spec:
5437        For certain Asn1Value classes (such as OctetString and BitString), the
5438        contents can be further parsed and interpreted as another Asn1Value.
5439        This parameter controls the spec for that sub-parsing.
5440
5441    :return:
5442        An object of the type spec, or if not specified, a child of Asn1Value
5443    """
5444
5445    if spec_params is not None:
5446        _tag_type_to_explicit_implicit(spec_params)
5447
5448    if header is None:
5449        return VOID
5450
5451    header_set = False
5452
5453    # If an explicit specification was passed in, make sure it matches
5454    if spec is not None:
5455        # If there is explicit tagging and contents, we have to split
5456        # the header and trailer off before we do the parsing
5457        no_explicit = spec_params and 'no_explicit' in spec_params
5458        if not no_explicit and (spec.explicit or (spec_params and 'explicit' in spec_params)):
5459            if spec_params:
5460                value = spec(**spec_params)
5461            else:
5462                value = spec()
5463            original_explicit = value.explicit
5464            explicit_info = reversed(original_explicit)
5465            parsed_class = class_
5466            parsed_method = method
5467            parsed_tag = tag
5468            to_parse = contents
5469            explicit_header = header
5470            explicit_trailer = trailer or b''
5471            for expected_class, expected_tag in explicit_info:
5472                if parsed_class != expected_class:
5473                    raise ValueError(unwrap(
5474                        '''
5475                        Error parsing %s - explicitly-tagged class should have been
5476                        %s, but %s was found
5477                        ''',
5478                        type_name(value),
5479                        CLASS_NUM_TO_NAME_MAP.get(expected_class),
5480                        CLASS_NUM_TO_NAME_MAP.get(parsed_class, parsed_class)
5481                    ))
5482                if parsed_method != 1:
5483                    raise ValueError(unwrap(
5484                        '''
5485                        Error parsing %s - explicitly-tagged method should have
5486                        been %s, but %s was found
5487                        ''',
5488                        type_name(value),
5489                        METHOD_NUM_TO_NAME_MAP.get(1),
5490                        METHOD_NUM_TO_NAME_MAP.get(parsed_method, parsed_method)
5491                    ))
5492                if parsed_tag != expected_tag:
5493                    raise ValueError(unwrap(
5494                        '''
5495                        Error parsing %s - explicitly-tagged tag should have been
5496                        %s, but %s was found
5497                        ''',
5498                        type_name(value),
5499                        expected_tag,
5500                        parsed_tag
5501                    ))
5502                info, _ = _parse(to_parse, len(to_parse))
5503                parsed_class, parsed_method, parsed_tag, parsed_header, to_parse, parsed_trailer = info
5504
5505                if not isinstance(value, Choice):
5506                    explicit_header += parsed_header
5507                    explicit_trailer = parsed_trailer + explicit_trailer
5508
5509            value = _build(*info, spec=spec, spec_params={'no_explicit': True})
5510            value._header = explicit_header
5511            value._trailer = explicit_trailer
5512            value.explicit = original_explicit
5513            header_set = True
5514        else:
5515            if spec_params:
5516                value = spec(contents=contents, **spec_params)
5517            else:
5518                value = spec(contents=contents)
5519
5520            if spec is Any:
5521                pass
5522
5523            elif isinstance(value, Choice):
5524                value.validate(class_, tag, contents)
5525                try:
5526                    # Force parsing the Choice now
5527                    value.contents = header + value.contents
5528                    header = b''
5529                    value.parse()
5530                except (ValueError, TypeError) as e:
5531                    args = e.args[1:]
5532                    e.args = (e.args[0] + '\n    while parsing %s' % type_name(value),) + args
5533                    raise e
5534
5535            else:
5536                if class_ != value.class_:
5537                    raise ValueError(unwrap(
5538                        '''
5539                        Error parsing %s - class should have been %s, but %s was
5540                        found
5541                        ''',
5542                        type_name(value),
5543                        CLASS_NUM_TO_NAME_MAP.get(value.class_),
5544                        CLASS_NUM_TO_NAME_MAP.get(class_, class_)
5545                    ))
5546                if method != value.method:
5547                    # Allow parsing a primitive method as constructed if the value
5548                    # is indefinite length. This is to allow parsing BER.
5549                    ber_indef = method == 1 and value.method == 0 and trailer == b'\x00\x00'
5550                    if not ber_indef or not isinstance(value, Constructable):
5551                        raise ValueError(unwrap(
5552                            '''
5553                            Error parsing %s - method should have been %s, but %s was found
5554                            ''',
5555                            type_name(value),
5556                            METHOD_NUM_TO_NAME_MAP.get(value.method),
5557                            METHOD_NUM_TO_NAME_MAP.get(method, method)
5558                        ))
5559                    else:
5560                        value.method = method
5561                        value._indefinite = True
5562                if tag != value.tag:
5563                    if isinstance(value._bad_tag, tuple):
5564                        is_bad_tag = tag in value._bad_tag
5565                    else:
5566                        is_bad_tag = tag == value._bad_tag
5567                    if not is_bad_tag:
5568                        raise ValueError(unwrap(
5569                            '''
5570                            Error parsing %s - tag should have been %s, but %s was found
5571                            ''',
5572                            type_name(value),
5573                            value.tag,
5574                            tag
5575                        ))
5576
5577    # For explicitly tagged, un-speced parsings, we use a generic container
5578    # since we will be parsing the contents and discarding the outer object
5579    # anyway a little further on
5580    elif spec_params and 'explicit' in spec_params:
5581        original_value = Asn1Value(contents=contents, **spec_params)
5582        original_explicit = original_value.explicit
5583
5584        to_parse = contents
5585        explicit_header = header
5586        explicit_trailer = trailer or b''
5587        for expected_class, expected_tag in reversed(original_explicit):
5588            info, _ = _parse(to_parse, len(to_parse))
5589            _, _, _, parsed_header, to_parse, parsed_trailer = info
5590            explicit_header += parsed_header
5591            explicit_trailer = parsed_trailer + explicit_trailer
5592        value = _build(*info, spec=spec, spec_params={'no_explicit': True})
5593        value._header = header + value._header
5594        value._trailer += trailer or b''
5595        value.explicit = original_explicit
5596        header_set = True
5597
5598    # If no spec was specified, allow anything and just process what
5599    # is in the input data
5600    else:
5601        if tag not in _UNIVERSAL_SPECS:
5602            raise ValueError(unwrap(
5603                '''
5604                Unknown element - %s class, %s method, tag %s
5605                ''',
5606                CLASS_NUM_TO_NAME_MAP.get(class_),
5607                METHOD_NUM_TO_NAME_MAP.get(method),
5608                tag
5609            ))
5610
5611        spec = _UNIVERSAL_SPECS[tag]
5612
5613        value = spec(contents=contents, class_=class_)
5614        ber_indef = method == 1 and value.method == 0 and trailer == b'\x00\x00'
5615        if ber_indef and isinstance(value, Constructable):
5616            value._indefinite = True
5617        value.method = method
5618
5619    if not header_set:
5620        value._header = header
5621        value._trailer = trailer or b''
5622
5623    # Destroy any default value that our contents have overwritten
5624    value._native = None
5625
5626    if nested_spec:
5627        try:
5628            value.parse(nested_spec)
5629        except (ValueError, TypeError) as e:
5630            args = e.args[1:]
5631            e.args = (e.args[0] + '\n    while parsing %s' % type_name(value),) + args
5632            raise e
5633
5634    return value
5635
5636
5637def _parse_build(encoded_data, pointer=0, spec=None, spec_params=None, strict=False):
5638    """
5639    Parses a byte string generically, or using a spec with optional params
5640
5641    :param encoded_data:
5642        A byte string that contains BER-encoded data
5643
5644    :param pointer:
5645        The index in the byte string to parse from
5646
5647    :param spec:
5648        A class derived from Asn1Value that defines what class_ and tag the
5649        value should have, and the semantics of the encoded value. The
5650        return value will be of this type. If omitted, the encoded value
5651        will be decoded using the standard universal tag based on the
5652        encoded tag number.
5653
5654    :param spec_params:
5655        A dict of params to pass to the spec object
5656
5657    :param strict:
5658        A boolean indicating if trailing data should be forbidden - if so, a
5659        ValueError will be raised when trailing data exists
5660
5661    :return:
5662        A 2-element tuple:
5663         - 0: An object of the type spec, or if not specified, a child of Asn1Value
5664         - 1: An integer indicating how many bytes were consumed
5665    """
5666
5667    encoded_len = len(encoded_data)
5668    info, new_pointer = _parse(encoded_data, encoded_len, pointer)
5669    if strict and new_pointer != pointer + encoded_len:
5670        extra_bytes = pointer + encoded_len - new_pointer
5671        raise ValueError('Extra data - %d bytes of trailing data were provided' % extra_bytes)
5672    return (_build(*info, spec=spec, spec_params=spec_params), new_pointer)
5673