[crypto] ssh: reject certificate signature keys before recursing

3 views
Skip to first unread message

Nicola Murino (Gerrit)

unread,
Jul 19, 2026, 3:08:12 PM (2 days ago) Jul 19
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Nicola Murino has uploaded the change for review

Commit message

ssh: reject certificate signature keys before recursing

parseCert parsed a certificate's SignatureKey by calling ParsePublicKey
again, and only afterwards rejected a certificate used as a signing key.
For a chain of nested certificates this recursed once per level, so a key
with enough nesting exhausted the goroutine stack and crashed with a fatal
stack overflow that recover cannot catch.

Reject the signature key by its declared algorithm before parsing it, which
bounds parsing.

Fixes golang/go#80414
Change-Id: Id51401aae15bfbec006c03ab97635a3b68bd6058

Change diff

diff --git a/ssh/certs.go b/ssh/certs.go
index 6f75d77..fa848f5 100644
--- a/ssh/certs.go
+++ b/ssh/certs.go
@@ -229,15 +229,20 @@
return nil, err
}
c.Reserved = g.Reserved
+ // Reject a certificate whose signature key is itself a certificate before
+ // parsing it. Certificates signed by certificates are not supported (see
+ // PROTOCOL.certkeys), and rejecting after ParsePublicKey returns would allow
+ // a chain of nested certificates to recurse once per level, exhausting the
+ // goroutine stack.
+ if sigAlgo, _, ok := parseString(g.SignatureKey); !ok {
+ return nil, errShortRead
+ } else if _, ok := certKeyAlgoNames[string(sigAlgo)]; ok {
+ return nil, fmt.Errorf("ssh: the signature key type %q is invalid for certificates", sigAlgo)
+ }
k, err := ParsePublicKey(g.SignatureKey)
if err != nil {
return nil, err
}
- // The Type() function is intended to return only certificate key types, but
- // we use certKeyAlgoNames anyway for safety, to match [Certificate.Type].
- if _, ok := certKeyAlgoNames[k.Type()]; ok {
- return nil, fmt.Errorf("ssh: the signature key type %q is invalid for certificates", k.Type())
- }
c.SignatureKey = k
c.Signature, rest, ok = parseSignatureBody(g.Signature)
if !ok || len(rest) > 0 {
diff --git a/ssh/certs_test.go b/ssh/certs_test.go
index 43280e3..8358b33 100644
--- a/ssh/certs_test.go
+++ b/ssh/certs_test.go
@@ -15,6 +15,7 @@
"math/big"
"net"
"reflect"
+ "strings"
"testing"
"time"

@@ -45,6 +46,34 @@
}
}

+func TestParseCertNestedSignatureKey(t *testing.T) {
+ signer, err := NewSignerFromKey(testPrivateKeys["ed25519"])
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cert := &Certificate{
+ Key: signer.PublicKey(),
+ CertType: UserCert,
+ ValidBefore: CertTimeInfinity,
+ }
+ if err := cert.SignCert(rand.Reader, signer); err != nil {
+ t.Fatal(err)
+ }
+
+ inner := *cert
+ cert.SignatureKey = &inner
+ blob := cert.Marshal()
+
+ _, err = ParsePublicKey(blob)
+ if err == nil {
+ t.Fatal("ParsePublicKey: expected error for certificate signed by a certificate, got nil")
+ }
+ if !strings.Contains(err.Error(), "invalid for certificates") {
+ t.Errorf("ParsePublicKey: got error %q, want it to mention the signature key is invalid for certificates", err)
+ }
+}
+
// Cert generated by ssh-keygen OpenSSH_6.8p1 OS X 10.10.3
// % ssh-keygen -s ca -I testcert -O source-address=192.168.1.0/24 -O force-command=/bin/sleep user.pub
// user.pub key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDACh1rt2DXfV3hk6fszSQcQ/rueMId0kVD9U7nl8cfEnFxqOCrNT92g4laQIGl2mn8lsGZfTLg8ksHq3gkvgO3oo/0wHy4v32JeBOHTsN5AL4gfHNEhWeWb50ev47hnTsRIt9P4dxogeUo/hTu7j9+s9lLpEQXCvq6xocXQt0j8MV9qZBBXFLXVT3cWIkSqOdwt/5ZBg+1GSrc7WfCXVWgTk4a20uPMuJPxU4RQwZW6X3+O8Pqo8C3cW0OzZRFP6gUYUKUsTI5WntlS+LAxgw1mZNsozFGdbiOPRnEryE3SRldh9vjDR3tin1fGpA5P7+CEB/bqaXtG3V+F2OkqaMN

Change information

Files:
  • M ssh/certs.go
  • M ssh/certs_test.go
Change size: S
Delta: 2 files changed, 39 insertions(+), 5 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: crypto
Gerrit-Branch: master
Gerrit-Change-Id: Id51401aae15bfbec006c03ab97635a3b68bd6058
Gerrit-Change-Number: 802145
Gerrit-PatchSet: 1
Gerrit-Owner: Nicola Murino <nicola...@gmail.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Nicola Murino (Gerrit)

