github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/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 "crypto/x509" 15 "encoding/base64" 16 "encoding/hex" 17 "encoding/pem" 18 "fmt" 19 "io" 20 "reflect" 21 "strings" 22 "testing" 23 24 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh/testdata" 25 "golang.org/x/crypto/ed25519" 26 ) 27 28 func rawKey(pub PublicKey) interface{} { 29 switch k := pub.(type) { 30 case *rsaPublicKey: 31 return (*rsa.PublicKey)(k) 32 case *dsaPublicKey: 33 return (*dsa.PublicKey)(k) 34 case *ecdsaPublicKey: 35 return (*ecdsa.PublicKey)(k) 36 case ed25519PublicKey: 37 return (ed25519.PublicKey)(k) 38 case *Certificate: 39 return k 40 } 41 panic("unknown key type") 42 } 43 44 func TestKeyMarshalParse(t *testing.T) { 45 for _, priv := range testSigners { 46 pub := priv.PublicKey() 47 roundtrip, err := ParsePublicKey(pub.Marshal()) 48 if err != nil { 49 t.Errorf("ParsePublicKey(%T): %v", pub, err) 50 } 51 52 k1 := rawKey(pub) 53 k2 := rawKey(roundtrip) 54 55 if !reflect.DeepEqual(k1, k2) { 56 t.Errorf("got %#v in roundtrip, want %#v", k2, k1) 57 } 58 } 59 } 60 61 func TestUnsupportedCurves(t *testing.T) { 62 raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) 63 if err != nil { 64 t.Fatalf("GenerateKey: %v", err) 65 } 66 67 if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P-256") { 68 t.Fatalf("NewPrivateKey should not succeed with P-224, got: %v", err) 69 } 70 71 if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P-256") { 72 t.Fatalf("NewPublicKey should not succeed with P-224, got: %v", err) 73 } 74 } 75 76 func TestNewPublicKey(t *testing.T) { 77 for _, k := range testSigners { 78 raw := rawKey(k.PublicKey()) 79 // Skip certificates, as NewPublicKey does not support them. 80 if _, ok := raw.(*Certificate); ok { 81 continue 82 } 83 pub, err := NewPublicKey(raw) 84 if err != nil { 85 t.Errorf("NewPublicKey(%#v): %v", raw, err) 86 } 87 if !reflect.DeepEqual(k.PublicKey(), pub) { 88 t.Errorf("NewPublicKey(%#v) = %#v, want %#v", raw, pub, k.PublicKey()) 89 } 90 } 91 } 92 93 func TestKeySignVerify(t *testing.T) { 94 for _, priv := range testSigners { 95 pub := priv.PublicKey() 96 97 data := []byte("sign me") 98 sig, err := priv.Sign(rand.Reader, data) 99 if err != nil { 100 t.Fatalf("Sign(%T): %v", priv, err) 101 } 102 103 if err := pub.Verify(data, sig); err != nil { 104 t.Errorf("publicKey.Verify(%T): %v", priv, err) 105 } 106 sig.Blob[5]++ 107 if err := pub.Verify(data, sig); err == nil { 108 t.Errorf("publicKey.Verify on broken sig did not fail") 109 } 110 } 111 } 112 113 func TestKeySignWithAlgorithmVerify(t *testing.T) { 114 for _, priv := range testSigners { 115 if algorithmSigner, ok := priv.(AlgorithmSigner); !ok { 116 t.Errorf("Signers constructed by ssh package should always implement the AlgorithmSigner interface: %T", priv) 117 } else { 118 pub := priv.PublicKey() 119 data := []byte("sign me") 120 121 signWithAlgTestCase := func(algorithm string, expectedAlg string) { 122 sig, err := algorithmSigner.SignWithAlgorithm(rand.Reader, data, algorithm) 123 if err != nil { 124 t.Fatalf("Sign(%T): %v", priv, err) 125 } 126 if sig.Format != expectedAlg { 127 t.Errorf("signature format did not match requested signature algorithm: %s != %s", sig.Format, expectedAlg) 128 } 129 130 if err := pub.Verify(data, sig); err != nil { 131 t.Errorf("publicKey.Verify(%T): %v", priv, err) 132 } 133 sig.Blob[5]++ 134 if err := pub.Verify(data, sig); err == nil { 135 t.Errorf("publicKey.Verify on broken sig did not fail") 136 } 137 } 138 139 // Using the empty string as the algorithm name should result in the same signature format as the algorithm-free Sign method. 140 defaultSig, err := priv.Sign(rand.Reader, data) 141 if err != nil { 142 t.Fatalf("Sign(%T): %v", priv, err) 143 } 144 signWithAlgTestCase("", defaultSig.Format) 145 146 // RSA keys are the only ones which currently support more than one signing algorithm 147 if pub.Type() == KeyAlgoRSA { 148 for _, algorithm := range []string{SigAlgoRSA, SigAlgoRSASHA2256, SigAlgoRSASHA2512} { 149 signWithAlgTestCase(algorithm, algorithm) 150 } 151 } 152 } 153 } 154 } 155 156 func TestParseRSAPrivateKey(t *testing.T) { 157 key := testPrivateKeys["rsa"] 158 159 rsa, ok := key.(*rsa.PrivateKey) 160 if !ok { 161 t.Fatalf("got %T, want *rsa.PrivateKey", rsa) 162 } 163 164 if err := rsa.Validate(); err != nil { 165 t.Errorf("Validate: %v", err) 166 } 167 } 168 169 func TestParseECPrivateKey(t *testing.T) { 170 key := testPrivateKeys["ecdsa"] 171 172 ecKey, ok := key.(*ecdsa.PrivateKey) 173 if !ok { 174 t.Fatalf("got %T, want *ecdsa.PrivateKey", ecKey) 175 } 176 177 if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) { 178 t.Fatalf("public key does not validate.") 179 } 180 } 181 182 func TestParseEncryptedPrivateKeysWithPassphrase(t *testing.T) { 183 data := []byte("sign me") 184 for _, tt := range testdata.PEMEncryptedKeys { 185 t.Run(tt.Name, func(t *testing.T) { 186 _, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte("incorrect")) 187 if err != x509.IncorrectPasswordError { 188 t.Errorf("got %v want IncorrectPasswordError", err) 189 } 190 191 s, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte(tt.EncryptionKey)) 192 if err != nil { 193 t.Fatalf("ParsePrivateKeyWithPassphrase returned error: %s", err) 194 } 195 196 sig, err := s.Sign(rand.Reader, data) 197 if err != nil { 198 t.Fatalf("Signer.Sign: %v", err) 199 } 200 if err := s.PublicKey().Verify(data, sig); err != nil { 201 t.Errorf("Verify failed: %v", err) 202 } 203 204 _, err = ParsePrivateKey(tt.PEMBytes) 205 if err == nil { 206 t.Fatalf("ParsePrivateKey succeeded, expected an error") 207 } 208 209 if err, ok := err.(*PassphraseMissingError); !ok { 210 t.Errorf("got error %q, want PassphraseMissingError", err) 211 } else if tt.IncludesPublicKey { 212 if err.PublicKey == nil { 213 t.Fatalf("expected PassphraseMissingError.PublicKey not to be nil") 214 } 215 got, want := err.PublicKey.Marshal(), s.PublicKey().Marshal() 216 if !bytes.Equal(got, want) { 217 t.Errorf("error field %q doesn't match signer public key %q", got, want) 218 } 219 } 220 }) 221 } 222 } 223 224 func TestParseDSA(t *testing.T) { 225 // We actually exercise the ParsePrivateKey codepath here, as opposed to 226 // using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go 227 // uses. 228 s, err := ParsePrivateKey(testdata.PEMBytes["dsa"]) 229 if err != nil { 230 t.Fatalf("ParsePrivateKey returned error: %s", err) 231 } 232 233 data := []byte("sign me") 234 sig, err := s.Sign(rand.Reader, data) 235 if err != nil { 236 t.Fatalf("dsa.Sign: %v", err) 237 } 238 239 if err := s.PublicKey().Verify(data, sig); err != nil { 240 t.Errorf("Verify failed: %v", err) 241 } 242 } 243 244 // Tests for authorized_keys parsing. 245 246 // getTestKey returns a public key, and its base64 encoding. 247 func getTestKey() (PublicKey, string) { 248 k := testPublicKeys["rsa"] 249 250 b := &bytes.Buffer{} 251 e := base64.NewEncoder(base64.StdEncoding, b) 252 e.Write(k.Marshal()) 253 e.Close() 254 255 return k, b.String() 256 } 257 258 func TestMarshalParsePublicKey(t *testing.T) { 259 pub, pubSerialized := getTestKey() 260 line := fmt.Sprintf("%s %s user@host", pub.Type(), pubSerialized) 261 262 authKeys := MarshalAuthorizedKey(pub) 263 actualFields := strings.Fields(string(authKeys)) 264 if len(actualFields) == 0 { 265 t.Fatalf("failed authKeys: %v", authKeys) 266 } 267 268 // drop the comment 269 expectedFields := strings.Fields(line)[0:2] 270 271 if !reflect.DeepEqual(actualFields, expectedFields) { 272 t.Errorf("got %v, expected %v", actualFields, expectedFields) 273 } 274 275 actPub, _, _, _, err := ParseAuthorizedKey([]byte(line)) 276 if err != nil { 277 t.Fatalf("cannot parse %v: %v", line, err) 278 } 279 if !reflect.DeepEqual(actPub, pub) { 280 t.Errorf("got %v, expected %v", actPub, pub) 281 } 282 } 283 284 type testAuthResult struct { 285 pubKey PublicKey 286 options []string 287 comments string 288 rest string 289 ok bool 290 } 291 292 func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []testAuthResult) { 293 rest := authKeys 294 var values []testAuthResult 295 for len(rest) > 0 { 296 var r testAuthResult 297 var err error 298 r.pubKey, r.comments, r.options, rest, err = ParseAuthorizedKey(rest) 299 r.ok = (err == nil) 300 t.Log(err) 301 r.rest = string(rest) 302 values = append(values, r) 303 } 304 305 if !reflect.DeepEqual(values, expected) { 306 t.Errorf("got %#v, expected %#v", values, expected) 307 } 308 } 309 310 func TestAuthorizedKeyBasic(t *testing.T) { 311 pub, pubSerialized := getTestKey() 312 line := "ssh-rsa " + pubSerialized + " user@host" 313 testAuthorizedKeys(t, []byte(line), 314 []testAuthResult{ 315 {pub, nil, "user@host", "", true}, 316 }) 317 } 318 319 func TestAuth(t *testing.T) { 320 pub, pubSerialized := getTestKey() 321 authWithOptions := []string{ 322 `# comments to ignore before any keys...`, 323 ``, 324 `env="HOME=/home/root",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`, 325 `# comments to ignore, along with a blank line`, 326 ``, 327 `env="HOME=/home/root2" ssh-rsa ` + pubSerialized + ` user2@host2`, 328 ``, 329 `# more comments, plus a invalid entry`, 330 `ssh-rsa data-that-will-not-parse user@host3`, 331 } 332 for _, eol := range []string{"\n", "\r\n"} { 333 authOptions := strings.Join(authWithOptions, eol) 334 rest2 := strings.Join(authWithOptions[3:], eol) 335 rest3 := strings.Join(authWithOptions[6:], eol) 336 testAuthorizedKeys(t, []byte(authOptions), []testAuthResult{ 337 {pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true}, 338 {pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true}, 339 {nil, nil, "", "", false}, 340 }) 341 } 342 } 343 344 func TestAuthWithQuotedSpaceInEnv(t *testing.T) { 345 pub, pubSerialized := getTestKey() 346 authWithQuotedSpaceInEnv := []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`) 347 testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []testAuthResult{ 348 {pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true}, 349 }) 350 } 351 352 func TestAuthWithQuotedCommaInEnv(t *testing.T) { 353 pub, pubSerialized := getTestKey() 354 authWithQuotedCommaInEnv := []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`) 355 testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []testAuthResult{ 356 {pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true}, 357 }) 358 } 359 360 func TestAuthWithQuotedQuoteInEnv(t *testing.T) { 361 pub, pubSerialized := getTestKey() 362 authWithQuotedQuoteInEnv := []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + pubSerialized + ` user@host`) 363 authWithDoubleQuotedQuote := []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + pubSerialized + "\t" + `user@host`) 364 testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []testAuthResult{ 365 {pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true}, 366 }) 367 368 testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []testAuthResult{ 369 {pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true}, 370 }) 371 } 372 373 func TestAuthWithInvalidSpace(t *testing.T) { 374 _, pubSerialized := getTestKey() 375 authWithInvalidSpace := []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host 376 #more to follow but still no valid keys`) 377 testAuthorizedKeys(t, []byte(authWithInvalidSpace), []testAuthResult{ 378 {nil, nil, "", "", false}, 379 }) 380 } 381 382 func TestAuthWithMissingQuote(t *testing.T) { 383 pub, pubSerialized := getTestKey() 384 authWithMissingQuote := []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host 385 env="HOME=/home/root",shared-control ssh-rsa ` + pubSerialized + ` user@host`) 386 387 testAuthorizedKeys(t, []byte(authWithMissingQuote), []testAuthResult{ 388 {pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true}, 389 }) 390 } 391 392 func TestInvalidEntry(t *testing.T) { 393 authInvalid := []byte(`ssh-rsa`) 394 _, _, _, _, err := ParseAuthorizedKey(authInvalid) 395 if err == nil { 396 t.Errorf("got valid entry for %q", authInvalid) 397 } 398 } 399 400 var knownHostsParseTests = []struct { 401 input string 402 err string 403 404 marker string 405 comment string 406 hosts []string 407 rest string 408 }{ 409 { 410 "", 411 "EOF", 412 413 "", "", nil, "", 414 }, 415 { 416 "# Just a comment", 417 "EOF", 418 419 "", "", nil, "", 420 }, 421 { 422 " \t ", 423 "EOF", 424 425 "", "", nil, "", 426 }, 427 { 428 "localhost ssh-rsa {RSAPUB}", 429 "", 430 431 "", "", []string{"localhost"}, "", 432 }, 433 { 434 "localhost\tssh-rsa {RSAPUB}", 435 "", 436 437 "", "", []string{"localhost"}, "", 438 }, 439 { 440 "localhost\tssh-rsa {RSAPUB}\tcomment comment", 441 "", 442 443 "", "comment comment", []string{"localhost"}, "", 444 }, 445 { 446 "localhost\tssh-rsa {RSAPUB}\tcomment comment\n", 447 "", 448 449 "", "comment comment", []string{"localhost"}, "", 450 }, 451 { 452 "localhost\tssh-rsa {RSAPUB}\tcomment comment\r\n", 453 "", 454 455 "", "comment comment", []string{"localhost"}, "", 456 }, 457 { 458 "localhost\tssh-rsa {RSAPUB}\tcomment comment\r\nnext line", 459 "", 460 461 "", "comment comment", []string{"localhost"}, "next line", 462 }, 463 { 464 "localhost,[host2:123]\tssh-rsa {RSAPUB}\tcomment comment", 465 "", 466 467 "", "comment comment", []string{"localhost", "[host2:123]"}, "", 468 }, 469 { 470 "@marker \tlocalhost,[host2:123]\tssh-rsa {RSAPUB}", 471 "", 472 473 "marker", "", []string{"localhost", "[host2:123]"}, "", 474 }, 475 { 476 "@marker \tlocalhost,[host2:123]\tssh-rsa aabbccdd", 477 "short read", 478 479 "", "", nil, "", 480 }, 481 } 482 483 func TestKnownHostsParsing(t *testing.T) { 484 rsaPub, rsaPubSerialized := getTestKey() 485 486 for i, test := range knownHostsParseTests { 487 var expectedKey PublicKey 488 const rsaKeyToken = "{RSAPUB}" 489 490 input := test.input 491 if strings.Contains(input, rsaKeyToken) { 492 expectedKey = rsaPub 493 input = strings.Replace(test.input, rsaKeyToken, rsaPubSerialized, -1) 494 } 495 496 marker, hosts, pubKey, comment, rest, err := ParseKnownHosts([]byte(input)) 497 if err != nil { 498 if len(test.err) == 0 { 499 t.Errorf("#%d: unexpectedly failed with %q", i, err) 500 } else if !strings.Contains(err.Error(), test.err) { 501 t.Errorf("#%d: expected error containing %q, but got %q", i, test.err, err) 502 } 503 continue 504 } else if len(test.err) != 0 { 505 t.Errorf("#%d: succeeded but expected error including %q", i, test.err) 506 continue 507 } 508 509 if !reflect.DeepEqual(expectedKey, pubKey) { 510 t.Errorf("#%d: expected key %#v, but got %#v", i, expectedKey, pubKey) 511 } 512 513 if marker != test.marker { 514 t.Errorf("#%d: expected marker %q, but got %q", i, test.marker, marker) 515 } 516 517 if comment != test.comment { 518 t.Errorf("#%d: expected comment %q, but got %q", i, test.comment, comment) 519 } 520 521 if !reflect.DeepEqual(test.hosts, hosts) { 522 t.Errorf("#%d: expected hosts %#v, but got %#v", i, test.hosts, hosts) 523 } 524 525 if rest := string(rest); rest != test.rest { 526 t.Errorf("#%d: expected remaining input to be %q, but got %q", i, test.rest, rest) 527 } 528 } 529 } 530 531 func TestFingerprintLegacyMD5(t *testing.T) { 532 pub, _ := getTestKey() 533 fingerprint := FingerprintLegacyMD5(pub) 534 want := "fb:61:6d:1a:e3:f0:95:45:3c:a0:79:be:4a:93:63:66" // ssh-keygen -lf -E md5 rsa 535 if fingerprint != want { 536 t.Errorf("got fingerprint %q want %q", fingerprint, want) 537 } 538 } 539 540 func TestFingerprintSHA256(t *testing.T) { 541 pub, _ := getTestKey() 542 fingerprint := FingerprintSHA256(pub) 543 want := "SHA256:Anr3LjZK8YVpjrxu79myrW9Hrb/wpcMNpVvTq/RcBm8" // ssh-keygen -lf rsa 544 if fingerprint != want { 545 t.Errorf("got fingerprint %q want %q", fingerprint, want) 546 } 547 } 548 549 func TestInvalidKeys(t *testing.T) { 550 keyTypes := []string{ 551 "RSA PRIVATE KEY", 552 "PRIVATE KEY", 553 "EC PRIVATE KEY", 554 "DSA PRIVATE KEY", 555 "OPENSSH PRIVATE KEY", 556 } 557 558 for _, keyType := range keyTypes { 559 for _, dataLen := range []int{0, 1, 2, 5, 10, 20} { 560 data := make([]byte, dataLen) 561 if _, err := io.ReadFull(rand.Reader, data); err != nil { 562 t.Fatal(err) 563 } 564 565 var buf bytes.Buffer 566 pem.Encode(&buf, &pem.Block{ 567 Type: keyType, 568 Bytes: data, 569 }) 570 571 // This test is just to ensure that the function 572 // doesn't panic so the return value is ignored. 573 ParseRawPrivateKey(buf.Bytes()) 574 } 575 } 576 } 577 578 func TestSKKeys(t *testing.T) { 579 for _, d := range testdata.SKData { 580 pk, _, _, _, err := ParseAuthorizedKey(d.PubKey) 581 if err != nil { 582 t.Fatalf("parseAuthorizedKey returned error: %v", err) 583 } 584 585 sigBuf := make([]byte, hex.DecodedLen(len(d.HexSignature))) 586 if _, err := hex.Decode(sigBuf, d.HexSignature); err != nil { 587 t.Fatalf("hex.Decode() failed: %v", err) 588 } 589 590 dataBuf := make([]byte, hex.DecodedLen(len(d.HexData))) 591 if _, err := hex.Decode(dataBuf, d.HexData); err != nil { 592 t.Fatalf("hex.Decode() failed: %v", err) 593 } 594 595 sig, _, ok := parseSignature(sigBuf) 596 if !ok { 597 t.Fatalf("parseSignature(%v) failed", sigBuf) 598 } 599 600 // Test that good data and signature pass verification 601 if err := pk.Verify(dataBuf, sig); err != nil { 602 t.Errorf("%s: PublicKey.Verify(%v, %v) failed: %v", d.Name, dataBuf, sig, err) 603 } 604 605 // Invalid data being passed in 606 invalidData := []byte("INVALID DATA") 607 if err := pk.Verify(invalidData, sig); err == nil { 608 t.Errorf("%s with invalid data: PublicKey.Verify(%v, %v) passed unexpectedly", d.Name, invalidData, sig) 609 } 610 611 // Change byte in blob to corrup signature 612 sig.Blob[5] = byte('A') 613 // Corrupted data being passed in 614 if err := pk.Verify(dataBuf, sig); err == nil { 615 t.Errorf("%s with corrupted signature: PublicKey.Verify(%v, %v) passed unexpectedly", d.Name, dataBuf, sig) 616 } 617 } 618 }