Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cryptography/x509/extensions.py: 45%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1182 statements  

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 

12from collections.abc import Iterable, Iterator 

13 

14from cryptography import utils 

15from cryptography.hazmat.bindings._rust import asn1 

16from cryptography.hazmat.bindings._rust import x509 as rust_x509 

17from cryptography.hazmat.primitives import constant_time, serialization 

18from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey 

19from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey 

20from cryptography.hazmat.primitives.asymmetric.types import ( 

21 CertificateIssuerPublicKeyTypes, 

22 CertificatePublicKeyTypes, 

23) 

24from cryptography.x509.certificate_transparency import ( 

25 SignedCertificateTimestamp, 

26) 

27from cryptography.x509.general_name import ( 

28 DirectoryName, 

29 DNSName, 

30 GeneralName, 

31 IPAddress, 

32 OtherName, 

33 RegisteredID, 

34 RFC822Name, 

35 UniformResourceIdentifier, 

36 _IPAddressTypes, 

37) 

38from cryptography.x509.name import Name, RelativeDistinguishedName 

39from cryptography.x509.oid import ( 

40 CRLEntryExtensionOID, 

41 ExtensionOID, 

42 ObjectIdentifier, 

43 OCSPExtensionOID, 

44) 

45 

46ExtensionTypeVar = typing.TypeVar( 

47 "ExtensionTypeVar", bound="ExtensionType", covariant=True 

48) 

49 

50 

51def _key_identifier_from_public_key( 

52 public_key: CertificatePublicKeyTypes, 

53) -> bytes: 

54 if isinstance(public_key, RSAPublicKey): 

55 data = public_key.public_bytes( 

56 serialization.Encoding.DER, 

57 serialization.PublicFormat.PKCS1, 

58 ) 

59 elif isinstance(public_key, EllipticCurvePublicKey): 

60 data = public_key.public_bytes( 

61 serialization.Encoding.X962, 

62 serialization.PublicFormat.UncompressedPoint, 

63 ) 

64 else: 

65 # This is a very slow way to do this. 

66 serialized = public_key.public_bytes( 

67 serialization.Encoding.DER, 

68 serialization.PublicFormat.SubjectPublicKeyInfo, 

69 ) 

70 data = asn1.parse_spki_for_data(serialized) 

71 

72 return hashlib.sha1(data).digest() 

73 

74 

75def _make_sequence_methods(field_name: str): 

76 def len_method(self) -> int: 

77 return len(getattr(self, field_name)) 

78 

79 def iter_method(self): 

80 return iter(getattr(self, field_name)) 

81 

82 def getitem_method(self, idx): 

83 return getattr(self, field_name)[idx] 

84 

85 return len_method, iter_method, getitem_method 

86 

87 

88class DuplicateExtension(Exception): 

89 def __init__(self, msg: str, oid: ObjectIdentifier) -> None: 

90 super().__init__(msg) 

91 self.oid = oid 

92 

93 

94class ExtensionNotFound(Exception): 

95 def __init__(self, msg: str, oid: ObjectIdentifier) -> None: 

96 super().__init__(msg) 

97 self.oid = oid 

98 

99 

100class ExtensionType(metaclass=abc.ABCMeta): 

101 oid: typing.ClassVar[ObjectIdentifier] 

102 

103 def public_bytes(self) -> bytes: 

104 """ 

105 Serializes the extension type to DER. 

106 """ 

107 raise NotImplementedError( 

108 f"public_bytes is not implemented for extension type {self!r}" 

109 ) 

110 

111 

112class Extensions: 

113 def __init__(self, extensions: Iterable[Extension[ExtensionType]]) -> None: 

114 self._extensions = list(extensions) 

115 

116 def get_extension_for_oid( 

117 self, oid: ObjectIdentifier 

118 ) -> Extension[ExtensionType]: 

119 for ext in self: 

120 if ext.oid == oid: 

121 return ext 

122 

123 raise ExtensionNotFound(f"No {oid} extension was found", oid) 

124 

125 def get_extension_for_class( 

126 self, extclass: type[ExtensionTypeVar] 

127 ) -> Extension[ExtensionTypeVar]: 

128 if extclass is UnrecognizedExtension: 

129 raise TypeError( 

130 "UnrecognizedExtension can't be used with " 

131 "get_extension_for_class because more than one instance of the" 

132 " class may be present." 

133 ) 

134 

135 for ext in self: 

136 if isinstance(ext.value, extclass): 

137 return ext 

138 

139 raise ExtensionNotFound( 

140 f"No {extclass} extension was found", extclass.oid 

141 ) 

142 

143 __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions") 

144 

145 def __repr__(self) -> str: 

146 return f"<Extensions({self._extensions})>" 

147 

148 

149class CRLNumber(ExtensionType): 

150 oid = ExtensionOID.CRL_NUMBER 

151 

152 def __init__(self, crl_number: int) -> None: 

153 if not isinstance(crl_number, int): 

154 raise TypeError("crl_number must be an integer") 

155 

156 self._crl_number = crl_number 

157 

158 def __eq__(self, other: object) -> bool: 

159 if not isinstance(other, CRLNumber): 

160 return NotImplemented 

161 

162 return self.crl_number == other.crl_number 

163 

164 def __hash__(self) -> int: 

165 return hash(self.crl_number) 

166 

167 def __repr__(self) -> str: 

168 return f"<CRLNumber({self.crl_number})>" 

169 

170 @property 

171 def crl_number(self) -> int: 

172 return self._crl_number 

173 

174 def public_bytes(self) -> bytes: 

175 return rust_x509.encode_extension_value(self) 

176 

177 

178class AuthorityKeyIdentifier(ExtensionType): 

179 oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER 

180 

181 def __init__( 

182 self, 

183 key_identifier: bytes | None, 

184 authority_cert_issuer: Iterable[GeneralName] | None, 

185 authority_cert_serial_number: int | None, 

186 ) -> None: 

187 if (authority_cert_issuer is None) != ( 

188 authority_cert_serial_number is None 

189 ): 

190 raise ValueError( 

191 "authority_cert_issuer and authority_cert_serial_number " 

192 "must both be present or both None" 

193 ) 

194 

195 if authority_cert_issuer is not None: 

196 authority_cert_issuer = list(authority_cert_issuer) 

197 if not all( 

198 isinstance(x, GeneralName) for x in authority_cert_issuer 

199 ): 

200 raise TypeError( 

201 "authority_cert_issuer must be a list of GeneralName " 

202 "objects" 

203 ) 

204 

205 if authority_cert_serial_number is not None and not isinstance( 

206 authority_cert_serial_number, int 

207 ): 

208 raise TypeError("authority_cert_serial_number must be an integer") 

209 

210 self._key_identifier = key_identifier 

211 self._authority_cert_issuer = authority_cert_issuer 

212 self._authority_cert_serial_number = authority_cert_serial_number 

213 

214 # This takes a subset of CertificatePublicKeyTypes because an issuer 

215 # cannot have an X25519/X448 key. This introduces some unfortunate 

216 # asymmetry that requires typing users to explicitly 

217 # narrow their type, but we should make this accurate and not just 

218 # convenient. 

219 @classmethod 

220 def from_issuer_public_key( 

221 cls, public_key: CertificateIssuerPublicKeyTypes 

222 ) -> AuthorityKeyIdentifier: 

223 digest = _key_identifier_from_public_key(public_key) 

224 return cls( 

225 key_identifier=digest, 

226 authority_cert_issuer=None, 

227 authority_cert_serial_number=None, 

228 ) 

229 

230 @classmethod 

231 def from_issuer_subject_key_identifier( 

232 cls, ski: SubjectKeyIdentifier 

233 ) -> AuthorityKeyIdentifier: 

234 return cls( 

235 key_identifier=ski.digest, 

236 authority_cert_issuer=None, 

237 authority_cert_serial_number=None, 

238 ) 

239 

240 def __repr__(self) -> str: 

241 return ( 

242 f"<AuthorityKeyIdentifier(key_identifier={self.key_identifier!r}, " 

243 f"authority_cert_issuer={self.authority_cert_issuer}, " 

244 f"authority_cert_serial_number={self.authority_cert_serial_number}" 

245 ")>" 

246 ) 

247 

248 def __eq__(self, other: object) -> bool: 

249 if not isinstance(other, AuthorityKeyIdentifier): 

250 return NotImplemented 

251 

252 return ( 

253 self.key_identifier == other.key_identifier 

254 and self.authority_cert_issuer == other.authority_cert_issuer 

255 and self.authority_cert_serial_number 

256 == other.authority_cert_serial_number 

257 ) 

258 

259 def __hash__(self) -> int: 

260 if self.authority_cert_issuer is None: 

261 aci = None 

262 else: 

263 aci = tuple(self.authority_cert_issuer) 

264 return hash( 

265 (self.key_identifier, aci, self.authority_cert_serial_number) 

266 ) 

267 

268 @property 

269 def key_identifier(self) -> bytes | None: 

270 return self._key_identifier 

271 

272 @property 

273 def authority_cert_issuer( 

274 self, 

275 ) -> list[GeneralName] | None: 

276 return self._authority_cert_issuer 

277 

278 @property 

279 def authority_cert_serial_number(self) -> int | None: 

280 return self._authority_cert_serial_number 

281 

282 def public_bytes(self) -> bytes: 

283 return rust_x509.encode_extension_value(self) 

284 

285 

286class SubjectKeyIdentifier(ExtensionType): 

287 oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER 

288 

289 def __init__(self, digest: bytes) -> None: 

290 self._digest = digest 

291 

292 @classmethod 

293 def from_public_key( 

294 cls, public_key: CertificatePublicKeyTypes 

295 ) -> SubjectKeyIdentifier: 

296 return cls(_key_identifier_from_public_key(public_key)) 

297 

298 @property 

299 def digest(self) -> bytes: 

300 return self._digest 

301 

302 @property 

303 def key_identifier(self) -> bytes: 

304 return self._digest 

305 

306 def __repr__(self) -> str: 

307 return f"<SubjectKeyIdentifier(digest={self.digest!r})>" 

308 

309 def __eq__(self, other: object) -> bool: 

310 if not isinstance(other, SubjectKeyIdentifier): 

311 return NotImplemented 

312 

313 return constant_time.bytes_eq(self.digest, other.digest) 

314 

315 def __hash__(self) -> int: 

316 return hash(self.digest) 

317 

318 def public_bytes(self) -> bytes: 

319 return rust_x509.encode_extension_value(self) 

320 

321 

322class AuthorityInformationAccess(ExtensionType): 

323 oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS 

324 

325 def __init__(self, descriptions: Iterable[AccessDescription]) -> None: 

326 descriptions = list(descriptions) 

327 if not all(isinstance(x, AccessDescription) for x in descriptions): 

328 raise TypeError( 

329 "Every item in the descriptions list must be an " 

330 "AccessDescription" 

331 ) 

332 

333 self._descriptions = descriptions 

334 

335 __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") 

336 

337 def __repr__(self) -> str: 

338 return f"<AuthorityInformationAccess({self._descriptions})>" 

339 

340 def __eq__(self, other: object) -> bool: 

341 if not isinstance(other, AuthorityInformationAccess): 

342 return NotImplemented 

343 

344 return self._descriptions == other._descriptions 

345 

346 def __hash__(self) -> int: 

347 return hash(tuple(self._descriptions)) 

348 

349 def public_bytes(self) -> bytes: 

350 return rust_x509.encode_extension_value(self) 

351 

352 

353class SubjectInformationAccess(ExtensionType): 

354 oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS 

355 

356 def __init__(self, descriptions: Iterable[AccessDescription]) -> None: 

357 descriptions = list(descriptions) 

358 if not all(isinstance(x, AccessDescription) for x in descriptions): 

359 raise TypeError( 

360 "Every item in the descriptions list must be an " 

361 "AccessDescription" 

362 ) 

363 

364 self._descriptions = descriptions 

365 

366 __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") 

367 

368 def __repr__(self) -> str: 

369 return f"<SubjectInformationAccess({self._descriptions})>" 

370 

371 def __eq__(self, other: object) -> bool: 

372 if not isinstance(other, SubjectInformationAccess): 

373 return NotImplemented 

374 

375 return self._descriptions == other._descriptions 

376 

377 def __hash__(self) -> int: 

378 return hash(tuple(self._descriptions)) 

379 

380 def public_bytes(self) -> bytes: 

381 return rust_x509.encode_extension_value(self) 

382 

383 

384class AccessDescription: 

385 def __init__( 

386 self, access_method: ObjectIdentifier, access_location: GeneralName 

387 ) -> None: 

388 if not isinstance(access_method, ObjectIdentifier): 

389 raise TypeError("access_method must be an ObjectIdentifier") 

390 

391 if not isinstance(access_location, GeneralName): 

392 raise TypeError("access_location must be a GeneralName") 

393 

394 self._access_method = access_method 

395 self._access_location = access_location 

396 

397 def __repr__(self) -> str: 

398 return ( 

399 f"<AccessDescription(access_method={self.access_method}, " 

400 f"access_location={self.access_location})>" 

401 ) 

402 

403 def __eq__(self, other: object) -> bool: 

404 if not isinstance(other, AccessDescription): 

405 return NotImplemented 

406 

407 return ( 

408 self.access_method == other.access_method 

409 and self.access_location == other.access_location 

410 ) 

411 

412 def __hash__(self) -> int: 

413 return hash((self.access_method, self.access_location)) 

414 

415 @property 

416 def access_method(self) -> ObjectIdentifier: 

417 return self._access_method 

418 

419 @property 

420 def access_location(self) -> GeneralName: 

421 return self._access_location 

422 

423 

424class BasicConstraints(ExtensionType): 

425 oid = ExtensionOID.BASIC_CONSTRAINTS 

426 

427 def __init__(self, ca: bool, path_length: int | None) -> None: 

428 if not isinstance(ca, bool): 

429 raise TypeError("ca must be a boolean value") 

430 

431 if path_length is not None and not ca: 

432 raise ValueError("path_length must be None when ca is False") 

433 

434 if path_length is not None and ( 

435 not isinstance(path_length, int) or path_length < 0 

436 ): 

437 raise TypeError( 

438 "path_length must be a non-negative integer or None" 

439 ) 

440 

441 self._ca = ca 

442 self._path_length = path_length 

443 

444 @property 

445 def ca(self) -> bool: 

446 return self._ca 

447 

448 @property 

449 def path_length(self) -> int | None: 

450 return self._path_length 

451 

452 def __repr__(self) -> str: 

453 return ( 

454 f"<BasicConstraints(ca={self.ca}, path_length={self.path_length})>" 

455 ) 

456 

457 def __eq__(self, other: object) -> bool: 

458 if not isinstance(other, BasicConstraints): 

459 return NotImplemented 

460 

461 return self.ca == other.ca and self.path_length == other.path_length 

462 

463 def __hash__(self) -> int: 

464 return hash((self.ca, self.path_length)) 

465 

466 def public_bytes(self) -> bytes: 

467 return rust_x509.encode_extension_value(self) 

468 

469 

470class DeltaCRLIndicator(ExtensionType): 

471 oid = ExtensionOID.DELTA_CRL_INDICATOR 

472 

473 def __init__(self, crl_number: int) -> None: 

474 if not isinstance(crl_number, int): 

475 raise TypeError("crl_number must be an integer") 

476 

477 self._crl_number = crl_number 

478 

479 @property 

480 def crl_number(self) -> int: 

481 return self._crl_number 

482 

483 def __eq__(self, other: object) -> bool: 

484 if not isinstance(other, DeltaCRLIndicator): 

485 return NotImplemented 

486 

487 return self.crl_number == other.crl_number 

488 

489 def __hash__(self) -> int: 

490 return hash(self.crl_number) 

491 

492 def __repr__(self) -> str: 

493 return f"<DeltaCRLIndicator(crl_number={self.crl_number})>" 

494 

495 def public_bytes(self) -> bytes: 

496 return rust_x509.encode_extension_value(self) 

497 

498 

499class CRLDistributionPoints(ExtensionType): 

500 oid = ExtensionOID.CRL_DISTRIBUTION_POINTS 

501 

502 def __init__( 

503 self, distribution_points: Iterable[DistributionPoint] 

504 ) -> None: 

505 distribution_points = list(distribution_points) 

506 if not all( 

507 isinstance(x, DistributionPoint) for x in distribution_points 

508 ): 

509 raise TypeError( 

510 "distribution_points must be a list of DistributionPoint " 

511 "objects" 

512 ) 

513 

514 self._distribution_points = distribution_points 

515 

516 __len__, __iter__, __getitem__ = _make_sequence_methods( 

517 "_distribution_points" 

518 ) 

519 

520 def __repr__(self) -> str: 

521 return f"<CRLDistributionPoints({self._distribution_points})>" 

522 

523 def __eq__(self, other: object) -> bool: 

524 if not isinstance(other, CRLDistributionPoints): 

525 return NotImplemented 

526 

527 return self._distribution_points == other._distribution_points 

528 

529 def __hash__(self) -> int: 

530 return hash(tuple(self._distribution_points)) 

531 

532 def public_bytes(self) -> bytes: 

533 return rust_x509.encode_extension_value(self) 

534 

535 

536class FreshestCRL(ExtensionType): 

537 oid = ExtensionOID.FRESHEST_CRL 

538 

539 def __init__( 

540 self, distribution_points: Iterable[DistributionPoint] 

541 ) -> None: 

542 distribution_points = list(distribution_points) 

543 if not all( 

544 isinstance(x, DistributionPoint) for x in distribution_points 

545 ): 

546 raise TypeError( 

547 "distribution_points must be a list of DistributionPoint " 

548 "objects" 

549 ) 

550 

551 self._distribution_points = distribution_points 

552 

553 __len__, __iter__, __getitem__ = _make_sequence_methods( 

554 "_distribution_points" 

555 ) 

556 

557 def __repr__(self) -> str: 

558 return f"<FreshestCRL({self._distribution_points})>" 

559 

560 def __eq__(self, other: object) -> bool: 

561 if not isinstance(other, FreshestCRL): 

562 return NotImplemented 

563 

564 return self._distribution_points == other._distribution_points 

565 

566 def __hash__(self) -> int: 

567 return hash(tuple(self._distribution_points)) 

568 

569 def public_bytes(self) -> bytes: 

570 return rust_x509.encode_extension_value(self) 

571 

572 

573class DistributionPoint: 

574 def __init__( 

575 self, 

576 full_name: Iterable[GeneralName] | None, 

577 relative_name: RelativeDistinguishedName | None, 

578 reasons: frozenset[ReasonFlags] | None, 

579 crl_issuer: Iterable[GeneralName] | None, 

580 ) -> None: 

581 if full_name and relative_name: 

582 raise ValueError( 

583 "You cannot provide both full_name and relative_name, at " 

584 "least one must be None." 

585 ) 

586 if not full_name and not relative_name and not crl_issuer: 

587 raise ValueError( 

588 "Either full_name, relative_name or crl_issuer must be " 

589 "provided." 

590 ) 

591 

592 if full_name is not None: 

593 full_name = list(full_name) 

594 if not all(isinstance(x, GeneralName) for x in full_name): 

595 raise TypeError( 

596 "full_name must be a list of GeneralName objects" 

597 ) 

598 

599 if relative_name: 

600 if not isinstance(relative_name, RelativeDistinguishedName): 

601 raise TypeError( 

602 "relative_name must be a RelativeDistinguishedName" 

603 ) 

604 

605 if crl_issuer is not None: 

606 crl_issuer = list(crl_issuer) 

607 if not all(isinstance(x, GeneralName) for x in crl_issuer): 

608 raise TypeError( 

609 "crl_issuer must be None or a list of general names" 

610 ) 

611 

612 if reasons and ( 

613 not isinstance(reasons, frozenset) 

614 or not all(isinstance(x, ReasonFlags) for x in reasons) 

615 ): 

616 raise TypeError("reasons must be None or frozenset of ReasonFlags") 

617 

618 if reasons and ( 

619 ReasonFlags.unspecified in reasons 

620 or ReasonFlags.remove_from_crl in reasons 

621 ): 

622 raise ValueError( 

623 "unspecified and remove_from_crl are not valid reasons in a " 

624 "DistributionPoint" 

625 ) 

626 

627 self._full_name = full_name 

628 self._relative_name = relative_name 

629 self._reasons = reasons 

630 self._crl_issuer = crl_issuer 

631 

632 def __repr__(self) -> str: 

633 return ( 

634 "<DistributionPoint(full_name={0.full_name}, relative_name={0.rela" 

635 "tive_name}, reasons={0.reasons}, " 

636 "crl_issuer={0.crl_issuer})>".format(self) 

637 ) 

638 

639 def __eq__(self, other: object) -> bool: 

640 if not isinstance(other, DistributionPoint): 

641 return NotImplemented 

642 

643 return ( 

644 self.full_name == other.full_name 

645 and self.relative_name == other.relative_name 

646 and self.reasons == other.reasons 

647 and self.crl_issuer == other.crl_issuer 

648 ) 

649 

650 def __hash__(self) -> int: 

651 if self.full_name is not None: 

652 fn: tuple[GeneralName, ...] | None = tuple(self.full_name) 

653 else: 

654 fn = None 

655 

656 if self.crl_issuer is not None: 

657 crl_issuer: tuple[GeneralName, ...] | None = tuple(self.crl_issuer) 

658 else: 

