Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ocsp.py 18KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  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. from __future__ import annotations
  5. import abc
  6. import datetime
  7. import typing
  8. from cryptography import utils, x509
  9. from cryptography.hazmat.bindings._rust import ocsp
  10. from cryptography.hazmat.primitives import hashes, serialization
  11. from cryptography.hazmat.primitives.asymmetric.types import (
  12. CertificateIssuerPrivateKeyTypes,
  13. )
  14. from cryptography.x509.base import (
  15. _EARLIEST_UTC_TIME,
  16. _convert_to_naive_utc_time,
  17. _reject_duplicate_extension,
  18. )
  19. class OCSPResponderEncoding(utils.Enum):
  20. HASH = "By Hash"
  21. NAME = "By Name"
  22. class OCSPResponseStatus(utils.Enum):
  23. SUCCESSFUL = 0
  24. MALFORMED_REQUEST = 1
  25. INTERNAL_ERROR = 2
  26. TRY_LATER = 3
  27. SIG_REQUIRED = 5
  28. UNAUTHORIZED = 6
  29. _ALLOWED_HASHES = (
  30. hashes.SHA1,
  31. hashes.SHA224,
  32. hashes.SHA256,
  33. hashes.SHA384,
  34. hashes.SHA512,
  35. )
  36. def _verify_algorithm(algorithm: hashes.HashAlgorithm) -> None:
  37. if not isinstance(algorithm, _ALLOWED_HASHES):
  38. raise ValueError(
  39. "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512"
  40. )
  41. class OCSPCertStatus(utils.Enum):
  42. GOOD = 0
  43. REVOKED = 1
  44. UNKNOWN = 2
  45. class _SingleResponse:
  46. def __init__(
  47. self,
  48. cert: x509.Certificate,
  49. issuer: x509.Certificate,
  50. algorithm: hashes.HashAlgorithm,
  51. cert_status: OCSPCertStatus,
  52. this_update: datetime.datetime,
  53. next_update: typing.Optional[datetime.datetime],
  54. revocation_time: typing.Optional[datetime.datetime],
  55. revocation_reason: typing.Optional[x509.ReasonFlags],
  56. ):
  57. if not isinstance(cert, x509.Certificate) or not isinstance(
  58. issuer, x509.Certificate
  59. ):
  60. raise TypeError("cert and issuer must be a Certificate")
  61. _verify_algorithm(algorithm)
  62. if not isinstance(this_update, datetime.datetime):
  63. raise TypeError("this_update must be a datetime object")
  64. if next_update is not None and not isinstance(
  65. next_update, datetime.datetime
  66. ):
  67. raise TypeError("next_update must be a datetime object or None")
  68. self._cert = cert
  69. self._issuer = issuer
  70. self._algorithm = algorithm
  71. self._this_update = this_update
  72. self._next_update = next_update
  73. if not isinstance(cert_status, OCSPCertStatus):
  74. raise TypeError(
  75. "cert_status must be an item from the OCSPCertStatus enum"
  76. )
  77. if cert_status is not OCSPCertStatus.REVOKED:
  78. if revocation_time is not None:
  79. raise ValueError(
  80. "revocation_time can only be provided if the certificate "
  81. "is revoked"
  82. )
  83. if revocation_reason is not None:
  84. raise ValueError(
  85. "revocation_reason can only be provided if the certificate"
  86. " is revoked"
  87. )
  88. else:
  89. if not isinstance(revocation_time, datetime.datetime):
  90. raise TypeError("revocation_time must be a datetime object")
  91. revocation_time = _convert_to_naive_utc_time(revocation_time)
  92. if revocation_time < _EARLIEST_UTC_TIME:
  93. raise ValueError(
  94. "The revocation_time must be on or after"
  95. " 1950 January 1."
  96. )
  97. if revocation_reason is not None and not isinstance(
  98. revocation_reason, x509.ReasonFlags
  99. ):
  100. raise TypeError(
  101. "revocation_reason must be an item from the ReasonFlags "
  102. "enum or None"
  103. )
  104. self._cert_status = cert_status
  105. self._revocation_time = revocation_time
  106. self._revocation_reason = revocation_reason
  107. class OCSPRequest(metaclass=abc.ABCMeta):
  108. @property
  109. @abc.abstractmethod
  110. def issuer_key_hash(self) -> bytes:
  111. """
  112. The hash of the issuer public key
  113. """
  114. @property
  115. @abc.abstractmethod
  116. def issuer_name_hash(self) -> bytes:
  117. """
  118. The hash of the issuer name
  119. """
  120. @property
  121. @abc.abstractmethod
  122. def hash_algorithm(self) -> hashes.HashAlgorithm:
  123. """
  124. The hash algorithm used in the issuer name and key hashes
  125. """
  126. @property
  127. @abc.abstractmethod
  128. def serial_number(self) -> int:
  129. """
  130. The serial number of the cert whose status is being checked
  131. """
  132. @abc.abstractmethod
  133. def public_bytes(self, encoding: serialization.Encoding) -> bytes:
  134. """
  135. Serializes the request to DER
  136. """
  137. @property
  138. @abc.abstractmethod
  139. def extensions(self) -> x509.Extensions:
  140. """
  141. The list of request extensions. Not single request extensions.
  142. """
  143. class OCSPSingleResponse(metaclass=abc.ABCMeta):
  144. @property
  145. @abc.abstractmethod
  146. def certificate_status(self) -> OCSPCertStatus:
  147. """
  148. The status of the certificate (an element from the OCSPCertStatus enum)
  149. """
  150. @property
  151. @abc.abstractmethod
  152. def revocation_time(self) -> typing.Optional[datetime.datetime]:
  153. """
  154. The date of when the certificate was revoked or None if not
  155. revoked.
  156. """
  157. @property
  158. @abc.abstractmethod
  159. def revocation_reason(self) -> typing.Optional[x509.ReasonFlags]:
  160. """
  161. The reason the certificate was revoked or None if not specified or
  162. not revoked.
  163. """
  164. @property
  165. @abc.abstractmethod
  166. def this_update(self) -> datetime.datetime:
  167. """
  168. The most recent time at which the status being indicated is known by
  169. the responder to have been correct
  170. """
  171. @property
  172. @abc.abstractmethod
  173. def next_update(self) -> typing.Optional[datetime.datetime]:
  174. """
  175. The time when newer information will be available
  176. """
  177. @property
  178. @abc.abstractmethod
  179. def issuer_key_hash(self) -> bytes:
  180. """
  181. The hash of the issuer public key
  182. """
  183. @property
  184. @abc.abstractmethod
  185. def issuer_name_hash(self) -> bytes:
  186. """
  187. The hash of the issuer name
  188. """
  189. @property
  190. @abc.abstractmethod
  191. def hash_algorithm(self) -> hashes.HashAlgorithm:
  192. """
  193. The hash algorithm used in the issuer name and key hashes
  194. """
  195. @property
  196. @abc.abstractmethod
  197. def serial_number(self) -> int:
  198. """
  199. The serial number of the cert whose status is being checked
  200. """
  201. class OCSPResponse(metaclass=abc.ABCMeta):
  202. @property
  203. @abc.abstractmethod
  204. def responses(self) -> typing.Iterator[OCSPSingleResponse]:
  205. """
  206. An iterator over the individual SINGLERESP structures in the
  207. response
  208. """
  209. @property
  210. @abc.abstractmethod
  211. def response_status(self) -> OCSPResponseStatus:
  212. """
  213. The status of the response. This is a value from the OCSPResponseStatus
  214. enumeration
  215. """
  216. @property
  217. @abc.abstractmethod
  218. def signature_algorithm_oid(self) -> x509.ObjectIdentifier:
  219. """
  220. The ObjectIdentifier of the signature algorithm
  221. """
  222. @property
  223. @abc.abstractmethod
  224. def signature_hash_algorithm(
  225. self,
  226. ) -> typing.Optional[hashes.HashAlgorithm]:
  227. """
  228. Returns a HashAlgorithm corresponding to the type of the digest signed
  229. """
  230. @property
  231. @abc.abstractmethod
  232. def signature(self) -> bytes:
  233. """
  234. The signature bytes
  235. """
  236. @property
  237. @abc.abstractmethod
  238. def tbs_response_bytes(self) -> bytes:
  239. """
  240. The tbsResponseData bytes
  241. """
  242. @property
  243. @abc.abstractmethod
  244. def certificates(self) -> typing.List[x509.Certificate]:
  245. """
  246. A list of certificates used to help build a chain to verify the OCSP
  247. response. This situation occurs when the OCSP responder uses a delegate
  248. certificate.
  249. """
  250. @property
  251. @abc.abstractmethod
  252. def responder_key_hash(self) -> typing.Optional[bytes]:
  253. """
  254. The responder's key hash or None
  255. """
  256. @property
  257. @abc.abstractmethod
  258. def responder_name(self) -> typing.Optional[x509.Name]:
  259. """
  260. The responder's Name or None
  261. """
  262. @property
  263. @abc.abstractmethod
  264. def produced_at(self) -> datetime.datetime:
  265. """
  266. The time the response was produced
  267. """
  268. @property
  269. @abc.abstractmethod
  270. def certificate_status(self) -> OCSPCertStatus:
  271. """
  272. The status of the certificate (an element from the OCSPCertStatus enum)
  273. """
  274. @property
  275. @abc.abstractmethod
  276. def revocation_time(self) -> typing.Optional[datetime.datetime]:
  277. """
  278. The date of when the certificate was revoked or None if not
  279. revoked.
  280. """
  281. @property
  282. @abc.abstractmethod
  283. def revocation_reason(self) -> typing.Optional[x509.ReasonFlags]:
  284. """
  285. The reason the certificate was revoked or None if not specified or
  286. not revoked.
  287. """
  288. @property
  289. @abc.abstractmethod
  290. def this_update(self) -> datetime.datetime:
  291. """
  292. The most recent time at which the status being indicated is known by
  293. the responder to have been correct
  294. """
  295. @property
  296. @abc.abstractmethod
  297. def next_update(self) -> typing.Optional[datetime.datetime]:
  298. """
  299. The time when newer information will be available
  300. """
  301. @property
  302. @abc.abstractmethod
  303. def issuer_key_hash(self) -> bytes:
  304. """
  305. The hash of the issuer public key
  306. """
  307. @property
  308. @abc.abstractmethod
  309. def issuer_name_hash(self) -> bytes:
  310. """
  311. The hash of the issuer name
  312. """
  313. @property
  314. @abc.abstractmethod
  315. def hash_algorithm(self) -> hashes.HashAlgorithm:
  316. """
  317. The hash algorithm used in the issuer name and key hashes
  318. """
  319. @property
  320. @abc.abstractmethod
  321. def serial_number(self) -> int:
  322. """
  323. The serial number of the cert whose status is being checked
  324. """
  325. @property
  326. @abc.abstractmethod
  327. def extensions(self) -> x509.Extensions:
  328. """
  329. The list of response extensions. Not single response extensions.
  330. """
  331. @property
  332. @abc.abstractmethod
  333. def single_extensions(self) -> x509.Extensions:
  334. """
  335. The list of single response extensions. Not response extensions.
  336. """
  337. @abc.abstractmethod
  338. def public_bytes(self, encoding: serialization.Encoding) -> bytes:
  339. """
  340. Serializes the response to DER
  341. """
  342. class OCSPRequestBuilder:
  343. def __init__(
  344. self,
  345. request: typing.Optional[
  346. typing.Tuple[
  347. x509.Certificate, x509.Certificate, hashes.HashAlgorithm
  348. ]
  349. ] = None,
  350. request_hash: typing.Optional[
  351. typing.Tuple[bytes, bytes, int, hashes.HashAlgorithm]
  352. ] = None,
  353. extensions: typing.List[x509.Extension[x509.ExtensionType]] = [],
  354. ) -> None:
  355. self._request = request
  356. self._request_hash = request_hash
  357. self._extensions = extensions
  358. def add_certificate(
  359. self,
  360. cert: x509.Certificate,
  361. issuer: x509.Certificate,
  362. algorithm: hashes.HashAlgorithm,
  363. ) -> OCSPRequestBuilder:
  364. if self._request is not None or self._request_hash is not None:
  365. raise ValueError("Only one certificate can be added to a request")
  366. _verify_algorithm(algorithm)
  367. if not isinstance(cert, x509.Certificate) or not isinstance(
  368. issuer, x509.Certificate
  369. ):
  370. raise TypeError("cert and issuer must be a Certificate")
  371. return OCSPRequestBuilder(
  372. (cert, issuer, algorithm), self._request_hash, self._extensions
  373. )
  374. def add_certificate_by_hash(
  375. self,
  376. issuer_name_hash: bytes,
  377. issuer_key_hash: bytes,
  378. serial_number: int,
  379. algorithm: hashes.HashAlgorithm,
  380. ) -> OCSPRequestBuilder:
  381. if self._request is not None or self._request_hash is not None:
  382. raise ValueError("Only one certificate can be added to a request")
  383. if not isinstance(serial_number, int):
  384. raise TypeError("serial_number must be an integer")
  385. _verify_algorithm(algorithm)
  386. utils._check_bytes("issuer_name_hash", issuer_name_hash)
  387. utils._check_bytes("issuer_key_hash", issuer_key_hash)
  388. if algorithm.digest_size != len(
  389. issuer_name_hash
  390. ) or algorithm.digest_size != len(issuer_key_hash):
  391. raise ValueError(
  392. "issuer_name_hash and issuer_key_hash must be the same length "
  393. "as the digest size of the algorithm"
  394. )
  395. return OCSPRequestBuilder(
  396. self._request,
  397. (issuer_name_hash, issuer_key_hash, serial_number, algorithm),
  398. self._extensions,
  399. )
  400. def add_extension(
  401. self, extval: x509.ExtensionType, critical: bool
  402. ) -> OCSPRequestBuilder:
  403. if not isinstance(extval, x509.ExtensionType):
  404. raise TypeError("extension must be an ExtensionType")
  405. extension = x509.Extension(extval.oid, critical, extval)
  406. _reject_duplicate_extension(extension, self._extensions)
  407. return OCSPRequestBuilder(
  408. self._request, self._request_hash, self._extensions + [extension]
  409. )
  410. def build(self) -> OCSPRequest:
  411. if self._request is None and self._request_hash is None:
  412. raise ValueError("You must add a certificate before building")
  413. return ocsp.create_ocsp_request(self)
  414. class OCSPResponseBuilder:
  415. def __init__(
  416. self,
  417. response: typing.Optional[_SingleResponse] = None,
  418. responder_id: typing.Optional[
  419. typing.Tuple[x509.Certificate, OCSPResponderEncoding]
  420. ] = None,
  421. certs: typing.Optional[typing.List[x509.Certificate]] = None,
  422. extensions: typing.List[x509.Extension[x509.ExtensionType]] = [],
  423. ):
  424. self._response = response
  425. self._responder_id = responder_id
  426. self._certs = certs
  427. self._extensions = extensions
  428. def add_response(
  429. self,
  430. cert: x509.Certificate,
  431. issuer: x509.Certificate,
  432. algorithm: hashes.HashAlgorithm,
  433. cert_status: OCSPCertStatus,
  434. this_update: datetime.datetime,
  435. next_update: typing.Optional[datetime.datetime],
  436. revocation_time: typing.Optional[datetime.datetime],
  437. revocation_reason: typing.Optional[x509.ReasonFlags],
  438. ) -> OCSPResponseBuilder:
  439. if self._response is not None:
  440. raise ValueError("Only one response per OCSPResponse.")
  441. singleresp = _SingleResponse(
  442. cert,
  443. issuer,
  444. algorithm,
  445. cert_status,
  446. this_update,
  447. next_update,
  448. revocation_time,
  449. revocation_reason,
  450. )
  451. return OCSPResponseBuilder(
  452. singleresp,
  453. self._responder_id,
  454. self._certs,
  455. self._extensions,
  456. )
  457. def responder_id(
  458. self, encoding: OCSPResponderEncoding, responder_cert: x509.Certificate
  459. ) -> OCSPResponseBuilder:
  460. if self._responder_id is not None:
  461. raise ValueError("responder_id can only be set once")
  462. if not isinstance(responder_cert, x509.Certificate):
  463. raise TypeError("responder_cert must be a Certificate")
  464. if not isinstance(encoding, OCSPResponderEncoding):
  465. raise TypeError(
  466. "encoding must be an element from OCSPResponderEncoding"
  467. )
  468. return OCSPResponseBuilder(
  469. self._response,
  470. (responder_cert, encoding),
  471. self._certs,
  472. self._extensions,
  473. )
  474. def certificates(
  475. self, certs: typing.Iterable[x509.Certificate]
  476. ) -> OCSPResponseBuilder:
  477. if self._certs is not None:
  478. raise ValueError("certificates may only be set once")
  479. certs = list(certs)
  480. if len(certs) == 0:
  481. raise ValueError("certs must not be an empty list")
  482. if not all(isinstance(x, x509.Certificate) for x in certs):
  483. raise TypeError("certs must be a list of Certificates")
  484. return OCSPResponseBuilder(
  485. self._response,
  486. self._responder_id,
  487. certs,
  488. self._extensions,
  489. )
  490. def add_extension(
  491. self, extval: x509.ExtensionType, critical: bool
  492. ) -> OCSPResponseBuilder:
  493. if not isinstance(extval, x509.ExtensionType):
  494. raise TypeError("extension must be an ExtensionType")
  495. extension = x509.Extension(extval.oid, critical, extval)
  496. _reject_duplicate_extension(extension, self._extensions)
  497. return OCSPResponseBuilder(
  498. self._response,
  499. self._responder_id,
  500. self._certs,
  501. self._extensions + [extension],
  502. )
  503. def sign(
  504. self,
  505. private_key: CertificateIssuerPrivateKeyTypes,
  506. algorithm: typing.Optional[hashes.HashAlgorithm],
  507. ) -> OCSPResponse:
  508. if self._response is None:
  509. raise ValueError("You must add a response before signing")
  510. if self._responder_id is None:
  511. raise ValueError("You must add a responder_id before signing")
  512. return ocsp.create_ocsp_response(
  513. OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm
  514. )
  515. @classmethod
  516. def build_unsuccessful(
  517. cls, response_status: OCSPResponseStatus
  518. ) -> OCSPResponse:
  519. if not isinstance(response_status, OCSPResponseStatus):
  520. raise TypeError(
  521. "response_status must be an item from OCSPResponseStatus"
  522. )
  523. if response_status is OCSPResponseStatus.SUCCESSFUL:
  524. raise ValueError("response_status cannot be SUCCESSFUL")
  525. return ocsp.create_ocsp_response(response_status, None, None, None)
  526. def load_der_ocsp_request(data: bytes) -> OCSPRequest:
  527. return ocsp.load_der_ocsp_request(data)
  528. def load_der_ocsp_response(data: bytes) -> OCSPResponse:
  529. return ocsp.load_der_ocsp_response(data)