blob: 16f7deb1eb2eb894e826a14b5e3bef32c2d2ca40 [file] [log] [blame]
wbonde91513e2015-06-03 14:52:18 -04001# coding: utf-8
wbondea25fc22015-06-19 15:07:04 -04002
3"""
wbondd913db52015-08-06 12:44:15 -04004ASN.1 type classes for X.509 certificates. Exports the following items:
wbondea25fc22015-06-19 15:07:04 -04005
6 - Attributes()
7 - Certificate()
8 - Extensions()
9 - GeneralName()
10 - GeneralNames()
11 - Name()
12
wbondea25fc22015-06-19 15:07:04 -040013Other type classes are defined that help compose the types listed above.
14"""
15
wbond6b66ab52015-06-21 10:26:45 -040016from __future__ import unicode_literals, division, absolute_import, print_function
wbonde91513e2015-06-03 14:52:18 -040017
wbond1ba880e2017-09-14 13:08:50 -040018from contextlib import contextmanager
wbonda26664f2015-10-07 11:57:35 -040019from encodings import idna # noqa
wbond5cf77ba2015-10-08 09:47:34 -040020import hashlib
21import re
22import socket
23import stringprep
24import sys
25import unicodedata
wbonde91513e2015-06-03 14:52:18 -040026
wbond5cf77ba2015-10-08 09:47:34 -040027from ._errors import unwrap
wbond2b31d952015-11-30 12:54:03 -050028from ._iri import iri_to_uri, uri_to_iri
wbond44b89192015-08-24 09:34:01 -040029from ._ordereddict import OrderedDict
wbond59960ea2016-03-14 21:46:39 -040030from ._types import type_name, str_cls, bytes_to_list
wbonda11c4502017-11-22 11:13:00 -050031from .algos import AlgorithmIdentifier, AnyAlgorithmIdentifier, DigestAlgorithm, SignedDigestAlgorithm
wbonde91513e2015-06-03 14:52:18 -040032from .core import (
33 Any,
34 BitString,
35 BMPString,
36 Boolean,
37 Choice,
wbond54dc6852016-03-18 12:24:41 -040038 Concat,
wbonda11c4502017-11-22 11:13:00 -050039 Enumerated,
wbonde91513e2015-06-03 14:52:18 -040040 GeneralizedTime,
41 GeneralString,
42 IA5String,
43 Integer,
44 Null,
45 NumericString,
46 ObjectIdentifier,
47 OctetBitString,
48 OctetString,
wbonde5a1c6e2015-08-03 07:42:28 -040049 ParsableOctetString,
wbonde91513e2015-06-03 14:52:18 -040050 PrintableString,
51 Sequence,
52 SequenceOf,
53 Set,
54 SetOf,
55 TeletexString,
56 UniversalString,
57 UTCTime,
58 UTF8String,
59 VisibleString,
wbond093f9862015-10-22 11:54:37 -040060 VOID,
wbonde91513e2015-06-03 14:52:18 -040061)
wbonde91513e2015-06-03 14:52:18 -040062from .keys import PublicKeyInfo
wbond9a968c82015-08-31 10:42:52 -040063from .util import int_to_bytes, int_from_bytes, inet_ntop, inet_pton
wbonde91513e2015-06-03 14:52:18 -040064
wbondaf1f5a82015-07-17 12:13:15 -040065
wbonde91513e2015-06-03 14:52:18 -040066# 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
wbond35701c92015-08-07 13:45:21 -040070
71class DNSName(IA5String):
72
73 _encoding = 'idna'
wbond4a0b9712019-10-01 00:53:21 -040074 _bad_tag = (12, 19)
wbond35701c92015-08-07 13:45:21 -040075
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
wbondabf76d12017-03-03 07:18:21 -050093 return self.__unicode__().lower() == other.__unicode__().lower()
wbond35701c92015-08-07 13:45:21 -040094
wbonded82e3f2017-02-27 06:52:50 -050095 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
wbondabf76d12017-03-03 07:18:21 -0500117 self._unicode = value
wbonded82e3f2017-02-27 06:52:50 -0500118 self.contents = encoded_value
119 self._header = None
120 if self._trailer != b'':
121 self._trailer = b''
122
wbond35701c92015-08-07 13:45:21 -0400123
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):
wbonda26664f2015-10-07 11:57:35 -0400135 raise TypeError(unwrap(
136 '''
137 %s value must be a unicode string, not %s
138 ''',
139 type_name(self),
140 type_name(value)
141 ))
wbond35701c92015-08-07 13:45:21 -0400142
wbondabf76d12017-03-03 07:18:21 -0500143 self._unicode = value
wbond2b31d952015-11-30 12:54:03 -0500144 self.contents = iri_to_uri(value)
wbond35701c92015-08-07 13:45:21 -0400145 self._header = None
146 if self._trailer != b'':
147 self._trailer = b''
148
wbond35701c92015-08-07 13:45:21 -0400149 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
wbond9e15efd2018-09-04 07:11:27 -0400166 return iri_to_uri(self.native, True) == iri_to_uri(other.native, True)
wbond2b31d952015-11-30 12:54:03 -0500167
168 def __unicode__(self):
169 """
170 :return:
171 A unicode string
172 """
173
wbondabf76d12017-03-03 07:18:21 -0500174 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
wbond35701c92015-08-07 13:45:21 -0400179
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
wbond4a0b9712019-10-01 00:53:21 -0400188 # In the wild we've seen this encoded as a UTF8String and PrintableString
189 _bad_tag = (12, 19)
wbond5e751e42017-12-15 14:20:56 -0500190
wbond35701c92015-08-07 13:45:21 -0400191 @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):
wbonda26664f2015-10-07 11:57:35 -0400219 raise TypeError(unwrap(
220 '''
221 %s value must be a unicode string, not %s
222 ''',
223 type_name(self),
224 type_name(value)
225 ))
wbond35701c92015-08-07 13:45:21 -0400226
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
wbondabf76d12017-03-03 07:18:21 -0500233 self._normalized = True
234 self._unicode = value
wbond35701c92015-08-07 13:45:21 -0400235 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
wbond5e751e42017-12-15 14:20:56 -0500246 # 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
wbondabf76d12017-03-03 07:18:21 -0500248 if self._unicode is None:
249 contents = self._merge_chunks()
250 if contents.find(b'@') == -1:
wbond5e751e42017-12-15 14:20:56 -0500251 self._unicode = contents.decode('cp1252')
wbondabf76d12017-03-03 07:18:21 -0500252 else:
253 mailbox, hostname = contents.rsplit(b'@', 1)
wbond5e751e42017-12-15 14:20:56 -0500254 self._unicode = mailbox.decode('cp1252') + '@' + hostname.decode('idna')
wbondabf76d12017-03-03 07:18:21 -0500255 return self._unicode
wbond35701c92015-08-07 13:45:21 -0400256
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)
wbonda26664f2015-10-07 11:57:35 -0400276 if not other._normalized:
wbond35701c92015-08-07 13:45:21 -0400277 other.set(other.native)
278
wbonda26664f2015-10-07 11:57:35 -0400279 if self._contents.find(b'@') == -1 or other._contents.find(b'@') == -1:
280 return self._contents == other._contents
wbond35701c92015-08-07 13:45:21 -0400281
wbonda26664f2015-10-07 11:57:35 -0400282 other_mailbox, other_hostname = other._contents.rsplit(b'@', 1)
wbond35701c92015-08-07 13:45:21 -0400283 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):
wbonda26664f2015-10-07 11:57:35 -0400295 def parse(self, spec=None, spec_params=None):
wbond35701c92015-08-07 13:45:21 -0400296 """
297 This method is not applicable to IP addresses
298 """
299
wbonda26664f2015-10-07 11:57:35 -0400300 raise ValueError(unwrap(
301 '''
302 IP address values can not be parsed
303 '''
304 ))
wbond35701c92015-08-07 13:45:21 -0400305
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):
wbonda26664f2015-10-07 11:57:35 -0400316 raise TypeError(unwrap(
317 '''
318 %s value must be a unicode string, not %s
319 ''',
320 type_name(self),
321 type_name(value)
322 ))
wbond35701c92015-08-07 13:45:21 -0400323
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:
wbonda26664f2015-10-07 11:57:35 -0400333 raise ValueError(unwrap(
334 '''
335 %s value contains a CIDR range less than 0
336 ''',
337 type_name(self)
338 ))
wbond35701c92015-08-07 13:45:21 -0400339
340 if value.find(':') != -1:
341 family = socket.AF_INET6
342 if cidr > 128:
wbonda26664f2015-10-07 11:57:35 -0400343 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 ))
wbond35701c92015-08-07 13:45:21 -0400350 cidr_size = 128
351 else:
352 family = socket.AF_INET
353 if cidr > 32:
wbonda26664f2015-10-07 11:57:35 -0400354 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 ))
wbond35701c92015-08-07 13:45:21 -0400361 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
wbondabf76d12017-03-03 07:18:21 -0500372 self._bytes = self.contents
wbond35701c92015-08-07 13:45:21 -0400373 self._header = None
374 if self._trailer != b'':
375 self._trailer = b''
376
377 @property
378 def native(self):
379 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +0200380 The native Python datatype representation of this value
wbond35701c92015-08-07 13:45:21 -0400381
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)
wbond011ab062019-10-01 00:35:30 -0400392 value = None
wbond35701c92015-08-07 13:45:21 -0400393 cidr_int = None
wbond407e9e32015-08-24 09:35:28 -0400394 if byte_len in set([32, 16]):
wbond35701c92015-08-07 13:45:21 -0400395 value = inet_ntop(socket.AF_INET6, byte_string[0:16])
396 if byte_len > 16:
397 cidr_int = int_from_bytes(byte_string[16:])
wbond407e9e32015-08-24 09:35:28 -0400398 elif byte_len in set([8, 4]):
wbond35701c92015-08-07 13:45:21 -0400399 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
wbondabf76d12017-03-03 07:18:21 -0500424 return self.__bytes__() == other.__bytes__()
wbond35701c92015-08-07 13:45:21 -0400425
426
wbonde91513e2015-06-03 14:52:18 -0400427class 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 = [
wbondd62ed9a2017-09-15 07:13:52 -0400454 ('not_before', GeneralizedTime, {'implicit': 0, 'optional': True}),
455 ('not_after', GeneralizedTime, {'implicit': 1, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -0400456 ]
457
458
wbond1ba880e2017-09-14 13:08:50 -0400459class 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
wbonde91513e2015-06-03 14:52:18 -0400492class DirectoryString(Choice):
493 _alternatives = [
wbond1ba880e2017-09-14 13:08:50 -0400494 ('teletex_string', NotReallyTeletexString),
wbonde91513e2015-06-03 14:52:18 -0400495 ('printable_string', PrintableString),
496 ('universal_string', UniversalString),
497 ('utf8_string', UTF8String),
498 ('bmp_string', BMPString),
wbond5024f072016-03-18 14:14:10 -0400499 # This is an invalid/bad alternative, but some broken certs use it
500 ('ia5_string', IA5String),
wbonde91513e2015-06-03 14:52:18 -0400501 ]
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',
wbond50959e92015-07-23 07:13:45 -0400512 '2.5.4.9': 'street_address',
wbonde91513e2015-06-03 14:52:18 -0400513 '2.5.4.10': 'organization_name',
514 '2.5.4.11': 'organizational_unit_name',
515 '2.5.4.12': 'title',
wbond29720122015-07-17 12:01:24 -0400516 '2.5.4.15': 'business_category',
wbond50959e92015-07-23 07:13:45 -0400517 '2.5.4.17': 'postal_code',
wbond526dd542015-12-07 10:58:06 -0500518 '2.5.4.20': 'telephone_number',
wbonde91513e2015-06-03 14:52:18 -0400519 '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',
wbondd9d3d852016-12-05 06:50:12 -0500523 '2.5.4.45': 'unique_identifier',
wbonde91513e2015-06-03 14:52:18 -0400524 '2.5.4.46': 'dn_qualifier',
wbond50959e92015-07-23 07:13:45 -0400525 '2.5.4.65': 'pseudonym',
wbond526dd542015-12-07 10:58:06 -0500526 '2.5.4.97': 'organization_identifier',
wbonda11c4502017-11-22 11:13:00 -0500527 # 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',
wbonde91513e2015-06-03 14:52:18 -0400534 # 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',
spchaned33d3f2019-10-08 10:09:24 +0800540 # https://tools.ietf.org/html/rfc4519#section-2.39
541 '0.9.2342.19200300.100.1.1': 'user_id',
wbond50959e92015-07-23 07:13:45 -0400542 # https://tools.ietf.org/html/rfc2247#section-4
543 '0.9.2342.19200300.100.1.25': 'domain_component',
wbond0c0050f2015-12-07 10:22:08 -0500544 # http://www.alvestrand.no/objectid/0.2.262.1.10.7.20.html
545 '0.2.262.1.10.7.20': 'name_distinguisher',
wbonde91513e2015-06-03 14:52:18 -0400546 }
547
wbond0c8f2ca2015-07-23 07:15:06 -0400548 # 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',
spchaned33d3f2019-10-08 10:09:24 +0800566 'user_id',
wbond0c8f2ca2015-07-23 07:15:06 -0400567 'initials',
568 'generation_qualifier',
569 'surname',
570 'given_name',
571 'name',
572 'pseudonym',
573 'dn_qualifier',
wbond526dd542015-12-07 10:58:06 -0500574 'telephone_number',
wbond0c8f2ca2015-07-23 07:15:06 -0400575 'email_address',
576 'domain_component',
wbond526dd542015-12-07 10:58:06 -0500577 'name_distinguisher',
578 'organization_identifier',
wbonda11c4502017-11-22 11:13:00 -0500579 'tpm_manufacturer',
580 'tpm_model',
581 'tpm_version',
582 'platform_manufacturer',
583 'platform_model',
584 'platform_version',
wbond0c8f2ca2015-07-23 07:15:06 -0400585 ]
586
Peter Sagersonc429d602016-10-21 12:02:35 -0700587 @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 """
wbonda01911d2016-11-23 06:54:42 -0500598
599 attr_name = cls.map(attr_name)
Peter Sagersonc429d602016-10-21 12:02:35 -0700600 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
wbondfce13382015-07-17 12:10:43 -0400607 @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',
wbond50959e92015-07-23 07:13:45 -0400621 'street_address': 'Street Address',
wbondfce13382015-07-17 12:10:43 -0400622 'organization_name': 'Organization',
623 'organizational_unit_name': 'Organizational Unit',
624 'title': 'Title',
625 'business_category': 'Business Category',
wbond50959e92015-07-23 07:13:45 -0400626 'postal_code': 'Postal Code',
wbond526dd542015-12-07 10:58:06 -0500627 'telephone_number': 'Telephone Number',
wbondfce13382015-07-17 12:10:43 -0400628 'name': 'Name',
629 'given_name': 'Given Name',
630 'initials': 'Initials',
631 'generation_qualifier': 'Generation Qualifier',
wbondd9d3d852016-12-05 06:50:12 -0500632 'unique_identifier': 'Unique Identifier',
wbondfce13382015-07-17 12:10:43 -0400633 'dn_qualifier': 'DN Qualifier',
wbond50959e92015-07-23 07:13:45 -0400634 'pseudonym': 'Pseudonym',
wbondfce13382015-07-17 12:10:43 -0400635 'email_address': 'Email Address',
636 'incorporation_locality': 'Incorporation Locality',
637 'incorporation_state_or_province': 'Incorporation State/Province',
638 'incorporation_country': 'Incorporation Country',
wbond50959e92015-07-23 07:13:45 -0400639 'domain_component': 'Domain Component',
wbond526dd542015-12-07 10:58:06 -0500640 'name_distinguisher': 'Name Distinguisher',
641 'organization_identifier': 'Organization Identifier',
wbonda11c4502017-11-22 11:13:00 -0500642 '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',
spchaned33d3f2019-10-08 10:09:24 +0800648 'user_id': 'User ID',
wbondff8b0342015-12-07 10:58:49 -0500649 }.get(self.native, self.native)
wbondfce13382015-07-17 12:10:43 -0400650
wbonde91513e2015-06-03 14:52:18 -0400651
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,
wbond25e96a92015-07-01 09:23:12 -0400662 'serial_number': DirectoryString,
663 'country_name': DirectoryString,
wbonde91513e2015-06-03 14:52:18 -0400664 'locality_name': DirectoryString,
665 'state_or_province_name': DirectoryString,
wbond50959e92015-07-23 07:13:45 -0400666 'street_address': DirectoryString,
wbonde91513e2015-06-03 14:52:18 -0400667 'organization_name': DirectoryString,
668 'organizational_unit_name': DirectoryString,
669 'title': DirectoryString,
wbond50959e92015-07-23 07:13:45 -0400670 'business_category': DirectoryString,
671 'postal_code': DirectoryString,
wbond526dd542015-12-07 10:58:06 -0500672 'telephone_number': PrintableString,
wbonde91513e2015-06-03 14:52:18 -0400673 'name': DirectoryString,
674 'given_name': DirectoryString,
675 'initials': DirectoryString,
676 'generation_qualifier': DirectoryString,
wbondd9d3d852016-12-05 06:50:12 -0500677 'unique_identifier': OctetBitString,
wbond25e96a92015-07-01 09:23:12 -0400678 'dn_qualifier': DirectoryString,
wbond50959e92015-07-23 07:13:45 -0400679 'pseudonym': DirectoryString,
wbonde91513e2015-06-03 14:52:18 -0400680 # https://tools.ietf.org/html/rfc2985#page-26
wbond35701c92015-08-07 13:45:21 -0400681 'email_address': EmailAddress,
wbonde91513e2015-06-03 14:52:18 -0400682 # Page 10 of https://cabforum.org/wp-content/uploads/EV-V1_5_5.pdf
683 'incorporation_locality': DirectoryString,
684 'incorporation_state_or_province': DirectoryString,
wbond25e96a92015-07-01 09:23:12 -0400685 'incorporation_country': DirectoryString,
wbond35701c92015-08-07 13:45:21 -0400686 'domain_component': DNSName,
wbond526dd542015-12-07 10:58:06 -0500687 'name_distinguisher': DirectoryString,
688 'organization_identifier': DirectoryString,
wbonda11c4502017-11-22 11:13:00 -0500689 'tpm_manufacturer': UTF8String,
690 'tpm_model': UTF8String,
691 'tpm_version': UTF8String,
692 'platform_manufacturer': UTF8String,
693 'platform_model': UTF8String,
694 'platform_version': UTF8String,
spchaned33d3f2019-10-08 10:09:24 +0800695 'user_id': DirectoryString,
wbonde91513e2015-06-03 14:52:18 -0400696 }
697
wbondfd65d602015-07-23 07:16:44 -0400698 _prepped = None
699
700 @property
701 def prepped_value(self):
702 """
703 Returns the value after being processed by the internationalized string
wbondd913db52015-08-06 12:44:15 -0400704 preparation as specified by RFC 5280
wbondfd65d602015-07-23 07:16:44 -0400705
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
wbond15da2ac2015-07-27 10:23:40 -0400714 def __ne__(self, other):
715 return not self == other
716
wbondfd65d602015-07-23 07:16:44 -0400717 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)
wbond93ad1432015-07-27 10:26:24 -0400751 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)
wbonda26664f2015-10-07 11:57:35 -0400758 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 )
wbondfd65d602015-07-23 07:16:44 -0400764 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):
wbonda26664f2015-10-07 11:57:35 -0400775 raise ValueError(unwrap(
776 '''
777 X.509 Name objects may not contain unassigned code points
778 '''
779 ))
wbondfd65d602015-07-23 07:16:44 -0400780
781 if stringprep.in_table_c8(char):
wbonda26664f2015-10-07 11:57:35 -0400782 raise ValueError(unwrap(
783 '''
784 X.509 Name objects may not contain change display or
785 zzzzdeprecated characters
786 '''
787 ))
wbondfd65d602015-07-23 07:16:44 -0400788
789 if stringprep.in_table_c3(char):
wbonda26664f2015-10-07 11:57:35 -0400790 raise ValueError(unwrap(
791 '''
792 X.509 Name objects may not contain private use characters
793 '''
794 ))
wbondfd65d602015-07-23 07:16:44 -0400795
796 if stringprep.in_table_c4(char):
wbonda26664f2015-10-07 11:57:35 -0400797 raise ValueError(unwrap(
798 '''
799 X.509 Name objects may not contain non-character code points
800 '''
801 ))
wbondfd65d602015-07-23 07:16:44 -0400802
803 if stringprep.in_table_c5(char):
wbonda26664f2015-10-07 11:57:35 -0400804 raise ValueError(unwrap(
805 '''
806 X.509 Name objects may not contain surrogate code points
807 '''
808 ))
wbondfd65d602015-07-23 07:16:44 -0400809
810 if char == '\ufffd':
wbonda26664f2015-10-07 11:57:35 -0400811 raise ValueError(unwrap(
812 '''
813 X.509 Name objects may not contain the replacement character
814 '''
815 ))
wbondfd65d602015-07-23 07:16:44 -0400816
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:
wbonda26664f2015-10-07 11:57:35 -0400832 raise ValueError(unwrap(
833 '''
834 X.509 Name object contains a malformed bidirectional
835 sequence
836 '''
837 ))
wbondfd65d602015-07-23 07:16:44 -0400838
839 # Insignificant space handling step
840 string = ' ' + re.sub(' +', ' ', string).strip() + ' '
841
842 return string
843
wbonde91513e2015-06-03 14:52:18 -0400844
845class RelativeDistinguishedName(SetOf):
846 _child_spec = NameTypeAndValue
847
wbond936b9712015-07-24 08:38:55 -0400848 @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 = []
wbond5fb30302015-08-06 12:44:57 -0400856 values = self._get_values(self)
wbond936b9712015-07-24 08:38:55 -0400857 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
wbond15da2ac2015-07-27 10:23:40 -0400864 def __ne__(self, other):
865 return not self == other
866
wbondfd65d602015-07-23 07:16:44 -0400867 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
wbonda26664f2015-10-07 11:57:35 -0400893 for type_name_ in self_types:
894 if self_values[type_name_] != other_values[type_name_]:
wbondfd65d602015-07-23 07:16:44 -0400895 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
wbond153cee92015-08-24 09:37:48 -0400925 output = {}
wbonda26664f2015-10-07 11:57:35 -0400926 [output.update([(ntv['type'].native, ntv.prepped_value)]) for ntv in rdn]
wbond153cee92015-08-24 09:37:48 -0400927 return output
wbondfd65d602015-07-23 07:16:44 -0400928
wbonde91513e2015-06-03 14:52:18 -0400929
930class RDNSequence(SequenceOf):
931 _child_spec = RelativeDistinguishedName
932
wbond936b9712015-07-24 08:38:55 -0400933 @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
wbond15da2ac2015-07-27 10:23:40 -0400945 def __ne__(self, other):
946 return not self == other
947
wbondfd65d602015-07-23 07:16:44 -0400948 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
wbonde91513e2015-06-03 14:52:18 -0400971
972class Name(Choice):
973 _alternatives = [
974 ('', RDNSequence),
975 ]
976
wbondfce13382015-07-17 12:10:43 -0400977 _human_friendly = None
wbond91d70322015-07-17 12:04:55 -0400978 _sha1 = None
979 _sha256 = None
980
wbondfd65d602015-07-23 07:16:44 -0400981 @classmethod
wbond965ea212015-11-30 15:54:01 -0500982 def build(cls, name_dict, use_printable=False):
wbondfd65d602015-07-23 07:16:44 -0400983 """
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
wbond965ea212015-11-30 15:54:01 -0500992 :param use_printable:
993 A bool - if PrintableString should be used for encoding instead of
Edward Betts42bed252017-08-30 20:49:39 +0100994 UTF8String. This is for backwards compatibility with old software.
wbond965ea212015-11-30 15:54:01 -0500995
wbondfd65d602015-07-23 07:16:44 -0400996 :return:
997 An x509.Name object
998 """
999
wbonde1fa6cd2016-07-21 21:46:25 -04001000 rdns = []
wbond965ea212015-11-30 15:54:01 -05001001 if not use_printable:
1002 encoding_name = 'utf8_string'
1003 encoding_class = UTF8String
1004 else:
1005 encoding_name = 'printable_string'
1006 encoding_class = PrintableString
wbondfd65d602015-07-23 07:16:44 -04001007
wbonda01911d2016-11-23 06:54:42 -05001008 # Sort the attributes according to NameType.preferred_order
Peter Sagersonc429d602016-10-21 12:02:35 -07001009 name_dict = OrderedDict(
1010 sorted(
1011 name_dict.items(),
1012 key=lambda item: NameType.preferred_ordinal(item[0])
1013 )
1014 )
wbondfd65d602015-07-23 07:16:44 -04001015
Peter Sagersonc429d602016-10-21 12:02:35 -07001016 for attribute_name, attribute_value in name_dict.items():
wbonda01911d2016-11-23 06:54:42 -05001017 attribute_name = NameType.map(attribute_name)
wbond21db1212015-10-20 00:49:25 -04001018 if attribute_name == 'email_address':
Peter Sagersonc429d602016-10-21 12:02:35 -07001019 value = EmailAddress(attribute_value)
wbond21db1212015-10-20 00:49:25 -04001020 elif attribute_name == 'domain_component':
Peter Sagersonc429d602016-10-21 12:02:35 -07001021 value = DNSName(attribute_value)
wbond21db1212015-10-20 00:49:25 -04001022 elif attribute_name in set(['dn_qualifier', 'country_name', 'serial_number']):
1023 value = DirectoryString(
1024 name='printable_string',
Peter Sagersonc429d602016-10-21 12:02:35 -07001025 value=PrintableString(attribute_value)
wbond21db1212015-10-20 00:49:25 -04001026 )
wbondfd65d602015-07-23 07:16:44 -04001027 else:
wbond21db1212015-10-20 00:49:25 -04001028 value = DirectoryString(
wbond965ea212015-11-30 15:54:01 -05001029 name=encoding_name,
Peter Sagersonc429d602016-10-21 12:02:35 -07001030 value=encoding_class(attribute_value)
wbond21db1212015-10-20 00:49:25 -04001031 )
wbondfd65d602015-07-23 07:16:44 -04001032
wbonde1fa6cd2016-07-21 21:46:25 -04001033 rdns.append(RelativeDistinguishedName([
1034 NameTypeAndValue({
1035 'type': attribute_name,
1036 'value': value
1037 })
1038 ]))
wbondfd65d602015-07-23 07:16:44 -04001039
wbonde1fa6cd2016-07-21 21:46:25 -04001040 return cls(name='', value=RDNSequence(rdns))
wbondfd65d602015-07-23 07:16:44 -04001041
wbond936b9712015-07-24 08:38:55 -04001042 @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
wbond3298a822015-10-24 13:10:38 -04001051 def __len__(self):
1052 return len(self.chosen)
1053
wbond15da2ac2015-07-27 10:23:40 -04001054 def __ne__(self, other):
1055 return not self == other
1056
wbondfd65d602015-07-23 07:16:44 -04001057 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
wbonde91513e2015-06-03 14:52:18 -04001072 @property
1073 def native(self):
wbonde91513e2015-06-03 14:52:18 -04001074 if self._native is None:
1075 self._native = OrderedDict()
1076 for rdn in self.chosen.native:
1077 for type_val in rdn:
wbond47f78fd2015-07-17 12:08:51 -04001078 field_name = type_val['type']
1079 if field_name in self._native:
Alexander Duryagin4ae81c92015-12-15 14:20:15 +03001080 existing = self._native[field_name]
1081 if not isinstance(existing, list):
1082 existing = self._native[field_name] = [existing]
1083 existing.append(type_val['value'])
wbonde91513e2015-06-03 14:52:18 -04001084 else:
wbond47f78fd2015-07-17 12:08:51 -04001085 self._native[field_name] = type_val['value']
wbonde91513e2015-06-03 14:52:18 -04001086 return self._native
1087
wbond91d70322015-07-17 12:04:55 -04001088 @property
wbondfce13382015-07-17 12:10:43 -04001089 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()
wbond8cbf4042015-12-07 10:59:42 -05001097 last_field = None
wbondfce13382015-07-17 12:10:43 -04001098 for rdn in self.chosen:
1099 for type_val in rdn:
1100 field_name = type_val['type'].human_friendly
wbond8cbf4042015-12-07 10:59:42 -05001101 last_field = field_name
wbondfce13382015-07-17 12:10:43 -04001102 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 = []
wbond8cbf4042015-12-07 10:59:42 -05001108 keys = data.keys()
1109 if last_field == 'Country':
1110 keys = reversed(list(keys))
1111 for key in keys:
wbondfce13382015-07-17 12:10:43 -04001112 value = data[key]
wbond7dac7a92015-12-07 11:00:07 -05001113 native_value = self._recursive_humanize(value)
1114 to_join.append('%s: %s' % (key, native_value))
wbondfce13382015-07-17 12:10:43 -04001115
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
wbond7dac7a92015-12-07 11:00:07 -05001127 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):
Miro Hrončokcbbd6412020-05-04 00:58:07 +02001139 return ', '.join(
wbond7dac7a92015-12-07 11:00:07 -05001140 reversed([self._recursive_humanize(sub_value) for sub_value in value])
1141 )
1142 return value.native
1143
wbondfce13382015-07-17 12:10:43 -04001144 @property
wbond91d70322015-07-17 12:04:55 -04001145 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
wbonde91513e2015-06-03 14:52:18 -04001166
1167class AnotherName(Sequence):
1168 _fields = [
1169 ('type_id', ObjectIdentifier),
wbondd62ed9a2017-09-15 07:13:52 -04001170 ('value', Any, {'explicit': 0}),
wbonde91513e2015-06-03 14:52:18 -04001171 ]
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 = [
wbondd62ed9a2017-09-15 07:13:52 -04001203 ('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}),
wbonde91513e2015-06-03 14:52:18 -04001207 ]
1208
1209
1210class TeletexPersonalName(Set):
1211 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001212 ('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}),
wbonde91513e2015-06-03 14:52:18 -04001216 ]
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}),
wbondd62ed9a2017-09-15 07:13:52 -04001231 ('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}),
wbonde91513e2015-06-03 14:52:18 -04001238 ]
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 = [
wbondd62ed9a2017-09-15 07:13:52 -04001297 ('number', NumericString, {'implicit': 0}),
1298 ('sub_address', NumericString, {'implicit': 1, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001299 ]
1300
1301
1302class NAddresses(SetOf):
1303 _child_spec = OctetString
1304
1305
1306class PresentationAddress(Sequence):
1307 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001308 ('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}),
wbonde91513e2015-06-03 14:52:18 -04001312 ]
1313
1314
1315class ExtendedNetworkAddress(Choice):
1316 _alternatives = [
1317 ('e163_4_address', E1634Address),
wbondd62ed9a2017-09-15 07:13:52 -04001318 ('psap_address', PresentationAddress, {'implicit': 0})
wbonde91513e2015-06-03 14:52:18 -04001319 ]
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 = [
wbondd62ed9a2017-09-15 07:13:52 -04001363 ('extension_attribute_type', ExtensionAttributeType, {'implicit': 0}),
1364 ('extension_attribute_value', Any, {'explicit': 1}),
wbonde91513e2015-06-03 14:52:18 -04001365 ]
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 = [
wbondd62ed9a2017-09-15 07:13:52 -04001409 ('name_assigner', DirectoryString, {'implicit': 0, 'optional': True}),
1410 ('party_name', DirectoryString, {'implicit': 1}),
wbonde91513e2015-06-03 14:52:18 -04001411 ]
1412
1413
1414class GeneralName(Choice):
1415 _alternatives = [
wbondd62ed9a2017-09-15 07:13:52 -04001416 ('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}),
wbonde91513e2015-06-03 14:52:18 -04001425 ]
1426
wbond15da2ac2015-07-27 10:23:40 -04001427 def __ne__(self, other):
1428 return not self == other
1429
wbondfd65d602015-07-23 07:16:44 -04001430 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'):
wbonda26664f2015-10-07 11:57:35 -04001442 raise ValueError(unwrap(
1443 '''
1444 Comparison is not supported for GeneralName objects of
1445 choice %s
1446 ''',
1447 self.name
1448 ))
wbondfd65d602015-07-23 07:16:44 -04001449
1450 if other.name in ('other_name', 'x400_address', 'edi_party_name'):
wbonda26664f2015-10-07 11:57:35 -04001451 raise ValueError(unwrap(
1452 '''
1453 Comparison is not supported for GeneralName objects of choice
1454 %s''',
1455 other.name
1456 ))
wbondfd65d602015-07-23 07:16:44 -04001457
1458 if self.name != other.name:
1459 return False
1460
wbond35701c92015-08-07 13:45:21 -04001461 return self.chosen == other.chosen
wbondfd65d602015-07-23 07:16:44 -04001462
wbonde91513e2015-06-03 14:52:18 -04001463
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 = [
wbondd62ed9a2017-09-15 07:13:52 -04001491 ('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}),
wbonde91513e2015-06-03 14:52:18 -04001494 ]
1495
1496
1497class DistributionPointName(Choice):
1498 _alternatives = [
wbondd62ed9a2017-09-15 07:13:52 -04001499 ('full_name', GeneralNames, {'implicit': 0}),
1500 ('name_relative_to_crl_issuer', RelativeDistinguishedName, {'implicit': 1}),
wbonde91513e2015-06-03 14:52:18 -04001501 ]
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),
wbondd62ed9a2017-09-15 07:13:52 -04001521 ('minimum', Integer, {'implicit': 0, 'default': 0}),
1522 ('maximum', Integer, {'implicit': 1, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001523 ]
1524
1525
1526class GeneralSubtrees(SequenceOf):
1527 _child_spec = GeneralSubtree
1528
1529
1530class NameConstraints(Sequence):
1531 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001532 ('permitted_subtrees', GeneralSubtrees, {'implicit': 0, 'optional': True}),
1533 ('excluded_subtrees', GeneralSubtrees, {'implicit': 1, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001534 ]
1535
1536
1537class DistributionPoint(Sequence):
1538 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001539 ('distribution_point', DistributionPointName, {'explicit': 0, 'optional': True}),
1540 ('reasons', ReasonFlags, {'implicit': 1, 'optional': True}),
1541 ('crl_issuer', GeneralNames, {'implicit': 2, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001542 ]
1543
wbond3b22a4c2015-07-21 15:27:40 -04001544 _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':
wbonda26664f2015-10-07 11:57:35 -04001557 raise ValueError(unwrap(
1558 '''
1559 CRL distribution points that are relative to the issuer are
1560 not supported
1561 '''
1562 ))
wbond3b22a4c2015-07-21 15:27:40 -04001563
1564 for general_name in name.chosen:
1565 if general_name.name == 'uniform_resource_identifier':
1566 url = general_name.native
Christian Heimes48bcd642017-09-19 18:45:33 +02001567 if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')):
wbond3b22a4c2015-07-21 15:27:40 -04001568 self._url = url
1569 break
1570
1571 return self._url
1572
wbonde91513e2015-06-03 14:52:18 -04001573
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
wbond8bb77d02015-07-13 17:44:29 -04001629class PolicyIdentifier(ObjectIdentifier):
1630 _map = {
1631 '2.5.29.32.0': 'any_policy',
1632 }
1633
1634
wbonde91513e2015-06-03 14:52:18 -04001635class PolicyInformation(Sequence):
1636 _fields = [
wbond8bb77d02015-07-13 17:44:29 -04001637 ('policy_identifier', PolicyIdentifier),
wbonde91513e2015-06-03 14:52:18 -04001638 ('policy_qualifiers', PolicyQualifierInfos, {'optional': True})
1639 ]
1640
1641
1642class CertificatePolicies(SequenceOf):
1643 _child_spec = PolicyInformation
1644
1645
1646class PolicyMapping(Sequence):
1647 _fields = [
wbond20ec1192015-12-04 10:31:49 -05001648 ('issuer_domain_policy', PolicyIdentifier),
1649 ('subject_domain_policy', PolicyIdentifier),
wbonde91513e2015-06-03 14:52:18 -04001650 ]
1651
1652
1653class PolicyMappings(SequenceOf):
1654 _child_spec = PolicyMapping
1655
1656
1657class PolicyConstraints(Sequence):
1658 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001659 ('require_explicit_policy', Integer, {'implicit': 0, 'optional': True}),
1660 ('inhibit_policy_mapping', Integer, {'implicit': 1, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001661 ]
1662
1663
1664class KeyPurposeId(ObjectIdentifier):
1665 _map = {
wbondd17a77d2016-03-18 12:26:14 -04001666 # https://tools.ietf.org/html/rfc5280#page-45
wbondadcdebc2015-07-27 15:58:10 -04001667 '2.5.29.37.0': 'any_extended_key_usage',
wbonde91513e2015-06-03 14:52:18 -04001668 '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',
wbondd17a77d2016-03-18 12:26:14 -04001677 # 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',
Lauri Võsandi8e6defb2018-04-26 09:18:12 +03001706 # 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',
wbondd17a77d2016-03-18 12:26:14 -04001708 # 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',
Matt Cooperdc9d09c2017-11-17 19:29:45 -05001726 # 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',
wbondace45a12016-06-16 05:32:48 -04001728 # 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',
Matt Cooperdc9d09c2017-11-17 19:29:45 -05001763 # 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',
Matt Cooper34f87fa2017-11-17 19:46:32 -05001768 '1.3.6.1.5.2.3.5': 'pkinit_kpkdc',
Matt Cooperdc9d09c2017-11-17 19:29:45 -05001769 # https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSig/changes.html
Matt Cooper34f87fa2017-11-17 19:46:32 -05001770 '1.2.840.113583.1.1.5': 'adobe_authentic_documents_trust',
wbond7f587d52017-11-22 11:13:45 -05001771 # 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'
wbonde91513e2015-06-03 14:52:18 -04001773 }
1774
1775
1776class ExtKeyUsageSyntax(SequenceOf):
1777 _child_spec = KeyPurposeId
1778
1779
wbonda0d45482015-07-13 22:10:20 -04001780class 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',
wbond35f81352015-07-20 08:56:03 -04001784 '1.3.6.1.5.5.7.48.3': 'time_stamping',
1785 '1.3.6.1.5.5.7.48.5': 'ca_repository',
wbonda0d45482015-07-13 22:10:20 -04001786 }
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
wbond35f81352015-07-20 08:56:03 -04001800class SubjectInfoAccessSyntax(SequenceOf):
1801 _child_spec = AccessDescription
1802
1803
wbondea278182015-11-23 13:55:29 -05001804# https://tools.ietf.org/html/rfc7633
1805class Features(SequenceOf):
1806 _child_spec = Integer
1807
1808
wbonde91513e2015-06-03 14:52:18 -04001809class 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
wbonda11c4502017-11-22 11:13:00 -05001829class 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
wbonde91513e2015-06-03 14:52:18 -04002055class 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',
wbondada58e72015-07-20 08:57:00 -04002062 '2.5.29.18': 'issuer_alt_name',
wbonde91513e2015-06-03 14:52:18 -04002063 '2.5.29.19': 'basic_constraints',
wbonde91513e2015-06-03 14:52:18 -04002064 '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',
wbondfb88b8c2015-07-20 08:58:16 -04002071 '2.5.29.46': 'freshest_crl',
wbonda43b3fb2015-07-13 17:52:25 -04002072 '2.5.29.54': 'inhibit_any_policy',
wbonda0d45482015-07-13 22:10:20 -04002073 '1.3.6.1.5.5.7.1.1': 'authority_information_access',
wbond35f81352015-07-20 08:56:03 -04002074 '1.3.6.1.5.5.7.1.11': 'subject_information_access',
wbondea278182015-11-23 13:55:29 -05002075 # https://tools.ietf.org/html/rfc7633
2076 '1.3.6.1.5.5.7.1.24': 'tls_feature',
wbonde91513e2015-06-03 14:52:18 -04002077 '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',
wbond9e74abf2017-06-13 20:34:22 -04002080 # https://tools.ietf.org/html/rfc6962.html#page-14
2081 '1.3.6.1.4.1.11129.2.4.2': 'signed_certificate_timestamp_list',
wbonde91513e2015-06-03 14:52:18 -04002082 }
2083
2084
2085class Extension(Sequence):
2086 _fields = [
2087 ('extn_id', ExtensionId),
2088 ('critical', Boolean, {'default': False}),
wbonde5a1c6e2015-08-03 07:42:28 -04002089 ('extn_value', ParsableOctetString),
wbonde91513e2015-06-03 14:52:18 -04002090 ]
2091
2092 _oid_pair = ('extn_id', 'extn_value')
2093 _oid_specs = {
wbonda11c4502017-11-22 11:13:00 -05002094 'subject_directory_attributes': SubjectDirectoryAttributes,
wbonde91513e2015-06-03 14:52:18 -04002095 'key_identifier': OctetString,
2096 'key_usage': KeyUsage,
2097 'private_key_usage_period': PrivateKeyUsagePeriod,
2098 'subject_alt_name': GeneralNames,
wbondada58e72015-07-20 08:57:00 -04002099 'issuer_alt_name': GeneralNames,
wbonde91513e2015-06-03 14:52:18 -04002100 'basic_constraints': BasicConstraints,
wbonde91513e2015-06-03 14:52:18 -04002101 '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,
wbondfb88b8c2015-07-20 08:58:16 -04002108 'freshest_crl': CRLDistributionPoints,
wbonda43b3fb2015-07-13 17:52:25 -04002109 'inhibit_any_policy': Integer,
wbonda0d45482015-07-13 22:10:20 -04002110 'authority_information_access': AuthorityInfoAccessSyntax,
wbond35f81352015-07-20 08:56:03 -04002111 'subject_information_access': SubjectInfoAccessSyntax,
wbondea278182015-11-23 13:55:29 -05002112 'tls_feature': Features,
wbonde91513e2015-06-03 14:52:18 -04002113 'ocsp_no_check': Null,
2114 'entrust_version_extension': EntrustVersionInfo,
2115 'netscape_certificate_type': NetscapeCertificateType,
wbond9e74abf2017-06-13 20:34:22 -04002116 'signed_certificate_timestamp_list': OctetString,
wbonde91513e2015-06-03 14:52:18 -04002117 }
2118
2119
2120class Extensions(SequenceOf):
2121 _child_spec = Extension
2122
2123
wbonde91513e2015-06-03 14:52:18 -04002124class TbsCertificate(Sequence):
2125 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04002126 ('version', Version, {'explicit': 0, 'default': 'v1'}),
wbonde91513e2015-06-03 14:52:18 -04002127 ('serial_number', Integer),
2128 ('signature', SignedDigestAlgorithm),
2129 ('issuer', Name),
2130 ('validity', Validity),
2131 ('subject', Name),
2132 ('subject_public_key_info', PublicKeyInfo),
wbondd62ed9a2017-09-15 07:13:52 -04002133 ('issuer_unique_id', OctetBitString, {'implicit': 1, 'optional': True}),
2134 ('subject_unique_id', OctetBitString, {'implicit': 2, 'optional': True}),
2135 ('extensions', Extensions, {'explicit': 3, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04002136 ]
2137
2138
2139class Certificate(Sequence):
2140 _fields = [
2141 ('tbs_certificate', TbsCertificate),
2142 ('signature_algorithm', SignedDigestAlgorithm),
2143 ('signature_value', OctetBitString),
2144 ]
wbond8bb77d02015-07-13 17:44:29 -04002145
2146 _processed_extensions = False
2147 _critical_extensions = None
Jörn Heissler3340d122019-11-02 21:05:28 +01002148 _subject_directory_attributes_value = None
wbond8bb77d02015-07-13 17:44:29 -04002149 _key_identifier_value = None
2150 _key_usage_value = None
2151 _subject_alt_name_value = None
wbondada58e72015-07-20 08:57:00 -04002152 _issuer_alt_name_value = None
wbond8bb77d02015-07-13 17:44:29 -04002153 _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
wbondfb88b8c2015-07-20 08:58:16 -04002160 _freshest_crl_value = None
wbond433adbb2015-07-20 08:59:57 -04002161 _inhibit_any_policy_value = None
wbond8bb77d02015-07-13 17:44:29 -04002162 _extended_key_usage_value = None
wbond08c60fa2015-07-13 23:02:13 -04002163 _authority_information_access_value = None
wbond35f81352015-07-20 08:56:03 -04002164 _subject_information_access_value = None
Matt Cooper8300e4d2017-12-05 15:19:46 -05002165 _private_key_usage_period_value = None
wbondea278182015-11-23 13:55:29 -05002166 _tls_feature_value = None
wbond8bb77d02015-07-13 17:44:29 -04002167 _ocsp_no_check_value = None
wbondaf1f5a82015-07-17 12:13:15 -04002168 _issuer_serial = None
2169 _authority_issuer_serial = False
wbond6888bc62015-07-21 15:05:59 -04002170 _crl_distribution_points = None
2171 _delta_crl_distribution_points = None
wbondaf1f5a82015-07-17 12:13:15 -04002172 _valid_domains = None
2173 _valid_ips = None
wbond9a7a0992015-07-23 09:59:06 -04002174 _self_issued = None
2175 _self_signed = None
wbond5314f4b2015-09-01 13:25:47 -04002176 _sha1 = None
2177 _sha256 = None
wbond8bb77d02015-07-13 17:44:29 -04002178
2179 def _set_extensions(self):
2180 """
2181 Sets common named extensions to private attributes and creates a list
2182 of critical extensions
2183 """
2184
wbond2fde6452015-07-23 10:54:13 -04002185 self._critical_extensions = set()
wbond8bb77d02015-07-13 17:44:29 -04002186
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:
wbond2fde6452015-07-23 10:54:13 -04002193 self._critical_extensions.add(name)
wbond8bb77d02015-07-13 17:44:29 -04002194
2195 self._processed_extensions = True
2196
2197 @property
2198 def critical_extensions(self):
2199 """
wbond2fde6452015-07-23 10:54:13 -04002200 Returns a set of the names (or OID if not a known extension) of the
wbond8bb77d02015-07-13 17:44:29 -04002201 extensions marked as critical
2202
2203 :return:
wbond2fde6452015-07-23 10:54:13 -04002204 A set of unicode strings
wbond8bb77d02015-07-13 17:44:29 -04002205 """
2206
2207 if not self._processed_extensions:
2208 self._set_extensions()
2209 return self._critical_extensions
2210
2211 @property
Matt Cooper8300e4d2017-12-05 15:19:46 -05002212 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
wbond433adbb2015-07-20 08:59:57 -04002226 def subject_directory_attributes_value(self):
2227 """
2228 This extension is used to contain additional identification attributes
2229 about the subject.
2230
2231 :return:
Matt Cooper8300e4d2017-12-05 15:19:46 -05002232 None or a SubjectDirectoryAttributes object
wbond433adbb2015-07-20 08:59:57 -04002233 """
2234
2235 if not self._processed_extensions:
2236 self._set_extensions()
Jörn Heissler3340d122019-11-02 21:05:28 +01002237 return self._subject_directory_attributes_value
wbond433adbb2015-07-20 08:59:57 -04002238
2239 @property
wbond8bb77d02015-07-13 17:44:29 -04002240 def key_identifier_value(self):
2241 """
wbondea860012015-07-20 09:00:29 -04002242 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
wbond8bb77d02015-07-13 17:44:29 -04002246 :return:
wbondea860012015-07-20 09:00:29 -04002247 None or an OctetString object
wbond8bb77d02015-07-13 17:44:29 -04002248 """
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 """
wbondea860012015-07-20 09:00:29 -04002257 This extension is used to define the purpose of the public key
2258 contained within the certificate.
2259
wbond8bb77d02015-07-13 17:44:29 -04002260 :return:
wbondea860012015-07-20 09:00:29 -04002261 None or a KeyUsage
wbond8bb77d02015-07-13 17:44:29 -04002262 """
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 """
wbondea860012015-07-20 09:00:29 -04002271 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
wbond8bb77d02015-07-13 17:44:29 -04002276 :return:
wbondea860012015-07-20 09:00:29 -04002277 None or a GeneralNames object
wbond8bb77d02015-07-13 17:44:29 -04002278 """
2279
2280 if not self._processed_extensions:
2281 self._set_extensions()
2282 return self._subject_alt_name_value
2283
2284 @property
wbondada58e72015-07-20 08:57:00 -04002285 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
wbondffb857a2016-03-31 10:59:08 -04002294 if not self._processed_extensions:
2295 self._set_extensions()
wbondada58e72015-07-20 08:57:00 -04002296 return self._issuer_alt_name_value
2297
2298 @property
wbond8bb77d02015-07-13 17:44:29 -04002299 def basic_constraints_value(self):
2300 """
wbondea860012015-07-20 09:00:29 -04002301 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
wbond8bb77d02015-07-13 17:44:29 -04002305 :return:
wbondea860012015-07-20 09:00:29 -04002306 None or a BasicConstraints object
wbond8bb77d02015-07-13 17:44:29 -04002307 """
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 """
wbondea860012015-07-20 09:00:29 -04002316 This extension is used in CA certificates, and is used to limit the
2317 possible names of certificates issued.
2318
wbond8bb77d02015-07-13 17:44:29 -04002319 :return:
wbondea860012015-07-20 09:00:29 -04002320 None or a NameConstraints object
wbond8bb77d02015-07-13 17:44:29 -04002321 """
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 """
wbondea860012015-07-20 09:00:29 -04002330 This extension is used to help in locating the CRL for this certificate.
2331
wbond8bb77d02015-07-13 17:44:29 -04002332 :return:
wbondea860012015-07-20 09:00:29 -04002333 None or a CRLDistributionPoints object
wbond8bb77d02015-07-13 17:44:29 -04002334 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 """
wbondea860012015-07-20 09:00:29 -04002344 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
wbond8bb77d02015-07-13 17:44:29 -04002349 :return:
wbondea860012015-07-20 09:00:29 -04002350 None or a CertificatePolicies object
wbond8bb77d02015-07-13 17:44:29 -04002351 """
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 """
wbondea860012015-07-20 09:00:29 -04002360 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
wbond8bb77d02015-07-13 17:44:29 -04002364 :return:
wbondea860012015-07-20 09:00:29 -04002365 None or a PolicyMappings object
wbond8bb77d02015-07-13 17:44:29 -04002366 """
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 """
wbondea860012015-07-20 09:00:29 -04002375 This extension helps in identifying the public key with which to
2376 validate the authenticity of the certificate.
2377
wbond8bb77d02015-07-13 17:44:29 -04002378 :return:
wbondea860012015-07-20 09:00:29 -04002379 None or an AuthorityKeyIdentifier object
wbond8bb77d02015-07-13 17:44:29 -04002380 """
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 """
wbondea860012015-07-20 09:00:29 -04002389 This extension is used to control if policy mapping is allowed and
2390 when policies are required.
2391
wbond8bb77d02015-07-13 17:44:29 -04002392 :return:
wbondea860012015-07-20 09:00:29 -04002393 None or a PolicyConstraints object
wbond8bb77d02015-07-13 17:44:29 -04002394 """
2395
2396 if not self._processed_extensions:
2397 self._set_extensions()
2398 return self._policy_constraints_value
2399
2400 @property
wbondfb88b8c2015-07-20 08:58:16 -04002401 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
wbond433adbb2015-07-20 08:59:57 -04002414 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
wbond8bb77d02015-07-13 17:44:29 -04002428 def extended_key_usage_value(self):
2429 """
wbondea860012015-07-20 09:00:29 -04002430 This extension is used to define additional purposes for the public key
2431 beyond what is contained in the basic constraints.
2432
wbond8bb77d02015-07-13 17:44:29 -04002433 :return:
wbondea860012015-07-20 09:00:29 -04002434 None or an ExtKeyUsageSyntax object
wbond8bb77d02015-07-13 17:44:29 -04002435 """
2436
2437 if not self._processed_extensions:
2438 self._set_extensions()
2439 return self._extended_key_usage_value
2440
2441 @property
wbond08c60fa2015-07-13 23:02:13 -04002442 def authority_information_access_value(self):
2443 """
wbondea860012015-07-20 09:00:29 -04002444 This extension is used to locate the CA certificate used to sign this
2445 certificate, or the OCSP responder for this certificate.
2446
wbond08c60fa2015-07-13 23:02:13 -04002447 :return:
wbondea860012015-07-20 09:00:29 -04002448 None or an AuthorityInfoAccessSyntax object
wbond08c60fa2015-07-13 23:02:13 -04002449 """
2450
2451 if not self._processed_extensions:
2452 self._set_extensions()
2453 return self._authority_information_access_value
2454
2455 @property
wbond35f81352015-07-20 08:56:03 -04002456 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
wbondea278182015-11-23 13:55:29 -05002470 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
wbond8bb77d02015-07-13 17:44:29 -04002484 def ocsp_no_check_value(self):
2485 """
wbondea860012015-07-20 09:00:29 -04002486 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
wbond8bb77d02015-07-13 17:44:29 -04002490 :return:
wbondd0f71af2015-07-20 10:17:01 -04002491 None or a Null object (if present)
wbond8bb77d02015-07-13 17:44:29 -04002492 """
2493
2494 if not self._processed_extensions:
2495 self._set_extensions()
2496 return self._ocsp_no_check_value
wbondaf1f5a82015-07-17 12:13:15 -04002497
2498 @property
wbond06921bb2015-07-24 08:39:59 -04002499 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
wbondaf1f5a82015-07-17 12:13:15 -04002527 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
Sebastian Bachmann4c0d7e92018-05-02 16:31:58 +02002589 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
wbondaf1f5a82015-07-17 12:13:15 -04002605 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:
wbonda26664f2015-10-07 11:57:35 -04002628 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
wbondaf1f5a82015-07-17 12:13:15 -04002631 # We untag the element since it is tagged via being a choice from GeneralName
wbonda26664f2015-10-07 11:57:35 -04002632 issuer = issuer.untag()
wbondaf1f5a82015-07-17 12:13:15 -04002633 authority_serial = self.authority_key_identifier_value['authority_cert_serial_number'].native
wbonda26664f2015-10-07 11:57:35 -04002634 self._authority_issuer_serial = issuer.sha256 + b':' + str_cls(authority_serial).encode('ascii')
wbondaf1f5a82015-07-17 12:13:15 -04002635 else:
2636 self._authority_issuer_serial = None
2637 return self._authority_issuer_serial
2638
2639 @property
wbond6888bc62015-07-21 15:05:59 -04002640 def crl_distribution_points(self):
wbondaf1f5a82015-07-17 12:13:15 -04002641 """
wbond6888bc62015-07-21 15:05:59 -04002642 Returns complete CRL URLs - does not include delta CRLs
2643
wbondaf1f5a82015-07-17 12:13:15 -04002644 :return:
wbond6888bc62015-07-21 15:05:59 -04002645 A list of zero or more DistributionPoint objects
wbondaf1f5a82015-07-17 12:13:15 -04002646 """
2647
wbond6888bc62015-07-21 15:05:59 -04002648 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 """
wbondaf1f5a82015-07-17 12:13:15 -04002676
2677 output = []
wbond6888bc62015-07-21 15:05:59 -04002678
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']
wbond093f9862015-10-22 11:54:37 -04002684 if distribution_point_name is VOID:
wbondbabedc62015-10-22 01:48:14 -04002685 continue
wbondd913db52015-08-06 12:44:15 -04002686 # RFC 5280 indicates conforming CA should not use the relative form
wbondaf1f5a82015-07-17 12:13:15 -04002687 if distribution_point_name.name == 'name_relative_to_crl_issuer':
2688 continue
wbond6888bc62015-07-21 15:05:59 -04002689 # This library is currently only concerned with HTTP-based CRLs
wbondaf1f5a82015-07-17 12:13:15 -04002690 for general_name in distribution_point_name.chosen:
2691 if general_name.name == 'uniform_resource_identifier':
wbond6888bc62015-07-21 15:05:59 -04002692 output.append(distribution_point)
wbondaf1f5a82015-07-17 12:13:15 -04002693
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':
wbond4c248d52015-07-21 15:28:28 -04002710 location = entry['access_location']
2711 if location.name != 'uniform_resource_identifier':
2712 continue
2713 url = location.native
Christian Heimes48bcd642017-09-19 18:45:33 +02002714 if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')):
wbond4c248d52015-07-21 15:28:28 -04002715 output.append(url)
wbondaf1f5a82015-07-17 12:13:15 -04002716 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
wbondaf1f5a82015-07-17 12:13:15 -04002729 # 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
wbond06cf0992015-08-31 11:43:45 -04002737 # 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
wbondaf1f5a82015-07-17 12:13:15 -04002751 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
wbond9a7a0992015-07-23 09:59:06 -04002769
2770 @property
wbondb5e193e2015-07-24 08:40:24 -04002771 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
wbond9a7a0992015-07-23 09:59:06 -04002791 def self_issued(self):
2792 """
2793 :return:
wbondd913db52015-08-06 12:44:15 -04002794 A boolean - if the certificate is self-issued, as defined by RFC
2795 5280
wbond9a7a0992015-07-23 09:59:06 -04002796 """
2797
2798 if self._self_issued is None:
wbonddf6b8972015-08-31 08:53:12 -04002799 self._self_issued = self.subject == self.issuer
wbond9a7a0992015-07-23 09:59:06 -04002800 return self._self_issued
2801
2802 @property
2803 def self_signed(self):
2804 """
2805 :return:
wbond76471632017-11-20 07:31:11 -05002806 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.
wbond9a7a0992015-07-23 09:59:06 -04002814 """
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:
wbond76471632017-11-20 07:31:11 -05002821 self._self_signed = 'maybe'
wbond9a7a0992015-07-23 09:59:06 -04002822 elif self.authority_key_identifier == self.key_identifier:
wbond76471632017-11-20 07:31:11 -05002823 self._self_signed = 'maybe'
wbond9a7a0992015-07-23 09:59:06 -04002824 else:
2825 self._self_signed = 'maybe'
2826 return self._self_signed
wbond35701c92015-08-07 13:45:21 -04002827
wbond5314f4b2015-09-01 13:25:47 -04002828 @property
2829 def sha1(self):
2830 """
2831 :return:
wbond12152692015-11-13 02:24:10 -05002832 The SHA-1 hash of the DER-encoded bytes of this complete certificate
wbond5314f4b2015-09-01 13:25:47 -04002833 """
2834
2835 if self._sha1 is None:
2836 self._sha1 = hashlib.sha1(self.dump()).digest()
2837 return self._sha1
2838
2839 @property
wbond12152692015-11-13 02:24:10 -05002840 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
wbond4c518b22015-11-17 15:15:38 -05002847 return ' '.join('%02X' % c for c in bytes_to_list(self.sha1))
wbond12152692015-11-13 02:24:10 -05002848
2849 @property
wbond5314f4b2015-09-01 13:25:47 -04002850 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
Pim Coster42e7a832017-05-20 19:56:51 +02002861 @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
wbondd4fc7ea2015-08-31 11:44:11 -04002871 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):
wbonda26664f2015-10-07 11:57:35 -04002884 raise TypeError(unwrap(
2885 '''
2886 domain_ip must be a unicode string, not %s
2887 ''',
2888 type_name(domain_ip)
2889 ))
wbondd4fc7ea2015-08-31 11:44:11 -04002890
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
wbonda26664f2015-10-07 11:57:35 -04002915 is_wildcard = self._is_wildcard_domain(encoded_valid_domain)
2916 if is_wildcard and self._is_wildcard_match(domain_labels, valid_domain_labels):
wbondd4fc7ea2015-08-31 11:44:11 -04002917 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
wbond54dc6852016-03-18 12:24:41 -04003006
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}),
wbondd62ed9a2017-09-15 07:13:52 -04003023 ('reject', KeyPurposeIdentifiers, {'implicit': 0, 'optional': True}),
wbond54dc6852016-03-18 12:24:41 -04003024 ('alias', UTF8String, {'optional': True}),
3025 ('keyid', OctetString, {'optional': True}),
wbondd62ed9a2017-09-15 07:13:52 -04003026 ('other', SequenceOfAlgorithmIdentifiers, {'implicit': 1, 'optional': True}),
wbond54dc6852016-03-18 12:24:41 -04003027 ]
3028
3029
3030class TrustedCertificate(Concat):
3031 _child_specs = [Certificate, CertificateAux]