@@ -39,4 +39,40 @@ OSSL_provider_init_fn xkey_provider_init;
#define XKEY_PROV_PROPS "provider=ovpn.xkey"
+/**
+ * Stuct to encapsulate signature algorithm parameters to pass
+ * to sign operation.
+ */
+typedef struct {
+ const char *padmode; /* "pkcs1", "pss" or "none" */
+ const char *mdname; /* "SHA256" or "SHA2-256" etc. */
+ const char *saltlen; /* "digest", "auto" or "max" */
+ const char *keytype; /* "EC" or "RSA" */
+} XKEY_SIGALG;
+
+/**
+ * Callback for sign operation -- must be implemented for each backend and
+ * is used in xkey_signature_sign(), or set when loading the key.
+ * (custom key loading not yet implemented).
+ *
+ * @param handle opaque key handle provided by the backend -- could be null
+ * or unused for management interface.
+ * @param sig On return caller should fill this with the signature
+ * @param siglen On entry *siglen has max size of sig and on return must be
+ * set to the actual size of the signature
+ * @param tbs buffer to sign
+ * @param tbslen size of data in tbs buffer
+ * @sigalg contains the signature algorithm parameters
+ *
+ * @returns 1 on success, 0 on error.
+ *
+ * The data in tbs is just the digest with no DigestInfo header added. 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.
+ */
+typedef int (XKEY_EXTERNAL_SIGN_fn)(void *handle, unsigned char *sig, size_t *siglen,
+ const unsigned char *tbs, size_t tbslen,
+ XKEY_SIGALG sigalg);
+
#endif /* XKEY_PUBLIC_H_ */
@@ -87,6 +87,14 @@ typedef struct
#define KEYTYPE(key) ((key)->pubkey ? EVP_PKEY_get_id((key)->pubkey) : 0)
#define KEYSIZE(key) ((key)->pubkey ? EVP_PKEY_get_size((key)->pubkey) : 0)
+/**
+ * Helper sign function for native keys -- this is not
+ * an external key, but we use the same function signature
+ * for consistency. Implemented using OpenSSL calls.
+ */
+XKEY_EXTERNAL_SIGN_fn xkey_native_sign;
+
+
/* keymgmt provider */
/* keymgmt callbacks we implement */
@@ -403,6 +411,482 @@ const OSSL_ALGORITHM keymgmts[] = {
{NULL, NULL, NULL}
};
+
+/* signature provider */
+
+/* signature provider callbacks we provide */
+static OSSL_FUNC_signature_newctx_fn signature_newctx;
+static OSSL_FUNC_signature_freectx_fn signature_freectx;
+static OSSL_FUNC_signature_sign_init_fn signature_sign_init;
+static OSSL_FUNC_signature_sign_fn signature_sign;
+static OSSL_FUNC_signature_digest_verify_init_fn signature_digest_verify_init;
+static OSSL_FUNC_signature_digest_verify_fn signature_digest_verify;
+static OSSL_FUNC_signature_digest_sign_init_fn signature_digest_sign_init;
+static OSSL_FUNC_signature_digest_sign_fn signature_digest_sign;
+static OSSL_FUNC_signature_set_ctx_params_fn signature_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn signature_settable_ctx_params;
+static OSSL_FUNC_signature_get_ctx_params_fn signature_get_ctx_params;
+static OSSL_FUNC_signature_gettable_ctx_params_fn signature_gettable_ctx_params;
+
+typedef struct
+{
+ XKEY_PROVIDER_CTX *prov;
+ XKEY_KEYDATA *keydata;
+ EVP_MD_CTX *mdctx; /* Used for digest-verify ops */
+ EVP_PKEY_CTX *ectx; /* Used for digest-verify ops */
+ XKEY_SIGALG sigalg;
+} XKEY_SIGNATURE_CTX;
+
+static const XKEY_SIGALG default_sigalg = { .mdname="MD5-SHA1", .saltlen="digest",
+ .padmode="pkcs1", .keytype = "RSA"};
+
+const struct {
+ int id;
+ const char *name;
+} digest_names[] = {{NID_md5_sha1, "MD5-SHA1"}, {NID_sha1, "SHA1"},
+ {NID_sha224, "SHA224",}, {NID_sha256, "SHA256"}, {NID_sha384, "SHA384"},
+ {NID_sha224, "SHA2-224"}, {NID_sha256, "SHA2-256"}, {NID_sha384, "SHA2-384"},
+ {NID_sha512, "SHA512"}, {NID_sha512, "SHA2-512"}, {NID_md5_sha1, ""},
+ {0, NULL}};
+
+/* return a string literal for digest name */
+static const char *
+xkey_mdname(const char *s)
+{
+ int i = 0;
+ while(digest_names[i].name && strcasecmp(digest_names[i].name, s))
+ {
+ i++;
+ }
+ return (digest_names[i].id != 0) ? OBJ_nid2sn(digest_names[i].id) : "MD5-SHA1";
+}
+
+static void *
+signature_newctx(void *provctx, const char *props)
+{
+ dmsg(D_LOW, "In xkey signature_newctx");
+
+ XKEY_SIGNATURE_CTX *sctx = calloc(sizeof(*sctx), 1);
+ if (!sctx)
+ {
+ msg(M_NONFATAL, "xkey_signature_newctx: out of memory");
+ return NULL;
+ }
+
+ sctx->prov = provctx;
+ sctx->sigalg = default_sigalg;
+
+ return sctx;
+}
+
+static void
+signature_freectx(void *ctx)
+{
+ dmsg(D_LOW, "In xkey signature_freectx");
+
+ XKEY_SIGNATURE_CTX *sctx = ctx;
+
+ if (sctx->mdctx)
+ {
+ EVP_MD_CTX_free(sctx->mdctx);
+ /* sctx->ectx is owned by mdctx, do not free */
+ }
+
+ free(sctx);
+}
+
+static const OSSL_PARAM *
+signature_settable_ctx_params(void *ctx, void *provctx)
+{
+ dmsg(D_LOW, "In xkey signature_settable_ctx_params");
+
+ static OSSL_PARAM settable[] = {
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, NULL, 0),
+ OSSL_PARAM_END
+ };
+
+ return settable;
+}
+
+static int
+signature_set_ctx_params(void *ctx, const OSSL_PARAM params[])
+{
+ dmsg(D_LOW, "In signature_set_ctx_params");
+
+ XKEY_SIGNATURE_CTX *sctx = ctx;
+ const OSSL_PARAM *p;
+
+ if (sctx->ectx)
+ {
+ EVP_PKEY_CTX_set_params(sctx->ectx, params);
+ }
+
+ if (params == NULL)
+ {
+ return 1; /* not an error */
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_PAD_MODE);
+ if (p && p->data_type == OSSL_PARAM_UTF8_STRING)
+ {
+ if (!strcmp(p->data, "pss"))
+ {
+ sctx->sigalg.padmode = "pss";
+ }
+ else if (!strcmp(p->data, "pkcs1"))
+ {
+ sctx->sigalg.padmode = "pkcs1";
+ }
+ else if (!strcmp(p->data, "none"))
+ {
+ sctx->sigalg.padmode = "none";
+ }
+ else
+ {
+ msg(D_LOW, "xkey signature_ctx: padmode <%s>, treating as <pkcs1>",
+ (char *)p->data);
+ sctx->sigalg.padmode = "none";
+ }
+ dmsg(D_LOW, "xkey_sign_parameters: setting padmode to %s", sctx->sigalg.padmode);
+ }
+ else if (p && p->data_type == OSSL_PARAM_INTEGER)
+ {
+ int padmode;
+ if (OSSL_PARAM_get_int(p, &padmode))
+ {
+ if (padmode == RSA_PKCS1_PSS_PADDING)
+ {
+ sctx->sigalg.padmode = "pss";
+ }
+ else if (padmode == RSA_PKCS1_PADDING)
+ {
+ sctx->sigalg.padmode = "pkcs1";
+ }
+ else
+ {
+ sctx->sigalg.padmode = "none";
+ }
+ }
+ dmsg(D_LOW, "xkey_sign_parameters: setting padmode to <%s>", sctx->sigalg.padmode);
+ }
+ else if (p)
+ {
+ msg(M_WARN, "xkey_signature_params: unknown padmode ignored");
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST);
+ if (p && p->data_type == OSSL_PARAM_UTF8_STRING)
+ {
+ sctx->sigalg.mdname = xkey_mdname(p->data);
+ msg(D_LOW, "xkey_sign_parameters: setting hashalg to %s", sctx->sigalg.mdname);
+ }
+ else if (p)
+ {
+ msg(M_WARN, "xkey_signature_params: unknown digest type ignored");
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_PSS_SALTLEN);
+ if (p && p->data_type == OSSL_PARAM_UTF8_STRING)
+ {
+ if (!strcmp((char *)p->data, "digest"))
+ {
+ sctx->sigalg.saltlen = "digest";
+ }
+ else if (!strcmp(p->data, "max"))
+ {
+ sctx->sigalg.saltlen = "max";
+ }
+ else if (!strcmp(p->data, "auto"))
+ {
+ sctx->sigalg.saltlen = "auto";
+ }
+ else
+ {
+ msg(M_WARN, "xkey_signature_params: unknown saltlen <%s>",
+ (char *)p->data);
+ sctx->sigalg.saltlen = "digest"; /* most common ? */
+ }
+ msg(D_LOW, "xkey_sign_parameters: setting saltlen to %s", sctx->sigalg.saltlen);
+ }
+ else if (p)
+ {
+ msg(M_WARN, "xkey_signature_params: unknown saltlen ignored");
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM *
+signature_gettable_ctx_params(void *ctx, void *provctx)
+{
+ dmsg(D_LOW,"In xkey signature_gettable_ctx_params");
+
+ static OSSL_PARAM gettable[] = {
+ OSSL_PARAM_END
+ };
+
+ return gettable;
+}
+
+static int
+signature_get_ctx_params(void *ctx, OSSL_PARAM params[])
+{
+ dmsg(D_LOW, "In signature_get_ctx_params -- not implemented!!");
+ return 0;
+}
+
+static int
+signature_sign_init(void *ctx, void *provkey, const OSSL_PARAM params[])
+{
+ dmsg(D_LOW, "In xkey sign_init");
+
+ XKEY_SIGNATURE_CTX *sctx = ctx;
+
+ sctx->keydata = provkey;
+ sctx->keydata->refcount++;
+ sctx->sigalg.keytype = KEYTYPE(sctx->keydata) == EVP_PKEY_RSA ? "RSA" : "EC";
+
+ signature_set_ctx_params(sctx, params);
+
+ return 1;
+}
+
+static int
+signature_sign(void *ctx, unsigned char *sig, size_t *siglen, size_t sigsize,
+ const unsigned char *tbs, size_t tbslen)
+{
+ dmsg(D_LOW, "In xkey signature_sign with siglen = %zu\n", *siglen);
+
+ XKEY_SIGNATURE_CTX *sctx = ctx;
+ ASSERT(sctx);
+ ASSERT(sctx->keydata);
+
+ if (!sig)
+ {
+ *siglen = KEYSIZE(sctx->keydata);
+ return 1;
+ }
+
+ if (sctx->keydata->origin == OPENSSL_NATIVE)
+ {
+ return xkey_native_sign(sctx->keydata->handle, sig, siglen, tbs, tbslen, sctx->sigalg);
+ }
+ else
+ {
+ /* external key handling not yet implemented */
+ return 0;
+ }
+}
+
+/* Digest verify ops are simply delegated to the default provider using pubkey */
+static int
+signature_digest_init_helper(void *ctx, const char *mdname, void *provkey)
+{
+ dmsg(D_LOW, "In xkey digest_init_helper with mdname = <%s>", mdname);
+
+ XKEY_SIGNATURE_CTX *sctx = ctx;
+
+ ASSERT(sctx);
+ ASSERT(provkey);
+
+ if (!sctx->mdctx) {
+ sctx->mdctx = EVP_MD_CTX_new();
+ }
+ if (!sctx->mdctx) {
+ msg(M_WARN, "xkey_signature_digest_init: EVP_MD_CTX_new failed");
+ return 0;
+ }
+
+ EVP_MD_CTX_init(sctx->mdctx);
+ sctx->keydata = provkey; /* used by digest_sign */
+ sctx->keydata->refcount++;
+
+ return 1;
+}
+
+static int
+signature_digest_verify_init(void *ctx, const char *mdname, void *provkey,
+ const OSSL_PARAM params[])
+{
+ dmsg(D_LOW, "In xkey digest_verify init with mdname <%s>", mdname);
+
+ XKEY_SIGNATURE_CTX *sctx = ctx;
+ ASSERT(sctx);
+ ASSERT(sctx->prov);
+
+
+ int ret = signature_digest_init_helper(ctx, mdname, provkey);
+ if (ret)
+ {
+ EVP_PKEY *pubkey = ((XKEY_KEYDATA*)provkey)->pubkey;
+ ret = EVP_DigestVerifyInit_ex(sctx->mdctx, &sctx->ectx, mdname,
+ sctx->prov->libctx, NULL, pubkey, params);
+ }
+ return ret;
+}
+
+static int
+signature_digest_verify(void *ctx, const unsigned char *sig, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ dmsg(D_LOW, "In xkey digest_verify");
+
+ XKEY_SIGNATURE_CTX *sctx = ctx;
+
+ if (!sctx || !sctx->mdctx)
+ {
+ return 0;
+ }
+ return EVP_DigestVerify(sctx->mdctx, sig, siglen, tbs, tbslen);
+}
+
+static int
+signature_digest_sign_init(void *ctx, const char *mdname,
+ void *provkey, const OSSL_PARAM params[])
+{
+ dmsg(D_LOW, "In xkey digest_sign_init with mdname = %s>", mdname);
+
+ XKEY_SIGNATURE_CTX *sctx = ctx;
+
+ ASSERT(sctx);
+ ASSERT(provkey);
+ ASSERT(sctx->prov);
+
+ sctx->keydata = provkey; /* used by digest_sign */
+ sctx->keydata->refcount++;
+ sctx->sigalg.keytype = KEYTYPE(sctx->keydata) == EVP_PKEY_RSA ? "RSA" : "EC";
+
+ signature_set_ctx_params(ctx, params);
+ if (mdname)
+ {
+ sctx->sigalg.mdname = xkey_mdname(mdname); /* get a string literal pointer */
+ }
+ else
+ {
+ msg(M_WARN, "xkey digest_sign_init: mdname is NULL.");
+ }
+ return 1;
+}
+
+static int
+signature_digest_sign(void *ctx, unsigned char *sig, size_t *siglen,
+ size_t sigsize, const unsigned char *tbs, size_t tbslen)
+{
+ dmsg(D_LOW, "In xkey digest_sign");
+
+ XKEY_SIGNATURE_CTX *sctx = ctx;
+
+ ASSERT(sctx);
+ ASSERT(sctx->keydata);
+
+ if (!sig) /* set siglen and return */
+ {
+ *siglen = KEYSIZE(sctx->keydata);
+ return 1;
+ }
+
+ /* create digest and pass on to signature_sign() */
+
+ const char *mdname = sctx->sigalg.mdname;
+ EVP_MD *md = EVP_MD_fetch(sctx->prov->libctx, mdname, NULL);
+ if (!md)
+ {
+ msg(M_WARN, "WARN: xkey digest_sign_init: MD_fetch failed for <%s>", mdname);
+ return 0;
+ }
+
+ /* construct digest using OpenSSL */
+ unsigned char buf[EVP_MAX_MD_SIZE];
+ unsigned int sz;
+ if (EVP_Digest(tbs, tbslen, buf, &sz, md, NULL) != 1)
+ {
+ msg(M_WARN, "WARN: xkey digest_sign: EVP_Digest failed");
+ return 0;
+ }
+
+ return signature_sign(ctx, sig, siglen, sigsize, buf, sz);
+}
+
+/* Sign digest using native sign function -- will only work for native keys
+ */
+int
+xkey_native_sign(void *handle, unsigned char *sig, size_t *siglen, const unsigned char *tbs,
+ size_t tbslen, XKEY_SIGALG sigalg)
+{
+ dmsg(D_LOW, "In xkey_native_sign");
+
+ EVP_PKEY *pkey = handle;
+ int ret = 0;
+
+ ASSERT(sig);
+ ASSERT(pkey);
+
+ const char *saltlen = sigalg.saltlen;
+ const char *mdname = sigalg.mdname;
+ const char *padmode = sigalg.padmode;
+
+ dmsg(D_LOW, "In xkey_native_sign with digest <%s> padmode = <%s> saltlen=<%s>", mdname, padmode, saltlen);
+
+ int i = 0;
+ OSSL_PARAM params[6];
+ params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, (char *)mdname, 0);
+ if (EVP_PKEY_get_id(pkey) == EVP_PKEY_RSA)
+ {
+ params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, (char *)padmode, 0);
+ if (!strcmp(sigalg.padmode, "pss"))
+ {
+ params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, (char *) saltlen, 0);
+ /* same digest for mgf1 */
+ params[i++] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, (char *) mdname, 0);
+ }
+ }
+ params[i++] = OSSL_PARAM_construct_end();
+
+ EVP_PKEY_CTX *ectx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL);
+
+ if (!ectx)
+ {
+ msg(M_WARN, "WARN: xkey test_sign: call to EVP_PKEY_CTX_new...failed");
+ return 0;
+ }
+
+ /* params must be set in a separate call after the sign_init */
+ if (EVP_PKEY_sign_init_ex(ectx, NULL) != 1)
+ {
+ msg(M_WARN, "WARN: xkey test_sign: call to EVP_PKEY_sign_init failed");
+ return 0;
+ }
+ EVP_PKEY_CTX_set_params(ectx, params);
+
+ ret = EVP_PKEY_sign(ectx, sig, siglen, tbs, tbslen);
+ EVP_PKEY_CTX_free(ectx);
+
+ return ret;
+}
+
+static const OSSL_DISPATCH signature_functions[] = {
+ {OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))signature_newctx},
+ {OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))signature_freectx},
+ {OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))signature_sign_init},
+ {OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))signature_sign},
+ {OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, (void (*)(void))signature_digest_verify_init},
+ {OSSL_FUNC_SIGNATURE_DIGEST_VERIFY, (void (*)(void))signature_digest_verify},
+ {OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, (void (*)(void))signature_digest_sign_init},
+ {OSSL_FUNC_SIGNATURE_DIGEST_SIGN, (void (*)(void))signature_digest_sign},
+ {OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))signature_set_ctx_params},
+ {OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, (void (*)(void))signature_settable_ctx_params},
+ {OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))signature_get_ctx_params},
+ {OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, (void (*)(void))signature_gettable_ctx_params},
+ {0, NULL }
+};
+
+const OSSL_ALGORITHM signatures[] = {
+ {"RSA:rsaEncryption", props, signature_functions},
+ {"ECDSA", props, signature_functions},
+ {NULL, NULL, NULL}
+};
+
/* main provider interface */
/* provider callbacks we implement */
@@ -421,7 +905,7 @@ query_operation(void *provctx, int op, int *no_store)
switch (op)
{
case OSSL_OP_SIGNATURE:
- return NULL;
+ return signatures;
case OSSL_OP_KEYMGMT:
return keymgmts;