659 crl_issuer = None 

660 

661 return hash((fn, self.relative_name, self.reasons, crl_issuer)) 

662 

663 @property 

664 def full_name(self) -> list[GeneralName] | None: 

665 return self._full_name 

666 

667 @property 

668 def relative_name(self) -> RelativeDistinguishedName | None: 

669 return self._relative_name 

670 

671 @property 

672 def reasons(self) -> frozenset[ReasonFlags] | None: 

673 return self._reasons 

674 

675 @property 

676 def crl_issuer(self) -> list[GeneralName] | None: 

677 return self._crl_issuer 

678 

679 

680class ReasonFlags(utils.Enum): 

681 unspecified = "unspecified" 

682 key_compromise = "keyCompromise" 

683 ca_compromise = "cACompromise" 

684 affiliation_changed = "affiliationChanged" 

685 superseded = "superseded" 

686 cessation_of_operation = "cessationOfOperation" 

687 certificate_hold = "certificateHold" 

688 privilege_withdrawn = "privilegeWithdrawn" 

689 aa_compromise = "aACompromise" 

690 remove_from_crl = "removeFromCRL" 

691 

692 

693# These are distribution point bit string mappings. Not to be confused with 

694# CRLReason reason flags bit string mappings. 

695# ReasonFlags ::= BIT STRING { 

696# unused (0), 

697# keyCompromise (1), 

698# cACompromise (2), 

699# affiliationChanged (3), 

700# superseded (4), 

701# cessationOfOperation (5), 

702# certificateHold (6), 

703# privilegeWithdrawn (7), 

704# aACompromise (8) } 

705_REASON_BIT_MAPPING = { 

706 1: ReasonFlags.key_compromise, 

707 2: ReasonFlags.ca_compromise, 

708 3: ReasonFlags.affiliation_changed, 

709 4: ReasonFlags.superseded, 

710 5: ReasonFlags.cessation_of_operation, 

711 6: ReasonFlags.certificate_hold, 

712 7: ReasonFlags.privilege_withdrawn, 

713 8: ReasonFlags.aa_compromise, 

714} 

715 

716_CRLREASONFLAGS = { 

717 ReasonFlags.key_compromise: 1, 

718 ReasonFlags.ca_compromise: 2, 

719 ReasonFlags.affiliation_changed: 3, 

720 ReasonFlags.superseded: 4, 

721 ReasonFlags.cessation_of_operation: 5, 

722 ReasonFlags.certificate_hold: 6, 

723 ReasonFlags.privilege_withdrawn: 7, 

724 ReasonFlags.aa_compromise: 8, 

725} 

726 

727# CRLReason ::= ENUMERATED { 

728# unspecified (0), 

729# keyCompromise (1), 

730# cACompromise (2), 

731# affiliationChanged (3), 

732# superseded (4), 

733# cessationOfOperation (5), 

734# certificateHold (6), 

735# -- value 7 is not used 

736# removeFromCRL (8), 

737# privilegeWithdrawn (9), 

738# aACompromise (10) } 

739_CRL_ENTRY_REASON_ENUM_TO_CODE = { 

740 ReasonFlags.unspecified: 0, 

741 ReasonFlags.key_compromise: 1, 

742 ReasonFlags.ca_compromise: 2, 

743 ReasonFlags.affiliation_changed: 3, 

744 ReasonFlags.superseded: 4, 

745 ReasonFlags.cessation_of_operation: 5, 

746 ReasonFlags.certificate_hold: 6, 

747 ReasonFlags.remove_from_crl: 8, 

748 ReasonFlags.privilege_withdrawn: 9, 

749 ReasonFlags.aa_compromise: 10, 

750} 

751 

752 

753class PolicyConstraints(ExtensionType): 

754 oid = ExtensionOID.POLICY_CONSTRAINTS 

755 

756 def __init__( 

757 self, 

758 require_explicit_policy: int | None, 

759 inhibit_policy_mapping: int | None, 

760 ) -> None: 

761 if require_explicit_policy is not None and not isinstance( 

762 require_explicit_policy, int 

763 ): 

764 raise TypeError( 

765 "require_explicit_policy must be a non-negative integer or " 

766 "None" 

767 ) 

768 

769 if inhibit_policy_mapping is not None and not isinstance( 

770 inhibit_policy_mapping, int 

771 ): 

772 raise TypeError( 

773 "inhibit_policy_mapping must be a non-negative integer or None" 

774 ) 

775 

776 if inhibit_policy_mapping is None and require_explicit_policy is None: 

777 raise ValueError( 

778 "At least one of require_explicit_policy and " 

779 "inhibit_policy_mapping must not be None" 

780 ) 

781 

782 self._require_explicit_policy = require_explicit_policy 

783 self._inhibit_policy_mapping = inhibit_policy_mapping 

784 

785 def __repr__(self) -> str: 

786 return ( 

787 "<PolicyConstraints(require_explicit_policy={0.require_explicit" 

788 "_policy}, inhibit_policy_mapping={0.inhibit_policy_" 

789 "mapping})>".format(self) 

790 ) 

791 

792 def __eq__(self, other: object) -> bool: 

793 if not isinstance(other, PolicyConstraints): 

794 return NotImplemented 

795 

796 return ( 

797 self.require_explicit_policy == other.require_explicit_policy 

798 and self.inhibit_policy_mapping == other.inhibit_policy_mapping 

799 ) 

800 

801 def __hash__(self) -> int: 

802 return hash( 

803 (self.require_explicit_policy, self.inhibit_policy_mapping) 

804 ) 

805 

806 @property 

807 def require_explicit_policy(self) -> int | None: 

808 return self._require_explicit_policy 

809 

810 @property 

811 def inhibit_policy_mapping(self) -> int | None: 

812 return self._inhibit_policy_mapping 

813 

814 def public_bytes(self) -> bytes: 

815 return rust_x509.encode_extension_value(self) 

816 

817 

818class CertificatePolicies(ExtensionType): 

819 oid = ExtensionOID.CERTIFICATE_POLICIES 

820 

821 def __init__(self, policies: Iterable[PolicyInformation]) -> None: 

822 policies = list(policies) 

823 if not all(isinstance(x, PolicyInformation) for x in policies): 

824 raise TypeError( 

825 "Every item in the policies list must be a PolicyInformation" 

826 ) 

827 

828 self._policies = policies 

829 

830 __len__, __iter__, __getitem__ = _make_sequence_methods("_policies") 

831 

832 def __repr__(self) -> str: 

833 return f"<CertificatePolicies({self._policies})>" 

834 

835 def __eq__(self, other: object) -> bool: 

836 if not isinstance(other, CertificatePolicies): 

837 return NotImplemented 

838 

839 return self._policies == other._policies 

840 

841 def __hash__(self) -> int: 

842 return hash(tuple(self._policies)) 

843 

844 def public_bytes(self) -> bytes: 

845 return rust_x509.encode_extension_value(self) 

846 

847 

848class PolicyInformation: 

849 def __init__( 

850 self, 

851 policy_identifier: ObjectIdentifier, 

852 policy_qualifiers: Iterable[str | UserNotice] | None, 

853 ) -> None: 

854 if not isinstance(policy_identifier, ObjectIdentifier): 

855 raise TypeError("policy_identifier must be an ObjectIdentifier") 

856 

857 self._policy_identifier = policy_identifier 

858 

859 if policy_qualifiers is not None: 

860 policy_qualifiers = list(policy_qualifiers) 

861 if not all( 

862 isinstance(x, (str, UserNotice)) for x in policy_qualifiers 

863 ): 

864 raise TypeError( 

865 "policy_qualifiers must be a list of strings and/or " 

866 "UserNotice objects or None" 

867 ) 

868 

869 self._policy_qualifiers = policy_qualifiers 

870 

871 def __repr__(self) -> str: 

872 return ( 

873 f"<PolicyInformation(policy_identifier={self.policy_identifier}, " 

874 f"policy_qualifiers={self.policy_qualifiers})>" 

875 ) 

876 

877 def __eq__(self, other: object) -> bool: 

878 if not isinstance(other, PolicyInformation): 

879 return NotImplemented 

880 

881 return ( 

882 self.policy_identifier == other.policy_identifier 

883 and self.policy_qualifiers == other.policy_qualifiers 

884 ) 

885 

886 def __hash__(self) -> int: 

887 if self.policy_qualifiers is not None: 

888 pq = tuple(self.policy_qualifiers) 

889 else: 

890 pq = None 

891 

892 return hash((self.policy_identifier, pq)) 

893 

894 @property 

895 def policy_identifier(self) -> ObjectIdentifier: 

896 return self._policy_identifier 

897 

898 @property 

899 def policy_qualifiers( 

900 self, 

901 ) -> list[str | UserNotice] | None: 

902 return self._policy_qualifiers 

903 

904 

905class UserNotice: 

906 def __init__( 

907 self, 

908 notice_reference: NoticeReference | None, 

909 explicit_text: str | None, 

910 ) -> None: 

911 if notice_reference and not isinstance( 

912 notice_reference, NoticeReference 

913 ): 

914 raise TypeError( 

915 "notice_reference must be None or a NoticeReference" 

916 ) 

917 

918 self._notice_reference = notice_reference 

919 self._explicit_text = explicit_text 

920 

921 def __repr__(self) -> str: 

922 return ( 

923 f"<UserNotice(notice_reference={self.notice_reference}, " 

924 f"explicit_text={self.explicit_text!r})>" 

925 ) 

926 

927 def __eq__(self, other: object) -> bool: 

928 if not isinstance(other, UserNotice): 

929 return NotImplemented 

930 

931 return ( 

932 self.notice_reference == other.notice_reference 

933 and self.explicit_text == other.explicit_text 

934 ) 

935 

936 def __hash__(self) -> int: 

937 return hash((self.notice_reference, self.explicit_text)) 

938 

939 @property 

940 def notice_reference(self) -> NoticeReference | None: 

941 return self._notice_reference 

942 

943 @property 

944 def explicit_text(self) -> str | None: 

945 return self._explicit_text 

946 

947 

948class NoticeReference: 

949 def __init__( 

950 self, 

951 organization: str | None, 

952 notice_numbers: Iterable[int], 

953 ) -> None: 

954 self._organization = organization 

955 notice_numbers = list(notice_numbers) 

956 if not all(isinstance(x, int) for x in notice_numbers): 

957 raise TypeError("notice_numbers must be a list of integers") 

958 

959 self._notice_numbers = notice_numbers 

960 

961 def __repr__(self) -> str: 

962 return ( 

963 f"<NoticeReference(organization={self.organization!r}, " 

964 f"notice_numbers={self.notice_numbers})>" 

965 ) 

966 

967 def __eq__(self, other: object) -> bool: 

968 if not isinstance(other, NoticeReference): 

969 return NotImplemented 

970 

