diff options
author | Alex Stapleton <alexs@prol.etari.at> | 2014-02-02 19:30:03 +0000 |
---|---|---|
committer | Alex Stapleton <alexs@prol.etari.at> | 2014-02-05 18:34:25 +0000 |
commit | 52026b85c3df15476d38f308cee59a29a9b43195 (patch) | |
tree | 5763cf2c950c8c473f05f6f4bdd4c2f4d8e62029 | |
parent | f970eaa676eb0cd89cdb2389f03d365899812822 (diff) | |
download | cryptography-52026b85c3df15476d38f308cee59a29a9b43195.tar.gz cryptography-52026b85c3df15476d38f308cee59a29a9b43195.tar.bz2 cryptography-52026b85c3df15476d38f308cee59a29a9b43195.zip |
RSA keys
These are implemented such that they don't depend on the backend. This
means we don't have to worry about passing an RSA key created with one
backend to a different one so much at the expense of having to create a
backend specific context on demand.
This is slightly non-trivial in (at least) OpenSSL as there are 3
additional derived parameters kept in its RSA struct. They aren't
difficult to generate but it requires adding 30-40 lines of BN_* stuff
to the backend so I'm leaving that out for now. We'll need to implement
that before we can actually do any useful operations with the keys.
This also adds a loader for some of the PKCS #1 test vectors. It only
extracts the 10 key pairs from pss_vect.txt currently be should be
extenable to include the example signatures and other files later.
-rw-r--r-- | cryptography/hazmat/primitives/asymmetric/__init__.py | 0 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/asymmetric/rsa.py | 117 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_rsa.py | 58 |
3 files changed, 175 insertions, 0 deletions
diff --git a/cryptography/hazmat/primitives/asymmetric/__init__.py b/cryptography/hazmat/primitives/asymmetric/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/cryptography/hazmat/primitives/asymmetric/__init__.py diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py new file mode 100644 index 00000000..aa24aee4 --- /dev/null +++ b/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -0,0 +1,117 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import, division, print_function + +import six + +from cryptography import utils +from cryptography.hazmat.primitives import interfaces + + +def _bit_length(x): + try: + return x.bit_length() + except AttributeError: + return len(bin(x)) - (2 + (x <= 0)) + + +@utils.register_interface(interfaces.RSAPublicKey) +class RSAPublicKey(object): + def __init__(self, public_exponent, modulus): + if ( + not isinstance(public_exponent, six.integer_types) or + not isinstance(modulus, six.integer_types) + ): + raise TypeError("RSAPublicKey arguments must be integers") + + self._public_exponent = public_exponent + self._modulus = modulus + + @property + def key_size(self): + return _bit_length(self.modulus) + + @property + def public_exponent(self): + return self._public_exponent + + @property + def modulus(self): + return self._modulus + + @property + def e(self): + return self.public_exponent + + @property + def n(self): + return self.modulus + + +@utils.register_interface(interfaces.RSAPrivateKey) +class RSAPrivateKey(object): + def __init__(self, p, q, private_exponent, public_exponent, modulus): + if ( + not isinstance(p, six.integer_types) or + not isinstance(q, 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) + ): + raise TypeError("RSAPrivateKey arguments must be integers") + + self._p = p + self._q = q + self._private_exponent = private_exponent + self._public_exponent = public_exponent + self._modulus = modulus + + @property + def key_size(self): + return _bit_length(self.modulus) + + def public_key(self): + return RSAPublicKey(self.public_exponent, self.modulus) + + @property + def p(self): + return self._p + + @property + def q(self): + return self._q + + @property + def private_exponent(self): + return self._private_exponent + + @property + def public_exponent(self): + return self._public_exponent + + @property + def modulus(self): + return self._modulus + + @property + def d(self): + return self.private_exponent + + @property + def e(self): + return self.public_exponent + + @property + def n(self): + return self.modulus diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py new file mode 100644 index 00000000..e50417b8 --- /dev/null +++ b/tests/hazmat/primitives/test_rsa.py @@ -0,0 +1,58 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from __future__ import absolute_import, division, print_function + +import pytest + +from cryptography.hazmat.primitives.asymmetric import rsa + +from ...utils import load_pkcs1_vectors, load_vectors_from_file + + +class TestRSA(object): + @pytest.mark.parametrize( + "pkcs1_example", + load_vectors_from_file( + "asymmetric/RSA/pkcs-1v2-1d2-vec/pss-vect.txt", + load_pkcs1_vectors + ) + ) + def test_load_pss_vect_example_keys(self, pkcs1_example): + secret, public = pkcs1_example + + skey = rsa.RSAPrivateKey(**secret) + pkey = rsa.RSAPublicKey(**public) + pkey2 = skey.public_key() + + assert skey and pkey and pkey2 + + assert skey.modulus + assert skey.modulus == pkey.modulus + assert skey.public_exponent == pkey.public_exponent + + assert pkey.modulus + assert pkey.modulus == pkey2.modulus + assert pkey.public_exponent == pkey2.public_exponent + + assert skey.key_size + assert skey.key_size == pkey.key_size + assert skey.key_size == pkey2.key_size + + def test_invalid_arguments(self): + with pytest.raises(TypeError): + rsa.RSAPrivateKey(None, None, None, None, None) + + with pytest.raises(TypeError): + rsa.RSAPublicKey(None, None) |