ssh/knownhosts: respect @revoked CA keys

Fixes CVE-2026-42508
Fixes golang/go#79568

Change-Id: I20f33cba20756b048726ff3464b83871859d3b5c
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/781220
Reviewed-by: Nicholas Husin <husin@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Nicholas Husin <nsh@golang.org>
Reviewed-by: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
diff --git a/ssh/knownhosts/knownhosts.go b/ssh/knownhosts/knownhosts.go
index 1ebd7e6..e57cf5b 100644
--- a/ssh/knownhosts/knownhosts.go
+++ b/ssh/knownhosts/knownhosts.go
@@ -160,8 +160,13 @@
 
 // IsRevoked can be used as a callback in ssh.CertChecker
 func (db *hostKeyDB) IsRevoked(key *ssh.Certificate) bool {
-	_, ok := db.revoked[string(key.Marshal())]
-	return ok
+	if _, ok := db.revoked[string(key.Marshal())]; ok {
+		return true
+	}
+	if _, ok := db.revoked[string(key.SignatureKey.Marshal())]; ok {
+		return true
+	}
+	return false
 }
 
 const markerCert = "@cert-authority"
diff --git a/ssh/knownhosts/knownhosts_test.go b/ssh/knownhosts/knownhosts_test.go
index 552a556..4d3ca59 100644
--- a/ssh/knownhosts/knownhosts_test.go
+++ b/ssh/knownhosts/knownhosts_test.go
@@ -6,6 +6,8 @@
 
 import (
 	"bytes"
+	"crypto/ed25519"
+	"crypto/rand"
 	"fmt"
 	"net"
 	"reflect"
@@ -272,7 +274,45 @@
 	}
 }
 
-// TODO(hanwen): test coverage for certificates.
+func TestRevokedCA(t *testing.T) {
+	_, caPriv, err := ed25519.GenerateKey(rand.Reader)
+	if err != nil {
+		t.Fatal(err)
+	}
+	caSigner, err := ssh.NewSignerFromKey(caPriv)
+	if err != nil {
+		t.Fatal(err)
+	}
+	caKey := caSigner.PublicKey()
+
+	_, hostPriv, err := ed25519.GenerateKey(rand.Reader)
+	if err != nil {
+		t.Fatal(err)
+	}
+	hostKey, err := ssh.NewPublicKey(hostPriv.Public())
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	cert := &ssh.Certificate{
+		CertType:        ssh.HostCert,
+		Key:             hostKey,
+		ValidBefore:     ssh.CertTimeInfinity,
+		ValidPrincipals: []string{"server.org"},
+	}
+	if err := cert.SignCert(rand.Reader, caSigner); err != nil {
+		t.Fatal(err)
+	}
+
+	caLine := "ssh-ed25519 " + serialize(caKey)[len("ssh-ed25519 "):]
+	knownHostsData := "@revoked server.org " + caLine + "\n" +
+		"@cert-authority server.org " + caLine + "\n"
+	db := testDB(t, knownHostsData)
+
+	if !db.IsRevoked(cert) {
+		t.Error("IsRevoked returned false for certificate signed by revoked CA")
+	}
+}
 
 const testHostname = "hostname"