backend.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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. from cryptography.hazmat.bindings._rust import openssl as rust_openssl
  6. from cryptography.hazmat.bindings.openssl import binding
  7. from cryptography.hazmat.primitives import hashes
  8. from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding
  9. from cryptography.hazmat.primitives.asymmetric import ec
  10. from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
  11. from cryptography.hazmat.primitives.asymmetric.padding import (
  12. MGF1,
  13. OAEP,
  14. PSS,
  15. PKCS1v15,
  16. )
  17. from cryptography.hazmat.primitives.ciphers import (
  18. CipherAlgorithm,
  19. )
  20. from cryptography.hazmat.primitives.ciphers.algorithms import (
  21. AES,
  22. )
  23. from cryptography.hazmat.primitives.ciphers.modes import (
  24. CBC,
  25. Mode,
  26. )
  27. class Backend:
  28. """
  29. OpenSSL API binding interfaces.
  30. """
  31. name = "openssl"
  32. # TripleDES encryption is disallowed/deprecated throughout 2023 in
  33. # FIPS 140-3. To keep it simple we denylist any use of TripleDES (TDEA).
  34. _fips_ciphers = (AES,)
  35. # Sometimes SHA1 is still permissible. That logic is contained
  36. # within the various *_supported methods.
  37. _fips_hashes = (
  38. hashes.SHA224,
  39. hashes.SHA256,
  40. hashes.SHA384,
  41. hashes.SHA512,
  42. hashes.SHA512_224,
  43. hashes.SHA512_256,
  44. hashes.SHA3_224,
  45. hashes.SHA3_256,
  46. hashes.SHA3_384,
  47. hashes.SHA3_512,
  48. hashes.SHAKE128,
  49. hashes.SHAKE256,
  50. )
  51. _fips_ecdh_curves = (
  52. ec.SECP224R1,
  53. ec.SECP256R1,
  54. ec.SECP384R1,
  55. ec.SECP521R1,
  56. )
  57. _fips_rsa_min_key_size = 2048
  58. _fips_rsa_min_public_exponent = 65537
  59. _fips_dsa_min_modulus = 1 << 2048
  60. _fips_dh_min_key_size = 2048
  61. _fips_dh_min_modulus = 1 << _fips_dh_min_key_size
  62. def __init__(self) -> None:
  63. self._binding = binding.Binding()
  64. self._ffi = self._binding.ffi
  65. self._lib = self._binding.lib
  66. self._fips_enabled = rust_openssl.is_fips_enabled()
  67. def __repr__(self) -> str:
  68. return (
  69. f"<OpenSSLBackend(version: {self.openssl_version_text()}, "
  70. f"FIPS: {self._fips_enabled}, "
  71. f"Legacy: {rust_openssl._legacy_provider_loaded})>"
  72. )
  73. def openssl_assert(self, ok: bool) -> None:
  74. return binding._openssl_assert(ok)
  75. def _enable_fips(self) -> None:
  76. # This function enables FIPS mode for OpenSSL 3.0.0 on installs that
  77. # have the FIPS provider installed properly.
  78. rust_openssl.enable_fips(rust_openssl._providers)
  79. assert rust_openssl.is_fips_enabled()
  80. self._fips_enabled = rust_openssl.is_fips_enabled()
  81. def openssl_version_text(self) -> str:
  82. """
  83. Friendly string name of the loaded OpenSSL library. This is not
  84. necessarily the same version as it was compiled against.
  85. Example: OpenSSL 3.2.1 30 Jan 2024
  86. """
  87. return rust_openssl.openssl_version_text()
  88. def openssl_version_number(self) -> int:
  89. return rust_openssl.openssl_version()
  90. def _evp_md_from_algorithm(self, algorithm: hashes.HashAlgorithm):
  91. if algorithm.name in ("blake2b", "blake2s"):
  92. alg = f"{algorithm.name}{algorithm.digest_size * 8}".encode(
  93. "ascii"
  94. )
  95. else:
  96. alg = algorithm.name.encode("ascii")
  97. evp_md = self._lib.EVP_get_digestbyname(alg)
  98. return evp_md
  99. def hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
  100. if self._fips_enabled and not isinstance(algorithm, self._fips_hashes):
  101. return False
  102. evp_md = self._evp_md_from_algorithm(algorithm)
  103. return evp_md != self._ffi.NULL
  104. def signature_hash_supported(
  105. self, algorithm: hashes.HashAlgorithm
  106. ) -> bool:
  107. # Dedicated check for hashing algorithm use in message digest for
  108. # signatures, e.g. RSA PKCS#1 v1.5 SHA1 (sha1WithRSAEncryption).
  109. if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
  110. return False
  111. return self.hash_supported(algorithm)
  112. def scrypt_supported(self) -> bool:
  113. if self._fips_enabled:
  114. return False
  115. else:
  116. return hasattr(rust_openssl.kdf, "derive_scrypt")
  117. def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
  118. # FIPS mode still allows SHA1 for HMAC
  119. if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
  120. return True
  121. return self.hash_supported(algorithm)
  122. def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool:
  123. if self._fips_enabled:
  124. # FIPS mode requires AES. TripleDES is disallowed/deprecated in
  125. # FIPS 140-3.
  126. if not isinstance(cipher, self._fips_ciphers):
  127. return False
  128. return rust_openssl.ciphers.cipher_supported(cipher, mode)
  129. def pbkdf2_hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
  130. return self.hmac_supported(algorithm)
  131. def _consume_errors(self) -> list[rust_openssl.OpenSSLError]:
  132. return rust_openssl.capture_error_stack()
  133. def _oaep_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
  134. if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
  135. return False
  136. return isinstance(
  137. algorithm,
  138. (
  139. hashes.SHA1,
  140. hashes.SHA224,
  141. hashes.SHA256,
  142. hashes.SHA384,
  143. hashes.SHA512,
  144. ),
  145. )
  146. def rsa_padding_supported(self, padding: AsymmetricPadding) -> bool:
  147. if isinstance(padding, PKCS1v15):
  148. return True
  149. elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1):
  150. # SHA1 is permissible in MGF1 in FIPS even when SHA1 is blocked
  151. # as signature algorithm.
  152. if self._fips_enabled and isinstance(
  153. padding._mgf._algorithm, hashes.SHA1
  154. ):
  155. return True
  156. else:
  157. return self.hash_supported(padding._mgf._algorithm)
  158. elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1):
  159. return self._oaep_hash_supported(
  160. padding._mgf._algorithm
  161. ) and self._oaep_hash_supported(padding._algorithm)
  162. else:
  163. return False
  164. def rsa_encryption_supported(self, padding: AsymmetricPadding) -> bool:
  165. if self._fips_enabled and isinstance(padding, PKCS1v15):
  166. return False
  167. else:
  168. return self.rsa_padding_supported(padding)
  169. def dsa_supported(self) -> bool:
  170. return (
  171. not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
  172. and not self._fips_enabled
  173. )
  174. def dsa_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
  175. if not self.dsa_supported():
  176. return False
  177. return self.signature_hash_supported(algorithm)
  178. def cmac_algorithm_supported(self, algorithm) -> bool:
  179. return self.cipher_supported(
  180. algorithm, CBC(b"\x00" * algorithm.block_size)
  181. )
  182. def elliptic_curve_supported(self, curve: ec.EllipticCurve) -> bool:
  183. if self._fips_enabled and not isinstance(
  184. curve, self._fips_ecdh_curves
  185. ):
  186. return False
  187. return rust_openssl.ec.curve_supported(curve)
  188. def elliptic_curve_signature_algorithm_supported(
  189. self,
  190. signature_algorithm: ec.EllipticCurveSignatureAlgorithm,
  191. curve: ec.EllipticCurve,
  192. ) -> bool:
  193. # We only support ECDSA right now.
  194. if not isinstance(signature_algorithm, ec.ECDSA):
  195. return False
  196. return self.elliptic_curve_supported(curve) and (
  197. isinstance(signature_algorithm.algorithm, asym_utils.Prehashed)
  198. or self.hash_supported(signature_algorithm.algorithm)
  199. )
  200. def elliptic_curve_exchange_algorithm_supported(
  201. self, algorithm: ec.ECDH, curve: ec.EllipticCurve
  202. ) -> bool:
  203. return self.elliptic_curve_supported(curve) and isinstance(
  204. algorithm, ec.ECDH
  205. )
  206. def dh_supported(self) -> bool:
  207. return not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
  208. def dh_x942_serialization_supported(self) -> bool:
  209. return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1
  210. def x25519_supported(self) -> bool:
  211. if self._fips_enabled:
  212. return False
  213. return True
  214. def x448_supported(self) -> bool:
  215. if self._fips_enabled:
  216. return False
  217. return (
  218. not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL
  219. and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
  220. )
  221. def ed25519_supported(self) -> bool:
  222. if self._fips_enabled:
  223. return False
  224. return True
  225. def ed448_supported(self) -> bool:
  226. if self._fips_enabled:
  227. return False
  228. return (
  229. not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL
  230. and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
  231. )
  232. def ecdsa_deterministic_supported(self) -> bool:
  233. return (
  234. rust_openssl.CRYPTOGRAPHY_OPENSSL_320_OR_GREATER
  235. and not self._fips_enabled
  236. )
  237. def poly1305_supported(self) -> bool:
  238. if self._fips_enabled:
  239. return False
  240. return True
  241. def pkcs7_supported(self) -> bool:
  242. return not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
  243. backend = Backend()