• 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": "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    }
2083
2084
2085class Extension(Sequence):
2086    _fields = [
2087        ('extn_id', ExtensionId),
2088        ('critical', Boolean, {'default': False}),
2089        ('extn_value', ParsableOctetString),
2090    ]
2091
2092    _oid_pair = ('extn_id', 'extn_value')
2093    _oid_specs = {
2094        'subject_directory_attributes': SubjectDirectoryAttributes,
2095        'key_identifier': OctetString,
2096        'key_usage': KeyUsage,
2097        'private_key_usage_period': PrivateKeyUsagePeriod,
2098        'subject_alt_name': GeneralNames,
2099        'issuer_alt_name': GeneralNames,
2100        'basic_constraints': BasicConstraints,
2101        'name_constraints': NameConstraints,
2102        'crl_distribution_points': CRLDistributionPoints,
2103        'certificate_policies': CertificatePolicies,
2104        'policy_mappings': PolicyMappings,
2105        'authority_key_identifier': AuthorityKeyIdentifier,
2106        'policy_constraints': PolicyConstraints,
2107        'extended_key_usage': ExtKeyUsageSyntax,
2108        'freshest_crl': CRLDistributionPoints,
2109        'inhibit_any_policy': Integer,
2110        'authority_information_access': AuthorityInfoAccessSyntax,
2111        'subject_information_access': SubjectInfoAccessSyntax,
2112        'tls_feature': Features,
2113        'ocsp_no_check': Null,
2114        'entrust_version_extension': EntrustVersionInfo,
2115        'netscape_certificate_type': NetscapeCertificateType,
2116        'signed_certificate_timestamp_list': OctetString,
2117    }
2118
2119
2120class Extensions(SequenceOf):
2121    _child_spec = Extension
2122
2123
2124class TbsCertificate(Sequence):
2125    _fields = [
2126        ('version', Version, {'explicit': 0, 'default': 'v1'}),
2127        ('serial_number', Integer),
2128        ('signature', SignedDigestAlgorithm),
2129        ('issuer', Name),
2130        ('validity', Validity),
2131        ('subject', Name),
2132        ('subject_public_key_info', PublicKeyInfo),
2133        ('issuer_unique_id', OctetBitString, {'implicit': 1, 'optional': True}),
2134        ('subject_unique_id', OctetBitString, {'implicit': 2, 'optional': True}),
2135        ('extensions', Extensions, {'explicit': 3, 'optional': True}),
2136    ]
2137
2138
2139class Certificate(Sequence):
2140    _fields = [
2141        ('tbs_certificate', TbsCertificate),
2142        ('signature_algorithm', SignedDigestAlgorithm),
2143        ('signature_value', OctetBitString),
2144    ]
2145
2146    _processed_extensions = False
2147    _critical_extensions = None
2148    _subject_directory_attributes_value = None
2149    _key_identifier_value = None
2150    _key_usage_value = None
2151    _subject_alt_name_value = None
2152    _issuer_alt_name_value = None
2153    _basic_constraints_value = None
2154    _name_constraints_value = None
2155    _crl_distribution_points_value = None
2156    _certificate_policies_value = None
2157    _policy_mappings_value = None
2158    _authority_key_identifier_value = None
2159    _policy_constraints_value = None
2160    _freshest_crl_value = None
2161    _inhibit_any_policy_value = None
2162    _extended_key_usage_value = None
2163    _authority_information_access_value = None
2164    _subject_information_access_value = None
2165    _private_key_usage_period_value = None
2166    _tls_feature_value = None
2167    _ocsp_no_check_value = None
2168    _issuer_serial = None
2169    _authority_issuer_serial = False
2170    _crl_distribution_points = None
2171    _delta_crl_distribution_points = None
2172    _valid_domains = None
2173    _valid_ips = None
2174    _self_issued = None
2175    _self_signed = None
2176    _sha1 = None
2177    _sha256 = None
2178
2179    def _set_extensions(self):
2180        """
2181        Sets common named extensions to private attributes and creates a list
2182        of critical extensions
2183        """
2184
2185        self._critical_extensions = set()
2186
2187        for extension in self['tbs_certificate']['extensions']:
2188            name = extension['extn_id'].native
2189            attribute_name = '_%s_value' % name
2190            if hasattr(self, attribute_name):
2191                setattr(self, attribute_name, extension['extn_value'].parsed)
2192            if extension['critical'].native:
2193                self._critical_extensions.add(name)
2194
2195        self._processed_extensions = True
2196
2197    @property
2198    def critical_extensions(self):
2199        """
2200        Returns a set of the names (or OID if not a known extension) of the
2201        extensions marked as critical
2202
2203        :return:
2204            A set of unicode strings
2205        """
2206
2207        if not self._processed_extensions:
2208            self._set_extensions()
2209        return self._critical_extensions
2210
2211    @property
2212    def private_key_usage_period_value(self):
2213        """
2214        This extension is used to constrain the period over which the subject
2215        private key may be used
2216
2217        :return:
2218            None or a PrivateKeyUsagePeriod object
2219        """
2220
2221        if not self._processed_extensions:
2222            self._set_extensions()
2223        return self._private_key_usage_period_value
2224
2225    @property
2226    def subject_directory_attributes_value(self):
2227        """
2228        This extension is used to contain additional identification attributes
2229        about the subject.
2230
2231        :return:
2232            None or a SubjectDirectoryAttributes object
2233        """
2234
2235        if not self._processed_extensions:
2236            self._set_extensions()
2237        return self._subject_directory_attributes_value
2238
2239    @property
2240    def key_identifier_value(self):
2241        """
2242        This extension is used to help in creating certificate validation paths.
2243        It contains an identifier that should generally, but is not guaranteed
2244        to, be unique.
2245
2246        :return:
2247            None or an OctetString object
2248        """
2249
2250        if not self._processed_extensions:
2251            self._set_extensions()
2252        return self._key_identifier_value
2253
2254    @property
2255    def key_usage_value(self):
2256        """
2257        This extension is used to define the purpose of the public key
2258        contained within the certificate.
2259
2260        :return:
2261            None or a KeyUsage
2262        """
2263
2264        if not self._processed_extensions:
2265            self._set_extensions()
2266        return self._key_usage_value
2267
2268    @property
2269    def subject_alt_name_value(self):
2270        """
2271        This extension allows for additional names to be associate with the
2272        subject of the certificate. While it may contain a whole host of
2273        possible names, it is usually used to allow certificates to be used
2274        with multiple different domain names.
2275
2276        :return:
2277            None or a GeneralNames object
2278        """
2279
2280        if not self._processed_extensions:
2281            self._set_extensions()
2282        return self._subject_alt_name_value
2283
2284    @property
2285    def issuer_alt_name_value(self):
2286        """
2287        This extension allows associating one or more alternative names with
2288        the issuer of the certificate.
2289
2290        :return:
2291            None or an x509.GeneralNames object
2292        """
2293
2294        if not self._processed_extensions:
2295            self._set_extensions()
2296        return self._issuer_alt_name_value
2297
2298    @property
2299    def basic_constraints_value(self):
2300        """
2301        This extension is used to determine if the subject of the certificate
2302        is a CA, and if so, what the maximum number of intermediate CA certs
2303        after this are, before an end-entity certificate is found.
2304
2305        :return:
2306            None or a BasicConstraints object
2307        """
2308
2309        if not self._processed_extensions:
2310            self._set_extensions()
2311        return self._basic_constraints_value
2312
2313    @property
2314    def name_constraints_value(self):
2315        """
2316        This extension is used in CA certificates, and is used to limit the
2317        possible names of certificates issued.
2318
2319        :return:
2320            None or a NameConstraints object
2321        """
2322
2323        if not self._processed_extensions:
2324            self._set_extensions()
2325        return self._name_constraints_value
2326
2327    @property
2328    def crl_distribution_points_value(self):
2329        """
2330        This extension is used to help in locating the CRL for this certificate.
2331
2332        :return:
2333            None or a CRLDistributionPoints object
2334            extension
2335        """
2336
2337        if not self._processed_extensions:
2338            self._set_extensions()
2339        return self._crl_distribution_points_value
2340
2341    @property
2342    def certificate_policies_value(self):
2343        """
2344        This extension defines policies in CA certificates under which
2345        certificates may be issued. In end-entity certificates, the inclusion
2346        of a policy indicates the issuance of the certificate follows the
2347        policy.
2348
2349        :return:
2350            None or a CertificatePolicies object
2351        """
2352
2353        if not self._processed_extensions:
2354            self._set_extensions()
2355        return self._certificate_policies_value
2356
2357    @property
2358    def policy_mappings_value(self):
2359        """
2360        This extension allows mapping policy OIDs to other OIDs. This is used
2361        to allow different policies to be treated as equivalent in the process
2362        of validation.
2363
2364        :return:
2365            None or a PolicyMappings object
2366        """
2367
2368        if not self._processed_extensions:
2369            self._set_extensions()
2370        return self._policy_mappings_value
2371
2372    @property
2373    def authority_key_identifier_value(self):
2374        """
2375        This extension helps in identifying the public key with which to
2376        validate the authenticity of the certificate.
2377
2378        :return:
2379            None or an AuthorityKeyIdentifier object
2380        """
2381
2382        if not self._processed_extensions:
2383            self._set_extensions()
2384        return self._authority_key_identifier_value
2385
2386    @property
2387    def policy_constraints_value(self):
2388        """
2389        This extension is used to control if policy mapping is allowed and
2390        when policies are required.
2391
2392        :return:
2393            None or a PolicyConstraints object
2394        """
2395
2396        if not self._processed_extensions:
2397            self._set_extensions()
2398        return self._policy_constraints_value
2399
2400    @property
2401    def freshest_crl_value(self):
2402        """
2403        This extension is used to help locate any available delta CRLs
2404
2405        :return:
2406            None or an CRLDistributionPoints object
2407        """
2408
2409        if not self._processed_extensions:
2410            self._set_extensions()
2411        return self._freshest_crl_value
2412
2413    @property
2414    def inhibit_any_policy_value(self):
2415        """
2416        This extension is used to prevent mapping of the any policy to
2417        specific requirements
2418
2419        :return:
2420            None or a Integer object
2421        """
2422
2423        if not self._processed_extensions:
2424            self._set_extensions()
2425        return self._inhibit_any_policy_value
2426
2427    @property
2428    def extended_key_usage_value(self):
2429        """
2430        This extension is used to define additional purposes for the public key
2431        beyond what is contained in the basic constraints.
2432
2433        :return:
2434            None or an ExtKeyUsageSyntax object
2435        """
2436
2437        if not self._processed_extensions:
2438            self._set_extensions()
2439        return self._extended_key_usage_value
2440
2441    @property
2442    def authority_information_access_value(self):
2443        """
2444        This extension is used to locate the CA certificate used to sign this
2445        certificate, or the OCSP responder for this certificate.
2446
2447        :return:
2448            None or an AuthorityInfoAccessSyntax object
2449        """
2450
2451        if not self._processed_extensions:
2452            self._set_extensions()
2453        return self._authority_information_access_value
2454
2455    @property
2456    def subject_information_access_value(self):
2457        """
2458        This extension is used to access information about the subject of this
2459        certificate.
2460
2461        :return:
2462            None or a SubjectInfoAccessSyntax object
2463        """
2464
2465        if not self._processed_extensions:
2466            self._set_extensions()
2467        return self._subject_information_access_value
2468
2469    @property
2470    def tls_feature_value(self):
2471        """
2472        This extension is used to list the TLS features a server must respond
2473        with if a client initiates a request supporting them.
2474
2475        :return:
2476            None or a Features object
2477        """
2478
2479        if not self._processed_extensions:
2480            self._set_extensions()
2481        return self._tls_feature_value
2482
2483    @property
2484    def ocsp_no_check_value(self):
2485        """
2486        This extension is used on certificates of OCSP responders, indicating
2487        that revocation information for the certificate should never need to
2488        be verified, thus preventing possible loops in path validation.
2489
2490        :return:
2491            None or a Null object (if present)
2492        """
2493
2494        if not self._processed_extensions:
2495            self._set_extensions()
2496        return self._ocsp_no_check_value
2497
2498    @property
2499    def signature(self):
2500        """
2501        :return:
2502            A byte string of the signature
2503        """
2504
2505        return self['signature_value'].native
2506
2507    @property
2508    def signature_algo(self):
2509        """
2510        :return:
2511            A unicode string of "rsassa_pkcs1v15", "rsassa_pss", "dsa", "ecdsa"
2512        """
2513
2514        return self['signature_algorithm'].signature_algo
2515
2516    @property
2517    def hash_algo(self):
2518        """
2519        :return:
2520            A unicode string of "md2", "md5", "sha1", "sha224", "sha256",
2521            "sha384", "sha512", "sha512_224", "sha512_256"
2522        """
2523
2524        return self['signature_algorithm'].hash_algo
2525
2526    @property
2527    def public_key(self):
2528        """
2529        :return:
2530            The PublicKeyInfo object for this certificate
2531        """
2532
2533        return self['tbs_certificate']['subject_public_key_info']
2534
2535    @property
2536    def subject(self):
2537        """
2538        :return:
2539            The Name object for the subject of this certificate
2540        """
2541
2542        return self['tbs_certificate']['subject']
2543
2544    @property
2545    def issuer(self):
2546        """
2547        :return:
2548            The Name object for the issuer of this certificate
2549        """
2550
2551        return self['tbs_certificate']['issuer']
2552
2553    @property
2554    def serial_number(self):
2555        """
2556        :return:
2557            An integer of the certificate's serial number
2558        """
2559
2560        return self['tbs_certificate']['serial_number'].native
2561
2562    @property
2563    def key_identifier(self):
2564        """
2565        :return:
2566            None or a byte string of the certificate's key identifier from the
2567            key identifier extension
2568        """
2569
2570        if not self.key_identifier_value:
2571            return None
2572
2573        return self.key_identifier_value.native
2574
2575    @property
2576    def issuer_serial(self):
2577        """
2578        :return:
2579            A byte string of the SHA-256 hash of the issuer concatenated with
2580            the ascii character ":", concatenated with the serial number as
2581            an ascii string
2582        """
2583
2584        if self._issuer_serial is None:
2585            self._issuer_serial = self.issuer.sha256 + b':' + str_cls(self.serial_number).encode('ascii')
2586        return self._issuer_serial
2587
2588    @property
2589    def not_valid_after(self):
2590        """
2591        :return:
2592            A datetime of latest time when the certificate is still valid
2593        """
2594        return self['tbs_certificate']['validity']['not_after'].native
2595
2596    @property
2597    def not_valid_before(self):
2598        """
2599        :return:
2600            A datetime of the earliest time when the certificate is valid
2601        """
2602        return self['tbs_certificate']['validity']['not_before'].native
2603
2604    @property
2605    def authority_key_identifier(self):
2606        """
2607        :return:
2608            None or a byte string of the key_identifier from the authority key
2609            identifier extension
2610        """
2611
2612        if not self.authority_key_identifier_value:
2613            return None
2614
2615        return self.authority_key_identifier_value['key_identifier'].native
2616
2617    @property
2618    def authority_issuer_serial(self):
2619        """
2620        :return:
2621            None or a byte string of the SHA-256 hash of the isser from the
2622            authority key identifier extension concatenated with the ascii
2623            character ":", concatenated with the serial number from the
2624            authority key identifier extension as an ascii string
2625        """
2626
2627        if self._authority_issuer_serial is False:
2628            akiv = self.authority_key_identifier_value
2629            if akiv and akiv['authority_cert_issuer'].native:
2630                issuer = self.authority_key_identifier_value['authority_cert_issuer'][0].chosen
2631                # We untag the element since it is tagged via being a choice from GeneralName
2632                issuer = issuer.untag()
2633                authority_serial = self.authority_key_identifier_value['authority_cert_serial_number'].native
2634                self._authority_issuer_serial = issuer.sha256 + b':' + str_cls(authority_serial).encode('ascii')
2635            else:
2636                self._authority_issuer_serial = None
2637        return self._authority_issuer_serial
2638
2639    @property
2640    def crl_distribution_points(self):
2641        """
2642        Returns complete CRL URLs - does not include delta CRLs
2643
2644        :return:
2645            A list of zero or more DistributionPoint objects
2646        """
2647
2648        if self._crl_distribution_points is None:
2649            self._crl_distribution_points = self._get_http_crl_distribution_points(self.crl_distribution_points_value)
2650        return self._crl_distribution_points
2651
2652    @property
2653    def delta_crl_distribution_points(self):
2654        """
2655        Returns delta CRL URLs - does not include complete CRLs
2656
2657        :return:
2658            A list of zero or more DistributionPoint objects
2659        """
2660
2661        if self._delta_crl_distribution_points is None:
2662            self._delta_crl_distribution_points = self._get_http_crl_distribution_points(self.freshest_crl_value)
2663        return self._delta_crl_distribution_points
2664
2665    def _get_http_crl_distribution_points(self, crl_distribution_points):
2666        """
2667        Fetches the DistributionPoint object for non-relative, HTTP CRLs
2668        referenced by the certificate
2669
2670        :param crl_distribution_points:
2671            A CRLDistributionPoints object to grab the DistributionPoints from
2672
2673        :return:
2674            A list of zero or more DistributionPoint objects
2675        """
2676
2677        output = []
2678
2679        if crl_distribution_points is None:
2680            return []
2681
2682        for distribution_point in crl_distribution_points:
2683            distribution_point_name = distribution_point['distribution_point']
2684            if distribution_point_name is VOID:
2685                continue
2686            # RFC 5280 indicates conforming CA should not use the relative form
2687            if distribution_point_name.name == 'name_relative_to_crl_issuer':
2688                continue
2689            # This library is currently only concerned with HTTP-based CRLs
2690            for general_name in distribution_point_name.chosen:
2691                if general_name.name == 'uniform_resource_identifier':
2692                    output.append(distribution_point)
2693
2694        return output
2695
2696    @property
2697    def ocsp_urls(self):
2698        """
2699        :return:
2700            A list of zero or more unicode strings of the OCSP URLs for this
2701            cert
2702        """
2703
2704        if not self.authority_information_access_value:
2705            return []
2706
2707        output = []
2708        for entry in self.authority_information_access_value:
2709            if entry['access_method'].native == 'ocsp':
2710                location = entry['access_location']
2711                if location.name != 'uniform_resource_identifier':
2712                    continue
2713                url = location.native
2714                if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')):
2715                    output.append(url)
2716        return output
2717
2718    @property
2719    def valid_domains(self):
2720        """
2721        :return:
2722            A list of unicode strings of valid domain names for the certificate.
2723            Wildcard certificates will have a domain in the form: *.example.com
2724        """
2725
2726        if self._valid_domains is None:
2727            self._valid_domains = []
2728
2729            # For the subject alt name extension, we can look at the name of
2730            # the choice selected since it distinguishes between domain names,
2731            # email addresses, IPs, etc
2732            if self.subject_alt_name_value:
2733                for general_name in self.subject_alt_name_value:
2734                    if general_name.name == 'dns_name' and general_name.native not in self._valid_domains:
2735                        self._valid_domains.append(general_name.native)
2736
2737            # If there was no subject alt name extension, and the common name
2738            # in the subject looks like a domain, that is considered the valid
2739            # list. This is done because according to
2740            # https://tools.ietf.org/html/rfc6125#section-6.4.4, the common
2741            # name should not be used if the subject alt name is present.
2742            else:
2743                pattern = re.compile('^(\\*\\.)?(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$')
2744                for rdn in self.subject.chosen:
2745                    for name_type_value in rdn:
2746                        if name_type_value['type'].native == 'common_name':
2747                            value = name_type_value['value'].native
2748                            if pattern.match(value):
2749                                self._valid_domains.append(value)
2750
2751        return self._valid_domains
2752
2753    @property
2754    def valid_ips(self):
2755        """
2756        :return:
2757            A list of unicode strings of valid IP addresses for the certificate
2758        """
2759
2760        if self._valid_ips is None:
2761            self._valid_ips = []
2762
2763            if self.subject_alt_name_value:
2764                for general_name in self.subject_alt_name_value:
2765                    if general_name.name == 'ip_address':
2766                        self._valid_ips.append(general_name.native)
2767
2768        return self._valid_ips
2769
2770    @property
2771    def ca(self):
2772        """
2773        :return;
2774            A boolean - if the certificate is marked as a CA
2775        """
2776
2777        return self.basic_constraints_value and self.basic_constraints_value['ca'].native
2778
2779    @property
2780    def max_path_length(self):
2781        """
2782        :return;
2783            None or an integer of the maximum path length
2784        """
2785
2786        if not self.ca:
2787            return None
2788        return self.basic_constraints_value['path_len_constraint'].native
2789
2790    @property
2791    def self_issued(self):
2792        """
2793        :return:
2794            A boolean - if the certificate is self-issued, as defined by RFC
2795            5280
2796        """
2797
2798        if self._self_issued is None:
2799            self._self_issued = self.subject == self.issuer
2800        return self._self_issued
2801
2802    @property
2803    def self_signed(self):
2804        """
2805        :return:
2806            A unicode string of "no" or "maybe". The "maybe" result will
2807            be returned if the certificate issuer and subject are the same.
2808            If a key identifier and authority key identifier are present,
2809            they will need to match otherwise "no" will be returned.
2810
2811            To verify is a certificate is truly self-signed, the signature
2812            will need to be verified. See the certvalidator package for
2813            one possible solution.
2814        """
2815
2816        if self._self_signed is None:
2817            self._self_signed = 'no'
2818            if self.self_issued:
2819                if self.key_identifier:
2820                    if not self.authority_key_identifier:
2821                        self._self_signed = 'maybe'
2822                    elif self.authority_key_identifier == self.key_identifier:
2823                        self._self_signed = 'maybe'
2824                else:
2825                    self._self_signed = 'maybe'
2826        return self._self_signed
2827
2828    @property
2829    def sha1(self):
2830        """
2831        :return:
2832            The SHA-1 hash of the DER-encoded bytes of this complete certificate
2833        """
2834
2835        if self._sha1 is None:
2836            self._sha1 = hashlib.sha1(self.dump()).digest()
2837        return self._sha1
2838
2839    @property
2840    def sha1_fingerprint(self):
2841        """
2842        :return:
2843            A unicode string of the SHA-1 hash, formatted using hex encoding
2844            with a space between each pair of characters, all uppercase
2845        """
2846
2847        return ' '.join('%02X' % c for c in bytes_to_list(self.sha1))
2848
2849    @property
2850    def sha256(self):
2851        """
2852        :return:
2853            The SHA-256 hash of the DER-encoded bytes of this complete
2854            certificate
2855        """
2856
2857        if self._sha256 is None:
2858            self._sha256 = hashlib.sha256(self.dump()).digest()
2859        return self._sha256
2860
2861    @property
2862    def sha256_fingerprint(self):
2863        """
2864        :return:
2865            A unicode string of the SHA-256 hash, formatted using hex encoding
2866            with a space between each pair of characters, all uppercase
2867        """
2868
2869        return ' '.join('%02X' % c for c in bytes_to_list(self.sha256))
2870
2871    def is_valid_domain_ip(self, domain_ip):
2872        """
2873        Check if a domain name or IP address is valid according to the
2874        certificate
2875
2876        :param domain_ip:
2877            A unicode string of a domain name or IP address
2878
2879        :return:
2880            A boolean - if the domain or IP is valid for the certificate
2881        """
2882
2883        if not isinstance(domain_ip, str_cls):
2884            raise TypeError(unwrap(
2885                '''
2886                domain_ip must be a unicode string, not %s
2887                ''',
2888                type_name(domain_ip)
2889            ))
2890
2891        encoded_domain_ip = domain_ip.encode('idna').decode('ascii').lower()
2892
2893        is_ipv6 = encoded_domain_ip.find(':') != -1
2894        is_ipv4 = not is_ipv6 and re.match('^\\d+\\.\\d+\\.\\d+\\.\\d+$', encoded_domain_ip)
2895        is_domain = not is_ipv6 and not is_ipv4
2896
2897        # Handle domain name checks
2898        if is_domain:
2899            if not self.valid_domains:
2900                return False
2901
2902            domain_labels = encoded_domain_ip.split('.')
2903
2904            for valid_domain in self.valid_domains:
2905                encoded_valid_domain = valid_domain.encode('idna').decode('ascii').lower()
2906                valid_domain_labels = encoded_valid_domain.split('.')
2907
2908                # The domain must be equal in label length to match
2909                if len(valid_domain_labels) != len(domain_labels):
2910                    continue
2911
2912                if valid_domain_labels == domain_labels:
2913                    return True
2914
2915                is_wildcard = self._is_wildcard_domain(encoded_valid_domain)
2916                if is_wildcard and self._is_wildcard_match(domain_labels, valid_domain_labels):
2917                    return True
2918
2919            return False
2920
2921        # Handle IP address checks
2922        if not self.valid_ips:
2923            return False
2924
2925        family = socket.AF_INET if is_ipv4 else socket.AF_INET6
2926        normalized_ip = inet_pton(family, encoded_domain_ip)
2927
2928        for valid_ip in self.valid_ips:
2929            valid_family = socket.AF_INET if valid_ip.find('.') != -1 else socket.AF_INET6
2930            normalized_valid_ip = inet_pton(valid_family, valid_ip)
2931
2932            if normalized_valid_ip == normalized_ip:
2933                return True
2934
2935        return False
2936
2937    def _is_wildcard_domain(self, domain):
2938        """
2939        Checks if a domain is a valid wildcard according to
2940        https://tools.ietf.org/html/rfc6125#section-6.4.3
2941
2942        :param domain:
2943            A unicode string of the domain name, where any U-labels from an IDN
2944            have been converted to A-labels
2945
2946        :return:
2947            A boolean - if the domain is a valid wildcard domain
2948        """
2949
2950        # The * character must be present for a wildcard match, and if there is
2951        # most than one, it is an invalid wildcard specification
2952        if domain.count('*') != 1:
2953            return False
2954
2955        labels = domain.lower().split('.')
2956
2957        if not labels:
2958            return False
2959
2960        # Wildcards may only appear in the left-most label
2961        if labels[0].find('*') == -1:
2962            return False
2963
2964        # Wildcards may not be embedded in an A-label from an IDN
2965        if labels[0][0:4] == 'xn--':
2966            return False
2967
2968        return True
2969
2970    def _is_wildcard_match(self, domain_labels, valid_domain_labels):
2971        """
2972        Determines if the labels in a domain are a match for labels from a
2973        wildcard valid domain name
2974
2975        :param domain_labels:
2976            A list of unicode strings, with A-label form for IDNs, of the labels
2977            in the domain name to check
2978
2979        :param valid_domain_labels:
2980            A list of unicode strings, with A-label form for IDNs, of the labels
2981            in a wildcard domain pattern
2982
2983        :return:
2984            A boolean - if the domain matches the valid domain
2985        """
2986
2987        first_domain_label = domain_labels[0]
2988        other_domain_labels = domain_labels[1:]
2989
2990        wildcard_label = valid_domain_labels[0]
2991        other_valid_domain_labels = valid_domain_labels[1:]
2992
2993        # The wildcard is only allowed in the first label, so if
2994        # The subsequent labels are not equal, there is no match
2995        if other_domain_labels != other_valid_domain_labels:
2996            return False
2997
2998        if wildcard_label == '*':
2999            return True
3000
3001        wildcard_regex = re.compile('^' + wildcard_label.replace('*', '.*') + '$')
3002        if wildcard_regex.match(first_domain_label):
3003            return True
3004
3005        return False
3006
3007
3008# The structures are taken from the OpenSSL source file x_x509a.c, and specify
3009# extra information that is added to X.509 certificates to store trust
3010# information about the certificate.
3011
3012class KeyPurposeIdentifiers(SequenceOf):
3013    _child_spec = KeyPurposeId
3014
3015
3016class SequenceOfAlgorithmIdentifiers(SequenceOf):
3017    _child_spec = AlgorithmIdentifier
3018
3019
3020class CertificateAux(Sequence):
3021    _fields = [
3022        ('trust', KeyPurposeIdentifiers, {'optional': True}),
3023        ('reject', KeyPurposeIdentifiers, {'implicit': 0, 'optional': True}),
3024        ('alias', UTF8String, {'optional': True}),
3025        ('keyid', OctetString, {'optional': True}),
3026        ('other', SequenceOfAlgorithmIdentifiers, {'implicit': 1, 'optional': True}),
3027    ]
3028
3029
3030class TrustedCertificate(Concat):
3031    _child_specs = [Certificate, CertificateAux]
3032