Skip to content

Commit f4d47b0

Browse files
committed
ssh: return clearer error when signature algorithm is used as key format
ParsePublicKey now returns a more specific error when a signature algorithm like rsa-sha2-256 is mistakenly provided as a key format Change-Id: Ic08286a5b2b326e99dd3e61594919203f0c36791 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/695075 Reviewed-by: Filippo Valsorda <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Mark Freeman <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
1 parent 96dc232 commit f4d47b0

File tree

4 files changed

+76
-2
lines changed

4 files changed

+76
-2
lines changed

ssh/common.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,35 @@ func algorithmsForKeyFormat(keyFormat string) []string {
312312
}
313313
}
314314

315+
// keyFormatForAlgorithm returns the key format corresponding to the given
316+
// signature algorithm. It returns an empty string if the signature algorithm is
317+
// invalid or unsupported.
318+
func keyFormatForAlgorithm(sigAlgo string) string {
319+
switch sigAlgo {
320+
case KeyAlgoRSA, KeyAlgoRSASHA256, KeyAlgoRSASHA512:
321+
return KeyAlgoRSA
322+
case CertAlgoRSAv01, CertAlgoRSASHA256v01, CertAlgoRSASHA512v01:
323+
return CertAlgoRSAv01
324+
case KeyAlgoED25519,
325+
KeyAlgoSKED25519,
326+
KeyAlgoSKECDSA256,
327+
KeyAlgoECDSA256,
328+
KeyAlgoECDSA384,
329+
KeyAlgoECDSA521,
330+
InsecureKeyAlgoDSA,
331+
InsecureCertAlgoDSAv01,
332+
CertAlgoECDSA256v01,
333+
CertAlgoECDSA384v01,
334+
CertAlgoECDSA521v01,
335+
CertAlgoSKECDSA256v01,
336+
CertAlgoED25519v01,
337+
CertAlgoSKED25519v01:
338+
return sigAlgo
339+
default:
340+
return ""
341+
}
342+
}
343+
315344
// isRSA returns whether algo is a supported RSA algorithm, including certificate
316345
// algorithms.
317346
func isRSA(algo string) bool {

ssh/common_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
package ssh
66

77
import (
8+
"maps"
89
"reflect"
10+
"slices"
911
"testing"
1012
)
1113

@@ -174,3 +176,21 @@ func TestFindAgreedAlgorithms(t *testing.T) {
174176
})
175177
}
176178
}
179+
180+
func TestKeyFormatAlgorithms(t *testing.T) {
181+
supportedAlgos := SupportedAlgorithms()
182+
insecureAlgos := InsecureAlgorithms()
183+
algoritms := append(supportedAlgos.PublicKeyAuths, insecureAlgos.PublicKeyAuths...)
184+
algoritms = append(algoritms, slices.Collect(maps.Keys(certKeyAlgoNames))...)
185+
186+
for _, algo := range algoritms {
187+
keyFormat := keyFormatForAlgorithm(algo)
188+
if keyFormat == "" {
189+
t.Errorf("got empty key format for algorithm %q", algo)
190+
}
191+
if !slices.Contains(algorithmsForKeyFormat(keyFormat), algo) {
192+
t.Errorf("algorithms for key format %q, does not contain %q", keyFormat, algo)
193+
}
194+
195+
}
196+
}

ssh/keys.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
8989
}
9090
return cert, nil, nil
9191
}
92+
if keyFormat := keyFormatForAlgorithm(algo); keyFormat != "" {
93+
return nil, nil, fmt.Errorf("ssh: signature algorithm %q isn't a key format; key is malformed and should be re-encoded with type %q",
94+
algo, keyFormat)
95+
}
96+
9297
return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo)
9398
}
9499

@@ -191,9 +196,10 @@ func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey
191196
return "", nil, nil, "", nil, io.EOF
192197
}
193198

194-
// ParseAuthorizedKey parses a public key from an authorized_keys
195-
// file used in OpenSSH according to the sshd(8) manual page.
199+
// ParseAuthorizedKey parses a public key from an authorized_keys file used in
200+
// OpenSSH according to the sshd(8) manual page. Invalid lines are ignored.
196201
func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) {
202+
var lastErr error
197203
for len(in) > 0 {
198204
end := bytes.IndexByte(in, '\n')
199205
if end != -1 {
@@ -222,6 +228,8 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str
222228

223229
if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
224230
return out, comment, options, rest, nil
231+
} else {
232+
lastErr = err
225233
}
226234

227235
// No key type recognised. Maybe there's an options field at
@@ -264,12 +272,18 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str
264272
if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
265273
options = candidateOptions
266274
return out, comment, options, rest, nil
275+
} else {
276+
lastErr = err
267277
}
268278

269279
in = rest
270280
continue
271281
}
272282

283+
if lastErr != nil {
284+
return nil, "", nil, nil, fmt.Errorf("ssh: no key found; last parsing error for ignored line: %w", lastErr)
285+
}
286+
273287
return nil, "", nil, nil, errors.New("ssh: no key found")
274288
}
275289

ssh/keys_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ func TestKeyMarshalParse(t *testing.T) {
5959
}
6060
}
6161

62+
func TestParsePublicKeyWithSigningAlgoAsKeyFormat(t *testing.T) {
63+
key := []byte(`rsa-sha2-256 AAAADHJzYS1zaGEyLTI1NgAAAAMBAAEAAAEBAJ7qMyjLXEJCCJmRknuCLo0uPi5GrPY5pQYr84lhlN8Gor5KVL2LKYCW4e70r5xzj7SrHHSCft1FMlYg1KDO9xrprJh733kQqAPWETmSuH0EfRtGtcH6EarKyVxk6As076/yNiiMKVBtG0RPa1L7FviTfcYK4vnCCVrbv3RmA5CCzuG5BSMbRLxzVb4Ri3p8jhxYT8N4QGe/2yqvJLys5vQ9szpZR3tcFp3DJIVZhBRfR6LnoY23XZniAAMQaUVBX86dXQ++dNwAwZSXSt9Og+AniOCiBYqhNVa5n3DID/H7YtEtG+CbZr3r2KD3fv8AfSLRar4XOp8rsRdD31h/kr8=`)
64+
_, _, _, _, err := ParseAuthorizedKey(key)
65+
if err == nil {
66+
t.Fatal("parsing a public key using a signature algorithm as the key format succeeded unexpectedly")
67+
}
68+
if !strings.Contains(err.Error(), `signature algorithm "rsa-sha2-256" isn't a key format`) {
69+
t.Errorf(`got %v, expected 'signature algorithm "rsa-sha2-256" isn't a key format'`, err)
70+
}
71+
}
72+
6273
func TestUnsupportedCurves(t *testing.T) {
6374
raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
6475
if err != nil {

0 commit comments

Comments
 (0)