@@ -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, "pk-sig : Enter a signature in response to >PK_SIGN challenge");
+ msg(M_CLIENT, " Enter signature in 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_PK_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;
@@ -1103,18 +1106,21 @@ man_client_pf(struct management *man, const char *cid_str)
#ifdef MANAGMENT_EXTERNAL_KEY
static void
-man_rsa_sig(struct management *man)
+man_external_sig(struct management *man, int cmd)
{
+ ASSERT(cmd == IEC_RSA_SIGN || cmd == IEC_PK_SIGN);
struct man_connection *mc = &man->connection;
+ const char *cmd_name = (cmd == IEC_RSA_SIGN) ? "rsa-sig" : "pk-sig";
if (mc->ext_key_state == EKS_SOLICIT)
{
mc->ext_key_state = EKS_INPUT;
- mc->in_extra_cmd = IEC_RSA_SIGN;
+ mc->in_extra_cmd = cmd;
in_extra_reset(mc, IER_NEW);
}
else
{
- msg(M_CLIENT, "ERROR: The rsa-sig command is not currently available");
+ msg(M_CLIENT, "ERROR: The %s command is not currently available",
+ cmd_name);
}
}
@@ -1514,7 +1520,11 @@ man_dispatch_command(struct management *man, struct status_output *so, const cha
#ifdef MANAGMENT_EXTERNAL_KEY
else if (streq(p[0], "rsa-sig"))
{
- man_rsa_sig(man);
+ man_external_sig(man, IEC_RSA_SIGN);
+ }
+ else if (streq(p[0], "pk-sig"))
+ {
+ man_external_sig(man, IEC_PK_SIGN);
}
else if (streq(p[0], "certificate"))
{
@@ -3655,6 +3665,13 @@ management_query_rsa_sig(struct management *man,
&man->connection.ext_key_state, &man->connection.ext_key_input);
}
+char *
+management_query_pk_sig(struct management *man,
+ const char *b64_data)
+{
+ return management_query_multiline_flatten(man, b64_data, "PK_SIGN", "pk-sign",
+ &man->connection.ext_key_state, &man->connection.ext_key_input);
+}
char *
management_query_cert(struct management *man, const char *cert_name)
@@ -281,6 +281,7 @@ struct man_connection {
#define IEC_CLIENT_PF 2
#define IEC_RSA_SIGN 3
#define IEC_CERTIFICATE 4
+#define IEC_PK_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_pk_sig(struct management *man, const char *b64_data);
+
char *management_query_cert(struct management *man, const char *cert_name);
#endif
@@ -1009,58 +1009,58 @@ openvpn_extkey_rsa_finish(RSA *rsa)
return 1;
}
-/* sign arbitrary data */
+/* Pass the input hash in 'dgst' to management and get the signature back.
+ * On input siglen contains the capacity of the buffer 'sig'.
+ * On return signature is in sig.
+ * Return value is signature length or -1 on error.
+ */
static int
-rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
+get_sig_from_man(int type, const unsigned char *dgst, unsigned int dgstlen,
+ unsigned char *sig, unsigned int siglen)
{
- /* optional app data in rsa->meth->app_data; */
char *in_b64 = NULL;
char *out_b64 = NULL;
- int ret = -1;
- int len;
+ int len = -1;
- if (padding != RSA_PKCS1_PADDING)
+ /* convert 'dgst' to base64 */
+ if (management
+ && openvpn_base64_encode(dgst, dgstlen, &in_b64) > 0)
{
- RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
- goto done;
+ if (type == EVP_PKEY_RSA)
+ {
+ out_b64 = management_query_rsa_sig(management, in_b64);
+ }
+ else
+ {
+ out_b64 = management_query_pk_sig(management, in_b64);
+ }
}
-
- /* convert 'from' to base64 */
- if (openvpn_base64_encode(from, flen, &in_b64) <= 0)
+ if (out_b64)
{
- goto done;
+ len = openvpn_base64_decode(out_b64, sig, siglen);
}
- /* call MI for signature */
- if (management)
- {
- out_b64 = management_query_rsa_sig(management, in_b64);
- }
- if (!out_b64)
- {
- goto done;
- }
+ free(in_b64);
+ free(out_b64);
+ return len;
+}
- /* decode base64 signature to binary */
- len = RSA_size(rsa);
- ret = openvpn_base64_decode(out_b64, to, len);
+/* sign arbitrary data */
+static int
+rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
+{
+ unsigned int len = RSA_size(rsa);
+ int ret = -1;
- /* verify length */
- if (ret != len)
+ if (padding != RSA_PKCS1_PADDING)
{
- ret = -1;
+ RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
+ return -1;
}
-done:
- if (in_b64)
- {
- free(in_b64);
- }
- if (out_b64)
- {
- free(out_b64);
- }
- return ret;
+ ret = get_sig_from_man(EVP_PKEY_RSA, from, flen, to, len);
+
+ return (ret == len)? ret : -1;
}
static int
@@ -1132,6 +1132,130 @@ 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)
+{
+ int capacity = ECDSA_size(ec);
+ int len = get_sig_from_man(EVP_PKEY_EC, dgst, dgstlen, sig, capacity);
+
+ if (len > 0)
+ {
+ *siglen = len;
+ return 1;
+ }
+ return 0;
+}
+
+/* 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 ECDSA_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;
+ unsigned int len = ECDSA_size(ec);
+ struct gc_arena gc = gc_new();
+
+ unsigned char *buf = gc_malloc(len, false, &gc);
+ if (ecdsa_sign(0, dgst, dgstlen, buf, &len, NULL, NULL, ec) != 1)
+ {
+ 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 +1280,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 a RSA certificate");
+ 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 an RSA certificate");
+ goto err;
+ }
+#endif
return 1;
err: