aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/cryptography/x509/base.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/cryptography/x509/base.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/cryptography/x509/base.py')
-rw-r--r--.venv/lib/python3.12/site-packages/cryptography/x509/base.py815
1 files changed, 815 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/base.py b/.venv/lib/python3.12/site-packages/cryptography/x509/base.py
new file mode 100644
index 00000000..25b317af
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/cryptography/x509/base.py
@@ -0,0 +1,815 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import annotations
+
+import abc
+import datetime
+import os
+import typing
+import warnings
+
+from cryptography import utils
+from cryptography.hazmat.bindings._rust import x509 as rust_x509
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.asymmetric import (
+ dsa,
+ ec,
+ ed448,
+ ed25519,
+ padding,
+ rsa,
+ x448,
+ x25519,
+)
+from cryptography.hazmat.primitives.asymmetric.types import (
+ CertificateIssuerPrivateKeyTypes,
+ CertificatePublicKeyTypes,
+)
+from cryptography.x509.extensions import (
+ Extension,
+ Extensions,
+ ExtensionType,
+ _make_sequence_methods,
+)
+from cryptography.x509.name import Name, _ASN1Type
+from cryptography.x509.oid import ObjectIdentifier
+
+_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1)
+
+# This must be kept in sync with sign.rs's list of allowable types in
+# identify_hash_type
+_AllowedHashTypes = typing.Union[
+ hashes.SHA224,
+ hashes.SHA256,
+ hashes.SHA384,
+ hashes.SHA512,
+ hashes.SHA3_224,
+ hashes.SHA3_256,
+ hashes.SHA3_384,
+ hashes.SHA3_512,
+]
+
+
+class AttributeNotFound(Exception):
+ def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
+ super().__init__(msg)
+ self.oid = oid
+
+
+def _reject_duplicate_extension(
+ extension: Extension[ExtensionType],
+ extensions: list[Extension[ExtensionType]],
+) -> None:
+ # This is quadratic in the number of extensions
+ for e in extensions:
+ if e.oid == extension.oid:
+ raise ValueError("This extension has already been set.")
+
+
+def _reject_duplicate_attribute(
+ oid: ObjectIdentifier,
+ attributes: list[tuple[ObjectIdentifier, bytes, int | None]],
+) -> None:
+ # This is quadratic in the number of attributes
+ for attr_oid, _, _ in attributes:
+ if attr_oid == oid:
+ raise ValueError("This attribute has already been set.")
+
+
+def _convert_to_naive_utc_time(time: datetime.datetime) -> datetime.datetime:
+ """Normalizes a datetime to a naive datetime in UTC.
+
+ time -- datetime to normalize. Assumed to be in UTC if not timezone
+ aware.
+ """
+ if time.tzinfo is not None:
+ offset = time.utcoffset()
+ offset = offset if offset else datetime.timedelta()
+ return time.replace(tzinfo=None) - offset
+ else:
+ return time
+
+
+class Attribute:
+ def __init__(
+ self,
+ oid: ObjectIdentifier,
+ value: bytes,
+ _type: int = _ASN1Type.UTF8String.value,
+ ) -> None:
+ self._oid = oid
+ self._value = value
+ self._type = _type
+
+ @property
+ def oid(self) -> ObjectIdentifier:
+ return self._oid
+
+ @property
+ def value(self) -> bytes:
+ return self._value
+
+ def __repr__(self) -> str:
+ return f"<Attribute(oid={self.oid}, value={self.value!r})>"
+
+ def __eq__(self, other: object) -> bool:
+ if not isinstance(other, Attribute):
+ return NotImplemented
+
+ return (
+ self.oid == other.oid
+ and self.value == other.value
+ and self._type == other._type
+ )
+
+ def __hash__(self) -> int:
+ return hash((self.oid, self.value, self._type))
+
+
+class Attributes:
+ def __init__(
+ self,
+ attributes: typing.Iterable[Attribute],
+ ) -> None:
+ self._attributes = list(attributes)
+
+ __len__, __iter__, __getitem__ = _make_sequence_methods("_attributes")
+
+ def __repr__(self) -> str:
+ return f"<Attributes({self._attributes})>"
+
+ def get_attribute_for_oid(self, oid: ObjectIdentifier) -> Attribute:
+ for attr in self:
+ if attr.oid == oid:
+ return attr
+
+ raise AttributeNotFound(f"No {oid} attribute was found", oid)
+
+
+class Version(utils.Enum):
+ v1 = 0
+ v3 = 2
+
+
+class InvalidVersion(Exception):
+ def __init__(self, msg: str, parsed_version: int) -> None:
+ super().__init__(msg)
+ self.parsed_version = parsed_version
+
+
+Certificate = rust_x509.Certificate
+
+
+class RevokedCertificate(metaclass=abc.ABCMeta):
+ @property
+ @abc.abstractmethod
+ def serial_number(self) -> int:
+ """
+ Returns the serial number of the revoked certificate.
+ """
+
+ @property
+ @abc.abstractmethod
+ def revocation_date(self) -> datetime.datetime:
+ """
+ Returns the date of when this certificate was revoked.
+ """
+
+ @property
+ @abc.abstractmethod
+ def revocation_date_utc(self) -> datetime.datetime:
+ """
+ Returns the date of when this certificate was revoked as a non-naive
+ UTC datetime.
+ """
+
+ @property
+ @abc.abstractmethod
+ def extensions(self) -> Extensions:
+ """
+ Returns an Extensions object containing a list of Revoked extensions.
+ """
+
+
+# Runtime isinstance checks need this since the rust class is not a subclass.
+RevokedCertificate.register(rust_x509.RevokedCertificate)
+
+
+class _RawRevokedCertificate(RevokedCertificate):
+ def __init__(
+ self,
+ serial_number: int,
+ revocation_date: datetime.datetime,
+ extensions: Extensions,
+ ):
+ self._serial_number = serial_number
+ self._revocation_date = revocation_date
+ self._extensions = extensions
+
+ @property
+ def serial_number(self) -> int:
+ return self._serial_number
+
+ @property
+ def revocation_date(self) -> datetime.datetime:
+ warnings.warn(
+ "Properties that return a naïve datetime object have been "
+ "deprecated. Please switch to revocation_date_utc.",
+ utils.DeprecatedIn42,
+ stacklevel=2,
+ )
+ return self._revocation_date
+
+ @property
+ def revocation_date_utc(self) -> datetime.datetime:
+ return self._revocation_date.replace(tzinfo=datetime.timezone.utc)
+
+ @property
+ def extensions(self) -> Extensions:
+ return self._extensions
+
+
+CertificateRevocationList = rust_x509.CertificateRevocationList
+CertificateSigningRequest = rust_x509.CertificateSigningRequest
+
+
+load_pem_x509_certificate = rust_x509.load_pem_x509_certificate
+load_der_x509_certificate = rust_x509.load_der_x509_certificate
+
+load_pem_x509_certificates = rust_x509.load_pem_x509_certificates
+
+load_pem_x509_csr = rust_x509.load_pem_x509_csr
+load_der_x509_csr = rust_x509.load_der_x509_csr
+
+load_pem_x509_crl = rust_x509.load_pem_x509_crl
+load_der_x509_crl = rust_x509.load_der_x509_crl
+
+
+class CertificateSigningRequestBuilder:
+ def __init__(
+ self,
+ subject_name: Name | None = None,
+ extensions: list[Extension[ExtensionType]] = [],
+ attributes: list[tuple[ObjectIdentifier, bytes, int | None]] = [],
+ ):
+ """
+ Creates an empty X.509 certificate request (v1).
+ """
+ self._subject_name = subject_name
+ self._extensions = extensions
+ self._attributes = attributes
+
+ def subject_name(self, name: Name) -> CertificateSigningRequestBuilder:
+ """
+ Sets the certificate requestor's distinguished name.
+ """
+ if not isinstance(name, Name):
+ raise TypeError("Expecting x509.Name object.")
+ if self._subject_name is not None:
+ raise ValueError("The subject name may only be set once.")
+ return CertificateSigningRequestBuilder(
+ name, self._extensions, self._attributes
+ )
+
+ def add_extension(
+ self, extval: ExtensionType, critical: bool
+ ) -> CertificateSigningRequestBuilder:
+ """
+ Adds an X.509 extension to the certificate request.
+ """
+ if not isinstance(extval, ExtensionType):
+ raise TypeError("extension must be an ExtensionType")
+
+ extension = Extension(extval.oid, critical, extval)
+ _reject_duplicate_extension(extension, self._extensions)
+
+ return CertificateSigningRequestBuilder(
+ self._subject_name,
+ [*self._extensions, extension],
+ self._attributes,
+ )
+
+ def add_attribute(
+ self,
+ oid: ObjectIdentifier,
+ value: bytes,
+ *,
+ _tag: _ASN1Type | None = None,
+ ) -> CertificateSigningRequestBuilder:
+ """
+ Adds an X.509 attribute with an OID and associated value.
+ """
+ if not isinstance(oid, ObjectIdentifier):
+ raise TypeError("oid must be an ObjectIdentifier")
+
+ if not isinstance(value, bytes):
+ raise TypeError("value must be bytes")
+
+ if _tag is not None and not isinstance(_tag, _ASN1Type):
+ raise TypeError("tag must be _ASN1Type")
+
+ _reject_duplicate_attribute(oid, self._attributes)
+
+ if _tag is not None:
+ tag = _tag.value
+ else:
+ tag = None
+
+ return CertificateSigningRequestBuilder(
+ self._subject_name,
+ self._extensions,
+ [*self._attributes, (oid, value, tag)],
+ )
+
+ def sign(
+ self,
+ private_key: CertificateIssuerPrivateKeyTypes,
+ algorithm: _AllowedHashTypes | None,
+ backend: typing.Any = None,
+ *,
+ rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
+ ) -> CertificateSigningRequest:
+ """
+ Signs the request using the requestor's private key.
+ """
+ if self._subject_name is None:
+ raise ValueError("A CertificateSigningRequest must have a subject")
+
+ if rsa_padding is not None:
+ if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
+ raise TypeError("Padding must be PSS or PKCS1v15")
+ if not isinstance(private_key, rsa.RSAPrivateKey):
+ raise TypeError("Padding is only supported for RSA keys")
+
+ return rust_x509.create_x509_csr(
+ self, private_key, algorithm, rsa_padding
+ )
+
+
+class CertificateBuilder:
+ _extensions: list[Extension[ExtensionType]]
+
+ def __init__(
+ self,
+ issuer_name: Name | None = None,
+ subject_name: Name | None = None,
+ public_key: CertificatePublicKeyTypes | None = None,
+ serial_number: int | None = None,
+ not_valid_before: datetime.datetime | None = None,
+ not_valid_after: datetime.datetime | None = None,
+ extensions: list[Extension[ExtensionType]] = [],
+ ) -> None:
+ self._version = Version.v3
+ self._issuer_name = issuer_name
+ self._subject_name = subject_name
+ self._public_key = public_key
+ self._serial_number = serial_number
+ self._not_valid_before = not_valid_before
+ self._not_valid_after = not_valid_after
+ self._extensions = extensions
+
+ def issuer_name(self, name: Name) -> CertificateBuilder:
+ """
+ Sets the CA's distinguished name.
+ """
+ if not isinstance(name, Name):
+ raise TypeError("Expecting x509.Name object.")
+ if self._issuer_name is not None:
+ raise ValueError("The issuer name may only be set once.")
+ return CertificateBuilder(
+ name,
+ self._subject_name,
+ self._public_key,
+ self._serial_number,
+ self._not_valid_before,
+ self._not_valid_after,
+ self._extensions,
+ )
+
+ def subject_name(self, name: Name) -> CertificateBuilder:
+ """
+ Sets the requestor's distinguished name.
+ """
+ if not isinstance(name, Name):
+ raise TypeError("Expecting x509.Name object.")
+ if self._subject_name is not None:
+ raise ValueError("The subject name may only be set once.")
+ return CertificateBuilder(
+ self._issuer_name,
+ name,
+ self._public_key,
+ self._serial_number,
+ self._not_valid_before,
+ self._not_valid_after,
+ self._extensions,
+ )
+
+ def public_key(
+ self,
+ key: CertificatePublicKeyTypes,
+ ) -> CertificateBuilder:
+ """
+ Sets the requestor's public key (as found in the signing request).
+ """
+ if not isinstance(
+ key,
+ (
+ dsa.DSAPublicKey,
+ rsa.RSAPublicKey,
+ ec.EllipticCurvePublicKey,
+ ed25519.Ed25519PublicKey,
+ ed448.Ed448PublicKey,
+ x25519.X25519PublicKey,
+ x448.X448PublicKey,
+ ),
+ ):
+ raise TypeError(
+ "Expecting one of DSAPublicKey, RSAPublicKey,"
+ " EllipticCurvePublicKey, Ed25519PublicKey,"
+ " Ed448PublicKey, X25519PublicKey, or "
+ "X448PublicKey."
+ )
+ if self._public_key is not None:
+ raise ValueError("The public key may only be set once.")
+ return CertificateBuilder(
+ self._issuer_name,
+ self._subject_name,
+ key,
+ self._serial_number,
+ self._not_valid_before,
+ self._not_valid_after,
+ self._extensions,
+ )
+
+ def serial_number(self, number: int) -> CertificateBuilder:
+ """
+ Sets the certificate serial number.
+ """
+ if not isinstance(number, int):
+ raise TypeError("Serial number must be of integral type.")
+ if self._serial_number is not None:
+ raise ValueError("The serial number may only be set once.")
+ if number <= 0:
+ raise ValueError("The serial number should be positive.")
+
+ # ASN.1 integers are always signed, so most significant bit must be
+ # zero.
+ if number.bit_length() >= 160: # As defined in RFC 5280
+ raise ValueError(
+ "The serial number should not be more than 159 bits."
+ )
+ return CertificateBuilder(
+ self._issuer_name,
+ self._subject_name,
+ self._public_key,
+ number,
+ self._not_valid_before,
+ self._not_valid_after,
+ self._extensions,
+ )
+
+ def not_valid_before(self, time: datetime.datetime) -> CertificateBuilder:
+ """
+ Sets the certificate activation time.
+ """
+ if not isinstance(time, datetime.datetime):
+ raise TypeError("Expecting datetime object.")
+ if self._not_valid_before is not None:
+ raise ValueError("The not valid before may only be set once.")
+ time = _convert_to_naive_utc_time(time)
+ if time < _EARLIEST_UTC_TIME:
+ raise ValueError(
+ "The not valid before date must be on or after"
+ " 1950 January 1)."
+ )
+ if self._not_valid_after is not None and time > self._not_valid_after:
+ raise ValueError(
+ "The not valid before date must be before the not valid after "
+ "date."
+ )
+ return CertificateBuilder(
+ self._issuer_name,
+ self._subject_name,
+ self._public_key,
+ self._serial_number,
+ time,
+ self._not_valid_after,
+ self._extensions,
+ )
+
+ def not_valid_after(self, time: datetime.datetime) -> CertificateBuilder:
+ """
+ Sets the certificate expiration time.
+ """
+ if not isinstance(time, datetime.datetime):
+ raise TypeError("Expecting datetime object.")
+ if self._not_valid_after is not None:
+ raise ValueError("The not valid after may only be set once.")
+ time = _convert_to_naive_utc_time(time)
+ if time < _EARLIEST_UTC_TIME:
+ raise ValueError(
+ "The not valid after date must be on or after"
+ " 1950 January 1."
+ )
+ if (
+ self._not_valid_before is not None
+ and time < self._not_valid_before
+ ):
+ raise ValueError(
+ "The not valid after date must be after the not valid before "
+ "date."
+ )
+ return CertificateBuilder(
+ self._issuer_name,
+ self._subject_name,
+ self._public_key,
+ self._serial_number,
+ self._not_valid_before,
+ time,
+ self._extensions,
+ )
+
+ def add_extension(
+ self, extval: ExtensionType, critical: bool
+ ) -> CertificateBuilder:
+ """
+ Adds an X.509 extension to the certificate.
+ """
+ if not isinstance(extval, ExtensionType):
+ raise TypeError("extension must be an ExtensionType")
+
+ extension = Extension(extval.oid, critical, extval)
+ _reject_duplicate_extension(extension, self._extensions)
+
+ return CertificateBuilder(
+ self._issuer_name,
+ self._subject_name,
+ self._public_key,
+ self._serial_number,
+ self._not_valid_before,
+ self._not_valid_after,
+ [*self._extensions, extension],
+ )
+
+ def sign(
+ self,
+ private_key: CertificateIssuerPrivateKeyTypes,
+ algorithm: _AllowedHashTypes | None,
+ backend: typing.Any = None,
+ *,
+ rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
+ ) -> Certificate:
+ """
+ Signs the certificate using the CA's private key.
+ """
+ if self._subject_name is None:
+ raise ValueError("A certificate must have a subject name")
+
+ if self._issuer_name is None:
+ raise ValueError("A certificate must have an issuer name")
+
+ if self._serial_number is None:
+ raise ValueError("A certificate must have a serial number")
+
+ if self._not_valid_before is None:
+ raise ValueError("A certificate must have a not valid before time")
+
+ if self._not_valid_after is None:
+ raise ValueError("A certificate must have a not valid after time")
+
+ if self._public_key is None:
+ raise ValueError("A certificate must have a public key")
+
+ if rsa_padding is not None:
+ if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
+ raise TypeError("Padding must be PSS or PKCS1v15")
+ if not isinstance(private_key, rsa.RSAPrivateKey):
+ raise TypeError("Padding is only supported for RSA keys")
+
+ return rust_x509.create_x509_certificate(
+ self, private_key, algorithm, rsa_padding
+ )
+
+
+class CertificateRevocationListBuilder:
+ _extensions: list[Extension[ExtensionType]]
+ _revoked_certificates: list[RevokedCertificate]
+
+ def __init__(
+ self,
+ issuer_name: Name | None = None,
+ last_update: datetime.datetime | None = None,
+ next_update: datetime.datetime | None = None,
+ extensions: list[Extension[ExtensionType]] = [],
+ revoked_certificates: list[RevokedCertificate] = [],
+ ):
+ self._issuer_name = issuer_name
+ self._last_update = last_update
+ self._next_update = next_update
+ self._extensions = extensions
+ self._revoked_certificates = revoked_certificates
+
+ def issuer_name(
+ self, issuer_name: Name
+ ) -> CertificateRevocationListBuilder:
+ if not isinstance(issuer_name, Name):
+ raise TypeError("Expecting x509.Name object.")
+ if self._issuer_name is not None:
+ raise ValueError("The issuer name may only be set once.")
+ return CertificateRevocationListBuilder(
+ issuer_name,
+ self._last_update,
+ self._next_update,
+ self._extensions,
+ self._revoked_certificates,
+ )
+
+ def last_update(
+ self, last_update: datetime.datetime
+ ) -> CertificateRevocationListBuilder:
+ if not isinstance(last_update, datetime.datetime):
+ raise TypeError("Expecting datetime object.")
+ if self._last_update is not None:
+ raise ValueError("Last update may only be set once.")
+ last_update = _convert_to_naive_utc_time(last_update)
+ if last_update < _EARLIEST_UTC_TIME:
+ raise ValueError(
+ "The last update date must be on or after 1950 January 1."
+ )
+ if self._next_update is not None and last_update > self._next_update:
+ raise ValueError(
+ "The last update date must be before the next update date."
+ )
+ return CertificateRevocationListBuilder(
+ self._issuer_name,
+ last_update,
+ self._next_update,
+ self._extensions,
+ self._revoked_certificates,
+ )
+
+ def next_update(
+ self, next_update: datetime.datetime
+ ) -> CertificateRevocationListBuilder:
+ if not isinstance(next_update, datetime.datetime):
+ raise TypeError("Expecting datetime object.")
+ if self._next_update is not None:
+ raise ValueError("Last update may only be set once.")
+ next_update = _convert_to_naive_utc_time(next_update)
+ if next_update < _EARLIEST_UTC_TIME:
+ raise ValueError(
+ "The last update date must be on or after 1950 January 1."
+ )
+ if self._last_update is not None and next_update < self._last_update:
+ raise ValueError(
+ "The next update date must be after the last update date."
+ )
+ return CertificateRevocationListBuilder(
+ self._issuer_name,
+ self._last_update,
+ next_update,
+ self._extensions,
+ self._revoked_certificates,
+ )
+
+ def add_extension(
+ self, extval: ExtensionType, critical: bool
+ ) -> CertificateRevocationListBuilder:
+ """
+ Adds an X.509 extension to the certificate revocation list.
+ """
+ if not isinstance(extval, ExtensionType):
+ raise TypeError("extension must be an ExtensionType")
+
+ extension = Extension(extval.oid, critical, extval)
+ _reject_duplicate_extension(extension, self._extensions)
+ return CertificateRevocationListBuilder(
+ self._issuer_name,
+ self._last_update,
+ self._next_update,
+ [*self._extensions, extension],
+ self._revoked_certificates,
+ )
+
+ def add_revoked_certificate(
+ self, revoked_certificate: RevokedCertificate
+ ) -> CertificateRevocationListBuilder:
+ """
+ Adds a revoked certificate to the CRL.
+ """
+ if not isinstance(revoked_certificate, RevokedCertificate):
+ raise TypeError("Must be an instance of RevokedCertificate")
+
+ return CertificateRevocationListBuilder(
+ self._issuer_name,
+ self._last_update,
+ self._next_update,
+ self._extensions,
+ [*self._revoked_certificates, revoked_certificate],
+ )
+
+ def sign(
+ self,
+ private_key: CertificateIssuerPrivateKeyTypes,
+ algorithm: _AllowedHashTypes | None,
+ backend: typing.Any = None,
+ *,
+ rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
+ ) -> CertificateRevocationList:
+ if self._issuer_name is None:
+ raise ValueError("A CRL must have an issuer name")
+
+ if self._last_update is None:
+ raise ValueError("A CRL must have a last update time")
+
+ if self._next_update is None:
+ raise ValueError("A CRL must have a next update time")
+
+ if rsa_padding is not None:
+ if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
+ raise TypeError("Padding must be PSS or PKCS1v15")
+ if not isinstance(private_key, rsa.RSAPrivateKey):
+ raise TypeError("Padding is only supported for RSA keys")
+
+ return rust_x509.create_x509_crl(
+ self, private_key, algorithm, rsa_padding
+ )
+
+
+class RevokedCertificateBuilder:
+ def __init__(
+ self,
+ serial_number: int | None = None,
+ revocation_date: datetime.datetime | None = None,
+ extensions: list[Extension[ExtensionType]] = [],
+ ):
+ self._serial_number = serial_number
+ self._revocation_date = revocation_date
+ self._extensions = extensions
+
+ def serial_number(self, number: int) -> RevokedCertificateBuilder:
+ if not isinstance(number, int):
+ raise TypeError("Serial number must be of integral type.")
+ if self._serial_number is not None:
+ raise ValueError("The serial number may only be set once.")
+ if number <= 0:
+ raise ValueError("The serial number should be positive")
+
+ # ASN.1 integers are always signed, so most significant bit must be
+ # zero.
+ if number.bit_length() >= 160: # As defined in RFC 5280
+ raise ValueError(
+ "The serial number should not be more than 159 bits."
+ )
+ return RevokedCertificateBuilder(
+ number, self._revocation_date, self._extensions
+ )
+
+ def revocation_date(
+ self, time: datetime.datetime
+ ) -> RevokedCertificateBuilder:
+ if not isinstance(time, datetime.datetime):
+ raise TypeError("Expecting datetime object.")
+ if self._revocation_date is not None:
+ raise ValueError("The revocation date may only be set once.")
+ time = _convert_to_naive_utc_time(time)
+ if time < _EARLIEST_UTC_TIME:
+ raise ValueError(
+ "The revocation date must be on or after 1950 January 1."
+ )
+ return RevokedCertificateBuilder(
+ self._serial_number, time, self._extensions
+ )
+
+ def add_extension(
+ self, extval: ExtensionType, critical: bool
+ ) -> RevokedCertificateBuilder:
+ if not isinstance(extval, ExtensionType):
+ raise TypeError("extension must be an ExtensionType")
+
+ extension = Extension(extval.oid, critical, extval)
+ _reject_duplicate_extension(extension, self._extensions)
+ return RevokedCertificateBuilder(
+ self._serial_number,
+ self._revocation_date,
+ [*self._extensions, extension],
+ )
+
+ def build(self, backend: typing.Any = None) -> RevokedCertificate:
+ if self._serial_number is None:
+ raise ValueError("A revoked certificate must have a serial number")
+ if self._revocation_date is None:
+ raise ValueError(
+ "A revoked certificate must have a revocation date"
+ )
+ return _RawRevokedCertificate(
+ self._serial_number,
+ self._revocation_date,
+ Extensions(self._extensions),
+ )
+
+
+def random_serial_number() -> int:
+ return int.from_bytes(os.urandom(20), "big") >> 1