971 return ( 

972 self.organization == other.organization 

973 and self.notice_numbers == other.notice_numbers 

974 ) 

975 

976 def __hash__(self) -> int: 

977 return hash((self.organization, tuple(self.notice_numbers))) 

978 

979 @property 

980 def organization(self) -> str | None: 

981 return self._organization 

982 

983 @property 

984 def notice_numbers(self) -> list[int]: 

985 return self._notice_numbers 

986 

987 

988class ExtendedKeyUsage(ExtensionType): 

989 oid = ExtensionOID.EXTENDED_KEY_USAGE 

990 

991 def __init__(self, usages: Iterable[ObjectIdentifier]) -> None: 

992 usages = list(usages) 

993 if not all(isinstance(x, ObjectIdentifier) for x in usages): 

994 raise TypeError( 

995 "Every item in the usages list must be an ObjectIdentifier" 

996 ) 

997 

998 self._usages = usages 

999 

1000 __len__, __iter__, __getitem__ = _make_sequence_methods("_usages") 

1001 

1002 def __repr__(self) -> str: 

1003 return f"<ExtendedKeyUsage({self._usages})>" 

1004 

1005 def __eq__(self, other: object) -> bool: 

1006 if not isinstance(other, ExtendedKeyUsage): 

1007 return NotImplemented 

1008 

1009 return self._usages == other._usages 

1010 

1011 def __hash__(self) -> int: 

1012 return hash(tuple(self._usages)) 

1013 

1014 def public_bytes(self) -> bytes: 

1015 return rust_x509.encode_extension_value(self) 

1016 

1017 

1018class OCSPNoCheck(ExtensionType): 

1019 oid = ExtensionOID.OCSP_NO_CHECK 

1020 

1021 def __eq__(self, other: object) -> bool: 

1022 if not isinstance(other, OCSPNoCheck): 

1023 return NotImplemented 

1024 

1025 return True 

1026 

1027 def __hash__(self) -> int: 

1028 return hash(OCSPNoCheck) 

1029 

1030 def __repr__(self) -> str: 

1031 return "<OCSPNoCheck()>" 

1032 

1033 def public_bytes(self) -> bytes: 

1034 return rust_x509.encode_extension_value(self) 

1035 

1036 

1037class PrecertPoison(ExtensionType): 

1038 oid = ExtensionOID.PRECERT_POISON 

1039 

1040 def __eq__(self, other: object) -> bool: 

1041 if not isinstance(other, PrecertPoison): 

1042 return NotImplemented 

1043 

1044 return True 

1045 

1046 def __hash__(self) -> int: 

1047 return hash(PrecertPoison) 

1048 

1049 def __repr__(self) -> str: 

1050 return "<PrecertPoison()>" 

1051 

1052 def public_bytes(self) -> bytes: 

1053 return rust_x509.encode_extension_value(self) 

1054 

1055 

1056class TLSFeature(ExtensionType): 

1057 oid = ExtensionOID.TLS_FEATURE 

1058 

1059 def __init__(self, features: Iterable[TLSFeatureType]) -> None: 

1060 features = list(features) 

1061 if ( 

1062 not all(isinstance(x, TLSFeatureType) for x in features) 

1063 or len(features) == 0 

1064 ): 

1065 raise TypeError( 

1066 "features must be a list of elements from the TLSFeatureType " 

1067 "enum" 

1068 ) 

1069 

1070 self._features = features 

1071 

1072 __len__, __iter__, __getitem__ = _make_sequence_methods("_features") 

1073 

1074 def __repr__(self) -> str: 

1075 return f"<TLSFeature(features={self._features})>" 

1076 

1077 def __eq__(self, other: object) -> bool: 

1078 if not isinstance(other, TLSFeature): 

1079 return NotImplemented 

1080 

1081 return self._features == other._features 

1082 

1083 def __hash__(self) -> int: 

1084 return hash(tuple(self._features)) 

1085 

1086 def public_bytes(self) -> bytes: 

1087 return rust_x509.encode_extension_value(self) 

1088 

1089 

1090class TLSFeatureType(utils.Enum): 

1091 # status_request is defined in RFC 6066 and is used for what is commonly 

1092 # called OCSP Must-Staple when present in the TLS Feature extension in an 

1093 # X.509 certificate. 

1094 status_request = 5 

1095 # status_request_v2 is defined in RFC 6961 and allows multiple OCSP 

1096 # responses to be provided. It is not currently in use by clients or 

1097 # servers. 

1098 status_request_v2 = 17 

1099 

1100 

1101_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType} 

1102 

1103 

1104class InhibitAnyPolicy(ExtensionType): 

1105 oid = ExtensionOID.INHIBIT_ANY_POLICY 

1106 

1107 def __init__(self, skip_certs: int) -> None: 

1108 if not isinstance(skip_certs, int): 

1109 raise TypeError("skip_certs must be an integer") 

1110 

1111 if skip_certs < 0: 

1112 raise ValueError("skip_certs must be a non-negative integer") 

1113 

1114 self._skip_certs = skip_certs 

1115 

1116 def __repr__(self) -> str: 

1117 return f"<InhibitAnyPolicy(skip_certs={self.skip_certs})>" 

1118 

1119 def __eq__(self, other: object) -> bool: 

1120 if not isinstance(other, InhibitAnyPolicy): 

1121 return NotImplemented 

1122 

1123 return self.skip_certs == other.skip_certs 

1124 

1125 def __hash__(self) -> int: 

1126 return hash(self.skip_certs) 

1127 

1128 @property 

1129 def skip_certs(self) -> int: 

1130 return self._skip_certs 

1131 

1132 def public_bytes(self) -> bytes: 

1133 return rust_x509.encode_extension_value(self) 

1134 

1135 

1136class KeyUsage(ExtensionType): 

1137 oid = ExtensionOID.KEY_USAGE 

1138 

1139 def __init__( 

1140 self, 

1141 digital_signature: bool, 

1142 content_commitment: bool, 

1143 key_encipherment: bool, 

1144 data_encipherment: bool, 

1145 key_agreement: bool, 

1146 key_cert_sign: bool, 

1147 crl_sign: bool, 

1148 encipher_only: bool, 

1149 decipher_only: bool, 

1150 ) -> None: 

1151 if not key_agreement and (encipher_only or decipher_only): 

1152 raise ValueError( 

1153 "encipher_only and decipher_only can only be true when " 

1154 "key_agreement is true" 

1155 ) 

1156 

1157 self._digital_signature = digital_signature 

1158 self._content_commitment = content_commitment 

1159 self._key_encipherment = key_encipherment 

1160 self._data_encipherment = data_encipherment 

1161 self._key_agreement = key_agreement 

1162 self._key_cert_sign = key_cert_sign 

1163 self._crl_sign = crl_sign 

1164 self._encipher_only = encipher_only 

1165 self._decipher_only = decipher_only 

1166 

1167 @property 

1168 def digital_signature(self) -> bool: 

1169 return self._digital_signature 

1170 

1171 @property 

1172 def content_commitment(self) -> bool: 

1173 return self._content_commitment 

1174 

1175 @property 

1176 def key_encipherment(self) -> bool: 

1177 return self._key_encipherment 

1178 

1179 @property 

1180 def data_encipherment(self) -> bool: 

1181 return self._data_encipherment 

1182 

1183 @property 

1184 def key_agreement(self) -> bool: 

1185 return self._key_agreement 

1186 

1187 @property 

1188 def key_cert_sign(self) -> bool: 

1189 return self._key_cert_sign 

1190 

1191 @property 

1192 def crl_sign(self) -> bool: 

1193 return self._crl_sign 

1194 

1195 @property 

1196 def encipher_only(self) -> bool: 

1197 if not self.key_agreement: 

1198 raise ValueError( 

1199 "encipher_only is undefined unless key_agreement is true" 

1200 ) 

1201 else: 

1202 return self._encipher_only 

1203 

1204 @property 

1205 def decipher_only(self) -> bool: 

1206 if not self.key_agreement: 

1207 raise ValueError( 

1208 "decipher_only is undefined unless key_agreement is true" 

1209 ) 

1210 else: 

1211 return self._decipher_only 

1212 

1213 def __repr__(self) -> str: 

1214 try: 

1215 encipher_only = self.encipher_only 

1216 decipher_only = self.decipher_only 

1217 except ValueError: 

1218 # Users found None confusing because even though encipher/decipher 

1219 # have no meaning unless key_agreement is true, to construct an 

1220 # instance of the class you still need to pass False. 

1221 encipher_only = False 

1222 decipher_only = False 

1223 

1224 return ( 

1225 f"<KeyUsage(digital_signature={self.digital_signature}, " 

1226 f"content_commitment={self.content_commitment}, " 

1227 f"key_encipherment={self.key_encipherment}, " 

1228 f"data_encipherment={self.data_encipherment}, " 

1229 f"key_agreement={self.key_agreement}, " 

1230 f"key_cert_sign={self.key_cert_sign}, crl_sign={self.crl_sign}, " 

1231 f"encipher_only={encipher_only}, decipher_only={decipher_only})>" 

1232 ) 

1233 

1234 def __eq__(self, other: object) -> bool: 

1235 if not isinstance(other, KeyUsage): 

1236 return NotImplemented 

1237 

1238 return ( 

1239 self.digital_signature == other.digital_signature 

1240 and self.content_commitment == other.content_commitment 

1241 and self.key_encipherment == other.key_encipherment 

1242 and self.data_encipherment == other.data_encipherment 

1243 and self.key_agreement == other.key_agreement 

1244 and self.key_cert_sign == other.key_cert_sign 

1245 and self.crl_sign == other.crl_sign 

1246 and self._encipher_only == other._encipher_only 

1247 and self._decipher_only == other._decipher_only 

1248 ) 

1249 

1250 def __hash__(self) -> int: 

1251 return hash( 

1252 ( 

1253 self.digital_signature, 

1254 self.content_commitment, 

1255 self.key_encipherment, 

1256 self.data_encipherment, 

1257 self.key_agreement, 

1258 self.key_cert_sign, 

1259 self.crl_sign, 

1260 self._encipher_only, 

1261 self._decipher_only, 

1262 ) 

1263 ) 

1264 

1265 def public_bytes(self) -> bytes: 

1266 return rust_x509.encode_extension_value(self) 

1267 

1268 

1269class PrivateKeyUsagePeriod(ExtensionType): 

1270 oid = ExtensionOID.PRIVATE_KEY_USAGE_PERIOD 

1271 

1272 def __init__( 

1273 self, 

1274 not_before: datetime.datetime | None, 

1275 not_after: datetime.datetime | None, 

1276 ) -> None: 

1277 if ( 

1278 not isinstance(not_before, datetime.datetime) 

1279 and not_before is not None 

1280 ): 

1281 raise TypeError("not_before must be a datetime.datetime or None") 

