[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