Skip to content

Commit c6fce02

Browse files
drakkangopherbot
authored andcommitted
ssh: refuse to parse certificates that use a certificate as signing key
According to draft-miller-ssh-cert-01, Section 2.1.1, certificates with certificate keys as signature keys are invalid Change-Id: I474524ea444deb78f2fa7c2682e47c0fd057f0b8 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/678716 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Auto-Submit: Nicola Murino <nicola.murino@gmail.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org>
1 parent 0ae49b8 commit c6fce02

File tree

3 files changed

+37
-9
lines changed

3 files changed

+37
-9
lines changed

ssh/certs.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,11 @@ func parseCert(in []byte, privAlgo string) (*Certificate, error) {
233233
if err != nil {
234234
return nil, err
235235
}
236-
236+
// The Type() function is intended to return only certificate key types, but
237+
// we use certKeyAlgoNames anyway for safety, to match [Certificate.Type].
238+
if _, ok := certKeyAlgoNames[k.Type()]; ok {
239+
return nil, fmt.Errorf("ssh: the signature key type %q is invalid for certificates", k.Type())
240+
}
237241
c.SignatureKey = k
238242
c.Signature, rest, ok = parseSignatureBody(g.Signature)
239243
if !ok || len(rest) > 0 {
@@ -301,16 +305,13 @@ type CertChecker struct {
301305
SupportedCriticalOptions []string
302306

303307
// IsUserAuthority should return true if the key is recognized as an
304-
// authority for the given user certificate. This allows for
305-
// certificates to be signed by other certificates. This must be set
306-
// if this CertChecker will be checking user certificates.
308+
// authority for user certificate. This must be set if this CertChecker
309+
// will be checking user certificates.
307310
IsUserAuthority func(auth PublicKey) bool
308311

309312
// IsHostAuthority should report whether the key is recognized as
310-
// an authority for this host. This allows for certificates to be
311-
// signed by other keys, and for those other keys to only be valid
312-
// signers for particular hostnames. This must be set if this
313-
// CertChecker will be checking host certificates.
313+
// an authority for this host. This must be set if this CertChecker
314+
// will be checking host certificates.
314315
IsHostAuthority func(auth PublicKey, address string) bool
315316

316317
// Clock is used for verifying time stamps. If nil, time.Now

ssh/keys.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str
273273
return nil, "", nil, nil, errors.New("ssh: no key found")
274274
}
275275

276-
// ParsePublicKey parses an SSH public key formatted for use in
276+
// ParsePublicKey parses an SSH public key or certificate formatted for use in
277277
// the SSH wire protocol according to RFC 4253, section 6.6.
278278
func ParsePublicKey(in []byte) (out PublicKey, err error) {
279279
algo, in, ok := parseString(in)

ssh/keys_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,3 +810,30 @@ func TestCryptoPublicKey(t *testing.T) {
810810
}
811811
}
812812
}
813+
814+
func TestParseCertWithCertSignatureKey(t *testing.T) {
815+
certBytes := []byte(`-----BEGIN SSH CERTIFICATE-----
816+
AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIPSp27hvNSB0
817+
IotJnVhjC4zxNgNS8BHlUCxD0VJi4D/eAAAAIIJMi1e5qfx+IFuKD/p/Ssqcb3os
818+
CpOw/4wBs1pQ53zwAAAAAAAAAAEAAAACAAAAAAAAABMAAAAPZm9vLmV4YW1wbGUu
819+
Y29tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0AAAAgc3NoLWVkMjU1
820+
MTktY2VydC12MDFAb3BlbnNzaC5jb20AAAAg+sNYhCO35mQT1UBMpmMk8ey+culd
821+
IU8vBlPEl4B07swAAAAggiv+RLnboS4znGCVl/n1jDg2uD0h15tW4s/04eS2mLQA
822+
AAAAAAAAAQAAAAIAAAAAAAAAEwAAAA9mb28uZXhhbXBsZS5jb20AAAAAAAAAAAAA
823+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACCV2wETgLKL
824+
Kt0bRl3YUnd/ZYSlq0xJMbn4Jj3cdPWykQAAAFMAAAALc3NoLWVkMjU1MTkAAABA
825+
WOdbRGEzyRAhiIK227CLUQD5caXYMV8FvSIB7toEE2M/8HnWdG9H3Rsg/v3unruQ
826+
JrQldnuPJNe7KOP2+zvUDgAAAFMAAAALc3NoLWVkMjU1MTkAAABAm3bIPp85ZpIe
827+
D+izJcUqlcAOri7HO8bULFNHT6LVegvB06xQ5TLwMlrxWUF4cafl1tSe8JQck4a6
828+
cLYUOHfQDw==
829+
-----END SSH CERTIFICATE-----
830+
`)
831+
block, _ := pem.Decode(certBytes)
832+
if block == nil {
833+
t.Fatal("invalid test certificate")
834+
}
835+
836+
if _, err := ParsePublicKey(block.Bytes); err == nil {
837+
t.Fatal("parsing an SSH certificate using another certificate as signature key succeeded; expected failure")
838+
}
839+
}

0 commit comments

Comments
 (0)