1282 

1283 if ( 

1284 not isinstance(not_after, datetime.datetime) 

1285 and not_after is not None 

1286 ): 

1287 raise TypeError("not_after must be a datetime.datetime or None") 

1288 

1289 if not_before is None and not_after is None: 

1290 raise ValueError( 

1291 "At least one of not_before and not_after must not be None" 

1292 ) 

1293 

1294 if ( 

1295 not_before is not None 

1296 and not_after is not None 

1297 and not_before > not_after 

1298 ): 

1299 raise ValueError("not_before must be before not_after") 

1300 

1301 self._not_before = not_before 

1302 self._not_after = not_after 

1303 

1304 @property 

1305 def not_before(self) -> datetime.datetime | None: 

1306 return self._not_before 

1307 

1308 @property 

1309 def not_after(self) -> datetime.datetime | None: 

1310 return self._not_after 

1311 

1312 def __repr__(self) -> str: 

1313 return ( 

1314 f"<PrivateKeyUsagePeriod(not_before={self.not_before}, " 

1315 f"not_after={self.not_after})>" 

1316 ) 

1317 

1318 def __eq__(self, other: object) -> bool: 

1319 if not isinstance(other, PrivateKeyUsagePeriod): 

1320 return NotImplemented 

1321 

1322 return ( 

1323 self.not_before == other.not_before 

1324 and self.not_after == other.not_after 

1325 ) 

1326 

1327 def __hash__(self) -> int: 

1328 return hash((self.not_before, self.not_after)) 

1329 

1330 def public_bytes(self) -> bytes: 

1331 return rust_x509.encode_extension_value(self) 

1332 

1333 

1334class NameConstraints(ExtensionType): 

1335 oid = ExtensionOID.NAME_CONSTRAINTS 

1336 

1337 def __init__( 

1338 self, 

1339 permitted_subtrees: Iterable[GeneralName] | None, 

1340 excluded_subtrees: Iterable[GeneralName] | None, 

1341 ) -> None: 

1342 if permitted_subtrees is not None: 

1343 permitted_subtrees = list(permitted_subtrees) 

1344 if not permitted_subtrees: 

1345 raise ValueError( 

1346 "permitted_subtrees must be a non-empty list or None" 

1347 ) 

1348 if not all(isinstance(x, GeneralName) for x in permitted_subtrees): 

1349 raise TypeError( 

1350 "permitted_subtrees must be a list of GeneralName objects " 

1351 "or None" 

1352 ) 

1353 

1354 self._validate_tree(permitted_subtrees) 

1355 

1356 if excluded_subtrees is not None: 

1357 excluded_subtrees = list(excluded_subtrees) 

1358 if not excluded_subtrees: 

1359 raise ValueError( 

1360 "excluded_subtrees must be a non-empty list or None" 

1361 ) 

1362 if not all(isinstance(x, GeneralName) for x in excluded_subtrees): 

1363 raise TypeError( 

1364 "excluded_subtrees must be a list of GeneralName objects " 

1365 "or None" 

1366 ) 

1367 

1368 self._validate_tree(excluded_subtrees) 

1369 

1370 if permitted_subtrees is None and excluded_subtrees is None: 

1371 raise ValueError( 

1372 "At least one of permitted_subtrees and excluded_subtrees " 

1373 "must not be None" 

1374 ) 

1375 

1376 self._permitted_subtrees = permitted_subtrees 

1377 self._excluded_subtrees = excluded_subtrees 

1378 

1379 def __eq__(self, other: object) -> bool: 

1380 if not isinstance(other, NameConstraints): 

1381 return NotImplemented 

1382 

1383 return ( 

1384 self.excluded_subtrees == other.excluded_subtrees 

1385 and self.permitted_subtrees == other.permitted_subtrees 

1386 ) 

1387 

1388 def _validate_tree(self, tree: Iterable[GeneralName]) -> None: 

1389 self._validate_ip_name(tree) 

1390 self._validate_dns_name(tree) 

1391 

1392 def _validate_ip_name(self, tree: Iterable[GeneralName]) -> None: 

1393 if any( 

1394 isinstance(name, IPAddress) 

1395 and not isinstance( 

1396 name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network) 

1397 ) 

1398 for name in tree 

1399 ): 

1400 raise TypeError( 

1401 "IPAddress name constraints must be an IPv4Network or" 

1402 " IPv6Network object" 

1403 ) 

1404 

1405 def _validate_dns_name(self, tree: Iterable[GeneralName]) -> None: 

1406 if any( 

1407 isinstance(name, DNSName) and "*" in name.value for name in tree 

1408 ): 

1409 raise ValueError( 

1410 "DNSName name constraints must not contain the '*' wildcard" 

1411 " character" 

1412 ) 

1413 

1414 def __repr__(self) -> str: 

1415 return ( 

1416 f"<NameConstraints(permitted_subtrees={self.permitted_subtrees}, " 

1417 f"excluded_subtrees={self.excluded_subtrees})>" 

1418 ) 

1419 

1420 def __hash__(self) -> int: 

1421 if self.permitted_subtrees is not None: 

1422 ps: tuple[GeneralName, ...] | None = tuple(self.permitted_subtrees) 

1423 else: 

1424 ps = None 

1425 

1426 if self.excluded_subtrees is not None: 

1427 es: tuple[GeneralName, ...] | None = tuple(self.excluded_subtrees) 

1428 else: 

1429 es = None 

1430 

1431 return hash((ps, es)) 

1432 

1433 @property 

1434 def permitted_subtrees( 

1435 self, 

1436 ) -> list[GeneralName] | None: 

1437 return self._permitted_subtrees 

1438 

1439 @property 

1440 def excluded_subtrees( 

1441 self, 

1442 ) -> list[GeneralName] | None: 

1443 return self._excluded_subtrees 

1444 

1445 def public_bytes(self) -> bytes: 

1446 return rust_x509.encode_extension_value(self) 

1447 

1448 

1449class Extension(typing.Generic[ExtensionTypeVar]): 

1450 def __init__( 

1451 self, oid: ObjectIdentifier, critical: bool, value: ExtensionTypeVar 

1452 ) -> None: 

1453 if not isinstance(oid, ObjectIdentifier): 

1454 raise TypeError( 

1455 "oid argument must be an ObjectIdentifier instance." 

1456 ) 

1457 

1458 if not isinstance(critical, bool): 

1459 raise TypeError("critical must be a boolean value") 

1460 

1461 self._oid = oid 

1462 self._critical = critical 

1463 self._value = value 

1464 

1465 @property 

1466 def oid(self) -> ObjectIdentifier: 

1467 return self._oid 

1468 

1469 @property 

1470 def critical(self) -> bool: 

1471 return self._critical 

1472 

1473 @property 

1474 def value(self) -> ExtensionTypeVar: 

1475 return self._value 

1476 

1477 def __repr__(self) -> str: 

1478 return ( 

1479 f"<Extension(oid={self.oid}, critical={self.critical}, " 

1480 f"value={self.value})>" 

1481 ) 

1482 

1483 def __eq__(self, other: object) -> bool: 

1484 if not isinstance(other, Extension): 

1485 return NotImplemented 

1486 

1487 return ( 

1488 self.oid == other.oid 

1489 and self.critical == other.critical 

1490 and self.value == other.value 

1491 ) 

1492 

1493 def __hash__(self) -> int: 

1494 return hash((self.oid, self.critical, self.value)) 

1495 

1496 

1497class GeneralNames: 

1498 def __init__(self, general_names: Iterable[GeneralName]) -> None: 

1499 general_names = list(general_names) 

1500 if not all(isinstance(x, GeneralName) for x in general_names): 

1501 raise TypeError( 

1502 "Every item in the general_names list must be an " 

1503 "object conforming to the GeneralName interface" 

1504 ) 

1505 

1506 self._general_names = general_names 

1507 

1508 __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") 

1509 

1510 @typing.overload 

1511 def get_values_for_type( 

1512 self, 

1513 type: type[DNSName] 

1514 | type[UniformResourceIdentifier] 

1515 | type[RFC822Name], 

1516 ) -> list[str]: ... 

1517 

1518 @typing.overload 

1519 def get_values_for_type( 

1520 self, 

1521 type: type[DirectoryName], 

1522 ) -> list[Name]: ... 

1523 

1524 @typing.overload 

1525 def get_values_for_type( 

1526 self, 

1527 type: type[RegisteredID], 

1528 ) -> list[ObjectIdentifier]: ... 

1529 

1530 @typing.overload 

1531 def get_values_for_type( 

1532 self, type: type[IPAddress] 

1533 ) -> list[_IPAddressTypes]: ... 

1534 

1535 @typing.overload 

1536 def get_values_for_type( 

1537 self, type: type[OtherName] 

1538 ) -> list[OtherName]: ... 

1539 

1540 def get_values_for_type( 

1541 self, 

1542 type: type[DNSName] 

1543 | type[DirectoryName] 

1544 | type[IPAddress] 

1545 | type[OtherName] 

1546 | type[RFC822Name] 

1547 | type[RegisteredID] 

1548 | type[UniformResourceIdentifier], 

1549 ) -> ( 

1550 list[_IPAddressTypes] 

1551 | list[str] 

1552 | list[OtherName] 

1553 | list[Name] 

1554 | list[ObjectIdentifier] 

1555 ): 

1556 # Return the value of each GeneralName, except for OtherName instances 

1557 # which we return directly because it has two important properties not 

1558 # just one value. 

1559 objs = (i for i in self if isinstance(i, type)) 

1560 if type != OtherName: 

1561 return [i.value for i in objs] 

1562 return list(objs) 

1563 

1564 def __repr__(self) -> str: 

1565 return f"<GeneralNames({self._general_names})>" 

1566 

1567 def __eq__(self, other: object) -> bool: 

1568 if not isinstance(other, GeneralNames): 

1569 return NotImplemented 

1570 

1571 return self._general_names == other._general_names 

1572 

1573 def __hash__(self) -> int: 

1574 return hash(tuple(self._general_names)) 

1575 

1576 

1577class SubjectAlternativeName(ExtensionType): 

1578 oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME 

1579 

1580 def __init__(self, general_names: Iterable[GeneralName]) -> None: 

1581 self._general_names = GeneralNames(general_names) 

1582 

1583 __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") 

1584 

1585 @typing.overload 

1586 def get_values_for_type( 

1587 self, 

1588 type: type[DNSName] 

1589 | type[UniformResourceIdentifier] 

1590 | type[RFC822Name], 

1591 ) -> list[str]: ... 

1592 

1593 @typing.overload 

1594 def get_values_for_type( 

1595 self, 

1596 type: type[DirectoryName], 

1597 ) -> list[Name]: ... 

1598 

1599 @typing.overload 

