Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/cryptography/hazmat/backends/openssl/rsa.py: 17%
257 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:36 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:36 +0000
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.
5import threading
6import typing
8from cryptography.exceptions import (
9 InvalidSignature,
10 UnsupportedAlgorithm,
11 _Reasons,
12)
13from cryptography.hazmat.backends.openssl.utils import (
14 _calculate_digest_and_algorithm,
15)
16from cryptography.hazmat.primitives import hashes, serialization
17from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
18from cryptography.hazmat.primitives.asymmetric.padding import (
19 MGF1,
20 OAEP,
21 PSS,
22 AsymmetricPadding,
23 PKCS1v15,
24 _Auto,
25 _DigestLength,
26 _MaxLength,
27 calculate_max_pss_salt_length,
28)
29from cryptography.hazmat.primitives.asymmetric.rsa import (
30 RSAPrivateKey,
31 RSAPrivateNumbers,
32 RSAPublicKey,
33 RSAPublicNumbers,
34)
36if typing.TYPE_CHECKING:
37 from cryptography.hazmat.backends.openssl.backend import Backend
40def _get_rsa_pss_salt_length(
41 backend: "Backend",
42 pss: PSS,
43 key: typing.Union[RSAPrivateKey, RSAPublicKey],
44 hash_algorithm: hashes.HashAlgorithm,
45) -> int:
46 salt = pss._salt_length
48 if isinstance(salt, _MaxLength):
49 return calculate_max_pss_salt_length(key, hash_algorithm)
50 elif isinstance(salt, _DigestLength):
51 return hash_algorithm.digest_size
52 elif isinstance(salt, _Auto):
53 if isinstance(key, RSAPrivateKey):
54 raise ValueError(
55 "PSS salt length can only be set to AUTO when verifying"
56 )
57 return backend._lib.RSA_PSS_SALTLEN_AUTO
58 else:
59 return salt
62def _enc_dec_rsa(
63 backend: "Backend",
64 key: typing.Union["_RSAPrivateKey", "_RSAPublicKey"],
65 data: bytes,
66 padding: AsymmetricPadding,
67) -> bytes:
68 if not isinstance(padding, AsymmetricPadding):
69 raise TypeError("Padding must be an instance of AsymmetricPadding.")
71 if isinstance(padding, PKCS1v15):
72 padding_enum = backend._lib.RSA_PKCS1_PADDING
73 elif isinstance(padding, OAEP):
74 padding_enum = backend._lib.RSA_PKCS1_OAEP_PADDING
76 if not isinstance(padding._mgf, MGF1):
77 raise UnsupportedAlgorithm(
78 "Only MGF1 is supported by this backend.",
79 _Reasons.UNSUPPORTED_MGF,
80 )
82 if not backend.rsa_padding_supported(padding):
83 raise UnsupportedAlgorithm(
84 "This combination of padding and hash algorithm is not "
85 "supported by this backend.",
86 _Reasons.UNSUPPORTED_PADDING,
87 )
89 else:
90 raise UnsupportedAlgorithm(
91 f"{padding.name} is not supported by this backend.",
92 _Reasons.UNSUPPORTED_PADDING,
93 )
95 return _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding)
98def _enc_dec_rsa_pkey_ctx(
99 backend: "Backend",
100 key: typing.Union["_RSAPrivateKey", "_RSAPublicKey"],
101 data: bytes,
102 padding_enum: int,
103 padding: AsymmetricPadding,
104) -> bytes:
105 init: typing.Callable[[typing.Any], int]
106 crypt: typing.Callable[[typing.Any, typing.Any, int, bytes, int], int]
107 if isinstance(key, _RSAPublicKey):
108 init = backend._lib.EVP_PKEY_encrypt_init
109 crypt = backend._lib.EVP_PKEY_encrypt
110 else:
111 init = backend._lib.EVP_PKEY_decrypt_init
112 crypt = backend._lib.EVP_PKEY_decrypt
114 pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL)
115 backend.openssl_assert(pkey_ctx != backend._ffi.NULL)
116 pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free)
117 res = init(pkey_ctx)
118 backend.openssl_assert(res == 1)
119 res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum)
120 backend.openssl_assert(res > 0)
121 buf_size = backend._lib.EVP_PKEY_size(key._evp_pkey)
122 backend.openssl_assert(buf_size > 0)
123 if isinstance(padding, OAEP):
124 mgf1_md = backend._evp_md_non_null_from_algorithm(
125 padding._mgf._algorithm
126 )
127 res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md)
128 backend.openssl_assert(res > 0)
129 oaep_md = backend._evp_md_non_null_from_algorithm(padding._algorithm)
130 res = backend._lib.EVP_PKEY_CTX_set_rsa_oaep_md(pkey_ctx, oaep_md)
131 backend.openssl_assert(res > 0)
133 if (
134 isinstance(padding, OAEP)
135 and padding._label is not None
136 and len(padding._label) > 0
137 ):
138 # set0_rsa_oaep_label takes ownership of the char * so we need to
139 # copy it into some new memory
140 labelptr = backend._lib.OPENSSL_malloc(len(padding._label))
141 backend.openssl_assert(labelptr != backend._ffi.NULL)
142 backend._ffi.memmove(labelptr, padding._label, len(padding._label))
143 res = backend._lib.EVP_PKEY_CTX_set0_rsa_oaep_label(
144 pkey_ctx, labelptr, len(padding._label)
145 )
146 backend.openssl_assert(res == 1)
148 outlen = backend._ffi.new("size_t *", buf_size)
149 buf = backend._ffi.new("unsigned char[]", buf_size)
150 # Everything from this line onwards is written with the goal of being as
151 # constant-time as is practical given the constraints of Python and our
152 # API. See Bleichenbacher's '98 attack on RSA, and its many many variants.
153 # As such, you should not attempt to change this (particularly to "clean it
154 # up") without understanding why it was written this way (see
155 # Chesterton's Fence), and without measuring to verify you have not
156 # introduced observable time differences.
157 res = crypt(pkey_ctx, buf, outlen, data, len(data))
158 resbuf = backend._ffi.buffer(buf)[: outlen[0]]
159 backend._lib.ERR_clear_error()
160 if res <= 0:
161 raise ValueError("Encryption/decryption failed.")
162 return resbuf
165def _rsa_sig_determine_padding(
166 backend: "Backend",
167 key: typing.Union["_RSAPrivateKey", "_RSAPublicKey"],
168 padding: AsymmetricPadding,
169 algorithm: typing.Optional[hashes.HashAlgorithm],
170) -> int:
171 if not isinstance(padding, AsymmetricPadding):
172 raise TypeError("Expected provider of AsymmetricPadding.")
174 pkey_size = backend._lib.EVP_PKEY_size(key._evp_pkey)
175 backend.openssl_assert(pkey_size > 0)
177 if isinstance(padding, PKCS1v15):
178 # Hash algorithm is ignored for PKCS1v15-padding, may be None.
179 padding_enum = backend._lib.RSA_PKCS1_PADDING
180 elif isinstance(padding, PSS):
181 if not isinstance(padding._mgf, MGF1):
182 raise UnsupportedAlgorithm(
183 "Only MGF1 is supported by this backend.",
184 _Reasons.UNSUPPORTED_MGF,
185 )
187 # PSS padding requires a hash algorithm
188 if not isinstance(algorithm, hashes.HashAlgorithm):
189 raise TypeError("Expected instance of hashes.HashAlgorithm.")
191 # Size of key in bytes - 2 is the maximum
192 # PSS signature length (salt length is checked later)
193 if pkey_size - algorithm.digest_size - 2 < 0:
194 raise ValueError(
195 "Digest too large for key size. Use a larger "
196 "key or different digest."
197 )
199 padding_enum = backend._lib.RSA_PKCS1_PSS_PADDING
200 else:
201 raise UnsupportedAlgorithm(
202 f"{padding.name} is not supported by this backend.",
203 _Reasons.UNSUPPORTED_PADDING,
204 )
206 return padding_enum
209# Hash algorithm can be absent (None) to initialize the context without setting
210# any message digest algorithm. This is currently only valid for the PKCS1v15
211# padding type, where it means that the signature data is encoded/decoded
212# as provided, without being wrapped in a DigestInfo structure.
213def _rsa_sig_setup(
214 backend: "Backend",
215 padding: AsymmetricPadding,
216 algorithm: typing.Optional[hashes.HashAlgorithm],
217 key: typing.Union["_RSAPublicKey", "_RSAPrivateKey"],
218 init_func: typing.Callable[[typing.Any], int],
219):
220 padding_enum = _rsa_sig_determine_padding(backend, key, padding, algorithm)
221 pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL)
222 backend.openssl_assert(pkey_ctx != backend._ffi.NULL)
223 pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free)
224 res = init_func(pkey_ctx)
225 if res != 1:
226 errors = backend._consume_errors()
227 raise ValueError("Unable to sign/verify with this key", errors)
229 if algorithm is not None:
230 evp_md = backend._evp_md_non_null_from_algorithm(algorithm)
231 res = backend._lib.EVP_PKEY_CTX_set_signature_md(pkey_ctx, evp_md)
232 if res <= 0:
233 backend._consume_errors()
234 raise UnsupportedAlgorithm(
235 "{} is not supported by this backend for RSA signing.".format(
236 algorithm.name
237 ),
238 _Reasons.UNSUPPORTED_HASH,
239 )
240 res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum)
241 if res <= 0:
242 backend._consume_errors()
243 raise UnsupportedAlgorithm(
244 "{} is not supported for the RSA signature operation.".format(
245 padding.name
246 ),
247 _Reasons.UNSUPPORTED_PADDING,
248 )
249 if isinstance(padding, PSS):
250 assert isinstance(algorithm, hashes.HashAlgorithm)
251 res = backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen(
252 pkey_ctx,
253 _get_rsa_pss_salt_length(backend, padding, key, algorithm),
254 )
255 backend.openssl_assert(res > 0)
257 mgf1_md = backend._evp_md_non_null_from_algorithm(
258 padding._mgf._algorithm
259 )
260 res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md)
261 backend.openssl_assert(res > 0)
263 return pkey_ctx
266def _rsa_sig_sign(
267 backend: "Backend",
268 padding: AsymmetricPadding,
269 algorithm: hashes.HashAlgorithm,
270 private_key: "_RSAPrivateKey",
271 data: bytes,
272) -> bytes:
273 pkey_ctx = _rsa_sig_setup(
274 backend,
275 padding,
276 algorithm,
277 private_key,
278 backend._lib.EVP_PKEY_sign_init,
279 )
280 buflen = backend._ffi.new("size_t *")
281 res = backend._lib.EVP_PKEY_sign(
282 pkey_ctx, backend._ffi.NULL, buflen, data, len(data)
283 )
284 backend.openssl_assert(res == 1)
285 buf = backend._ffi.new("unsigned char[]", buflen[0])
286 res = backend._lib.EVP_PKEY_sign(pkey_ctx, buf, buflen, data, len(data))
287 if res != 1:
288 errors = backend._consume_errors()
289 raise ValueError(
290 "Digest or salt length too long for key size. Use a larger key "
291 "or shorter salt length if you are specifying a PSS salt",
292 errors,
293 )
295 return backend._ffi.buffer(buf)[:]
298def _rsa_sig_verify(
299 backend: "Backend",
300 padding: AsymmetricPadding,
301 algorithm: hashes.HashAlgorithm,
302 public_key: "_RSAPublicKey",
303 signature: bytes,
304 data: bytes,
305) -> None:
306 pkey_ctx = _rsa_sig_setup(
307 backend,
308 padding,
309 algorithm,
310 public_key,
311 backend._lib.EVP_PKEY_verify_init,
312 )
313 res = backend._lib.EVP_PKEY_verify(
314 pkey_ctx, signature, len(signature), data, len(data)
315 )
316 # The previous call can return negative numbers in the event of an
317 # error. This is not a signature failure but we need to fail if it
318 # occurs.
319 backend.openssl_assert(res >= 0)
320 if res == 0:
321 backend._consume_errors()
322 raise InvalidSignature
325def _rsa_sig_recover(
326 backend: "Backend",
327 padding: AsymmetricPadding,
328 algorithm: typing.Optional[hashes.HashAlgorithm],
329 public_key: "_RSAPublicKey",
330 signature: bytes,
331) -> bytes:
332 pkey_ctx = _rsa_sig_setup(
333 backend,
334 padding,
335 algorithm,
336 public_key,
337 backend._lib.EVP_PKEY_verify_recover_init,
338 )
340 # Attempt to keep the rest of the code in this function as constant/time
341 # as possible. See the comment in _enc_dec_rsa_pkey_ctx. Note that the
342 # buflen parameter is used even though its value may be undefined in the
343 # error case. Due to the tolerant nature of Python slicing this does not
344 # trigger any exceptions.
345 maxlen = backend._lib.EVP_PKEY_size(public_key._evp_pkey)
346 backend.openssl_assert(maxlen > 0)
347 buf = backend._ffi.new("unsigned char[]", maxlen)
348 buflen = backend._ffi.new("size_t *", maxlen)
349 res = backend._lib.EVP_PKEY_verify_recover(
350 pkey_ctx, buf, buflen, signature, len(signature)
351 )
352 resbuf = backend._ffi.buffer(buf)[: buflen[0]]
353 backend._lib.ERR_clear_error()
354 # Assume that all parameter errors are handled during the setup phase and
355 # any error here is due to invalid signature.
356 if res != 1:
357 raise InvalidSignature
358 return resbuf
361class _RSAPrivateKey(RSAPrivateKey):
362 _evp_pkey: object
363 _rsa_cdata: object
364 _key_size: int
366 def __init__(
367 self,
368 backend: "Backend",
369 rsa_cdata,
370 evp_pkey,
371 *,
372 unsafe_skip_rsa_key_validation: bool,
373 ):
374 res: int
375 # RSA_check_key is slower in OpenSSL 3.0.0 due to improved
376 # primality checking. In normal use this is unlikely to be a problem
377 # since users don't load new keys constantly, but for TESTING we've
378 # added an init arg that allows skipping the checks. You should not
379 # use this in production code unless you understand the consequences.
380 if not unsafe_skip_rsa_key_validation:
381 res = backend._lib.RSA_check_key(rsa_cdata)
382 if res != 1:
383 errors = backend._consume_errors()
384 raise ValueError("Invalid private key", errors)
385 # 2 is prime and passes an RSA key check, so we also check
386 # if p and q are odd just to be safe.
387 p = backend._ffi.new("BIGNUM **")
388 q = backend._ffi.new("BIGNUM **")
389 backend._lib.RSA_get0_factors(rsa_cdata, p, q)
390 backend.openssl_assert(p[0] != backend._ffi.NULL)
391 backend.openssl_assert(q[0] != backend._ffi.NULL)
392 p_odd = backend._lib.BN_is_odd(p[0])
393 q_odd = backend._lib.BN_is_odd(q[0])
394 if p_odd != 1 or q_odd != 1:
395 errors = backend._consume_errors()
396 raise ValueError("Invalid private key", errors)
398 self._backend = backend
399 self._rsa_cdata = rsa_cdata
400 self._evp_pkey = evp_pkey
401 # Used for lazy blinding
402 self._blinded = False
403 self._blinding_lock = threading.Lock()
405 n = self._backend._ffi.new("BIGNUM **")
406 self._backend._lib.RSA_get0_key(
407 self._rsa_cdata,
408 n,
409 self._backend._ffi.NULL,
410 self._backend._ffi.NULL,
411 )
412 self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
413 self._key_size = self._backend._lib.BN_num_bits(n[0])
415 def _enable_blinding(self) -> None:
416 # If you call blind on an already blinded RSA key OpenSSL will turn
417 # it off and back on, which is a performance hit we want to avoid.
418 if not self._blinded:
419 with self._blinding_lock:
420 self._non_threadsafe_enable_blinding()
422 def _non_threadsafe_enable_blinding(self) -> None:
423 # This is only a separate function to allow for testing to cover both
424 # branches. It should never be invoked except through _enable_blinding.
425 # Check if it's not True again in case another thread raced past the
426 # first non-locked check.
427 if not self._blinded:
428 res = self._backend._lib.RSA_blinding_on(
429 self._rsa_cdata, self._backend._ffi.NULL
430 )
431 self._backend.openssl_assert(res == 1)
432 self._blinded = True
434 @property
435 def key_size(self) -> int:
436 return self._key_size
438 def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes:
439 self._enable_blinding()
440 key_size_bytes = (self.key_size + 7) // 8
441 if key_size_bytes != len(ciphertext):
442 raise ValueError("Ciphertext length must be equal to key size.")
444 return _enc_dec_rsa(self._backend, self, ciphertext, padding)
446 def public_key(self) -> RSAPublicKey:
447 ctx = self._backend._lib.RSAPublicKey_dup(self._rsa_cdata)
448 self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
449 ctx = self._backend._ffi.gc(ctx, self._backend._lib.RSA_free)
450 evp_pkey = self._backend._rsa_cdata_to_evp_pkey(ctx)
451 return _RSAPublicKey(self._backend, ctx, evp_pkey)
453 def private_numbers(self) -> RSAPrivateNumbers:
454 n = self._backend._ffi.new("BIGNUM **")
455 e = self._backend._ffi.new("BIGNUM **")
456 d = self._backend._ffi.new("BIGNUM **")
457 p = self._backend._ffi.new("BIGNUM **")
458 q = self._backend._ffi.new("BIGNUM **")
459 dmp1 = self._backend._ffi.new("BIGNUM **")
460 dmq1 = self._backend._ffi.new("BIGNUM **")
461 iqmp = self._backend._ffi.new("BIGNUM **")
462 self._backend._lib.RSA_get0_key(self._rsa_cdata, n, e, d)
463 self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
464 self._backend.openssl_assert(e[0] != self._backend._ffi.NULL)
465 self._backend.openssl_assert(d[0] != self._backend._ffi.NULL)
466 self._backend._lib.RSA_get0_factors(self._rsa_cdata, p, q)
467 self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
468 self._backend.openssl_assert(q[0] != self._backend._ffi.NULL)
469 self._backend._lib.RSA_get0_crt_params(
470 self._rsa_cdata, dmp1, dmq1, iqmp
471 )
472 self._backend.openssl_assert(dmp1[0] != self._backend._ffi.NULL)
473 self._backend.openssl_assert(dmq1[0] != self._backend._ffi.NULL)
474 self._backend.openssl_assert(iqmp[0] != self._backend._ffi.NULL)
475 return RSAPrivateNumbers(
476 p=self._backend._bn_to_int(p[0]),
477 q=self._backend._bn_to_int(q[0]),
478 d=self._backend._bn_to_int(d[0]),
479 dmp1=self._backend._bn_to_int(dmp1[0]),
480 dmq1=self._backend._bn_to_int(dmq1[0]),
481 iqmp=self._backend._bn_to_int(iqmp[0]),
482 public_numbers=RSAPublicNumbers(
483 e=self._backend._bn_to_int(e[0]),
484 n=self._backend._bn_to_int(n[0]),
485 ),
486 )
488 def private_bytes(
489 self,
490 encoding: serialization.Encoding,
491 format: serialization.PrivateFormat,
492 encryption_algorithm: serialization.KeySerializationEncryption,
493 ) -> bytes:
494 return self._backend._private_key_bytes(
495 encoding,
496 format,
497 encryption_algorithm,
498 self,
499 self._evp_pkey,
500 self._rsa_cdata,
501 )
503 def sign(
504 self,
505 data: bytes,
506 padding: AsymmetricPadding,
507 algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm],
508 ) -> bytes:
509 self._enable_blinding()
510 data, algorithm = _calculate_digest_and_algorithm(data, algorithm)
511 return _rsa_sig_sign(self._backend, padding, algorithm, self, data)
514class _RSAPublicKey(RSAPublicKey):
515 _evp_pkey: object
516 _rsa_cdata: object
517 _key_size: int
519 def __init__(self, backend: "Backend", rsa_cdata, evp_pkey):
520 self._backend = backend
521 self._rsa_cdata = rsa_cdata
522 self._evp_pkey = evp_pkey
524 n = self._backend._ffi.new("BIGNUM **")
525 self._backend._lib.RSA_get0_key(
526 self._rsa_cdata,
527 n,
528 self._backend._ffi.NULL,
529 self._backend._ffi.NULL,
530 )
531 self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
532 self._key_size = self._backend._lib.BN_num_bits(n[0])
534 @property
535 def key_size(self) -> int:
536 return self._key_size
538 def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes:
539 return _enc_dec_rsa(self._backend, self, plaintext, padding)
541 def public_numbers(self) -> RSAPublicNumbers:
542 n = self._backend._ffi.new("BIGNUM **")
543 e = self._backend._ffi.new("BIGNUM **")
544 self._backend._lib.RSA_get0_key(
545 self._rsa_cdata, n, e, self._backend._ffi.NULL
546 )
547 self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
548 self._backend.openssl_assert(e[0] != self._backend._ffi.NULL)
549 return RSAPublicNumbers(
550 e=self._backend._bn_to_int(e[0]),
551 n=self._backend._bn_to_int(n[0]),
552 )
554 def public_bytes(
555 self,
556 encoding: serialization.Encoding,
557 format: serialization.PublicFormat,
558 ) -> bytes:
559 return self._backend._public_key_bytes(
560 encoding, format, self, self._evp_pkey, self._rsa_cdata
561 )
563 def verify(
564 self,
565 signature: bytes,
566 data: bytes,
567 padding: AsymmetricPadding,
568 algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm],
569 ) -> None:
570 data, algorithm = _calculate_digest_and_algorithm(data, algorithm)
571 _rsa_sig_verify(
572 self._backend, padding, algorithm, self, signature, data
573 )
575 def recover_data_from_signature(
576 self,
577 signature: bytes,
578 padding: AsymmetricPadding,
579 algorithm: typing.Optional[hashes.HashAlgorithm],
580 ) -> bytes:
581 if isinstance(algorithm, asym_utils.Prehashed):
582 raise TypeError(
583 "Prehashed is only supported in the sign and verify methods. "
584 "It cannot be used with recover_data_from_signature."
585 )
586 return _rsa_sig_recover(
587 self._backend, padding, algorithm, self, signature
588 )