From: Selva Nair <selva.n...@gmail.com>

- This automatically supports EC certificates through
  --management-external-cert
- EC signature request from management has the same format
  as for rsa with >RSA_SIGN replaced by >ECDSA_SIGN
  Response should be of the form 'ecdsa-sig' followed
  by DER encoded signature as base64 followed by 'END'

Signed-off-by: Selva Nair <selva.n...@gmail.com>
---
 src/openvpn/manage.c      |  30 ++++++++
 src/openvpn/manage.h      |   3 +
 src/openvpn/ssl_openssl.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 202 insertions(+)

diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c
index 55b106c..0152dee 100644
--- a/src/openvpn/manage.c
+++ b/src/openvpn/manage.c
@@ -113,6 +113,8 @@ man_help(void)
 #ifdef MANAGMENT_EXTERNAL_KEY
     msg(M_CLIENT, "rsa-sig                : Enter an RSA signature in response 
to >RSA_SIGN challenge");
     msg(M_CLIENT, "                         Enter signature base64 on 
subsequent lines followed by END");
+    msg(M_CLIENT, "ecdsa-sig              : Enter an ECDSA signature in 
response to >ECDSA_SIGN challenge");
+    msg(M_CLIENT, "                         Enter signature base64 on 
subsequent lines followed by END");
     msg(M_CLIENT, "certificate            : Enter a client certificate in 
response to >NEED-CERT challenge");
     msg(M_CLIENT, "                         Enter certificate base64 on 
subsequent lines followed by END");
 #endif
@@ -936,6 +938,7 @@ in_extra_dispatch(struct management *man)
 #endif /* ifdef MANAGEMENT_PF */
 #ifdef MANAGMENT_EXTERNAL_KEY
         case IEC_RSA_SIGN:
+        case IEC_ECDSA_SIGN:
             man->connection.ext_key_state = EKS_READY;
             buffer_list_free(man->connection.ext_key_input);
             man->connection.ext_key_input = man->connection.in_extra;
@@ -1119,6 +1122,22 @@ man_rsa_sig(struct management *man)
 }
 
 static void
+man_ecdsa_sig(struct management *man)
+{
+    struct man_connection *mc = &man->connection;
+    if (mc->ext_key_state == EKS_SOLICIT)
+    {
+        mc->ext_key_state = EKS_INPUT;
+        mc->in_extra_cmd = IEC_ECDSA_SIGN;
+        in_extra_reset(mc, IER_NEW);
+    }
+    else
+    {
+        msg(M_CLIENT, "ERROR: The ecdsa-sig command is not currently 
available");
+    }
+}
+
+static void
 man_certificate(struct management *man)
 {
     struct man_connection *mc = &man->connection;
@@ -1516,6 +1535,10 @@ man_dispatch_command(struct management *man, struct 
status_output *so, const cha
     {
         man_rsa_sig(man);
     }
+    else if (streq(p[0], "ecdsa-sig"))
+    {
+        man_ecdsa_sig(man);
+    }
     else if (streq(p[0], "certificate"))
     {
         man_certificate(man);
@@ -3655,6 +3678,13 @@ management_query_rsa_sig(struct management *man,
                                               &man->connection.ext_key_state, 
&man->connection.ext_key_input);
 }
 
+char *
+management_query_ecdsa_sig(struct management *man,
+                           const char *b64_data)
+{
+    return management_query_multiline_flatten(man, b64_data, "ECDSA_SIGN", 
"ecdsa-sign",
+                                              &man->connection.ext_key_state, 
&man->connection.ext_key_input);
+}
 
 char *
 management_query_cert(struct management *man, const char *cert_name)
diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h
index 676be64..0b1ae5b 100644
--- a/src/openvpn/manage.h
+++ b/src/openvpn/manage.h
@@ -281,6 +281,7 @@ struct man_connection {
 #define IEC_CLIENT_PF   2
 #define IEC_RSA_SIGN    3
 #define IEC_CERTIFICATE 4
+#define IEC_ECDSA_SIGN  5
     int in_extra_cmd;
     struct buffer_list *in_extra;
 #ifdef MANAGEMENT_DEF_AUTH
@@ -441,6 +442,8 @@ void management_learn_addr(struct management *management,
 
 char *management_query_rsa_sig(struct management *man, const char *b64_data);
 
+char *management_query_ecdsa_sig(struct management *man, const char *b64_data);
+
 char *management_query_cert(struct management *man, const char *cert_name);
 
 #endif
diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c
index c29dbcf..9379784 100644
--- a/src/openvpn/ssl_openssl.c
+++ b/src/openvpn/ssl_openssl.c
@@ -1132,6 +1132,160 @@ err:
     return 0;
 }
 
+#if OPENSSL_VERSION_NUMBER >= 0x10100001L && !defined(OPENSSL_NO_EC)
+
+/* called when EC_KEY is destroyed */
+static void
+openvpn_extkey_ec_finish(EC_KEY *ec)
+{
+    /* release the method structure */
+    const EC_KEY_METHOD *ec_meth = EC_KEY_get_method(ec);
+    EC_KEY_METHOD_free((EC_KEY_METHOD *) ec_meth);
+}
+
+/* EC_KEY_METHOD callback: sign().
+ * Sign the hash using EC key and return DER encoded signature in sig,
+ * its length in siglen. Return value is 1 on success, 0 on error.
+ */
+static int
+ecdsa_sign(int type, const unsigned char *dgst, int dgstlen, unsigned char 
*sig,
+           unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY 
*ec)
+{
+    char *in_b64 = NULL;
+    char *out_b64 = NULL;
+    int ret = 0;
+    int len;
+
+    /* convert 'from' to base64 */
+    if (openvpn_base64_encode(dgst, dgstlen, &in_b64) <= 0)
+    {
+        goto done;
+    }
+
+    /* call MI for signature */
+    if (management)
+    {
+        out_b64 = management_query_ecdsa_sig(management, in_b64);
+    }
+    if (!out_b64)
+    {
+        goto done;
+    }
+
+    /* decode base64 signature to binary */
+    len = ECDSA_size(ec);
+    *siglen = openvpn_base64_decode(out_b64, sig, len);
+    if (*siglen > 0)
+    {
+        ret = 1;
+    }
+
+done:
+    if (in_b64)
+    {
+        free(in_b64);
+    }
+    if (out_b64)
+    {
+        free(out_b64);
+    }
+    return ret;
+}
+
+/* EC_KEY_METHOD callback: sign_setup(). We do no precomputations */
+static int
+ecdsa_sign_setup(EC_KEY *ec, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp)
+{
+    return 1;
+}
+
+/* EC_KEY_METHOD callback: sign_sig().
+ * Sign the hash and return the result as a newly allocated ECDS_SIG
+ * struct or NULL on error.
+ */
+static ECDSA_SIG *
+ecdsa_sign_sig(const unsigned char *dgst, int dgstlen, const BIGNUM *in_kinv,
+               const BIGNUM *in_r, EC_KEY *ec)
+{
+    ECDSA_SIG *ecsig = NULL;
+    int len = ECDSA_size(ec);
+    struct gc_arena gc = gc_new();
+
+    unsigned char *buf = gc_malloc(len, false, &gc);
+    if (1 != ecdsa_sign(0, dgst, dgstlen, buf, &len, NULL, NULL, ec))
+    {
+        goto out;
+    }
+    /* const char ** should be avoided: not up to us, so we cast our way 
through */
+    ecsig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&buf, len);
+
+out:
+    gc_free(&gc);
+    return ecsig;
+}
+
+static int
+tls_ctx_use_external_ec_key(struct tls_root_ctx *ctx, EVP_PKEY *pkey)
+{
+    EC_KEY *ec = NULL;
+    EVP_PKEY *privkey = NULL;
+    EC_KEY_METHOD *ec_method;
+
+    ASSERT(ctx);
+
+    ec_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
+    if (!ec_method)
+    {
+        goto err;
+    }
+
+    /* Among init methods, we only need the finish method */
+    EC_KEY_METHOD_set_init(ec_method, NULL, openvpn_extkey_ec_finish, NULL, 
NULL, NULL, NULL);
+    EC_KEY_METHOD_set_sign(ec_method, ecdsa_sign, ecdsa_sign_setup, 
ecdsa_sign_sig);
+
+    ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(pkey));
+    if (!ec)
+    {
+        EC_KEY_METHOD_free(ec_method);
+        goto err;
+    }
+    if (!EC_KEY_set_method(ec, ec_method))
+    {
+        EC_KEY_METHOD_free(ec_method);
+        goto err;
+    }
+    /* from this point ec_method will get freed when ec is freed */
+
+    privkey = EVP_PKEY_new();
+    if (!EVP_PKEY_assign_EC_KEY(privkey, ec))
+    {
+        goto err;
+    }
+    /* from this point ec will get freed when privkey is freed */
+
+    if (!SSL_CTX_use_PrivateKey(ctx->ctx, privkey))
+    {
+        ec = NULL; /* avoid double freeing it below */
+        goto err;
+    }
+
+    EVP_PKEY_free(privkey); /* this will down ref privkey and ec */
+    return 1;
+
+err:
+    /* Reach here only when ec and privkey can be independenly freed */
+    if (privkey)
+    {
+        EVP_PKEY_free(privkey);
+    }
+    if(ec)
+    {
+        EC_KEY_free(ec);
+    }
+    return 0;
+}
+#endif // OPENSSL_VERSION_NUMBER > 1.1.0 dev
+
 int
 tls_ctx_use_external_private_key(struct tls_root_ctx *ctx,
                                  const char *cert_file, const char 
*cert_file_inline)
@@ -1156,11 +1310,26 @@ tls_ctx_use_external_private_key(struct tls_root_ctx 
*ctx,
             goto err;
         }
     }
+#if OPENSSL_VERSION_NUMBER >= 0x10100001L && !defined(OPENSSL_NO_EC)
+    else if (EVP_PKEY_get0_EC_KEY(pkey))
+    {
+        if (!tls_ctx_use_external_ec_key(ctx, pkey))
+        {
+            goto err;
+        }
+    }
+    else
+    {
+        crypto_msg(M_WARN, "management-external-key requires an RSA or EC 
certificate");
+        goto err;
+    }
+#else
     else
     {
         crypto_msg(M_WARN, "management-external-key requires a RSA 
certificate");
         goto err;
     }
+#endif
     return 1;
 
 err:
-- 
2.1.4


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to