Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
ocsp.py380 linesDownload Raw Back to x509
1# This file is dual licensed under the terms of the Apache License, Version2# 2.0, and the BSD License. See the LICENSE file in the root of this repository3# for complete details.4 5from __future__ import annotations6 7import datetime8from collections.abc import Iterable9 10from cryptography import utils, x50911from cryptography.hazmat.bindings._rust import ocsp12from cryptography.hazmat.primitives import hashes13from cryptography.hazmat.primitives.asymmetric.types import (14    CertificateIssuerPrivateKeyTypes,15)16from cryptography.x509.base import _reject_duplicate_extension17 18 19class OCSPResponderEncoding(utils.Enum):20    HASH = "By Hash"21    NAME = "By Name"22 23 24class OCSPResponseStatus(utils.Enum):25    SUCCESSFUL = 026    MALFORMED_REQUEST = 127    INTERNAL_ERROR = 228    TRY_LATER = 329    SIG_REQUIRED = 530    UNAUTHORIZED = 631 32 33_ALLOWED_HASHES = (34    hashes.SHA1,35    hashes.SHA224,36    hashes.SHA256,37    hashes.SHA384,38    hashes.SHA512,39)40 41 42def _verify_algorithm(algorithm: hashes.HashAlgorithm) -> None:43    if not isinstance(algorithm, _ALLOWED_HASHES):44        raise ValueError(45            "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512"46        )47 48 49class OCSPCertStatus(utils.Enum):50    GOOD = 051    REVOKED = 152    UNKNOWN = 253 54 55class _SingleResponse:56    def __init__(57        self,58        resp: tuple[x509.Certificate, x509.Certificate] | None,59        resp_hash: tuple[bytes, bytes, int] | None,60        algorithm: hashes.HashAlgorithm,61        cert_status: OCSPCertStatus,62        this_update: datetime.datetime,63        next_update: datetime.datetime | None,64        revocation_time: datetime.datetime | None,65        revocation_reason: x509.ReasonFlags | None,66    ):67        _verify_algorithm(algorithm)68        if not isinstance(this_update, datetime.datetime):69            raise TypeError("this_update must be a datetime object")70        if next_update is not None and not isinstance(71            next_update, datetime.datetime72        ):73            raise TypeError("next_update must be a datetime object or None")74 75        self._resp = resp76        self._resp_hash = resp_hash77        self._algorithm = algorithm78        self._this_update = this_update79        self._next_update = next_update80 81        if not isinstance(cert_status, OCSPCertStatus):82            raise TypeError(83                "cert_status must be an item from the OCSPCertStatus enum"84            )85        if cert_status is not OCSPCertStatus.REVOKED:86            if revocation_time is not None:87                raise ValueError(88                    "revocation_time can only be provided if the certificate "89                    "is revoked"90                )91            if revocation_reason is not None:92                raise ValueError(93                    "revocation_reason can only be provided if the certificate"94                    " is revoked"95                )96        else:97            if not isinstance(revocation_time, datetime.datetime):98                raise TypeError("revocation_time must be a datetime object")99 100            if revocation_reason is not None and not isinstance(101                revocation_reason, x509.ReasonFlags102            ):103                raise TypeError(104                    "revocation_reason must be an item from the ReasonFlags "105                    "enum or None"106                )107 108        self._cert_status = cert_status109        self._revocation_time = revocation_time110        self._revocation_reason = revocation_reason111 112 113OCSPRequest = ocsp.OCSPRequest114OCSPResponse = ocsp.OCSPResponse115OCSPSingleResponse = ocsp.OCSPSingleResponse116 117 118class OCSPRequestBuilder:119    def __init__(120        self,121        request: tuple[122            x509.Certificate, x509.Certificate, hashes.HashAlgorithm123        ]124        | None = None,125        request_hash: tuple[bytes, bytes, int, hashes.HashAlgorithm]126        | None = None,127        extensions: list[x509.Extension[x509.ExtensionType]] = [],128    ) -> None:129        self._request = request130        self._request_hash = request_hash131        self._extensions = extensions132 133    def add_certificate(134        self,135        cert: x509.Certificate,136        issuer: x509.Certificate,137        algorithm: hashes.HashAlgorithm,138    ) -> OCSPRequestBuilder:139        if self._request is not None or self._request_hash is not None:140            raise ValueError("Only one certificate can be added to a request")141 142        _verify_algorithm(algorithm)143        if not isinstance(cert, x509.Certificate) or not isinstance(144            issuer, x509.Certificate145        ):146            raise TypeError("cert and issuer must be a Certificate")147 148        return OCSPRequestBuilder(149            (cert, issuer, algorithm), self._request_hash, self._extensions150        )151 152    def add_certificate_by_hash(153        self,154        issuer_name_hash: bytes,155        issuer_key_hash: bytes,156        serial_number: int,157        algorithm: hashes.HashAlgorithm,158    ) -> OCSPRequestBuilder:159        if self._request is not None or self._request_hash is not None:160            raise ValueError("Only one certificate can be added to a request")161 162        if not isinstance(serial_number, int):163            raise TypeError("serial_number must be an integer")164 165        _verify_algorithm(algorithm)166        utils._check_bytes("issuer_name_hash", issuer_name_hash)167        utils._check_bytes("issuer_key_hash", issuer_key_hash)168        if algorithm.digest_size != len(169            issuer_name_hash170        ) or algorithm.digest_size != len(issuer_key_hash):171            raise ValueError(172                "issuer_name_hash and issuer_key_hash must be the same length "173                "as the digest size of the algorithm"174            )175 176        return OCSPRequestBuilder(177            self._request,178            (issuer_name_hash, issuer_key_hash, serial_number, algorithm),179            self._extensions,180        )181 182    def add_extension(183        self, extval: x509.ExtensionType, critical: bool184    ) -> OCSPRequestBuilder:185        if not isinstance(extval, x509.ExtensionType):186            raise TypeError("extension must be an ExtensionType")187 188        extension = x509.Extension(extval.oid, critical, extval)189        _reject_duplicate_extension(extension, self._extensions)190 191        return OCSPRequestBuilder(192            self._request, self._request_hash, [*self._extensions, extension]193        )194 195    def build(self) -> OCSPRequest:196        if self._request is None and self._request_hash is None:197            raise ValueError("You must add a certificate before building")198 199        return ocsp.create_ocsp_request(self)200 201 202class OCSPResponseBuilder:203    def __init__(204        self,205        response: _SingleResponse | None = None,206        responder_id: tuple[x509.Certificate, OCSPResponderEncoding]207        | None = None,208        certs: list[x509.Certificate] | None = None,209        extensions: list[x509.Extension[x509.ExtensionType]] = [],210    ):211        self._response = response212        self._responder_id = responder_id213        self._certs = certs214        self._extensions = extensions215 216    def add_response(217        self,218        cert: x509.Certificate,219        issuer: x509.Certificate,220        algorithm: hashes.HashAlgorithm,221        cert_status: OCSPCertStatus,222        this_update: datetime.datetime,223        next_update: datetime.datetime | None,224        revocation_time: datetime.datetime | None,225        revocation_reason: x509.ReasonFlags | None,226    ) -> OCSPResponseBuilder:227        if self._response is not None:228            raise ValueError("Only one response per OCSPResponse.")229 230        if not isinstance(cert, x509.Certificate) or not isinstance(231            issuer, x509.Certificate232        ):233            raise TypeError("cert and issuer must be a Certificate")234 235        singleresp = _SingleResponse(236            (cert, issuer),237            None,238            algorithm,239            cert_status,240            this_update,241            next_update,242            revocation_time,243            revocation_reason,244        )245        return OCSPResponseBuilder(246            singleresp,247            self._responder_id,248            self._certs,249            self._extensions,250        )251 252    def add_response_by_hash(253        self,254        issuer_name_hash: bytes,255        issuer_key_hash: bytes,256        serial_number: int,257        algorithm: hashes.HashAlgorithm,258        cert_status: OCSPCertStatus,259        this_update: datetime.datetime,260        next_update: datetime.datetime | None,261        revocation_time: datetime.datetime | None,262        revocation_reason: x509.ReasonFlags | None,263    ) -> OCSPResponseBuilder:264        if self._response is not None:265            raise ValueError("Only one response per OCSPResponse.")266 267        if not isinstance(serial_number, int):268            raise TypeError("serial_number must be an integer")269 270        utils._check_bytes("issuer_name_hash", issuer_name_hash)271        utils._check_bytes("issuer_key_hash", issuer_key_hash)272        _verify_algorithm(algorithm)273        if algorithm.digest_size != len(274            issuer_name_hash275        ) or algorithm.digest_size != len(issuer_key_hash):276            raise ValueError(277                "issuer_name_hash and issuer_key_hash must be the same length "278                "as the digest size of the algorithm"279            )280 281        singleresp = _SingleResponse(282            None,283            (issuer_name_hash, issuer_key_hash, serial_number),284            algorithm,285            cert_status,286            this_update,287            next_update,288            revocation_time,289            revocation_reason,290        )291        return OCSPResponseBuilder(292            singleresp,293            self._responder_id,294            self._certs,295            self._extensions,296        )297 298    def responder_id(299        self, encoding: OCSPResponderEncoding, responder_cert: x509.Certificate300    ) -> OCSPResponseBuilder:301        if self._responder_id is not None:302            raise ValueError("responder_id can only be set once")303        if not isinstance(responder_cert, x509.Certificate):304            raise TypeError("responder_cert must be a Certificate")305        if not isinstance(encoding, OCSPResponderEncoding):306            raise TypeError(307                "encoding must be an element from OCSPResponderEncoding"308            )309 310        return OCSPResponseBuilder(311            self._response,312            (responder_cert, encoding),313            self._certs,314            self._extensions,315        )316 317    def certificates(318        self, certs: Iterable[x509.Certificate]319    ) -> OCSPResponseBuilder:320        if self._certs is not None:321            raise ValueError("certificates may only be set once")322        certs = list(certs)323        if len(certs) == 0:324            raise ValueError("certs must not be an empty list")325        if not all(isinstance(x, x509.Certificate) for x in certs):326            raise TypeError("certs must be a list of Certificates")327        return OCSPResponseBuilder(328            self._response,329            self._responder_id,330            certs,331            self._extensions,332        )333 334    def add_extension(335        self, extval: x509.ExtensionType, critical: bool336    ) -> OCSPResponseBuilder:337        if not isinstance(extval, x509.ExtensionType):338            raise TypeError("extension must be an ExtensionType")339 340        extension = x509.Extension(extval.oid, critical, extval)341        _reject_duplicate_extension(extension, self._extensions)342 343        return OCSPResponseBuilder(344            self._response,345            self._responder_id,346            self._certs,347            [*self._extensions, extension],348        )349 350    def sign(351        self,352        private_key: CertificateIssuerPrivateKeyTypes,353        algorithm: hashes.HashAlgorithm | None,354    ) -> OCSPResponse:355        if self._response is None:356            raise ValueError("You must add a response before signing")357        if self._responder_id is None:358            raise ValueError("You must add a responder_id before signing")359 360        return ocsp.create_ocsp_response(361            OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm362        )363 364    @classmethod365    def build_unsuccessful(366        cls, response_status: OCSPResponseStatus367    ) -> OCSPResponse:368        if not isinstance(response_status, OCSPResponseStatus):369            raise TypeError(370                "response_status must be an item from OCSPResponseStatus"371            )372        if response_status is OCSPResponseStatus.SUCCESSFUL:373            raise ValueError("response_status cannot be SUCCESSFUL")374 375        return ocsp.create_ocsp_response(response_status, None, None, None)376 377 378load_der_ocsp_request = ocsp.load_der_ocsp_request379load_der_ocsp_response = ocsp.load_der_ocsp_response380