From: djm@openbsd.org Date: Thu, 4 Jun 2026 04:26:51 +0000 (+0000) Subject: upstream: add signature malleability and pubkey validity checks to X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=refs%2Fpull%2F686%2Fhead;p=thirdparty%2Fopenssh-portable.git 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 --- 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 ' 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