]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: add signature malleability and pubkey validity checks to master 686/head 687/head anongit/master
authordjm@openbsd.org <djm@openbsd.org>
Thu, 4 Jun 2026 04:26:51 +0000 (04:26 +0000)
committerDamien Miller <djm@mindrot.org>
Thu, 4 Jun 2026 04:40:51 +0000 (14:40 +1000)
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
ed25519.sh

index 2452dff0f62db75c74ed6ff26632c27343f5973f..8f683a2c0f675d8a2b30ce95443a1471525c0bf8 100644 (file)
--- 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);
index 9e6cbc9c312da95131b66d740b3433b4dd5b93b9..d24cdc0fcef434cb9c24d4945be88bc3c64a1ee2 100644 (file)
@@ -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