Skip to content

Commit 29feb79

Browse files
committed
Fix sybrenstuvel#18: Add an 'exponent' argument to key.newkeys()
Adds the possibility to create a new key using a custom exponent. Mostly for compatibility. Also removed the unused parameter nbits from calculate_keys(). I added a new function calculate_keys_custom_exponent() so that people still passing a value to nbits don't accidentally use it as the exponent.
1 parent 467ca03 commit 29feb79

2 files changed

Lines changed: 55 additions & 17 deletions

File tree

rsa/key.py

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import rsa.core
4444

4545
log = logging.getLogger(__name__)
46+
DEFAULT_EXPONENT = 65537
4647

4748

4849
class AbstractKey(object):
@@ -597,31 +598,48 @@ def is_acceptable(p, q):
597598
return max(p, q), min(p, q)
598599

599600

600-
def calculate_keys(p, q, nbits):
601-
"""Calculates an encryption and a decryption key given p and q, and
602-
returns them as a tuple (e, d)
601+
def calculate_keys_custom_exponent(p, q, exponent):
602+
"""Calculates an encryption and a decryption key given p, q and an exponent,
603+
and returns them as a tuple (e, d)
604+
605+
:param p: the first large prime
606+
:param q: the second large prime
607+
:param exponent: the exponent for the key; only change this if you know
608+
what you're doing, as the exponent influences how difficult your
609+
private key can be cracked. A very common choice for e is 65537.
610+
:type exponent: int
603611
604612
"""
605613

606614
phi_n = (p - 1) * (q - 1)
607615

608-
# A very common choice for e is 65537
609-
e = 65537
610-
611616
try:
612-
d = rsa.common.inverse(e, phi_n)
617+
d = rsa.common.inverse(exponent, phi_n)
613618
except ValueError:
614619
raise ValueError("e (%d) and phi_n (%d) are not relatively prime" %
615-
(e, phi_n))
620+
(exponent, phi_n))
616621

617-
if (e * d) % phi_n != 1:
622+
if (exponent * d) % phi_n != 1:
618623
raise ValueError("e (%d) and d (%d) are not mult. inv. modulo "
619-
"phi_n (%d)" % (e, d, phi_n))
624+
"phi_n (%d)" % (exponent, d, phi_n))
625+
626+
return exponent, d
627+
628+
629+
def calculate_keys(p, q):
630+
"""Calculates an encryption and a decryption key given p and q, and
631+
returns them as a tuple (e, d)
632+
633+
:param p: the first large prime
634+
:param q: the second large prime
635+
636+
:return: tuple (e, d) with the encryption and decryption exponents.
637+
"""
620638

621-
return e, d
639+
return calculate_keys_custom_exponent(p, q, DEFAULT_EXPONENT)
622640

623641

624-
def gen_keys(nbits, getprime_func, accurate=True):
642+
def gen_keys(nbits, getprime_func, accurate=True, exponent=DEFAULT_EXPONENT):
625643
"""Generate RSA keys of nbits bits. Returns (p, q, e, d).
626644
627645
Note: this can take a long time, depending on the key size.
@@ -630,22 +648,26 @@ def gen_keys(nbits, getprime_func, accurate=True):
630648
``q`` will use ``nbits/2`` bits.
631649
:param getprime_func: either :py:func:`rsa.prime.getprime` or a function
632650
with similar signature.
651+
:param exponent: the exponent for the key; only change this if you know
652+
what you're doing, as the exponent influences how difficult your
653+
private key can be cracked. A very common choice for e is 65537.
654+
:type exponent: int
633655
"""
634656

635657
# Regenerate p and q values, until calculate_keys doesn't raise a
636658
# ValueError.
637659
while True:
638660
(p, q) = find_p_q(nbits // 2, getprime_func, accurate)
639661
try:
640-
(e, d) = calculate_keys(p, q, nbits // 2)
662+
(e, d) = calculate_keys_custom_exponent(p, q, exponent=exponent)
641663
break
642664
except ValueError:
643665
pass
644666

645667
return p, q, e, d
646668

647669

648-
def newkeys(nbits, accurate=True, poolsize=1):
670+
def newkeys(nbits, accurate=True, poolsize=1, exponent=DEFAULT_EXPONENT):
649671
"""Generates public and private keys, and returns them as (pub, priv).
650672
651673
The public key is also known as the 'encryption key', and is a
@@ -659,6 +681,10 @@ def newkeys(nbits, accurate=True, poolsize=1):
659681
:param poolsize: the number of processes to use to generate the prime
660682
numbers. If set to a number > 1, a parallel algorithm will be used.
661683
This requires Python 2.6 or newer.
684+
:param exponent: the exponent for the key; only change this if you know
685+
what you're doing, as the exponent influences how difficult your
686+
private key can be cracked. A very common choice for e is 65537.
687+
:type exponent: int
662688
663689
:returns: a tuple (:py:class:`rsa.PublicKey`, :py:class:`rsa.PrivateKey`)
664690
@@ -683,7 +709,7 @@ def newkeys(nbits, accurate=True, poolsize=1):
683709
getprime_func = rsa.prime.getprime
684710

685711
# Generate the key components
686-
(p, q, e, d) = gen_keys(nbits, getprime_func, accurate=accurate)
712+
(p, q, e, d) = gen_keys(nbits, getprime_func, accurate=accurate, exponent=exponent)
687713

688714
# Create the key objects
689715
n = p * q

tests/test_key.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
Some tests for the rsa/key.py file.
33
"""
44

5-
65
import unittest
76

87
import rsa.key
98
import rsa.core
109

1110

1211
class BlindingTest(unittest.TestCase):
13-
1412
def test_blinding(self):
1513
"""Test blinding and unblinding.
1614
@@ -28,3 +26,17 @@ def test_blinding(self):
2826
unblinded = pk.unblind(decrypted, 4134431)
2927

3028
self.assertEqual(unblinded, message)
29+
30+
31+
class KeyGenTest(unittest.TestCase):
32+
def test_custom_exponent(self):
33+
priv, pub = rsa.key.newkeys(16, exponent=3)
34+
35+
self.assertEqual(3, priv.e)
36+
self.assertEqual(3, pub.e)
37+
38+
def test_default_exponent(self):
39+
priv, pub = rsa.key.newkeys(16)
40+
41+
self.assertEqual(0x10001, priv.e)
42+
self.assertEqual(0x10001, pub.e)

0 commit comments

Comments
 (0)