• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# coding: utf-8
2
3"""
4ASN.1 type classes for X.509 certificates. Exports the following items:
5
6 - Attributes()
7 - Certificate()
8 - Extensions()
9 - GeneralName()
10 - GeneralNames()
11 - Name()
12
13Other type classes are defined that help compose the types listed above.
14"""
15
16from __future__ import unicode_literals, division, absolute_import, print_function
17
18from contextlib import contextmanager
19from encodings import idna  # noqa
20import hashlib
21import re
22import socket
23import stringprep
24import sys
25import unicodedata
26
27from ._errors import unwrap
28from ._iri import iri_to_uri, uri_to_iri
29from ._ordereddict import OrderedDict
30from ._types import type_name, str_cls, bytes_to_list
31from .algos import AlgorithmIdentifier, AnyAlgorithmIdentifier, DigestAlgorithm, SignedDigestAlgorithm
32from .core import (
33    Any,
34    BitString,
35    BMPString,
36    Boolean,
37    Choice,
38    Concat,
39    Enumerated,
40    GeneralizedTime,
41    GeneralString,
42    IA5String,
43    Integer,
44    Null,
45    NumericString,
46    ObjectIdentifier,
47    OctetBitString,
48    OctetString,
49    ParsableOctetString,
50    PrintableString,
51    Sequence,
52    SequenceOf,
53    Set,
54    SetOf,
55    TeletexString,
56    UniversalString,
57    UTCTime,
58    UTF8String,
59    VisibleString,
60    VOID,
61)
62from .keys import PublicKeyInfo
63from .util import int_to_bytes, int_from_bytes, inet_ntop, inet_pton
64
65
66# The structures in this file are taken from https://tools.ietf.org/html/rfc5280
67# and a few other supplementary sources, mostly due to extra supported
68# extension and name OIDs
69
70
71class DNSName(IA5String):
72
73    _encoding = 'idna'
74    _bad_tag = (12, 19)
75
76    def __ne__(self, other):
77        return not self == other
78
79    def __eq__(self, other):
80        """
81        Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.2
82
83        :param other:
84            Another DNSName object
85
86        :return:
87            A boolean
88        """
89
90        if not isinstance(other, DNSName):
91            return False
92
93        return self.__unicode__().lower() == other.__unicode__().lower()
94
95    def set(self, value):
96        """
97        Sets the value of the DNS name
98
99        :param value:
100            A unicode string
101        """
102
103        if not isinstance(value, str_cls):
104            raise TypeError(unwrap(
105                '''
106                %s value must be a unicode string, not %s
107                ''',
108                type_name(self),
109                type_name(value)
110            ))
111
112        if value.startswith('.'):
113            encoded_value = b'.' + value[1:].encode(self._encoding)
114        else:
115            encoded_value = value.encode(self._encoding)
116
117        self._unicode = value
118        self.contents = encoded_value
119        self._header = None
120        if self._trailer != b'':
121            self._trailer = b''
122
123
124class URI(IA5String):
125
126    def set(self, value):
127        """
128        Sets the value of the string
129
130        :param value:
131            A unicode string
132        """
133
134        if not isinstance(value, str_cls):
135            raise TypeError(unwrap(
136                '''
137                %s value must be a unicode string, not %s
138                ''',
139                type_name(self),
140                type_name(value)
141            ))
142
143        self._unicode = value
144        self.contents = iri_to_uri(value)
145        self._header = None
146        if self._trailer != b'':
147            self._trailer = b''
148
149    def __ne__(self, other):
150        return not self == other
151
152    def __eq__(self, other):
153        """
154        Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.4
155
156        :param other:
157            Another URI object
158
159        :return:
160            A boolean
161        """
162
163        if not isinstance(other, URI):
164            return False
165
166        return iri_to_uri(self.native, True) == iri_to_uri(other.native, True)
167
168    def __unicode__(self):
169        """
170        :return:
171            A unicode string
172        """
173
174        if self.contents is None:
175            return ''
176        if self._unicode is None:
177            self._unicode = uri_to_iri(self._merge_chunks())
178        return self._unicode
179
180
181class EmailAddress(IA5String):
182
183    _contents = None
184
185    # If the value has gone through the .set() method, thus normalizing it
186    _normalized = False
187
188    # In the wild we've seen this encoded as a UTF8String and PrintableString
189    _bad_tag = (12, 19)
190
191    @property
192    def contents(self):
193        """
194        :return:
195            A byte string of the DER-encoded contents of the sequence
196        """
197
198        return self._contents
199
200    @contents.setter
201    def contents(self, value):
202        """
203        :param value:
204            A byte string of the DER-encoded contents of the sequence
205        """
206
207        self._normalized = False
208        self._contents = value
209
210    def set(self, value):
211        """
212        Sets the value of the string
213
214        :param value:
215            A unicode string
216        """
217
218        if not isinstance(value, str_cls):
219            raise TypeError(unwrap(
220                '''
221                %s value must be a unicode string, not %s
222                ''',
223                type_name(self),
224                type_name(value)
225            ))
226
227        if value.find('@') != -1:
228            mailbox, hostname = value.rsplit('@', 1)
229            encoded_value = mailbox.encode('ascii') + b'@' + hostname.encode('idna')
230        else:
231            encoded_value = value.encode('ascii')
232
233        self._normalized = True
234        self._unicode = value
235        self.contents = encoded_value
236        self._header = None
237        if self._trailer != b'':
238            self._trailer = b''
239
240    def __unicode__(self):
241        """
242        :return:
243            A unicode string
244        """
245
246        # We've seen this in the wild as a PrintableString, and since ascii is a
247        # subset of cp1252, we use the later for decoding to be more user friendly
248        if self._unicode is None:
249            contents = self._merge_chunks()
250            if contents.find(b'@') == -1:
251                self._unicode = contents.decode('cp1252')
252            else:
253                mailbox, hostname = contents.rsplit(b'@', 1)
254                self._unicode = mailbox.decode('cp1252') + '@' + hostname.decode('idna')
255        return self._unicode
256
257    def __ne__(self, other):
258        return not self == other
259
260    def __eq__(self, other):
261        """
262        Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.5
263
264        :param other:
265            Another EmailAddress object
266
267        :return:
268            A boolean
269        """
270
271        if not isinstance(other, EmailAddress):
272            return False
273
274        if not self._normalized:
275            self.set(self.native)
276        if not other._normalized:
277            other.set(other.native)
278
279        if self._contents.find(b'@') == -1 or other._contents.find(b'@') == -1:
280            return self._contents == other._contents
281
282        other_mailbox, other_hostname = other._contents.rsplit(b'@', 1)
283        mailbox, hostname = self._contents.rsplit(b'@', 1)
284
285        if mailbox != other_mailbox:
286            return False
287
288        if hostname.lower() != other_hostname.lower():
289            return False
290
291        return True
292
293
294class IPAddress(OctetString):
295    def parse(self, spec=None, spec_params=None):
296        """
297        This method is not applicable to IP addresses
298        """
299
300        raise ValueError(unwrap(
301            '''
302            IP address values can not be parsed
303            '''
304        ))
305
306    def set(self, value):
307        """
308        Sets the value of the object
309
310        :param value:
311            A unicode string containing an IPv4 address, IPv4 address with CIDR,
312            an IPv6 address or IPv6 address with CIDR
313        """
314
315        if not isinstance(value, str_cls):
316            raise TypeError(unwrap(
317                '''
318                %s value must be a unicode string, not %s
319                ''',
320                type_name(self),
321                type_name(value)
322            ))
323
324        original_value = value
325
326        has_cidr = value.find('/') != -1
327        cidr = 0
328        if has_cidr:
329            parts = value.split('/', 1)
330            value = parts[0]
331            cidr = int(parts[1])
332            if cidr < 0:
333                raise ValueError(unwrap(
334                    '''
335                    %s value contains a CIDR range less than 0
336                    ''',
337                    type_name(self)
338                ))
339
340        if value.find(':') != -1:
341            family = socket.AF_INET6
342            if cidr > 128:
343                raise ValueError(unwrap(
344                    '''
345                    %s value contains a CIDR range bigger than 128, the maximum
346                    value for an IPv6 address
347                    ''',
348                    type_name(self)
349                ))
350            cidr_size = 128
351        else:
352            family = socket.AF_INET
353            if cidr > 32:
354                raise ValueError(unwrap(
355                    '''
356                    %s value contains a CIDR range bigger than 32, the maximum
357                    value for an IPv4 address
358                    ''',
359                    type_name(self)
360                ))
361            cidr_size = 32
362
363        cidr_bytes = b''
364        if has_cidr:
365            cidr_mask = '1' * cidr
366            cidr_mask += '0' * (cidr_size - len(cidr_mask))
367            cidr_bytes = int_to_bytes(int(cidr_mask, 2))
368            cidr_bytes = (b'\x00' * ((cidr_size // 8) - len(cidr_bytes))) + cidr_bytes
369
370        self._native = original_value
371        self.contents = inet_pton(family, value) + cidr_bytes
372        self._bytes = self.contents
373        self._header = None
374        if self._trailer != b'':
375            self._trailer = b''
376
377    @property
378    def native(self):
379        """
380        The native Python datatype representation of this value
381
382        :return:
383            A unicode string or None
384        """
385
386        if self.contents is None:
387            return None
388
389        if self._native is None:
390            byte_string = self.__bytes__()
391            byte_len = len(byte_string)
392            value = None
393            cidr_int = None
394            if byte_len in set([32, 16]):
395                value = inet_ntop(socket.AF_INET6, byte_string[0:16])
396                if byte_len > 16:
397                    cidr_int = int_from_bytes(byte_string[16:])
398            elif byte_len in set([8, 4]):
399                value = inet_ntop(socket.AF_INET, byte_string[0:4])
400                if byte_len > 4:
401                    cidr_int = int_from_bytes(byte_string[4:])
402            if cidr_int is not None:
403                cidr_bits = '{0:b}'.format(cidr_int)
404                cidr = len(cidr_bits.rstrip('0'))
405                value = value + '/' + str_cls(cidr)
406            self._native = value
407        return self._native
408
409    def __ne__(self, other):
410        return not self == other
411
412    def __eq__(self, other):
413        """
414        :param other:
415            Another IPAddress object
416
417        :return:
418            A boolean
419        """
420
421        if not isinstance(other, IPAddress):
422            return False
423
424        return self.__bytes__() == other.__bytes__()
425
426
427class Attribute(Sequence):
428    _fields = [
429        ('type', ObjectIdentifier),
430        ('values', SetOf, {'spec': Any}),
431    ]
432
433
434class Attributes(SequenceOf):
435    _child_spec = Attribute
436
437
438class KeyUsage(BitString):
439    _map = {
440        0: 'digital_signature',
441        1: 'non_repudiation',
442        2: 'key_encipherment',
443        3: 'data_encipherment',
444        4: 'key_agreement',
445        5: 'key_cert_sign',
446        6: 'crl_sign',
447        7: 'encipher_only',
448        8: 'decipher_only',
449    }
450
451
452class PrivateKeyUsagePeriod(Sequence):
453    _fields = [
454        ('not_before', GeneralizedTime, {'implicit': 0, 'optional': True}),
455        ('not_after', GeneralizedTime, {'implicit': 1, 'optional': True}),
456    ]
457
458
459class NotReallyTeletexString(TeletexString):
460    """
461    OpenSSL (and probably some other libraries) puts ISO-8859-1
462    into TeletexString instead of ITU T.61. We use Windows-1252 when
463    decoding since it is a superset of ISO-8859-1, and less likely to
464    cause encoding issues, but we stay strict with encoding to prevent
465    us from creating bad data.
466    """
467
468    _decoding_encoding = 'cp1252'
469
470    def __unicode__(self):
471        """
472        :return:
473            A unicode string
474        """
475
476        if self.contents is None:
477            return ''
478        if self._unicode is None:
479            self._unicode = self._merge_chunks().decode(self._decoding_encoding)
480        return self._unicode
481
482
483@contextmanager
484def strict_teletex():
485    try:
486        NotReallyTeletexString._decoding_encoding = 'teletex'
487        yield
488    finally:
489        NotReallyTeletexString._decoding_encoding = 'cp1252'
490
491
492class DirectoryString(Choice):
493    _alternatives = [
494        ('teletex_string', NotReallyTeletexString),
495        ('printable_string', PrintableString),
496        ('universal_string', UniversalString),
497        ('utf8_string', UTF8String),
498        ('bmp_string', BMPString),
499        # This is an invalid/bad alternative, but some broken certs use it
500        ('ia5_string', IA5String),
501    ]
502
503
504class NameType(ObjectIdentifier):
505    _map = {
506        '2.5.4.3': 'common_name',
507        '2.5.4.4': 'surname',
508        '2.5.4.5': 'serial_number',
509        '2.5.4.6': 'country_name',
510        '2.5.4.7': 'locality_name',
511        '2.5.4.8': 'state_or_province_name',
512        '2.5.4.9': 'street_address',
513        '2.5.4.10': 'organization_name',
514        '2.5.4.11': 'organizational_unit_name',
515        '2.5.4.12': 'title',
516        '2.5.4.15': 'business_category',
517        '2.5.4.17': 'postal_code',
518        '2.5.4.20': 'telephone_number',
519        '2.5.4.41': 'name',
520        '2.5.4.42': 'given_name',
521        '2.5.4.43': 'initials',
522        '2.5.4.44': 'generation_qualifier',
523        '2.5.4.45': 'unique_identifier',
524        '2.5.4.46': 'dn_qualifier',
525        '2.5.4.65': 'pseudonym',
526        '2.5.4.97': 'organization_identifier',
527        # https://www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf
528        '2.23.133.2.1': 'tpm_manufacturer',
529        '2.23.133.2.2': 'tpm_model',
530        '2.23.133.2.3': 'tpm_version',
531        '2.23.133.2.4': 'platform_manufacturer',
532        '2.23.133.2.5': 'platform_model',
533        '2.23.133.2.6': 'platform_version',
534        # https://tools.ietf.org/html/rfc2985#page-26
535        '1.2.840.113549.1.9.1': 'email_address',
536        # Page 10 of https://cabforum.org/wp-content/uploads/EV-V1_5_5.pdf
537        '1.3.6.1.4.1.311.60.2.1.1': 'incorporation_locality',
538        '1.3.6.1.4.1.311.60.2.1.2': 'incorporation_state_or_province',
539        '1.3.6.1.4.1.311.60.2.1.3': 'incorporation_country',
540        # https://tools.ietf.org/html/rfc4519#section-2.39
541        '0.9.2342.19200300.100.1.1': 'user_id',
542        # https://tools.ietf.org/html/rfc2247#section-4
543        '0.9.2342.19200300.100.1.25': 'domain_component',
544        # http://www.alvestrand.no/objectid/0.2.262.1.10.7.20.html
545        '0.2.262.1.10.7.20': 'name_distinguisher',
546    }
547
548    # This order is largely based on observed order seen in EV certs from
549    # Symantec and DigiCert. Some of the uncommon name-related fields are
550    # just placed in what seems like a reasonable order.
551    preferred_order = [
552        'incorporation_country',
553        'incorporation_state_or_province',
554        'incorporation_locality',
555        'business_category',
556        'serial_number',
557        'country_name',
558        'postal_code',
559        'state_or_province_name',
560        'locality_name',
561        'street_address',
562        'organization_name',
563        'organizational_unit_name',
564        'title',
565        'common_name',
566        'user_id',
567        'initials',
568        'generation_qualifier',
569        'surname',
570        'given_name',
571        'name',
572        'pseudonym',
573        'dn_qualifier',
574        'telephone_number',
575        'email_address',
576        'domain_component',
577        'name_distinguisher',
578        'organization_identifier',
579        'tpm_manufacturer',
580        'tpm_model',
581        'tpm_version',
582        'platform_manufacturer',
583        'platform_model',
584        'platform_version',
585    ]
586
587    @classmethod
588    def preferred_ordinal(cls, attr_name):
589        """
590        Returns an ordering value for a particular attribute key.
591
592        Unrecognized attributes and OIDs will be sorted lexically at the end.
593
594        :return:
595            An orderable value.
596
597        """
598
599        attr_name = cls.map(attr_name)
600        if attr_name in cls.preferred_order:
601            ordinal = cls.preferred_order.index(attr_name)
602        else:
603            ordinal = len(cls.preferred_order)
604
605        return (ordinal, attr_name)
606
607    @property
608    def human_friendly(self):
609        """
610        :return:
611            A human-friendly unicode string to display to users
612        """
613
614        return {
615            'common_name': 'Common Name',
616            'surname': 'Surname',
617            'serial_number': 'Serial Number',
618            'country_name': 'Country',
619            'locality_name': 'Locality',
620            'state_or_province_name': 'State/Province',
621            'street_address': 'Street Address',
622            'organization_name': 'Organization',
623            'organizational_unit_name': 'Organizational Unit',
624            'title': 'Title',
625            'business_category': 'Business Category',
626            'postal_code': 'Postal Code',
627            'telephone_number': 'Telephone Number',
628            'name': 'Name',
629            'given_name': 'Given Name',
630            'initials': 'Initials',
631            'generation_qualifier': 'Generation Qualifier',
632            'unique_identifier': 'Unique Identifier',
633            'dn_qualifier': 'DN Qualifier',
634            'pseudonym': 'Pseudonym',
635            'email_address': 'Email Address',
636            'incorporation_locality': 'Incorporation Locality',
637            'incorporation_state_or_province': 'Incorporation State/Province',
638            'incorporation_country': 'Incorporation Country',
639            'domain_component': 'Domain Component',
640            'name_distinguisher': 'Name Distinguisher',
641            'organization_identifier': 'Organization Identifier',
642            'tpm_manufacturer': 'TPM Manufacturer',
643            'tpm_model': 'TPM Model',
644            'tpm_version': 'TPM Version',
645            'platform_manufacturer': 'Platform Manufacturer',
646            'platform_model': 'Platform Model',
647            'platform_version': 'Platform Version',
648            'user_id': 'User ID',
649        }.get(self.native, self.native)
650
651
652class NameTypeAndValue(Sequence):
653    _fields = [
654        ('type', NameType),
655        ('value', Any),
656    ]
657
658    _oid_pair = ('type', 'value')
659    _oid_specs = {
660        'common_name': DirectoryString,
661        'surname': DirectoryString,
662        'serial_number': DirectoryString,
663        'country_name': DirectoryString,
664        'locality_name': DirectoryString,
665        'state_or_province_name': DirectoryString,
666        'street_address': DirectoryString,
667        'organization_name': DirectoryString,
668        'organizational_unit_name': DirectoryString,
669        'title': DirectoryString,
670        'business_category': DirectoryString,
671        'postal_code': DirectoryString,
672        'telephone_number': PrintableString,
673        'name': DirectoryString,
674        'given_name': DirectoryString,
675        'initials': DirectoryString,
676        'generation_qualifier': DirectoryString,
677        'unique_identifier': OctetBitString,
678        'dn_qualifier': DirectoryString,
679        'pseudonym': DirectoryString,
680        # https://tools.ietf.org/html/rfc2985#page-26
681        'email_address': EmailAddress,
682        # Page 10 of https://cabforum.org/wp-content/uploads/EV-V1_5_5.pdf
683        'incorporation_locality': DirectoryString,
684        'incorporation_state_or_province': DirectoryString,
685        'incorporation_country': DirectoryString,
686        'domain_component': DNSName,
687        'name_distinguisher': DirectoryString,
688        'organization_identifier': DirectoryString,
689        'tpm_manufacturer': UTF8String,
690        'tpm_model': UTF8String,
691        'tpm_version': UTF8String,
692        'platform_manufacturer': UTF8String,
693        'platform_model': UTF8String,
694        'platform_version': UTF8String,
695        'user_id': DirectoryString,
696    }
697
698    _prepped = None
699
700    @property
701    def prepped_value(self):
702        """
703        Returns the value after being processed by the internationalized string
704        preparation as specified by RFC 5280
705
706        :return:
707            A unicode string
708        """
709
710        if self._prepped is None:
711            self._prepped = self._ldap_string_prep(self['value'].native)
712        return self._prepped
713
714    def __ne__(self, other):
715        return not self == other
716
717    def __eq__(self, other):
718        """
719        Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
720
721        :param other:
722            Another NameTypeAndValue object
723
724        :return:
725            A boolean
726        """
727
728        if not isinstance(other, NameTypeAndValue):
729            return False
730
731        if other['type'].native != self['type'].native:
732            return False
733
734        return other.prepped_value == self.prepped_value
735
736    def _ldap_string_prep(self, string):
737        """
738        Implements the internationalized string preparation algorithm from
739        RFC 4518. https://tools.ietf.org/html/rfc4518#section-2
740
741        :param string:
742            A unicode string to prepare
743
744        :return:
745            A prepared unicode string, ready for comparison
746        """
747
748        # Map step
749        string = re.sub('[\u00ad\u1806\u034f\u180b-\u180d\ufe0f-\uff00\ufffc]+', '', string)
750        string = re.sub('[\u0009\u000a\u000b\u000c\u000d\u0085]', ' ', string)
751        if sys.maxunicode == 0xffff:
752            # Some installs of Python 2.7 don't support 8-digit unicode escape
753            # ranges, so we have to break them into pieces
754            # Original was: \U0001D173-\U0001D17A and \U000E0020-\U000E007F
755            string = re.sub('\ud834[\udd73-\udd7a]|\udb40[\udc20-\udc7f]|\U000e0001', '', string)
756        else:
757            string = re.sub('[\U0001D173-\U0001D17A\U000E0020-\U000E007F\U000e0001]', '', string)
758        string = re.sub(
759            '[\u0000-\u0008\u000e-\u001f\u007f-\u0084\u0086-\u009f\u06dd\u070f\u180e\u200c-\u200f'
760            '\u202a-\u202e\u2060-\u2063\u206a-\u206f\ufeff\ufff9-\ufffb]+',
761            '',
762            string
763        )
764        string = string.replace('\u200b', '')
765        string = re.sub('[\u00a0\u1680\u2000-\u200a\u2028-\u2029\u202f\u205f\u3000]', ' ', string)
766
767        string = ''.join(map(stringprep.map_table_b2, string))
768
769        # Normalize step
770        string = unicodedata.normalize('NFKC', string)
771
772        # Prohibit step
773        for char in string:
774            if stringprep.in_table_a1(char):
775                raise ValueError(unwrap(
776                    '''
777                    X.509 Name objects may not contain unassigned code points
778                    '''
779                ))
780
781            if stringprep.in_table_c8(char):
782                raise ValueError(unwrap(
783                    '''
784                    X.509 Name objects may not contain change display or
785                    zzzzdeprecated characters
786                    '''
787                ))
788
789            if stringprep.in_table_c3(char):
790                raise ValueError(unwrap(
791                    '''
792                    X.509 Name objects may not contain private use characters
793                    '''
794                ))
795
796            if stringprep.in_table_c4(char):
797                raise ValueError(unwrap(
798                    '''
799                    X.509 Name objects may not contain non-character code points
800                    '''
801                ))
802
803            if stringprep.in_table_c5(char):
804                raise ValueError(unwrap(
805                    '''
806                    X.509 Name objects may not contain surrogate code points
807                    '''
808                ))
809
810            if char == '\ufffd':
811                raise ValueError(unwrap(
812                    '''
813                    X.509 Name objects may not contain the replacement character
814                    '''
815                ))
816
817        # Check bidirectional step - here we ensure that we are not mixing
818        # left-to-right and right-to-left text in the string
819        has_r_and_al_cat = False
820        has_l_cat = False
821        for char in string:
822            if stringprep.in_table_d1(char):
823                has_r_and_al_cat = True
824            elif stringprep.in_table_d2(char):
825                has_l_cat = True
826
827        if has_r_and_al_cat:
828            first_is_r_and_al = stringprep.in_table_d1(string[0])
829            last_is_r_and_al = stringprep.in_table_d1(string[-1])
830
831            if has_l_cat or not first_is_r_and_al or not last_is_r_and_al:
832                raise ValueError(unwrap(
833                    '''
834                    X.509 Name object contains a malformed bidirectional
835                    sequence
836                    '''
837                ))
838
839        # Insignificant space handling step
840        string = ' ' + re.sub(' +', '  ', string).strip() + ' '
841
842        return string
843
844
845class RelativeDistinguishedName(SetOf):
846    _child_spec = NameTypeAndValue
847
848    @property
849    def hashable(self):
850        """
851        :return:
852            A unicode string that can be used as a dict key or in a set
853        """
854
855        output = []
856        values = self._get_values(self)
857        for key in sorted(values.keys()):
858            output.append('%s: %s' % (key, values[key]))
859        # Unit separator is used here since the normalization process for
860        # values moves any such character, and the keys are all dotted integers
861        # or under_score_words
862        return '\x1F'.join(output)
863
864    def __ne__(self, other):
865        return not self == other
866
867    def __eq__(self, other):
868        """
869        Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
870
871        :param other:
872            Another RelativeDistinguishedName object
873
874        :return:
875            A boolean
876        """
877
878        if not isinstance(other, RelativeDistinguishedName):
879            return False
880
881        if len(self) != len(other):
882            return False
883
884        self_types = self._get_types(self)
885        other_types = self._get_types(other)
886
887        if self_types != other_types:
888            return False
889
890        self_values = self._get_values(self)
891        other_values = self._get_values(other)
892
893        for type_name_ in self_types:
894            if self_values[type_name_] != other_values[type_name_]:
895                return False
896
897        return True
898
899    def _get_types(self, rdn):
900        """
901        Returns a set of types contained in an RDN
902
903        :param rdn:
904            A RelativeDistinguishedName object
905
906        :return:
907            A set object with unicode strings of NameTypeAndValue type field
908            values
909        """
910
911        return set([ntv['type'].native for ntv in rdn])
912
913    def _get_values(self, rdn):
914        """
915        Returns a dict of prepped values contained in an RDN
916
917        :param rdn:
918            A RelativeDistinguishedName object
919
920        :return:
921            A dict object with unicode strings of NameTypeAndValue value field
922            values that have been prepped for comparison
923        """
924
925        output = {}
926        [output.update([(ntv['type'].native, ntv.prepped_value)]) for ntv in rdn]
927        return output
928
929
930class RDNSequence(SequenceOf):
931    _child_spec = RelativeDistinguishedName
932
933    @property
934    def hashable(self):
935        """
936        :return:
937            A unicode string that can be used as a dict key or in a set
938        """
939
940        # Record separator is used here since the normalization process for
941        # values moves any such character, and the keys are all dotted integers
942        # or under_score_words
943        return '\x1E'.join(rdn.hashable for rdn in self)
944
945    def __ne__(self, other):
946        return not self == other
947
948    def __eq__(self, other):
949        """
950        Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
951
952        :param other:
953            Another RDNSequence object
954
955        :return:
956            A boolean
957        """
958
959        if not isinstance(other, RDNSequence):
960            return False
961
962        if len(self) != len(other):
963            return False
964
965        for index, self_rdn in enumerate(self):
966            if other[index] != self_rdn:
967                return False
968
969        return True
970
971
972class Name(Choice):
973    _alternatives = [
974        ('', RDNSequence),
975    ]
976
977    _human_friendly = None
978    _sha1 = None
979    _sha256 = None
980
981    @classmethod
982    def build(cls, name_dict, use_printable=False):
983        """
984        Creates a Name object from a dict of unicode string keys and values.
985        The keys should be from NameType._map, or a dotted-integer OID unicode
986        string.
987
988        :param name_dict:
989            A dict of name information, e.g. {"common_name": "Will Bond",
990            "country_name": "US", "organization_name": "Codex Non Sufficit LC"}
991
992        :param use_printable:
993            A bool - if PrintableString should be used for encoding instead of
994            UTF8String. This is for backwards compatibility with old software.
995
996        :return:
997            An x509.Name object
998        """
999
1000        rdns = []
1001        if not use_printable:
1002            encoding_name = 'utf8_string'
1003            encoding_class = UTF8String
1004        else:
1005            encoding_name = 'printable_string'
1006            encoding_class = PrintableString
1007
1008        # Sort the attributes according to NameType.preferred_order
1009        name_dict = OrderedDict(
1010            sorted(
1011                name_dict.items(),
1012                key=lambda item: NameType.preferred_ordinal(item[0])
1013            )
1014        )
1015
1016        for attribute_name, attribute_value in name_dict.items():
1017            attribute_name = NameType.map(attribute_name)
1018            if attribute_name == 'email_address':
1019                value = EmailAddress(attribute_value)
1020            elif attribute_name == 'domain_component':
1021                value = DNSName(attribute_value)
1022            elif attribute_name in set(['dn_qualifier', 'country_name', 'serial_number']):
1023                value = DirectoryString(
1024                    name='printable_string',
1025                    value=PrintableString(attribute_value)
1026                )
1027            else:
1028                value = DirectoryString(
1029                    name=encoding_name,
1030                    value=encoding_class(attribute_value)
1031                )
1032
1033            rdns.append(RelativeDistinguishedName([
1034                NameTypeAndValue({
1035                    'type': attribute_name,
1036                    'value': value
1037                })
1038            ]))
1039
1040        return cls(name='', value=RDNSequence(rdns))
1041
1042    @property
1043    def hashable(self):
1044        """
1045        :return:
1046            A unicode string that can be used as a dict key or in a set
1047        """
1048
1049        return self.chosen.hashable
1050
1051    def __len__(self):
1052        return len(self.chosen)
1053
1054    def __ne__(self, other):
1055        return not self == other
1056
1057    def __eq__(self, other):
1058        """
1059        Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
1060
1061        :param other:
1062            Another Name object
1063
1064        :return:
1065            A boolean
1066        """
1067
1068        if not isinstance(other, Name):
1069            return False
1070        return self.chosen == other.chosen
1071
1072    @property
1073    def native(self):
1074        if self._native is None:
1075            self._native = OrderedDict()
1076            for rdn in self.chosen.native:
1077                for type_val in rdn:
1078                    field_name = type_val['type']
1079                    if field_name in self._native:
1080                        existing = self._native[field_name]
1081                        if not isinstance(existing, list):
1082                            existing = self._native[field_name] = [existing]
1083                        existing.append(type_val['value'])
1084                    else:
1085                        self._native[field_name] = type_val['value']
1086        return self._native
1087
1088    @property
1089    def human_friendly(self):
1090        """
1091        :return:
1092            A human-friendly unicode string containing the parts of the name
1093        """
1094
1095        if self._human_friendly is None:
1096            data = OrderedDict()
1097            last_field = None
1098            for rdn in self.chosen:
1099                for type_val in rdn:
1100                    field_name = type_val['type'].human_friendly
1101                    last_field = field_name
1102                    if field_name in data:
1103                        data[field_name] = [data[field_name]]
1104                        data[field_name].append(type_val['value'])
1105                    else:
1106                        data[field_name] = type_val['value']
1107            to_join = []
1108            keys = data.keys()
1109            if last_field == 'Country':
1110                keys = reversed(list(keys))
1111            for key in keys:
1112                value = data[key]
1113                native_value = self._recursive_humanize(value)
1114                to_join.append('%s: %s' % (key, native_value))
1115
1116            has_comma = False
1117            for element in to_join:
1118                if element.find(',') != -1:
1119                    has_comma = True
1120                    break
1121
1122            separator = ', ' if not has_comma else '; '
1123            self._human_friendly = separator.join(to_join[::-1])
1124
1125        return self._human_friendly
1126
1127    def _recursive_humanize(self, value):
1128        """
1129        Recursively serializes data compiled from the RDNSequence
1130
1131        :param value:
1132            An Asn1Value object, or a list of Asn1Value objects
1133
1134        :return:
1135            A unicode string
1136        """
1137
1138        if isinstance(value, list):
1139            return ', '.join(
1140                reversed([self._recursive_humanize(sub_value) for sub_value in value])
1141            )
1142        return value.native
1143
1144    @property
1145    def sha1(self):
1146        """
1147        :return:
1148            The SHA1 hash of the DER-encoded bytes of this name
1149        """
1150
1151        if self._sha1 is None:
1152            self._sha1 = hashlib.sha1(self.dump()).digest()
1153        return self._sha1
1154
1155    @property
1156    def sha256(self):
1157        """
1158        :return:
1159            The SHA-256 hash of the DER-encoded bytes of this name
1160        """
1161
1162        if self._sha256 is None:
1163            self._sha256 = hashlib.sha256(self.dump()).digest()
1164        return self._sha256
1165
1166
1167class AnotherName(Sequence):
1168    _fields = [
1169        ('type_id', ObjectIdentifier),
1170        ('value', Any, {'explicit': 0}),
1171    ]
1172
1173
1174class CountryName(Choice):
1175    class_ = 1
1176    tag = 1
1177
1178    _alternatives = [
1179        ('x121_dcc_code', NumericString),
1180        ('iso_3166_alpha2_code', PrintableString),
1181    ]
1182
1183
1184class AdministrationDomainName(Choice):
1185    class_ = 1
1186    tag = 2
1187
1188    _alternatives = [
1189        ('numeric', NumericString),
1190        ('printable', PrintableString),
1191    ]
1192
1193
1194class PrivateDomainName(Choice):
1195    _alternatives = [
1196        ('numeric', NumericString),
1197        ('printable', PrintableString),
1198    ]
1199
1200
1201class PersonalName(Set):
1202    _fields = [
1203        ('surname', PrintableString, {'implicit': 0}),
1204        ('given_name', PrintableString, {'implicit': 1, 'optional': True}),
1205        ('initials', PrintableString, {'implicit': 2, 'optional': True}),
1206        ('generation_qualifier', PrintableString, {'implicit': 3, 'optional': True}),
1207    ]
1208
1209
1210class TeletexPersonalName(Set):
1211    _fields = [
1212        ('surname', TeletexString, {'implicit': 0}),
1213        ('given_name', TeletexString, {'implicit': 1, 'optional': True}),
1214        ('initials', TeletexString, {'implicit': 2, 'optional': True}),
1215        ('generation_qualifier', TeletexString, {'implicit': 3, 'optional': True}),
1216    ]
1217
1218
1219class OrganizationalUnitNames(SequenceOf):
1220    _child_spec = PrintableString
1221
1222
1223class TeletexOrganizationalUnitNames(SequenceOf):
1224    _child_spec = TeletexString
1225
1226
1227class BuiltInStandardAttributes(Sequence):
1228    _fields = [
1229        ('country_name', CountryName, {'optional': True}),
1230        ('administration_domain_name', AdministrationDomainName, {'optional': True}),
1231        ('network_address', NumericString, {'implicit': 0, 'optional': True}),
1232        ('terminal_identifier', PrintableString, {'implicit': 1, 'optional': True}),
1233        ('private_domain_name', PrivateDomainName, {'explicit': 2, 'optional': True}),
1234        ('organization_name', PrintableString, {'implicit': 3, 'optional': True}),
1235        ('numeric_user_identifier', NumericString, {'implicit': 4, 'optional': True}),
1236        ('personal_name', PersonalName, {'implicit': 5, 'optional': True}),
1237        ('organizational_unit_names', OrganizationalUnitNames, {'implicit': 6, 'optional': True}),
1238    ]
1239
1240
1241class BuiltInDomainDefinedAttribute(Sequence):
1242    _fields = [
1243        ('type', PrintableString),
1244        ('value', PrintableString),
1245    ]
1246
1247
1248class BuiltInDomainDefinedAttributes(SequenceOf):
1249    _child_spec = BuiltInDomainDefinedAttribute
1250
1251
1252class TeletexDomainDefinedAttribute(Sequence):
1253    _fields = [
1254        ('type', TeletexString),
1255        ('value', TeletexString),
1256    ]
1257
1258
1259class TeletexDomainDefinedAttributes(SequenceOf):
1260    _child_spec = TeletexDomainDefinedAttribute
1261
1262
1263class PhysicalDeliveryCountryName(Choice):
1264    _alternatives = [
1265        ('x121_dcc_code', NumericString),
1266        ('iso_3166_alpha2_code', PrintableString),
1267    ]
1268
1269
1270class PostalCode(Choice):
1271    _alternatives = [
1272        ('numeric_code', NumericString),
1273        ('printable_code', PrintableString),
1274    ]
1275
1276
1277class PDSParameter(Set):
1278    _fields = [
1279        ('printable_string', PrintableString, {'optional': True}),
1280        ('teletex_string', TeletexString, {'optional': True}),
1281    ]
1282
1283
1284class PrintableAddress(SequenceOf):
1285    _child_spec = PrintableString
1286
1287
1288class UnformattedPostalAddress(Set):
1289    _fields = [
1290        ('printable_address', PrintableAddress, {'optional': True}),
1291        ('teletex_string', TeletexString, {'optional': True}),
1292    ]
1293
1294
1295class E1634Address(Sequence):
1296    _fields = [
1297        ('number', NumericString, {'implicit': 0}),
1298        ('sub_address', NumericString, {'implicit': 1, 'optional': True}),
1299    ]
1300
1301
1302class NAddresses(SetOf):
1303    _child_spec = OctetString
1304
1305
1306class PresentationAddress(Sequence):
1307    _fields = [
1308        ('p_selector', OctetString, {'explicit': 0, 'optional': True}),
1309        ('s_selector', OctetString, {'explicit': 1, 'optional': True}),
1310        ('t_selector', OctetString, {'explicit': 2, 'optional': True}),
1311        ('n_addresses', NAddresses, {'explicit': 3}),
1312    ]
1313
1314
1315class ExtendedNetworkAddress(Choice):
1316    _alternatives = [
1317        ('e163_4_address', E1634Address),
1318        ('psap_address', PresentationAddress, {'implicit': 0})
1319    ]
1320
1321
1322class TerminalType(Integer):
1323    _map = {
1324        3: 'telex',
1325        4: 'teletex',
1326        5: 'g3_facsimile',
1327        6: 'g4_facsimile',
1328        7: 'ia5_terminal',
1329        8: 'videotex',
1330    }
1331
1332
1333class ExtensionAttributeType(Integer):
1334    _map = {
1335        1: 'common_name',
1336        2: 'teletex_common_name',
1337        3: 'teletex_organization_name',
1338        4: 'teletex_personal_name',
1339        5: 'teletex_organization_unit_names',
1340        6: 'teletex_domain_defined_attributes',
1341        7: 'pds_name',
1342        8: 'physical_delivery_country_name',
1343        9: 'postal_code',
1344        10: 'physical_delivery_office_name',
1345        11: 'physical_delivery_office_number',
1346        12: 'extension_of_address_components',
1347        13: 'physical_delivery_personal_name',
1348        14: 'physical_delivery_organization_name',
1349        15: 'extension_physical_delivery_address_components',
1350        16: 'unformatted_postal_address',
1351        17: 'street_address',
1352        18: 'post_office_box_address',
1353        19: 'poste_restante_address',
1354        20: 'unique_postal_name',
1355        21: 'local_postal_attributes',
1356        22: 'extended_network_address',
1357        23: 'terminal_type',
1358    }
1359
1360
1361class ExtensionAttribute(Sequence):
1362    _fields = [
1363        ('extension_attribute_type', ExtensionAttributeType, {'implicit': 0}),
1364        ('extension_attribute_value', Any, {'explicit': 1}),
1365    ]
1366
1367    _oid_pair = ('extension_attribute_type', 'extension_attribute_value')
1368    _oid_specs = {
1369        'common_name': PrintableString,
1370        'teletex_common_name': TeletexString,
1371        'teletex_organization_name': TeletexString,
1372        'teletex_personal_name': TeletexPersonalName,
1373        'teletex_organization_unit_names': TeletexOrganizationalUnitNames,
1374        'teletex_domain_defined_attributes': TeletexDomainDefinedAttributes,
1375        'pds_name': PrintableString,
1376        'physical_delivery_country_name': PhysicalDeliveryCountryName,
1377        'postal_code': PostalCode,
1378        'physical_delivery_office_name': PDSParameter,
1379        'physical_delivery_office_number': PDSParameter,
1380        'extension_of_address_components': PDSParameter,
1381        'physical_delivery_personal_name': PDSParameter,
1382        'physical_delivery_organization_name': PDSParameter,
1383        'extension_physical_delivery_address_components': PDSParameter,
1384        'unformatted_postal_address': UnformattedPostalAddress,
1385        'street_address': PDSParameter,
1386        'post_office_box_address': PDSParameter,
1387        'poste_restante_address': PDSParameter,
1388        'unique_postal_name': PDSParameter,
1389        'local_postal_attributes': PDSParameter,
1390        'extended_network_address': ExtendedNetworkAddress,
1391        'terminal_type': TerminalType,
1392    }
1393
1394
1395class ExtensionAttributes(SequenceOf):
1396    _child_spec = ExtensionAttribute
1397
1398
1399class ORAddress(Sequence):
1400    _fields = [
1401        ('built_in_standard_attributes', BuiltInStandardAttributes),
1402        ('built_in_domain_defined_attributes', BuiltInDomainDefinedAttributes, {'optional': True}),
1403        ('extension_attributes', ExtensionAttributes, {'optional': True}),
1404    ]
1405
1406
1407class EDIPartyName(Sequence):
1408    _fields = [
1409        ('name_assigner', DirectoryString, {'implicit': 0, 'optional': True}),
1410        ('party_name', DirectoryString, {'implicit': 1}),
1411    ]
1412
1413
1414class GeneralName(Choice):
1415    _alternatives = [
1416        ('other_name', AnotherName, {'implicit': 0}),
1417        ('rfc822_name', EmailAddress, {'implicit': 1}),
1418        ('dns_name', DNSName, {'implicit': 2}),
1419        ('x400_address', ORAddress, {'implicit': 3}),
1420        ('directory_name', Name, {'explicit': 4}),
1421        ('edi_party_name', EDIPartyName, {'implicit': 5}),
1422        ('uniform_resource_identifier', URI, {'implicit': 6}),
1423        ('ip_address', IPAddress, {'implicit': 7}),
1424        ('registered_id', ObjectIdentifier, {'implicit': 8}),
1425    ]
1426
1427    def __ne__(self, other):
1428        return not self == other
1429
1430    def __eq__(self, other):
1431        """
1432        Does not support other_name, x400_address or edi_party_name
1433
1434        :param other:
1435            The other GeneralName to compare to
1436
1437        :return:
1438            A boolean
1439        """
1440
1441        if self.name in ('other_name', 'x400_address', 'edi_party_name'):
1442            raise ValueError(unwrap(
1443                '''
1444                Comparison is not supported for GeneralName objects of
1445                choice %s
1446                ''',
1447                self.name
1448            ))
1449
1450        if other.name in ('other_name', 'x400_address', 'edi_party_name'):
1451            raise ValueError(unwrap(
1452                '''
1453                Comparison is not supported for GeneralName objects of choice
1454                %s''',
1455                other.name
1456            ))
1457
1458        if self.name != other.name:
1459            return False
1460
1461        return self.chosen == other.chosen
1462
1463
1464class GeneralNames(SequenceOf):
1465    _child_spec = GeneralName
1466
1467
1468class Time(Choice):
1469    _alternatives = [
1470        ('utc_time', UTCTime),
1471        ('general_time', GeneralizedTime),
1472    ]
1473
1474
1475class Validity(Sequence):
1476    _fields = [
1477        ('not_before', Time),
1478        ('not_after', Time),
1479    ]
1480
1481
1482class BasicConstraints(Sequence):
1483    _fields = [
1484        ('ca', Boolean, {'default': False}),
1485        ('path_len_constraint', Integer, {'optional': True}),
1486    ]
1487
1488
1489class AuthorityKeyIdentifier(Sequence):
1490    _fields = [
1491        ('key_identifier', OctetString, {'implicit': 0, 'optional': True}),
1492        ('authority_cert_issuer', GeneralNames, {'implicit': 1, 'optional': True}),
1493        ('authority_cert_serial_number', Integer, {'implicit': 2, 'optional': True}),
1494    ]
1495
1496
1497class DistributionPointName(Choice):
1498    _alternatives = [
1499        ('full_name', GeneralNames, {'implicit': 0}),
1500        ('name_relative_to_crl_issuer', RelativeDistinguishedName, {'implicit': 1}),
1501    ]
1502
1503
1504class ReasonFlags(BitString):
1505    _map = {
1506        0: 'unused',
1507        1: 'key_compromise',
1508        2: 'ca_compromise',
1509        3: 'affiliation_changed',
1510        4: 'superseded',
1511        5: 'cessation_of_operation',
1512        6: 'certificate_hold',
1513        7: 'privilege_withdrawn',
1514        8: 'aa_compromise',
1515    }
1516
1517
1518class GeneralSubtree(Sequence):
1519    _fields = [
1520        ('base', GeneralName),
1521        ('minimum', Integer, {'implicit': 0, 'default': 0}),
1522        ('maximum', Integer, {'implicit': 1, 'optional': True}),
1523    ]
1524
1525
1526class GeneralSubtrees(SequenceOf):
1527    _child_spec = GeneralSubtree
1528
1529
1530class NameConstraints(Sequence):
1531    _fields = [
1532        ('permitted_subtrees', GeneralSubtrees, {'implicit': 0, 'optional': True}),
1533        ('excluded_subtrees', GeneralSubtrees, {'implicit': 1, 'optional': True}),
1534    ]
1535
1536
1537class DistributionPoint(Sequence):
1538    _fields = [
1539        ('distribution_point', DistributionPointName, {'explicit': 0, 'optional': True}),
1540        ('reasons', ReasonFlags, {'implicit': 1, 'optional': True}),
1541        ('crl_issuer', GeneralNames, {'implicit': 2, 'optional': True}),
1542    ]
1543
1544    _url = False
1545
1546    @property
1547    def url(self):
1548        """
1549        :return:
1550            None or a unicode string of the distribution point's URL
1551        """
1552
1553        if self._url is False:
1554            self._url = None
1555            name = self['distribution_point']
1556            if name.name != 'full_name':
1557                raise ValueError(unwrap(
1558                    '''
1559                    CRL distribution points that are relative to the issuer are
1560                    not supported
1561                    '''
1562                ))
1563
1564            for general_name in name.chosen:
1565                if general_name.name == 'uniform_resource_identifier':
1566                    url = general_name.native
1567                    if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')):
1568                        self._url = url
1569                        break
1570
1571        return self._url
1572
1573
1574class CRLDistributionPoints(SequenceOf):
1575    _child_spec = DistributionPoint
1576
1577
1578class DisplayText(Choice):
1579    _alternatives = [
1580        ('ia5_string', IA5String),
1581        ('visible_string', VisibleString),
1582        ('bmp_string', BMPString),
1583        ('utf8_string', UTF8String),
1584    ]
1585
1586
1587class NoticeNumbers(SequenceOf):
1588    _child_spec = Integer
1589
1590
1591class NoticeReference(Sequence):
1592    _fields = [
1593        ('organization', DisplayText),
1594        ('notice_numbers', NoticeNumbers),
1595    ]
1596
1597
1598class UserNotice(Sequence):
1599    _fields = [
1600        ('notice_ref', NoticeReference, {'optional': True}),
1601        ('explicit_text', DisplayText, {'optional': True}),
1602    ]
1603
1604
1605class PolicyQualifierId(ObjectIdentifier):
1606    _map = {
1607        '1.3.6.1.5.5.7.2.1': 'certification_practice_statement',
1608        '1.3.6.1.5.5.7.2.2': 'user_notice',
1609    }
1610
1611
1612class PolicyQualifierInfo(Sequence):
1613    _fields = [
1614        ('policy_qualifier_id', PolicyQualifierId),
1615        ('qualifier', Any),
1616    ]
1617
1618    _oid_pair = ('policy_qualifier_id', 'qualifier')
1619    _oid_specs = {
1620        'certification_practice_statement': IA5String,
1621        'user_notice': UserNotice,
1622    }
1623
1624
1625class PolicyQualifierInfos(SequenceOf):
1626    _child_spec = PolicyQualifierInfo
1627
1628
1629class PolicyIdentifier(ObjectIdentifier):
1630    _map = {
1631        '2.5.29.32.0': 'any_policy',
1632    }
1633
1634
1635class PolicyInformation(Sequence):
1636    _fields = [
1637        ('policy_identifier', PolicyIdentifier),
1638        ('policy_qualifiers', PolicyQualifierInfos, {'optional': True})
1639    ]
1640
1641
1642class CertificatePolicies(SequenceOf):
1643    _child_spec = PolicyInformation
1644
1645
1646class PolicyMapping(Sequence):
1647    _fields = [
1648        ('issuer_domain_policy', PolicyIdentifier),
1649        ('subject_domain_policy', PolicyIdentifier),
1650    ]
1651
1652
1653class PolicyMappings(SequenceOf):
1654    _child_spec = PolicyMapping
1655
1656
1657class PolicyConstraints(Sequence):
1658    _fields = [
1659        ('require_explicit_policy', Integer, {'implicit': 0, 'optional': True}),
1660        ('inhibit_policy_mapping', Integer, {'implicit': 1, 'optional': True}),
1661    ]
1662
1663
1664class KeyPurposeId(ObjectIdentifier):
1665    _map = {
1666        # https://tools.ietf.org/html/rfc5280#page-45
1667        '2.5.29.37.0': 'any_extended_key_usage',
1668        '1.3.6.1.5.5.7.3.1': 'server_auth',
1669        '1.3.6.1.5.5.7.3.2': 'client_auth',
1670        '1.3.6.1.5.5.7.3.3': 'code_signing',
1671        '1.3.6.1.5.5.7.3.4': 'email_protection',
1672        '1.3.6.1.5.5.7.3.5': 'ipsec_end_system',
1673        '1.3.6.1.5.5.7.3.6': 'ipsec_tunnel',
1674        '1.3.6.1.5.5.7.3.7': 'ipsec_user',
1675        '1.3.6.1.5.5.7.3.8': 'time_stamping',
1676        '1.3.6.1.5.5.7.3.9': 'ocsp_signing',
1677        # http://tools.ietf.org/html/rfc3029.html#page-9
1678        '1.3.6.1.5.5.7.3.10': 'dvcs',
1679        # http://tools.ietf.org/html/rfc6268.html#page-16
1680        '1.3.6.1.5.5.7.3.13': 'eap_over_ppp',
1681        '1.3.6.1.5.5.7.3.14': 'eap_over_lan',
1682        # https://tools.ietf.org/html/rfc5055#page-76
1683        '1.3.6.1.5.5.7.3.15': 'scvp_server',
1684        '1.3.6.1.5.5.7.3.16': 'scvp_client',
1685        # https://tools.ietf.org/html/rfc4945#page-31
1686        '1.3.6.1.5.5.7.3.17': 'ipsec_ike',
1687        # https://tools.ietf.org/html/rfc5415#page-38
1688        '1.3.6.1.5.5.7.3.18': 'capwap_ac',
1689        '1.3.6.1.5.5.7.3.19': 'capwap_wtp',
1690        # https://tools.ietf.org/html/rfc5924#page-8
1691        '1.3.6.1.5.5.7.3.20': 'sip_domain',
1692        # https://tools.ietf.org/html/rfc6187#page-7
1693        '1.3.6.1.5.5.7.3.21': 'secure_shell_client',
1694        '1.3.6.1.5.5.7.3.22': 'secure_shell_server',
1695        # https://tools.ietf.org/html/rfc6494#page-7
1696        '1.3.6.1.5.5.7.3.23': 'send_router',
1697        '1.3.6.1.5.5.7.3.24': 'send_proxied_router',
1698        '1.3.6.1.5.5.7.3.25': 'send_owner',
1699        '1.3.6.1.5.5.7.3.26': 'send_proxied_owner',
1700        # https://tools.ietf.org/html/rfc6402#page-10
1701        '1.3.6.1.5.5.7.3.27': 'cmc_ca',
1702        '1.3.6.1.5.5.7.3.28': 'cmc_ra',
1703        '1.3.6.1.5.5.7.3.29': 'cmc_archive',
1704        # https://tools.ietf.org/html/draft-ietf-sidr-bgpsec-pki-profiles-15#page-6
1705        '1.3.6.1.5.5.7.3.30': 'bgpspec_router',
1706        # https://www.ietf.org/proceedings/44/I-D/draft-ietf-ipsec-pki-req-01.txt
1707        '1.3.6.1.5.5.8.2.2': 'ike_intermediate',
1708        # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378132(v=vs.85).aspx
1709        # and https://support.microsoft.com/en-us/kb/287547
1710        '1.3.6.1.4.1.311.10.3.1': 'microsoft_trust_list_signing',
1711        '1.3.6.1.4.1.311.10.3.2': 'microsoft_time_stamp_signing',
1712        '1.3.6.1.4.1.311.10.3.3': 'microsoft_server_gated',
1713        '1.3.6.1.4.1.311.10.3.3.1': 'microsoft_serialized',
1714        '1.3.6.1.4.1.311.10.3.4': 'microsoft_efs',
1715        '1.3.6.1.4.1.311.10.3.4.1': 'microsoft_efs_recovery',
1716        '1.3.6.1.4.1.311.10.3.5': 'microsoft_whql',
1717        '1.3.6.1.4.1.311.10.3.6': 'microsoft_nt5',
1718        '1.3.6.1.4.1.311.10.3.7': 'microsoft_oem_whql',
1719        '1.3.6.1.4.1.311.10.3.8': 'microsoft_embedded_nt',
1720        '1.3.6.1.4.1.311.10.3.9': 'microsoft_root_list_signer',
1721        '1.3.6.1.4.1.311.10.3.10': 'microsoft_qualified_subordination',
1722        '1.3.6.1.4.1.311.10.3.11': 'microsoft_key_recovery',
1723        '1.3.6.1.4.1.311.10.3.12': 'microsoft_document_signing',
1724        '1.3.6.1.4.1.311.10.3.13': 'microsoft_lifetime_signing',
1725        '1.3.6.1.4.1.311.10.3.14': 'microsoft_mobile_device_software',
1726        # https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography
1727        '1.3.6.1.4.1.311.20.2.2': 'microsoft_smart_card_logon',
1728        # https://opensource.apple.com/source
1729        #  - /Security/Security-57031.40.6/Security/libsecurity_keychain/lib/SecPolicy.cpp
1730        #  - /libsecurity_cssm/libsecurity_cssm-36064/lib/oidsalg.c
1731        '1.2.840.113635.100.1.2': 'apple_x509_basic',
1732        '1.2.840.113635.100.1.3': 'apple_ssl',
1733        '1.2.840.113635.100.1.4': 'apple_local_cert_gen',
1734        '1.2.840.113635.100.1.5': 'apple_csr_gen',
1735        '1.2.840.113635.100.1.6': 'apple_revocation_crl',
1736        '1.2.840.113635.100.1.7': 'apple_revocation_ocsp',
1737        '1.2.840.113635.100.1.8': 'apple_smime',
1738        '1.2.840.113635.100.1.9': 'apple_eap',
1739        '1.2.840.113635.100.1.10': 'apple_software_update_signing',
1740        '1.2.840.113635.100.1.11': 'apple_ipsec',
1741        '1.2.840.113635.100.1.12': 'apple_ichat',
1742        '1.2.840.113635.100.1.13': 'apple_resource_signing',
1743        '1.2.840.113635.100.1.14': 'apple_pkinit_client',
1744        '1.2.840.113635.100.1.15': 'apple_pkinit_server',
1745        '1.2.840.113635.100.1.16': 'apple_code_signing',
1746        '1.2.840.113635.100.1.17': 'apple_package_signing',
1747        '1.2.840.113635.100.1.18': 'apple_id_validation',
1748        '1.2.840.113635.100.1.20': 'apple_time_stamping',
1749        '1.2.840.113635.100.1.21': 'apple_revocation',
1750        '1.2.840.113635.100.1.22': 'apple_passbook_signing',
1751        '1.2.840.113635.100.1.23': 'apple_mobile_store',
1752        '1.2.840.113635.100.1.24': 'apple_escrow_service',
1753        '1.2.840.113635.100.1.25': 'apple_profile_signer',
1754        '1.2.840.113635.100.1.26': 'apple_qa_profile_signer',
1755        '1.2.840.113635.100.1.27': 'apple_test_mobile_store',
1756        '1.2.840.113635.100.1.28': 'apple_otapki_signer',
1757        '1.2.840.113635.100.1.29': 'apple_test_otapki_signer',
1758        '1.2.840.113625.100.1.30': 'apple_id_validation_record_signing_policy',
1759        '1.2.840.113625.100.1.31': 'apple_smp_encryption',
1760        '1.2.840.113625.100.1.32': 'apple_test_smp_encryption',
1761        '1.2.840.113635.100.1.33': 'apple_server_authentication',
1762        '1.2.840.113635.100.1.34': 'apple_pcs_escrow_service',
1763        # http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.201-2.pdf
1764        '2.16.840.1.101.3.6.8': 'piv_card_authentication',
1765        '2.16.840.1.101.3.6.7': 'piv_content_signing',
1766        # https://tools.ietf.org/html/rfc4556.html
1767        '1.3.6.1.5.2.3.4': 'pkinit_kpclientauth',
1768        '1.3.6.1.5.2.3.5': 'pkinit_kpkdc',
1769        # https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSig/changes.html
1770        '1.2.840.113583.1.1.5': 'adobe_authentic_documents_trust',
1771        # https://www.idmanagement.gov/wp-content/uploads/sites/1171/uploads/fpki-pivi-cert-profiles.pdf
1772        '2.16.840.1.101.3.8.7': 'fpki_pivi_content_signing'
1773    }
1774
1775
1776class ExtKeyUsageSyntax(SequenceOf):
1777    _child_spec = KeyPurposeId
1778
1779
1780class AccessMethod(ObjectIdentifier):
1781    _map = {
1782        '1.3.6.1.5.5.7.48.1': 'ocsp',
1783        '1.3.6.1.5.5.7.48.2': 'ca_issuers',
1784        '1.3.6.1.5.5.7.48.3': 'time_stamping',
1785        '1.3.6.1.5.5.7.48.5': 'ca_repository',
1786    }
1787
1788
1789class AccessDescription(Sequence):
1790    _fields = [
1791        ('access_method', AccessMethod),
1792        ('access_location', GeneralName),
1793    ]
1794
1795
1796class AuthorityInfoAccessSyntax(SequenceOf):
1797    _child_spec = AccessDescription
1798
1799
1800class SubjectInfoAccessSyntax(SequenceOf):
1801    _child_spec = AccessDescription
1802
1803
1804# https://tools.ietf.org/html/rfc7633
1805class Features(SequenceOf):
1806    _child_spec = Integer
1807
1808
1809class EntrustVersionInfo(Sequence):
1810    _fields = [
1811        ('entrust_vers', GeneralString),
1812        ('entrust_info_flags', BitString)
1813    ]
1814
1815
1816class NetscapeCertificateType(BitString):
1817    _map = {
1818        0: 'ssl_client',
1819        1: 'ssl_server',
1820        2: 'email',
1821        3: 'object_signing',
1822        4: 'reserved',
1823        5: 'ssl_ca',
1824        6: 'email_ca',
1825        7: 'object_signing_ca',
1826    }
1827
1828
1829class Version(Integer):
1830    _map = {
1831        0: 'v1',
1832        1: 'v2',
1833        2: 'v3',
1834    }
1835
1836
1837class TPMSpecification(Sequence):
1838    _fields = [
1839        ('family', UTF8String),
1840        ('level', Integer),
1841        ('revision', Integer),
1842    ]
1843
1844
1845class SetOfTPMSpecification(SetOf):
1846    _child_spec = TPMSpecification
1847
1848
1849class TCGSpecificationVersion(Sequence):
1850    _fields = [
1851        ('major_version', Integer),
1852        ('minor_version', Integer),
1853        ('revision', Integer),
1854    ]
1855
1856
1857class TCGPlatformSpecification(Sequence):
1858    _fields = [
1859        ('version', TCGSpecificationVersion),
1860        ('platform_class', OctetString),
1861    ]
1862
1863
1864class SetOfTCGPlatformSpecification(SetOf):
1865    _child_spec = TCGPlatformSpecification
1866
1867
1868class EKGenerationType(Enumerated):
1869    _map = {
1870        0: 'internal',
1871        1: 'injected',
1872        2: 'internal_revocable',
1873        3: 'injected_revocable',
1874    }
1875
1876
1877class EKGenerationLocation(Enumerated):
1878    _map = {
1879        0: 'tpm_manufacturer',
1880        1: 'platform_manufacturer',
1881        2: 'ek_cert_signer',
1882    }
1883
1884
1885class EKCertificateGenerationLocation(Enumerated):
1886    _map = {
1887        0: 'tpm_manufacturer',
1888        1: 'platform_manufacturer',
1889        2: 'ek_cert_signer',
1890    }
1891
1892
1893class EvaluationAssuranceLevel(Enumerated):
1894    _map = {
1895        1: 'level1',
1896        2: 'level2',
1897        3: 'level3',
1898        4: 'level4',
1899        5: 'level5',
1900        6: 'level6',
1901        7: 'level7',
1902    }
1903
1904
1905class EvaluationStatus(Enumerated):
1906    _map = {
1907        0: 'designed_to_meet',
1908        1: 'evaluation_in_progress',
1909        2: 'evaluation_completed',
1910    }
1911
1912
1913class StrengthOfFunction(Enumerated):
1914    _map = {
1915        0: 'basic',
1916        1: 'medium',
1917        2: 'high',
1918    }
1919
1920
1921class URIReference(Sequence):
1922    _fields = [
1923        ('uniform_resource_identifier', IA5String),
1924        ('hash_algorithm', DigestAlgorithm, {'optional': True}),
1925        ('hash_value', BitString, {'optional': True}),
1926    ]
1927
1928
1929class CommonCriteriaMeasures(Sequence):
1930    _fields = [
1931        ('version', IA5String),
1932        ('assurance_level', EvaluationAssuranceLevel),
1933        ('evaluation_status', EvaluationStatus),
1934        ('plus', Boolean, {'default': False}),
1935        ('strengh_of_function', StrengthOfFunction, {'implicit': 0, 'optional': True}),
1936        ('profile_oid', ObjectIdentifier, {'implicit': 1, 'optional': True}),
1937        ('profile_url', URIReference, {'implicit': 2, 'optional': True}),
1938        ('target_oid', ObjectIdentifier, {'implicit': 3, 'optional': True}),
1939        ('target_uri', URIReference, {'implicit': 4, 'optional': True}),
1940    ]
1941
1942
1943class SecurityLevel(Enumerated):
1944    _map = {
1945        1: 'level1',
1946        2: 'level2',
1947        3: 'level3',
1948        4: 'level4',
1949    }
1950
1951
1952class FIPSLevel(Sequence):
1953    _fields = [
1954        ('version', IA5String),
1955        ('level', SecurityLevel),
1956        ('plus', Boolean, {'default': False}),
1957    ]
1958
1959
1960class TPMSecurityAssertions(Sequence):
1961    _fields = [
1962        ('version', Version, {'default': 'v1'}),
1963        ('field_upgradable', Boolean, {'default': False}),
1964        ('ek_generation_type', EKGenerationType, {'implicit': 0, 'optional': True}),
1965        ('ek_generation_location', EKGenerationLocation, {'implicit': 1, 'optional': True}),
1966        ('ek_certificate_generation_location', EKCertificateGenerationLocation, {'implicit': 2, 'optional': True}),
1967        ('cc_info', CommonCriteriaMeasures, {'implicit': 3, 'optional': True}),
1968        ('fips_level', FIPSLevel, {'implicit': 4, 'optional': True}),
1969        ('iso_9000_certified', Boolean, {'implicit': 5, 'default': False}),
1970        ('iso_9000_uri', IA5String, {'optional': True}),
1971    ]
1972
1973
1974class SetOfTPMSecurityAssertions(SetOf):
1975    _child_spec = TPMSecurityAssertions
1976
1977
1978class SubjectDirectoryAttributeId(ObjectIdentifier):
1979    _map = {
1980        # https://tools.ietf.org/html/rfc2256#page-11
1981        '2.5.4.52': 'supported_algorithms',
1982        # https://www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf
1983        '2.23.133.2.16': 'tpm_specification',
1984        '2.23.133.2.17': 'tcg_platform_specification',
1985        '2.23.133.2.18': 'tpm_security_assertions',
1986        # https://tools.ietf.org/html/rfc3739#page-18
1987        '1.3.6.1.5.5.7.9.1': 'pda_date_of_birth',
1988        '1.3.6.1.5.5.7.9.2': 'pda_place_of_birth',
1989        '1.3.6.1.5.5.7.9.3': 'pda_gender',
1990        '1.3.6.1.5.5.7.9.4': 'pda_country_of_citizenship',
1991        '1.3.6.1.5.5.7.9.5': 'pda_country_of_residence',
1992        # https://holtstrom.com/michael/tools/asn1decoder.php
1993        '1.2.840.113533.7.68.29': 'entrust_user_role',
1994    }
1995
1996
1997class SetOfGeneralizedTime(SetOf):
1998    _child_spec = GeneralizedTime
1999
2000
2001class SetOfDirectoryString(SetOf):
2002    _child_spec = DirectoryString
2003
2004
2005class SetOfPrintableString(SetOf):
2006    _child_spec = PrintableString
2007
2008
2009class SupportedAlgorithm(Sequence):
2010    _fields = [
2011        ('algorithm_identifier', AnyAlgorithmIdentifier),
2012        ('intended_usage', KeyUsage, {'explicit': 0, 'optional': True}),
2013        ('intended_certificate_policies', CertificatePolicies, {'explicit': 1, 'optional': True}),
2014    ]
2015
2016
2017class SetOfSupportedAlgorithm(SetOf):
2018    _child_spec = SupportedAlgorithm
2019
2020
2021class SubjectDirectoryAttribute(Sequence):
2022    _fields = [
2023        ('type', SubjectDirectoryAttributeId),
2024        ('values', Any),
2025    ]
2026
2027    _oid_pair = ('type', 'values')
2028    _oid_specs = {
2029        'supported_algorithms': SetOfSupportedAlgorithm,
2030        'tpm_specification': SetOfTPMSpecification,
2031        'tcg_platform_specification': SetOfTCGPlatformSpecification,
2032        'tpm_security_assertions': SetOfTPMSecurityAssertions,
2033        'pda_date_of_birth': SetOfGeneralizedTime,
2034        'pda_place_of_birth': SetOfDirectoryString,
2035        'pda_gender': SetOfPrintableString,
2036        'pda_country_of_citizenship': SetOfPrintableString,
2037        'pda_country_of_residence': SetOfPrintableString,
2038    }
2039
2040    def _values_spec(self):
2041        type_ = self['type'].native
2042        if type_ in self._oid_specs:
2043            return self._oid_specs[type_]
2044        return SetOf
2045
2046    _spec_callbacks = {
2047        'values': _values_spec
2048    }
2049
2050
2051class SubjectDirectoryAttributes(SequenceOf):
2052    _child_spec = SubjectDirectoryAttribute
2053
2054
2055class ExtensionId(ObjectIdentifier):
2056    _map = {
2057        '2.5.29.9': 'subject_directory_attributes',
2058        '2.5.29.14': 'key_identifier',
2059        '2.5.29.15': 'key_usage',
2060        '2.5.29.16': 'private_key_usage_period',
2061        '2.5.29.17': 'subject_alt_name',
2062        '2.5.29.18': 'issuer_alt_name',
2063        '2.5.29.19': 'basic_constraints',
2064        '2.5.29.30': 'name_constraints',
2065        '2.5.29.31': 'crl_distribution_points',
2066        '2.5.29.32': 'certificate_policies',
2067        '2.5.29.33': 'policy_mappings',
2068        '2.5.29.35': 'authority_key_identifier',
2069        '2.5.29.36': 'policy_constraints',
2070        '2.5.29.37': 'extended_key_usage',
2071        '2.5.29.46': 'freshest_crl',
2072        '2.5.29.54': 'inhibit_any_policy',
2073        '1.3.6.1.5.5.7.1.1': 'authority_information_access',
2074        '1.3.6.1.5.5.7.1.11': 'subject_information_access',
2075        # https://tools.ietf.org/html/rfc7633
2076        '1.3.6.1.5.5.7.1.24': 'tls_feature',
2077        '1.3.6.1.5.5.7.48.1.5': 'ocsp_no_check',
2078        '1.2.840.113533.7.65.0': 'entrust_version_extension',
2079        '2.16.840.1.113730.1.1': 'netscape_certificate_type',
2080        # https://tools.ietf.org/html/rfc6962.html#page-14
2081        '1.3.6.1.4.1.11129.2.4.2': 'signed_certificate_timestamp_list',
2082        # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/3aec3e50-511a-42f9-a5d5-240af503e470
2083        '1.3.6.1.4.1.311.20.2': 'microsoft_enroll_certtype',
2084    }
2085
2086
2087class Extension(Sequence):
2088    _fields = [
2089        ('extn_id', ExtensionId),
2090        ('critical', Boolean, {'default': False}),
2091        ('extn_value', ParsableOctetString),
2092    ]
2093
2094    _oid_pair = ('extn_id', 'extn_value')
2095    _oid_specs = {
2096        'subject_directory_attributes': SubjectDirectoryAttributes,
2097        'key_identifier': OctetString,
2098        'key_usage': KeyUsage,
2099        'private_key_usage_period': PrivateKeyUsagePeriod,
2100        'subject_alt_name': GeneralNames,
2101        'issuer_alt_name': GeneralNames,
2102        'basic_constraints': BasicConstraints,
2103        'name_constraints': NameConstraints,
2104        'crl_distribution_points': CRLDistributionPoints,
2105        'certificate_policies': CertificatePolicies,
2106        'policy_mappings': PolicyMappings,
2107        'authority_key_identifier': AuthorityKeyIdentifier,
2108        'policy_constraints': PolicyConstraints,
2109        'extended_key_usage': ExtKeyUsageSyntax,
2110        'freshest_crl': CRLDistributionPoints,
2111        'inhibit_any_policy': Integer,
2112        'authority_information_access': AuthorityInfoAccessSyntax,
2113        'subject_information_access': SubjectInfoAccessSyntax,
2114        'tls_feature': Features,
2115        'ocsp_no_check': Null,
2116        'entrust_version_extension': EntrustVersionInfo,
2117        'netscape_certificate_type': NetscapeCertificateType,
2118        'signed_certificate_timestamp_list': OctetString,
2119        # Not UTF8String as Microsofts docs claim, see:
2120        # https://www.alvestrand.no/objectid/1.3.6.1.4.1.311.20.2.html
2121        'microsoft_enroll_certtype': BMPString,
2122    }
2123
2124
2125class Extensions(SequenceOf):
2126    _child_spec = Extension
2127
2128
2129class TbsCertificate(Sequence):
2130    _fields = [
2131        ('version', Version, {'explicit': 0, 'default': 'v1'}),
2132        ('serial_number', Integer),
2133        ('signature', SignedDigestAlgorithm),
2134        ('issuer', Name),
2135        ('validity', Validity),
2136        ('subject', Name),
2137        ('subject_public_key_info', PublicKeyInfo),
2138        ('issuer_unique_id', OctetBitString, {'implicit': 1, 'optional': True}),
2139        ('subject_unique_id', OctetBitString, {'implicit': 2, 'optional': True}),
2140        ('extensions', Extensions, {'explicit': 3, 'optional': True}),
2141    ]
2142
2143
2144class Certificate(Sequence):
2145    _fields = [
2146        ('tbs_certificate', TbsCertificate),
2147        ('signature_algorithm', SignedDigestAlgorithm),
2148        ('signature_value', OctetBitString),
2149    ]
2150
2151    _processed_extensions = False
2152    _critical_extensions = None
2153    _subject_directory_attributes_value = None
2154    _key_identifier_value = None
2155    _key_usage_value = None
2156    _subject_alt_name_value = None
2157    _issuer_alt_name_value = None
2158    _basic_constraints_value = None
2159    _name_constraints_value = None
2160    _crl_distribution_points_value = None
2161    _certificate_policies_value = None
2162    _policy_mappings_value = None
2163    _authority_key_identifier_value = None
2164    _policy_constraints_value = None
2165    _freshest_crl_value = None
2166    _inhibit_any_policy_value = None
2167    _extended_key_usage_value = None
2168    _authority_information_access_value = None
2169    _subject_information_access_value = None
2170    _private_key_usage_period_value = None
2171    _tls_feature_value = None
2172    _ocsp_no_check_value = None
2173    _issuer_serial = None
2174    _authority_issuer_serial = False
2175    _crl_distribution_points = None
2176    _delta_crl_distribution_points = None
2177    _valid_domains = None
2178    _valid_ips = None
2179    _self_issued = None
2180    _self_signed = None
2181    _sha1 = None
2182    _sha256 = None
2183
2184    def _set_extensions(self):
2185        """
2186        Sets common named extensions to private attributes and creates a list
2187        of critical extensions
2188        """
2189
2190        self._critical_extensions = set()
2191
2192        for extension in self['tbs_certificate']['extensions']:
2193            name = extension['extn_id'].native
2194            attribute_name = '_%s_value' % name
2195            if hasattr(self, attribute_name):
2196                setattr(self, attribute_name, extension['extn_value'].parsed)
2197            if extension['critical'].native:
2198                self._critical_extensions.add(name)
2199
2200        self._processed_extensions = True
2201
2202    @property
2203    def critical_extensions(self):
2204        """
2205        Returns a set of the names (or OID if not a known extension) of the
2206        extensions marked as critical
2207
2208        :return:
2209            A set of unicode strings
2210        """
2211
2212        if not self._processed_extensions:
2213            self._set_extensions()
2214        return self._critical_extensions
2215
2216    @property
2217    def private_key_usage_period_value(self):
2218        """
2219        This extension is used to constrain the period over which the subject
2220        private key may be used
2221
2222        :return:
2223            None or a PrivateKeyUsagePeriod object
2224        """
2225
2226        if not self._processed_extensions:
2227            self._set_extensions()
2228        return self._private_key_usage_period_value
2229
2230    @property
2231    def subject_directory_attributes_value(self):
2232        """
2233        This extension is used to contain additional identification attributes
2234        about the subject.
2235
2236        :return:
2237            None or a SubjectDirectoryAttributes object
2238        """
2239
2240        if not self._processed_extensions:
2241            self._set_extensions()
2242        return self._subject_directory_attributes_value
2243
2244    @property
2245    def key_identifier_value(self):
2246        """
2247        This extension is used to help in creating certificate validation paths.
2248        It contains an identifier that should generally, but is not guaranteed
2249        to, be unique.
2250
2251        :return:
2252            None or an OctetString object
2253        """
2254
2255        if not self._processed_extensions:
2256            self._set_extensions()
2257        return self._key_identifier_value
2258
2259    @property
2260    def key_usage_value(self):
2261        """
2262        This extension is used to define the purpose of the public key
2263        contained within the certificate.
2264
2265        :return:
2266            None or a KeyUsage
2267        """
2268
2269        if not self._processed_extensions:
2270            self._set_extensions()
2271        return self._key_usage_value
2272
2273    @property
2274    def subject_alt_name_value(self):
2275        """
2276        This extension allows for additional names to be associate with the
2277        subject of the certificate. While it may contain a whole host of
2278        possible names, it is usually used to allow certificates to be used
2279        with multiple different domain names.
2280
2281        :return:
2282            None or a GeneralNames object
2283        """
2284
2285        if not self._processed_extensions:
2286            self._set_extensions()
2287        return self._subject_alt_name_value
2288
2289    @property
2290    def issuer_alt_name_value(self):
2291        """
2292        This extension allows associating one or more alternative names with
2293        the issuer of the certificate.
2294
2295        :return:
2296            None or an x509.GeneralNames object
2297        """
2298
2299        if not self._processed_extensions:
2300            self._set_extensions()
2301        return self._issuer_alt_name_value
2302
2303    @property
2304    def basic_constraints_value(self):
2305        """
2306        This extension is used to determine if the subject of the certificate
2307        is a CA, and if so, what the maximum number of intermediate CA certs
2308        after this are, before an end-entity certificate is found.
2309
2310        :return:
2311            None or a BasicConstraints object
2312        """
2313
2314        if not self._processed_extensions:
2315            self._set_extensions()
2316        return self._basic_constraints_value
2317
2318    @property
2319    def name_constraints_value(self):
2320        """
2321        This extension is used in CA certificates, and is used to limit the
2322        possible names of certificates issued.
2323
2324        :return:
2325            None or a NameConstraints object
2326        """
2327
2328        if not self._processed_extensions:
2329            self._set_extensions()
2330        return self._name_constraints_value
2331
2332    @property
2333    def crl_distribution_points_value(self):
2334        """
2335        This extension is used to help in locating the CRL for this certificate.
2336
2337        :return:
2338            None or a CRLDistributionPoints object
2339            extension
2340        """
2341
2342        if not self._processed_extensions:
2343            self._set_extensions()
2344        return self._crl_distribution_points_value
2345
2346    @property
2347    def certificate_policies_value(self):
2348        """
2349        This extension defines policies in CA certificates under which
2350        certificates may be issued. In end-entity certificates, the inclusion
2351        of a policy indicates the issuance of the certificate follows the
2352        policy.
2353
2354        :return:
2355            None or a CertificatePolicies object
2356        """
2357
2358        if not self._processed_extensions:
2359            self._set_extensions()
2360        return self._certificate_policies_value
2361
2362    @property
2363    def policy_mappings_value(self):
2364        """
2365        This extension allows mapping policy OIDs to other OIDs. This is used
2366        to allow different policies to be treated as equivalent in the process
2367        of validation.
2368
2369        :return:
2370            None or a PolicyMappings object
2371        """
2372
2373        if not self._processed_extensions:
2374            self._set_extensions()
2375        return self._policy_mappings_value
2376
2377    @property
2378    def authority_key_identifier_value(self):
2379        """
2380        This extension helps in identifying the public key with which to
2381        validate the authenticity of the certificate.
2382
2383        :return:
2384            None or an AuthorityKeyIdentifier object
2385        """
2386
2387        if not self._processed_extensions:
2388            self._set_extensions()
2389        return self._authority_key_identifier_value
2390
2391    @property
2392    def policy_constraints_value(self):
2393        """
2394        This extension is used to control if policy mapping is allowed and
2395        when policies are required.
2396
2397        :return:
2398            None or a PolicyConstraints object
2399        """
2400
2401        if not self._processed_extensions:
2402            self._set_extensions()
2403        return self._policy_constraints_value
2404
2405    @property
2406    def freshest_crl_value(self):
2407        """
2408        This extension is used to help locate any available delta CRLs
2409
2410        :return:
2411            None or an CRLDistributionPoints object
2412        """
2413
2414        if not self._processed_extensions:
2415            self._set_extensions()
2416        return self._freshest_crl_value
2417
2418    @property
2419    def inhibit_any_policy_value(self):
2420        """
2421        This extension is used to prevent mapping of the any policy to
2422        specific requirements
2423
2424        :return:
2425            None or a Integer object
2426        """
2427
2428        if not self._processed_extensions:
2429            self._set_extensions()
2430        return self._inhibit_any_policy_value
2431
2432    @property
2433    def extended_key_usage_value(self):
2434        """
2435        This extension is used to define additional purposes for the public key
2436        beyond what is contained in the basic constraints.
2437
2438        :return:
2439            None or an ExtKeyUsageSyntax object
2440        """
2441
2442        if not self._processed_extensions:
2443            self._set_extensions()
2444        return self._extended_key_usage_value
2445
2446    @property
2447    def authority_information_access_value(self):
2448        """
2449        This extension is used to locate the CA certificate used to sign this
2450        certificate, or the OCSP responder for this certificate.
2451
2452        :return:
2453            None or an AuthorityInfoAccessSyntax object
2454        """
2455
2456        if not self._processed_extensions:
2457            self._set_extensions()
2458        return self._authority_information_access_value
2459
2460    @property
2461    def subject_information_access_value(self):
2462        """
2463        This extension is used to access information about the subject of this
2464        certificate.
2465
2466        :return:
2467            None or a SubjectInfoAccessSyntax object
2468        """
2469
2470        if not self._processed_extensions:
2471            self._set_extensions()
2472        return self._subject_information_access_value
2473
2474    @property
2475    def tls_feature_value(self):
2476        """
2477        This extension is used to list the TLS features a server must respond
2478        with if a client initiates a request supporting them.
2479
2480        :return:
2481            None or a Features object
2482        """
2483
2484        if not self._processed_extensions:
2485            self._set_extensions()
2486        return self._tls_feature_value
2487
2488    @property
2489    def ocsp_no_check_value(self):
2490        """
2491        This extension is used on certificates of OCSP responders, indicating
2492        that revocation information for the certificate should never need to
2493        be verified, thus preventing possible loops in path validation.
2494
2495        :return:
2496            None or a Null object (if present)
2497        """
2498
2499        if not self._processed_extensions:
2500            self._set_extensions()
2501        return self._ocsp_no_check_value
2502
2503    @property
2504    def signature(self):
2505        """
2506        :return:
2507            A byte string of the signature
2508        """
2509
2510        return self['signature_value'].native
2511
2512    @property
2513    def signature_algo(self):
2514        """
2515        :return:
2516            A unicode string of "rsassa_pkcs1v15", "rsassa_pss", "dsa", "ecdsa"
2517        """
2518
2519        return self['signature_algorithm'].signature_algo
2520
2521    @property
2522    def hash_algo(self):
2523        """
2524        :return:
2525            A unicode string of "md2", "md5", "sha1", "sha224", "sha256",
2526            "sha384", "sha512", "sha512_224", "sha512_256"
2527        """
2528
2529        return self['signature_algorithm'].hash_algo
2530
2531    @property
2532    def public_key(self):
2533        """
2534        :return:
2535            The PublicKeyInfo object for this certificate
2536        """
2537
2538        return self['tbs_certificate']['subject_public_key_info']
2539
2540    @property
2541    def subject(self):
2542        """
2543        :return:
2544            The Name object for the subject of this certificate
2545        """
2546
2547        return self['tbs_certificate']['subject']
2548
2549    @property
2550    def issuer(self):
2551        """
2552        :return:
2553            The Name object for the issuer of this certificate
2554        """
2555
2556        return self['tbs_certificate']['issuer']
2557
2558    @property
2559    def serial_number(self):
2560        """
2561        :return:
2562            An integer of the certificate's serial number
2563        """
2564
2565        return self['tbs_certificate']['serial_number'].native
2566
2567    @property
2568    def key_identifier(self):
2569        """
2570        :return:
2571            None or a byte string of the certificate's key identifier from the
2572            key identifier extension
2573        """
2574
2575        if not self.key_identifier_value:
2576            return None
2577
2578        return self.key_identifier_value.native
2579
2580    @property
2581    def issuer_serial(self):
2582        """
2583        :return:
2584            A byte string of the SHA-256 hash of the issuer concatenated with
2585            the ascii character ":", concatenated with the serial number as
2586            an ascii string
2587        """
2588
2589        if self._issuer_serial is None:
2590            self._issuer_serial = self.issuer.sha256 + b':' + str_cls(self.serial_number).encode('ascii')
2591        return self._issuer_serial
2592
2593    @property
2594    def not_valid_after(self):
2595        """
2596        :return:
2597            A datetime of latest time when the certificate is still valid
2598        """
2599        return self['tbs_certificate']['validity']['not_after'].native
2600
2601    @property
2602    def not_valid_before(self):
2603        """
2604        :return:
2605            A datetime of the earliest time when the certificate is valid
2606        """
2607        return self['tbs_certificate']['validity']['not_before'].native
2608
2609    @property
2610    def authority_key_identifier(self):
2611        """
2612        :return:
2613            None or a byte string of the key_identifier from the authority key
2614            identifier extension
2615        """
2616
2617        if not self.authority_key_identifier_value:
2618            return None
2619
2620        return self.authority_key_identifier_value['key_identifier'].native
2621
2622    @property
2623    def authority_issuer_serial(self):
2624        """
2625        :return:
2626            None or a byte string of the SHA-256 hash of the isser from the
2627            authority key identifier extension concatenated with the ascii
2628            character ":", concatenated with the serial number from the
2629            authority key identifier extension as an ascii string
2630        """
2631
2632        if self._authority_issuer_serial is False:
2633            akiv = self.authority_key_identifier_value
2634            if akiv and akiv['authority_cert_issuer'].native:
2635                issuer = self.authority_key_identifier_value['authority_cert_issuer'][0].chosen
2636                # We untag the element since it is tagged via being a choice from GeneralName
2637                issuer = issuer.untag()
2638                authority_serial = self.authority_key_identifier_value['authority_cert_serial_number'].native
2639                self._authority_issuer_serial = issuer.sha256 + b':' + str_cls(authority_serial).encode('ascii')
2640            else:
2641                self._authority_issuer_serial = None
2642        return self._authority_issuer_serial
2643
2644    @property
2645    def crl_distribution_points(self):
2646        """
2647        Returns complete CRL URLs - does not include delta CRLs
2648
2649        :return:
2650            A list of zero or more DistributionPoint objects
2651        """
2652
2653        if self._crl_distribution_points is None:
2654            self._crl_distribution_points = self._get_http_crl_distribution_points(self.crl_distribution_points_value)
2655        return self._crl_distribution_points
2656
2657    @property
2658    def delta_crl_distribution_points(self):
2659        """
2660        Returns delta CRL URLs - does not include complete CRLs
2661
2662        :return:
2663            A list of zero or more DistributionPoint objects
2664        """
2665
2666        if self._delta_crl_distribution_points is None:
2667            self._delta_crl_distribution_points = self._get_http_crl_distribution_points(self.freshest_crl_value)
2668        return self._delta_crl_distribution_points
2669
2670    def _get_http_crl_distribution_points(self, crl_distribution_points):
2671        """
2672        Fetches the DistributionPoint object for non-relative, HTTP CRLs
2673        referenced by the certificate
2674
2675        :param crl_distribution_points:
2676            A CRLDistributionPoints object to grab the DistributionPoints from
2677
2678        :return:
2679            A list of zero or more DistributionPoint objects
2680        """
2681
2682        output = []
2683
2684        if crl_distribution_points is None:
2685            return []
2686
2687        for distribution_point in crl_distribution_points:
2688            distribution_point_name = distribution_point['distribution_point']
2689            if distribution_point_name is VOID:
2690                continue
2691            # RFC 5280 indicates conforming CA should not use the relative form
2692            if distribution_point_name.name == 'name_relative_to_crl_issuer':
2693                continue
2694            # This library is currently only concerned with HTTP-based CRLs
2695            for general_name in distribution_point_name.chosen:
2696                if general_name.name == 'uniform_resource_identifier':
2697                    output.append(distribution_point)
2698
2699        return output
2700
2701    @property
2702    def ocsp_urls(self):
2703        """
2704        :return:
2705            A list of zero or more unicode strings of the OCSP URLs for this
2706            cert
2707        """
2708
2709        if not self.authority_information_access_value:
2710            return []
2711
2712        output = []
2713        for entry in self.authority_information_access_value:
2714            if entry['access_method'].native == 'ocsp':
2715                location = entry['access_location']
2716                if location.name != 'uniform_resource_identifier':
2717                    continue
2718                url = location.native
2719                if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')):
2720                    output.append(url)
2721        return output
2722
2723    @property
2724    def valid_domains(self):
2725        """
2726        :return:
2727            A list of unicode strings of valid domain names for the certificate.
2728            Wildcard certificates will have a domain in the form: *.example.com
2729        """
2730
2731        if self._valid_domains is None:
2732            self._valid_domains = []
2733
2734            # For the subject alt name extension, we can look at the name of
2735            # the choice selected since it distinguishes between domain names,
2736            # email addresses, IPs, etc
2737            if self.subject_alt_name_value:
2738                for general_name in self.subject_alt_name_value:
2739                    if general_name.name == 'dns_name' and general_name.native not in self._valid_domains:
2740                        self._valid_domains.append(general_name.native)
2741
2742            # If there was no subject alt name extension, and the common name
2743            # in the subject looks like a domain, that is considered the valid
2744            # list. This is done because according to
2745            # https://tools.ietf.org/html/rfc6125#section-6.4.4, the common
2746            # name should not be used if the subject alt name is present.
2747            else:
2748                pattern = re.compile('^(\\*\\.)?(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$')
2749                for rdn in self.subject.chosen:
2750                    for name_type_value in rdn:
2751                        if name_type_value['type'].native == 'common_name':
2752                            value = name_type_value['value'].native
2753                            if pattern.match(value):
2754                                self._valid_domains.append(value)
2755
2756        return self._valid_domains
2757
2758    @property
2759    def valid_ips(self):
2760        """
2761        :return:
2762            A list of unicode strings of valid IP addresses for the certificate
2763        """
2764
2765        if self._valid_ips is None:
2766            self._valid_ips = []
2767
2768            if self.subject_alt_name_value:
2769                for general_name in self.subject_alt_name_value:
2770                    if general_name.name == 'ip_address':
2771                        self._valid_ips.append(general_name.native)
2772
2773        return self._valid_ips
2774
2775    @property
2776    def ca(self):
2777        """
2778        :return;
2779            A boolean - if the certificate is marked as a CA
2780        """
2781
2782        return self.basic_constraints_value and self.basic_constraints_value['ca'].native
2783
2784    @property
2785    def max_path_length(self):
2786        """
2787        :return;
2788            None or an integer of the maximum path length
2789        """
2790
2791        if not self.ca:
2792            return None
2793        return self.basic_constraints_value['path_len_constraint'].native
2794
2795    @property
2796    def self_issued(self):
2797        """
2798        :return:
2799            A boolean - if the certificate is self-issued, as defined by RFC
2800            5280
2801        """
2802
2803        if self._self_issued is None:
2804            self._self_issued = self.subject == self.issuer
2805        return self._self_issued
2806
2807    @property
2808    def self_signed(self):
2809        """
2810        :return:
2811            A unicode string of "no" or "maybe". The "maybe" result will
2812            be returned if the certificate issuer and subject are the same.
2813            If a key identifier and authority key identifier are present,
2814            they will need to match otherwise "no" will be returned.
2815
2816            To verify is a certificate is truly self-signed, the signature
2817            will need to be verified. See the certvalidator package for
2818            one possible solution.
2819        """
2820
2821        if self._self_signed is None:
2822            self._self_signed = 'no'
2823            if self.self_issued:
2824                if self.key_identifier:
2825                    if not self.authority_key_identifier:
2826                        self._self_signed = 'maybe'
2827                    elif self.authority_key_identifier == self.key_identifier:
2828                        self._self_signed = 'maybe'
2829                else:
2830                    self._self_signed = 'maybe'
2831        return self._self_signed
2832
2833    @property
2834    def sha1(self):
2835        """
2836        :return:
2837            The SHA-1 hash of the DER-encoded bytes of this complete certificate
2838        """
2839
2840        if self._sha1 is None:
2841            self._sha1 = hashlib.sha1(self.dump()).digest()
2842        return self._sha1
2843
2844    @property
2845    def sha1_fingerprint(self):
2846        """
2847        :return:
2848            A unicode string of the SHA-1 hash, formatted using hex encoding
2849            with a space between each pair of characters, all uppercase
2850        """
2851
2852        return ' '.join('%02X' % c for c in bytes_to_list(self.sha1))
2853
2854    @property
2855    def sha256(self):
2856        """
2857        :return:
2858            The SHA-256 hash of the DER-encoded bytes of this complete
2859            certificate
2860        """
2861
2862        if self._sha256 is None:
2863            self._sha256 = hashlib.sha256(self.dump()).digest()
2864        return self._sha256
2865
2866    @property
2867    def sha256_fingerprint(self):
2868        """
2869        :return:
2870            A unicode string of the SHA-256 hash, formatted using hex encoding
2871            with a space between each pair of characters, all uppercase
2872        """
2873
2874        return ' '.join('%02X' % c for c in bytes_to_list(self.sha256))
2875
2876    def is_valid_domain_ip(self, domain_ip):
2877        """
2878        Check if a domain name or IP address is valid according to the
2879        certificate
2880
2881        :param domain_ip:
2882            A unicode string of a domain name or IP address
2883
2884        :return:
2885            A boolean - if the domain or IP is valid for the certificate
2886        """
2887
2888        if not isinstance(domain_ip, str_cls):
2889            raise TypeError(unwrap(
2890                '''
2891                domain_ip must be a unicode string, not %s
2892                ''',
2893                type_name(domain_ip)
2894            ))
2895
2896        encoded_domain_ip = domain_ip.encode('idna').decode('ascii').lower()
2897
2898        is_ipv6 = encoded_domain_ip.find(':') != -1
2899        is_ipv4 = not is_ipv6 and re.match('^\\d+\\.\\d+\\.\\d+\\.\\d+$', encoded_domain_ip)
2900        is_domain = not is_ipv6 and not is_ipv4
2901
2902        # Handle domain name checks
2903        if is_domain:
2904            if not self.valid_domains:
2905                return False
2906
2907            domain_labels = encoded_domain_ip.split('.')
2908
2909            for valid_domain in self.valid_domains:
2910                encoded_valid_domain = valid_domain.encode('idna').decode('ascii').lower()
2911                valid_domain_labels = encoded_valid_domain.split('.')
2912
2913                # The domain must be equal in label length to match
2914                if len(valid_domain_labels) != len(domain_labels):
2915                    continue
2916
2917                if valid_domain_labels == domain_labels:
2918                    return True
2919
2920                is_wildcard = self._is_wildcard_domain(encoded_valid_domain)
2921                if is_wildcard and self._is_wildcard_match(domain_labels, valid_domain_labels):
2922                    return True
2923
2924            return False
2925
2926        # Handle IP address checks
2927        if not self.valid_ips:
2928            return False
2929
2930        family = socket.AF_INET if is_ipv4 else socket.AF_INET6
2931        normalized_ip = inet_pton(family, encoded_domain_ip)
2932
2933        for valid_ip in self.valid_ips:
2934            valid_family = socket.AF_INET if valid_ip.find('.') != -1 else socket.AF_INET6
2935            normalized_valid_ip = inet_pton(valid_family, valid_ip)
2936
2937            if normalized_valid_ip == normalized_ip:
2938                return True
2939
2940        return False
2941
2942    def _is_wildcard_domain(self, domain):
2943        """
2944        Checks if a domain is a valid wildcard according to
2945        https://tools.ietf.org/html/rfc6125#section-6.4.3
2946
2947        :param domain:
2948            A unicode string of the domain name, where any U-labels from an IDN
2949            have been converted to A-labels
2950
2951        :return:
2952            A boolean - if the domain is a valid wildcard domain
2953        """
2954
2955        # The * character must be present for a wildcard match, and if there is
2956        # most than one, it is an invalid wildcard specification
2957        if domain.count('*') != 1:
2958            return False
2959
2960        labels = domain.lower().split('.')
2961
2962        if not labels:
2963            return False
2964
2965        # Wildcards may only appear in the left-most label
2966        if labels[0].find('*') == -1:
2967            return False
2968
2969        # Wildcards may not be embedded in an A-label from an IDN
2970        if labels[0][0:4] == 'xn--':
2971            return False
2972
2973        return True
2974
2975    def _is_wildcard_match(self, domain_labels, valid_domain_labels):
2976        """
2977        Determines if the labels in a domain are a match for labels from a
2978        wildcard valid domain name
2979
2980        :param domain_labels:
2981            A list of unicode strings, with A-label form for IDNs, of the labels
2982            in the domain name to check
2983
2984        :param valid_domain_labels:
2985            A list of unicode strings, with A-label form for IDNs, of the labels
2986            in a wildcard domain pattern
2987
2988        :return:
2989            A boolean - if the domain matches the valid domain
2990        """
2991
2992        first_domain_label = domain_labels[0]
2993        other_domain_labels = domain_labels[1:]
2994
2995        wildcard_label = valid_domain_labels[0]
2996        other_valid_domain_labels = valid_domain_labels[1:]
2997
2998        # The wildcard is only allowed in the first label, so if
2999        # The subsequent labels are not equal, there is no match
3000        if other_domain_labels != other_valid_domain_labels:
3001            return False
3002
3003        if wildcard_label == '*':
3004            return True
3005
3006        wildcard_regex = re.compile('^' + wildcard_label.replace('*', '.*') + '$')
3007        if wildcard_regex.match(first_domain_label):
3008            return True
3009
3010        return False
3011
3012
3013# The structures are taken from the OpenSSL source file x_x509a.c, and specify
3014# extra information that is added to X.509 certificates to store trust
3015# information about the certificate.
3016
3017class KeyPurposeIdentifiers(SequenceOf):
3018    _child_spec = KeyPurposeId
3019
3020
3021class SequenceOfAlgorithmIdentifiers(SequenceOf):
3022    _child_spec = AlgorithmIdentifier
3023
3024
3025class CertificateAux(Sequence):
3026    _fields = [
3027        ('trust', KeyPurposeIdentifiers, {'optional': True}),
3028        ('reject', KeyPurposeIdentifiers, {'implicit': 0, 'optional': True}),
3029        ('alias', UTF8String, {'optional': True}),
3030        ('keyid', OctetString, {'optional': True}),
3031        ('other', SequenceOfAlgorithmIdentifiers, {'implicit': 1, 'optional': True}),
3032    ]
3033
3034
3035class TrustedCertificate(Concat):
3036    _child_specs = [Certificate, CertificateAux]
3037