[openssh-commits] [openssh] 02/03: upstream: Support ed25519 keys hosted on PKCS#11 tokens.
git+noreply at mindrot.org
git+noreply at mindrot.org
Sat Jul 26 11:58:24 AEST 2025
This is an automated email from the git hooks/post-receive script.
djm pushed a commit to branch master
in repository openssh.
commit 361ff0ca308ac02449e71689fc5ea72114db43db
Author: djm at openbsd.org <djm at openbsd.org>
AuthorDate: Sat Jul 26 01:51:44 2025 +0000
upstream: Support ed25519 keys hosted on PKCS#11 tokens.
Tested on Yubikeys and against SoftHSM2.
feedback/ok tb@
OpenBSD-Commit-ID: 90ddb6529f2e12e98e8bba21d8592e60579ce2e4
---
ssh-pkcs11.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 214 insertions(+), 3 deletions(-)
diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c
index e22b3e419..34e430875 100644
--- a/ssh-pkcs11.c
+++ b/ssh-pkcs11.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-pkcs11.c,v 1.66 2025/07/24 06:59:51 djm Exp $ */
+/* $OpenBSD: ssh-pkcs11.c,v 1.67 2025/07/26 01:51:44 djm Exp $ */
/*
* Copyright (c) 2010 Markus Friedl. All rights reserved.
* Copyright (c) 2014 Pedro Martelletto. All rights reserved.
@@ -51,6 +51,7 @@
#include "ssh-pkcs11.h"
#include "digest.h"
#include "xmalloc.h"
+#include "crypto_api.h"
struct pkcs11_slotinfo {
CK_TOKEN_INFO token;
@@ -709,6 +710,69 @@ pkcs11_sign_ecdsa(struct sshkey *key,
#endif /* OPENSSL_HAS_ECC */
#endif /* WITH_OPENSSL */
+static int
+pkcs11_sign_ed25519(struct sshkey *key,
+ u_char **sigp, size_t *lenp,
+ const u_char *data, size_t datalen,
+ const char *alg, const char *sk_provider,
+ const char *sk_pin, u_int compat)
+{
+ struct pkcs11_key *k11;
+ struct pkcs11_slotinfo *si;
+ CK_FUNCTION_LIST *f;
+ CK_ULONG slen = 0;
+ CK_RV rv;
+ u_char *sig = NULL;
+ CK_BYTE *xdata = NULL;
+ int ret = -1;
+
+ if (sigp != NULL)
+ *sigp = 0;
+ if (lenp != NULL)
+ *lenp = 0;
+
+ if ((k11 = pkcs11_lookup_key(key)) == NULL) {
+ error_f("no key found");
+ return SSH_ERR_KEY_NOT_FOUND;
+ }
+
+ if (pkcs11_get_key(k11, CKM_EDDSA) == -1) {
+ error("pkcs11_get_key failed");
+ return SSH_ERR_AGENT_FAILURE;
+ }
+
+ debug3_f("sign using provider %s slotidx %lu",
+ k11->provider->name, (u_long)k11->slotidx);
+
+ f = k11->provider->function_list;
+ si = &k11->provider->slotinfo[k11->slotidx];
+
+ xdata = xmalloc(datalen);
+ memcpy(xdata, data, datalen);
+ sig = xmalloc(crypto_sign_ed25519_BYTES);
+ slen = crypto_sign_ed25519_BYTES;
+
+ rv = f->C_Sign(si->session, xdata, datalen, sig, &slen);
+ if (rv != CKR_OK) {
+ error("C_Sign failed: %lu", rv);
+ goto done;
+ }
+ if (slen != crypto_sign_ed25519_BYTES) {
+ error_f("bad signature length: %lu", (u_long)slen);
+ goto done;
+ }
+ if ((ret = ssh_ed25519_encode_store_sig(sig, slen, sigp, lenp)) != 0)
+ fatal_fr(ret, "couldn't store signature");
+
+ /* success */
+ ret = 0;
+ done:
+ if (xdata != NULL)
+ freezero(xdata, datalen);
+ free(sig);
+ return ret;
+}
+
/* remove trailing spaces */
static char *
rmspace(u_char *buf, size_t len)
@@ -1022,6 +1086,115 @@ fail:
return key;
}
+static struct sshkey *
+pkcs11_fetch_ed25519_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
+ CK_OBJECT_HANDLE *obj)
+{
+ CK_ATTRIBUTE key_attr[3];
+ CK_SESSION_HANDLE session;
+ CK_FUNCTION_LIST *f = NULL;
+ CK_RV rv;
+ struct sshkey *key = NULL;
+ const unsigned char *d = NULL;
+ size_t len;
+ char *hex = NULL;
+ int success = -1, i;
+ /* https://docs.oasis-open.org/pkcs11/pkcs11-curr/v3.0/os/pkcs11-curr-v3.0-os.html#_Toc30061180 */
+ const u_char id1[14] = {
+ 0x13, 0x0c, 0x65, 0x64, 0x77, 0x61, 0x72, 0x64,
+ 0x73, 0x32, 0x35, 0x35, 0x31, 0x39,
+ }; /* PrintableString { "edwards25519" } */
+ const u_char id2[5] = {
+ 0x06, 0x03, 0x2b, 0x65, 0x70,
+ }; /* OBJECT_IDENTIFIER { 1.3.101.112 } */
+
+ memset(&key_attr, 0, sizeof(key_attr));
+ key_attr[0].type = CKA_ID;
+ key_attr[1].type = CKA_EC_POINT; /* XXX or CKA_VALUE ? */
+ key_attr[2].type = CKA_EC_PARAMS;
+
+ session = p->slotinfo[slotidx].session;
+ f = p->function_list;
+
+ /* figure out size of the attributes */
+ rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
+ if (rv != CKR_OK) {
+ error("C_GetAttributeValue failed: %lu", rv);
+ return (NULL);
+ }
+
+ /*
+ * Allow CKA_ID (always first attribute) to be empty, but
+ * ensure that none of the others are zero length.
+ * XXX assumes CKA_ID is always first.
+ */
+ if (key_attr[1].ulValueLen == 0 ||
+ key_attr[2].ulValueLen == 0) {
+ error("invalid attribute length");
+ return (NULL);
+ }
+
+ /* allocate buffers for attributes */
+ for (i = 0; i < 3; i++) {
+ if (key_attr[i].ulValueLen > 0)
+ key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
+ }
+
+ /* retrieve ID, public point and curve parameters of EC key */
+ rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
+ if (rv != CKR_OK) {
+ error("C_GetAttributeValue failed: %lu", rv);
+ goto fail;
+ }
+
+ /* Expect one of the supported identifiers in CKA_EC_PARAMS */
+ d = (u_char *)key_attr[2].pValue;
+ len = key_attr[2].ulValueLen;
+ if ((len != sizeof(id1) || memcmp(d, id1, sizeof(id1)) != 0) &&
+ (len != sizeof(id2) || memcmp(d, id2, sizeof(id2)) != 0)) {
+ hex = tohex(d, len);
+ logit_f("unsupported CKA_EC_PARAMS: %s (len %zu)", hex, len);
+ goto fail;
+ }
+
+ /*
+ * Expect either a raw 32 byte pubkey or an OCTET STRING with
+ * a 32 byte pubkey in CKA_VALUE
+ */
+ d = (u_char *)key_attr[1].pValue;
+ len = key_attr[1].ulValueLen;
+ if (len == ED25519_PK_SZ + 2 && d[0] == 0x04 && d[1] == ED25519_PK_SZ) {
+ d += 2;
+ len -= 2;
+ }
+ if (len != ED25519_PK_SZ) {
+ hex = tohex(key_attr[1].pValue, key_attr[1].ulValueLen);
+ logit_f("CKA_EC_POINT invalid octet str: %s (len %lu)",
+ hex, (u_long)key_attr[1].ulValueLen);
+ goto fail;
+ }
+
+ if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
+ fatal_f("sshkey_new failed");
+ key->ed25519_pk = xmalloc(ED25519_PK_SZ);
+ memcpy(key->ed25519_pk, d, ED25519_PK_SZ);
+ key->type = KEY_ED25519;
+ key->flags |= SSHKEY_FLAG_EXT;
+ if (pkcs11_record_key(p, slotidx, &key_attr[0], key))
+ goto fail;
+ /* success */
+ success = 0;
+ fail:
+ if (success != 0) {
+ sshkey_free(key);
+ key = NULL;
+ }
+ free(hex);
+ for (i = 0; i < 3; i++)
+ free(key_attr[i].pValue);
+ return key;
+}
+
static int
pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
CK_OBJECT_HANDLE *obj, struct sshkey **keyp, char **labelp)
@@ -1039,6 +1212,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
int i, success = -1;
const u_char *cp;
char *subject = NULL;
+ size_t len;
#ifdef OPENSSL_HAS_ECC
int r, nid;
#endif
@@ -1176,6 +1350,26 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
/* success */
success = 0;
#endif /* OPENSSL_HAS_ECC */
+ } else if (EVP_PKEY_base_id(evp) == EVP_PKEY_ED25519) {
+ if ((key = sshkey_new(KEY_UNSPEC)) == NULL ||
+ (key->ed25519_pk = calloc(1, ED25519_PK_SZ)) == NULL)
+ fatal_f("allocation failed");
+ len = ED25519_PK_SZ;
+ if (!EVP_PKEY_get_raw_public_key(evp, key->ed25519_pk, &len)) {
+ ossl_error("EVP_PKEY_get_raw_public_key failed");
+ goto out;
+ }
+ if (len != ED25519_PK_SZ) {
+ error_f("incorrect returned public key "
+ "length for ed25519");
+ goto out;
+ }
+ key->type = KEY_ED25519;
+ key->flags |= SSHKEY_FLAG_EXT;
+ if (pkcs11_record_key(p, slotidx, &cert_attr[0], key))
+ goto out;
+ /* success */
+ success = 0;
} else {
error("unknown certificate key type");
goto out;
@@ -1406,6 +1600,9 @@ pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
key = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
break;
#endif /* OPENSSL_HAS_ECC */
+ case CKK_EC_EDWARDS:
+ key = pkcs11_fetch_ed25519_pubkey(p, slotidx, &obj);
+ break;
default:
/* XXX print key type? */
key = NULL;
@@ -1879,6 +2076,10 @@ pkcs11_sign(struct sshkey *key,
return pkcs11_sign_ecdsa(key, sigp, lenp, data, datalen,
alg, sk_provider, sk_pin, compat);
#endif /* OPENSSL_HAS_ECC */
+ case KEY_ED25519:
+ case KEY_ED25519_CERT:
+ return pkcs11_sign_ed25519(key, sigp, lenp, data, datalen,
+ alg, sk_provider, sk_pin, compat);
default:
return SSH_ERR_KEY_TYPE_UNKNOWN;
}
@@ -2033,10 +2234,20 @@ pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx,
*err = rv;
key_type = -1;
}
- if (key_type == CKK_RSA)
+ switch (key_type) {
+ case CKK_RSA:
k = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
- else if (key_type == CKK_ECDSA)
+ break;
+ case CKK_ECDSA:
k = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
+ break;
+ case CKK_EC_EDWARDS:
+ k = pkcs11_fetch_ed25519_pubkey(p, slotidx, &obj);
+ break;
+ default:
+ debug_f("unsupported key type %lu", (u_long)key_type);
+ continue;
+ }
if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
debug_f("could not destroy public key 0x%hhx", keyid);
--
To stop receiving notification emails like this one, please contact
djm at mindrot.org.
More information about the openssh-commits
mailing list