github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/src/crypto/tls/handshake_client_test.go (about) 1 // Copyright 2010 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 tls 6 7 import ( 8 "bytes" 9 "crypto/ecdsa" 10 "crypto/rsa" 11 "crypto/x509" 12 "encoding/pem" 13 "fmt" 14 "io" 15 "net" 16 "os" 17 "os/exec" 18 "path/filepath" 19 "strconv" 20 "testing" 21 "time" 22 ) 23 24 // Note: see comment in handshake_test.go for details of how the reference 25 // tests work. 26 27 // blockingSource is an io.Reader that blocks a Read call until it's closed. 28 type blockingSource chan bool 29 30 func (b blockingSource) Read([]byte) (n int, err error) { 31 <-b 32 return 0, io.EOF 33 } 34 35 // clientTest represents a test of the TLS client handshake against a reference 36 // implementation. 37 type clientTest struct { 38 // name is a freeform string identifying the test and the file in which 39 // the expected results will be stored. 40 name string 41 // command, if not empty, contains a series of arguments for the 42 // command to run for the reference server. 43 command []string 44 // config, if not nil, contains a custom Config to use for this test. 45 config *Config 46 // cert, if not empty, contains a DER-encoded certificate for the 47 // reference server. 48 cert []byte 49 // key, if not nil, contains either a *rsa.PrivateKey or 50 // *ecdsa.PrivateKey which is the private key for the reference server. 51 key interface{} 52 // validate, if not nil, is a function that will be called with the 53 // ConnectionState of the resulting connection. It returns a non-nil 54 // error if the ConnectionState is unacceptable. 55 validate func(ConnectionState) error 56 } 57 58 var defaultServerCommand = []string{"openssl", "s_server"} 59 60 // connFromCommand starts the reference server process, connects to it and 61 // returns a recordingConn for the connection. The stdin return value is a 62 // blockingSource for the stdin of the child process. It must be closed before 63 // Waiting for child. 64 func (test *clientTest) connFromCommand() (conn *recordingConn, child *exec.Cmd, stdin blockingSource, err error) { 65 cert := testRSACertificate 66 if len(test.cert) > 0 { 67 cert = test.cert 68 } 69 certPath := tempFile(string(cert)) 70 defer os.Remove(certPath) 71 72 var key interface{} = testRSAPrivateKey 73 if test.key != nil { 74 key = test.key 75 } 76 var pemType string 77 var derBytes []byte 78 switch key := key.(type) { 79 case *rsa.PrivateKey: 80 pemType = "RSA" 81 derBytes = x509.MarshalPKCS1PrivateKey(key) 82 case *ecdsa.PrivateKey: 83 pemType = "EC" 84 var err error 85 derBytes, err = x509.MarshalECPrivateKey(key) 86 if err != nil { 87 panic(err) 88 } 89 default: 90 panic("unknown key type") 91 } 92 93 var pemOut bytes.Buffer 94 pem.Encode(&pemOut, &pem.Block{Type: pemType + " PRIVATE KEY", Bytes: derBytes}) 95 96 keyPath := tempFile(string(pemOut.Bytes())) 97 defer os.Remove(keyPath) 98 99 var command []string 100 if len(test.command) > 0 { 101 command = append(command, test.command...) 102 } else { 103 command = append(command, defaultServerCommand...) 104 } 105 command = append(command, "-cert", certPath, "-certform", "DER", "-key", keyPath) 106 // serverPort contains the port that OpenSSL will listen on. OpenSSL 107 // can't take "0" as an argument here so we have to pick a number and 108 // hope that it's not in use on the machine. Since this only occurs 109 // when -update is given and thus when there's a human watching the 110 // test, this isn't too bad. 111 const serverPort = 24323 112 command = append(command, "-accept", strconv.Itoa(serverPort)) 113 114 cmd := exec.Command(command[0], command[1:]...) 115 stdin = blockingSource(make(chan bool)) 116 cmd.Stdin = stdin 117 var out bytes.Buffer 118 cmd.Stdout = &out 119 cmd.Stderr = &out 120 if err := cmd.Start(); err != nil { 121 return nil, nil, nil, err 122 } 123 124 // OpenSSL does print an "ACCEPT" banner, but it does so *before* 125 // opening the listening socket, so we can't use that to wait until it 126 // has started listening. Thus we are forced to poll until we get a 127 // connection. 128 var tcpConn net.Conn 129 for i := uint(0); i < 5; i++ { 130 var err error 131 tcpConn, err = net.DialTCP("tcp", nil, &net.TCPAddr{ 132 IP: net.IPv4(127, 0, 0, 1), 133 Port: serverPort, 134 }) 135 if err == nil { 136 break 137 } 138 time.Sleep((1 << i) * 5 * time.Millisecond) 139 } 140 if tcpConn == nil { 141 close(stdin) 142 out.WriteTo(os.Stdout) 143 cmd.Process.Kill() 144 return nil, nil, nil, cmd.Wait() 145 } 146 147 record := &recordingConn{ 148 Conn: tcpConn, 149 } 150 151 return record, cmd, stdin, nil 152 } 153 154 func (test *clientTest) dataPath() string { 155 return filepath.Join("testdata", "Client-"+test.name) 156 } 157 158 func (test *clientTest) loadData() (flows [][]byte, err error) { 159 in, err := os.Open(test.dataPath()) 160 if err != nil { 161 return nil, err 162 } 163 defer in.Close() 164 return parseTestData(in) 165 } 166 167 func (test *clientTest) run(t *testing.T, write bool) { 168 var clientConn, serverConn net.Conn 169 var recordingConn *recordingConn 170 var childProcess *exec.Cmd 171 var stdin blockingSource 172 173 if write { 174 var err error 175 recordingConn, childProcess, stdin, err = test.connFromCommand() 176 if err != nil { 177 t.Fatalf("Failed to start subcommand: %s", err) 178 } 179 clientConn = recordingConn 180 } else { 181 clientConn, serverConn = net.Pipe() 182 } 183 184 config := test.config 185 if config == nil { 186 config = testConfig 187 } 188 client := Client(clientConn, config) 189 190 doneChan := make(chan bool) 191 go func() { 192 if _, err := client.Write([]byte("hello\n")); err != nil { 193 t.Logf("Client.Write failed: %s", err) 194 } 195 if test.validate != nil { 196 if err := test.validate(client.ConnectionState()); err != nil { 197 t.Logf("validate callback returned error: %s", err) 198 } 199 } 200 client.Close() 201 clientConn.Close() 202 doneChan <- true 203 }() 204 205 if !write { 206 flows, err := test.loadData() 207 if err != nil { 208 t.Fatalf("%s: failed to load data from %s: %v", test.name, test.dataPath(), err) 209 } 210 for i, b := range flows { 211 if i%2 == 1 { 212 serverConn.Write(b) 213 continue 214 } 215 bb := make([]byte, len(b)) 216 _, err := io.ReadFull(serverConn, bb) 217 if err != nil { 218 t.Fatalf("%s #%d: %s", test.name, i, err) 219 } 220 if !bytes.Equal(b, bb) { 221 t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", test.name, i, bb, b) 222 } 223 } 224 serverConn.Close() 225 } 226 227 <-doneChan 228 229 if write { 230 path := test.dataPath() 231 out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 232 if err != nil { 233 t.Fatalf("Failed to create output file: %s", err) 234 } 235 defer out.Close() 236 recordingConn.Close() 237 close(stdin) 238 childProcess.Process.Kill() 239 childProcess.Wait() 240 if len(recordingConn.flows) < 3 { 241 childProcess.Stdout.(*bytes.Buffer).WriteTo(os.Stdout) 242 t.Fatalf("Client connection didn't work") 243 } 244 recordingConn.WriteTo(out) 245 fmt.Printf("Wrote %s\n", path) 246 } 247 } 248 249 func runClientTestForVersion(t *testing.T, template *clientTest, prefix, option string) { 250 test := *template 251 test.name = prefix + test.name 252 if len(test.command) == 0 { 253 test.command = defaultClientCommand 254 } 255 test.command = append([]string(nil), test.command...) 256 test.command = append(test.command, option) 257 test.run(t, *update) 258 } 259 260 func runClientTestTLS10(t *testing.T, template *clientTest) { 261 runClientTestForVersion(t, template, "TLSv10-", "-tls1") 262 } 263 264 func runClientTestTLS11(t *testing.T, template *clientTest) { 265 runClientTestForVersion(t, template, "TLSv11-", "-tls1_1") 266 } 267 268 func runClientTestTLS12(t *testing.T, template *clientTest) { 269 runClientTestForVersion(t, template, "TLSv12-", "-tls1_2") 270 } 271 272 func TestHandshakeClientRSARC4(t *testing.T) { 273 test := &clientTest{ 274 name: "RSA-RC4", 275 command: []string{"openssl", "s_server", "-cipher", "RC4-SHA"}, 276 } 277 runClientTestTLS10(t, test) 278 runClientTestTLS11(t, test) 279 runClientTestTLS12(t, test) 280 } 281 282 func TestHandshakeClientECDHERSAAES(t *testing.T) { 283 test := &clientTest{ 284 name: "ECDHE-RSA-AES", 285 command: []string{"openssl", "s_server", "-cipher", "ECDHE-RSA-AES128-SHA"}, 286 } 287 runClientTestTLS10(t, test) 288 runClientTestTLS11(t, test) 289 runClientTestTLS12(t, test) 290 } 291 292 func TestHandshakeClientECDHEECDSAAES(t *testing.T) { 293 test := &clientTest{ 294 name: "ECDHE-ECDSA-AES", 295 command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA"}, 296 cert: testECDSACertificate, 297 key: testECDSAPrivateKey, 298 } 299 runClientTestTLS10(t, test) 300 runClientTestTLS11(t, test) 301 runClientTestTLS12(t, test) 302 } 303 304 func TestHandshakeClientECDHEECDSAAESGCM(t *testing.T) { 305 test := &clientTest{ 306 name: "ECDHE-ECDSA-AES-GCM", 307 command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-GCM-SHA256"}, 308 cert: testECDSACertificate, 309 key: testECDSAPrivateKey, 310 } 311 runClientTestTLS12(t, test) 312 } 313 314 func TestHandshakeClientCertRSA(t *testing.T) { 315 config := *testConfig 316 cert, _ := X509KeyPair([]byte(clientCertificatePEM), []byte(clientKeyPEM)) 317 config.Certificates = []Certificate{cert} 318 319 test := &clientTest{ 320 name: "ClientCert-RSA-RSA", 321 command: []string{"openssl", "s_server", "-cipher", "RC4-SHA", "-verify", "1"}, 322 config: &config, 323 } 324 325 runClientTestTLS10(t, test) 326 runClientTestTLS12(t, test) 327 328 test = &clientTest{ 329 name: "ClientCert-RSA-ECDSA", 330 command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA", "-verify", "1"}, 331 config: &config, 332 cert: testECDSACertificate, 333 key: testECDSAPrivateKey, 334 } 335 336 runClientTestTLS10(t, test) 337 runClientTestTLS12(t, test) 338 } 339 340 func TestHandshakeClientCertECDSA(t *testing.T) { 341 config := *testConfig 342 cert, _ := X509KeyPair([]byte(clientECDSACertificatePEM), []byte(clientECDSAKeyPEM)) 343 config.Certificates = []Certificate{cert} 344 345 test := &clientTest{ 346 name: "ClientCert-ECDSA-RSA", 347 command: []string{"openssl", "s_server", "-cipher", "RC4-SHA", "-verify", "1"}, 348 config: &config, 349 } 350 351 runClientTestTLS10(t, test) 352 runClientTestTLS12(t, test) 353 354 test = &clientTest{ 355 name: "ClientCert-ECDSA-ECDSA", 356 command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA", "-verify", "1"}, 357 config: &config, 358 cert: testECDSACertificate, 359 key: testECDSAPrivateKey, 360 } 361 362 runClientTestTLS10(t, test) 363 runClientTestTLS12(t, test) 364 } 365 366 func TestClientResumption(t *testing.T) { 367 serverConfig := &Config{ 368 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA}, 369 Certificates: testConfig.Certificates, 370 } 371 clientConfig := &Config{ 372 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, 373 InsecureSkipVerify: true, 374 ClientSessionCache: NewLRUClientSessionCache(32), 375 } 376 377 testResumeState := func(test string, didResume bool) { 378 hs, err := testHandshake(clientConfig, serverConfig) 379 if err != nil { 380 t.Fatalf("%s: handshake failed: %s", test, err) 381 } 382 if hs.DidResume != didResume { 383 t.Fatalf("%s resumed: %v, expected: %v", test, hs.DidResume, didResume) 384 } 385 } 386 387 testResumeState("Handshake", false) 388 testResumeState("Resume", true) 389 390 if _, err := io.ReadFull(serverConfig.rand(), serverConfig.SessionTicketKey[:]); err != nil { 391 t.Fatalf("Failed to invalidate SessionTicketKey") 392 } 393 testResumeState("InvalidSessionTicketKey", false) 394 testResumeState("ResumeAfterInvalidSessionTicketKey", true) 395 396 clientConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA} 397 testResumeState("DifferentCipherSuite", false) 398 testResumeState("DifferentCipherSuiteRecovers", true) 399 400 clientConfig.ClientSessionCache = nil 401 testResumeState("WithoutSessionCache", false) 402 } 403 404 func TestLRUClientSessionCache(t *testing.T) { 405 // Initialize cache of capacity 4. 406 cache := NewLRUClientSessionCache(4) 407 cs := make([]ClientSessionState, 6) 408 keys := []string{"0", "1", "2", "3", "4", "5", "6"} 409 410 // Add 4 entries to the cache and look them up. 411 for i := 0; i < 4; i++ { 412 cache.Put(keys[i], &cs[i]) 413 } 414 for i := 0; i < 4; i++ { 415 if s, ok := cache.Get(keys[i]); !ok || s != &cs[i] { 416 t.Fatalf("session cache failed lookup for added key: %s", keys[i]) 417 } 418 } 419 420 // Add 2 more entries to the cache. First 2 should be evicted. 421 for i := 4; i < 6; i++ { 422 cache.Put(keys[i], &cs[i]) 423 } 424 for i := 0; i < 2; i++ { 425 if s, ok := cache.Get(keys[i]); ok || s != nil { 426 t.Fatalf("session cache should have evicted key: %s", keys[i]) 427 } 428 } 429 430 // Touch entry 2. LRU should evict 3 next. 431 cache.Get(keys[2]) 432 cache.Put(keys[0], &cs[0]) 433 if s, ok := cache.Get(keys[3]); ok || s != nil { 434 t.Fatalf("session cache should have evicted key 3") 435 } 436 437 // Update entry 0 in place. 438 cache.Put(keys[0], &cs[3]) 439 if s, ok := cache.Get(keys[0]); !ok || s != &cs[3] { 440 t.Fatalf("session cache failed update for key 0") 441 } 442 443 // Adding a nil entry is valid. 444 cache.Put(keys[0], nil) 445 if s, ok := cache.Get(keys[0]); !ok || s != nil { 446 t.Fatalf("failed to add nil entry to cache") 447 } 448 } 449 450 func TestHandshakeClientALPNMatch(t *testing.T) { 451 config := *testConfig 452 config.NextProtos = []string{"proto2", "proto1"} 453 454 test := &clientTest{ 455 name: "ALPN", 456 // Note that this needs OpenSSL 1.0.2 because that is the first 457 // version that supports the -alpn flag. 458 command: []string{"openssl", "s_server", "-alpn", "proto1,proto2"}, 459 config: &config, 460 validate: func(state ConnectionState) error { 461 // The server's preferences should override the client. 462 if state.NegotiatedProtocol != "proto1" { 463 return fmt.Errorf("Got protocol %q, wanted proto1", state.NegotiatedProtocol) 464 } 465 return nil 466 }, 467 } 468 runClientTestTLS12(t, test) 469 } 470 471 func TestHandshakeClientALPNNoMatch(t *testing.T) { 472 config := *testConfig 473 config.NextProtos = []string{"proto3"} 474 475 test := &clientTest{ 476 name: "ALPN-NoMatch", 477 // Note that this needs OpenSSL 1.0.2 because that is the first 478 // version that supports the -alpn flag. 479 command: []string{"openssl", "s_server", "-alpn", "proto1,proto2"}, 480 config: &config, 481 validate: func(state ConnectionState) error { 482 // There's no overlap so OpenSSL will not select a protocol. 483 if state.NegotiatedProtocol != "" { 484 return fmt.Errorf("Got protocol %q, wanted ''", state.NegotiatedProtocol) 485 } 486 return nil 487 }, 488 } 489 runClientTestTLS12(t, test) 490 }