From 72265392537495e79f4db6292e865bc4e6af95a8 Mon Sep 17 00:00:00 2001 From: Teodor Calin Date: Sat, 25 Jul 2026 10:36:43 +0300 Subject: [PATCH] security: crypto.Verify rejects wrong-length public keys (panic-DoS fix) ed25519.Verify panics on a public key that is not exactly 32 bytes. crypto.Verify is reached from unauthenticated message paths (e.g. the handshake decodes a peer-supplied public_key and calls it), so a wrong-length key was a remote, unauthenticated crash. Guard the length and return false instead of panicking. Co-Authored-By: Claude Opus 4.8 --- crypto/identity.go | 8 +++++++- crypto/zz_verify_badkey_test.go | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 crypto/zz_verify_badkey_test.go diff --git a/crypto/identity.go b/crypto/identity.go index 6e969f2..c9bc6c5 100644 --- a/crypto/identity.go +++ b/crypto/identity.go @@ -34,8 +34,14 @@ func (id *Identity) Sign(message []byte) []byte { return ed25519.Sign(id.PrivateKey, message) } -// Verify checks a signature against the public key. +// Verify checks a signature against the public key. A public key that is not +// exactly ed25519.PublicKeySize bytes is rejected rather than passed to +// ed25519.Verify, which panics on a wrong-length key — an attacker-supplied +// key reaches this from unauthenticated message paths. func Verify(publicKey ed25519.PublicKey, message, signature []byte) bool { + if len(publicKey) != ed25519.PublicKeySize { + return false + } return ed25519.Verify(publicKey, message, signature) } diff --git a/crypto/zz_verify_badkey_test.go b/crypto/zz_verify_badkey_test.go new file mode 100644 index 0000000..34ff58a --- /dev/null +++ b/crypto/zz_verify_badkey_test.go @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +package crypto + +import ( + "crypto/ed25519" + "testing" +) + +func TestVerifyRejectsWrongLengthKeyWithoutPanic(t *testing.T) { + id, err := GenerateIdentity() + if err != nil { + t.Fatal(err) + } + msg := []byte("challenge") + sig := id.Sign(msg) + + for _, n := range []int{0, 1, 5, 31, 33, 64} { + bad := make([]byte, n) + if Verify(bad, msg, sig) { + t.Fatalf("Verify accepted a %d-byte public key", n) + } + } + + if Verify(nil, msg, sig) { + t.Fatal("Verify accepted a nil public key") + } + + if !Verify(id.PublicKey, msg, sig) { + t.Fatal("Verify rejected a valid signature") + } + if len(id.PublicKey) != ed25519.PublicKeySize { + t.Fatalf("unexpected key size %d", len(id.PublicKey)) + } +}