unread,
Jul 19, 2026, 3:08:21 PM (2 days ago) Jul 19
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Nicola Murino voted Commit-Queue+1

Commit-Queue+1
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: crypto
Gerrit-Branch: master
Gerrit-Change-Id: Id51401aae15bfbec006c03ab97635a3b68bd6058
Gerrit-Change-Number: 802145
Gerrit-PatchSet: 1
Gerrit-Owner: Nicola Murino <nicola...@gmail.com>
Gerrit-Reviewer: Nicola Murino <nicola...@gmail.com>
Gerrit-Comment-Date: Sun, 19 Jul 2026 19:08:13 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy

Filippo Valsorda (Gerrit)

unread,
6:31 AM (16 hours ago) 6:31 AM
to Nicola Murino, goph...@pubsubhelper.golang.org, Filippo Valsorda, Roland Shoemaker, Gopher Robot, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com
Attention needed from Nicola Murino

Filippo Valsorda voted Code-Review+2

Code-Review+2
Open in Gerrit

Related details

Attention is currently required from:
  • Nicola Murino
Submit Requirements:
  • requirement satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: crypto
Gerrit-Branch: master
Gerrit-Change-Id: Id51401aae15bfbec006c03ab97635a3b68bd6058
Gerrit-Change-Number: 802145
Gerrit-PatchSet: 1
Gerrit-Owner: Nicola Murino <nicola...@gmail.com>
Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
Gerrit-Reviewer: Nicola Murino <nicola...@gmail.com>
Gerrit-CC: Gopher Robot <go...@golang.org>
Gerrit-CC: Roland Shoemaker <rol...@golang.org>
Gerrit-Attention: Nicola Murino <nicola...@gmail.com>
Gerrit-Comment-Date: Tue, 21 Jul 2026 10:31:01 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
satisfied_requirement
unsatisfied_requirement
open
diffy

Carlos Amedee (Gerrit)

unread,
5:33 PM (5 hours ago) 5:33 PM
to Nicola Murino, goph...@pubsubhelper.golang.org, Filippo Valsorda, Roland Shoemaker, Gopher Robot, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com
Attention needed from Nicola Murino

Carlos Amedee voted Code-Review+1

Code-Review+1
Open in Gerrit

Related details

Attention is currently required from:
  • Nicola Murino
Submit Requirements:
  • requirement satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: crypto
Gerrit-Branch: master
Gerrit-Change-Id: Id51401aae15bfbec006c03ab97635a3b68bd6058
Gerrit-Change-Number: 802145
Gerrit-PatchSet: 1
Gerrit-Owner: Nicola Murino <nicola...@gmail.com>
Gerrit-Reviewer: Carlos Amedee <car...@golang.org>
Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
Gerrit-Reviewer: Nicola Murino <nicola...@gmail.com>
Gerrit-CC: Gopher Robot <go...@golang.org>
Gerrit-CC: Roland Shoemaker <rol...@golang.org>
Gerrit-Attention: Nicola Murino <nicola...@gmail.com>
Gerrit-Comment-Date: Tue, 21 Jul 2026 21:32:55 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
satisfied_requirement
unsatisfied_requirement
open
diffy

Cherry Mui (Gerrit)

unread,
7:25 PM (3 hours ago) 7:25 PM
to Nicola Murino, goph...@pubsubhelper.golang.org, Carlos Amedee, Filippo Valsorda, Roland Shoemaker, Gopher Robot, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com
Attention needed from Nicola Murino

Cherry Mui voted Code-Review+1

Code-Review+1
Open in Gerrit

Related details

Attention is currently required from:
  • Nicola Murino
Submit Requirements:
    • requirement satisfiedCode-Review
    • requirement satisfiedNo-Unresolved-Comments
    • requirement satisfiedReview-Enforcement
    • requirement satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: crypto
    Gerrit-Branch: master
    Gerrit-Change-Id: Id51401aae15bfbec006c03ab97635a3b68bd6058
    Gerrit-Change-Number: 802145
    Gerrit-PatchSet: 1
    Gerrit-Owner: Nicola Murino <nicola...@gmail.com>
    Gerrit-Reviewer: Carlos Amedee <car...@golang.org>
    Gerrit-Reviewer: Cherry Mui <cher...@google.com>
    Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
    Gerrit-Reviewer: Nicola Murino <nicola...@gmail.com>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-CC: Roland Shoemaker <rol...@golang.org>
    Gerrit-Attention: Nicola Murino <nicola...@gmail.com>
    Gerrit-Comment-Date: Tue, 21 Jul 2026 23:25:45 +0000
    Gerrit-HasComments: No
    Gerrit-Has-Labels: Yes
    satisfied_requirement
    open
    diffy
    Reply all
    Reply to author
    Forward
    0 new messages