aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/hazmat/backends/interfaces.rst9
-rw-r--r--docs/hazmat/primitives/asymmetric/ec.rst16
-rw-r--r--src/cryptography/hazmat/backends/interfaces.py6
-rw-r--r--src/cryptography/hazmat/backends/multibackend.py13
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py34
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/ec.py13
-rw-r--r--tests/hazmat/backends/test_multibackend.py16
-rw-r--r--tests/hazmat/primitives/test_ec.py26
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,