Skip to content

Commit

Permalink
Speedup rsa_recover_prime_factors() by using random value (#11899)
Browse files Browse the repository at this point in the history
* Speedup rsa_recover_prime_factors() by using random value

* Comply with ruff codingstyle

* Reject invalid combinations of n, d, e early to avoid excessive runtime

* Add second failure test case for rsa_recover_prime_factors to hit early error path

* Remove leftover debug code

* Reduce _MAX_RECOVERY_ATTEMPTS and remove obsolete comment

Previously, the code would increase a in steps of 2, therefore,
_MAX_RECOVERY_ATTEMPTS was twice the number of tries. With the new
code, this is no longer the case.
  • Loading branch information
hannob authored Nov 10, 2024
1 parent 7ddddf1 commit 78e89e4
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 7 deletions.
17 changes: 10 additions & 7 deletions src/cryptography/hazmat/primitives/asymmetric/rsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import annotations

import abc
import random
import typing
from math import gcd

Expand Down Expand Up @@ -212,16 +213,18 @@ def rsa_recover_private_exponent(e: int, p: int, q: int) -> int:


# Controls the number of iterations rsa_recover_prime_factors will perform
# to obtain the prime factors. Each iteration increments by 2 so the actual
# maximum attempts is half this number.
_MAX_RECOVERY_ATTEMPTS = 1000
# to obtain the prime factors.
_MAX_RECOVERY_ATTEMPTS = 500


def rsa_recover_prime_factors(n: int, e: int, d: int) -> tuple[int, int]:
"""
Compute factors p and q from the private exponent d. We assume that n has
no more than two factors. This function is adapted from code in PyCrypto.
"""
# reject invalid values early
if 17 != pow(17, e * d, n):
raise ValueError("n, d, e don't match")
# See 8.2.2(i) in Handbook of Applied Cryptography.
ktot = d * e - 1
# The quantity d*e-1 is a multiple of phi(n), even,
Expand All @@ -235,8 +238,10 @@ def rsa_recover_prime_factors(n: int, e: int, d: int) -> tuple[int, int]:
# See "Digitalized Signatures and Public Key Functions as Intractable
# as Factorization", M. Rabin, 1979
spotted = False
a = 2
while not spotted and a < _MAX_RECOVERY_ATTEMPTS:
tries = 0
while not spotted and tries < _MAX_RECOVERY_ATTEMPTS:
a = random.randint(2, n - 1)
tries += 1
k = t
# Cycle through all values a^{t*2^i}=a^k
while k < ktot:
Expand All @@ -249,8 +254,6 @@ def rsa_recover_prime_factors(n: int, e: int, d: int) -> tuple[int, int]:
spotted = True
break
k *= 2
# This value was not any good... let's try another!
a += 2
if not spotted:
raise ValueError("Unable to compute factors p and q from exponent d.")
# Found !
Expand Down
2 changes: 2 additions & 0 deletions tests/hazmat/primitives/test_rsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -2398,6 +2398,8 @@ def test_recover_prime_factors(self, subtests):
def test_invalid_recover_prime_factors(self):
with pytest.raises(ValueError):
rsa.rsa_recover_prime_factors(34, 3, 7)
with pytest.raises(ValueError):
rsa.rsa_recover_prime_factors(629, 17, 20)


class TestRSAPrivateKeySerialization:
Expand Down

0 comments on commit 78e89e4

Please sign in to comment.