Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 

380 řádky
12 KiB

  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 datetime
  6. from collections.abc import Iterable
  7. from cryptography import utils, x509
  8. from cryptography.hazmat.bindings._rust import ocsp
  9. from cryptography.hazmat.primitives import hashes
  10. from cryptography.hazmat.primitives.asymmetric.types import (
  11. CertificateIssuerPrivateKeyTypes,
  12. )
  13. from cryptography.x509.base import _reject_duplicate_extension
  14. class OCSPResponderEncoding(utils.Enum):
  15. HASH = "By Hash"
  16. NAME = "By Name"
  17. class OCSPResponseStatus(utils.Enum):
  18. SUCCESSFUL = 0
  19. MALFORMED_REQUEST = 1
  20. INTERNAL_ERROR = 2
  21. TRY_LATER = 3
  22. SIG_REQUIRED = 5
  23. UNAUTHORIZED = 6
  24. _ALLOWED_HASHES = (
  25. hashes.SHA1,
  26. hashes.SHA224,
  27. hashes.SHA256,
  28. hashes.SHA384,
  29. hashes.SHA512,
  30. )
  31. def _verify_algorithm(algorithm: hashes.HashAlgorithm) -> None:
  32. if not isinstance(algorithm, _ALLOWED_HASHES):
  33. raise ValueError(
  34. "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512"
  35. )
  36. class OCSPCertStatus(utils.Enum):
  37. GOOD = 0
  38. REVOKED = 1
  39. UNKNOWN = 2
  40. class _SingleResponse:
  41. def __init__(
  42. self,
  43. resp: tuple[x509.Certificate, x509.Certificate] | None,
  44. resp_hash: tuple[bytes, bytes, int] | None,
  45. algorithm: hashes.HashAlgorithm,
  46. cert_status: OCSPCertStatus,
  47. this_update: datetime.datetime,
  48. next_update: datetime.datetime | None,
  49. revocation_time: datetime.datetime | None,
  50. revocation_reason: x509.ReasonFlags | None,
  51. ):
  52. _verify_algorithm(algorithm)
  53. if not isinstance(this_update, datetime.datetime):
  54. raise TypeError("this_update must be a datetime object")
  55. if next_update is not None and not isinstance(
  56. next_update, datetime.datetime
  57. ):
  58. raise TypeError("next_update must be a datetime object or None")
  59. self._resp = resp
  60. self._resp_hash = resp_hash
  61. self._algorithm = algorithm
  62. self._this_update = this_update
  63. self._next_update = next_update
  64. if not isinstance(cert_status, OCSPCertStatus):
  65. raise TypeError(
  66. "cert_status must be an item from the OCSPCertStatus enum"
  67. )
  68. if cert_status is not OCSPCertStatus.REVOKED:
  69. if revocation_time is not None:
  70. raise ValueError(
  71. "revocation_time can only be provided if the certificate "
  72. "is revoked"
  73. )
  74. if revocation_reason is not None:
  75. raise ValueError(
  76. "revocation_reason can only be provided if the certificate"
  77. " is revoked"
  78. )
  79. else:
  80. if not isinstance(revocation_time, datetime.datetime):
  81. raise TypeError("revocation_time must be a datetime object")
  82. if revocation_reason is not None and not isinstance(
  83. revocation_reason, x509.ReasonFlags
  84. ):
  85. raise TypeError(
  86. "revocation_reason must be an item from the ReasonFlags "
  87. "enum or None"
  88. )
  89. self._cert_status = cert_status
  90. self._revocation_time = revocation_time
  91. self._revocation_reason = revocation_reason
  92. OCSPRequest = ocsp.OCSPRequest
  93. OCSPResponse = ocsp.OCSPResponse
  94. OCSPSingleResponse = ocsp.OCSPSingleResponse
  95. class OCSPRequestBuilder:
  96. def __init__(
  97. self,
  98. request: tuple[
  99. x509.Certificate, x509.Certificate, hashes.HashAlgorithm
  100. ]
  101. | None = None,
  102. request_hash: tuple[bytes, bytes, int, hashes.HashAlgorithm]
  103. | None = None,
  104. extensions: list[x509.Extension[x509.ExtensionType]] = [],
  105. ) -> None:
  106. self._request = request
  107. self._request_hash = request_hash
  108. self._extensions = extensions
  109. def add_certificate(
  110. self,
  111. cert: x509.Certificate,
  112. issuer: x509.Certificate,
  113. algorithm: hashes.HashAlgorithm,
  114. ) -> OCSPRequestBuilder:
  115. if self._request is not None or self._request_hash is not None:
  116. raise ValueError("Only one certificate can be added to a request")
  117. _verify_algorithm(algorithm)
  118. if not isinstance(cert, x509.Certificate) or not isinstance(
  119. issuer, x509.Certificate
  120. ):
  121. raise TypeError("cert and issuer must be a Certificate")
  122. return OCSPRequestBuilder(
  123. (cert, issuer, algorithm), self._request_hash, self._extensions
  124. )
  125. def add_certificate_by_hash(
  126. self,
  127. issuer_name_hash: bytes,
  128. issuer_key_hash: bytes,
  129. serial_number: int,
  130. algorithm: hashes.HashAlgorithm,
  131. ) -> OCSPRequestBuilder:
  132. if self._request is not None or self._request_hash is not None:
  133. raise ValueError("Only one certificate can be added to a request")
  134. if not isinstance(serial_number, int):
  135. raise TypeError("serial_number must be an integer")
  136. _verify_algorithm(algorithm)
  137. utils._check_bytes("issuer_name_hash", issuer_name_hash)
  138. utils._check_bytes("issuer_key_hash", issuer_key_hash)
  139. if algorithm.digest_size != len(
  140. issuer_name_hash
  141. ) or algorithm.digest_size != len(issuer_key_hash):
  142. raise ValueError(
  143. "issuer_name_hash and issuer_key_hash must be the same length "
  144. "as the digest size of the algorithm"
  145. )
  146. return OCSPRequestBuilder(
  147. self._request,
  148. (issuer_name_hash, issuer_key_hash, serial_number, algorithm),
  149. self._extensions,
  150. )
  151. def add_extension(
  152. self, extval: x509.ExtensionType, critical: bool
  153. ) -> OCSPRequestBuilder:
  154. if not isinstance(extval, x509.ExtensionType):
  155. raise TypeError("extension must be an ExtensionType")
  156. extension = x509.Extension(extval.oid, critical, extval)
  157. _reject_duplicate_extension(extension, self._extensions)
  158. return OCSPRequestBuilder(
  159. self._request, self._request_hash, [*self._extensions, extension]
  160. )
  161. def build(self) -> OCSPRequest:
  162. if self._request is None and self._request_hash is None:
  163. raise ValueError("You must add a certificate before building")
  164. return ocsp.create_ocsp_request(self)
  165. class OCSPResponseBuilder:
  166. def __init__(
  167. self,
  168. response: _SingleResponse | None = None,
  169. responder_id: tuple[x509.Certificate, OCSPResponderEncoding]
  170. | None = None,
  171. certs: list[x509.Certificate] | None = None,
  172. extensions: list[x509.Extension[x509.ExtensionType]] = [],
  173. ):
  174. self._response = response
  175. self._responder_id = responder_id
  176. self._certs = certs
  177. self._extensions = extensions
  178. def add_response(
  179. self,
  180. cert: x509.Certificate,
  181. issuer: x509.Certificate,
  182. algorithm: hashes.HashAlgorithm,
  183. cert_status: OCSPCertStatus,
  184. this_update: datetime.datetime,
  185. next_update: datetime.datetime | None,
  186. revocation_time: datetime.datetime | None,
  187. revocation_reason: x509.ReasonFlags | None,
  188. ) -> OCSPResponseBuilder:
  189. if self._response is not None:
  190. raise ValueError("Only one response per OCSPResponse.")
  191. if not isinstance(cert, x509.Certificate) or not isinstance(
  192. issuer, x509.Certificate
  193. ):
  194. raise TypeError("cert and issuer must be a Certificate")
  195. singleresp = _SingleResponse(
  196. (cert, issuer),
  197. None,
  198. algorithm,
  199. cert_status,
  200. this_update,
  201. next_update,
  202. revocation_time,
  203. revocation_reason,
  204. )
  205. return OCSPResponseBuilder(
  206. singleresp,
  207. self._responder_id,
  208. self._certs,
  209. self._extensions,
  210. )
  211. def add_response_by_hash(
  212. self,
  213. issuer_name_hash: bytes,
  214. issuer_key_hash: bytes,
  215. serial_number: int,
  216. algorithm: hashes.HashAlgorithm,
  217. cert_status: OCSPCertStatus,
  218. this_update: datetime.datetime,
  219. next_update: datetime.datetime | None,
  220. revocation_time: datetime.datetime | None,
  221. revocation_reason: x509.ReasonFlags | None,
  222. ) -> OCSPResponseBuilder:
  223. if self._response is not None:
  224. raise ValueError("Only one response per OCSPResponse.")
  225. if not isinstance(serial_number, int):
  226. raise TypeError("serial_number must be an integer")
  227. utils._check_bytes("issuer_name_hash", issuer_name_hash)
  228. utils._check_bytes("issuer_key_hash", issuer_key_hash)
  229. _verify_algorithm(algorithm)
  230. if algorithm.digest_size != len(
  231. issuer_name_hash
  232. ) or algorithm.digest_size != len(issuer_key_hash):
  233. raise ValueError(
  234. "issuer_name_hash and issuer_key_hash must be the same length "
  235. "as the digest size of the algorithm"
  236. )
  237. singleresp = _SingleResponse(
  238. None,
  239. (issuer_name_hash, issuer_key_hash, serial_number),
  240. algorithm,
  241. cert_status,
  242. this_update,
  243. next_update,
  244. revocation_time,
  245. revocation_reason,
  246. )
  247. return OCSPResponseBuilder(
  248. singleresp,
  249. self._responder_id,
  250. self._certs,
  251. self._extensions,
  252. )
  253. def responder_id(
  254. self, encoding: OCSPResponderEncoding, responder_cert: x509.Certificate
  255. ) -> OCSPResponseBuilder:
  256. if self._responder_id is not None:
  257. raise ValueError("responder_id can only be set once")
  258. if not isinstance(responder_cert, x509.Certificate):
  259. raise TypeError("responder_cert must be a Certificate")
  260. if not isinstance(encoding, OCSPResponderEncoding):
  261. raise TypeError(
  262. "encoding must be an element from OCSPResponderEncoding"
  263. )
  264. return OCSPResponseBuilder(
  265. self._response,
  266. (responder_cert, encoding),
  267. self._certs,
  268. self._extensions,
  269. )
  270. def certificates(
  271. self, certs: Iterable[x509.Certificate]
  272. ) -> OCSPResponseBuilder:
  273. if self._certs is not None:
  274. raise ValueError("certificates may only be set once")
  275. certs = list(certs)
  276. if len(certs) == 0:
  277. raise ValueError("certs must not be an empty list")
  278. if not all(isinstance(x, x509.Certificate) for x in certs):
  279. raise TypeError("certs must be a list of Certificates")
  280. return OCSPResponseBuilder(
  281. self._response,
  282. self._responder_id,
  283. certs,
  284. self._extensions,
  285. )
  286. def add_extension(
  287. self, extval: x509.ExtensionType, critical: bool
  288. ) -> OCSPResponseBuilder:
  289. if not isinstance(extval, x509.ExtensionType):
  290. raise TypeError("extension must be an ExtensionType")
  291. extension = x509.Extension(extval.oid, critical, extval)
  292. _reject_duplicate_extension(extension, self._extensions)
  293. return OCSPResponseBuilder(
  294. self._response,
  295. self._responder_id,
  296. self._certs,
  297. [*self._extensions, extension],
  298. )
  299. def sign(
  300. self,
  301. private_key: CertificateIssuerPrivateKeyTypes,
  302. algorithm: hashes.HashAlgorithm | None,
  303. ) -> OCSPResponse:
  304. if self._response is None:
  305. raise ValueError("You must add a response before signing")
  306. if self._responder_id is None:
  307. raise ValueError("You must add a responder_id before signing")
  308. return ocsp.create_ocsp_response(
  309. OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm
  310. )
  311. @classmethod
  312. def build_unsuccessful(
  313. cls, response_status: OCSPResponseStatus
  314. ) -> OCSPResponse:
  315. if not isinstance(response_status, OCSPResponseStatus):
  316. raise TypeError(
  317. "response_status must be an item from OCSPResponseStatus"
  318. )
  319. if response_status is OCSPResponseStatus.SUCCESSFUL:
  320. raise ValueError("response_status cannot be SUCCESSFUL")
  321. return ocsp.create_ocsp_response(response_status, None, None, None)
  322. load_der_ocsp_request = ocsp.load_der_ocsp_request
  323. load_der_ocsp_response = ocsp.load_der_ocsp_response