diff options
author | Ofek Lev <ofekmeister@gmail.com> | 2016-11-11 10:54:00 -0500 |
---|---|---|
committer | Paul Kehrer <paul.l.kehrer@gmail.com> | 2016-11-11 07:54:00 -0800 |
commit | c816735f0e9250328e4a697c8dfb23f0aa1e584b (patch) | |
tree | 2e95196a00131e26f078abee5516926dc72797a3 | |
parent | 01ee6f5e391eee76e6cd3062de8fc84851bd06e3 (diff) | |
download | cryptography-c816735f0e9250328e4a697c8dfb23f0aa1e584b.tar.gz cryptography-c816735f0e9250328e4a697c8dfb23f0aa1e584b.tar.bz2 cryptography-c816735f0e9250328e4a697c8dfb23f0aa1e584b.zip |
add ec.private_key_from_secret_and_curve (#3225)
* finish https://github.com/pyca/cryptography/pull/1973
* change API & add test
Function will now return an instance of EllipticCurvePrivateKey, as that
is the users' ultimate goal anyway.
* fix test
* improve coverage
* complete coverage
* final fix
* centos fix
* retry
* cleanup asserts
* use openssl_assert
* skip unsupported platforms
* change API name to derive_private_key
* change version added
* improve description of `secret` param
* separate successful and failure test cases
* simplify successful case
* add docs for derive_elliptic_curve_public_point
* add period
-rw-r--r-- | docs/hazmat/backends/interfaces.rst | 9 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/ec.rst | 16 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/interfaces.py | 6 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/multibackend.py | 13 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 34 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/asymmetric/ec.py | 13 | ||||
-rw-r--r-- | tests/hazmat/backends/test_multibackend.py | 16 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_ec.py | 26 |
8 files changed, 133 insertions, 0 deletions
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst index b79bb239..0a0d1456 100644 --- a/docs/hazmat/backends/interfaces.rst +++ b/docs/hazmat/backends/interfaces.rst @@ -422,6 +422,15 @@ A specific ``backend`` may provide one or more of these interfaces. :returns: An instance of :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey`. + .. method:: derive_elliptic_curve_public_point(private_value, curve) + + :param private_value: A secret scalar value. + + :param curve: An instance of + :class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurve`. + + :returns: A tuple (x, y). + .. class:: PEMSerializationBackend .. versionadded:: 0.6 diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst index 2421d921..33ebee0f 100644 --- a/docs/hazmat/primitives/asymmetric/ec.rst +++ b/docs/hazmat/primitives/asymmetric/ec.rst @@ -20,6 +20,22 @@ Elliptic curve cryptography :returns: A new instance of :class:`EllipticCurvePrivateKey`. +.. function:: derive_private_key(secret, curve, backend) + + .. versionadded:: 1.6 + + Derive a private key from ``secret`` on ``curve`` for use with ``backend``. + + :param int secret: The secret scalar value. + + :param curve: An instance of :class:`EllipticCurve`. + + :param backend: An instance of + :class:`~cryptography.hazmat.backends.interfaces.EllipticCurveBackend`. + + :returns: A new instance of :class:`EllipticCurvePrivateKey`. + + Elliptic Curve Signature Algorithms ----------------------------------- diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py index 9a1d704a..ad4a4364 100644 --- a/src/cryptography/hazmat/backends/interfaces.py +++ b/src/cryptography/hazmat/backends/interfaces.py @@ -221,6 +221,12 @@ class EllipticCurveBackend(object): Returns whether the exchange algorithm is supported by this backend. """ + @abc.abstractmethod + def derive_elliptic_curve_public_point(self, private_value, curve): + """ + Compute the public key point (x, y) given the private value and curve. + """ + @six.add_metaclass(abc.ABCMeta) class PEMSerializationBackend(object): diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py index deca020e..ab9127f7 100644 --- a/src/cryptography/hazmat/backends/multibackend.py +++ b/src/cryptography/hazmat/backends/multibackend.py @@ -279,6 +279,19 @@ class MultiBackend(object): _Reasons.UNSUPPORTED_ELLIPTIC_CURVE ) + def derive_elliptic_curve_public_point(self, private_value, curve): + for b in self._filtered_backends(EllipticCurveBackend): + try: + return b.derive_elliptic_curve_public_point(private_value, + curve) + except UnsupportedAlgorithm: + continue + + raise UnsupportedAlgorithm( + "This backend does not support this elliptic curve.", + _Reasons.UNSUPPORTED_ELLIPTIC_CURVE + ) + def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve): return any( b.elliptic_curve_exchange_algorithm_supported(algorithm, curve) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 41e7e773..79914293 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1386,6 +1386,40 @@ class Backend(object): return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) + def derive_elliptic_curve_public_point(self, private_value, curve): + curve_nid = self._elliptic_curve_to_nid(curve) + + ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) + self.openssl_assert(ec_cdata != self._ffi.NULL) + ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) + + set_func, get_func, group = ( + self._ec_key_determine_group_get_set_funcs(ec_cdata) + ) + + point = self._lib.EC_POINT_new(group) + self.openssl_assert(point != self._ffi.NULL) + point = self._ffi.gc(point, self._lib.EC_POINT_free) + + value = self._int_to_bn(private_value) + value = self._ffi.gc(value, self._lib.BN_free) + + with self._tmp_bn_ctx() as bn_ctx: + res = self._lib.EC_POINT_mul(group, point, value, self._ffi.NULL, + self._ffi.NULL, bn_ctx) + self.openssl_assert(res == 1) + + bn_x = self._lib.BN_CTX_get(bn_ctx) + bn_y = self._lib.BN_CTX_get(bn_ctx) + + res = get_func(group, point, bn_x, bn_y, bn_ctx) + self.openssl_assert(res == 1) + + point_x = self._bn_to_int(bn_x) + point_y = self._bn_to_int(bn_y) + + return point_x, point_y + def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve): return ( self.elliptic_curve_supported(curve) and diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py index 1c576c6d..1005ccd6 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/ec.py +++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py @@ -253,6 +253,19 @@ def generate_private_key(curve, backend): return backend.generate_elliptic_curve_private_key(curve) +def derive_private_key(secret, curve, backend): + if not isinstance(secret, six.integer_types): + raise TypeError("secret must be an integer type.") + + if not isinstance(curve, EllipticCurve): + raise TypeError("curve must provide the EllipticCurve interface.") + + x, y = backend.derive_elliptic_curve_public_point(secret, curve) + public_numbers = EllipticCurvePublicNumbers(x, y, curve) + private_numbers = EllipticCurvePrivateNumbers(secret, public_numbers) + return private_numbers.private_key(backend) + + class EllipticCurvePublicNumbers(object): def __init__(self, x, y, curve): if ( diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py index 1cd87336..319edf7d 100644 --- a/tests/hazmat/backends/test_multibackend.py +++ b/tests/hazmat/backends/test_multibackend.py @@ -27,6 +27,12 @@ class DummyBackend(object): pass +@utils.register_interface(ec.EllipticCurve) +class DummyCurve(object): + name = "dummy-curve" + key_size = 1 + + @utils.register_interface(CipherBackend) class DummyCipherBackend(object): def __init__(self, supported_ciphers): @@ -179,6 +185,10 @@ class DummyEllipticCurveBackend(object): self.elliptic_curve_supported(curve) ) + def derive_elliptic_curve_public_point(self, private_value, curve): + if not self.elliptic_curve_supported(curve): + raise UnsupportedAlgorithm(_Reasons.UNSUPPORTED_ELLIPTIC_CURVE) + @utils.register_interface(PEMSerializationBackend) class DummyPEMSerializationBackend(object): @@ -501,6 +511,12 @@ class TestMultiBackend(object): ec.ECDH(), ec.SECT163K1() ) + with pytest.raises(UnsupportedAlgorithm): + backend.derive_elliptic_curve_public_point(123, DummyCurve()) + + assert backend.derive_elliptic_curve_public_point( + 123, ec.SECT283K1()) is None + def test_pem_serialization_backend(self): backend = MultiBackend([DummyPEMSerializationBackend()]) diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py index dff2f3e1..523f3f4e 100644 --- a/tests/hazmat/primitives/test_ec.py +++ b/tests/hazmat/primitives/test_ec.py @@ -100,6 +100,32 @@ def test_skip_ecdsa_vector(backend): _skip_ecdsa_vector(backend, DummyCurve, hashes.SHA256) +@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) +def test_derive_private_key_success(backend): + curve = ec.SECP256K1() + _skip_curve_unsupported(backend, curve) + + private_numbers = ec.generate_private_key(curve, backend).private_numbers() + + derived_key = ec.derive_private_key( + private_numbers.private_value, curve, backend + ) + + assert private_numbers == derived_key.private_numbers() + + +@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) +def test_derive_private_key_errors(backend): + curve = ec.SECP256K1() + _skip_curve_unsupported(backend, curve) + + with pytest.raises(TypeError): + ec.derive_private_key('one', curve, backend) + + with pytest.raises(TypeError): + ec.derive_private_key(10, 'five', backend) + + def test_ec_numbers(): numbers = ec.EllipticCurvePrivateNumbers( 1, |