]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: support ed25519 signatures via libcrypto. Mostly by Jeremy
authordjm@openbsd.org <djm@openbsd.org>
Thu, 30 Oct 2025 20:49:10 +0000 (20:49 +0000)
committerDamien Miller <djm@mindrot.org>
Thu, 30 Oct 2025 21:04:35 +0000 (08:04 +1100)
Allison Feedback tb@, ok tb@ markus@

OpenBSD-Commit-ID: e8edf8adffd5975d05769dde897df882d7933526

.depend
Makefile.in
ed25519-openssl.c [new file with mode: 0644]
ed25519.c

diff --git a/.depend b/.depend
index 660f515caf54384b69180f550ffec912c1beef9b..cb8c28eac558f25b67f09b79f3061acc1781f5db 100644 (file)
--- a/.depend
+++ b/.depend
@@ -51,6 +51,7 @@ digest-libc.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-c
 digest-openssl.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h
 dispatch.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ssh2.h log.h ssherr.h dispatch.h packet.h openbsd-compat/sys-queue.h
 dns.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h sshkey.h ssherr.h dns.h log.h digest.h
+ed25519-openssl.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h
 ed25519.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h crypto_api.h
 entropy.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h
 fatal.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h log.h ssherr.h
index 66f15968928cf8cd3497c2aa7f7ed9412d395b38..35a6fb15e4bfcc8b72693492f329b0095d577be6 100644 (file)
@@ -102,7 +102,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
        smult_curve25519_ref.o \
        poly1305.o chacha.o cipher-chachapoly.o cipher-chachapoly-libcrypto.o \
        ssh-ed25519.o digest-openssl.o digest-libc.o \
-       hmac.o ed25519.o hash.o \
+       hmac.o ed25519.o ed25519-openssl.o hash.o \
        kex.o kex-names.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
        kexgexc.o kexgexs.o \
        kexsntrup761x25519.o kexmlkem768x25519.o sntrup761.o kexgen.o \
diff --git a/ed25519-openssl.c b/ed25519-openssl.c
new file mode 100644 (file)
index 0000000..5d1e343
--- /dev/null
@@ -0,0 +1,207 @@
+/* $OpenBSD: ed25519-openssl.c,v 1.1 2025/10/30 20:49:10 djm Exp $ */
+/*
+ * Copyright (c) 2025 OpenSSH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * OpenSSL-based implementation of Ed25519 crypto_sign API
+ * Alternative to the internal SUPERCOP-based implementation in ed25519.c
+ */
+
+#include "includes.h"
+
+#ifdef OPENSSL_HAS_ED25519
+
+#include <sys/types.h>
+
+#include <string.h>
+#include <stdint.h>
+#include <limits.h>
+
+#include <openssl/evp.h>
+
+#include "crypto_api.h"
+#include "log.h"
+
+#if crypto_sign_ed25519_SECRETKEYBYTES <= crypto_sign_ed25519_PUBLICKEYBYTES
+#error "crypto_sign_ed25519_SECRETKEYBYTES < crypto_sign_ed25519_PUBLICKEYBYTES"
+#endif
+
+#define SSH_ED25519_RAW_SECRET_KEY_LEN \
+    (crypto_sign_ed25519_SECRETKEYBYTES - crypto_sign_ed25519_PUBLICKEYBYTES)
+
+int
+crypto_sign_ed25519_keypair(unsigned char *pk, unsigned char *sk)
+{
+       EVP_PKEY_CTX *ctx = NULL;
+       EVP_PKEY *pkey = NULL;
+       size_t pklen, sklen;
+       int ret = -1;
+
+       if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL)) == NULL) {
+               debug3_f("EVP_PKEY_CTX_new_id failed");
+               goto out;
+       }
+       if (EVP_PKEY_keygen_init(ctx) <= 0) {
+               debug3_f("EVP_PKEY_keygen_init failed");
+               goto out;
+       }
+       if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
+               debug3_f("EVP_PKEY_keygen failed");
+               goto out;
+       }
+
+       /* Extract public key */
+       pklen = crypto_sign_ed25519_PUBLICKEYBYTES;
+       if (!EVP_PKEY_get_raw_public_key(pkey, pk, &pklen)) {
+               debug3_f("EVP_PKEY_get_raw_public_key failed");
+               goto out;
+       }
+       if (pklen != crypto_sign_ed25519_PUBLICKEYBYTES) {
+               debug3_f("public key length mismatch: %zu", pklen);
+               goto out;
+       }
+
+       sklen = SSH_ED25519_RAW_SECRET_KEY_LEN;
+       /* Extract private key (32 bytes seed) */
+       if (!EVP_PKEY_get_raw_private_key(pkey, sk, &sklen)) {
+               debug3_f("EVP_PKEY_get_raw_private_key failed");
+               goto out;
+       }
+       if (sklen != SSH_ED25519_RAW_SECRET_KEY_LEN) {
+               debug3_f("private key length mismatch: %zu", sklen);
+               goto out;
+       }
+
+       /* Append public key to secret key (SUPERCOP format compatibility) */
+       memcpy(sk + sklen, pk, crypto_sign_ed25519_PUBLICKEYBYTES);
+
+       ret = 0;
+out:
+       EVP_PKEY_free(pkey);
+       EVP_PKEY_CTX_free(ctx);
+       return ret;
+}
+
+int
+crypto_sign_ed25519(unsigned char *sm, unsigned long long *smlen,
+    const unsigned char *m, unsigned long long mlen,
+    const unsigned char *sk)
+{
+       EVP_PKEY *pkey = NULL;
+       EVP_MD_CTX *mdctx = NULL;
+       size_t siglen;
+       int ret = -1;
+
+       /* Create EVP_PKEY from secret key (first 32 bytes are the seed) */
+       if ((pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL,
+           sk, SSH_ED25519_RAW_SECRET_KEY_LEN)) == NULL) {
+               debug3_f("EVP_PKEY_new_raw_private_key failed");
+               goto out;
+       }
+
+       /* Sign the message */
+       if ((mdctx = EVP_MD_CTX_new()) == NULL) {
+               debug3_f("EVP_MD_CTX_new failed");
+               goto out;
+       }
+       if (EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey) != 1) {
+               debug3_f("EVP_DigestSignInit failed");
+               goto out;
+       }
+       siglen = crypto_sign_ed25519_BYTES;
+       if (EVP_DigestSign(mdctx, sm, &siglen, m, mlen) != 1) {
+               debug3_f("EVP_DigestSign failed");
+               goto out;
+       }
+       if (siglen != crypto_sign_ed25519_BYTES) {
+               debug3_f("signature length mismatch: %zu", siglen);
+               goto out;
+       }
+
+       /* Append message after signature (SUPERCOP format) */
+       if (mlen > ULLONG_MAX - siglen) {
+               debug3_f("message length overflow: siglen=%zu mlen=%llu",
+                   siglen, mlen);
+               goto out;
+       }
+       memmove(sm + siglen, m, mlen);
+       *smlen = siglen + mlen;
+
+       ret = 0;
+out:
+       EVP_MD_CTX_free(mdctx);
+       EVP_PKEY_free(pkey);
+       return ret;
+}
+
+int
+crypto_sign_ed25519_open(unsigned char *m, unsigned long long *mlen,
+    const unsigned char *sm, unsigned long long smlen,
+    const unsigned char *pk)
+{
+       EVP_PKEY *pkey = NULL;
+       EVP_MD_CTX *mdctx = NULL;
+       int ret = -1;
+       const unsigned char *msg;
+       size_t msglen;
+
+       if (smlen < crypto_sign_ed25519_BYTES) {
+               debug3_f("signed message bad length: %llu", smlen);
+               return -1;
+       }
+       /* Signature is first crypto_sign_ed25519_BYTES, message follows */
+       msg = sm + crypto_sign_ed25519_BYTES;
+       msglen = smlen - crypto_sign_ed25519_BYTES;
+
+       /* Make sure the message buffer is big enough. */
+       if (*mlen < msglen) {
+               debug_f("message bad length: %llu", *mlen);
+               return -1;
+       }
+
+       /* Create EVP_PKEY from public key */
+       if ((pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL,
+           pk, crypto_sign_ed25519_PUBLICKEYBYTES)) == NULL) {
+               debug3_f("EVP_PKEY_new_raw_public_key failed");
+               goto out;
+       }
+
+       if ((mdctx = EVP_MD_CTX_new()) == NULL) {
+               debug3_f("EVP_MD_CTX_new failed");
+               goto out;
+       }
+       if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) <= 0) {
+               debug3_f("EVP_DigestVerifyInit failed");
+               goto out;
+       }
+       if (EVP_DigestVerify(mdctx, sm, crypto_sign_ed25519_BYTES,
+           msg, msglen) != 1) {
+               debug3_f("EVP_DigestVerify failed");
+               goto out;
+       }
+
+       /* Copy message out */
+       *mlen = msglen;
+       memmove(m, msg, msglen);
+
+       ret = 0;
+out:
+       EVP_MD_CTX_free(mdctx);
+       EVP_PKEY_free(pkey);
+       return ret;
+}
+
+#endif /* OPENSSL_HAS_ED25519 */
index 0e167ae1f6bcf7e2c1753cb8c968d78468bc072e..2452dff0f62db75c74ed6ff26632c27343f5973f 100644 (file)
--- a/ed25519.c
+++ b/ed25519.c
@@ -11,6 +11,8 @@
 
 #include "includes.h"
 
+#ifndef OPENSSL_HAS_ED25519
+
 #include <string.h>
 
 #include "crypto_api.h"
@@ -2028,3 +2030,5 @@ badsig:
   memset(m,0,smlen);
   return -1;
 }
+
+#endif /* OPENSSL_HAS_ED25519 */