Merge branch 'master' into add-crt-coefficients
* master:
Also clean up this syntax
Fixed a missing word in the RSA docs
Fix comments in padding.py to be accurate
add versionadded to cast5
A few style nits in the docs
add CAST5 support to changelog
Changed .... lines to ~~~~ and s/Gnu\/Linux/Linux/
Pypy is not a real word either apparently.
Added Pypy note and fixed libffi's "spelling"
Added Debian mention, extra missing packages
Added a docs section on Linux installation
remove some extra linebreaks
add cast5 docs
Syntax highlight the go code. Be mad Rob Pike.
add cbc, cfb, ofb support to CAST5 (aka CAST128) for openssl & cc
re-add CAST5 ECB support (OpenSSL & CC backends). fixes #417
Switch this to a warning block
Be clear about HKDF's applicability for password storage
Conflicts:
docs/hazmat/primitives/rsa.rst
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index ef34cb4..8a4aeac 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -302,6 +302,9 @@
return rsa.RSAPrivateKey(
p=self._bn_to_int(ctx.p),
q=self._bn_to_int(ctx.q),
+ dmp1=self._bn_to_int(ctx.dmp1),
+ dmq1=self._bn_to_int(ctx.dmq1),
+ iqmp=self._bn_to_int(ctx.iqmp),
private_exponent=self._bn_to_int(ctx.d),
public_exponent=self._bn_to_int(ctx.e),
modulus=self._bn_to_int(ctx.n),
diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py
index 60c5c80..0121859 100644
--- a/cryptography/hazmat/primitives/asymmetric/rsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -72,10 +72,14 @@
@utils.register_interface(interfaces.RSAPrivateKey)
class RSAPrivateKey(object):
- def __init__(self, p, q, private_exponent, public_exponent, modulus):
+ def __init__(self, p, q, private_exponent, dmp1, dmq1, iqmp,
+ public_exponent, modulus):
if (
not isinstance(p, six.integer_types) or
not isinstance(q, six.integer_types) or
+ not isinstance(dmp1, six.integer_types) or
+ not isinstance(dmq1, six.integer_types) or
+ not isinstance(iqmp, six.integer_types) or
not isinstance(private_exponent, six.integer_types) or
not isinstance(public_exponent, six.integer_types) or
not isinstance(modulus, six.integer_types)
@@ -91,6 +95,15 @@
if q >= modulus:
raise ValueError("q must be < modulus")
+ if dmp1 >= modulus:
+ raise ValueError("dmp1 must be < modulus")
+
+ if dmq1 >= modulus:
+ raise ValueError("dmq1 must be < modulus")
+
+ if iqmp >= modulus:
+ raise ValueError("iqmp must be < modulus")
+
if private_exponent >= modulus:
raise ValueError("private_exponent must be < modulus")
@@ -100,11 +113,20 @@
if public_exponent & 1 == 0:
raise ValueError("public_exponent must be odd")
+ if dmp1 & 1 == 0:
+ raise ValueError("dmp1 must be odd")
+
+ if dmq1 & 1 == 0:
+ raise ValueError("dmq1 must be odd")
+
if p * q != modulus:
raise ValueError("p*q must equal modulus")
self._p = p
self._q = q
+ self._dmp1 = dmp1
+ self._dmq1 = dmq1
+ self._iqmp = iqmp
self._private_exponent = private_exponent
self._public_exponent = public_exponent
self._modulus = modulus
@@ -145,6 +167,18 @@
return self.private_exponent
@property
+ def dmp1(self):
+ return self._dmp1
+
+ @property
+ def dmq1(self):
+ return self._dmq1
+
+ @property
+ def iqmp(self):
+ return self._iqmp
+
+ @property
def e(self):
return self.public_exponent
diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py
index 460aab7..5ef469d 100644
--- a/cryptography/hazmat/primitives/interfaces.py
+++ b/cryptography/hazmat/primitives/interfaces.py
@@ -228,6 +228,27 @@
"""
@abc.abstractproperty
+ def dmp1(self):
+ """
+ A Chinese remainder theorem coefficient used to speed up RSA
+ calculations. Calculated as: d mod (p-1)
+ """
+
+ @abc.abstractproperty
+ def dmq1(self):
+ """
+ A Chinese remainder theorem coefficient used to speed up RSA
+ calculations. Calculated as: d mod (q-1)
+ """
+
+ @abc.abstractproperty
+ def iqmp(self):
+ """
+ A Chinese remainder theorem coefficient used to speed up RSA
+ calculations. The modular inverse of q modulo p
+ """
+
+ @abc.abstractproperty
def e(self):
"""
The public exponent of the RSA key. Alias for public_exponent.
diff --git a/docs/hazmat/primitives/interfaces.rst b/docs/hazmat/primitives/interfaces.rst
index cbca5ed..df17e59 100644
--- a/docs/hazmat/primitives/interfaces.rst
+++ b/docs/hazmat/primitives/interfaces.rst
@@ -160,6 +160,27 @@
The private exponent. Alias for :attr:`private_exponent`.
+ .. attribute:: dmp1
+
+ :type: int
+
+ A `Chinese remainder theorem`_ coefficient used to speed up RSA
+ operations. Calculated as: d mod (p-1)
+
+ .. attribute:: dmq1
+
+ :type: int
+
+ A `Chinese remainder theorem`_ coefficient used to speed up RSA
+ operations. Calculated as: d mod (q-1)
+
+ .. attribute:: iqmp
+
+ :type: int
+
+ A `Chinese remainder theorem`_ coefficient used to speed up RSA
+ operations. Calculated as: q\ :sup:`-1` mod p
+
.. attribute:: n
:type: int
@@ -279,4 +300,5 @@
something like checking whether a user's password attempt matches the
stored derived key.
-.. _`RSA`: http://en.wikipedia.org/wiki/RSA_(cryptosystem)
+.. _`RSA`: https://en.wikipedia.org/wiki/RSA_(cryptosystem)
+.. _`Chinese remainder theorem`: https://en.wikipedia.org/wiki/Chinese_remainder_theorem
diff --git a/docs/hazmat/primitives/rsa.rst b/docs/hazmat/primitives/rsa.rst
index 2875b20..e7ec474 100644
--- a/docs/hazmat/primitives/rsa.rst
+++ b/docs/hazmat/primitives/rsa.rst
@@ -7,7 +7,8 @@
`RSA`_ is a `public-key`_ algorithm for encrypting and signing messages.
-.. class:: RSAPrivateKey(p, q, private_exponent, public_exponent, modulus)
+.. class:: RSAPrivateKey(p, q, private_exponent, dmp1, dmq1, iqmp,
+ public_exponent, modulus)
.. versionadded:: 0.2
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py
index 0e930e4..312d5a6 100644
--- a/tests/hazmat/primitives/test_rsa.py
+++ b/tests/hazmat/primitives/test_rsa.py
@@ -24,6 +24,20 @@
from ...utils import load_pkcs1_vectors, load_vectors_from_file
+def _egcd(a, b):
+ if a == 0:
+ return (b, 0, 1)
+ else:
+ g, y, x = _egcd(b % a, a)
+ return (g, x - (b // a) * y, y)
+
+
+def _modinv(a, m):
+ g, x, y = _egcd(a, m)
+ assert g == 1
+ return x % m
+
+
def _check_rsa_private_key(skey):
assert skey
assert skey.modulus
@@ -31,6 +45,9 @@
assert skey.private_exponent
assert skey.p * skey.q == skey.modulus
assert skey.key_size
+ assert skey.dmp1 == skey.d % (skey.p - 1)
+ assert skey.dmq1 == skey.d % (skey.q - 1)
+ assert skey.iqmp == _modinv(skey.q, skey.p)
pkey = skey.public_key()
assert pkey
@@ -115,22 +132,26 @@
def test_invalid_private_key_argument_types(self):
with pytest.raises(TypeError):
- rsa.RSAPrivateKey(None, None, None, None, None)
+ rsa.RSAPrivateKey(None, None, None, None, None, None, None, None)
def test_invalid_public_key_argument_types(self):
with pytest.raises(TypeError):
rsa.RSAPublicKey(None, None)
def test_invalid_private_key_argument_values(self):
- # Start with p=3, q=5, private_exponent=14, public_exponent=7,
- # modulus=15. Then change one value at a time to test the bounds.
+ # Start with p=3, q=11, private_exponent=3, public_exponent=7,
+ # modulus=33, dmp1=1, dmq1=3, iqmp=2. Then change one value at
+ # a time to test the bounds.
# Test a modulus < 3.
with pytest.raises(ValueError):
rsa.RSAPrivateKey(
p=3,
- q=5,
- private_exponent=14,
+ q=11,
+ private_exponent=3,
+ dmp1=1,
+ dmq1=3,
+ iqmp=2,
public_exponent=7,
modulus=2
)
@@ -139,70 +160,156 @@
with pytest.raises(ValueError):
rsa.RSAPrivateKey(
p=3,
- q=5,
- private_exponent=14,
+ q=11,
+ private_exponent=3,
+ dmp1=1,
+ dmq1=3,
+ iqmp=2,
public_exponent=7,
- modulus=16
+ modulus=35
)
# Test a p > modulus.
with pytest.raises(ValueError):
rsa.RSAPrivateKey(
- p=16,
- q=5,
- private_exponent=14,
+ p=37,
+ q=11,
+ private_exponent=3,
+ dmp1=1,
+ dmq1=3,
+ iqmp=2,
public_exponent=7,
- modulus=15
+ modulus=33
)
# Test a q > modulus.
with pytest.raises(ValueError):
rsa.RSAPrivateKey(
p=3,
- q=16,
- private_exponent=14,
+ q=37,
+ private_exponent=3,
+ dmp1=1,
+ dmq1=3,
+ iqmp=2,
public_exponent=7,
- modulus=15
+ modulus=33
+ )
+
+ # Test a dmp1 > modulus.
+ with pytest.raises(ValueError):
+ rsa.RSAPrivateKey(
+ p=3,
+ q=11,
+ private_exponent=3,
+ dmp1=35,
+ dmq1=3,
+ iqmp=2,
+ public_exponent=7,
+ modulus=33
+ )
+
+ # Test a dmq1 > modulus.
+ with pytest.raises(ValueError):
+ rsa.RSAPrivateKey(
+ p=3,
+ q=11,
+ private_exponent=3,
+ dmp1=1,
+ dmq1=35,
+ iqmp=2,
+ public_exponent=7,
+ modulus=33
+ )
+
+ # Test an iqmp > modulus.
+ with pytest.raises(ValueError):
+ rsa.RSAPrivateKey(
+ p=3,
+ q=11,
+ private_exponent=3,
+ dmp1=1,
+ dmq1=3,
+ iqmp=35,
+ public_exponent=7,
+ modulus=33
)
# Test a private_exponent > modulus
with pytest.raises(ValueError):
rsa.RSAPrivateKey(
p=3,
- q=5,
- private_exponent=16,
+ q=11,
+ private_exponent=37,
+ dmp1=1,
+ dmq1=3,
+ iqmp=2,
public_exponent=7,
- modulus=15
+ modulus=33
)
# Test a public_exponent < 3
with pytest.raises(ValueError):
rsa.RSAPrivateKey(
p=3,
- q=5,
- private_exponent=14,
+ q=11,
+ private_exponent=3,
+ dmp1=1,
+ dmq1=3,
+ iqmp=2,
public_exponent=1,
- modulus=15
+ modulus=33
)
# Test a public_exponent > modulus
with pytest.raises(ValueError):
rsa.RSAPrivateKey(
p=3,
- q=5,
- private_exponent=14,
- public_exponent=17,
- modulus=15
+ q=11,
+ private_exponent=3,
+ dmp1=1,
+ dmq1=3,
+ iqmp=2,
+ public_exponent=65537,
+ modulus=33
)
# Test a public_exponent that is not odd.
with pytest.raises(ValueError):
rsa.RSAPrivateKey(
p=3,
- q=5,
- private_exponent=14,
+ q=11,
+ private_exponent=3,
+ dmp1=1,
+ dmq1=3,
+ iqmp=2,
public_exponent=6,
- modulus=15
+ modulus=33
+ )
+
+ # Test a dmp1 that is not odd.
+ with pytest.raises(ValueError):
+ rsa.RSAPrivateKey(
+ p=3,
+ q=11,
+ private_exponent=3,
+ dmp1=2,
+ dmq1=3,
+ iqmp=2,
+ public_exponent=7,
+ modulus=33
+ )
+
+ # Test a dmq1 that is not odd.
+ with pytest.raises(ValueError):
+ rsa.RSAPrivateKey(
+ p=3,
+ q=11,
+ private_exponent=3,
+ dmp1=1,
+ dmq1=4,
+ iqmp=2,
+ public_exponent=7,
+ modulus=33
)
def test_invalid_public_key_argument_values(self):
diff --git a/tests/test_utils.py b/tests/test_utils.py
index a0571ad..0e1d77b 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -768,7 +768,19 @@
'q': int(
'847e732376fc7900f898ea82eb2b0fc418565fdae62f7d9ec4ce2217b'
'97990dd272db157f99f63c0dcbb9fbacdbd4c4dadb6df67756358ca41'
- '74825b48f49706d', 16)
+ '74825b48f49706d', 16),
+ 'dmp1': int(
+ '05c2a83c124b3621a2aa57ea2c3efe035eff4560f33ddebb7adab81fc'
+ 'e69a0c8c2edc16520dda83d59a23be867963ac65f2cc710bbcfb96ee1'
+ '03deb771d105fd85', 16),
+ 'dmq1': int(
+ '04cae8aa0d9faa165c87b682ec140b8ed3b50b24594b7a3b2c220b366'
+ '9bb819f984f55310a1ae7823651d4a02e99447972595139363434e5e3'
+ '0a7e7d241551e1b9', 16),
+ 'iqmp': int(
+ '07d3e47bf686600b11ac283ce88dbb3f6051e8efd04680e44c171ef53'
+ '1b80b2b7c39fc766320e2cf15d8d99820e96ff30dc69691839c4b40d7'
+ 'b06e45307dc91f3f', 16)
},
{
@@ -809,7 +821,22 @@
'ed4d71d0a6e24b93c2e5f6b4bbe05f5fb0afa042d204fe3378d365c2f'
'288b6a8dad7efe45d153eef40cacc7b81ff934002d108994b94a5e472'
'8cd9c963375ae49965bda55cbf0efed8d6553b4027f2d86208a6e6b48'
- '9c176128092d629e49d3d', 16)
+ '9c176128092d629e49d3d', 16),
+ 'dmp1': int(
+ '2bb68bddfb0c4f56c8558bffaf892d8043037841e7fa81cfa61a38c5e'
+ '39b901c8ee71122a5da2227bd6cdeeb481452c12ad3d61d5e4f776a0a'
+ 'b556591befe3e59e5a7fddb8345e1f2f35b9f4cee57c32414c086aec9'
+ '93e9353e480d9eec6289f', 16),
+ 'dmq1': int(
+ '4ff897709fad079746494578e70fd8546130eeab5627c49b080f05ee4'
+ 'ad9f3e4b7cba9d6a5dff113a41c3409336833f190816d8a6bc42e9bec'
+ '56b7567d0f3c9c696db619b245d901dd856db7c8092e77e9a1cccd56e'
+ 'e4dba42c5fdb61aec2669', 16),
+ 'iqmp': int(
+ '77b9d1137b50404a982729316efafc7dfe66d34e5a182600d5f30a0a8'
+ '512051c560d081d4d0a1835ec3d25a60f4e4d6aa948b2bf3dbb5b124c'
+ 'bbc3489255a3a948372f6978496745f943e1db4f18382ceaa505dfc65'
+ '757bb3f857a58dce52156', 16)
},
{
diff --git a/tests/utils.py b/tests/utils.py
index 408b05f..2bbecd7 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -262,6 +262,12 @@
attr = "p"
elif line.startswith("# Prime 2:"):
attr = "q"
+ elif line.startswith("# Prime exponent 1:"):
+ attr = "dmp1"
+ elif line.startswith("# Prime exponent 2:"):
+ attr = "dmq1"
+ elif line.startswith("# Coefficient:"):
+ attr = "iqmp"
elif line.startswith("#"):
attr = None
else: