github.com/devops-filetransfer/sshego@v7.0.4+incompatible/_vendor/golang.org/x/crypto/ssh/keys_test.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ssh 6 7 import ( 8 "bytes" 9 "crypto/dsa" 10 "crypto/ecdsa" 11 "crypto/elliptic" 12 "crypto/rand" 13 "crypto/rsa" 14 "encoding/base64" 15 "fmt" 16 "reflect" 17 "strings" 18 "testing" 19 20 "golang.org/x/crypto/ed25519" 21 "golang.org/x/crypto/ssh/testdata" 22 ) 23 24 func rawKey(pub PublicKey) interface{} { 25 switch k := pub.(type) { 26 case *rsaPublicKey: 27 return (*rsa.PublicKey)(k) 28 case *dsaPublicKey: 29 return (*dsa.PublicKey)(k) 30 case *ecdsaPublicKey: 31 return (*ecdsa.PublicKey)(k) 32 case ed25519PublicKey: 33 return (ed25519.PublicKey)(k) 34 case *Certificate: 35 return k 36 } 37 panic("unknown key type") 38 } 39 40 func TestKeyMarshalParse(t *testing.T) { 41 for _, priv := range testSigners { 42 pub := priv.PublicKey() 43 roundtrip, err := ParsePublicKey(pub.Marshal()) 44 if err != nil { 45 t.Errorf("ParsePublicKey(%T): %v", pub, err) 46 } 47 48 k1 := rawKey(pub) 49 k2 := rawKey(roundtrip) 50 51 if !reflect.DeepEqual(k1, k2) { 52 t.Errorf("got %#v in roundtrip, want %#v", k2, k1) 53 } 54 } 55 } 56 57 func TestUnsupportedCurves(t *testing.T) { 58 raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) 59 if err != nil { 60 t.Fatalf("GenerateKey: %v", err) 61 } 62 63 if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P-256") { 64 t.Fatalf("NewPrivateKey should not succeed with P-224, got: %v", err) 65 } 66 67 if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P-256") { 68 t.Fatalf("NewPublicKey should not succeed with P-224, got: %v", err) 69 } 70 } 71 72 func TestNewPublicKey(t *testing.T) { 73 for _, k := range testSigners { 74 raw := rawKey(k.PublicKey()) 75 // Skip certificates, as NewPublicKey does not support them. 76 if _, ok := raw.(*Certificate); ok { 77 continue 78 } 79 pub, err := NewPublicKey(raw) 80 if err != nil { 81 t.Errorf("NewPublicKey(%#v): %v", raw, err) 82 } 83 if !reflect.DeepEqual(k.PublicKey(), pub) { 84 t.Errorf("NewPublicKey(%#v) = %#v, want %#v", raw, pub, k.PublicKey()) 85 } 86 } 87 } 88 89 func TestKeySignVerify(t *testing.T) { 90 for _, priv := range testSigners { 91 pub := priv.PublicKey() 92 93 data := []byte("sign me") 94 sig, err := priv.Sign(rand.Reader, data) 95 if err != nil { 96 t.Fatalf("Sign(%T): %v", priv, err) 97 } 98 99 if err := pub.Verify(data, sig); err != nil { 100 t.Errorf("publicKey.Verify(%T): %v", priv, err) 101 } 102 sig.Blob[5]++ 103 if err := pub.Verify(data, sig); err == nil { 104 t.Errorf("publicKey.Verify on broken sig did not fail") 105 } 106 } 107 } 108 109 func TestParseRSAPrivateKey(t *testing.T) { 110 key := testPrivateKeys["rsa"] 111 112 rsa, ok := key.(*rsa.PrivateKey) 113 if !ok { 114 t.Fatalf("got %T, want *rsa.PrivateKey", rsa) 115 } 116 117 if err := rsa.Validate(); err != nil { 118 t.Errorf("Validate: %v", err) 119 } 120 } 121 122 func TestParseECPrivateKey(t *testing.T) { 123 key := testPrivateKeys["ecdsa"] 124 125 ecKey, ok := key.(*ecdsa.PrivateKey) 126 if !ok { 127 t.Fatalf("got %T, want *ecdsa.PrivateKey", ecKey) 128 } 129 130 if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) { 131 t.Fatalf("public key does not validate.") 132 } 133 } 134 135 // See Issue https://github.com/golang/go/issues/6650. 136 func TestParseEncryptedPrivateKeysFails(t *testing.T) { 137 const wantSubstring = "encrypted" 138 for i, tt := range testdata.PEMEncryptedKeys { 139 _, err := ParsePrivateKey(tt.PEMBytes) 140 if err == nil { 141 t.Errorf("#%d key %s: ParsePrivateKey successfully parsed, expected an error", i, tt.Name) 142 continue 143 } 144 145 if !strings.Contains(err.Error(), wantSubstring) { 146 t.Errorf("#%d key %s: got error %q, want substring %q", i, tt.Name, err, wantSubstring) 147 } 148 } 149 } 150 151 func TestParseDSA(t *testing.T) { 152 // We actually exercise the ParsePrivateKey codepath here, as opposed to 153 // using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go 154 // uses. 155 s, err := ParsePrivateKey(testdata.PEMBytes["dsa"]) 156 if err != nil { 157 t.Fatalf("ParsePrivateKey returned error: %s", err) 158 } 159 160 data := []byte("sign me") 161 sig, err := s.Sign(rand.Reader, data) 162 if err != nil { 163 t.Fatalf("dsa.Sign: %v", err) 164 } 165 166 if err := s.PublicKey().Verify(data, sig); err != nil { 167 t.Errorf("Verify failed: %v", err) 168 } 169 } 170 171 // Tests for authorized_keys parsing. 172 173 // getTestKey returns a public key, and its base64 encoding. 174 func getTestKey() (PublicKey, string) { 175 k := testPublicKeys["rsa"] 176 177 b := &bytes.Buffer{} 178 e := base64.NewEncoder(base64.StdEncoding, b) 179 e.Write(k.Marshal()) 180 e.Close() 181 182 return k, b.String() 183 } 184 185 func TestMarshalParsePublicKey(t *testing.T) { 186 pub, pubSerialized := getTestKey() 187 line := fmt.Sprintf("%s %s user@host", pub.Type(), pubSerialized) 188 189 authKeys := MarshalAuthorizedKey(pub) 190 actualFields := strings.Fields(string(authKeys)) 191 if len(actualFields) == 0 { 192 t.Fatalf("failed authKeys: %v", authKeys) 193 } 194 195 // drop the comment 196 expectedFields := strings.Fields(line)[0:2] 197 198 if !reflect.DeepEqual(actualFields, expectedFields) { 199 t.Errorf("got %v, expected %v", actualFields, expectedFields) 200 } 201 202 actPub, _, _, _, err := ParseAuthorizedKey([]byte(line)) 203 if err != nil { 204 t.Fatalf("cannot parse %v: %v", line, err) 205 } 206 if !reflect.DeepEqual(actPub, pub) { 207 t.Errorf("got %v, expected %v", actPub, pub) 208 } 209 } 210 211 type authResult struct { 212 pubKey PublicKey 213 options []string 214 comments string 215 rest string 216 ok bool 217 } 218 219 func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) { 220 rest := authKeys 221 var values []authResult 222 for len(rest) > 0 { 223 var r authResult 224 var err error 225 r.pubKey, r.comments, r.options, rest, err = ParseAuthorizedKey(rest) 226 r.ok = (err == nil) 227 t.Log(err) 228 r.rest = string(rest) 229 values = append(values, r) 230 } 231 232 if !reflect.DeepEqual(values, expected) { 233 t.Errorf("got %#v, expected %#v", values, expected) 234 } 235 } 236 237 func TestAuthorizedKeyBasic(t *testing.T) { 238 pub, pubSerialized := getTestKey() 239 line := "ssh-rsa " + pubSerialized + " user@host" 240 testAuthorizedKeys(t, []byte(line), 241 []authResult{ 242 {pub, nil, "user@host", "", true}, 243 }) 244 } 245 246 func TestAuth(t *testing.T) { 247 pub, pubSerialized := getTestKey() 248 authWithOptions := []string{ 249 `# comments to ignore before any keys...`, 250 ``, 251 `env="HOME=/home/root",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`, 252 `# comments to ignore, along with a blank line`, 253 ``, 254 `env="HOME=/home/root2" ssh-rsa ` + pubSerialized + ` user2@host2`, 255 ``, 256 `# more comments, plus a invalid entry`, 257 `ssh-rsa data-that-will-not-parse user@host3`, 258 } 259 for _, eol := range []string{"\n", "\r\n"} { 260 authOptions := strings.Join(authWithOptions, eol) 261 rest2 := strings.Join(authWithOptions[3:], eol) 262 rest3 := strings.Join(authWithOptions[6:], eol) 263 testAuthorizedKeys(t, []byte(authOptions), []authResult{ 264 {pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true}, 265 {pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true}, 266 {nil, nil, "", "", false}, 267 }) 268 } 269 } 270 271 func TestAuthWithQuotedSpaceInEnv(t *testing.T) { 272 pub, pubSerialized := getTestKey() 273 authWithQuotedSpaceInEnv := []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`) 274 testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{ 275 {pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true}, 276 }) 277 } 278 279 func TestAuthWithQuotedCommaInEnv(t *testing.T) { 280 pub, pubSerialized := getTestKey() 281 authWithQuotedCommaInEnv := []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`) 282 testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{ 283 {pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true}, 284 }) 285 } 286 287 func TestAuthWithQuotedQuoteInEnv(t *testing.T) { 288 pub, pubSerialized := getTestKey() 289 authWithQuotedQuoteInEnv := []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + pubSerialized + ` user@host`) 290 authWithDoubleQuotedQuote := []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + pubSerialized + "\t" + `user@host`) 291 testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{ 292 {pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true}, 293 }) 294 295 testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{ 296 {pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true}, 297 }) 298 } 299 300 func TestAuthWithInvalidSpace(t *testing.T) { 301 _, pubSerialized := getTestKey() 302 authWithInvalidSpace := []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host 303 #more to follow but still no valid keys`) 304 testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{ 305 {nil, nil, "", "", false}, 306 }) 307 } 308 309 func TestAuthWithMissingQuote(t *testing.T) { 310 pub, pubSerialized := getTestKey() 311 authWithMissingQuote := []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host 312 env="HOME=/home/root",shared-control ssh-rsa ` + pubSerialized + ` user@host`) 313 314 testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{ 315 {pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true}, 316 }) 317 } 318 319 func TestInvalidEntry(t *testing.T) { 320 authInvalid := []byte(`ssh-rsa`) 321 _, _, _, _, err := ParseAuthorizedKey(authInvalid) 322 if err == nil { 323 t.Errorf("got valid entry for %q", authInvalid) 324 } 325 } 326 327 var knownHostsParseTests = []struct { 328 input string 329 err string 330 331 marker string 332 comment string 333 hosts []string 334 rest string 335 }{ 336 { 337 "", 338 "EOF", 339 340 "", "", nil, "", 341 }, 342 { 343 "# Just a comment", 344 "EOF", 345 346 "", "", nil, "", 347 }, 348 { 349 " \t ", 350 "EOF", 351 352 "", "", nil, "", 353 }, 354 { 355 "localhost ssh-rsa {RSAPUB}", 356 "", 357 358 "", "", []string{"localhost"}, "", 359 }, 360 { 361 "localhost\tssh-rsa {RSAPUB}", 362 "", 363 364 "", "", []string{"localhost"}, "", 365 }, 366 { 367 "localhost\tssh-rsa {RSAPUB}\tcomment comment", 368 "", 369 370 "", "comment comment", []string{"localhost"}, "", 371 }, 372 { 373 "localhost\tssh-rsa {RSAPUB}\tcomment comment\n", 374 "", 375 376 "", "comment comment", []string{"localhost"}, "", 377 }, 378 { 379 "localhost\tssh-rsa {RSAPUB}\tcomment comment\r\n", 380 "", 381 382 "", "comment comment", []string{"localhost"}, "", 383 }, 384 { 385 "localhost\tssh-rsa {RSAPUB}\tcomment comment\r\nnext line", 386 "", 387 388 "", "comment comment", []string{"localhost"}, "next line", 389 }, 390 { 391 "localhost,[host2:123]\tssh-rsa {RSAPUB}\tcomment comment", 392 "", 393 394 "", "comment comment", []string{"localhost", "[host2:123]"}, "", 395 }, 396 { 397 "@marker \tlocalhost,[host2:123]\tssh-rsa {RSAPUB}", 398 "", 399 400 "marker", "", []string{"localhost", "[host2:123]"}, "", 401 }, 402 { 403 "@marker \tlocalhost,[host2:123]\tssh-rsa aabbccdd", 404 "short read", 405 406 "", "", nil, "", 407 }, 408 } 409 410 func TestKnownHostsParsing(t *testing.T) { 411 rsaPub, rsaPubSerialized := getTestKey() 412 413 for i, test := range knownHostsParseTests { 414 var expectedKey PublicKey 415 const rsaKeyToken = "{RSAPUB}" 416 417 input := test.input 418 if strings.Contains(input, rsaKeyToken) { 419 expectedKey = rsaPub 420 input = strings.Replace(test.input, rsaKeyToken, rsaPubSerialized, -1) 421 } 422 423 marker, hosts, pubKey, comment, rest, err := ParseKnownHosts([]byte(input)) 424 if err != nil { 425 if len(test.err) == 0 { 426 t.Errorf("#%d: unexpectedly failed with %q", i, err) 427 } else if !strings.Contains(err.Error(), test.err) { 428 t.Errorf("#%d: expected error containing %q, but got %q", i, test.err, err) 429 } 430 continue 431 } else if len(test.err) != 0 { 432 t.Errorf("#%d: succeeded but expected error including %q", i, test.err) 433 continue 434 } 435 436 if !reflect.DeepEqual(expectedKey, pubKey) { 437 t.Errorf("#%d: expected key %#v, but got %#v", i, expectedKey, pubKey) 438 } 439 440 if marker != test.marker { 441 t.Errorf("#%d: expected marker %q, but got %q", i, test.marker, marker) 442 } 443 444 if comment != test.comment { 445 t.Errorf("#%d: expected comment %q, but got %q", i, test.comment, comment) 446 } 447 448 if !reflect.DeepEqual(test.hosts, hosts) { 449 t.Errorf("#%d: expected hosts %#v, but got %#v", i, test.hosts, hosts) 450 } 451 452 if rest := string(rest); rest != test.rest { 453 t.Errorf("#%d: expected remaining input to be %q, but got %q", i, test.rest, rest) 454 } 455 } 456 } 457 458 func TestFingerprintLegacyMD5(t *testing.T) { 459 pub, _ := getTestKey() 460 fingerprint := FingerprintLegacyMD5(pub) 461 want := "fb:61:6d:1a:e3:f0:95:45:3c:a0:79:be:4a:93:63:66" // ssh-keygen -lf -E md5 rsa 462 if fingerprint != want { 463 t.Errorf("got fingerprint %q want %q", fingerprint, want) 464 } 465 } 466 467 func TestFingerprintSHA256(t *testing.T) { 468 pub, _ := getTestKey() 469 fingerprint := FingerprintSHA256(pub) 470 want := "SHA256:Anr3LjZK8YVpjrxu79myrW9Hrb/wpcMNpVvTq/RcBm8" // ssh-keygen -lf rsa 471 if fingerprint != want { 472 t.Errorf("got fingerprint %q want %q", fingerprint, want) 473 } 474 }