1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
4
5from __future__ import annotations
6
7import abc
8import datetime
9import hashlib
10import ipaddress
11import typing
12
13from cryptography import utils
14from cryptography.hazmat.bindings._rust import asn1
15from cryptography.hazmat.bindings._rust import x509 as rust_x509
16from cryptography.hazmat.primitives import constant_time, serialization
17from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey
18from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
19from cryptography.hazmat.primitives.asymmetric.types import (
20 CertificateIssuerPublicKeyTypes,
21 CertificatePublicKeyTypes,
22)
23from cryptography.x509.certificate_transparency import (
24 SignedCertificateTimestamp,
25)
26from cryptography.x509.general_name import (
27 DirectoryName,
28 DNSName,
29 GeneralName,
30 IPAddress,
31 OtherName,
32 RegisteredID,
33 RFC822Name,
34 UniformResourceIdentifier,
35 _IPAddressTypes,
36)
37from cryptography.x509.name import Name, RelativeDistinguishedName
38from cryptography.x509.oid import (
39 CRLEntryExtensionOID,
40 ExtensionOID,
41 ObjectIdentifier,
42 OCSPExtensionOID,
43)
44
45ExtensionTypeVar = typing.TypeVar(
46 "ExtensionTypeVar", bound="ExtensionType", covariant=True
47)
48
49
50def _key_identifier_from_public_key(
51 public_key: CertificatePublicKeyTypes,
52) -> bytes:
53 if isinstance(public_key, RSAPublicKey):
54 data = public_key.public_bytes(
55 serialization.Encoding.DER,
56 serialization.PublicFormat.PKCS1,
57 )
58 elif isinstance(public_key, EllipticCurvePublicKey):
59 data = public_key.public_bytes(
60 serialization.Encoding.X962,
61 serialization.PublicFormat.UncompressedPoint,
62 )
63 else:
64 # This is a very slow way to do this.
65 serialized = public_key.public_bytes(
66 serialization.Encoding.DER,
67 serialization.PublicFormat.SubjectPublicKeyInfo,
68 )
69 data = asn1.parse_spki_for_data(serialized)
70
71 return hashlib.sha1(data).digest()
72
73
74def _make_sequence_methods(field_name: str):
75 def len_method(self) -> int:
76 return len(getattr(self, field_name))
77
78 def iter_method(self):
79 return iter(getattr(self, field_name))
80
81 def getitem_method(self, idx):
82 return getattr(self, field_name)[idx]
83
84 return len_method, iter_method, getitem_method
85
86
87class DuplicateExtension(Exception):
88 def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
89 super().__init__(msg)
90 self.oid = oid
91
92
93class ExtensionNotFound(Exception):
94 def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
95 super().__init__(msg)
96 self.oid = oid
97
98
99class ExtensionType(metaclass=abc.ABCMeta):
100 oid: typing.ClassVar[ObjectIdentifier]
101
102 def public_bytes(self) -> bytes:
103 """
104 Serializes the extension type to DER.
105 """
106 raise NotImplementedError(
107 f"public_bytes is not implemented for extension type {self!r}"
108 )
109
110
111class Extensions:
112 def __init__(
113 self, extensions: typing.Iterable[Extension[ExtensionType]]
114 ) -> None:
115 self._extensions = list(extensions)
116
117 def get_extension_for_oid(
118 self, oid: ObjectIdentifier
119 ) -> Extension[ExtensionType]:
120 for ext in self:
121 if ext.oid == oid:
122 return ext
123
124 raise ExtensionNotFound(f"No {oid} extension was found", oid)
125
126 def get_extension_for_class(
127 self, extclass: type[ExtensionTypeVar]
128 ) -> Extension[ExtensionTypeVar]:
129 if extclass is UnrecognizedExtension:
130 raise TypeError(
131 "UnrecognizedExtension can't be used with "
132 "get_extension_for_class because more than one instance of the"
133 " class may be present."
134 )
135
136 for ext in self:
137 if isinstance(ext.value, extclass):
138 return ext
139
140 raise ExtensionNotFound(
141 f"No {extclass} extension was found", extclass.oid
142 )
143
144 __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions")
145
146 def __repr__(self) -> str:
147 return f"<Extensions({self._extensions})>"
148
149
150class CRLNumber(ExtensionType):
151 oid = ExtensionOID.CRL_NUMBER
152
153 def __init__(self, crl_number: int) -> None:
154 if not isinstance(crl_number, int):
155 raise TypeError("crl_number must be an integer")
156
157 self._crl_number = crl_number
158
159 def __eq__(self, other: object) -> bool:
160 if not isinstance(other, CRLNumber):
161 return NotImplemented
162
163 return self.crl_number == other.crl_number
164
165 def __hash__(self) -> int:
166 return hash(self.crl_number)
167
168 def __repr__(self) -> str:
169 return f"<CRLNumber({self.crl_number})>"
170
171 @property
172 def crl_number(self) -> int:
173 return self._crl_number
174
175 def public_bytes(self) -> bytes:
176 return rust_x509.encode_extension_value(self)
177
178
179class AuthorityKeyIdentifier(ExtensionType):
180 oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER
181
182 def __init__(
183 self,
184 key_identifier: bytes | None,
185 authority_cert_issuer: typing.Iterable[GeneralName] | None,
186 authority_cert_serial_number: int | None,
187 ) -> None:
188 if (authority_cert_issuer is None) != (
189 authority_cert_serial_number is None
190 ):
191 raise ValueError(
192 "authority_cert_issuer and authority_cert_serial_number "
193 "must both be present or both None"
194 )
195
196 if authority_cert_issuer is not None:
197 authority_cert_issuer = list(authority_cert_issuer)
198 if not all(
199 isinstance(x, GeneralName) for x in authority_cert_issuer
200 ):
201 raise TypeError(
202 "authority_cert_issuer must be a list of GeneralName "
203 "objects"
204 )
205
206 if authority_cert_serial_number is not None and not isinstance(
207 authority_cert_serial_number, int
208 ):
209 raise TypeError("authority_cert_serial_number must be an integer")
210
211 self._key_identifier = key_identifier
212 self._authority_cert_issuer = authority_cert_issuer
213 self._authority_cert_serial_number = authority_cert_serial_number
214
215 # This takes a subset of CertificatePublicKeyTypes because an issuer
216 # cannot have an X25519/X448 key. This introduces some unfortunate
217 # asymmetry that requires typing users to explicitly
218 # narrow their type, but we should make this accurate and not just
219 # convenient.
220 @classmethod
221 def from_issuer_public_key(
222 cls, public_key: CertificateIssuerPublicKeyTypes
223 ) -> AuthorityKeyIdentifier:
224 digest = _key_identifier_from_public_key(public_key)
225 return cls(
226 key_identifier=digest,
227 authority_cert_issuer=None,
228 authority_cert_serial_number=None,
229 )
230
231 @classmethod
232 def from_issuer_subject_key_identifier(
233 cls, ski: SubjectKeyIdentifier
234 ) -> AuthorityKeyIdentifier:
235 return cls(
236 key_identifier=ski.digest,
237 authority_cert_issuer=None,
238 authority_cert_serial_number=None,
239 )
240
241 def __repr__(self) -> str:
242 return (
243 f"<AuthorityKeyIdentifier(key_identifier={self.key_identifier!r}, "
244 f"authority_cert_issuer={self.authority_cert_issuer}, "
245 f"authority_cert_serial_number={self.authority_cert_serial_number}"
246 ")>"
247 )
248
249 def __eq__(self, other: object) -> bool:
250 if not isinstance(other, AuthorityKeyIdentifier):
251 return NotImplemented
252
253 return (
254 self.key_identifier == other.key_identifier
255 and self.authority_cert_issuer == other.authority_cert_issuer
256 and self.authority_cert_serial_number
257 == other.authority_cert_serial_number
258 )
259
260 def __hash__(self) -> int:
261 if self.authority_cert_issuer is None:
262 aci = None
263 else:
264 aci = tuple(self.authority_cert_issuer)
265 return hash(
266 (self.key_identifier, aci, self.authority_cert_serial_number)
267 )
268
269 @property
270 def key_identifier(self) -> bytes | None:
271 return self._key_identifier
272
273 @property
274 def authority_cert_issuer(
275 self,
276 ) -> list[GeneralName] | None:
277 return self._authority_cert_issuer
278
279 @property
280 def authority_cert_serial_number(self) -> int | None:
281 return self._authority_cert_serial_number
282
283 def public_bytes(self) -> bytes:
284 return rust_x509.encode_extension_value(self)
285
286
287class SubjectKeyIdentifier(ExtensionType):
288 oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER
289
290 def __init__(self, digest: bytes) -> None:
291 self._digest = digest
292
293 @classmethod
294 def from_public_key(
295 cls, public_key: CertificatePublicKeyTypes
296 ) -> SubjectKeyIdentifier:
297 return cls(_key_identifier_from_public_key(public_key))
298
299 @property
300 def digest(self) -> bytes:
301 return self._digest
302
303 @property
304 def key_identifier(self) -> bytes:
305 return self._digest
306
307 def __repr__(self) -> str:
308 return f"<SubjectKeyIdentifier(digest={self.digest!r})>"
309
310 def __eq__(self, other: object) -> bool:
311 if not isinstance(other, SubjectKeyIdentifier):
312 return NotImplemented
313
314 return constant_time.bytes_eq(self.digest, other.digest)
315
316 def __hash__(self) -> int:
317 return hash(self.digest)
318
319 def public_bytes(self) -> bytes:
320 return rust_x509.encode_extension_value(self)
321
322
323class AuthorityInformationAccess(ExtensionType):
324 oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS
325
326 def __init__(
327 self, descriptions: typing.Iterable[AccessDescription]
328 ) -> None:
329 descriptions = list(descriptions)
330 if not all(isinstance(x, AccessDescription) for x in descriptions):
331 raise TypeError(
332 "Every item in the descriptions list must be an "
333 "AccessDescription"
334 )
335
336 self._descriptions = descriptions
337
338 __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions")
339
340 def __repr__(self) -> str:
341 return f"<AuthorityInformationAccess({self._descriptions})>"
342
343 def __eq__(self, other: object) -> bool:
344 if not isinstance(other, AuthorityInformationAccess):
345 return NotImplemented
346
347 return self._descriptions == other._descriptions
348
349 def __hash__(self) -> int:
350 return hash(tuple(self._descriptions))
351
352 def public_bytes(self) -> bytes:
353 return rust_x509.encode_extension_value(self)
354
355
356class SubjectInformationAccess(ExtensionType):
357 oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS
358
359 def __init__(
360 self, descriptions: typing.Iterable[AccessDescription]
361 ) -> None:
362 descriptions = list(descriptions)
363 if not all(isinstance(x, AccessDescription) for x in descriptions):
364 raise TypeError(
365 "Every item in the descriptions list must be an "
366 "AccessDescription"
367 )
368
369 self._descriptions = descriptions
370
371 __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions")
372
373 def __repr__(self) -> str:
374 return f"<SubjectInformationAccess({self._descriptions})>"
375
376 def __eq__(self, other: object) -> bool:
377 if not isinstance(other, SubjectInformationAccess):
378 return NotImplemented
379
380 return self._descriptions == other._descriptions
381
382 def __hash__(self) -> int:
383 return hash(tuple(self._descriptions))
384
385 def public_bytes(self) -> bytes:
386 return rust_x509.encode_extension_value(self)
387
388
389class AccessDescription:
390 def __init__(
391 self, access_method: ObjectIdentifier, access_location: GeneralName
392 ) -> None:
393 if not isinstance(access_method, ObjectIdentifier):
394 raise TypeError("access_method must be an ObjectIdentifier")
395
396 if not isinstance(access_location, GeneralName):
397 raise TypeError("access_location must be a GeneralName")
398
399 self._access_method = access_method
400 self._access_location = access_location
401
402 def __repr__(self) -> str:
403 return (
404 "<AccessDescription(access_method={0.access_method}, access_locati"
405 "on={0.access_location})>".format(self)
406 )
407
408 def __eq__(self, other: object) -> bool:
409 if not isinstance(other, AccessDescription):
410 return NotImplemented
411
412 return (
413 self.access_method == other.access_method
414 and self.access_location == other.access_location
415 )
416
417 def __hash__(self) -> int:
418 return hash((self.access_method, self.access_location))
419
420 @property
421 def access_method(self) -> ObjectIdentifier:
422 return self._access_method
423
424 @property
425 def access_location(self) -> GeneralName:
426 return self._access_location
427
428
429class BasicConstraints(ExtensionType):
430 oid = ExtensionOID.BASIC_CONSTRAINTS
431
432 def __init__(self, ca: bool, path_length: int | None) -> None:
433 if not isinstance(ca, bool):
434 raise TypeError("ca must be a boolean value")
435
436 if path_length is not None and not ca:
437 raise ValueError("path_length must be None when ca is False")
438
439 if path_length is not None and (
440 not isinstance(path_length, int) or path_length < 0
441 ):
442 raise TypeError(
443 "path_length must be a non-negative integer or None"
444 )
445
446 self._ca = ca
447 self._path_length = path_length
448
449 @property
450 def ca(self) -> bool:
451 return self._ca
452
453 @property
454 def path_length(self) -> int | None:
455 return self._path_length
456
457 def __repr__(self) -> str:
458 return (
459 "<BasicConstraints(ca={0.ca}, " "path_length={0.path_length})>"
460 ).format(self)
461
462 def __eq__(self, other: object) -> bool:
463 if not isinstance(other, BasicConstraints):
464 return NotImplemented
465
466 return self.ca == other.ca and self.path_length == other.path_length
467
468 def __hash__(self) -> int:
469 return hash((self.ca, self.path_length))
470
471 def public_bytes(self) -> bytes:
472 return rust_x509.encode_extension_value(self)
473
474
475class DeltaCRLIndicator(ExtensionType):
476 oid = ExtensionOID.DELTA_CRL_INDICATOR
477
478 def __init__(self, crl_number: int) -> None:
479 if not isinstance(crl_number, int):
480 raise TypeError("crl_number must be an integer")
481
482 self._crl_number = crl_number
483
484 @property
485 def crl_number(self) -> int:
486 return self._crl_number
487
488 def __eq__(self, other: object) -> bool:
489 if not isinstance(other, DeltaCRLIndicator):
490 return NotImplemented
491
492 return self.crl_number == other.crl_number
493
494 def __hash__(self) -> int:
495 return hash(self.crl_number)
496
497 def __repr__(self) -> str:
498 return f"<DeltaCRLIndicator(crl_number={self.crl_number})>"
499
500 def public_bytes(self) -> bytes:
501 return rust_x509.encode_extension_value(self)
502
503
504class CRLDistributionPoints(ExtensionType):
505 oid = ExtensionOID.CRL_DISTRIBUTION_POINTS
506
507 def __init__(
508 self, distribution_points: typing.Iterable[DistributionPoint]
509 ) -> None:
510 distribution_points = list(distribution_points)
511 if not all(
512 isinstance(x, DistributionPoint) for x in distribution_points
513 ):
514 raise TypeError(
515 "distribution_points must be a list of DistributionPoint "
516 "objects"
517 )
518
519 self._distribution_points = distribution_points
520
521 __len__, __iter__, __getitem__ = _make_sequence_methods(
522 "_distribution_points"
523 )
524
525 def __repr__(self) -> str:
526 return f"<CRLDistributionPoints({self._distribution_points})>"
527
528 def __eq__(self, other: object) -> bool:
529 if not isinstance(other, CRLDistributionPoints):
530 return NotImplemented
531
532 return self._distribution_points == other._distribution_points
533
534 def __hash__(self) -> int:
535 return hash(tuple(self._distribution_points))
536
537 def public_bytes(self) -> bytes:
538 return rust_x509.encode_extension_value(self)
539
540
541class FreshestCRL(ExtensionType):
542 oid = ExtensionOID.FRESHEST_CRL
543
544 def __init__(
545 self, distribution_points: typing.Iterable[DistributionPoint]
546 ) -> None:
547 distribution_points = list(distribution_points)
548 if not all(
549 isinstance(x, DistributionPoint) for x in distribution_points
550 ):
551 raise TypeError(
552 "distribution_points must be a list of DistributionPoint "
553 "objects"
554 )
555
556 self._distribution_points = distribution_points
557
558 __len__, __iter__, __getitem__ = _make_sequence_methods(
559 "_distribution_points"
560 )
561
562 def __repr__(self) -> str:
563 return f"<FreshestCRL({self._distribution_points})>"
564
565 def __eq__(self, other: object) -> bool:
566 if not isinstance(other, FreshestCRL):
567 return NotImplemented
568
569 return self._distribution_points == other._distribution_points
570
571 def __hash__(self) -> int:
572 return hash(tuple(self._distribution_points))
573
574 def public_bytes(self) -> bytes:
575 return rust_x509.encode_extension_value(self)
576
577
578class DistributionPoint:
579 def __init__(
580 self,
581 full_name: typing.Iterable[GeneralName] | None,
582 relative_name: RelativeDistinguishedName | None,
583 reasons: frozenset[ReasonFlags] | None,
584 crl_issuer: typing.Iterable[GeneralName] | None,
585 ) -> None:
586 if full_name and relative_name:
587 raise ValueError(
588 "You cannot provide both full_name and relative_name, at "
589 "least one must be None."
590 )
591 if not full_name and not relative_name and not crl_issuer:
592 raise ValueError(
593 "Either full_name, relative_name or crl_issuer must be "
594 "provided."
595 )
596
597 if full_name is not None:
598 full_name = list(full_name)
599 if not all(isinstance(x, GeneralName) for x in full_name):
600 raise TypeError(
601 "full_name must be a list of GeneralName objects"
602 )
603
604 if relative_name:
605 if not isinstance(relative_name, RelativeDistinguishedName):
606 raise TypeError(
607 "relative_name must be a RelativeDistinguishedName"
608 )
609
610 if crl_issuer is not None:
611 crl_issuer = list(crl_issuer)
612 if not all(isinstance(x, GeneralName) for x in crl_issuer):
613 raise TypeError(
614 "crl_issuer must be None or a list of general names"
615 )
616
617 if reasons and (
618 not isinstance(reasons, frozenset)
619 or not all(isinstance(x, ReasonFlags) for x in reasons)
620 ):
621 raise TypeError("reasons must be None or frozenset of ReasonFlags")
622
623 if reasons and (
624 ReasonFlags.unspecified in reasons
625 or ReasonFlags.remove_from_crl in reasons
626 ):
627 raise ValueError(
628 "unspecified and remove_from_crl are not valid reasons in a "
629 "DistributionPoint"
630 )
631
632 self._full_name = full_name
633 self._relative_name = relative_name
634 self._reasons = reasons
635 self._crl_issuer = crl_issuer
636
637 def __repr__(self) -> str:
638 return (
639 "<DistributionPoint(full_name={0.full_name}, relative_name={0.rela"
640 "tive_name}, reasons={0.reasons}, "
641 "crl_issuer={0.crl_issuer})>".format(self)
642 )
643
644 def __eq__(self, other: object) -> bool:
645 if not isinstance(other, DistributionPoint):
646 return NotImplemented
647
648 return (
649 self.full_name == other.full_name
650 and self.relative_name == other.relative_name
651 and self.reasons == other.reasons
652 and self.crl_issuer == other.crl_issuer
653 )
654
655 def __hash__(self) -> int:
656 if self.full_name is not None:
657 fn: tuple[GeneralName, ...] | None = tuple(self.full_name)
658 else:
659 fn = None
660
661 if self.crl_issuer is not None:
662 crl_issuer: tuple[GeneralName, ...] | None = tuple(self.crl_issuer)
663 else:
664 crl_issuer = None
665
666 return hash((fn, self.relative_name, self.reasons, crl_issuer))
667
668 @property
669 def full_name(self) -> list[GeneralName] | None:
670 return self._full_name
671
672 @property
673 def relative_name(self) -> RelativeDistinguishedName | None:
674 return self._relative_name
675
676 @property
677 def reasons(self) -> frozenset[ReasonFlags] | None:
678 return self._reasons
679
680 @property
681 def crl_issuer(self) -> list[GeneralName] | None:
682 return self._crl_issuer
683
684
685class ReasonFlags(utils.Enum):
686 unspecified = "unspecified"
687 key_compromise = "keyCompromise"
688 ca_compromise = "cACompromise"
689 affiliation_changed = "affiliationChanged"
690 superseded = "superseded"
691 cessation_of_operation = "cessationOfOperation"
692 certificate_hold = "certificateHold"
693 privilege_withdrawn = "privilegeWithdrawn"
694 aa_compromise = "aACompromise"
695 remove_from_crl = "removeFromCRL"
696
697
698# These are distribution point bit string mappings. Not to be confused with
699# CRLReason reason flags bit string mappings.
700# ReasonFlags ::= BIT STRING {
701# unused (0),
702# keyCompromise (1),
703# cACompromise (2),
704# affiliationChanged (3),
705# superseded (4),
706# cessationOfOperation (5),
707# certificateHold (6),
708# privilegeWithdrawn (7),
709# aACompromise (8) }
710_REASON_BIT_MAPPING = {
711 1: ReasonFlags.key_compromise,
712 2: ReasonFlags.ca_compromise,
713 3: ReasonFlags.affiliation_changed,
714 4: ReasonFlags.superseded,
715 5: ReasonFlags.cessation_of_operation,
716 6: ReasonFlags.certificate_hold,
717 7: ReasonFlags.privilege_withdrawn,
718 8: ReasonFlags.aa_compromise,
719}
720
721_CRLREASONFLAGS = {
722 ReasonFlags.key_compromise: 1,
723 ReasonFlags.ca_compromise: 2,
724 ReasonFlags.affiliation_changed: 3,
725 ReasonFlags.superseded: 4,
726 ReasonFlags.cessation_of_operation: 5,
727 ReasonFlags.certificate_hold: 6,
728 ReasonFlags.privilege_withdrawn: 7,
729 ReasonFlags.aa_compromise: 8,
730}
731
732
733class PolicyConstraints(ExtensionType):
734 oid = ExtensionOID.POLICY_CONSTRAINTS
735
736 def __init__(
737 self,
738 require_explicit_policy: int | None,
739 inhibit_policy_mapping: int | None,
740 ) -> None:
741 if require_explicit_policy is not None and not isinstance(
742 require_explicit_policy, int
743 ):
744 raise TypeError(
745 "require_explicit_policy must be a non-negative integer or "
746 "None"
747 )
748
749 if inhibit_policy_mapping is not None and not isinstance(
750 inhibit_policy_mapping, int
751 ):
752 raise TypeError(
753 "inhibit_policy_mapping must be a non-negative integer or None"
754 )
755
756 if inhibit_policy_mapping is None and require_explicit_policy is None:
757 raise ValueError(
758 "At least one of require_explicit_policy and "
759 "inhibit_policy_mapping must not be None"
760 )
761
762 self._require_explicit_policy = require_explicit_policy
763 self._inhibit_policy_mapping = inhibit_policy_mapping
764
765 def __repr__(self) -> str:
766 return (
767 "<PolicyConstraints(require_explicit_policy={0.require_explicit"
768 "_policy}, inhibit_policy_mapping={0.inhibit_policy_"
769 "mapping})>".format(self)
770 )
771
772 def __eq__(self, other: object) -> bool:
773 if not isinstance(other, PolicyConstraints):
774 return NotImplemented
775
776 return (
777 self.require_explicit_policy == other.require_explicit_policy
778 and self.inhibit_policy_mapping == other.inhibit_policy_mapping
779 )
780
781 def __hash__(self) -> int:
782 return hash(
783 (self.require_explicit_policy, self.inhibit_policy_mapping)
784 )
785
786 @property
787 def require_explicit_policy(self) -> int | None:
788 return self._require_explicit_policy
789
790 @property
791 def inhibit_policy_mapping(self) -> int | None:
792 return self._inhibit_policy_mapping
793
794 def public_bytes(self) -> bytes:
795 return rust_x509.encode_extension_value(self)
796
797
798class CertificatePolicies(ExtensionType):
799 oid = ExtensionOID.CERTIFICATE_POLICIES
800
801 def __init__(self, policies: typing.Iterable[PolicyInformation]) -> None:
802 policies = list(policies)
803 if not all(isinstance(x, PolicyInformation) for x in policies):
804 raise TypeError(
805 "Every item in the policies list must be a "
806 "PolicyInformation"
807 )
808
809 self._policies = policies
810
811 __len__, __iter__, __getitem__ = _make_sequence_methods("_policies")
812
813 def __repr__(self) -> str:
814 return f"<CertificatePolicies({self._policies})>"
815
816 def __eq__(self, other: object) -> bool:
817 if not isinstance(other, CertificatePolicies):
818 return NotImplemented
819
820 return self._policies == other._policies
821
822 def __hash__(self) -> int:
823 return hash(tuple(self._policies))
824
825 def public_bytes(self) -> bytes:
826 return rust_x509.encode_extension_value(self)
827
828
829class PolicyInformation:
830 def __init__(
831 self,
832 policy_identifier: ObjectIdentifier,
833 policy_qualifiers: typing.Iterable[str | UserNotice] | None,
834 ) -> None:
835 if not isinstance(policy_identifier, ObjectIdentifier):
836 raise TypeError("policy_identifier must be an ObjectIdentifier")
837
838 self._policy_identifier = policy_identifier
839
840 if policy_qualifiers is not None:
841 policy_qualifiers = list(policy_qualifiers)
842 if not all(
843 isinstance(x, (str, UserNotice)) for x in policy_qualifiers
844 ):
845 raise TypeError(
846 "policy_qualifiers must be a list of strings and/or "
847 "UserNotice objects or None"
848 )
849
850 self._policy_qualifiers = policy_qualifiers
851
852 def __repr__(self) -> str:
853 return (
854 "<PolicyInformation(policy_identifier={0.policy_identifier}, polic"
855 "y_qualifiers={0.policy_qualifiers})>".format(self)
856 )
857
858 def __eq__(self, other: object) -> bool:
859 if not isinstance(other, PolicyInformation):
860 return NotImplemented
861
862 return (
863 self.policy_identifier == other.policy_identifier
864 and self.policy_qualifiers == other.policy_qualifiers
865 )
866
867 def __hash__(self) -> int:
868 if self.policy_qualifiers is not None:
869 pq: tuple[str | UserNotice, ...] | None = tuple(
870 self.policy_qualifiers
871 )
872 else:
873 pq = None
874
875 return hash((self.policy_identifier, pq))
876
877 @property
878 def policy_identifier(self) -> ObjectIdentifier:
879 return self._policy_identifier
880
881 @property
882 def policy_qualifiers(
883 self,
884 ) -> list[str | UserNotice] | None:
885 return self._policy_qualifiers
886
887
888class UserNotice:
889 def __init__(
890 self,
891 notice_reference: NoticeReference | None,
892 explicit_text: str | None,
893 ) -> None:
894 if notice_reference and not isinstance(
895 notice_reference, NoticeReference
896 ):
897 raise TypeError(
898 "notice_reference must be None or a NoticeReference"
899 )
900
901 self._notice_reference = notice_reference
902 self._explicit_text = explicit_text
903
904 def __repr__(self) -> str:
905 return (
906 "<UserNotice(notice_reference={0.notice_reference}, explicit_text="
907 "{0.explicit_text!r})>".format(self)
908 )
909
910 def __eq__(self, other: object) -> bool:
911 if not isinstance(other, UserNotice):
912 return NotImplemented
913
914 return (
915 self.notice_reference == other.notice_reference
916 and self.explicit_text == other.explicit_text
917 )
918
919 def __hash__(self) -> int:
920 return hash((self.notice_reference, self.explicit_text))
921
922 @property
923 def notice_reference(self) -> NoticeReference | None:
924 return self._notice_reference
925
926 @property
927 def explicit_text(self) -> str | None:
928 return self._explicit_text
929
930
931class NoticeReference:
932 def __init__(
933 self,
934 organization: str | None,
935 notice_numbers: typing.Iterable[int],
936 ) -> None:
937 self._organization = organization
938 notice_numbers = list(notice_numbers)
939 if not all(isinstance(x, int) for x in notice_numbers):
940 raise TypeError("notice_numbers must be a list of integers")
941
942 self._notice_numbers = notice_numbers
943
944 def __repr__(self) -> str:
945 return (
946 "<NoticeReference(organization={0.organization!r}, notice_numbers="
947 "{0.notice_numbers})>".format(self)
948 )
949
950 def __eq__(self, other: object) -> bool:
951 if not isinstance(other, NoticeReference):
952 return NotImplemented
953
954 return (
955 self.organization == other.organization
956 and self.notice_numbers == other.notice_numbers
957 )
958
959 def __hash__(self) -> int:
960 return hash((self.organization, tuple(self.notice_numbers)))
961
962 @property
963 def organization(self) -> str | None:
964 return self._organization
965
966 @property
967 def notice_numbers(self) -> list[int]:
968 return self._notice_numbers
969
970
971class ExtendedKeyUsage(ExtensionType):
972 oid = ExtensionOID.EXTENDED_KEY_USAGE
973
974 def __init__(self, usages: typing.Iterable[ObjectIdentifier]) -> None:
975 usages = list(usages)
976 if not all(isinstance(x, ObjectIdentifier) for x in usages):
977 raise TypeError(
978 "Every item in the usages list must be an ObjectIdentifier"
979 )
980
981 self._usages = usages
982
983 __len__, __iter__, __getitem__ = _make_sequence_methods("_usages")
984
985 def __repr__(self) -> str:
986 return f"<ExtendedKeyUsage({self._usages})>"
987
988 def __eq__(self, other: object) -> bool:
989 if not isinstance(other, ExtendedKeyUsage):
990 return NotImplemented
991
992 return self._usages == other._usages
993
994 def __hash__(self) -> int:
995 return hash(tuple(self._usages))
996
997 def public_bytes(self) -> bytes:
998 return rust_x509.encode_extension_value(self)
999
1000
1001class OCSPNoCheck(ExtensionType):
1002 oid = ExtensionOID.OCSP_NO_CHECK
1003
1004 def __eq__(self, other: object) -> bool:
1005 if not isinstance(other, OCSPNoCheck):
1006 return NotImplemented
1007
1008 return True
1009
1010 def __hash__(self) -> int:
1011 return hash(OCSPNoCheck)
1012
1013 def __repr__(self) -> str:
1014 return "<OCSPNoCheck()>"
1015
1016 def public_bytes(self) -> bytes:
1017 return rust_x509.encode_extension_value(self)
1018
1019
1020class PrecertPoison(ExtensionType):
1021 oid = ExtensionOID.PRECERT_POISON
1022
1023 def __eq__(self, other: object) -> bool:
1024 if not isinstance(other, PrecertPoison):
1025 return NotImplemented
1026
1027 return True
1028
1029 def __hash__(self) -> int:
1030 return hash(PrecertPoison)
1031
1032 def __repr__(self) -> str:
1033 return "<PrecertPoison()>"
1034
1035 def public_bytes(self) -> bytes:
1036 return rust_x509.encode_extension_value(self)
1037
1038
1039class TLSFeature(ExtensionType):
1040 oid = ExtensionOID.TLS_FEATURE
1041
1042 def __init__(self, features: typing.Iterable[TLSFeatureType]) -> None:
1043 features = list(features)
1044 if (
1045 not all(isinstance(x, TLSFeatureType) for x in features)
1046 or len(features) == 0
1047 ):
1048 raise TypeError(
1049 "features must be a list of elements from the TLSFeatureType "
1050 "enum"
1051 )
1052
1053 self._features = features
1054
1055 __len__, __iter__, __getitem__ = _make_sequence_methods("_features")
1056
1057 def __repr__(self) -> str:
1058 return f"<TLSFeature(features={self._features})>"
1059
1060 def __eq__(self, other: object) -> bool:
1061 if not isinstance(other, TLSFeature):
1062 return NotImplemented
1063
1064 return self._features == other._features
1065
1066 def __hash__(self) -> int:
1067 return hash(tuple(self._features))
1068
1069 def public_bytes(self) -> bytes:
1070 return rust_x509.encode_extension_value(self)
1071
1072
1073class TLSFeatureType(utils.Enum):
1074 # status_request is defined in RFC 6066 and is used for what is commonly
1075 # called OCSP Must-Staple when present in the TLS Feature extension in an
1076 # X.509 certificate.
1077 status_request = 5
1078 # status_request_v2 is defined in RFC 6961 and allows multiple OCSP
1079 # responses to be provided. It is not currently in use by clients or
1080 # servers.
1081 status_request_v2 = 17
1082
1083
1084_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType}
1085
1086
1087class InhibitAnyPolicy(ExtensionType):
1088 oid = ExtensionOID.INHIBIT_ANY_POLICY
1089
1090 def __init__(self, skip_certs: int) -> None:
1091 if not isinstance(skip_certs, int):
1092 raise TypeError("skip_certs must be an integer")
1093
1094 if skip_certs < 0:
1095 raise ValueError("skip_certs must be a non-negative integer")
1096
1097 self._skip_certs = skip_certs
1098
1099 def __repr__(self) -> str:
1100 return f"<InhibitAnyPolicy(skip_certs={self.skip_certs})>"
1101
1102 def __eq__(self, other: object) -> bool:
1103 if not isinstance(other, InhibitAnyPolicy):
1104 return NotImplemented
1105
1106 return self.skip_certs == other.skip_certs
1107
1108 def __hash__(self) -> int:
1109 return hash(self.skip_certs)
1110
1111 @property
1112 def skip_certs(self) -> int:
1113 return self._skip_certs
1114
1115 def public_bytes(self) -> bytes:
1116 return rust_x509.encode_extension_value(self)
1117
1118
1119class KeyUsage(ExtensionType):
1120 oid = ExtensionOID.KEY_USAGE
1121
1122 def __init__(
1123 self,
1124 digital_signature: bool,
1125 content_commitment: bool,
1126 key_encipherment: bool,
1127 data_encipherment: bool,
1128 key_agreement: bool,
1129 key_cert_sign: bool,
1130 crl_sign: bool,
1131 encipher_only: bool,
1132 decipher_only: bool,
1133 ) -> None:
1134 if not key_agreement and (encipher_only or decipher_only):
1135 raise ValueError(
1136 "encipher_only and decipher_only can only be true when "
1137 "key_agreement is true"
1138 )
1139
1140 self._digital_signature = digital_signature
1141 self._content_commitment = content_commitment
1142 self._key_encipherment = key_encipherment
1143 self._data_encipherment = data_encipherment
1144 self._key_agreement = key_agreement
1145 self._key_cert_sign = key_cert_sign
1146 self._crl_sign = crl_sign
1147 self._encipher_only = encipher_only
1148 self._decipher_only = decipher_only
1149
1150 @property
1151 def digital_signature(self) -> bool:
1152 return self._digital_signature
1153
1154 @property
1155 def content_commitment(self) -> bool:
1156 return self._content_commitment
1157
1158 @property
1159 def key_encipherment(self) -> bool:
1160 return self._key_encipherment
1161
1162 @property
1163 def data_encipherment(self) -> bool:
1164 return self._data_encipherment
1165
1166 @property
1167 def key_agreement(self) -> bool:
1168 return self._key_agreement
1169
1170 @property
1171 def key_cert_sign(self) -> bool:
1172 return self._key_cert_sign
1173
1174 @property
1175 def crl_sign(self) -> bool:
1176 return self._crl_sign
1177
1178 @property
1179 def encipher_only(self) -> bool:
1180 if not self.key_agreement:
1181 raise ValueError(
1182 "encipher_only is undefined unless key_agreement is true"
1183 )
1184 else:
1185 return self._encipher_only
1186
1187 @property
1188 def decipher_only(self) -> bool:
1189 if not self.key_agreement:
1190 raise ValueError(
1191 "decipher_only is undefined unless key_agreement is true"
1192 )
1193 else:
1194 return self._decipher_only
1195
1196 def __repr__(self) -> str:
1197 try:
1198 encipher_only = self.encipher_only
1199 decipher_only = self.decipher_only
1200 except ValueError:
1201 # Users found None confusing because even though encipher/decipher
1202 # have no meaning unless key_agreement is true, to construct an
1203 # instance of the class you still need to pass False.
1204 encipher_only = False
1205 decipher_only = False
1206
1207 return (
1208 f"<KeyUsage(digital_signature={self.digital_signature}, "
1209 f"content_commitment={self.content_commitment}, "
1210 f"key_encipherment={self.key_encipherment}, "
1211 f"data_encipherment={self.data_encipherment}, "
1212 f"key_agreement={self.key_agreement}, "
1213 f"key_cert_sign={self.key_cert_sign}, crl_sign={self.crl_sign}, "
1214 f"encipher_only={encipher_only}, decipher_only={decipher_only})>"
1215 )
1216
1217 def __eq__(self, other: object) -> bool:
1218 if not isinstance(other, KeyUsage):
1219 return NotImplemented
1220
1221 return (
1222 self.digital_signature == other.digital_signature
1223 and self.content_commitment == other.content_commitment
1224 and self.key_encipherment == other.key_encipherment
1225 and self.data_encipherment == other.data_encipherment
1226 and self.key_agreement == other.key_agreement
1227 and self.key_cert_sign == other.key_cert_sign
1228 and self.crl_sign == other.crl_sign
1229 and self._encipher_only == other._encipher_only
1230 and self._decipher_only == other._decipher_only
1231 )
1232
1233 def __hash__(self) -> int:
1234 return hash(
1235 (
1236 self.digital_signature,
1237 self.content_commitment,
1238 self.key_encipherment,
1239 self.data_encipherment,
1240 self.key_agreement,
1241 self.key_cert_sign,
1242 self.crl_sign,
1243 self._encipher_only,
1244 self._decipher_only,
1245 )
1246 )
1247
1248 def public_bytes(self) -> bytes:
1249 return rust_x509.encode_extension_value(self)
1250
1251
1252class NameConstraints(ExtensionType):
1253 oid = ExtensionOID.NAME_CONSTRAINTS
1254
1255 def __init__(
1256 self,
1257 permitted_subtrees: typing.Iterable[GeneralName] | None,
1258 excluded_subtrees: typing.Iterable[GeneralName] | None,
1259 ) -> None:
1260 if permitted_subtrees is not None:
1261 permitted_subtrees = list(permitted_subtrees)
1262 if not permitted_subtrees:
1263 raise ValueError(
1264 "permitted_subtrees must be a non-empty list or None"
1265 )
1266 if not all(isinstance(x, GeneralName) for x in permitted_subtrees):
1267 raise TypeError(
1268 "permitted_subtrees must be a list of GeneralName objects "
1269 "or None"
1270 )
1271
1272 self._validate_tree(permitted_subtrees)
1273
1274 if excluded_subtrees is not None:
1275 excluded_subtrees = list(excluded_subtrees)
1276 if not excluded_subtrees:
1277 raise ValueError(
1278 "excluded_subtrees must be a non-empty list or None"
1279 )
1280 if not all(isinstance(x, GeneralName) for x in excluded_subtrees):
1281 raise TypeError(
1282 "excluded_subtrees must be a list of GeneralName objects "
1283 "or None"
1284 )
1285
1286 self._validate_tree(excluded_subtrees)
1287
1288 if permitted_subtrees is None and excluded_subtrees is None:
1289 raise ValueError(
1290 "At least one of permitted_subtrees and excluded_subtrees "
1291 "must not be None"
1292 )
1293
1294 self._permitted_subtrees = permitted_subtrees
1295 self._excluded_subtrees = excluded_subtrees
1296
1297 def __eq__(self, other: object) -> bool:
1298 if not isinstance(other, NameConstraints):
1299 return NotImplemented
1300
1301 return (
1302 self.excluded_subtrees == other.excluded_subtrees
1303 and self.permitted_subtrees == other.permitted_subtrees
1304 )
1305
1306 def _validate_tree(self, tree: typing.Iterable[GeneralName]) -> None:
1307 self._validate_ip_name(tree)
1308 self._validate_dns_name(tree)
1309
1310 def _validate_ip_name(self, tree: typing.Iterable[GeneralName]) -> None:
1311 if any(
1312 isinstance(name, IPAddress)
1313 and not isinstance(
1314 name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network)
1315 )
1316 for name in tree
1317 ):
1318 raise TypeError(
1319 "IPAddress name constraints must be an IPv4Network or"
1320 " IPv6Network object"
1321 )
1322
1323 def _validate_dns_name(self, tree: typing.Iterable[GeneralName]) -> None:
1324 if any(
1325 isinstance(name, DNSName) and "*" in name.value for name in tree
1326 ):
1327 raise ValueError(
1328 "DNSName name constraints must not contain the '*' wildcard"
1329 " character"
1330 )
1331
1332 def __repr__(self) -> str:
1333 return (
1334 f"<NameConstraints(permitted_subtrees={self.permitted_subtrees}, "
1335 f"excluded_subtrees={self.excluded_subtrees})>"
1336 )
1337
1338 def __hash__(self) -> int:
1339 if self.permitted_subtrees is not None:
1340 ps: tuple[GeneralName, ...] | None = tuple(self.permitted_subtrees)
1341 else:
1342 ps = None
1343
1344 if self.excluded_subtrees is not None:
1345 es: tuple[GeneralName, ...] | None = tuple(self.excluded_subtrees)
1346 else:
1347 es = None
1348
1349 return hash((ps, es))
1350
1351 @property
1352 def permitted_subtrees(
1353 self,
1354 ) -> list[GeneralName] | None:
1355 return self._permitted_subtrees
1356
1357 @property
1358 def excluded_subtrees(
1359 self,
1360 ) -> list[GeneralName] | None:
1361 return self._excluded_subtrees
1362
1363 def public_bytes(self) -> bytes:
1364 return rust_x509.encode_extension_value(self)
1365
1366
1367class Extension(typing.Generic[ExtensionTypeVar]):
1368 def __init__(
1369 self, oid: ObjectIdentifier, critical: bool, value: ExtensionTypeVar
1370 ) -> None:
1371 if not isinstance(oid, ObjectIdentifier):
1372 raise TypeError(
1373 "oid argument must be an ObjectIdentifier instance."
1374 )
1375
1376 if not isinstance(critical, bool):
1377 raise TypeError("critical must be a boolean value")
1378
1379 self._oid = oid
1380 self._critical = critical
1381 self._value = value
1382
1383 @property
1384 def oid(self) -> ObjectIdentifier:
1385 return self._oid
1386
1387 @property
1388 def critical(self) -> bool:
1389 return self._critical
1390
1391 @property
1392 def value(self) -> ExtensionTypeVar:
1393 return self._value
1394
1395 def __repr__(self) -> str:
1396 return (
1397 f"<Extension(oid={self.oid}, critical={self.critical}, "
1398 f"value={self.value})>"
1399 )
1400
1401 def __eq__(self, other: object) -> bool:
1402 if not isinstance(other, Extension):
1403 return NotImplemented
1404
1405 return (
1406 self.oid == other.oid
1407 and self.critical == other.critical
1408 and self.value == other.value
1409 )
1410
1411 def __hash__(self) -> int:
1412 return hash((self.oid, self.critical, self.value))
1413
1414
1415class GeneralNames:
1416 def __init__(self, general_names: typing.Iterable[GeneralName]) -> None:
1417 general_names = list(general_names)
1418 if not all(isinstance(x, GeneralName) for x in general_names):
1419 raise TypeError(
1420 "Every item in the general_names list must be an "
1421 "object conforming to the GeneralName interface"
1422 )
1423
1424 self._general_names = general_names
1425
1426 __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")
1427
1428 @typing.overload
1429 def get_values_for_type(
1430 self,
1431 type: type[DNSName]
1432 | type[UniformResourceIdentifier]
1433 | type[RFC822Name],
1434 ) -> list[str]:
1435 ...
1436
1437 @typing.overload
1438 def get_values_for_type(
1439 self,
1440 type: type[DirectoryName],
1441 ) -> list[Name]:
1442 ...
1443
1444 @typing.overload
1445 def get_values_for_type(
1446 self,
1447 type: type[RegisteredID],
1448 ) -> list[ObjectIdentifier]:
1449 ...
1450
1451 @typing.overload
1452 def get_values_for_type(
1453 self, type: type[IPAddress]
1454 ) -> list[_IPAddressTypes]:
1455 ...
1456
1457 @typing.overload
1458 def get_values_for_type(self, type: type[OtherName]) -> list[OtherName]:
1459 ...
1460
1461 def get_values_for_type(
1462 self,
1463 type: type[DNSName]
1464 | type[DirectoryName]
1465 | type[IPAddress]
1466 | type[OtherName]
1467 | type[RFC822Name]
1468 | type[RegisteredID]
1469 | type[UniformResourceIdentifier],
1470 ) -> (
1471 list[_IPAddressTypes]
1472 | list[str]
1473 | list[OtherName]
1474 | list[Name]
1475 | list[ObjectIdentifier]
1476 ):
1477 # Return the value of each GeneralName, except for OtherName instances
1478 # which we return directly because it has two important properties not
1479 # just one value.
1480 objs = (i for i in self if isinstance(i, type))
1481 if type != OtherName:
1482 return [i.value for i in objs]
1483 return list(objs)
1484
1485 def __repr__(self) -> str:
1486 return f"<GeneralNames({self._general_names})>"
1487
1488 def __eq__(self, other: object) -> bool:
1489 if not isinstance(other, GeneralNames):
1490 return NotImplemented
1491
1492 return self._general_names == other._general_names
1493
1494 def __hash__(self) -> int:
1495 return hash(tuple(self._general_names))
1496
1497
1498class SubjectAlternativeName(ExtensionType):
1499 oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME
1500
1501 def __init__(self, general_names: typing.Iterable[GeneralName]) -> None:
1502 self._general_names = GeneralNames(general_names)
1503
1504 __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")
1505
1506 @typing.overload
1507 def get_values_for_type(
1508 self,
1509 type: type[DNSName]
1510 | type[UniformResourceIdentifier]
1511 | type[RFC822Name],
1512 ) -> list[str]:
1513 ...
1514
1515 @typing.overload
1516 def get_values_for_type(
1517 self,
1518 type: type[DirectoryName],
1519 ) -> list[Name]:
1520 ...
1521
1522 @typing.overload
1523 def get_values_for_type(
1524 self,
1525 type: type[RegisteredID],
1526 ) -> list[ObjectIdentifier]:
1527 ...
1528
1529 @typing.overload
1530 def get_values_for_type(
1531 self, type: type[IPAddress]
1532 ) -> list[_IPAddressTypes]:
1533 ...
1534
1535 @typing.overload
1536 def get_values_for_type(self, type: type[OtherName]) -> list[OtherName]:
1537 ...
1538
1539 def get_values_for_type(
1540 self,
1541 type: type[DNSName]
1542 | type[DirectoryName]
1543 | type[IPAddress]
1544 | type[OtherName]
1545 | type[RFC822Name]
1546 | type[RegisteredID]
1547 | type[UniformResourceIdentifier],
1548 ) -> (
1549 list[_IPAddressTypes]
1550 | list[str]
1551 | list[OtherName]
1552 | list[Name]
1553 | list[ObjectIdentifier]
1554 ):
1555 return self._general_names.get_values_for_type(type)
1556
1557 def __repr__(self) -> str:
1558 return f"<SubjectAlternativeName({self._general_names})>"
1559
1560 def __eq__(self, other: object) -> bool:
1561 if not isinstance(other, SubjectAlternativeName):
1562 return NotImplemented
1563
1564 return self._general_names == other._general_names
1565
1566 def __hash__(self) -> int:
1567 return hash(self._general_names)
1568
1569 def public_bytes(self) -> bytes:
1570 return rust_x509.encode_extension_value(self)
1571
1572
1573class IssuerAlternativeName(ExtensionType):
1574 oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME
1575
1576 def __init__(self, general_names: typing.Iterable[GeneralName]) -> None:
1577 self._general_names = GeneralNames(general_names)
1578
1579 __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")
1580
1581 @typing.overload
1582 def get_values_for_type(
1583 self,
1584 type: type[DNSName]
1585 | type[UniformResourceIdentifier]
1586 | type[RFC822Name],
1587 ) -> list[str]:
1588 ...
1589
1590 @typing.overload
1591 def get_values_for_type(
1592 self,
1593 type: type[DirectoryName],
1594 ) -> list[Name]:
1595 ...
1596
1597 @typing.overload
1598 def get_values_for_type(
1599 self,
1600 type: type[RegisteredID],
1601 ) -> list[ObjectIdentifier]:
1602 ...
1603
1604 @typing.overload
1605 def get_values_for_type(
1606 self, type: type[IPAddress]
1607 ) -> list[_IPAddressTypes]:
1608 ...
1609
1610 @typing.overload
1611 def get_values_for_type(self, type: type[OtherName]) -> list[OtherName]:
1612 ...
1613
1614 def get_values_for_type(
1615 self,
1616 type: type[DNSName]
1617 | type[DirectoryName]
1618 | type[IPAddress]
1619 | type[OtherName]
1620 | type[RFC822Name]
1621 | type[RegisteredID]
1622 | type[UniformResourceIdentifier],
1623 ) -> (
1624 list[_IPAddressTypes]
1625 | list[str]
1626 | list[OtherName]
1627 | list[Name]
1628 | list[ObjectIdentifier]
1629 ):
1630 return self._general_names.get_values_for_type(type)
1631
1632 def __repr__(self) -> str:
1633 return f"<IssuerAlternativeName({self._general_names})>"
1634
1635 def __eq__(self, other: object) -> bool:
1636 if not isinstance(other, IssuerAlternativeName):
1637 return NotImplemented
1638
1639 return self._general_names == other._general_names
1640
1641 def __hash__(self) -> int:
1642 return hash(self._general_names)
1643
1644 def public_bytes(self) -> bytes:
1645 return rust_x509.encode_extension_value(self)
1646
1647
1648class CertificateIssuer(ExtensionType):
1649 oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER
1650
1651 def __init__(self, general_names: typing.Iterable[GeneralName]) -> None:
1652 self._general_names = GeneralNames(general_names)
1653
1654 __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")
1655
1656 @typing.overload
1657 def get_values_for_type(
1658 self,
1659 type: type[DNSName]
1660 | type[UniformResourceIdentifier]
1661 | type[RFC822Name],
1662 ) -> list[str]:
1663 ...
1664
1665 @typing.overload
1666 def get_values_for_type(
1667 self,
1668 type: type[DirectoryName],
1669 ) -> list[Name]:
1670 ...
1671
1672 @typing.overload
1673 def get_values_for_type(
1674 self,
1675 type: type[RegisteredID],
1676 ) -> list[ObjectIdentifier]:
1677 ...
1678
1679 @typing.overload
1680 def get_values_for_type(
1681 self, type: type[IPAddress]
1682 ) -> list[_IPAddressTypes]:
1683 ...
1684
1685 @typing.overload
1686 def get_values_for_type(self, type: type[OtherName]) -> list[OtherName]:
1687 ...
1688
1689 def get_values_for_type(
1690 self,
1691 type: type[DNSName]
1692 | type[DirectoryName]
1693 | type[IPAddress]
1694 | type[OtherName]
1695 | type[RFC822Name]
1696 | type[RegisteredID]
1697 | type[UniformResourceIdentifier],
1698 ) -> (
1699 list[_IPAddressTypes]
1700 | list[str]
1701 | list[OtherName]
1702 | list[Name]
1703 | list[ObjectIdentifier]
1704 ):
1705 return self._general_names.get_values_for_type(type)
1706
1707 def __repr__(self) -> str:
1708 return f"<CertificateIssuer({self._general_names})>"
1709
1710 def __eq__(self, other: object) -> bool:
1711 if not isinstance(other, CertificateIssuer):
1712 return NotImplemented
1713
1714 return self._general_names == other._general_names
1715
1716 def __hash__(self) -> int:
1717 return hash(self._general_names)
1718
1719 def public_bytes(self) -> bytes:
1720 return rust_x509.encode_extension_value(self)
1721
1722
1723class CRLReason(ExtensionType):
1724 oid = CRLEntryExtensionOID.CRL_REASON
1725
1726 def __init__(self, reason: ReasonFlags) -> None:
1727 if not isinstance(reason, ReasonFlags):
1728 raise TypeError("reason must be an element from ReasonFlags")
1729
1730 self._reason = reason
1731
1732 def __repr__(self) -> str:
1733 return f"<CRLReason(reason={self._reason})>"
1734
1735 def __eq__(self, other: object) -> bool:
1736 if not isinstance(other, CRLReason):
1737 return NotImplemented
1738
1739 return self.reason == other.reason
1740
1741 def __hash__(self) -> int:
1742 return hash(self.reason)
1743
1744 @property
1745 def reason(self) -> ReasonFlags:
1746 return self._reason
1747
1748 def public_bytes(self) -> bytes:
1749 return rust_x509.encode_extension_value(self)
1750
1751
1752class InvalidityDate(ExtensionType):
1753 oid = CRLEntryExtensionOID.INVALIDITY_DATE
1754
1755 def __init__(self, invalidity_date: datetime.datetime) -> None:
1756 if not isinstance(invalidity_date, datetime.datetime):
1757 raise TypeError("invalidity_date must be a datetime.datetime")
1758
1759 self._invalidity_date = invalidity_date
1760
1761 def __repr__(self) -> str:
1762 return f"<InvalidityDate(invalidity_date={self._invalidity_date})>"
1763
1764 def __eq__(self, other: object) -> bool:
1765 if not isinstance(other, InvalidityDate):
1766 return NotImplemented
1767
1768 return self.invalidity_date == other.invalidity_date
1769
1770 def __hash__(self) -> int:
1771 return hash(self.invalidity_date)
1772
1773 @property
1774 def invalidity_date(self) -> datetime.datetime:
1775 return self._invalidity_date
1776
1777 def public_bytes(self) -> bytes:
1778 return rust_x509.encode_extension_value(self)
1779
1780
1781class PrecertificateSignedCertificateTimestamps(ExtensionType):
1782 oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS
1783
1784 def __init__(
1785 self,
1786 signed_certificate_timestamps: typing.Iterable[
1787 SignedCertificateTimestamp
1788 ],
1789 ) -> None:
1790 signed_certificate_timestamps = list(signed_certificate_timestamps)
1791 if not all(
1792 isinstance(sct, SignedCertificateTimestamp)
1793 for sct in signed_certificate_timestamps
1794 ):
1795 raise TypeError(
1796 "Every item in the signed_certificate_timestamps list must be "
1797 "a SignedCertificateTimestamp"
1798 )
1799 self._signed_certificate_timestamps = signed_certificate_timestamps
1800
1801 __len__, __iter__, __getitem__ = _make_sequence_methods(
1802 "_signed_certificate_timestamps"
1803 )
1804
1805 def __repr__(self) -> str:
1806 return f"<PrecertificateSignedCertificateTimestamps({list(self)})>"
1807
1808 def __hash__(self) -> int:
1809 return hash(tuple(self._signed_certificate_timestamps))
1810
1811 def __eq__(self, other: object) -> bool:
1812 if not isinstance(other, PrecertificateSignedCertificateTimestamps):
1813 return NotImplemented
1814
1815 return (
1816 self._signed_certificate_timestamps
1817 == other._signed_certificate_timestamps
1818 )
1819
1820 def public_bytes(self) -> bytes:
1821 return rust_x509.encode_extension_value(self)
1822
1823
1824class SignedCertificateTimestamps(ExtensionType):
1825 oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS
1826
1827 def __init__(
1828 self,
1829 signed_certificate_timestamps: typing.Iterable[
1830 SignedCertificateTimestamp
1831 ],
1832 ) -> None:
1833 signed_certificate_timestamps = list(signed_certificate_timestamps)
1834 if not all(
1835 isinstance(sct, SignedCertificateTimestamp)
1836 for sct in signed_certificate_timestamps
1837 ):
1838 raise TypeError(
1839 "Every item in the signed_certificate_timestamps list must be "
1840 "a SignedCertificateTimestamp"
1841 )
1842 self._signed_certificate_timestamps = signed_certificate_timestamps
1843
1844 __len__, __iter__, __getitem__ = _make_sequence_methods(
1845 "_signed_certificate_timestamps"
1846 )
1847
1848 def __repr__(self) -> str:
1849 return f"<SignedCertificateTimestamps({list(self)})>"
1850
1851 def __hash__(self) -> int:
1852 return hash(tuple(self._signed_certificate_timestamps))
1853
1854 def __eq__(self, other: object) -> bool:
1855 if not isinstance(other, SignedCertificateTimestamps):
1856 return NotImplemented
1857
1858 return (
1859 self._signed_certificate_timestamps
1860 == other._signed_certificate_timestamps
1861 )
1862
1863 def public_bytes(self) -> bytes:
1864 return rust_x509.encode_extension_value(self)
1865
1866
1867class OCSPNonce(ExtensionType):
1868 oid = OCSPExtensionOID.NONCE
1869
1870 def __init__(self, nonce: bytes) -> None:
1871 if not isinstance(nonce, bytes):
1872 raise TypeError("nonce must be bytes")
1873
1874 self._nonce = nonce
1875
1876 def __eq__(self, other: object) -> bool:
1877 if not isinstance(other, OCSPNonce):
1878 return NotImplemented
1879
1880 return self.nonce == other.nonce
1881
1882 def __hash__(self) -> int:
1883 return hash(self.nonce)
1884
1885 def __repr__(self) -> str:
1886 return f"<OCSPNonce(nonce={self.nonce!r})>"
1887
1888 @property
1889 def nonce(self) -> bytes:
1890 return self._nonce
1891
1892 def public_bytes(self) -> bytes:
1893 return rust_x509.encode_extension_value(self)
1894
1895
1896class OCSPAcceptableResponses(ExtensionType):
1897 oid = OCSPExtensionOID.ACCEPTABLE_RESPONSES
1898
1899 def __init__(self, responses: typing.Iterable[ObjectIdentifier]) -> None:
1900 responses = list(responses)
1901 if any(not isinstance(r, ObjectIdentifier) for r in responses):
1902 raise TypeError("All responses must be ObjectIdentifiers")
1903
1904 self._responses = responses
1905
1906 def __eq__(self, other: object) -> bool:
1907 if not isinstance(other, OCSPAcceptableResponses):
1908 return NotImplemented
1909
1910 return self._responses == other._responses
1911
1912 def __hash__(self) -> int:
1913 return hash(tuple(self._responses))
1914
1915 def __repr__(self) -> str:
1916 return f"<OCSPAcceptableResponses(responses={self._responses})>"
1917
1918 def __iter__(self) -> typing.Iterator[ObjectIdentifier]:
1919 return iter(self._responses)
1920
1921 def public_bytes(self) -> bytes:
1922 return rust_x509.encode_extension_value(self)
1923
1924
1925class IssuingDistributionPoint(ExtensionType):
1926 oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT
1927
1928 def __init__(
1929 self,
1930 full_name: typing.Iterable[GeneralName] | None,
1931 relative_name: RelativeDistinguishedName | None,
1932 only_contains_user_certs: bool,
1933 only_contains_ca_certs: bool,
1934 only_some_reasons: frozenset[ReasonFlags] | None,
1935 indirect_crl: bool,
1936 only_contains_attribute_certs: bool,
1937 ) -> None:
1938 if full_name is not None:
1939 full_name = list(full_name)
1940
1941 if only_some_reasons and (
1942 not isinstance(only_some_reasons, frozenset)
1943 or not all(isinstance(x, ReasonFlags) for x in only_some_reasons)
1944 ):
1945 raise TypeError(
1946 "only_some_reasons must be None or frozenset of ReasonFlags"
1947 )
1948
1949 if only_some_reasons and (
1950 ReasonFlags.unspecified in only_some_reasons
1951 or ReasonFlags.remove_from_crl in only_some_reasons
1952 ):
1953 raise ValueError(
1954 "unspecified and remove_from_crl are not valid reasons in an "
1955 "IssuingDistributionPoint"
1956 )
1957
1958 if not (
1959 isinstance(only_contains_user_certs, bool)
1960 and isinstance(only_contains_ca_certs, bool)
1961 and isinstance(indirect_crl, bool)
1962 and isinstance(only_contains_attribute_certs, bool)
1963 ):
1964 raise TypeError(
1965 "only_contains_user_certs, only_contains_ca_certs, "
1966 "indirect_crl and only_contains_attribute_certs "
1967 "must all be boolean."
1968 )
1969
1970 crl_constraints = [
1971 only_contains_user_certs,
1972 only_contains_ca_certs,
1973 indirect_crl,
1974 only_contains_attribute_certs,
1975 ]
1976
1977 if len([x for x in crl_constraints if x]) > 1:
1978 raise ValueError(
1979 "Only one of the following can be set to True: "
1980 "only_contains_user_certs, only_contains_ca_certs, "
1981 "indirect_crl, only_contains_attribute_certs"
1982 )
1983
1984 if not any(
1985 [
1986 only_contains_user_certs,
1987 only_contains_ca_certs,
1988 indirect_crl,
1989 only_contains_attribute_certs,
1990 full_name,
1991 relative_name,
1992 only_some_reasons,
1993 ]
1994 ):
1995 raise ValueError(
1996 "Cannot create empty extension: "
1997 "if only_contains_user_certs, only_contains_ca_certs, "
1998 "indirect_crl, and only_contains_attribute_certs are all False"
1999 ", then either full_name, relative_name, or only_some_reasons "
2000 "must have a value."
2001 )
2002
2003 self._only_contains_user_certs = only_contains_user_certs
2004 self._only_contains_ca_certs = only_contains_ca_certs
2005 self._indirect_crl = indirect_crl
2006 self._only_contains_attribute_certs = only_contains_attribute_certs
2007 self._only_some_reasons = only_some_reasons
2008 self._full_name = full_name
2009 self._relative_name = relative_name
2010
2011 def __repr__(self) -> str:
2012 return (
2013 f"<IssuingDistributionPoint(full_name={self.full_name}, "
2014 f"relative_name={self.relative_name}, "
2015 f"only_contains_user_certs={self.only_contains_user_certs}, "
2016 f"only_contains_ca_certs={self.only_contains_ca_certs}, "
2017 f"only_some_reasons={self.only_some_reasons}, "
2018 f"indirect_crl={self.indirect_crl}, "
2019 "only_contains_attribute_certs="
2020 f"{self.only_contains_attribute_certs})>"
2021 )
2022
2023 def __eq__(self, other: object) -> bool:
2024 if not isinstance(other, IssuingDistributionPoint):
2025 return NotImplemented
2026
2027 return (
2028 self.full_name == other.full_name
2029 and self.relative_name == other.relative_name
2030 and self.only_contains_user_certs == other.only_contains_user_certs
2031 and self.only_contains_ca_certs == other.only_contains_ca_certs
2032 and self.only_some_reasons == other.only_some_reasons
2033 and self.indirect_crl == other.indirect_crl
2034 and self.only_contains_attribute_certs
2035 == other.only_contains_attribute_certs
2036 )
2037
2038 def __hash__(self) -> int:
2039 return hash(
2040 (
2041 self.full_name,
2042 self.relative_name,
2043 self.only_contains_user_certs,
2044 self.only_contains_ca_certs,
2045 self.only_some_reasons,
2046 self.indirect_crl,
2047 self.only_contains_attribute_certs,
2048 )
2049 )
2050
2051 @property
2052 def full_name(self) -> list[GeneralName] | None:
2053 return self._full_name
2054
2055 @property
2056 def relative_name(self) -> RelativeDistinguishedName | None:
2057 return self._relative_name
2058
2059 @property
2060 def only_contains_user_certs(self) -> bool:
2061 return self._only_contains_user_certs
2062
2063 @property
2064 def only_contains_ca_certs(self) -> bool:
2065 return self._only_contains_ca_certs
2066
2067 @property
2068 def only_some_reasons(
2069 self,
2070 ) -> frozenset[ReasonFlags] | None:
2071 return self._only_some_reasons
2072
2073 @property
2074 def indirect_crl(self) -> bool:
2075 return self._indirect_crl
2076
2077 @property
2078 def only_contains_attribute_certs(self) -> bool:
2079 return self._only_contains_attribute_certs
2080
2081 def public_bytes(self) -> bytes:
2082 return rust_x509.encode_extension_value(self)
2083
2084
2085class MSCertificateTemplate(ExtensionType):
2086 oid = ExtensionOID.MS_CERTIFICATE_TEMPLATE
2087
2088 def __init__(
2089 self,
2090 template_id: ObjectIdentifier,
2091 major_version: int | None,
2092 minor_version: int | None,
2093 ) -> None:
2094 if not isinstance(template_id, ObjectIdentifier):
2095 raise TypeError("oid must be an ObjectIdentifier")
2096 self._template_id = template_id
2097 if (
2098 major_version is not None and not isinstance(major_version, int)
2099 ) or (
2100 minor_version is not None and not isinstance(minor_version, int)
2101 ):
2102 raise TypeError(
2103 "major_version and minor_version must be integers or None"
2104 )
2105 self._major_version = major_version
2106 self._minor_version = minor_version
2107
2108 @property
2109 def template_id(self) -> ObjectIdentifier:
2110 return self._template_id
2111
2112 @property
2113 def major_version(self) -> int | None:
2114 return self._major_version
2115
2116 @property
2117 def minor_version(self) -> int | None:
2118 return self._minor_version
2119
2120 def __repr__(self) -> str:
2121 return (
2122 f"<MSCertificateTemplate(template_id={self.template_id}, "
2123 f"major_version={self.major_version}, "
2124 f"minor_version={self.minor_version})>"
2125 )
2126
2127 def __eq__(self, other: object) -> bool:
2128 if not isinstance(other, MSCertificateTemplate):
2129 return NotImplemented
2130
2131 return (
2132 self.template_id == other.template_id
2133 and self.major_version == other.major_version
2134 and self.minor_version == other.minor_version
2135 )
2136
2137 def __hash__(self) -> int:
2138 return hash((self.template_id, self.major_version, self.minor_version))
2139
2140 def public_bytes(self) -> bytes:
2141 return rust_x509.encode_extension_value(self)
2142
2143
2144class UnrecognizedExtension(ExtensionType):
2145 def __init__(self, oid: ObjectIdentifier, value: bytes) -> None:
2146 if not isinstance(oid, ObjectIdentifier):
2147 raise TypeError("oid must be an ObjectIdentifier")
2148 self._oid = oid
2149 self._value = value
2150
2151 @property
2152 def oid(self) -> ObjectIdentifier: # type: ignore[override]
2153 return self._oid
2154
2155 @property
2156 def value(self) -> bytes:
2157 return self._value
2158
2159 def __repr__(self) -> str:
2160 return (
2161 f"<UnrecognizedExtension(oid={self.oid}, "
2162 f"value={self.value!r})>"
2163 )
2164
2165 def __eq__(self, other: object) -> bool:
2166 if not isinstance(other, UnrecognizedExtension):
2167 return NotImplemented
2168
2169 return self.oid == other.oid and self.value == other.value
2170
2171 def __hash__(self) -> int:
2172 return hash((self.oid, self.value))
2173
2174 def public_bytes(self) -> bytes:
2175 return self.value