[openssh-commits] [openssh] 02/02: upstream: add signature malleability and pubkey validity checks to
git+noreply at mindrot.org
git+noreply at mindrot.org
Thu Jun 4 14:41:15 AEST 2026
This is an automated email from the git hooks/post-receive script.
djm pushed a commit to branch master
in repository openssh.
commit 7763a38bc2595be50baf1d72b19bf4720e41e8e3
Author: djm at openbsd.org <djm at openbsd.org>
AuthorDate: Thu Jun 4 04:26:51 2026 +0000
upstream: add signature malleability and pubkey validity checks to
ed25519 verification (SSH doesn't depend on these properties) Pointed out by
Soatok Dreamseeker
Add an explicit-seed variant of the keygen function.
feedback / "looks fine" tb@
OpenBSD-Commit-ID: 2a71926bfda24628cf34a88357f44a790e338d5d
---
ed25519.c | 36 +++++++++++++++++++++++++++---
ed25519.sh | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 102 insertions(+), 8 deletions(-)
diff --git a/ed25519.c b/ed25519.c
index 2452dff0f..8f683a2c0 100644
--- a/ed25519.c
+++ b/ed25519.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ed25519.c,v 1.4 2023/01/15 23:05:32 djm Exp $ */
+/* $OpenBSD: ed25519.c,v 1.5 2026/06/04 04:26:51 djm Exp $ */
/*
* Public Domain, Authors:
@@ -1912,13 +1912,13 @@ static void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s)
}
/* from supercop-20221122/crypto_sign/ed25519/ref/keypair.c */
-int crypto_sign_ed25519_keypair(unsigned char *pk,unsigned char *sk)
+int crypto_sign_ed25519_keypair_from_seed(unsigned char *pk,unsigned char *sk, const unsigned char *seed)
{
unsigned char az[64];
sc25519 scsk;
ge25519 gepk;
- randombytes(sk,32);
+ memcpy(sk, seed, 32);
crypto_hash_sha512(az,sk,32);
az[0] &= 248;
az[31] &= 127;
@@ -1931,6 +1931,18 @@ int crypto_sign_ed25519_keypair(unsigned char *pk,unsigned char *sk)
memmove(sk + 32,pk,32);
return 0;
}
+
+int
+crypto_sign_ed25519_keypair(unsigned char *pk, unsigned char *sk)
+{
+ unsigned char seed[32];
+ int r;
+
+ randombytes(seed, 32);
+ r = crypto_sign_ed25519_keypair_from_seed(pk, sk, seed);
+ explicit_bzero(seed, sizeof(seed));
+ return r;
+}
/* from supercop-20221122/crypto_sign/ed25519/ref/sign.c */
int crypto_sign_ed25519(
@@ -1987,6 +1999,22 @@ int crypto_sign_ed25519(
}
/* from supercop-20221122/crypto_sign/ed25519/ref/open.c */
+/*
+ * Local OpenSSH addition: check that S < group order L
+ * Where L = 2^{252} + 27742317777372353535851937790883648493
+ * This can be variable time as the signature is public.
+ */
+static inline int sc25519_inrange(const unsigned char *pk)
+{
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ if (pk[31 - i] > sc25519_m[31 - i]) return -1;
+ if (pk[31 - i] < sc25519_m[31 - i]) return 0;
+ }
+ return -1;
+}
+
int crypto_sign_ed25519_open(
unsigned char *m,unsigned long long *mlen,
const unsigned char *sm,unsigned long long smlen,
@@ -2002,7 +2030,9 @@ int crypto_sign_ed25519_open(
if (smlen < 64) goto badsig;
if (sm[63] & 224) goto badsig;
+ if (sc25519_inrange(sm+32)) goto badsig;
if (ge25519_unpackneg_vartime(&get1,pk)) goto badsig;
+ if (ge25519_isneutral_vartime(&get1)) goto badsig;
memmove(pkcopy,pk,32);
memmove(rcopy,sm,32);
diff --git a/ed25519.sh b/ed25519.sh
index 9e6cbc9c3..d24cdc0fc 100644
--- a/ed25519.sh
+++ b/ed25519.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-# $OpenBSD: ed25519.sh,v 1.2 2024/05/17 02:39:11 jsg Exp $
+# $OpenBSD: ed25519.sh,v 1.3 2026/06/04 04:26:51 djm Exp $
# Placed in the Public Domain.
#
AUTHOR="supercop-20221122/crypto_sign/ed25519/ref/implementors"
@@ -16,11 +16,12 @@ FILES="
supercop-20221122/crypto_sign/ed25519/ref/open.c
"
###
+PORTABLE=${PORTABLE:-0}
DATA="supercop-20221122/crypto_sign/ed25519/ref/ge25519_base.data"
set -e
-cd $1
+test -z "$1" || cd $1
echo -n '/* $'
echo 'OpenBSD: $ */'
echo
@@ -29,6 +30,12 @@ echo ' * Public Domain, Authors:'
sed -e '/Alphabetical order:/d' -e 's/^/ * - /' < $AUTHOR
echo ' */'
echo
+if [ "$PORTABLE" -ne 0 ]; then
+ echo '#include "includes.h"'
+ echo
+ echo '#ifndef OPENSSL_HAS_ED25519'
+ echo
+fi
echo '#include <string.h>'
echo
echo '#include "crypto_api.h"'
@@ -42,6 +49,32 @@ done
echo
for i in $FILES; do
echo "/* from $i */"
+
+ case "$i" in
+ */crypto_sign/ed25519/ref/open.c)
+ # Include our malleability fix at the start if open.c
+ cat << _EOF
+
+/*
+ * Local OpenSSH addition: check that S < group order L
+ * Where L = 2^{252} + 27742317777372353535851937790883648493
+ * This can be variable time as the signature is public.
+ */
+static inline int sc25519_inrange(const unsigned char *pk)
+{
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ if (pk[31 - i] > sc25519_m[31 - i]) return -1;
+ if (pk[31 - i] < sc25519_m[31 - i]) return 0;
+ }
+ return -1;
+}
+_EOF
+ ;;
+ esac
+
+ nl=`echo`
# Changes to all files:
# - inline ge25519_base.data where it is included
# - expand CRYPTO_NAMESPACE() namespacing define
@@ -66,12 +99,17 @@ for i in $FILES; do
sed -e "s/crypto_sign/crypto_sign_ed25519/g"
;;
*/crypto_sign/ed25519/ref/keypair.c)
- # rename key generation function to the name OpenSSH expects
- sed -e "s/crypto_sign_keypair/crypto_sign_ed25519_keypair/g"
+ # provide an explicit-seed key generation function and rename
+ # it to the name OpenSSH expects
+ sed -e "s/crypto_sign_keypair(unsigned char \*pk,unsigned char \*sk)/crypto_sign_ed25519_keypair_from_seed(unsigned char *pk,unsigned char *sk, const unsigned char *seed)/g" \
+ -e "s/randombytes(sk,32);/memcpy(sk, seed, 32);/g"
;;
*/crypto_sign/ed25519/ref/open.c)
# rename verification function to the name OpenSSH expects
- sed -e "s/crypto_sign_open/crypto_sign_ed25519_open/g"
+ # Insert malleability checks
+ sed -e "s/crypto_sign_open/crypto_sign_ed25519_open/g" | \
+ perl -0777 -pe 's/(.*if.*ge25519_unpackneg_vartime.*get1,pk.*)/ if (sc25519_inrange(sm+32)) goto badsig;\n\1\n if (ge25519_isneutral_vartime(&get1)) goto badsig;/'
+ #perl -0777 -pe 's/^(.*ge25519_unpackneg_vartime.*,pk.*)$/ if (sc25519_inrange(sm+32)) goto badsig;\n$1\n if (ge25519_isneutral_vartime(&get1)) goto badsig;/'
;;
*/crypto_sign/ed25519/ref/fe25519.*)
# avoid a couple of name collisions with other files
@@ -116,4 +154,30 @@ for i in $FILES; do
;;
esac | \
sed -e 's/[ ]*$//'
+
+ # Include implicit-seed keygen function used for ssh-ed25519
+ case "$i" in
+ */crypto_sign/ed25519/ref/keypair.c)
+ cat << _EOF
+
+int
+crypto_sign_ed25519_keypair(unsigned char *pk, unsigned char *sk)
+{
+ unsigned char seed[32];
+ int r;
+
+ randombytes(seed, 32);
+ r = crypto_sign_ed25519_keypair_from_seed(pk, sk, seed);
+ explicit_bzero(seed, sizeof(seed));
+ return r;
+}
+_EOF
+ ;;
+ esac
+
done
+
+if [ "$PORTABLE" -ne 0 ]; then
+ echo
+ echo '#endif /* OPENSSL_HAS_ED25519 */'
+fi
--
To stop receiving notification emails like this one, please contact
djm at mindrot.org.
More information about the openssh-commits
mailing list