Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/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

1038 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 

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