From c816735f0e9250328e4a697c8dfb23f0aa1e584b Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Fri, 11 Nov 2016 10:54:00 -0500 Subject: 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 --- src/cryptography/hazmat/backends/interfaces.py | 6 ++++ src/cryptography/hazmat/backends/multibackend.py | 13 +++++++++ .../hazmat/backends/openssl/backend.py | 34 ++++++++++++++++++++++ .../hazmat/primitives/asymmetric/ec.py | 13 +++++++++ 4 files changed, 66 insertions(+) (limited to 'src') 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 ( -- cgit v1.2.3