blob: 938bb4154917c4461078df6494ac993eefbe8d81 [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'
wbond558e1ae2015-12-07 10:57:14 -050074 _bad_tag = 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
wbond5e751e42017-12-15 14:20:56 -0500188 # In the wild we've seen this encoded as a PrintableString
189 _bad_tag = 19
190
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)
392 cidr_int = None
wbond407e9e32015-08-24 09:35:28 -0400393 if byte_len in set([32, 16]):
wbond35701c92015-08-07 13:45:21 -0400394 value = inet_ntop(socket.AF_INET6, byte_string[0:16])
395 if byte_len > 16:
396 cidr_int = int_from_bytes(byte_string[16:])
wbond407e9e32015-08-24 09:35:28 -0400397 elif byte_len in set([8, 4]):
wbond35701c92015-08-07 13:45:21 -0400398 value = inet_ntop(socket.AF_INET, byte_string[0:4])
399 if byte_len > 4:
400 cidr_int = int_from_bytes(byte_string[4:])
401 if cidr_int is not None:
402 cidr_bits = '{0:b}'.format(cidr_int)
403 cidr = len(cidr_bits.rstrip('0'))
404 value = value + '/' + str_cls(cidr)
405 self._native = value
406 return self._native
407
408 def __ne__(self, other):
409 return not self == other
410
411 def __eq__(self, other):
412 """
413 :param other:
414 Another IPAddress object
415
416 :return:
417 A boolean
418 """
419
420 if not isinstance(other, IPAddress):
421 return False
422
wbondabf76d12017-03-03 07:18:21 -0500423 return self.__bytes__() == other.__bytes__()
wbond35701c92015-08-07 13:45:21 -0400424
425
wbonde91513e2015-06-03 14:52:18 -0400426class Attribute(Sequence):
427 _fields = [
428 ('type', ObjectIdentifier),
429 ('values', SetOf, {'spec': Any}),
430 ]
431
432
433class Attributes(SequenceOf):
434 _child_spec = Attribute
435
436
437class KeyUsage(BitString):
438 _map = {
439 0: 'digital_signature',
440 1: 'non_repudiation',
441 2: 'key_encipherment',
442 3: 'data_encipherment',
443 4: 'key_agreement',
444 5: 'key_cert_sign',
445 6: 'crl_sign',
446 7: 'encipher_only',
447 8: 'decipher_only',
448 }
449
450
451class PrivateKeyUsagePeriod(Sequence):
452 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -0400453 ('not_before', GeneralizedTime, {'implicit': 0, 'optional': True}),
454 ('not_after', GeneralizedTime, {'implicit': 1, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -0400455 ]
456
457
wbond1ba880e2017-09-14 13:08:50 -0400458class NotReallyTeletexString(TeletexString):
459 """
460 OpenSSL (and probably some other libraries) puts ISO-8859-1
461 into TeletexString instead of ITU T.61. We use Windows-1252 when
462 decoding since it is a superset of ISO-8859-1, and less likely to
463 cause encoding issues, but we stay strict with encoding to prevent
464 us from creating bad data.
465 """
466
467 _decoding_encoding = 'cp1252'
468
469 def __unicode__(self):
470 """
471 :return:
472 A unicode string
473 """
474
475 if self.contents is None:
476 return ''
477 if self._unicode is None:
478 self._unicode = self._merge_chunks().decode(self._decoding_encoding)
479 return self._unicode
480
481
482@contextmanager
483def strict_teletex():
484 try:
485 NotReallyTeletexString._decoding_encoding = 'teletex'
486 yield
487 finally:
488 NotReallyTeletexString._decoding_encoding = 'cp1252'
489
490
wbonde91513e2015-06-03 14:52:18 -0400491class DirectoryString(Choice):
492 _alternatives = [
wbond1ba880e2017-09-14 13:08:50 -0400493 ('teletex_string', NotReallyTeletexString),
wbonde91513e2015-06-03 14:52:18 -0400494 ('printable_string', PrintableString),
495 ('universal_string', UniversalString),
496 ('utf8_string', UTF8String),
497 ('bmp_string', BMPString),
wbond5024f072016-03-18 14:14:10 -0400498 # This is an invalid/bad alternative, but some broken certs use it
499 ('ia5_string', IA5String),
wbonde91513e2015-06-03 14:52:18 -0400500 ]
501
502
503class NameType(ObjectIdentifier):
504 _map = {
505 '2.5.4.3': 'common_name',
506 '2.5.4.4': 'surname',
507 '2.5.4.5': 'serial_number',
508 '2.5.4.6': 'country_name',
509 '2.5.4.7': 'locality_name',
510 '2.5.4.8': 'state_or_province_name',
wbond50959e92015-07-23 07:13:45 -0400511 '2.5.4.9': 'street_address',
wbonde91513e2015-06-03 14:52:18 -0400512 '2.5.4.10': 'organization_name',
513 '2.5.4.11': 'organizational_unit_name',
514 '2.5.4.12': 'title',
wbond29720122015-07-17 12:01:24 -0400515 '2.5.4.15': 'business_category',
wbond50959e92015-07-23 07:13:45 -0400516 '2.5.4.17': 'postal_code',
wbond526dd542015-12-07 10:58:06 -0500517 '2.5.4.20': 'telephone_number',
wbonde91513e2015-06-03 14:52:18 -0400518 '2.5.4.41': 'name',
519 '2.5.4.42': 'given_name',
520 '2.5.4.43': 'initials',
521 '2.5.4.44': 'generation_qualifier',
wbondd9d3d852016-12-05 06:50:12 -0500522 '2.5.4.45': 'unique_identifier',
wbonde91513e2015-06-03 14:52:18 -0400523 '2.5.4.46': 'dn_qualifier',
wbond50959e92015-07-23 07:13:45 -0400524 '2.5.4.65': 'pseudonym',
wbond526dd542015-12-07 10:58:06 -0500525 '2.5.4.97': 'organization_identifier',
wbonda11c4502017-11-22 11:13:00 -0500526 # https://www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf
527 '2.23.133.2.1': 'tpm_manufacturer',
528 '2.23.133.2.2': 'tpm_model',
529 '2.23.133.2.3': 'tpm_version',
530 '2.23.133.2.4': 'platform_manufacturer',
531 '2.23.133.2.5': 'platform_model',
532 '2.23.133.2.6': 'platform_version',
wbonde91513e2015-06-03 14:52:18 -0400533 # https://tools.ietf.org/html/rfc2985#page-26
534 '1.2.840.113549.1.9.1': 'email_address',
535 # Page 10 of https://cabforum.org/wp-content/uploads/EV-V1_5_5.pdf
536 '1.3.6.1.4.1.311.60.2.1.1': 'incorporation_locality',
537 '1.3.6.1.4.1.311.60.2.1.2': 'incorporation_state_or_province',
538 '1.3.6.1.4.1.311.60.2.1.3': 'incorporation_country',
wbond50959e92015-07-23 07:13:45 -0400539 # https://tools.ietf.org/html/rfc2247#section-4
540 '0.9.2342.19200300.100.1.25': 'domain_component',
wbond0c0050f2015-12-07 10:22:08 -0500541 # http://www.alvestrand.no/objectid/0.2.262.1.10.7.20.html
542 '0.2.262.1.10.7.20': 'name_distinguisher',
wbonde91513e2015-06-03 14:52:18 -0400543 }
544
wbond0c8f2ca2015-07-23 07:15:06 -0400545 # This order is largely based on observed order seen in EV certs from
546 # Symantec and DigiCert. Some of the uncommon name-related fields are
547 # just placed in what seems like a reasonable order.
548 preferred_order = [
549 'incorporation_country',
550 'incorporation_state_or_province',
551 'incorporation_locality',
552 'business_category',
553 'serial_number',
554 'country_name',
555 'postal_code',
556 'state_or_province_name',
557 'locality_name',
558 'street_address',
559 'organization_name',
560 'organizational_unit_name',
561 'title',
562 'common_name',
563 'initials',
564 'generation_qualifier',
565 'surname',
566 'given_name',
567 'name',
568 'pseudonym',
569 'dn_qualifier',
wbond526dd542015-12-07 10:58:06 -0500570 'telephone_number',
wbond0c8f2ca2015-07-23 07:15:06 -0400571 'email_address',
572 'domain_component',
wbond526dd542015-12-07 10:58:06 -0500573 'name_distinguisher',
574 'organization_identifier',
wbonda11c4502017-11-22 11:13:00 -0500575 'tpm_manufacturer',
576 'tpm_model',
577 'tpm_version',
578 'platform_manufacturer',
579 'platform_model',
580 'platform_version',
wbond0c8f2ca2015-07-23 07:15:06 -0400581 ]
582
Peter Sagersonc429d602016-10-21 12:02:35 -0700583 @classmethod
584 def preferred_ordinal(cls, attr_name):
585 """
586 Returns an ordering value for a particular attribute key.
587
588 Unrecognized attributes and OIDs will be sorted lexically at the end.
589
590 :return:
591 An orderable value.
592
593 """
wbonda01911d2016-11-23 06:54:42 -0500594
595 attr_name = cls.map(attr_name)
Peter Sagersonc429d602016-10-21 12:02:35 -0700596 if attr_name in cls.preferred_order:
597 ordinal = cls.preferred_order.index(attr_name)
598 else:
599 ordinal = len(cls.preferred_order)
600
601 return (ordinal, attr_name)
602
wbondfce13382015-07-17 12:10:43 -0400603 @property
604 def human_friendly(self):
605 """
606 :return:
607 A human-friendly unicode string to display to users
608 """
609
610 return {
611 'common_name': 'Common Name',
612 'surname': 'Surname',
613 'serial_number': 'Serial Number',
614 'country_name': 'Country',
615 'locality_name': 'Locality',
616 'state_or_province_name': 'State/Province',
wbond50959e92015-07-23 07:13:45 -0400617 'street_address': 'Street Address',
wbondfce13382015-07-17 12:10:43 -0400618 'organization_name': 'Organization',
619 'organizational_unit_name': 'Organizational Unit',
620 'title': 'Title',
621 'business_category': 'Business Category',
wbond50959e92015-07-23 07:13:45 -0400622 'postal_code': 'Postal Code',
wbond526dd542015-12-07 10:58:06 -0500623 'telephone_number': 'Telephone Number',
wbondfce13382015-07-17 12:10:43 -0400624 'name': 'Name',
625 'given_name': 'Given Name',
626 'initials': 'Initials',
627 'generation_qualifier': 'Generation Qualifier',
wbondd9d3d852016-12-05 06:50:12 -0500628 'unique_identifier': 'Unique Identifier',
wbondfce13382015-07-17 12:10:43 -0400629 'dn_qualifier': 'DN Qualifier',
wbond50959e92015-07-23 07:13:45 -0400630 'pseudonym': 'Pseudonym',
wbondfce13382015-07-17 12:10:43 -0400631 'email_address': 'Email Address',
632 'incorporation_locality': 'Incorporation Locality',
633 'incorporation_state_or_province': 'Incorporation State/Province',
634 'incorporation_country': 'Incorporation Country',
wbond50959e92015-07-23 07:13:45 -0400635 'domain_component': 'Domain Component',
wbond526dd542015-12-07 10:58:06 -0500636 'name_distinguisher': 'Name Distinguisher',
637 'organization_identifier': 'Organization Identifier',
wbonda11c4502017-11-22 11:13:00 -0500638 'tpm_manufacturer': 'TPM Manufacturer',
639 'tpm_model': 'TPM Model',
640 'tpm_version': 'TPM Version',
641 'platform_manufacturer': 'Platform Manufacturer',
642 'platform_model': 'Platform Model',
643 'platform_version': 'Platform Version',
wbondff8b0342015-12-07 10:58:49 -0500644 }.get(self.native, self.native)
wbondfce13382015-07-17 12:10:43 -0400645
wbonde91513e2015-06-03 14:52:18 -0400646
647class NameTypeAndValue(Sequence):
648 _fields = [
649 ('type', NameType),
650 ('value', Any),
651 ]
652
653 _oid_pair = ('type', 'value')
654 _oid_specs = {
655 'common_name': DirectoryString,
656 'surname': DirectoryString,
wbond25e96a92015-07-01 09:23:12 -0400657 'serial_number': DirectoryString,
658 'country_name': DirectoryString,
wbonde91513e2015-06-03 14:52:18 -0400659 'locality_name': DirectoryString,
660 'state_or_province_name': DirectoryString,
wbond50959e92015-07-23 07:13:45 -0400661 'street_address': DirectoryString,
wbonde91513e2015-06-03 14:52:18 -0400662 'organization_name': DirectoryString,
663 'organizational_unit_name': DirectoryString,
664 'title': DirectoryString,
wbond50959e92015-07-23 07:13:45 -0400665 'business_category': DirectoryString,
666 'postal_code': DirectoryString,
wbond526dd542015-12-07 10:58:06 -0500667 'telephone_number': PrintableString,
wbonde91513e2015-06-03 14:52:18 -0400668 'name': DirectoryString,
669 'given_name': DirectoryString,
670 'initials': DirectoryString,
671 'generation_qualifier': DirectoryString,
wbondd9d3d852016-12-05 06:50:12 -0500672 'unique_identifier': OctetBitString,
wbond25e96a92015-07-01 09:23:12 -0400673 'dn_qualifier': DirectoryString,
wbond50959e92015-07-23 07:13:45 -0400674 'pseudonym': DirectoryString,
wbonde91513e2015-06-03 14:52:18 -0400675 # https://tools.ietf.org/html/rfc2985#page-26
wbond35701c92015-08-07 13:45:21 -0400676 'email_address': EmailAddress,
wbonde91513e2015-06-03 14:52:18 -0400677 # Page 10 of https://cabforum.org/wp-content/uploads/EV-V1_5_5.pdf
678 'incorporation_locality': DirectoryString,
679 'incorporation_state_or_province': DirectoryString,
wbond25e96a92015-07-01 09:23:12 -0400680 'incorporation_country': DirectoryString,
wbond35701c92015-08-07 13:45:21 -0400681 'domain_component': DNSName,
wbond526dd542015-12-07 10:58:06 -0500682 'name_distinguisher': DirectoryString,
683 'organization_identifier': DirectoryString,
wbonda11c4502017-11-22 11:13:00 -0500684 'tpm_manufacturer': UTF8String,
685 'tpm_model': UTF8String,
686 'tpm_version': UTF8String,
687 'platform_manufacturer': UTF8String,
688 'platform_model': UTF8String,
689 'platform_version': UTF8String,
wbonde91513e2015-06-03 14:52:18 -0400690 }
691
wbondfd65d602015-07-23 07:16:44 -0400692 _prepped = None
693
694 @property
695 def prepped_value(self):
696 """
697 Returns the value after being processed by the internationalized string
wbondd913db52015-08-06 12:44:15 -0400698 preparation as specified by RFC 5280
wbondfd65d602015-07-23 07:16:44 -0400699
700 :return:
701 A unicode string
702 """
703
704 if self._prepped is None:
705 self._prepped = self._ldap_string_prep(self['value'].native)
706 return self._prepped
707
wbond15da2ac2015-07-27 10:23:40 -0400708 def __ne__(self, other):
709 return not self == other
710
wbondfd65d602015-07-23 07:16:44 -0400711 def __eq__(self, other):
712 """
713 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
714
715 :param other:
716 Another NameTypeAndValue object
717
718 :return:
719 A boolean
720 """
721
722 if not isinstance(other, NameTypeAndValue):
723 return False
724
725 if other['type'].native != self['type'].native:
726 return False
727
728 return other.prepped_value == self.prepped_value
729
730 def _ldap_string_prep(self, string):
731 """
732 Implements the internationalized string preparation algorithm from
733 RFC 4518. https://tools.ietf.org/html/rfc4518#section-2
734
735 :param string:
736 A unicode string to prepare
737
738 :return:
739 A prepared unicode string, ready for comparison
740 """
741
742 # Map step
743 string = re.sub('[\u00ad\u1806\u034f\u180b-\u180d\ufe0f-\uff00\ufffc]+', '', string)
744 string = re.sub('[\u0009\u000a\u000b\u000c\u000d\u0085]', ' ', string)
wbond93ad1432015-07-27 10:26:24 -0400745 if sys.maxunicode == 0xffff:
746 # Some installs of Python 2.7 don't support 8-digit unicode escape
747 # ranges, so we have to break them into pieces
748 # Original was: \U0001D173-\U0001D17A and \U000E0020-\U000E007F
749 string = re.sub('\ud834[\udd73-\udd7a]|\udb40[\udc20-\udc7f]|\U000e0001', '', string)
750 else:
751 string = re.sub('[\U0001D173-\U0001D17A\U000E0020-\U000E007F\U000e0001]', '', string)
wbonda26664f2015-10-07 11:57:35 -0400752 string = re.sub(
753 '[\u0000-\u0008\u000e-\u001f\u007f-\u0084\u0086-\u009f\u06dd\u070f\u180e\u200c-\u200f'
754 '\u202a-\u202e\u2060-\u2063\u206a-\u206f\ufeff\ufff9-\ufffb]+',
755 '',
756 string
757 )
wbondfd65d602015-07-23 07:16:44 -0400758 string = string.replace('\u200b', '')
759 string = re.sub('[\u00a0\u1680\u2000-\u200a\u2028-\u2029\u202f\u205f\u3000]', ' ', string)
760
761 string = ''.join(map(stringprep.map_table_b2, string))
762
763 # Normalize step
764 string = unicodedata.normalize('NFKC', string)
765
766 # Prohibit step
767 for char in string:
768 if stringprep.in_table_a1(char):
wbonda26664f2015-10-07 11:57:35 -0400769 raise ValueError(unwrap(
770 '''
771 X.509 Name objects may not contain unassigned code points
772 '''
773 ))
wbondfd65d602015-07-23 07:16:44 -0400774
775 if stringprep.in_table_c8(char):
wbonda26664f2015-10-07 11:57:35 -0400776 raise ValueError(unwrap(
777 '''
778 X.509 Name objects may not contain change display or
779 zzzzdeprecated characters
780 '''
781 ))
wbondfd65d602015-07-23 07:16:44 -0400782
783 if stringprep.in_table_c3(char):
wbonda26664f2015-10-07 11:57:35 -0400784 raise ValueError(unwrap(
785 '''
786 X.509 Name objects may not contain private use characters
787 '''
788 ))
wbondfd65d602015-07-23 07:16:44 -0400789
790 if stringprep.in_table_c4(char):
wbonda26664f2015-10-07 11:57:35 -0400791 raise ValueError(unwrap(
792 '''
793 X.509 Name objects may not contain non-character code points
794 '''
795 ))
wbondfd65d602015-07-23 07:16:44 -0400796
797 if stringprep.in_table_c5(char):
wbonda26664f2015-10-07 11:57:35 -0400798 raise ValueError(unwrap(
799 '''
800 X.509 Name objects may not contain surrogate code points
801 '''
802 ))
wbondfd65d602015-07-23 07:16:44 -0400803
804 if char == '\ufffd':
wbonda26664f2015-10-07 11:57:35 -0400805 raise ValueError(unwrap(
806 '''
807 X.509 Name objects may not contain the replacement character
808 '''
809 ))
wbondfd65d602015-07-23 07:16:44 -0400810
811 # Check bidirectional step - here we ensure that we are not mixing
812 # left-to-right and right-to-left text in the string
813 has_r_and_al_cat = False
814 has_l_cat = False
815 for char in string:
816 if stringprep.in_table_d1(char):
817 has_r_and_al_cat = True
818 elif stringprep.in_table_d2(char):
819 has_l_cat = True
820
821 if has_r_and_al_cat:
822 first_is_r_and_al = stringprep.in_table_d1(string[0])
823 last_is_r_and_al = stringprep.in_table_d1(string[-1])
824
825 if has_l_cat or not first_is_r_and_al or not last_is_r_and_al:
wbonda26664f2015-10-07 11:57:35 -0400826 raise ValueError(unwrap(
827 '''
828 X.509 Name object contains a malformed bidirectional
829 sequence
830 '''
831 ))
wbondfd65d602015-07-23 07:16:44 -0400832
833 # Insignificant space handling step
834 string = ' ' + re.sub(' +', ' ', string).strip() + ' '
835
836 return string
837
wbonde91513e2015-06-03 14:52:18 -0400838
839class RelativeDistinguishedName(SetOf):
840 _child_spec = NameTypeAndValue
841
wbond936b9712015-07-24 08:38:55 -0400842 @property
843 def hashable(self):
844 """
845 :return:
846 A unicode string that can be used as a dict key or in a set
847 """
848
849 output = []
wbond5fb30302015-08-06 12:44:57 -0400850 values = self._get_values(self)
wbond936b9712015-07-24 08:38:55 -0400851 for key in sorted(values.keys()):
852 output.append('%s: %s' % (key, values[key]))
853 # Unit separator is used here since the normalization process for
854 # values moves any such character, and the keys are all dotted integers
855 # or under_score_words
856 return '\x1F'.join(output)
857
wbond15da2ac2015-07-27 10:23:40 -0400858 def __ne__(self, other):
859 return not self == other
860
wbondfd65d602015-07-23 07:16:44 -0400861 def __eq__(self, other):
862 """
863 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
864
865 :param other:
866 Another RelativeDistinguishedName object
867
868 :return:
869 A boolean
870 """
871
872 if not isinstance(other, RelativeDistinguishedName):
873 return False
874
875 if len(self) != len(other):
876 return False
877
878 self_types = self._get_types(self)
879 other_types = self._get_types(other)
880
881 if self_types != other_types:
882 return False
883
884 self_values = self._get_values(self)
885 other_values = self._get_values(other)
886
wbonda26664f2015-10-07 11:57:35 -0400887 for type_name_ in self_types:
888 if self_values[type_name_] != other_values[type_name_]:
wbondfd65d602015-07-23 07:16:44 -0400889 return False
890
891 return True
892
893 def _get_types(self, rdn):
894 """
895 Returns a set of types contained in an RDN
896
897 :param rdn:
898 A RelativeDistinguishedName object
899
900 :return:
901 A set object with unicode strings of NameTypeAndValue type field
902 values
903 """
904
905 return set([ntv['type'].native for ntv in rdn])
906
907 def _get_values(self, rdn):
908 """
909 Returns a dict of prepped values contained in an RDN
910
911 :param rdn:
912 A RelativeDistinguishedName object
913
914 :return:
915 A dict object with unicode strings of NameTypeAndValue value field
916 values that have been prepped for comparison
917 """
918
wbond153cee92015-08-24 09:37:48 -0400919 output = {}
wbonda26664f2015-10-07 11:57:35 -0400920 [output.update([(ntv['type'].native, ntv.prepped_value)]) for ntv in rdn]
wbond153cee92015-08-24 09:37:48 -0400921 return output
wbondfd65d602015-07-23 07:16:44 -0400922
wbonde91513e2015-06-03 14:52:18 -0400923
924class RDNSequence(SequenceOf):
925 _child_spec = RelativeDistinguishedName
926
wbond936b9712015-07-24 08:38:55 -0400927 @property
928 def hashable(self):
929 """
930 :return:
931 A unicode string that can be used as a dict key or in a set
932 """
933
934 # Record separator is used here since the normalization process for
935 # values moves any such character, and the keys are all dotted integers
936 # or under_score_words
937 return '\x1E'.join(rdn.hashable for rdn in self)
938
wbond15da2ac2015-07-27 10:23:40 -0400939 def __ne__(self, other):
940 return not self == other
941
wbondfd65d602015-07-23 07:16:44 -0400942 def __eq__(self, other):
943 """
944 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
945
946 :param other:
947 Another RDNSequence object
948
949 :return:
950 A boolean
951 """
952
953 if not isinstance(other, RDNSequence):
954 return False
955
956 if len(self) != len(other):
957 return False
958
959 for index, self_rdn in enumerate(self):
960 if other[index] != self_rdn:
961 return False
962
963 return True
964
wbonde91513e2015-06-03 14:52:18 -0400965
966class Name(Choice):
967 _alternatives = [
968 ('', RDNSequence),
969 ]
970
wbondfce13382015-07-17 12:10:43 -0400971 _human_friendly = None
wbond91d70322015-07-17 12:04:55 -0400972 _sha1 = None
973 _sha256 = None
974
wbondfd65d602015-07-23 07:16:44 -0400975 @classmethod
wbond965ea212015-11-30 15:54:01 -0500976 def build(cls, name_dict, use_printable=False):
wbondfd65d602015-07-23 07:16:44 -0400977 """
978 Creates a Name object from a dict of unicode string keys and values.
979 The keys should be from NameType._map, or a dotted-integer OID unicode
980 string.
981
982 :param name_dict:
983 A dict of name information, e.g. {"common_name": "Will Bond",
984 "country_name": "US", "organization": "Codex Non Sufficit LC"}
985
wbond965ea212015-11-30 15:54:01 -0500986 :param use_printable:
987 A bool - if PrintableString should be used for encoding instead of
Edward Betts42bed252017-08-30 20:49:39 +0100988 UTF8String. This is for backwards compatibility with old software.
wbond965ea212015-11-30 15:54:01 -0500989
wbondfd65d602015-07-23 07:16:44 -0400990 :return:
991 An x509.Name object
992 """
993
wbonde1fa6cd2016-07-21 21:46:25 -0400994 rdns = []
wbond965ea212015-11-30 15:54:01 -0500995 if not use_printable:
996 encoding_name = 'utf8_string'
997 encoding_class = UTF8String
998 else:
999 encoding_name = 'printable_string'
1000 encoding_class = PrintableString
wbondfd65d602015-07-23 07:16:44 -04001001
wbonda01911d2016-11-23 06:54:42 -05001002 # Sort the attributes according to NameType.preferred_order
Peter Sagersonc429d602016-10-21 12:02:35 -07001003 name_dict = OrderedDict(
1004 sorted(
1005 name_dict.items(),
1006 key=lambda item: NameType.preferred_ordinal(item[0])
1007 )
1008 )
wbondfd65d602015-07-23 07:16:44 -04001009
Peter Sagersonc429d602016-10-21 12:02:35 -07001010 for attribute_name, attribute_value in name_dict.items():
wbonda01911d2016-11-23 06:54:42 -05001011 attribute_name = NameType.map(attribute_name)
wbond21db1212015-10-20 00:49:25 -04001012 if attribute_name == 'email_address':
Peter Sagersonc429d602016-10-21 12:02:35 -07001013 value = EmailAddress(attribute_value)
wbond21db1212015-10-20 00:49:25 -04001014 elif attribute_name == 'domain_component':
Peter Sagersonc429d602016-10-21 12:02:35 -07001015 value = DNSName(attribute_value)
wbond21db1212015-10-20 00:49:25 -04001016 elif attribute_name in set(['dn_qualifier', 'country_name', 'serial_number']):
1017 value = DirectoryString(
1018 name='printable_string',
Peter Sagersonc429d602016-10-21 12:02:35 -07001019 value=PrintableString(attribute_value)
wbond21db1212015-10-20 00:49:25 -04001020 )
wbondfd65d602015-07-23 07:16:44 -04001021 else:
wbond21db1212015-10-20 00:49:25 -04001022 value = DirectoryString(
wbond965ea212015-11-30 15:54:01 -05001023 name=encoding_name,
Peter Sagersonc429d602016-10-21 12:02:35 -07001024 value=encoding_class(attribute_value)
wbond21db1212015-10-20 00:49:25 -04001025 )
wbondfd65d602015-07-23 07:16:44 -04001026
wbonde1fa6cd2016-07-21 21:46:25 -04001027 rdns.append(RelativeDistinguishedName([
1028 NameTypeAndValue({
1029 'type': attribute_name,
1030 'value': value
1031 })
1032 ]))
wbondfd65d602015-07-23 07:16:44 -04001033
wbonde1fa6cd2016-07-21 21:46:25 -04001034 return cls(name='', value=RDNSequence(rdns))
wbondfd65d602015-07-23 07:16:44 -04001035
wbond936b9712015-07-24 08:38:55 -04001036 @property
1037 def hashable(self):
1038 """
1039 :return:
1040 A unicode string that can be used as a dict key or in a set
1041 """
1042
1043 return self.chosen.hashable
1044
wbond3298a822015-10-24 13:10:38 -04001045 def __len__(self):
1046 return len(self.chosen)
1047
wbond15da2ac2015-07-27 10:23:40 -04001048 def __ne__(self, other):
1049 return not self == other
1050
wbondfd65d602015-07-23 07:16:44 -04001051 def __eq__(self, other):
1052 """
1053 Equality as defined by https://tools.ietf.org/html/rfc5280#section-7.1
1054
1055 :param other:
1056 Another Name object
1057
1058 :return:
1059 A boolean
1060 """
1061
1062 if not isinstance(other, Name):
1063 return False
1064 return self.chosen == other.chosen
1065
wbonde91513e2015-06-03 14:52:18 -04001066 @property
1067 def native(self):
wbonde91513e2015-06-03 14:52:18 -04001068 if self._native is None:
1069 self._native = OrderedDict()
1070 for rdn in self.chosen.native:
1071 for type_val in rdn:
wbond47f78fd2015-07-17 12:08:51 -04001072 field_name = type_val['type']
1073 if field_name in self._native:
Alexander Duryagin4ae81c92015-12-15 14:20:15 +03001074 existing = self._native[field_name]
1075 if not isinstance(existing, list):
1076 existing = self._native[field_name] = [existing]
1077 existing.append(type_val['value'])
wbonde91513e2015-06-03 14:52:18 -04001078 else:
wbond47f78fd2015-07-17 12:08:51 -04001079 self._native[field_name] = type_val['value']
wbonde91513e2015-06-03 14:52:18 -04001080 return self._native
1081
wbond91d70322015-07-17 12:04:55 -04001082 @property
wbondfce13382015-07-17 12:10:43 -04001083 def human_friendly(self):
1084 """
1085 :return:
1086 A human-friendly unicode string containing the parts of the name
1087 """
1088
1089 if self._human_friendly is None:
1090 data = OrderedDict()
wbond8cbf4042015-12-07 10:59:42 -05001091 last_field = None
wbondfce13382015-07-17 12:10:43 -04001092 for rdn in self.chosen:
1093 for type_val in rdn:
1094 field_name = type_val['type'].human_friendly
wbond8cbf4042015-12-07 10:59:42 -05001095 last_field = field_name
wbondfce13382015-07-17 12:10:43 -04001096 if field_name in data:
1097 data[field_name] = [data[field_name]]
1098 data[field_name].append(type_val['value'])
1099 else:
1100 data[field_name] = type_val['value']
1101 to_join = []
wbond8cbf4042015-12-07 10:59:42 -05001102 keys = data.keys()
1103 if last_field == 'Country':
1104 keys = reversed(list(keys))
1105 for key in keys:
wbondfce13382015-07-17 12:10:43 -04001106 value = data[key]
wbond7dac7a92015-12-07 11:00:07 -05001107 native_value = self._recursive_humanize(value)
1108 to_join.append('%s: %s' % (key, native_value))
wbondfce13382015-07-17 12:10:43 -04001109
1110 has_comma = False
1111 for element in to_join:
1112 if element.find(',') != -1:
1113 has_comma = True
1114 break
1115
1116 separator = ', ' if not has_comma else '; '
1117 self._human_friendly = separator.join(to_join[::-1])
1118
1119 return self._human_friendly
1120
wbond7dac7a92015-12-07 11:00:07 -05001121 def _recursive_humanize(self, value):
1122 """
1123 Recursively serializes data compiled from the RDNSequence
1124
1125 :param value:
1126 An Asn1Value object, or a list of Asn1Value objects
1127
1128 :return:
1129 A unicode string
1130 """
1131
1132 if isinstance(value, list):
1133 return', '.join(
1134 reversed([self._recursive_humanize(sub_value) for sub_value in value])
1135 )
1136 return value.native
1137
wbondfce13382015-07-17 12:10:43 -04001138 @property
wbond91d70322015-07-17 12:04:55 -04001139 def sha1(self):
1140 """
1141 :return:
1142 The SHA1 hash of the DER-encoded bytes of this name
1143 """
1144
1145 if self._sha1 is None:
1146 self._sha1 = hashlib.sha1(self.dump()).digest()
1147 return self._sha1
1148
1149 @property
1150 def sha256(self):
1151 """
1152 :return:
1153 The SHA-256 hash of the DER-encoded bytes of this name
1154 """
1155
1156 if self._sha256 is None:
1157 self._sha256 = hashlib.sha256(self.dump()).digest()
1158 return self._sha256
1159
wbonde91513e2015-06-03 14:52:18 -04001160
1161class AnotherName(Sequence):
1162 _fields = [
1163 ('type_id', ObjectIdentifier),
wbondd62ed9a2017-09-15 07:13:52 -04001164 ('value', Any, {'explicit': 0}),
wbonde91513e2015-06-03 14:52:18 -04001165 ]
1166
1167
1168class CountryName(Choice):
1169 class_ = 1
1170 tag = 1
1171
1172 _alternatives = [
1173 ('x121_dcc_code', NumericString),
1174 ('iso_3166_alpha2_code', PrintableString),
1175 ]
1176
1177
1178class AdministrationDomainName(Choice):
1179 class_ = 1
1180 tag = 2
1181
1182 _alternatives = [
1183 ('numeric', NumericString),
1184 ('printable', PrintableString),
1185 ]
1186
1187
1188class PrivateDomainName(Choice):
1189 _alternatives = [
1190 ('numeric', NumericString),
1191 ('printable', PrintableString),
1192 ]
1193
1194
1195class PersonalName(Set):
1196 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001197 ('surname', PrintableString, {'implicit': 0}),
1198 ('given_name', PrintableString, {'implicit': 1, 'optional': True}),
1199 ('initials', PrintableString, {'implicit': 2, 'optional': True}),
1200 ('generation_qualifier', PrintableString, {'implicit': 3, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001201 ]
1202
1203
1204class TeletexPersonalName(Set):
1205 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001206 ('surname', TeletexString, {'implicit': 0}),
1207 ('given_name', TeletexString, {'implicit': 1, 'optional': True}),
1208 ('initials', TeletexString, {'implicit': 2, 'optional': True}),
1209 ('generation_qualifier', TeletexString, {'implicit': 3, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001210 ]
1211
1212
1213class OrganizationalUnitNames(SequenceOf):
1214 _child_spec = PrintableString
1215
1216
1217class TeletexOrganizationalUnitNames(SequenceOf):
1218 _child_spec = TeletexString
1219
1220
1221class BuiltInStandardAttributes(Sequence):
1222 _fields = [
1223 ('country_name', CountryName, {'optional': True}),
1224 ('administration_domain_name', AdministrationDomainName, {'optional': True}),
wbondd62ed9a2017-09-15 07:13:52 -04001225 ('network_address', NumericString, {'implicit': 0, 'optional': True}),
1226 ('terminal_identifier', PrintableString, {'implicit': 1, 'optional': True}),
1227 ('private_domain_name', PrivateDomainName, {'explicit': 2, 'optional': True}),
1228 ('organization_name', PrintableString, {'implicit': 3, 'optional': True}),
1229 ('numeric_user_identifier', NumericString, {'implicit': 4, 'optional': True}),
1230 ('personal_name', PersonalName, {'implicit': 5, 'optional': True}),
1231 ('organizational_unit_names', OrganizationalUnitNames, {'implicit': 6, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001232 ]
1233
1234
1235class BuiltInDomainDefinedAttribute(Sequence):
1236 _fields = [
1237 ('type', PrintableString),
1238 ('value', PrintableString),
1239 ]
1240
1241
1242class BuiltInDomainDefinedAttributes(SequenceOf):
1243 _child_spec = BuiltInDomainDefinedAttribute
1244
1245
1246class TeletexDomainDefinedAttribute(Sequence):
1247 _fields = [
1248 ('type', TeletexString),
1249 ('value', TeletexString),
1250 ]
1251
1252
1253class TeletexDomainDefinedAttributes(SequenceOf):
1254 _child_spec = TeletexDomainDefinedAttribute
1255
1256
1257class PhysicalDeliveryCountryName(Choice):
1258 _alternatives = [
1259 ('x121_dcc_code', NumericString),
1260 ('iso_3166_alpha2_code', PrintableString),
1261 ]
1262
1263
1264class PostalCode(Choice):
1265 _alternatives = [
1266 ('numeric_code', NumericString),
1267 ('printable_code', PrintableString),
1268 ]
1269
1270
1271class PDSParameter(Set):
1272 _fields = [
1273 ('printable_string', PrintableString, {'optional': True}),
1274 ('teletex_string', TeletexString, {'optional': True}),
1275 ]
1276
1277
1278class PrintableAddress(SequenceOf):
1279 _child_spec = PrintableString
1280
1281
1282class UnformattedPostalAddress(Set):
1283 _fields = [
1284 ('printable_address', PrintableAddress, {'optional': True}),
1285 ('teletex_string', TeletexString, {'optional': True}),
1286 ]
1287
1288
1289class E1634Address(Sequence):
1290 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001291 ('number', NumericString, {'implicit': 0}),
1292 ('sub_address', NumericString, {'implicit': 1, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001293 ]
1294
1295
1296class NAddresses(SetOf):
1297 _child_spec = OctetString
1298
1299
1300class PresentationAddress(Sequence):
1301 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001302 ('p_selector', OctetString, {'explicit': 0, 'optional': True}),
1303 ('s_selector', OctetString, {'explicit': 1, 'optional': True}),
1304 ('t_selector', OctetString, {'explicit': 2, 'optional': True}),
1305 ('n_addresses', NAddresses, {'explicit': 3}),
wbonde91513e2015-06-03 14:52:18 -04001306 ]
1307
1308
1309class ExtendedNetworkAddress(Choice):
1310 _alternatives = [
1311 ('e163_4_address', E1634Address),
wbondd62ed9a2017-09-15 07:13:52 -04001312 ('psap_address', PresentationAddress, {'implicit': 0})
wbonde91513e2015-06-03 14:52:18 -04001313 ]
1314
1315
1316class TerminalType(Integer):
1317 _map = {
1318 3: 'telex',
1319 4: 'teletex',
1320 5: 'g3_facsimile',
1321 6: 'g4_facsimile',
1322 7: 'ia5_terminal',
1323 8: 'videotex',
1324 }
1325
1326
1327class ExtensionAttributeType(Integer):
1328 _map = {
1329 1: 'common_name',
1330 2: 'teletex_common_name',
1331 3: 'teletex_organization_name',
1332 4: 'teletex_personal_name',
1333 5: 'teletex_organization_unit_names',
1334 6: 'teletex_domain_defined_attributes',
1335 7: 'pds_name',
1336 8: 'physical_delivery_country_name',
1337 9: 'postal_code',
1338 10: 'physical_delivery_office_name',
1339 11: 'physical_delivery_office_number',
1340 12: 'extension_of_address_components',
1341 13: 'physical_delivery_personal_name',
1342 14: 'physical_delivery_organization_name',
1343 15: 'extension_physical_delivery_address_components',
1344 16: 'unformatted_postal_address',
1345 17: 'street_address',
1346 18: 'post_office_box_address',
1347 19: 'poste_restante_address',
1348 20: 'unique_postal_name',
1349 21: 'local_postal_attributes',
1350 22: 'extended_network_address',
1351 23: 'terminal_type',
1352 }
1353
1354
1355class ExtensionAttribute(Sequence):
1356 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001357 ('extension_attribute_type', ExtensionAttributeType, {'implicit': 0}),
1358 ('extension_attribute_value', Any, {'explicit': 1}),
wbonde91513e2015-06-03 14:52:18 -04001359 ]
1360
1361 _oid_pair = ('extension_attribute_type', 'extension_attribute_value')
1362 _oid_specs = {
1363 'common_name': PrintableString,
1364 'teletex_common_name': TeletexString,
1365 'teletex_organization_name': TeletexString,
1366 'teletex_personal_name': TeletexPersonalName,
1367 'teletex_organization_unit_names': TeletexOrganizationalUnitNames,
1368 'teletex_domain_defined_attributes': TeletexDomainDefinedAttributes,
1369 'pds_name': PrintableString,
1370 'physical_delivery_country_name': PhysicalDeliveryCountryName,
1371 'postal_code': PostalCode,
1372 'physical_delivery_office_name': PDSParameter,
1373 'physical_delivery_office_number': PDSParameter,
1374 'extension_of_address_components': PDSParameter,
1375 'physical_delivery_personal_name': PDSParameter,
1376 'physical_delivery_organization_name': PDSParameter,
1377 'extension_physical_delivery_address_components': PDSParameter,
1378 'unformatted_postal_address': UnformattedPostalAddress,
1379 'street_address': PDSParameter,
1380 'post_office_box_address': PDSParameter,
1381 'poste_restante_address': PDSParameter,
1382 'unique_postal_name': PDSParameter,
1383 'local_postal_attributes': PDSParameter,
1384 'extended_network_address': ExtendedNetworkAddress,
1385 'terminal_type': TerminalType,
1386 }
1387
1388
1389class ExtensionAttributes(SequenceOf):
1390 _child_spec = ExtensionAttribute
1391
1392
1393class ORAddress(Sequence):
1394 _fields = [
1395 ('built_in_standard_attributes', BuiltInStandardAttributes),
1396 ('built_in_domain_defined_attributes', BuiltInDomainDefinedAttributes, {'optional': True}),
1397 ('extension_attributes', ExtensionAttributes, {'optional': True}),
1398 ]
1399
1400
1401class EDIPartyName(Sequence):
1402 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001403 ('name_assigner', DirectoryString, {'implicit': 0, 'optional': True}),
1404 ('party_name', DirectoryString, {'implicit': 1}),
wbonde91513e2015-06-03 14:52:18 -04001405 ]
1406
1407
1408class GeneralName(Choice):
1409 _alternatives = [
wbondd62ed9a2017-09-15 07:13:52 -04001410 ('other_name', AnotherName, {'implicit': 0}),
1411 ('rfc822_name', EmailAddress, {'implicit': 1}),
1412 ('dns_name', DNSName, {'implicit': 2}),
1413 ('x400_address', ORAddress, {'implicit': 3}),
1414 ('directory_name', Name, {'explicit': 4}),
1415 ('edi_party_name', EDIPartyName, {'implicit': 5}),
1416 ('uniform_resource_identifier', URI, {'implicit': 6}),
1417 ('ip_address', IPAddress, {'implicit': 7}),
1418 ('registered_id', ObjectIdentifier, {'implicit': 8}),
wbonde91513e2015-06-03 14:52:18 -04001419 ]
1420
wbond15da2ac2015-07-27 10:23:40 -04001421 def __ne__(self, other):
1422 return not self == other
1423
wbondfd65d602015-07-23 07:16:44 -04001424 def __eq__(self, other):
1425 """
1426 Does not support other_name, x400_address or edi_party_name
1427
1428 :param other:
1429 The other GeneralName to compare to
1430
1431 :return:
1432 A boolean
1433 """
1434
1435 if self.name in ('other_name', 'x400_address', 'edi_party_name'):
wbonda26664f2015-10-07 11:57:35 -04001436 raise ValueError(unwrap(
1437 '''
1438 Comparison is not supported for GeneralName objects of
1439 choice %s
1440 ''',
1441 self.name
1442 ))
wbondfd65d602015-07-23 07:16:44 -04001443
1444 if other.name in ('other_name', 'x400_address', 'edi_party_name'):
wbonda26664f2015-10-07 11:57:35 -04001445 raise ValueError(unwrap(
1446 '''
1447 Comparison is not supported for GeneralName objects of choice
1448 %s''',
1449 other.name
1450 ))
wbondfd65d602015-07-23 07:16:44 -04001451
1452 if self.name != other.name:
1453 return False
1454
wbond35701c92015-08-07 13:45:21 -04001455 return self.chosen == other.chosen
wbondfd65d602015-07-23 07:16:44 -04001456
wbonde91513e2015-06-03 14:52:18 -04001457
1458class GeneralNames(SequenceOf):
1459 _child_spec = GeneralName
1460
1461
1462class Time(Choice):
1463 _alternatives = [
1464 ('utc_time', UTCTime),
1465 ('general_time', GeneralizedTime),
1466 ]
1467
1468
1469class Validity(Sequence):
1470 _fields = [
1471 ('not_before', Time),
1472 ('not_after', Time),
1473 ]
1474
1475
1476class BasicConstraints(Sequence):
1477 _fields = [
1478 ('ca', Boolean, {'default': False}),
1479 ('path_len_constraint', Integer, {'optional': True}),
1480 ]
1481
1482
1483class AuthorityKeyIdentifier(Sequence):
1484 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001485 ('key_identifier', OctetString, {'implicit': 0, 'optional': True}),
1486 ('authority_cert_issuer', GeneralNames, {'implicit': 1, 'optional': True}),
1487 ('authority_cert_serial_number', Integer, {'implicit': 2, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001488 ]
1489
1490
1491class DistributionPointName(Choice):
1492 _alternatives = [
wbondd62ed9a2017-09-15 07:13:52 -04001493 ('full_name', GeneralNames, {'implicit': 0}),
1494 ('name_relative_to_crl_issuer', RelativeDistinguishedName, {'implicit': 1}),
wbonde91513e2015-06-03 14:52:18 -04001495 ]
1496
1497
1498class ReasonFlags(BitString):
1499 _map = {
1500 0: 'unused',
1501 1: 'key_compromise',
1502 2: 'ca_compromise',
1503 3: 'affiliation_changed',
1504 4: 'superseded',
1505 5: 'cessation_of_operation',
1506 6: 'certificate_hold',
1507 7: 'privilege_withdrawn',
1508 8: 'aa_compromise',
1509 }
1510
1511
1512class GeneralSubtree(Sequence):
1513 _fields = [
1514 ('base', GeneralName),
wbondd62ed9a2017-09-15 07:13:52 -04001515 ('minimum', Integer, {'implicit': 0, 'default': 0}),
1516 ('maximum', Integer, {'implicit': 1, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001517 ]
1518
1519
1520class GeneralSubtrees(SequenceOf):
1521 _child_spec = GeneralSubtree
1522
1523
1524class NameConstraints(Sequence):
1525 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001526 ('permitted_subtrees', GeneralSubtrees, {'implicit': 0, 'optional': True}),
1527 ('excluded_subtrees', GeneralSubtrees, {'implicit': 1, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001528 ]
1529
1530
1531class DistributionPoint(Sequence):
1532 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001533 ('distribution_point', DistributionPointName, {'explicit': 0, 'optional': True}),
1534 ('reasons', ReasonFlags, {'implicit': 1, 'optional': True}),
1535 ('crl_issuer', GeneralNames, {'implicit': 2, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001536 ]
1537
wbond3b22a4c2015-07-21 15:27:40 -04001538 _url = False
1539
1540 @property
1541 def url(self):
1542 """
1543 :return:
1544 None or a unicode string of the distribution point's URL
1545 """
1546
1547 if self._url is False:
1548 self._url = None
1549 name = self['distribution_point']
1550 if name.name != 'full_name':
wbonda26664f2015-10-07 11:57:35 -04001551 raise ValueError(unwrap(
1552 '''
1553 CRL distribution points that are relative to the issuer are
1554 not supported
1555 '''
1556 ))
wbond3b22a4c2015-07-21 15:27:40 -04001557
1558 for general_name in name.chosen:
1559 if general_name.name == 'uniform_resource_identifier':
1560 url = general_name.native
Christian Heimes48bcd642017-09-19 18:45:33 +02001561 if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')):
wbond3b22a4c2015-07-21 15:27:40 -04001562 self._url = url
1563 break
1564
1565 return self._url
1566
wbonde91513e2015-06-03 14:52:18 -04001567
1568class CRLDistributionPoints(SequenceOf):
1569 _child_spec = DistributionPoint
1570
1571
1572class DisplayText(Choice):
1573 _alternatives = [
1574 ('ia5_string', IA5String),
1575 ('visible_string', VisibleString),
1576 ('bmp_string', BMPString),
1577 ('utf8_string', UTF8String),
1578 ]
1579
1580
1581class NoticeNumbers(SequenceOf):
1582 _child_spec = Integer
1583
1584
1585class NoticeReference(Sequence):
1586 _fields = [
1587 ('organization', DisplayText),
1588 ('notice_numbers', NoticeNumbers),
1589 ]
1590
1591
1592class UserNotice(Sequence):
1593 _fields = [
1594 ('notice_ref', NoticeReference, {'optional': True}),
1595 ('explicit_text', DisplayText, {'optional': True}),
1596 ]
1597
1598
1599class PolicyQualifierId(ObjectIdentifier):
1600 _map = {
1601 '1.3.6.1.5.5.7.2.1': 'certification_practice_statement',
1602 '1.3.6.1.5.5.7.2.2': 'user_notice',
1603 }
1604
1605
1606class PolicyQualifierInfo(Sequence):
1607 _fields = [
1608 ('policy_qualifier_id', PolicyQualifierId),
1609 ('qualifier', Any),
1610 ]
1611
1612 _oid_pair = ('policy_qualifier_id', 'qualifier')
1613 _oid_specs = {
1614 'certification_practice_statement': IA5String,
1615 'user_notice': UserNotice,
1616 }
1617
1618
1619class PolicyQualifierInfos(SequenceOf):
1620 _child_spec = PolicyQualifierInfo
1621
1622
wbond8bb77d02015-07-13 17:44:29 -04001623class PolicyIdentifier(ObjectIdentifier):
1624 _map = {
1625 '2.5.29.32.0': 'any_policy',
1626 }
1627
1628
wbonde91513e2015-06-03 14:52:18 -04001629class PolicyInformation(Sequence):
1630 _fields = [
wbond8bb77d02015-07-13 17:44:29 -04001631 ('policy_identifier', PolicyIdentifier),
wbonde91513e2015-06-03 14:52:18 -04001632 ('policy_qualifiers', PolicyQualifierInfos, {'optional': True})
1633 ]
1634
1635
1636class CertificatePolicies(SequenceOf):
1637 _child_spec = PolicyInformation
1638
1639
1640class PolicyMapping(Sequence):
1641 _fields = [
wbond20ec1192015-12-04 10:31:49 -05001642 ('issuer_domain_policy', PolicyIdentifier),
1643 ('subject_domain_policy', PolicyIdentifier),
wbonde91513e2015-06-03 14:52:18 -04001644 ]
1645
1646
1647class PolicyMappings(SequenceOf):
1648 _child_spec = PolicyMapping
1649
1650
1651class PolicyConstraints(Sequence):
1652 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04001653 ('require_explicit_policy', Integer, {'implicit': 0, 'optional': True}),
1654 ('inhibit_policy_mapping', Integer, {'implicit': 1, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04001655 ]
1656
1657
1658class KeyPurposeId(ObjectIdentifier):
1659 _map = {
wbondd17a77d2016-03-18 12:26:14 -04001660 # https://tools.ietf.org/html/rfc5280#page-45
wbondadcdebc2015-07-27 15:58:10 -04001661 '2.5.29.37.0': 'any_extended_key_usage',
wbonde91513e2015-06-03 14:52:18 -04001662 '1.3.6.1.5.5.7.3.1': 'server_auth',
1663 '1.3.6.1.5.5.7.3.2': 'client_auth',
1664 '1.3.6.1.5.5.7.3.3': 'code_signing',
1665 '1.3.6.1.5.5.7.3.4': 'email_protection',
1666 '1.3.6.1.5.5.7.3.5': 'ipsec_end_system',
1667 '1.3.6.1.5.5.7.3.6': 'ipsec_tunnel',
1668 '1.3.6.1.5.5.7.3.7': 'ipsec_user',
1669 '1.3.6.1.5.5.7.3.8': 'time_stamping',
1670 '1.3.6.1.5.5.7.3.9': 'ocsp_signing',
wbondd17a77d2016-03-18 12:26:14 -04001671 # http://tools.ietf.org/html/rfc3029.html#page-9
1672 '1.3.6.1.5.5.7.3.10': 'dvcs',
1673 # http://tools.ietf.org/html/rfc6268.html#page-16
1674 '1.3.6.1.5.5.7.3.13': 'eap_over_ppp',
1675 '1.3.6.1.5.5.7.3.14': 'eap_over_lan',
1676 # https://tools.ietf.org/html/rfc5055#page-76
1677 '1.3.6.1.5.5.7.3.15': 'scvp_server',
1678 '1.3.6.1.5.5.7.3.16': 'scvp_client',
1679 # https://tools.ietf.org/html/rfc4945#page-31
1680 '1.3.6.1.5.5.7.3.17': 'ipsec_ike',
1681 # https://tools.ietf.org/html/rfc5415#page-38
1682 '1.3.6.1.5.5.7.3.18': 'capwap_ac',
1683 '1.3.6.1.5.5.7.3.19': 'capwap_wtp',
1684 # https://tools.ietf.org/html/rfc5924#page-8
1685 '1.3.6.1.5.5.7.3.20': 'sip_domain',
1686 # https://tools.ietf.org/html/rfc6187#page-7
1687 '1.3.6.1.5.5.7.3.21': 'secure_shell_client',
1688 '1.3.6.1.5.5.7.3.22': 'secure_shell_server',
1689 # https://tools.ietf.org/html/rfc6494#page-7
1690 '1.3.6.1.5.5.7.3.23': 'send_router',
1691 '1.3.6.1.5.5.7.3.24': 'send_proxied_router',
1692 '1.3.6.1.5.5.7.3.25': 'send_owner',
1693 '1.3.6.1.5.5.7.3.26': 'send_proxied_owner',
1694 # https://tools.ietf.org/html/rfc6402#page-10
1695 '1.3.6.1.5.5.7.3.27': 'cmc_ca',
1696 '1.3.6.1.5.5.7.3.28': 'cmc_ra',
1697 '1.3.6.1.5.5.7.3.29': 'cmc_archive',
1698 # https://tools.ietf.org/html/draft-ietf-sidr-bgpsec-pki-profiles-15#page-6
1699 '1.3.6.1.5.5.7.3.30': 'bgpspec_router',
Lauri Võsandi8e6defb2018-04-26 09:18:12 +03001700 # https://www.ietf.org/proceedings/44/I-D/draft-ietf-ipsec-pki-req-01.txt
1701 '1.3.6.1.5.5.8.2.2': 'ike_intermediate',
wbondd17a77d2016-03-18 12:26:14 -04001702 # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378132(v=vs.85).aspx
1703 # and https://support.microsoft.com/en-us/kb/287547
1704 '1.3.6.1.4.1.311.10.3.1': 'microsoft_trust_list_signing',
1705 '1.3.6.1.4.1.311.10.3.2': 'microsoft_time_stamp_signing',
1706 '1.3.6.1.4.1.311.10.3.3': 'microsoft_server_gated',
1707 '1.3.6.1.4.1.311.10.3.3.1': 'microsoft_serialized',
1708 '1.3.6.1.4.1.311.10.3.4': 'microsoft_efs',
1709 '1.3.6.1.4.1.311.10.3.4.1': 'microsoft_efs_recovery',
1710 '1.3.6.1.4.1.311.10.3.5': 'microsoft_whql',
1711 '1.3.6.1.4.1.311.10.3.6': 'microsoft_nt5',
1712 '1.3.6.1.4.1.311.10.3.7': 'microsoft_oem_whql',
1713 '1.3.6.1.4.1.311.10.3.8': 'microsoft_embedded_nt',
1714 '1.3.6.1.4.1.311.10.3.9': 'microsoft_root_list_signer',
1715 '1.3.6.1.4.1.311.10.3.10': 'microsoft_qualified_subordination',
1716 '1.3.6.1.4.1.311.10.3.11': 'microsoft_key_recovery',
1717 '1.3.6.1.4.1.311.10.3.12': 'microsoft_document_signing',
1718 '1.3.6.1.4.1.311.10.3.13': 'microsoft_lifetime_signing',
1719 '1.3.6.1.4.1.311.10.3.14': 'microsoft_mobile_device_software',
Matt Cooperdc9d09c2017-11-17 19:29:45 -05001720 # https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography
1721 '1.3.6.1.4.1.311.20.2.2': 'microsoft_smart_card_logon',
wbondace45a12016-06-16 05:32:48 -04001722 # https://opensource.apple.com/source
1723 # - /Security/Security-57031.40.6/Security/libsecurity_keychain/lib/SecPolicy.cpp
1724 # - /libsecurity_cssm/libsecurity_cssm-36064/lib/oidsalg.c
1725 '1.2.840.113635.100.1.2': 'apple_x509_basic',
1726 '1.2.840.113635.100.1.3': 'apple_ssl',
1727 '1.2.840.113635.100.1.4': 'apple_local_cert_gen',
1728 '1.2.840.113635.100.1.5': 'apple_csr_gen',
1729 '1.2.840.113635.100.1.6': 'apple_revocation_crl',
1730 '1.2.840.113635.100.1.7': 'apple_revocation_ocsp',
1731 '1.2.840.113635.100.1.8': 'apple_smime',
1732 '1.2.840.113635.100.1.9': 'apple_eap',
1733 '1.2.840.113635.100.1.10': 'apple_software_update_signing',
1734 '1.2.840.113635.100.1.11': 'apple_ipsec',
1735 '1.2.840.113635.100.1.12': 'apple_ichat',
1736 '1.2.840.113635.100.1.13': 'apple_resource_signing',
1737 '1.2.840.113635.100.1.14': 'apple_pkinit_client',
1738 '1.2.840.113635.100.1.15': 'apple_pkinit_server',
1739 '1.2.840.113635.100.1.16': 'apple_code_signing',
1740 '1.2.840.113635.100.1.17': 'apple_package_signing',
1741 '1.2.840.113635.100.1.18': 'apple_id_validation',
1742 '1.2.840.113635.100.1.20': 'apple_time_stamping',
1743 '1.2.840.113635.100.1.21': 'apple_revocation',
1744 '1.2.840.113635.100.1.22': 'apple_passbook_signing',
1745 '1.2.840.113635.100.1.23': 'apple_mobile_store',
1746 '1.2.840.113635.100.1.24': 'apple_escrow_service',
1747 '1.2.840.113635.100.1.25': 'apple_profile_signer',
1748 '1.2.840.113635.100.1.26': 'apple_qa_profile_signer',
1749 '1.2.840.113635.100.1.27': 'apple_test_mobile_store',
1750 '1.2.840.113635.100.1.28': 'apple_otapki_signer',
1751 '1.2.840.113635.100.1.29': 'apple_test_otapki_signer',
1752 '1.2.840.113625.100.1.30': 'apple_id_validation_record_signing_policy',
1753 '1.2.840.113625.100.1.31': 'apple_smp_encryption',
1754 '1.2.840.113625.100.1.32': 'apple_test_smp_encryption',
1755 '1.2.840.113635.100.1.33': 'apple_server_authentication',
1756 '1.2.840.113635.100.1.34': 'apple_pcs_escrow_service',
Matt Cooperdc9d09c2017-11-17 19:29:45 -05001757 # http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.201-2.pdf
1758 '2.16.840.1.101.3.6.8': 'piv_card_authentication',
1759 '2.16.840.1.101.3.6.7': 'piv_content_signing',
1760 # https://tools.ietf.org/html/rfc4556.html
1761 '1.3.6.1.5.2.3.4': 'pkinit_kpclientauth',
Matt Cooper34f87fa2017-11-17 19:46:32 -05001762 '1.3.6.1.5.2.3.5': 'pkinit_kpkdc',
Matt Cooperdc9d09c2017-11-17 19:29:45 -05001763 # https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSig/changes.html
Matt Cooper34f87fa2017-11-17 19:46:32 -05001764 '1.2.840.113583.1.1.5': 'adobe_authentic_documents_trust',
wbond7f587d52017-11-22 11:13:45 -05001765 # https://www.idmanagement.gov/wp-content/uploads/sites/1171/uploads/fpki-pivi-cert-profiles.pdf
1766 '2.16.840.1.101.3.8.7': 'fpki_pivi_content_signing'
wbonde91513e2015-06-03 14:52:18 -04001767 }
1768
1769
1770class ExtKeyUsageSyntax(SequenceOf):
1771 _child_spec = KeyPurposeId
1772
1773
wbonda0d45482015-07-13 22:10:20 -04001774class AccessMethod(ObjectIdentifier):
1775 _map = {
1776 '1.3.6.1.5.5.7.48.1': 'ocsp',
1777 '1.3.6.1.5.5.7.48.2': 'ca_issuers',
wbond35f81352015-07-20 08:56:03 -04001778 '1.3.6.1.5.5.7.48.3': 'time_stamping',
1779 '1.3.6.1.5.5.7.48.5': 'ca_repository',
wbonda0d45482015-07-13 22:10:20 -04001780 }
1781
1782
1783class AccessDescription(Sequence):
1784 _fields = [
1785 ('access_method', AccessMethod),
1786 ('access_location', GeneralName),
1787 ]
1788
1789
1790class AuthorityInfoAccessSyntax(SequenceOf):
1791 _child_spec = AccessDescription
1792
1793
wbond35f81352015-07-20 08:56:03 -04001794class SubjectInfoAccessSyntax(SequenceOf):
1795 _child_spec = AccessDescription
1796
1797
wbondea278182015-11-23 13:55:29 -05001798# https://tools.ietf.org/html/rfc7633
1799class Features(SequenceOf):
1800 _child_spec = Integer
1801
1802
wbonde91513e2015-06-03 14:52:18 -04001803class EntrustVersionInfo(Sequence):
1804 _fields = [
1805 ('entrust_vers', GeneralString),
1806 ('entrust_info_flags', BitString)
1807 ]
1808
1809
1810class NetscapeCertificateType(BitString):
1811 _map = {
1812 0: 'ssl_client',
1813 1: 'ssl_server',
1814 2: 'email',
1815 3: 'object_signing',
1816 4: 'reserved',
1817 5: 'ssl_ca',
1818 6: 'email_ca',
1819 7: 'object_signing_ca',
1820 }
1821
1822
wbonda11c4502017-11-22 11:13:00 -05001823class Version(Integer):
1824 _map = {
1825 0: 'v1',
1826 1: 'v2',
1827 2: 'v3',
1828 }
1829
1830
1831class TPMSpecification(Sequence):
1832 _fields = [
1833 ('family', UTF8String),
1834 ('level', Integer),
1835 ('revision', Integer),
1836 ]
1837
1838
1839class SetOfTPMSpecification(SetOf):
1840 _child_spec = TPMSpecification
1841
1842
1843class TCGSpecificationVersion(Sequence):
1844 _fields = [
1845 ('major_version', Integer),
1846 ('minor_version', Integer),
1847 ('revision', Integer),
1848 ]
1849
1850
1851class TCGPlatformSpecification(Sequence):
1852 _fields = [
1853 ('version', TCGSpecificationVersion),
1854 ('platform_class', OctetString),
1855 ]
1856
1857
1858class SetOfTCGPlatformSpecification(SetOf):
1859 _child_spec = TCGPlatformSpecification
1860
1861
1862class EKGenerationType(Enumerated):
1863 _map = {
1864 0: 'internal',
1865 1: 'injected',
1866 2: 'internal_revocable',
1867 3: 'injected_revocable',
1868 }
1869
1870
1871class EKGenerationLocation(Enumerated):
1872 _map = {
1873 0: 'tpm_manufacturer',
1874 1: 'platform_manufacturer',
1875 2: 'ek_cert_signer',
1876 }
1877
1878
1879class EKCertificateGenerationLocation(Enumerated):
1880 _map = {
1881 0: 'tpm_manufacturer',
1882 1: 'platform_manufacturer',
1883 2: 'ek_cert_signer',
1884 }
1885
1886
1887class EvaluationAssuranceLevel(Enumerated):
1888 _map = {
1889 1: 'level1',
1890 2: 'level2',
1891 3: 'level3',
1892 4: 'level4',
1893 5: 'level5',
1894 6: 'level6',
1895 7: 'level7',
1896 }
1897
1898
1899class EvaluationStatus(Enumerated):
1900 _map = {
1901 0: 'designed_to_meet',
1902 1: 'evaluation_in_progress',
1903 2: 'evaluation_completed',
1904 }
1905
1906
1907class StrengthOfFunction(Enumerated):
1908 _map = {
1909 0: 'basic',
1910 1: 'medium',
1911 2: 'high',
1912 }
1913
1914
1915class URIReference(Sequence):
1916 _fields = [
1917 ('uniform_resource_identifier', IA5String),
1918 ('hash_algorithm', DigestAlgorithm, {'optional': True}),
1919 ('hash_value', BitString, {'optional': True}),
1920 ]
1921
1922
1923class CommonCriteriaMeasures(Sequence):
1924 _fields = [
1925 ('version', IA5String),
1926 ('assurance_level', EvaluationAssuranceLevel),
1927 ('evaluation_status', EvaluationStatus),
1928 ('plus', Boolean, {'default': False}),
1929 ('strengh_of_function', StrengthOfFunction, {'implicit': 0, 'optional': True}),
1930 ('profile_oid', ObjectIdentifier, {'implicit': 1, 'optional': True}),
1931 ('profile_url', URIReference, {'implicit': 2, 'optional': True}),
1932 ('target_oid', ObjectIdentifier, {'implicit': 3, 'optional': True}),
1933 ('target_uri', URIReference, {'implicit': 4, 'optional': True}),
1934 ]
1935
1936
1937class SecurityLevel(Enumerated):
1938 _map = {
1939 1: 'level1',
1940 2: 'level2',
1941 3: 'level3',
1942 4: 'level4',
1943 }
1944
1945
1946class FIPSLevel(Sequence):
1947 _fields = [
1948 ('version', IA5String),
1949 ('level', SecurityLevel),
1950 ('plus', Boolean, {'default': False}),
1951 ]
1952
1953
1954class TPMSecurityAssertions(Sequence):
1955 _fields = [
1956 ('version', Version, {'default': 'v1'}),
1957 ('field_upgradable', Boolean, {'default': False}),
1958 ('ek_generation_type', EKGenerationType, {'implicit': 0, 'optional': True}),
1959 ('ek_generation_location', EKGenerationLocation, {'implicit': 1, 'optional': True}),
1960 ('ek_certificate_generation_location', EKCertificateGenerationLocation, {'implicit': 2, 'optional': True}),
1961 ('cc_info', CommonCriteriaMeasures, {'implicit': 3, 'optional': True}),
1962 ('fips_level', FIPSLevel, {'implicit': 4, 'optional': True}),
1963 ('iso_9000_certified', Boolean, {'implicit': 5, 'default': False}),
1964 ('iso_9000_uri', IA5String, {'optional': True}),
1965 ]
1966
1967
1968class SetOfTPMSecurityAssertions(SetOf):
1969 _child_spec = TPMSecurityAssertions
1970
1971
1972class SubjectDirectoryAttributeId(ObjectIdentifier):
1973 _map = {
1974 # https://tools.ietf.org/html/rfc2256#page-11
1975 '2.5.4.52': 'supported_algorithms',
1976 # https://www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf
1977 '2.23.133.2.16': 'tpm_specification',
1978 '2.23.133.2.17': 'tcg_platform_specification',
1979 '2.23.133.2.18': 'tpm_security_assertions',
1980 # https://tools.ietf.org/html/rfc3739#page-18
1981 '1.3.6.1.5.5.7.9.1': 'pda_date_of_birth',
1982 '1.3.6.1.5.5.7.9.2': 'pda_place_of_birth',
1983 '1.3.6.1.5.5.7.9.3': 'pda_gender',
1984 '1.3.6.1.5.5.7.9.4': 'pda_country_of_citizenship',
1985 '1.3.6.1.5.5.7.9.5': 'pda_country_of_residence',
1986 # https://holtstrom.com/michael/tools/asn1decoder.php
1987 '1.2.840.113533.7.68.29': 'entrust_user_role',
1988 }
1989
1990
1991class SetOfGeneralizedTime(SetOf):
1992 _child_spec = GeneralizedTime
1993
1994
1995class SetOfDirectoryString(SetOf):
1996 _child_spec = DirectoryString
1997
1998
1999class SetOfPrintableString(SetOf):
2000 _child_spec = PrintableString
2001
2002
2003class SupportedAlgorithm(Sequence):
2004 _fields = [
2005 ('algorithm_identifier', AnyAlgorithmIdentifier),
2006 ('intended_usage', KeyUsage, {'explicit': 0, 'optional': True}),
2007 ('intended_certificate_policies', CertificatePolicies, {'explicit': 1, 'optional': True}),
2008 ]
2009
2010
2011class SetOfSupportedAlgorithm(SetOf):
2012 _child_spec = SupportedAlgorithm
2013
2014
2015class SubjectDirectoryAttribute(Sequence):
2016 _fields = [
2017 ('type', SubjectDirectoryAttributeId),
2018 ('values', Any),
2019 ]
2020
2021 _oid_pair = ('type', 'values')
2022 _oid_specs = {
2023 'supported_algorithms': SetOfSupportedAlgorithm,
2024 'tpm_specification': SetOfTPMSpecification,
2025 'tcg_platform_specification': SetOfTCGPlatformSpecification,
2026 'tpm_security_assertions': SetOfTPMSecurityAssertions,
2027 'pda_date_of_birth': SetOfGeneralizedTime,
2028 'pda_place_of_birth': SetOfDirectoryString,
2029 'pda_gender': SetOfPrintableString,
2030 'pda_country_of_citizenship': SetOfPrintableString,
2031 'pda_country_of_residence': SetOfPrintableString,
2032 }
2033
2034 def _values_spec(self):
2035 type_ = self['type'].native
2036 if type_ in self._oid_specs:
2037 return self._oid_specs[type_]
2038 return SetOf
2039
2040 _spec_callbacks = {
2041 'values': _values_spec
2042 }
2043
2044
2045class SubjectDirectoryAttributes(SequenceOf):
2046 _child_spec = SubjectDirectoryAttribute
2047
2048
wbonde91513e2015-06-03 14:52:18 -04002049class ExtensionId(ObjectIdentifier):
2050 _map = {
2051 '2.5.29.9': 'subject_directory_attributes',
2052 '2.5.29.14': 'key_identifier',
2053 '2.5.29.15': 'key_usage',
2054 '2.5.29.16': 'private_key_usage_period',
2055 '2.5.29.17': 'subject_alt_name',
wbondada58e72015-07-20 08:57:00 -04002056 '2.5.29.18': 'issuer_alt_name',
wbonde91513e2015-06-03 14:52:18 -04002057 '2.5.29.19': 'basic_constraints',
wbonde91513e2015-06-03 14:52:18 -04002058 '2.5.29.30': 'name_constraints',
2059 '2.5.29.31': 'crl_distribution_points',
2060 '2.5.29.32': 'certificate_policies',
2061 '2.5.29.33': 'policy_mappings',
2062 '2.5.29.35': 'authority_key_identifier',
2063 '2.5.29.36': 'policy_constraints',
2064 '2.5.29.37': 'extended_key_usage',
wbondfb88b8c2015-07-20 08:58:16 -04002065 '2.5.29.46': 'freshest_crl',
wbonda43b3fb2015-07-13 17:52:25 -04002066 '2.5.29.54': 'inhibit_any_policy',
wbonda0d45482015-07-13 22:10:20 -04002067 '1.3.6.1.5.5.7.1.1': 'authority_information_access',
wbond35f81352015-07-20 08:56:03 -04002068 '1.3.6.1.5.5.7.1.11': 'subject_information_access',
wbondea278182015-11-23 13:55:29 -05002069 # https://tools.ietf.org/html/rfc7633
2070 '1.3.6.1.5.5.7.1.24': 'tls_feature',
wbonde91513e2015-06-03 14:52:18 -04002071 '1.3.6.1.5.5.7.48.1.5': 'ocsp_no_check',
2072 '1.2.840.113533.7.65.0': 'entrust_version_extension',
2073 '2.16.840.1.113730.1.1': 'netscape_certificate_type',
wbond9e74abf2017-06-13 20:34:22 -04002074 # https://tools.ietf.org/html/rfc6962.html#page-14
2075 '1.3.6.1.4.1.11129.2.4.2': 'signed_certificate_timestamp_list',
wbonde91513e2015-06-03 14:52:18 -04002076 }
2077
2078
2079class Extension(Sequence):
2080 _fields = [
2081 ('extn_id', ExtensionId),
2082 ('critical', Boolean, {'default': False}),
wbonde5a1c6e2015-08-03 07:42:28 -04002083 ('extn_value', ParsableOctetString),
wbonde91513e2015-06-03 14:52:18 -04002084 ]
2085
2086 _oid_pair = ('extn_id', 'extn_value')
2087 _oid_specs = {
wbonda11c4502017-11-22 11:13:00 -05002088 'subject_directory_attributes': SubjectDirectoryAttributes,
wbonde91513e2015-06-03 14:52:18 -04002089 'key_identifier': OctetString,
2090 'key_usage': KeyUsage,
2091 'private_key_usage_period': PrivateKeyUsagePeriod,
2092 'subject_alt_name': GeneralNames,
wbondada58e72015-07-20 08:57:00 -04002093 'issuer_alt_name': GeneralNames,
wbonde91513e2015-06-03 14:52:18 -04002094 'basic_constraints': BasicConstraints,
wbonde91513e2015-06-03 14:52:18 -04002095 'name_constraints': NameConstraints,
2096 'crl_distribution_points': CRLDistributionPoints,
2097 'certificate_policies': CertificatePolicies,
2098 'policy_mappings': PolicyMappings,
2099 'authority_key_identifier': AuthorityKeyIdentifier,
2100 'policy_constraints': PolicyConstraints,
2101 'extended_key_usage': ExtKeyUsageSyntax,
wbondfb88b8c2015-07-20 08:58:16 -04002102 'freshest_crl': CRLDistributionPoints,
wbonda43b3fb2015-07-13 17:52:25 -04002103 'inhibit_any_policy': Integer,
wbonda0d45482015-07-13 22:10:20 -04002104 'authority_information_access': AuthorityInfoAccessSyntax,
wbond35f81352015-07-20 08:56:03 -04002105 'subject_information_access': SubjectInfoAccessSyntax,
wbondea278182015-11-23 13:55:29 -05002106 'tls_feature': Features,
wbonde91513e2015-06-03 14:52:18 -04002107 'ocsp_no_check': Null,
2108 'entrust_version_extension': EntrustVersionInfo,
2109 'netscape_certificate_type': NetscapeCertificateType,
wbond9e74abf2017-06-13 20:34:22 -04002110 'signed_certificate_timestamp_list': OctetString,
wbonde91513e2015-06-03 14:52:18 -04002111 }
2112
2113
2114class Extensions(SequenceOf):
2115 _child_spec = Extension
2116
2117
wbonde91513e2015-06-03 14:52:18 -04002118class TbsCertificate(Sequence):
2119 _fields = [
wbondd62ed9a2017-09-15 07:13:52 -04002120 ('version', Version, {'explicit': 0, 'default': 'v1'}),
wbonde91513e2015-06-03 14:52:18 -04002121 ('serial_number', Integer),
2122 ('signature', SignedDigestAlgorithm),
2123 ('issuer', Name),
2124 ('validity', Validity),
2125 ('subject', Name),
2126 ('subject_public_key_info', PublicKeyInfo),
wbondd62ed9a2017-09-15 07:13:52 -04002127 ('issuer_unique_id', OctetBitString, {'implicit': 1, 'optional': True}),
2128 ('subject_unique_id', OctetBitString, {'implicit': 2, 'optional': True}),
2129 ('extensions', Extensions, {'explicit': 3, 'optional': True}),
wbonde91513e2015-06-03 14:52:18 -04002130 ]
2131
2132
2133class Certificate(Sequence):
2134 _fields = [
2135 ('tbs_certificate', TbsCertificate),
2136 ('signature_algorithm', SignedDigestAlgorithm),
2137 ('signature_value', OctetBitString),
2138 ]
wbond8bb77d02015-07-13 17:44:29 -04002139
2140 _processed_extensions = False
2141 _critical_extensions = None
wbond433adbb2015-07-20 08:59:57 -04002142 _subject_directory_attributes = None
wbond8bb77d02015-07-13 17:44:29 -04002143 _key_identifier_value = None
2144 _key_usage_value = None
2145 _subject_alt_name_value = None
wbondada58e72015-07-20 08:57:00 -04002146 _issuer_alt_name_value = None
wbond8bb77d02015-07-13 17:44:29 -04002147 _basic_constraints_value = None
2148 _name_constraints_value = None
2149 _crl_distribution_points_value = None
2150 _certificate_policies_value = None
2151 _policy_mappings_value = None
2152 _authority_key_identifier_value = None
2153 _policy_constraints_value = None
wbondfb88b8c2015-07-20 08:58:16 -04002154 _freshest_crl_value = None
wbond433adbb2015-07-20 08:59:57 -04002155 _inhibit_any_policy_value = None
wbond8bb77d02015-07-13 17:44:29 -04002156 _extended_key_usage_value = None
wbond08c60fa2015-07-13 23:02:13 -04002157 _authority_information_access_value = None
wbond35f81352015-07-20 08:56:03 -04002158 _subject_information_access_value = None
Matt Cooper8300e4d2017-12-05 15:19:46 -05002159 _private_key_usage_period_value = None
wbondea278182015-11-23 13:55:29 -05002160 _tls_feature_value = None
wbond8bb77d02015-07-13 17:44:29 -04002161 _ocsp_no_check_value = None
wbondaf1f5a82015-07-17 12:13:15 -04002162 _issuer_serial = None
2163 _authority_issuer_serial = False
wbond6888bc62015-07-21 15:05:59 -04002164 _crl_distribution_points = None
2165 _delta_crl_distribution_points = None
wbondaf1f5a82015-07-17 12:13:15 -04002166 _valid_domains = None
2167 _valid_ips = None
wbond9a7a0992015-07-23 09:59:06 -04002168 _self_issued = None
2169 _self_signed = None
wbond5314f4b2015-09-01 13:25:47 -04002170 _sha1 = None
2171 _sha256 = None
wbond8bb77d02015-07-13 17:44:29 -04002172
2173 def _set_extensions(self):
2174 """
2175 Sets common named extensions to private attributes and creates a list
2176 of critical extensions
2177 """
2178
wbond2fde6452015-07-23 10:54:13 -04002179 self._critical_extensions = set()
wbond8bb77d02015-07-13 17:44:29 -04002180
2181 for extension in self['tbs_certificate']['extensions']:
2182 name = extension['extn_id'].native
2183 attribute_name = '_%s_value' % name
2184 if hasattr(self, attribute_name):
2185 setattr(self, attribute_name, extension['extn_value'].parsed)
2186 if extension['critical'].native:
wbond2fde6452015-07-23 10:54:13 -04002187 self._critical_extensions.add(name)
wbond8bb77d02015-07-13 17:44:29 -04002188
2189 self._processed_extensions = True
2190
2191 @property
2192 def critical_extensions(self):
2193 """
wbond2fde6452015-07-23 10:54:13 -04002194 Returns a set of the names (or OID if not a known extension) of the
wbond8bb77d02015-07-13 17:44:29 -04002195 extensions marked as critical
2196
2197 :return:
wbond2fde6452015-07-23 10:54:13 -04002198 A set of unicode strings
wbond8bb77d02015-07-13 17:44:29 -04002199 """
2200
2201 if not self._processed_extensions:
2202 self._set_extensions()
2203 return self._critical_extensions
2204
2205 @property
Matt Cooper8300e4d2017-12-05 15:19:46 -05002206 def private_key_usage_period_value(self):
2207 """
2208 This extension is used to constrain the period over which the subject
2209 private key may be used
2210
2211 :return:
2212 None or a PrivateKeyUsagePeriod object
2213 """
2214
2215 if not self._processed_extensions:
2216 self._set_extensions()
2217 return self._private_key_usage_period_value
2218
2219 @property
wbond433adbb2015-07-20 08:59:57 -04002220 def subject_directory_attributes_value(self):
2221 """
2222 This extension is used to contain additional identification attributes
2223 about the subject.
2224
2225 :return:
Matt Cooper8300e4d2017-12-05 15:19:46 -05002226 None or a SubjectDirectoryAttributes object
wbond433adbb2015-07-20 08:59:57 -04002227 """
2228
2229 if not self._processed_extensions:
2230 self._set_extensions()
Matt Cooperdc9d09c2017-11-17 19:29:45 -05002231 return self._subject_directory_attributes
wbond433adbb2015-07-20 08:59:57 -04002232
2233 @property
wbond8bb77d02015-07-13 17:44:29 -04002234 def key_identifier_value(self):
2235 """
wbondea860012015-07-20 09:00:29 -04002236 This extension is used to help in creating certificate validation paths.
2237 It contains an identifier that should generally, but is not guaranteed
2238 to, be unique.
2239
wbond8bb77d02015-07-13 17:44:29 -04002240 :return:
wbondea860012015-07-20 09:00:29 -04002241 None or an OctetString object
wbond8bb77d02015-07-13 17:44:29 -04002242 """
2243
2244 if not self._processed_extensions:
2245 self._set_extensions()
2246 return self._key_identifier_value
2247
2248 @property
2249 def key_usage_value(self):
2250 """
wbondea860012015-07-20 09:00:29 -04002251 This extension is used to define the purpose of the public key
2252 contained within the certificate.
2253
wbond8bb77d02015-07-13 17:44:29 -04002254 :return:
wbondea860012015-07-20 09:00:29 -04002255 None or a KeyUsage
wbond8bb77d02015-07-13 17:44:29 -04002256 """
2257
2258 if not self._processed_extensions:
2259 self._set_extensions()
2260 return self._key_usage_value
2261
2262 @property
2263 def subject_alt_name_value(self):
2264 """
wbondea860012015-07-20 09:00:29 -04002265 This extension allows for additional names to be associate with the
2266 subject of the certificate. While it may contain a whole host of
2267 possible names, it is usually used to allow certificates to be used
2268 with multiple different domain names.
2269
wbond8bb77d02015-07-13 17:44:29 -04002270 :return:
wbondea860012015-07-20 09:00:29 -04002271 None or a GeneralNames object
wbond8bb77d02015-07-13 17:44:29 -04002272 """
2273
2274 if not self._processed_extensions:
2275 self._set_extensions()
2276 return self._subject_alt_name_value
2277
2278 @property
wbondada58e72015-07-20 08:57:00 -04002279 def issuer_alt_name_value(self):
2280 """
2281 This extension allows associating one or more alternative names with
2282 the issuer of the certificate.
2283
2284 :return:
2285 None or an x509.GeneralNames object
2286 """
2287
wbondffb857a2016-03-31 10:59:08 -04002288 if not self._processed_extensions:
2289 self._set_extensions()
wbondada58e72015-07-20 08:57:00 -04002290 return self._issuer_alt_name_value
2291
2292 @property
wbond8bb77d02015-07-13 17:44:29 -04002293 def basic_constraints_value(self):
2294 """
wbondea860012015-07-20 09:00:29 -04002295 This extension is used to determine if the subject of the certificate
2296 is a CA, and if so, what the maximum number of intermediate CA certs
2297 after this are, before an end-entity certificate is found.
2298
wbond8bb77d02015-07-13 17:44:29 -04002299 :return:
wbondea860012015-07-20 09:00:29 -04002300 None or a BasicConstraints object
wbond8bb77d02015-07-13 17:44:29 -04002301 """
2302
2303 if not self._processed_extensions:
2304 self._set_extensions()
2305 return self._basic_constraints_value
2306
2307 @property
2308 def name_constraints_value(self):
2309 """
wbondea860012015-07-20 09:00:29 -04002310 This extension is used in CA certificates, and is used to limit the
2311 possible names of certificates issued.
2312
wbond8bb77d02015-07-13 17:44:29 -04002313 :return:
wbondea860012015-07-20 09:00:29 -04002314 None or a NameConstraints object
wbond8bb77d02015-07-13 17:44:29 -04002315 """
2316
2317 if not self._processed_extensions:
2318 self._set_extensions()
2319 return self._name_constraints_value
2320
2321 @property
2322 def crl_distribution_points_value(self):
2323 """
wbondea860012015-07-20 09:00:29 -04002324 This extension is used to help in locating the CRL for this certificate.
2325
wbond8bb77d02015-07-13 17:44:29 -04002326 :return:
wbondea860012015-07-20 09:00:29 -04002327 None or a CRLDistributionPoints object
wbond8bb77d02015-07-13 17:44:29 -04002328 extension
2329 """
2330
2331 if not self._processed_extensions:
2332 self._set_extensions()
2333 return self._crl_distribution_points_value
2334
2335 @property
2336 def certificate_policies_value(self):
2337 """
wbondea860012015-07-20 09:00:29 -04002338 This extension defines policies in CA certificates under which
2339 certificates may be issued. In end-entity certificates, the inclusion
2340 of a policy indicates the issuance of the certificate follows the
2341 policy.
2342
wbond8bb77d02015-07-13 17:44:29 -04002343 :return:
wbondea860012015-07-20 09:00:29 -04002344 None or a CertificatePolicies object
wbond8bb77d02015-07-13 17:44:29 -04002345 """
2346
2347 if not self._processed_extensions:
2348 self._set_extensions()
2349 return self._certificate_policies_value
2350
2351 @property
2352 def policy_mappings_value(self):
2353 """
wbondea860012015-07-20 09:00:29 -04002354 This extension allows mapping policy OIDs to other OIDs. This is used
2355 to allow different policies to be treated as equivalent in the process
2356 of validation.
2357
wbond8bb77d02015-07-13 17:44:29 -04002358 :return:
wbondea860012015-07-20 09:00:29 -04002359 None or a PolicyMappings object
wbond8bb77d02015-07-13 17:44:29 -04002360 """
2361
2362 if not self._processed_extensions:
2363 self._set_extensions()
2364 return self._policy_mappings_value
2365
2366 @property
2367 def authority_key_identifier_value(self):
2368 """
wbondea860012015-07-20 09:00:29 -04002369 This extension helps in identifying the public key with which to
2370 validate the authenticity of the certificate.
2371
wbond8bb77d02015-07-13 17:44:29 -04002372 :return:
wbondea860012015-07-20 09:00:29 -04002373 None or an AuthorityKeyIdentifier object
wbond8bb77d02015-07-13 17:44:29 -04002374 """
2375
2376 if not self._processed_extensions:
2377 self._set_extensions()
2378 return self._authority_key_identifier_value
2379
2380 @property
2381 def policy_constraints_value(self):
2382 """
wbondea860012015-07-20 09:00:29 -04002383 This extension is used to control if policy mapping is allowed and
2384 when policies are required.
2385
wbond8bb77d02015-07-13 17:44:29 -04002386 :return:
wbondea860012015-07-20 09:00:29 -04002387 None or a PolicyConstraints object
wbond8bb77d02015-07-13 17:44:29 -04002388 """
2389
2390 if not self._processed_extensions:
2391 self._set_extensions()
2392 return self._policy_constraints_value
2393
2394 @property
wbondfb88b8c2015-07-20 08:58:16 -04002395 def freshest_crl_value(self):
2396 """
2397 This extension is used to help locate any available delta CRLs
2398
2399 :return:
2400 None or an CRLDistributionPoints object
2401 """
2402
2403 if not self._processed_extensions:
2404 self._set_extensions()
2405 return self._freshest_crl_value
2406
2407 @property
wbond433adbb2015-07-20 08:59:57 -04002408 def inhibit_any_policy_value(self):
2409 """
2410 This extension is used to prevent mapping of the any policy to
2411 specific requirements
2412
2413 :return:
2414 None or a Integer object
2415 """
2416
2417 if not self._processed_extensions:
2418 self._set_extensions()
2419 return self._inhibit_any_policy_value
2420
2421 @property
wbond8bb77d02015-07-13 17:44:29 -04002422 def extended_key_usage_value(self):
2423 """
wbondea860012015-07-20 09:00:29 -04002424 This extension is used to define additional purposes for the public key
2425 beyond what is contained in the basic constraints.
2426
wbond8bb77d02015-07-13 17:44:29 -04002427 :return:
wbondea860012015-07-20 09:00:29 -04002428 None or an ExtKeyUsageSyntax object
wbond8bb77d02015-07-13 17:44:29 -04002429 """
2430
2431 if not self._processed_extensions:
2432 self._set_extensions()
2433 return self._extended_key_usage_value
2434
2435 @property
wbond08c60fa2015-07-13 23:02:13 -04002436 def authority_information_access_value(self):
2437 """
wbondea860012015-07-20 09:00:29 -04002438 This extension is used to locate the CA certificate used to sign this
2439 certificate, or the OCSP responder for this certificate.
2440
wbond08c60fa2015-07-13 23:02:13 -04002441 :return:
wbondea860012015-07-20 09:00:29 -04002442 None or an AuthorityInfoAccessSyntax object
wbond08c60fa2015-07-13 23:02:13 -04002443 """
2444
2445 if not self._processed_extensions:
2446 self._set_extensions()
2447 return self._authority_information_access_value
2448
2449 @property
wbond35f81352015-07-20 08:56:03 -04002450 def subject_information_access_value(self):
2451 """
2452 This extension is used to access information about the subject of this
2453 certificate.
2454
2455 :return:
2456 None or a SubjectInfoAccessSyntax object
2457 """
2458
2459 if not self._processed_extensions:
2460 self._set_extensions()
2461 return self._subject_information_access_value
2462
2463 @property
wbondea278182015-11-23 13:55:29 -05002464 def tls_feature_value(self):
2465 """
2466 This extension is used to list the TLS features a server must respond
2467 with if a client initiates a request supporting them.
2468
2469 :return:
2470 None or a Features object
2471 """
2472
2473 if not self._processed_extensions:
2474 self._set_extensions()
2475 return self._tls_feature_value
2476
2477 @property
wbond8bb77d02015-07-13 17:44:29 -04002478 def ocsp_no_check_value(self):
2479 """
wbondea860012015-07-20 09:00:29 -04002480 This extension is used on certificates of OCSP responders, indicating
2481 that revocation information for the certificate should never need to
2482 be verified, thus preventing possible loops in path validation.
2483
wbond8bb77d02015-07-13 17:44:29 -04002484 :return:
wbondd0f71af2015-07-20 10:17:01 -04002485 None or a Null object (if present)
wbond8bb77d02015-07-13 17:44:29 -04002486 """
2487
2488 if not self._processed_extensions:
2489 self._set_extensions()
2490 return self._ocsp_no_check_value
wbondaf1f5a82015-07-17 12:13:15 -04002491
2492 @property
wbond06921bb2015-07-24 08:39:59 -04002493 def signature(self):
2494 """
2495 :return:
2496 A byte string of the signature
2497 """
2498
2499 return self['signature_value'].native
2500
2501 @property
2502 def signature_algo(self):
2503 """
2504 :return:
2505 A unicode string of "rsassa_pkcs1v15", "rsassa_pss", "dsa", "ecdsa"
2506 """
2507
2508 return self['signature_algorithm'].signature_algo
2509
2510 @property
2511 def hash_algo(self):
2512 """
2513 :return:
2514 A unicode string of "md2", "md5", "sha1", "sha224", "sha256",
2515 "sha384", "sha512", "sha512_224", "sha512_256"
2516 """
2517
2518 return self['signature_algorithm'].hash_algo
2519
2520 @property
wbondaf1f5a82015-07-17 12:13:15 -04002521 def public_key(self):
2522 """
2523 :return:
2524 The PublicKeyInfo object for this certificate
2525 """
2526
2527 return self['tbs_certificate']['subject_public_key_info']
2528
2529 @property
2530 def subject(self):
2531 """
2532 :return:
2533 The Name object for the subject of this certificate
2534 """
2535
2536 return self['tbs_certificate']['subject']
2537
2538 @property
2539 def issuer(self):
2540 """
2541 :return:
2542 The Name object for the issuer of this certificate
2543 """
2544
2545 return self['tbs_certificate']['issuer']
2546
2547 @property
2548 def serial_number(self):
2549 """
2550 :return:
2551 An integer of the certificate's serial number
2552 """
2553
2554 return self['tbs_certificate']['serial_number'].native
2555
2556 @property
2557 def key_identifier(self):
2558 """
2559 :return:
2560 None or a byte string of the certificate's key identifier from the
2561 key identifier extension
2562 """
2563
2564 if not self.key_identifier_value:
2565 return None
2566
2567 return self.key_identifier_value.native
2568
2569 @property
2570 def issuer_serial(self):
2571 """
2572 :return:
2573 A byte string of the SHA-256 hash of the issuer concatenated with
2574 the ascii character ":", concatenated with the serial number as
2575 an ascii string
2576 """
2577
2578 if self._issuer_serial is None:
2579 self._issuer_serial = self.issuer.sha256 + b':' + str_cls(self.serial_number).encode('ascii')
2580 return self._issuer_serial
2581
2582 @property
Sebastian Bachmann4c0d7e92018-05-02 16:31:58 +02002583 def not_valid_after(self):
2584 """
2585 :return:
2586 A datetime of latest time when the certificate is still valid
2587 """
2588 return self['tbs_certificate']['validity']['not_after'].native
2589
2590 @property
2591 def not_valid_before(self):
2592 """
2593 :return:
2594 A datetime of the earliest time when the certificate is valid
2595 """
2596 return self['tbs_certificate']['validity']['not_before'].native
2597
2598 @property
wbondaf1f5a82015-07-17 12:13:15 -04002599 def authority_key_identifier(self):
2600 """
2601 :return:
2602 None or a byte string of the key_identifier from the authority key
2603 identifier extension
2604 """
2605
2606 if not self.authority_key_identifier_value:
2607 return None
2608
2609 return self.authority_key_identifier_value['key_identifier'].native
2610
2611 @property
2612 def authority_issuer_serial(self):
2613 """
2614 :return:
2615 None or a byte string of the SHA-256 hash of the isser from the
2616 authority key identifier extension concatenated with the ascii
2617 character ":", concatenated with the serial number from the
2618 authority key identifier extension as an ascii string
2619 """
2620
2621 if self._authority_issuer_serial is False:
wbonda26664f2015-10-07 11:57:35 -04002622 akiv = self.authority_key_identifier_value
2623 if akiv and akiv['authority_cert_issuer'].native:
2624 issuer = self.authority_key_identifier_value['authority_cert_issuer'][0].chosen
wbondaf1f5a82015-07-17 12:13:15 -04002625 # We untag the element since it is tagged via being a choice from GeneralName
wbonda26664f2015-10-07 11:57:35 -04002626 issuer = issuer.untag()
wbondaf1f5a82015-07-17 12:13:15 -04002627 authority_serial = self.authority_key_identifier_value['authority_cert_serial_number'].native
wbonda26664f2015-10-07 11:57:35 -04002628 self._authority_issuer_serial = issuer.sha256 + b':' + str_cls(authority_serial).encode('ascii')
wbondaf1f5a82015-07-17 12:13:15 -04002629 else:
2630 self._authority_issuer_serial = None
2631 return self._authority_issuer_serial
2632
2633 @property
wbond6888bc62015-07-21 15:05:59 -04002634 def crl_distribution_points(self):
wbondaf1f5a82015-07-17 12:13:15 -04002635 """
wbond6888bc62015-07-21 15:05:59 -04002636 Returns complete CRL URLs - does not include delta CRLs
2637
wbondaf1f5a82015-07-17 12:13:15 -04002638 :return:
wbond6888bc62015-07-21 15:05:59 -04002639 A list of zero or more DistributionPoint objects
wbondaf1f5a82015-07-17 12:13:15 -04002640 """
2641
wbond6888bc62015-07-21 15:05:59 -04002642 if self._crl_distribution_points is None:
2643 self._crl_distribution_points = self._get_http_crl_distribution_points(self.crl_distribution_points_value)
2644 return self._crl_distribution_points
2645
2646 @property
2647 def delta_crl_distribution_points(self):
2648 """
2649 Returns delta CRL URLs - does not include complete CRLs
2650
2651 :return:
2652 A list of zero or more DistributionPoint objects
2653 """
2654
2655 if self._delta_crl_distribution_points is None:
2656 self._delta_crl_distribution_points = self._get_http_crl_distribution_points(self.freshest_crl_value)
2657 return self._delta_crl_distribution_points
2658
2659 def _get_http_crl_distribution_points(self, crl_distribution_points):
2660 """
2661 Fetches the DistributionPoint object for non-relative, HTTP CRLs
2662 referenced by the certificate
2663
2664 :param crl_distribution_points:
2665 A CRLDistributionPoints object to grab the DistributionPoints from
2666
2667 :return:
2668 A list of zero or more DistributionPoint objects
2669 """
wbondaf1f5a82015-07-17 12:13:15 -04002670
2671 output = []
wbond6888bc62015-07-21 15:05:59 -04002672
2673 if crl_distribution_points is None:
2674 return []
2675
2676 for distribution_point in crl_distribution_points:
2677 distribution_point_name = distribution_point['distribution_point']
wbond093f9862015-10-22 11:54:37 -04002678 if distribution_point_name is VOID:
wbondbabedc62015-10-22 01:48:14 -04002679 continue
wbondd913db52015-08-06 12:44:15 -04002680 # RFC 5280 indicates conforming CA should not use the relative form
wbondaf1f5a82015-07-17 12:13:15 -04002681 if distribution_point_name.name == 'name_relative_to_crl_issuer':
2682 continue
wbond6888bc62015-07-21 15:05:59 -04002683 # This library is currently only concerned with HTTP-based CRLs
wbondaf1f5a82015-07-17 12:13:15 -04002684 for general_name in distribution_point_name.chosen:
2685 if general_name.name == 'uniform_resource_identifier':
wbond6888bc62015-07-21 15:05:59 -04002686 output.append(distribution_point)
wbondaf1f5a82015-07-17 12:13:15 -04002687
2688 return output
2689
2690 @property
2691 def ocsp_urls(self):
2692 """
2693 :return:
2694 A list of zero or more unicode strings of the OCSP URLs for this
2695 cert
2696 """
2697
2698 if not self.authority_information_access_value:
2699 return []
2700
2701 output = []
2702 for entry in self.authority_information_access_value:
2703 if entry['access_method'].native == 'ocsp':
wbond4c248d52015-07-21 15:28:28 -04002704 location = entry['access_location']
2705 if location.name != 'uniform_resource_identifier':
2706 continue
2707 url = location.native
Christian Heimes48bcd642017-09-19 18:45:33 +02002708 if url.lower().startswith(('http://', 'https://', 'ldap://', 'ldaps://')):
wbond4c248d52015-07-21 15:28:28 -04002709 output.append(url)
wbondaf1f5a82015-07-17 12:13:15 -04002710 return output
2711
2712 @property
2713 def valid_domains(self):
2714 """
2715 :return:
2716 A list of unicode strings of valid domain names for the certificate.
2717 Wildcard certificates will have a domain in the form: *.example.com
2718 """
2719
2720 if self._valid_domains is None:
2721 self._valid_domains = []
2722
wbondaf1f5a82015-07-17 12:13:15 -04002723 # For the subject alt name extension, we can look at the name of
2724 # the choice selected since it distinguishes between domain names,
2725 # email addresses, IPs, etc
2726 if self.subject_alt_name_value:
2727 for general_name in self.subject_alt_name_value:
2728 if general_name.name == 'dns_name' and general_name.native not in self._valid_domains:
2729 self._valid_domains.append(general_name.native)
2730
wbond06cf0992015-08-31 11:43:45 -04002731 # If there was no subject alt name extension, and the common name
2732 # in the subject looks like a domain, that is considered the valid
2733 # list. This is done because according to
2734 # https://tools.ietf.org/html/rfc6125#section-6.4.4, the common
2735 # name should not be used if the subject alt name is present.
2736 else:
2737 pattern = re.compile('^(\\*\\.)?(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$')
2738 for rdn in self.subject.chosen:
2739 for name_type_value in rdn:
2740 if name_type_value['type'].native == 'common_name':
2741 value = name_type_value['value'].native
2742 if pattern.match(value):
2743 self._valid_domains.append(value)
2744
wbondaf1f5a82015-07-17 12:13:15 -04002745 return self._valid_domains
2746
2747 @property
2748 def valid_ips(self):
2749 """
2750 :return:
2751 A list of unicode strings of valid IP addresses for the certificate
2752 """
2753
2754 if self._valid_ips is None:
2755 self._valid_ips = []
2756
2757 if self.subject_alt_name_value:
2758 for general_name in self.subject_alt_name_value:
2759 if general_name.name == 'ip_address':
2760 self._valid_ips.append(general_name.native)
2761
2762 return self._valid_ips
wbond9a7a0992015-07-23 09:59:06 -04002763
2764 @property
wbondb5e193e2015-07-24 08:40:24 -04002765 def ca(self):
2766 """
2767 :return;
2768 A boolean - if the certificate is marked as a CA
2769 """
2770
2771 return self.basic_constraints_value and self.basic_constraints_value['ca'].native
2772
2773 @property
2774 def max_path_length(self):
2775 """
2776 :return;
2777 None or an integer of the maximum path length
2778 """
2779
2780 if not self.ca:
2781 return None
2782 return self.basic_constraints_value['path_len_constraint'].native
2783
2784 @property
wbond9a7a0992015-07-23 09:59:06 -04002785 def self_issued(self):
2786 """
2787 :return:
wbondd913db52015-08-06 12:44:15 -04002788 A boolean - if the certificate is self-issued, as defined by RFC
2789 5280
wbond9a7a0992015-07-23 09:59:06 -04002790 """
2791
2792 if self._self_issued is None:
wbonddf6b8972015-08-31 08:53:12 -04002793 self._self_issued = self.subject == self.issuer
wbond9a7a0992015-07-23 09:59:06 -04002794 return self._self_issued
2795
2796 @property
2797 def self_signed(self):
2798 """
2799 :return:
wbond76471632017-11-20 07:31:11 -05002800 A unicode string of "no" or "maybe". The "maybe" result will
2801 be returned if the certificate issuer and subject are the same.
2802 If a key identifier and authority key identifier are present,
2803 they will need to match otherwise "no" will be returned.
2804
2805 To verify is a certificate is truly self-signed, the signature
2806 will need to be verified. See the certvalidator package for
2807 one possible solution.
wbond9a7a0992015-07-23 09:59:06 -04002808 """
2809
2810 if self._self_signed is None:
2811 self._self_signed = 'no'
2812 if self.self_issued:
2813 if self.key_identifier:
2814 if not self.authority_key_identifier:
wbond76471632017-11-20 07:31:11 -05002815 self._self_signed = 'maybe'
wbond9a7a0992015-07-23 09:59:06 -04002816 elif self.authority_key_identifier == self.key_identifier:
wbond76471632017-11-20 07:31:11 -05002817 self._self_signed = 'maybe'
wbond9a7a0992015-07-23 09:59:06 -04002818 else:
2819 self._self_signed = 'maybe'
2820 return self._self_signed
wbond35701c92015-08-07 13:45:21 -04002821
wbond5314f4b2015-09-01 13:25:47 -04002822 @property
2823 def sha1(self):
2824 """
2825 :return:
wbond12152692015-11-13 02:24:10 -05002826 The SHA-1 hash of the DER-encoded bytes of this complete certificate
wbond5314f4b2015-09-01 13:25:47 -04002827 """
2828
2829 if self._sha1 is None:
2830 self._sha1 = hashlib.sha1(self.dump()).digest()
2831 return self._sha1
2832
2833 @property
wbond12152692015-11-13 02:24:10 -05002834 def sha1_fingerprint(self):
2835 """
2836 :return:
2837 A unicode string of the SHA-1 hash, formatted using hex encoding
2838 with a space between each pair of characters, all uppercase
2839 """
2840
wbond4c518b22015-11-17 15:15:38 -05002841 return ' '.join('%02X' % c for c in bytes_to_list(self.sha1))
wbond12152692015-11-13 02:24:10 -05002842
2843 @property
wbond5314f4b2015-09-01 13:25:47 -04002844 def sha256(self):
2845 """
2846 :return:
2847 The SHA-256 hash of the DER-encoded bytes of this complete
2848 certificate
2849 """
2850
2851 if self._sha256 is None:
2852 self._sha256 = hashlib.sha256(self.dump()).digest()
2853 return self._sha256
2854
Pim Coster42e7a832017-05-20 19:56:51 +02002855 @property
2856 def sha256_fingerprint(self):
2857 """
2858 :return:
2859 A unicode string of the SHA-256 hash, formatted using hex encoding
2860 with a space between each pair of characters, all uppercase
2861 """
2862
2863 return ' '.join('%02X' % c for c in bytes_to_list(self.sha256))
2864
wbondd4fc7ea2015-08-31 11:44:11 -04002865 def is_valid_domain_ip(self, domain_ip):
2866 """
2867 Check if a domain name or IP address is valid according to the
2868 certificate
2869
2870 :param domain_ip:
2871 A unicode string of a domain name or IP address
2872
2873 :return:
2874 A boolean - if the domain or IP is valid for the certificate
2875 """
2876
2877 if not isinstance(domain_ip, str_cls):
wbonda26664f2015-10-07 11:57:35 -04002878 raise TypeError(unwrap(
2879 '''
2880 domain_ip must be a unicode string, not %s
2881 ''',
2882 type_name(domain_ip)
2883 ))
wbondd4fc7ea2015-08-31 11:44:11 -04002884
2885 encoded_domain_ip = domain_ip.encode('idna').decode('ascii').lower()
2886
2887 is_ipv6 = encoded_domain_ip.find(':') != -1
2888 is_ipv4 = not is_ipv6 and re.match('^\\d+\\.\\d+\\.\\d+\\.\\d+$', encoded_domain_ip)
2889 is_domain = not is_ipv6 and not is_ipv4
2890
2891 # Handle domain name checks
2892 if is_domain:
2893 if not self.valid_domains:
2894 return False
2895
2896 domain_labels = encoded_domain_ip.split('.')
2897
2898 for valid_domain in self.valid_domains:
2899 encoded_valid_domain = valid_domain.encode('idna').decode('ascii').lower()
2900 valid_domain_labels = encoded_valid_domain.split('.')
2901
2902 # The domain must be equal in label length to match
2903 if len(valid_domain_labels) != len(domain_labels):
2904 continue
2905
2906 if valid_domain_labels == domain_labels:
2907 return True
2908
wbonda26664f2015-10-07 11:57:35 -04002909 is_wildcard = self._is_wildcard_domain(encoded_valid_domain)
2910 if is_wildcard and self._is_wildcard_match(domain_labels, valid_domain_labels):
wbondd4fc7ea2015-08-31 11:44:11 -04002911 return True
2912
2913 return False
2914
2915 # Handle IP address checks
2916 if not self.valid_ips:
2917 return False
2918
2919 family = socket.AF_INET if is_ipv4 else socket.AF_INET6
2920 normalized_ip = inet_pton(family, encoded_domain_ip)
2921
2922 for valid_ip in self.valid_ips:
2923 valid_family = socket.AF_INET if valid_ip.find('.') != -1 else socket.AF_INET6
2924 normalized_valid_ip = inet_pton(valid_family, valid_ip)
2925
2926 if normalized_valid_ip == normalized_ip:
2927 return True
2928
2929 return False
2930
2931 def _is_wildcard_domain(self, domain):
2932 """
2933 Checks if a domain is a valid wildcard according to
2934 https://tools.ietf.org/html/rfc6125#section-6.4.3
2935
2936 :param domain:
2937 A unicode string of the domain name, where any U-labels from an IDN
2938 have been converted to A-labels
2939
2940 :return:
2941 A boolean - if the domain is a valid wildcard domain
2942 """
2943
2944 # The * character must be present for a wildcard match, and if there is
2945 # most than one, it is an invalid wildcard specification
2946 if domain.count('*') != 1:
2947 return False
2948
2949 labels = domain.lower().split('.')
2950
2951 if not labels:
2952 return False
2953
2954 # Wildcards may only appear in the left-most label
2955 if labels[0].find('*') == -1:
2956 return False
2957
2958 # Wildcards may not be embedded in an A-label from an IDN
2959 if labels[0][0:4] == 'xn--':
2960 return False
2961
2962 return True
2963
2964 def _is_wildcard_match(self, domain_labels, valid_domain_labels):
2965 """
2966 Determines if the labels in a domain are a match for labels from a
2967 wildcard valid domain name
2968
2969 :param domain_labels:
2970 A list of unicode strings, with A-label form for IDNs, of the labels
2971 in the domain name to check
2972
2973 :param valid_domain_labels:
2974 A list of unicode strings, with A-label form for IDNs, of the labels
2975 in a wildcard domain pattern
2976
2977 :return:
2978 A boolean - if the domain matches the valid domain
2979 """
2980
2981 first_domain_label = domain_labels[0]
2982 other_domain_labels = domain_labels[1:]
2983
2984 wildcard_label = valid_domain_labels[0]
2985 other_valid_domain_labels = valid_domain_labels[1:]
2986
2987 # The wildcard is only allowed in the first label, so if
2988 # The subsequent labels are not equal, there is no match
2989 if other_domain_labels != other_valid_domain_labels:
2990 return False
2991
2992 if wildcard_label == '*':
2993 return True
2994
2995 wildcard_regex = re.compile('^' + wildcard_label.replace('*', '.*') + '$')
2996 if wildcard_regex.match(first_domain_label):
2997 return True
2998
2999 return False
wbond54dc6852016-03-18 12:24:41 -04003000
3001
3002# The structures are taken from the OpenSSL source file x_x509a.c, and specify
3003# extra information that is added to X.509 certificates to store trust
3004# information about the certificate.
3005
3006class KeyPurposeIdentifiers(SequenceOf):
3007 _child_spec = KeyPurposeId
3008
3009
3010class SequenceOfAlgorithmIdentifiers(SequenceOf):
3011 _child_spec = AlgorithmIdentifier
3012
3013
3014class CertificateAux(Sequence):
3015 _fields = [
3016 ('trust', KeyPurposeIdentifiers, {'optional': True}),
wbondd62ed9a2017-09-15 07:13:52 -04003017 ('reject', KeyPurposeIdentifiers, {'implicit': 0, 'optional': True}),
wbond54dc6852016-03-18 12:24:41 -04003018 ('alias', UTF8String, {'optional': True}),
3019 ('keyid', OctetString, {'optional': True}),
wbondd62ed9a2017-09-15 07:13:52 -04003020 ('other', SequenceOfAlgorithmIdentifiers, {'implicit': 1, 'optional': True}),
wbond54dc6852016-03-18 12:24:41 -04003021 ]
3022
3023
3024class TrustedCertificate(Concat):
3025 _child_specs = [Certificate, CertificateAux]