1600 def get_values_for_type( 

1601 self, 

1602 type: type[RegisteredID], 

1603 ) -> list[ObjectIdentifier]: ... 

1604 

1605 @typing.overload 

1606 def get_values_for_type( 

1607 self, type: type[IPAddress] 

1608 ) -> list[_IPAddressTypes]: ... 

1609 

1610 @typing.overload 

1611 def get_values_for_type( 

1612 self, type: type[OtherName] 

1613 ) -> list[OtherName]: ... 

1614 

1615 def get_values_for_type( 

1616 self, 

1617 type: type[DNSName] 

1618 | type[DirectoryName] 

1619 | type[IPAddress] 

1620 | type[OtherName] 

1621 | type[RFC822Name] 

1622 | type[RegisteredID] 

1623 | type[UniformResourceIdentifier], 

1624 ) -> ( 

1625 list[_IPAddressTypes] 

1626 | list[str] 

1627 | list[OtherName] 

1628 | list[Name] 

1629 | list[ObjectIdentifier] 

1630 ): 

1631 return self._general_names.get_values_for_type(type) 

1632 

1633 def __repr__(self) -> str: 

1634 return f"<SubjectAlternativeName({self._general_names})>" 

1635 

1636 def __eq__(self, other: object) -> bool: 

1637 if not isinstance(other, SubjectAlternativeName): 

1638 return NotImplemented 

1639 

1640 return self._general_names == other._general_names 

1641 

1642 def __hash__(self) -> int: 

1643 return hash(self._general_names) 

1644 

1645 def public_bytes(self) -> bytes: 

1646 return rust_x509.encode_extension_value(self) 

1647 

1648 

1649class IssuerAlternativeName(ExtensionType): 

1650 oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME 

1651 

1652 def __init__(self, general_names: Iterable[GeneralName]) -> None: 

1653 self._general_names = GeneralNames(general_names) 

1654 

1655 __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") 

1656 

1657 @typing.overload 

1658 def get_values_for_type( 

1659 self, 

1660 type: type[DNSName] 

1661 | type[UniformResourceIdentifier] 

1662 | type[RFC822Name], 

1663 ) -> list[str]: ... 

1664 

1665 @typing.overload 

1666 def get_values_for_type( 

1667 self, 

1668 type: type[DirectoryName], 

1669 ) -> list[Name]: ... 

1670 

1671 @typing.overload 

1672 def get_values_for_type( 

1673 self, 

1674 type: type[RegisteredID], 

1675 ) -> list[ObjectIdentifier]: ... 

1676 

1677 @typing.overload 

1678 def get_values_for_type( 

1679 self, type: type[IPAddress] 

1680 ) -> list[_IPAddressTypes]: ... 

1681 

1682 @typing.overload 

1683 def get_values_for_type( 

1684 self, type: type[OtherName] 

1685 ) -> list[OtherName]: ... 

1686 

1687 def get_values_for_type( 

1688 self, 

1689 type: type[DNSName] 

1690 | type[DirectoryName] 

1691 | type[IPAddress] 

1692 | type[OtherName] 

1693 | type[RFC822Name] 

1694 | type[RegisteredID] 

1695 | type[UniformResourceIdentifier], 

1696 ) -> ( 

1697 list[_IPAddressTypes] 

1698 | list[str] 

1699 | list[OtherName] 

1700 | list[Name] 

1701 | list[ObjectIdentifier] 

1702 ): 

1703 return self._general_names.get_values_for_type(type) 

1704 

1705 def __repr__(self) -> str: 

1706 return f"<IssuerAlternativeName({self._general_names})>" 

1707 

1708 def __eq__(self, other: object) -> bool: 

1709 if not isinstance(other, IssuerAlternativeName): 

1710 return NotImplemented 

1711 

1712 return self._general_names == other._general_names 

1713 

1714 def __hash__(self) -> int: 

1715 return hash(self._general_names) 

1716 

1717 def public_bytes(self) -> bytes: 

1718 return rust_x509.encode_extension_value(self) 

1719 

1720 

1721class CertificateIssuer(ExtensionType): 

1722 oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER 

1723 

1724 def __init__(self, general_names: Iterable[GeneralName]) -> None: 

1725 self._general_names = GeneralNames(general_names) 

1726 

1727 __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") 

1728 

1729 @typing.overload 

1730 def get_values_for_type( 

1731 self, 

1732 type: type[DNSName] 

1733 | type[UniformResourceIdentifier] 

1734 | type[RFC822Name], 

1735 ) -> list[str]: ... 

1736 

1737 @typing.overload 

1738 def get_values_for_type( 

1739 self, 

1740 type: type[DirectoryName], 

1741 ) -> list[Name]: ... 

1742 

1743 @typing.overload 

1744 def get_values_for_type( 

1745 self, 

1746 type: type[RegisteredID], 

1747 ) -> list[ObjectIdentifier]: ... 

1748 

1749 @typing.overload 

1750 def get_values_for_type( 

1751 self, type: type[IPAddress] 

1752 ) -> list[_IPAddressTypes]: ... 

1753 

1754 @typing.overload 

1755 def get_values_for_type( 

1756 self, type: type[OtherName] 

1757 ) -> list[OtherName]: ... 

1758 

1759 def get_values_for_type( 

1760 self, 

1761 type: type[DNSName] 

1762 | type[DirectoryName] 

1763 | type[IPAddress] 

1764 | type[OtherName] 

1765 | type[RFC822Name] 

1766 | type[RegisteredID] 

1767 | type[UniformResourceIdentifier], 

1768 ) -> ( 

1769 list[_IPAddressTypes] 

1770 | list[str] 

1771 | list[OtherName] 

1772 | list[Name] 

1773 | list[ObjectIdentifier] 

1774 ): 

1775 return self._general_names.get_values_for_type(type) 

1776 

1777 def __repr__(self) -> str: 

1778 return f"<CertificateIssuer({self._general_names})>" 

1779 

1780 def __eq__(self, other: object) -> bool: 

1781 if not isinstance(other, CertificateIssuer): 

1782 return NotImplemented 

1783 

1784 return self._general_names == other._general_names 

1785 

1786 def __hash__(self) -> int: 

1787 return hash(self._general_names) 

1788 

1789 def public_bytes(self) -> bytes: 

1790 return rust_x509.encode_extension_value(self) 

1791 

1792 

1793class CRLReason(ExtensionType): 

1794 oid = CRLEntryExtensionOID.CRL_REASON 

1795 

1796 def __init__(self, reason: ReasonFlags) -> None: 

1797 if not isinstance(reason, ReasonFlags): 

1798 raise TypeError("reason must be an element from ReasonFlags") 

1799 

1800 self._reason = reason 

1801 

1802 def __repr__(self) -> str: 

1803 return f"<CRLReason(reason={self._reason})>" 

1804 

1805 def __eq__(self, other: object) -> bool: 

1806 if not isinstance(other, CRLReason): 

1807 return NotImplemented 

1808 

1809 return self.reason == other.reason 

1810 

1811 def __hash__(self) -> int: 

1812 return hash(self.reason) 

1813 

1814 @property 

1815 def reason(self) -> ReasonFlags: 

1816 return self._reason 

1817 

1818 def public_bytes(self) -> bytes: 

1819 return rust_x509.encode_extension_value(self) 

1820 

1821 

1822class InvalidityDate(ExtensionType): 

1823 oid = CRLEntryExtensionOID.INVALIDITY_DATE 

1824 

1825 def __init__(self, invalidity_date: datetime.datetime) -> None: 

1826 if not isinstance(invalidity_date, datetime.datetime): 

1827 raise TypeError("invalidity_date must be a datetime.datetime") 

1828 

1829 self._invalidity_date = invalidity_date 

1830 

1831 def __repr__(self) -> str: 

1832 return f"<InvalidityDate(invalidity_date={self._invalidity_date})>" 

1833 

1834 def __eq__(self, other: object) -> bool: 

1835 if not isinstance(other, InvalidityDate): 

1836 return NotImplemented 

1837 

1838 return self.invalidity_date == other.invalidity_date 

1839 

1840 def __hash__(self) -> int: 

1841 return hash(self.invalidity_date) 

1842 

1843 @property 

1844 def invalidity_date(self) -> datetime.datetime: 

1845 return self._invalidity_date 

1846 

1847 @property 

1848 def invalidity_date_utc(self) -> datetime.datetime: 

1849 if self._invalidity_date.tzinfo is None: 

1850 return self._invalidity_date.replace(tzinfo=datetime.timezone.utc) 

1851 else: 

1852 return self._invalidity_date.astimezone(tz=datetime.timezone.utc) 

1853 

1854 def public_bytes(self) -> bytes: 

1855 return rust_x509.encode_extension_value(self) 

1856 

1857 

1858class PrecertificateSignedCertificateTimestamps(ExtensionType): 

1859 oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS 

1860 

1861 def __init__( 

1862 self, 

1863 signed_certificate_timestamps: Iterable[SignedCertificateTimestamp], 

1864 ) -> None: 

1865 signed_certificate_timestamps = list(signed_certificate_timestamps) 

1866 if not all( 

1867 isinstance(sct, SignedCertificateTimestamp) 

1868 for sct in signed_certificate_timestamps 

1869 ): 

1870 raise TypeError( 

1871 "Every item in the signed_certificate_timestamps list must be " 

1872 "a SignedCertificateTimestamp" 

1873 ) 

1874 self._signed_certificate_timestamps = signed_certificate_timestamps 

1875 

1876 __len__, __iter__, __getitem__ = _make_sequence_methods( 

1877 "_signed_certificate_timestamps" 

1878 ) 

1879 

1880 def __repr__(self) -> str: 

1881 return f"<PrecertificateSignedCertificateTimestamps({list(self)})>" 

1882 

1883 def __hash__(self) -> int: 

1884 return hash(tuple(self._signed_certificate_timestamps)) 

1885 

1886 def __eq__(self, other: object) -> bool: 

1887 if not isinstance(other, PrecertificateSignedCertificateTimestamps): 

1888 return NotImplemented 

1889 

1890 return ( 

1891 self._signed_certificate_timestamps 

1892 == other._signed_certificate_timestamps 

1893 ) 

1894 

1895 def public_bytes(self) -> bytes: 

1896 return rust_x509.encode_extension_value(self) 

1897 

1898 

1899class SignedCertificateTimestamps(ExtensionType): 

1900 oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS 

1901 

1902 def __init__( 

1903 self, 

1904 signed_certificate_timestamps: Iterable[SignedCertificateTimestamp], 

1905 ) -> None: 

1906 signed_certificate_timestamps = list(signed_certificate_timestamps) 

1907 if not all( 

1908 isinstance(sct, SignedCertificateTimestamp) 

1909 for sct in signed_certificate_timestamps 

1910 ): 

