Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rsa_recover_private_exponent function #11193

Merged

Conversation

dlenskiSB
Copy link
Contributor

@dlenskiSB dlenskiSB commented Jul 3, 2024

Given the RSA public exponent (e), and the RSA primes (p, q), it is possible to calculate the corresponding private exponent d = e⁻¹ mod λ(n) where λ(n) = lcm(p-1, q-1).

With this function added, it becomes possible to use the library to reconstruct an RSA private key given only p, q, and e:

from cryptography.hazmat.primitives.asymmetric import rsa

n = p * q
d = rsa.rsa_recover_private_exponent(e, p, q)  # newly-added piece
iqmp = rsa.rsa_crt_iqmp(p, q)                  # preexisting
dmp1 = rsa.rsa_crt_dmp1(d, p)                  # preexisting
dmq1 = rsa.rsa_crt_dmq1(d, q)                  # preexisting

assert rsa.rsa_recover_prime_factors(n, e, d) in ((p, q), (q, p))  # verify consistency

privk = rsa.RSAPrivateNumbers(p, q, d, dmp1, dmq1, iqmp, rsa.RSAPublicNumbers(e, n)).private_key()

Older RSA implementations, including the original RSA paper, often used the Euler totient function ɸ(n) = (p-1) * (q-1) instead of λ(n). The private exponents generated by that method work equally well, but may be larger than strictly necessary (λ(n) always divides ɸ(n)). This commit additionally implements _rsa_recover_euler_private_exponent, so that tests of the internal structure of RSA private keys can allow for either the Euler or the Carmichael versions of the private exponents.

It makes sense to expose only the more modern version (using the Carmichael totient function) for public usage, given that it is slightly more computationally efficient to use the keys in this form, and that some standards like FIPS 186-4 require this form. (See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#page=63)

@dlenskiSB dlenskiSB force-pushed the add_rsa_recover_private_exponent_function branch 4 times, most recently from 73e5e7a to 36b56fe Compare July 3, 2024 19:42
Copy link
Member

@alex alex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two observations. Will defer the meat of the review to @reaperhulk

docs/hazmat/primitives/asymmetric/rsa.rst Outdated Show resolved Hide resolved
src/cryptography/hazmat/primitives/asymmetric/rsa.py Outdated Show resolved Hide resolved
@dlenskiSB dlenskiSB force-pushed the add_rsa_recover_private_exponent_function branch 2 times, most recently from 7ffb0b0 to bf5d783 Compare July 3, 2024 20:35
@dlenskiSB dlenskiSB requested a review from alex July 3, 2024 20:59
@dlenskiSB dlenskiSB force-pushed the add_rsa_recover_private_exponent_function branch from bf5d783 to a6db08e Compare July 4, 2024 17:00
Copy link
Member

@reaperhulk reaperhulk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generally lgtm, just needs changelog and doc updates.

@dlenskiSB dlenskiSB force-pushed the add_rsa_recover_private_exponent_function branch 2 times, most recently from 21d94e1 to 678e758 Compare July 5, 2024 18:34
@dlenskiSB dlenskiSB requested a review from reaperhulk July 5, 2024 18:37
@reaperhulk
Copy link
Member

Looks like you'll need to add totient to docs/spelling_wordlist.txt

@dlenskiSB dlenskiSB force-pushed the add_rsa_recover_private_exponent_function branch from 678e758 to 5469f86 Compare July 5, 2024 19:03
@dlenskiSB
Copy link
Contributor Author

Looks like you'll need to add totient to docs/spelling_wordlist.txt

@reaperhulk Yep, just fixed that. (And added Euler and Carmichael while we're at it.)

Copy link
Member

@reaperhulk reaperhulk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two final small requests, then this is ready. Thanks!

Given the RSA public exponent (`e`), and the RSA primes (`p`, `q`), it is possible
to calculate the corresponding private exponent `d = e⁻¹ mod λ(n)` where
`λ(n) = lcm(p-1, q-1)`.

With this function added, it becomes possible to use the library to reconstruct an RSA
private key given *only* `p`, `q`, and `e`:

    from cryptography.hazmat.primitives.asymmetric import rsa

    n = p * q
    d = rsa.rsa_recover_private_exponent(e, p, q)  # newly-added piece
    iqmp = rsa.rsa_crt_iqmp(p, q)                  # preexisting
    dmp1 = rsa.rsa_crt_dmp1(d, p)                  # preexisting
    dmq1 = rsa.rsa_crt_dmq1(d, q)                  # preexisting

    assert rsa.rsa_recover_prime_factors(n, e, d) in ((p, q), (q, p))  # verify consistency

    privk = rsa.RSAPrivateNumbers(p, q, d, dmp1, dmq1, iqmp, rsa.RSAPublicNumbers(e, n)).private_key()

Older RSA implementations, including the original RSA paper, often used the
Euler totient function `ɸ(n) = (p-1) * (q-1)` instead of `λ(n)`.  The
private exponents generated by that method work equally well, but may be
larger than strictly necessary (`λ(n)` always divides `ɸ(n)`).  This commit
additionally implements `_rsa_recover_euler_private_exponent`, so that tests
of the internal structure of RSA private keys can allow for either the Euler
or the Carmichael versions of the private exponents.

It makes sense to expose only the more modern version (using the Carmichael
totient function) for public usage, given that it is slightly more
computationally efficient to use the keys in this form, and that some
standards like FIPS 186-4 require this form.  (See
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#page=63)
@dlenskiSB dlenskiSB force-pushed the add_rsa_recover_private_exponent_function branch from 5469f86 to 5a58cb2 Compare July 5, 2024 21:52
@dlenskiSB dlenskiSB requested a review from reaperhulk July 5, 2024 22:00
@reaperhulk reaperhulk merged commit 8a7f27b into pyca:main Jul 5, 2024
57 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants