> On Feb 20, 2018, at 11:00 , cryptography-dev-requ...@python.org wrote:
> ec.derive_private_key_from_bytes(secret_bytes, ec.SECP384R1(), backend)
> could potentially be a way to do this specific operation while reducing the
> number of copies (to zero in Python and 2-3 in OpenSSL, although the latter
> are zeroed), but without tests that can detect non-required copies of
> secret material it would be extremely hard to prevent regression in the
> long term as the code is updated.


        Based upon your hint above I just went in to the code and looked 
around. It looks like a straightforward extension. Note: I am not a library 
developer and, hence, have not developed all of the various skills to properly 
build pyca/cryptography. Nor am I particularly knowledgable about Python v2.7 & 
v3.6 interoperation issues, much less CPython, Cython and PyPy interoperation. 
The below code is a gedanken exploration to see how hard or involved it might 
actually be to extend pyca/cryptography. 

        TL;DR: It isn’t hard. I C&P three functions and then modded them. Gratz 
to the team on an excellent design.

        It is clear that I could deploy my own copy of cryptography and call it 
a day. But I believe that key hygiene is an important social good. I am happy 
to help look at/write each routine that imports key material and propose 
versions that allow good key hygiene. As you note above, there is no solution 
for your regression testing issue except to discuss it in code commentary. Now 
to find a routine that can bang the contents of a bytes array to 0, 1 and then 

        Should I proceed to engage with your team or just continue on my merry 
way? I want to help but if it isn’t something the team prioritizes highly right 
now, I fully understand.

Andrew W. Donoho
Donoho Design Group, L.L.C.
andrew.don...@gmail.com, +1 (512) 666-7596, twitter.com/adonoho

No risk, no art.
        No art, no reward.
                -- Seth Godin

===== backend.py =====

def _bytes_to_bn(self, num, bn=None):
    # Was: def _int_to_bn(self, num, bn=None):
    Converts a python bytes array to a BIGNUM. The returned BIGNUM will not
    be garbage collected (to support adding them to structs that take
    ownership of the object). Be sure to register it for GC if it will
    be discarded after use.
    assert bn is None or bn != self._ffi.NULL

    if bn is None:
        bn = self._ffi.NULL

    bn_ptr = self._lib.BN_bin2bn(num, len(num), bn)
    self.openssl_assert(bn_ptr != self._ffi.NULL)
    return bn_ptr

def derive_elliptic_curve_private_key_bytes(self, private_bytes, curve):
    # Was: def derive_elliptic_curve_private_key(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)

    get_func, group = self._ec_key_determine_group_get_func(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._bytes_to_bn(private_bytes)
    value = self._ffi.gc(value, self._lib.BN_clear_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)

    res = self._lib.EC_KEY_set_public_key(ec_cdata, point)
    self.openssl_assert(res == 1)
    private = self._bytes_to_bn(private_bytes)
    private = self._ffi.gc(private, self._lib.BN_clear_free)
    res = self._lib.EC_KEY_set_private_key(ec_cdata, private)
    self.openssl_assert(res == 1)

    evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)

    return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)

===== ec.py =====

def derive_private_key_bytes(private_value, curve, backend):
    # Was: def derive_private_key(private_value, curve, backend):
   if not isinstance(curve, EllipticCurve):
        raise TypeError("curve must provide the EllipticCurve interface.")

    return backend.derive_elliptic_curve_private_key_bytes(private_value, curve)

Cryptography-dev mailing list

Reply via email to