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