backend.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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 hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
  91. if self._fips_enabled and not isinstance(algorithm, self._fips_hashes):
  92. return False
  93. return rust_openssl.hashes.hash_supported(algorithm)
  94. def signature_hash_supported(
  95. self, algorithm: hashes.HashAlgorithm
  96. ) -> bool:
  97. # Dedicated check for hashing algorithm use in message digest for
  98. # signatures, e.g. RSA PKCS#1 v1.5 SHA1 (sha1WithRSAEncryption).
  99. if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
  100. return False
  101. return self.hash_supported(algorithm)
  102. def scrypt_supported(self) -> bool:
  103. if self._fips_enabled:
  104. return False
  105. else:
  106. return hasattr(rust_openssl.kdf.Scrypt, "derive")
  107. def argon2_supported(self) -> bool:
  108. if self._fips_enabled:
  109. return False
  110. else:
  111. return hasattr(rust_openssl.kdf.Argon2id, "derive")
  112. def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
  113. # FIPS mode still allows SHA1 for HMAC
  114. if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
  115. return True
  116. return self.hash_supported(algorithm)
  117. def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool:
  118. if self._fips_enabled:
  119. # FIPS mode requires AES. TripleDES is disallowed/deprecated in
  120. # FIPS 140-3.
  121. if not isinstance(cipher, self._fips_ciphers):
  122. return False
  123. return rust_openssl.ciphers.cipher_supported(cipher, mode)
  124. def pbkdf2_hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
  125. return self.hmac_supported(algorithm)
  126. def _consume_errors(self) -> list[rust_openssl.OpenSSLError]:
  127. return rust_openssl.capture_error_stack()
  128. def _oaep_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
  129. if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
  130. return False
  131. return isinstance(
  132. algorithm,
  133. (
  134. hashes.SHA1,
  135. hashes.SHA224,
  136. hashes.SHA256,
  137. hashes.SHA384,
  138. hashes.SHA512,
  139. ),
  140. )
  141. def rsa_padding_supported(self, padding: AsymmetricPadding) -> bool:
  142. if isinstance(padding, PKCS1v15):
  143. return True
  144. elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1):
  145. # SHA1 is permissible in MGF1 in FIPS even when SHA1 is blocked
  146. # as signature algorithm.
  147. if self._fips_enabled and isinstance(
  148. padding._mgf._algorithm, hashes.SHA1
  149. ):
  150. return True
  151. else:
  152. return self.hash_supported(padding._mgf._algorithm)
  153. elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1):
  154. return self._oaep_hash_supported(
  155. padding._mgf._algorithm
  156. ) and self._oaep_hash_supported(padding._algorithm)
  157. else:
  158. return False
  159. def rsa_encryption_supported(self, padding: AsymmetricPadding) -> bool:
  160. if self._fips_enabled and isinstance(padding, PKCS1v15):
  161. return False
  162. else:
  163. return self.rsa_padding_supported(padding)
  164. def dsa_supported(self) -> bool:
  165. return (
  166. not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
  167. and not self._fips_enabled
  168. )
  169. def dsa_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
  170. if not self.dsa_supported():
  171. return False
  172. return self.signature_hash_supported(algorithm)
  173. def cmac_algorithm_supported(self, algorithm) -> bool:
  174. return self.cipher_supported(
  175. algorithm, CBC(b"\x00" * algorithm.block_size)
  176. )
  177. def elliptic_curve_supported(self, curve: ec.EllipticCurve) -> bool:
  178. if self._fips_enabled and not isinstance(
  179. curve, self._fips_ecdh_curves
  180. ):
  181. return False
  182. return rust_openssl.ec.curve_supported(curve)
  183. def elliptic_curve_signature_algorithm_supported(
  184. self,
  185. signature_algorithm: ec.EllipticCurveSignatureAlgorithm,
  186. curve: ec.EllipticCurve,
  187. ) -> bool:
  188. # We only support ECDSA right now.
  189. if not isinstance(signature_algorithm, ec.ECDSA):
  190. return False
  191. return self.elliptic_curve_supported(curve) and (
  192. isinstance(signature_algorithm.algorithm, asym_utils.Prehashed)
  193. or self.hash_supported(signature_algorithm.algorithm)
  194. )
  195. def elliptic_curve_exchange_algorithm_supported(
  196. self, algorithm: ec.ECDH, curve: ec.EllipticCurve
  197. ) -> bool:
  198. return self.elliptic_curve_supported(curve) and isinstance(
  199. algorithm, ec.ECDH
  200. )
  201. def dh_supported(self) -> bool:
  202. return not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
  203. def dh_x942_serialization_supported(self) -> bool:
  204. return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1
  205. def x25519_supported(self) -> bool:
  206. if self._fips_enabled:
  207. return False
  208. return True
  209. def x448_supported(self) -> bool:
  210. if self._fips_enabled:
  211. return False
  212. return (
  213. not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL
  214. and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
  215. )
  216. def ed25519_supported(self) -> bool:
  217. if self._fips_enabled:
  218. return False
  219. return True
  220. def ed448_supported(self) -> bool:
  221. if self._fips_enabled:
  222. return False
  223. return (
  224. not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL
  225. and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
  226. )
  227. def ecdsa_deterministic_supported(self) -> bool:
  228. return (
  229. rust_openssl.CRYPTOGRAPHY_OPENSSL_320_OR_GREATER
  230. and not self._fips_enabled
  231. )
  232. def poly1305_supported(self) -> bool:
  233. if self._fips_enabled:
  234. return False
  235. return True
  236. def pkcs7_supported(self) -> bool:
  237. return not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
  238. backend = Backend()