Enable signing via provider for management-external-key

- Add a function to set as sign_op during key import. The
  function passes the signature request to management interface,
  and returns the result to the provider.

v2 changes: Method to do digest added to match the changes in
            the provider signature callback.
TODO:
 - Allow passing the undigested message to management interface
 - Add pkcs1 DigestInfo header when required

Signed-off-by: Selva Nair <selva.nair@gmail.com>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Message-Id: <20211214165928.30676-8-selva.nair@gmail.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg23428.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
This commit is contained in:
Selva Nair 2021-12-14 11:59:17 -05:00 committed by Gert Doering
parent 57abdcfc38
commit 199df03bf5
3 changed files with 113 additions and 6 deletions

View file

@ -1169,7 +1169,7 @@ end:
}
#ifdef ENABLE_MANAGEMENT
#if defined(ENABLE_MANAGEMENT) && !defined(HAVE_XKEY_PROVIDER)
/* encrypt */
static int
@ -1470,7 +1470,9 @@ err:
return 0;
}
#endif /* OPENSSL_VERSION_NUMBER > 1.1.0 dev && !defined(OPENSSL_NO_EC) */
#endif /* ENABLE_MANAGEMENT && !HAVE_XKEY_PROVIDER */
#ifdef ENABLE_MANAGEMENT
int
tls_ctx_use_management_external_key(struct tls_root_ctx *ctx)
{

View file

@ -67,10 +67,13 @@ typedef struct {
*
* @returns 1 on success, 0 on error.
*
* The data in tbs is just the digest with no DigestInfo header added. This is
* If sigalg.op = "Sign", the data in tbs is the digest. If sigalg.op = "DigestSign"
* it is the message that the backend should hash wih appropriate hash algorithm before
* signing. In the former case no DigestInfo header is added to tbs. This is
* unlike the deprecated RSA_sign callback which provides encoded digest.
* For RSA_PKCS1 signatures, the external signing function must encode the digest
* before signing. The digest algorithm used is passed in the sigalg structure.
* before signing. The digest algorithm used (or to be used) is passed in the sigalg
* structure.
*/
typedef int (XKEY_EXTERNAL_SIGN_fn)(void *handle, unsigned char *sig, size_t *siglen,
const unsigned char *tbs, size_t tbslen,

View file

@ -32,6 +32,8 @@
#include "error.h"
#include "buffer.h"
#include "xkey_common.h"
#include "manage.h"
#include "base64.h"
#ifdef HAVE_XKEY_PROVIDER
@ -48,6 +50,31 @@ static const char *const props = XKEY_PROV_PROPS;
XKEY_EXTERNAL_SIGN_fn xkey_management_sign;
/** helper to compute digest */
static int
xkey_digest(const unsigned char *src, size_t srclen, unsigned char *buf,
size_t *buflen, const char *mdname)
{
dmsg(D_LOW, "In xkey_digest");
EVP_MD *md = EVP_MD_fetch(NULL, mdname, NULL); /* from default context */
if (!md)
{
msg(M_WARN, "WARN: xkey_digest: MD_fetch failed for <%s>", mdname);
return 0;
}
unsigned int len = (unsigned int) *buflen;
if (EVP_Digest(src, srclen, buf, &len, md, NULL) != 1)
{
msg(M_WARN, "WARN: xkey_digest: EVP_Digest failed");
return 0;
}
EVP_MD_free(md);
*buflen = len;
return 1;
}
/**
* Load external key for signing via management interface.
* The public key must be passed in by the caller as we may not
@ -94,13 +121,88 @@ xkey_load_management_key(OSSL_LIB_CTX *libctx, EVP_PKEY *pubkey)
return pkey;
}
/* not yet implemented */
/**
* Signature callback for xkey_provider with management-external-key
*
* @param handle Unused -- may be null
* @param sig On successful return signature is in sig.
* @param siglen On entry *siglen has length of buffer sig,
* on successful return size of signature
* @param tbs hash or message to be signed
* @param tbslen len of data in dgst
* @param sigalg extra signature parameters
*
* @return signature length or -1 on error.
*/
int
xkey_management_sign(void *unused, unsigned char *sig, size_t *siglen,
const unsigned char *tbs, size_t tbslen, XKEY_SIGALG alg)
{
msg(M_FATAL, "FATAL ERROR: A sign callback for this key is not implemented.");
return 0;
(void) unused;
char alg_str[128];
unsigned char buf[EVP_MAX_MD_SIZE]; /* for computing digest if required */
size_t buflen = sizeof(buf);
if (!strcmp(alg.op, "DigestSign"))
{
dmsg(D_LOW, "xkey_management_sign: computing digest");
if (xkey_digest(tbs, tbslen, buf, &buflen, alg.mdname))
{
tbs = buf;
tbslen = buflen;
alg.op = "Sign";
}
else
{
return 0;
}
}
if (!strcmp(alg.keytype, "EC"))
{
strncpynt(alg_str, "ECDSA", sizeof(alg_str));
}
/* else assume RSA key */
else if (!strcmp(alg.padmode, "pkcs1"))
{
strncpynt(alg_str, "RSA_PKCS1_PADDING", sizeof(alg_str));
}
else if (!strcmp(alg.padmode, "none"))
{
strncpynt(alg_str, "RSA_NO_PADDING", sizeof(alg_str));
}
else if (!strcmp(alg.padmode, "pss"))
{
openvpn_snprintf(alg_str, sizeof(alg_str), "%s,hashalg=%s,saltlen=%s",
"RSA_PKCS1_PSS_PADDING", alg.mdname,alg.saltlen);
}
else {
msg(M_NONFATAL, "Unsupported RSA padding mode in signature request<%s>",
alg.padmode);
return 0;
}
dmsg(D_LOW, "xkey management_sign: requesting sig with algorithm <%s>", alg_str);
char *in_b64 = NULL;
char *out_b64 = NULL;
int len = -1;
int bencret = openvpn_base64_encode(tbs, (int) tbslen, &in_b64);
if (management && bencret > 0)
{
out_b64 = management_query_pk_sig(management, in_b64, alg_str);
}
if (out_b64)
{
len = openvpn_base64_decode(out_b64, sig, (int) *siglen);
}
free(in_b64);
free(out_b64);
*siglen = (len > 0) ? len : 0;
return (*siglen > 0);
}
#endif /* HAVE_XKEY_PROVIDER */