1911 raise TypeError( 

1912 "Every item in the signed_certificate_timestamps list must be " 

1913 "a SignedCertificateTimestamp" 

1914 ) 

1915 self._signed_certificate_timestamps = signed_certificate_timestamps 

1916 

1917 __len__, __iter__, __getitem__ = _make_sequence_methods( 

1918 "_signed_certificate_timestamps" 

1919 ) 

1920 

1921 def __repr__(self) -> str: 

1922 return f"<SignedCertificateTimestamps({list(self)})>" 

1923 

1924 def __hash__(self) -> int: 

1925 return hash(tuple(self._signed_certificate_timestamps)) 

1926 

1927 def __eq__(self, other: object) -> bool: 

1928 if not isinstance(other, SignedCertificateTimestamps): 

1929 return NotImplemented 

1930 

1931 return ( 

1932 self._signed_certificate_timestamps 

1933 == other._signed_certificate_timestamps 

1934 ) 

1935 

1936 def public_bytes(self) -> bytes: 

1937 return rust_x509.encode_extension_value(self) 

1938 

1939 

1940class OCSPNonce(ExtensionType): 

1941 oid = OCSPExtensionOID.NONCE 

1942 

1943 def __init__(self, nonce: bytes) -> None: 

1944 if not isinstance(nonce, bytes): 

1945 raise TypeError("nonce must be bytes") 

1946 

1947 self._nonce = nonce 

1948 

1949 def __eq__(self, other: object) -> bool: 

1950 if not isinstance(other, OCSPNonce): 

1951 return NotImplemented 

1952 

1953 return self.nonce == other.nonce 

1954 

1955 def __hash__(self) -> int: 

1956 return hash(self.nonce) 

1957 

1958 def __repr__(self) -> str: 

1959 return f"<OCSPNonce(nonce={self.nonce!r})>" 

1960 

1961 @property 

1962 def nonce(self) -> bytes: 

1963 return self._nonce 

1964 

1965 def public_bytes(self) -> bytes: 

1966 return rust_x509.encode_extension_value(self) 

1967 

1968 

1969class OCSPAcceptableResponses(ExtensionType): 

1970 oid = OCSPExtensionOID.ACCEPTABLE_RESPONSES 

1971 

1972 def __init__(self, responses: Iterable[ObjectIdentifier]) -> None: 

1973 responses = list(responses) 

1974 if any(not isinstance(r, ObjectIdentifier) for r in responses): 

1975 raise TypeError("All responses must be ObjectIdentifiers") 

1976 

1977 self._responses = responses 

1978 

1979 def __eq__(self, other: object) -> bool: 

1980 if not isinstance(other, OCSPAcceptableResponses): 

1981 return NotImplemented 

1982 

1983 return self._responses == other._responses 

1984 

1985 def __hash__(self) -> int: 

1986 return hash(tuple(self._responses)) 

1987 

1988 def __repr__(self) -> str: 

1989 return f"<OCSPAcceptableResponses(responses={self._responses})>" 

1990 

1991 def __iter__(self) -> Iterator[ObjectIdentifier]: 

1992 return iter(self._responses) 

1993 

1994 def public_bytes(self) -> bytes: 

1995 return rust_x509.encode_extension_value(self) 

1996 

1997 

1998class IssuingDistributionPoint(ExtensionType): 

1999 oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT 

2000 

2001 def __init__( 

2002 self, 

2003 full_name: Iterable[GeneralName] | None, 

2004 relative_name: RelativeDistinguishedName | None, 

2005 only_contains_user_certs: bool, 

2006 only_contains_ca_certs: bool, 

2007 only_some_reasons: frozenset[ReasonFlags] | None, 

2008 indirect_crl: bool, 

2009 only_contains_attribute_certs: bool, 

2010 ) -> None: 

2011 if full_name is not None: 

2012 full_name = list(full_name) 

2013 

2014 if only_some_reasons and ( 

2015 not isinstance(only_some_reasons, frozenset) 

2016 or not all(isinstance(x, ReasonFlags) for x in only_some_reasons) 

2017 ): 

2018 raise TypeError( 

2019 "only_some_reasons must be None or frozenset of ReasonFlags" 

2020 ) 

2021 

2022 if only_some_reasons and ( 

2023 ReasonFlags.unspecified in only_some_reasons 

2024 or ReasonFlags.remove_from_crl in only_some_reasons 

2025 ): 

2026 raise ValueError( 

2027 "unspecified and remove_from_crl are not valid reasons in an " 

2028 "IssuingDistributionPoint" 

2029 ) 

2030 

2031 if not ( 

2032 isinstance(only_contains_user_certs, bool) 

2033 and isinstance(only_contains_ca_certs, bool) 

2034 and isinstance(indirect_crl, bool) 

2035 and isinstance(only_contains_attribute_certs, bool) 

2036 ): 

2037 raise TypeError( 

2038 "only_contains_user_certs, only_contains_ca_certs, " 

2039 "indirect_crl and only_contains_attribute_certs " 

2040 "must all be boolean." 

2041 ) 

2042 

2043 # Per RFC5280 Section 5.2.5, the Issuing Distribution Point extension 

2044 # in a CRL can have only one of onlyContainsUserCerts, 

2045 # onlyContainsCACerts, onlyContainsAttributeCerts set to TRUE. 

2046 crl_constraints = [ 

2047 only_contains_user_certs, 

2048 only_contains_ca_certs, 

2049 only_contains_attribute_certs, 

2050 ] 

2051 

2052 if len([x for x in crl_constraints if x]) > 1: 

2053 raise ValueError( 

2054 "Only one of the following can be set to True: " 

2055 "only_contains_user_certs, only_contains_ca_certs, " 

2056 "only_contains_attribute_certs" 

2057 ) 

2058 

2059 if not any( 

2060 [ 

2061 only_contains_user_certs, 

2062 only_contains_ca_certs, 

2063 indirect_crl, 

2064 only_contains_attribute_certs, 

2065 full_name, 

2066 relative_name, 

2067 only_some_reasons, 

2068 ] 

2069 ): 

2070 raise ValueError( 

2071 "Cannot create empty extension: " 

2072 "if only_contains_user_certs, only_contains_ca_certs, " 

2073 "indirect_crl, and only_contains_attribute_certs are all False" 

2074 ", then either full_name, relative_name, or only_some_reasons " 

2075 "must have a value." 

2076 ) 

2077 

2078 self._only_contains_user_certs = only_contains_user_certs 

2079 self._only_contains_ca_certs = only_contains_ca_certs 

2080 self._indirect_crl = indirect_crl 

2081 self._only_contains_attribute_certs = only_contains_attribute_certs 

2082 self._only_some_reasons = only_some_reasons 

2083 self._full_name = full_name 

2084 self._relative_name = relative_name 

2085 

2086 def __repr__(self) -> str: 

2087 return ( 

2088 f"<IssuingDistributionPoint(full_name={self.full_name}, " 

2089 f"relative_name={self.relative_name}, " 

2090 f"only_contains_user_certs={self.only_contains_user_certs}, " 

2091 f"only_contains_ca_certs={self.only_contains_ca_certs}, " 

2092 f"only_some_reasons={self.only_some_reasons}, " 

2093 f"indirect_crl={self.indirect_crl}, " 

2094 "only_contains_attribute_certs=" 

2095 f"{self.only_contains_attribute_certs})>" 

2096 ) 

2097 

2098 def __eq__(self, other: object) -> bool: 

2099 if not isinstance(other, IssuingDistributionPoint): 

2100 return NotImplemented 

2101 

2102 return ( 

2103 self.full_name == other.full_name 

2104 and self.relative_name == other.relative_name 

2105 and self.only_contains_user_certs == other.only_contains_user_certs 

2106 and self.only_contains_ca_certs == other.only_contains_ca_certs 

2107 and self.only_some_reasons == other.only_some_reasons 

2108 and self.indirect_crl == other.indirect_crl 

2109 and self.only_contains_attribute_certs 

2110 == other.only_contains_attribute_certs 

2111 ) 

2112 

2113 def __hash__(self) -> int: 

2114 return hash( 

2115 ( 

2116 self.full_name, 

2117 self.relative_name, 

2118 self.only_contains_user_certs, 

2119 self.only_contains_ca_certs, 

2120 self.only_some_reasons, 

2121 self.indirect_crl, 

2122 self.only_contains_attribute_certs, 

2123 ) 

2124 ) 

2125 

2126 @property 

2127 def full_name(self) -> list[GeneralName] | None: 

2128 return self._full_name 

2129 

2130 @property 

2131 def relative_name(self) -> RelativeDistinguishedName | None: 

2132 return self._relative_name 

2133 

2134 @property 

2135 def only_contains_user_certs(self) -> bool: 

2136 return self._only_contains_user_certs 

2137 

2138 @property 

2139 def only_contains_ca_certs(self) -> bool: 

2140 return self._only_contains_ca_certs 

2141 

2142 @property 

2143 def only_some_reasons( 

2144 self, 

2145 ) -> frozenset[ReasonFlags] | None: 

2146 return self._only_some_reasons 

2147 

2148 @property 

2149 def indirect_crl(self) -> bool: 

2150 return self._indirect_crl 

2151 

2152 @property 

2153 def only_contains_attribute_certs(self) -> bool: 

2154 return self._only_contains_attribute_certs 

2155 

2156 def public_bytes(self) -> bytes: 

2157 return rust_x509.encode_extension_value(self) 

2158 

2159 

2160class MSCertificateTemplate(ExtensionType): 

2161 oid = ExtensionOID.MS_CERTIFICATE_TEMPLATE 

2162 

2163 def __init__( 

2164 self, 

2165 template_id: ObjectIdentifier, 

2166 major_version: int | None, 

2167 minor_version: int | None, 

2168 ) -> None: 

2169 if not isinstance(template_id, ObjectIdentifier): 

2170 raise TypeError("oid must be an ObjectIdentifier") 

2171 self._template_id = template_id 

2172 if ( 

2173 major_version is not None and not isinstance(major_version, int) 

2174 ) or ( 

2175 minor_version is not None and not isinstance(minor_version, int) 

2176 ): 

2177 raise TypeError( 

2178 "major_version and minor_version must be integers or None" 

2179 ) 

2180 self._major_version = major_version 

2181 self._minor_version = minor_version 

2182 

2183 @property 

2184 def template_id(self) -> ObjectIdentifier: 

2185 return self._template_id 

2186 

2187 @property 

2188 def major_version(self) -> int | None: 

2189 return self._major_version 

2190 

2191 @property 

2192 def minor_version(self) -> int | None: 

2193 return self._minor_version 

2194 

2195 def __repr__(self) -> str: 

2196 return ( 

2197 f"<MSCertificateTemplate(template_id={self.template_id}, " 

2198 f"major_version={self.major_version}, " 

2199 f"minor_version={self.minor_version})>" 

2200 ) 

2201 

2202 def __eq__(self, other: object) -> bool: 

2203 if not isinstance(other, MSCertificateTemplate): 

2204 return NotImplemented 

2205 

2206 return ( 

2207 self.template_id == other.template_id 

2208 and self.major_version == other.major_version 

2209 and self.minor_version == other.minor_version 

2210 ) 

2211 

2212 def __hash__(self) -> int: 

2213 return hash((self.template_id, self.major_version, self.minor_version)) 

2214 

2215 def public_bytes(self) -> bytes: 

2216 return rust_x509.encode_extension_value(self) 

2217 

2218 

2219class NamingAuthority: 

2220 def __init__( 

2221 self, 

2222 id: ObjectIdentifier | None, 

2223 url: str | None, 

2224 text: str | None, 

2225 ) -> None: 

2226 if id is not None and not isinstance(id, ObjectIdentifier): 

2227 raise TypeError("id must be an ObjectIdentifier") 

2228 

2229 if url is not None and not isinstance(url, str): 

2230 raise TypeError("url must be a str") 

2231 

2232 if text is not None and not isinstance(text, str): 

2233 raise TypeError("text must be a str") 

2234 

2235 self._id = id 

2236 self._url = url 

2237 self._text = text 

2238 

2239 @property 

2240 def id(self) -> ObjectIdentifier | None: 

2241 return self._id 

2242 

2243 @property 

2244 def url(self) -> str | None: 

2245 return self._url 

2246 

2247 @property 

2248 def text(self) -> str | None: 

2249 return self._text 

2250 

2251 def __repr__(self) -> str: 

2252 return ( 

2253 f"<NamingAuthority(" 

2254 f"id={self.id}, url={self.url}, text={self.text})>" 

2255 ) 

2256 

2257 def __eq__(self, other: object) -> bool: 

2258 if not isinstance(other, NamingAuthority): 

2259 return NotImplemented 

2260 

2261 return ( 

2262 self.id == other.id 

2263 and self.url == other.url 

2264 and self.text == other.text 

2265 ) 

2266 

2267 def __hash__(self) -> int: 

2268 return hash( 

2269 ( 

2270 self.id, 

2271 self.url, 

2272 self.text, 

2273 ) 

2274 ) 

2275 

2276 

2277class ProfessionInfo: 

2278 def __init__( 

2279 self, 

2280 naming_authority: NamingAuthority | None, 

2281 profession_items: Iterable[str], 

2282 profession_oids: Iterable[ObjectIdentifier] | None, 

2283 registration_number: str | None, 

2284 add_profession_info: bytes | None, 

2285 ) -> None: 

2286 if naming_authority is not None and not isinstance( 

2287 naming_authority, NamingAuthority 

2288 ): 

2289 raise TypeError("naming_authority must be a NamingAuthority") 

2290 

2291 profession_items = list(profession_items) 

2292 if not all(isinstance(item, str) for item in profession_items): 

2293 raise TypeError( 

2294 "Every item in the profession_items list must be a str" 

2295 ) 

2296 

2297 if profession_oids is not None: 

2298 profession_oids = list(profession_oids) 

2299 if not all( 

2300 isinstance(oid, ObjectIdentifier) for oid in profession_oids 

2301 ): 

2302 raise TypeError( 

2303 "Every item in the profession_oids list must be an " 

2304 "ObjectIdentifier" 

2305 ) 

2306 

2307 if registration_number is not None and not isinstance( 

2308 registration_number, str 

2309 ): 

2310 raise TypeError("registration_number must be a str") 

2311 

2312 if add_profession_info is not None and not isinstance( 

2313 add_profession_info, bytes 

2314 ): 

2315 raise TypeError("add_profession_info must be bytes") 

2316 

2317 self._naming_authority = naming_authority 

2318 self._profession_items = profession_items 

2319 self._profession_oids = profession_oids 

2320 self._registration_number = registration_number 

2321 self._add_profession_info = add_profession_info 

2322 

2323 @property 

2324 def naming_authority(self) -> NamingAuthority | None: 

2325 return self._naming_authority 

2326 

2327 @property 

2328 def profession_items(self) -> list[str]: 

2329 return self._profession_items 

2330 

2331 @property 

2332 def profession_oids(self) -> list[ObjectIdentifier] | None: 

2333 return self._profession_oids 

2334 

2335 @property 

2336 def registration_number(self) -> str | None: 

2337 return self._registration_number 

2338 

2339 @property 

2340 def add_profession_info(self) -> bytes | None: 

2341 return self._add_profession_info 

2342 

2343 def __repr__(self) -> str: 

2344 return ( 

2345 f"<ProfessionInfo(naming_authority={self.naming_authority}, " 

2346 f"profession_items={self.profession_items}, " 

2347 f"profession_oids={self.profession_oids}, " 

2348 f"registration_number={self.registration_number}, " 

2349 f"add_profession_info={self.add_profession_info!r})>" 

2350 ) 

2351 

2352 def __eq__(self, other: object) -> bool: 

2353 if not isinstance(other, ProfessionInfo): 

2354 return NotImplemented 

2355 

2356 return ( 

2357 self.naming_authority == other.naming_authority 

2358 and self.profession_items == other.profession_items 

2359 and self.profession_oids == other.profession_oids 

2360 and self.registration_number == other.registration_number 

2361 and self.add_profession_info == other.add_profession_info 

2362 ) 

2363 

2364 def __hash__(self) -> int: 

2365 if self.profession_oids is not None: 

2366 profession_oids = tuple(self.profession_oids) 

2367 else: 

2368 profession_oids = None 

2369 return hash( 

2370 ( 

2371 self.naming_authority, 

2372 tuple(self.profession_items), 

2373 profession_oids, 

2374 self.registration_number, 

2375 self.add_profession_info, 

2376 ) 

2377 ) 

2378 

2379 

2380class Admission: 

2381 def __init__( 

2382 self, 

2383 admission_authority: GeneralName | None, 

2384 naming_authority: NamingAuthority | None, 

2385 profession_infos: Iterable[ProfessionInfo], 

2386 ) -> None: 

2387 if admission_authority is not None and not isinstance( 

2388 admission_authority, GeneralName 

2389 ): 

2390 raise TypeError("admission_authority must be a GeneralName") 

2391 

2392 if naming_authority is not None and not isinstance( 

2393 naming_authority, NamingAuthority 

2394 ): 

2395 raise TypeError("naming_authority must be a NamingAuthority") 

2396 

2397 profession_infos = list(profession_infos) 

2398 if not all( 

2399 isinstance(info, ProfessionInfo) for info in profession_infos 

2400 ): 

2401 raise TypeError( 

2402 "Every item in the profession_infos list must be a " 

2403 "ProfessionInfo" 

2404 ) 

2405 

2406 self._admission_authority = admission_authority 

2407 self._naming_authority = naming_authority 

2408 self._profession_infos = profession_infos 

2409 

2410 @property 

2411 def admission_authority(self) -> GeneralName | None: 

2412 return self._admission_authority 

2413 

2414 @property 

2415 def naming_authority(self) -> NamingAuthority | None: 

2416 return self._naming_authority 

2417 

2418 @property 

2419 def profession_infos(self) -> list[ProfessionInfo]: 

2420 return self._profession_infos 

2421 

2422 def __repr__(self) -> str: 

2423 return ( 

2424 f"<Admission(admission_authority={self.admission_authority}, " 

2425 f"naming_authority={self.naming_authority}, " 

2426 f"profession_infos={self.profession_infos})>" 

2427 ) 

2428 

2429 def __eq__(self, other: object) -> bool: 

2430 if not isinstance(other, Admission): 

2431 return NotImplemented 

2432 

2433 return ( 

2434 self.admission_authority == other.admission_authority 

2435 and self.naming_authority == other.naming_authority 

2436 and self.profession_infos == other.profession_infos 

2437 ) 

2438 

2439 def __hash__(self) -> int: 

2440 return hash( 

2441 ( 

2442 self.admission_authority, 

2443 self.naming_authority, 

2444 tuple(self.profession_infos), 

2445 ) 

2446 ) 

2447 

2448 

2449class Admissions(ExtensionType): 

2450 oid = ExtensionOID.ADMISSIONS 

2451 

2452 def __init__( 

2453 self, 

2454 authority: GeneralName | None, 

2455 admissions: Iterable[Admission], 

2456 ) -> None: 

2457 if authority is not None and not isinstance(authority, GeneralName): 

2458 raise TypeError("authority must be a GeneralName") 

2459 

2460 admissions = list(admissions) 

2461 if not all( 

2462 isinstance(admission, Admission) for admission in admissions 

2463 ): 

2464 raise TypeError( 

2465 "Every item in the contents_of_admissions list must be an " 

2466 "Admission" 

2467 ) 

2468 

2469 self._authority = authority 

2470 self._admissions = admissions 

2471 

2472 __len__, __iter__, __getitem__ = _make_sequence_methods("_admissions") 

2473 

2474 @property 

2475 def authority(self) -> GeneralName | None: 

2476 return self._authority 

2477 

2478 def __repr__(self) -> str: 

2479 return ( 

2480 f"<Admissions(authority={self._authority}, " 

2481 f"admissions={self._admissions})>" 

2482 ) 

2483 

2484 def __eq__(self, other: object) -> bool: 

2485 if not isinstance(other, Admissions): 

2486 return NotImplemented 

2487 

2488 return ( 

2489 self.authority == other.authority 

2490 and self._admissions == other._admissions 

2491 ) 

2492 

2493 def __hash__(self) -> int: 

2494 return hash((self.authority, tuple(self._admissions))) 

2495 

2496 def public_bytes(self) -> bytes: 

2497 return rust_x509.encode_extension_value(self) 

2498 

2499 

2500class UnrecognizedExtension(ExtensionType): 

2501 def __init__(self, oid: ObjectIdentifier, value: bytes) -> None: 

2502 if not isinstance(oid, ObjectIdentifier): 

2503 raise TypeError("oid must be an ObjectIdentifier") 

2504 self._oid = oid 

2505 self._value = value 

2506 

2507 @property 

2508 def oid(self) -> ObjectIdentifier: # type: ignore[override] 

2509 return self._oid 

2510 

2511 @property 

2512 def value(self) -> bytes: 

2513 return self._value 

2514 

2515 def __repr__(self) -> str: 

2516 return f"<UnrecognizedExtension(oid={self.oid}, value={self.value!r})>" 

2517 

2518 def __eq__(self, other: object) -> bool: 

2519 if not isinstance(other, UnrecognizedExtension): 

2520 return NotImplemented 

2521 

2522 return self.oid == other.oid and self.value == other.value 

2523 

2524 def __hash__(self) -> int: 

2525 return hash((self.oid, self.value)) 

2526 

2527 def public_bytes(self) -> bytes: 

2528 return self.value