Repository: pyca/cryptography

Issue:
Missing functions (i2o_ECPublicKey, i2d_ECPrivateKey, etc.)
Currently, `cryptography` is missing the following functions (useful if not necessary for ECDSA):
- [`d2i_ECPrivateKey`](http://openssl.sourcearchive.com/documentation/1.0.0d-2/crypto_2ec_2ec_8h_aeaa259adce95b0937cf6bf5cd2221639.html#aeaa259adce95b0937cf6bf5cd2221639)
- [`i2d_ECPrivateKey`](http://openssl.sourcearchive.com/documentation/1.0.0d-2/crypto_2ec_2ec_8h_ae714ab3e64494618b04e190bffa88030.html#ae714ab3e64494618b04e190bffa88030)
- [`i2o_ECPublicKey`](http://openssl.sourcearchive.com/documentation/1.0.0d-2/crypto_2ec_2ec_8h_a1a93f5739c093586ef83517b52b44a0c.html)
- [`o2i_ECPublicKey`](http://openssl.sourcearchive.com/documentation/1.0.0d-2/crypto_2ec_2ec_8h_a628017a03d4a4600b9f62ccb56699239.html#a628017a03d4a4600b9f62ccb56699239)

The latter two can be worked around using calls `cryptography` _does_ expose, but the first two are more difficult. petertodd/python-bitcoinlib#62 provides a use case.



Discussion:
We do have `i2d_ECPrivateKey_bio` as well as `d2i_ECPrivateKey_bio`, which will do the same thing as the d2i/i2d functions you list only using BIOs.

Hmmm...not sure I'm using them correctly, but:

``` py
>>> from binascii import unhexlify
>>> foo_der = unhexlify('06052b8104000a30740201010420bfc5f3e5fb7d9273551c01277643c258d72446aeb2f8cd8ec25ab6bfd5d71c7ea00706052b8104000aa1440342000448814850d8ded494faf8fbd7672849e6384ad505271e014e2cf11fec75b00fe0459f0493c1c579f12906955513860edb5eaeaf3d913a9ab43166b3a0fd40e95a')
>>> from cryptography.hazmat.bindings.openssl.binding import Binding
>>> _binding = Binding()
>>> _ssl = _binding.lib
>>> _ffi = _binding.ffi
>>> foo_der_cbuf = _ffi.new('char[]', foo_der)
>>> bio = _ssl.BIO_new_mem_buf(foo_der_cbuf, len(foo_der))
>>> bio = _ffi.gc(bio, _ssl.BIO_free)
>>> bio
<cdata 'BIO *' 0x7fee68751850>
>>> _checksslerr()
>>> k = _ssl.d2i_ECPrivateKey_bio(bio, _ffi.NULL)
>>> _checksslerr()
[[218529960L, 'error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag', 'asn1 encoding routines', 'ASN1_CHECK_TLEN', 'wrong tag'], [218595386L, 'error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error', 'asn1 encoding routines', 'ASN1_ITEM_EX_D2I', 'nested asn1 error'], [269033488L, 'error:10092010:elliptic curve routines:d2i_ECPrivateKey:EC lib', 'elliptic curve routines', 'd2i_ECPrivateKey', 'EC lib']]
```

Am I doing something wrong?

## 

`_checksslerr()` is as follows:

``` py
def _checksslerr():
    errno = _ssl.ERR_get_error()
    if errno == 0:
        return
    _ssl.SSL_load_error_strings()
    msgs = []
    while errno != 0:
        errmsg = _ffi.new('char[120]')
        _ssl.ERR_error_string_n(errno, errmsg, 120)
        msg = [
            errno,
            errmsg,
            _ssl.ERR_lib_error_string(errno),
            _ssl.ERR_func_error_string(errno),
            _ssl.ERR_reason_error_string(errno),
        ]
        msg[1:] = [ _ffi.string(c) if c else None for c in msg[1:] ]
        msgs.append(msg)
        errno = _ssl.ERR_get_error()
    return msgs
```

Take a look at how we do it here: https://github.com/pyca/cryptography/blob/master/src/cryptography/hazmat/backends/openssl/backend.py#L721

There's quite a bit of complexity unfortunately.

For serializing back to DER (PKCS8 or traditional) here's one way to do it:

https://github.com/pyca/cryptography/blob/master/src/cryptography/hazmat/backends/openssl/ec.py#L203
which calls
https://github.com/pyca/cryptography/blob/master/src/cryptography/hazmat/backends/openssl/backend.py#L1151


Resolving patch:
diff --git a/src/cryptography/hazmat/bindings/openssl/x509.py b/src/cryptography/hazmat/bindings/openssl/x509.py
--- a/src/cryptography/hazmat/bindings/openssl/x509.py
+++ b/src/cryptography/hazmat/bindings/openssl/x509.py
@@ -310,9 +310,14 @@
 EC_KEY *d2i_EC_PUBKEY(EC_KEY **, const unsigned char **, long);
 EC_KEY *d2i_EC_PUBKEY_bio(BIO *, EC_KEY **);
 int i2d_EC_PUBKEY_bio(BIO *, EC_KEY *);
+EC_KEY *d2i_ECPrivateKey(EC_KEY **, const unsigned char **, long);
 EC_KEY *d2i_ECPrivateKey_bio(BIO *, EC_KEY **);
+int i2d_ECPrivateKey(EC_KEY *, unsigned char **);
 int i2d_ECPrivateKey_bio(BIO *, EC_KEY *);
 
+EC_KEY *o2i_ECPublicKey(EC_KEY **, const unsigned char **, long);
+int i2o_ECPublicKey(EC_KEY *, unsigned char **);
+
 // declared in safestack
 int sk_ASN1_OBJECT_num(Cryptography_STACK_OF_ASN1_OBJECT *);
 ASN1_OBJECT *sk_ASN1_OBJECT_value(Cryptography_STACK_OF_ASN1_OBJECT *, int);
@@ -329,8 +334,13 @@
 EC_KEY *(*d2i_EC_PUBKEY)(EC_KEY **, const unsigned char **, long) = NULL;
 EC_KEY *(*d2i_EC_PUBKEY_bio)(BIO *, EC_KEY **) = NULL;
 int (*i2d_EC_PUBKEY_bio)(BIO *, EC_KEY *) = NULL;
+EC_KEY *(*d2i_ECPrivateKey)(EC_KEY **, const unsigned char **, long) = NULL;
 EC_KEY *(*d2i_ECPrivateKey_bio)(BIO *, EC_KEY **) = NULL;
+int (*i2d_ECPrivateKey)(EC_KEY *, unsigned char **) = NULL;
 int (*i2d_ECPrivateKey_bio)(BIO *, EC_KEY *) = NULL;
+
+EC_KEY *(*o2i_ECPublicKey)(EC_KEY **, const unsigned char **, long) = NULL;
+int (*i2o_ECPublicKey)(EC_KEY *, unsigned char **) = NULL;
 #endif
 """
 
@@ -340,7 +350,11 @@
         "d2i_EC_PUBKEY",
         "d2i_EC_PUBKEY_bio",
         "i2d_EC_PUBKEY_bio",
+        "d2i_ECPrivateKey",
         "d2i_ECPrivateKey_bio",
+        "i2d_ECPrivateKey",
         "i2d_ECPrivateKey_bio",
+        "i2o_ECPublicKey",
+        "o2i_ECPublicKey",
     ]
 }


Test patch: