Adam Langley | 4883b73 | 2010-12-16 17:10:50 -0500 | [diff] [blame] | 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" |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 9 | "crypto/ecdsa" |
| 10 | "crypto/rsa" |
| 11 | "crypto/x509" |
| 12 | "encoding/pem" |
| 13 | "fmt" |
Adam Langley | 4883b73 | 2010-12-16 17:10:50 -0500 | [diff] [blame] | 14 | "io" |
| 15 | "net" |
Adam Langley | 7247dca | 2012-04-11 12:55:57 -0400 | [diff] [blame] | 16 | "os" |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 17 | "os/exec" |
| 18 | "path/filepath" |
| 19 | "strconv" |
Adam Langley | 4883b73 | 2010-12-16 17:10:50 -0500 | [diff] [blame] | 20 | "testing" |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 21 | "time" |
Adam Langley | 4883b73 | 2010-12-16 17:10:50 -0500 | [diff] [blame] | 22 | ) |
| 23 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 24 | // Note: see comment in handshake_test.go for details of how the reference |
| 25 | // tests work. |
Adam Langley | 4883b73 | 2010-12-16 17:10:50 -0500 | [diff] [blame] | 26 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 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{} |
Adam Langley | d0e255f | 2014-08-05 11:36:20 -0700 | [diff] [blame] | 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 |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 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) |
Adam Langley | 4883b73 | 2010-12-16 17:10:50 -0500 | [diff] [blame] | 86 | if err != nil { |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 87 | panic(err) |
Adam Langley | 4883b73 | 2010-12-16 17:10:50 -0500 | [diff] [blame] | 88 | } |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 89 | default: |
| 90 | panic("unknown key type") |
Adam Langley | 4883b73 | 2010-12-16 17:10:50 -0500 | [diff] [blame] | 91 | } |
| 92 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 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++ { |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 130 | tcpConn, err = net.DialTCP("tcp", nil, &net.TCPAddr{ |
| 131 | IP: net.IPv4(127, 0, 0, 1), |
| 132 | Port: serverPort, |
| 133 | }) |
| 134 | if err == nil { |
| 135 | break |
| 136 | } |
| 137 | time.Sleep((1 << i) * 5 * time.Millisecond) |
| 138 | } |
Jacob H. Haven | f1d669a | 2015-02-03 16:15:18 -0800 | [diff] [blame^] | 139 | if err != nil { |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 140 | close(stdin) |
| 141 | out.WriteTo(os.Stdout) |
| 142 | cmd.Process.Kill() |
| 143 | return nil, nil, nil, cmd.Wait() |
Adam Langley | 4883b73 | 2010-12-16 17:10:50 -0500 | [diff] [blame] | 144 | } |
| 145 | |
Adam Langley | 7247dca | 2012-04-11 12:55:57 -0400 | [diff] [blame] | 146 | record := &recordingConn{ |
| 147 | Conn: tcpConn, |
| 148 | } |
| 149 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 150 | return record, cmd, stdin, nil |
Adam Langley | 4883b73 | 2010-12-16 17:10:50 -0500 | [diff] [blame] | 151 | } |
| 152 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 153 | func (test *clientTest) dataPath() string { |
| 154 | return filepath.Join("testdata", "Client-"+test.name) |
| 155 | } |
Adam Langley | a1dbfee | 2013-05-15 10:25:54 -0400 | [diff] [blame] | 156 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 157 | func (test *clientTest) loadData() (flows [][]byte, err error) { |
| 158 | in, err := os.Open(test.dataPath()) |
| 159 | if err != nil { |
| 160 | return nil, err |
| 161 | } |
| 162 | defer in.Close() |
| 163 | return parseTestData(in) |
| 164 | } |
Adam Langley | a1dbfee | 2013-05-15 10:25:54 -0400 | [diff] [blame] | 165 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 166 | func (test *clientTest) run(t *testing.T, write bool) { |
| 167 | var clientConn, serverConn net.Conn |
| 168 | var recordingConn *recordingConn |
| 169 | var childProcess *exec.Cmd |
| 170 | var stdin blockingSource |
| 171 | |
| 172 | if write { |
| 173 | var err error |
| 174 | recordingConn, childProcess, stdin, err = test.connFromCommand() |
Adam Langley | a1dbfee | 2013-05-15 10:25:54 -0400 | [diff] [blame] | 175 | if err != nil { |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 176 | t.Fatalf("Failed to start subcommand: %s", err) |
Adam Langley | a1dbfee | 2013-05-15 10:25:54 -0400 | [diff] [blame] | 177 | } |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 178 | clientConn = recordingConn |
| 179 | } else { |
| 180 | clientConn, serverConn = net.Pipe() |
| 181 | } |
| 182 | |
| 183 | config := test.config |
| 184 | if config == nil { |
| 185 | config = testConfig |
| 186 | } |
| 187 | client := Client(clientConn, config) |
| 188 | |
| 189 | doneChan := make(chan bool) |
| 190 | go func() { |
| 191 | if _, err := client.Write([]byte("hello\n")); err != nil { |
Jacob H. Haven | f1d669a | 2015-02-03 16:15:18 -0800 | [diff] [blame^] | 192 | t.Errorf("Client.Write failed: %s", err) |
Adam Langley | a1dbfee | 2013-05-15 10:25:54 -0400 | [diff] [blame] | 193 | } |
Adam Langley | d0e255f | 2014-08-05 11:36:20 -0700 | [diff] [blame] | 194 | if test.validate != nil { |
| 195 | if err := test.validate(client.ConnectionState()); err != nil { |
| 196 | t.Logf("validate callback returned error: %s", err) |
| 197 | } |
| 198 | } |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 199 | client.Close() |
| 200 | clientConn.Close() |
| 201 | doneChan <- true |
Adam Langley | a1dbfee | 2013-05-15 10:25:54 -0400 | [diff] [blame] | 202 | }() |
| 203 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 204 | if !write { |
| 205 | flows, err := test.loadData() |
Adam Langley | a1dbfee | 2013-05-15 10:25:54 -0400 | [diff] [blame] | 206 | if err != nil { |
Russ Cox | 2c14dbe | 2014-09-07 09:07:19 -0400 | [diff] [blame] | 207 | t.Fatalf("%s: failed to load data from %s: %v", test.name, test.dataPath(), err) |
Adam Langley | a1dbfee | 2013-05-15 10:25:54 -0400 | [diff] [blame] | 208 | } |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 209 | for i, b := range flows { |
| 210 | if i%2 == 1 { |
| 211 | serverConn.Write(b) |
| 212 | continue |
| 213 | } |
| 214 | bb := make([]byte, len(b)) |
| 215 | _, err := io.ReadFull(serverConn, bb) |
| 216 | if err != nil { |
| 217 | t.Fatalf("%s #%d: %s", test.name, i, err) |
| 218 | } |
| 219 | if !bytes.Equal(b, bb) { |
| 220 | t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", test.name, i, bb, b) |
| 221 | } |
Adam Langley | a1dbfee | 2013-05-15 10:25:54 -0400 | [diff] [blame] | 222 | } |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 223 | serverConn.Close() |
| 224 | } |
| 225 | |
| 226 | <-doneChan |
| 227 | |
| 228 | if write { |
| 229 | path := test.dataPath() |
| 230 | out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) |
| 231 | if err != nil { |
| 232 | t.Fatalf("Failed to create output file: %s", err) |
| 233 | } |
| 234 | defer out.Close() |
| 235 | recordingConn.Close() |
| 236 | close(stdin) |
| 237 | childProcess.Process.Kill() |
| 238 | childProcess.Wait() |
| 239 | if len(recordingConn.flows) < 3 { |
| 240 | childProcess.Stdout.(*bytes.Buffer).WriteTo(os.Stdout) |
| 241 | t.Fatalf("Client connection didn't work") |
| 242 | } |
| 243 | recordingConn.WriteTo(out) |
| 244 | fmt.Printf("Wrote %s\n", path) |
Adam Langley | a1dbfee | 2013-05-15 10:25:54 -0400 | [diff] [blame] | 245 | } |
| 246 | } |
| 247 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 248 | func runClientTestForVersion(t *testing.T, template *clientTest, prefix, option string) { |
| 249 | test := *template |
| 250 | test.name = prefix + test.name |
| 251 | if len(test.command) == 0 { |
| 252 | test.command = defaultClientCommand |
| 253 | } |
| 254 | test.command = append([]string(nil), test.command...) |
| 255 | test.command = append(test.command, option) |
| 256 | test.run(t, *update) |
Adam Langley | 4883b73 | 2010-12-16 17:10:50 -0500 | [diff] [blame] | 257 | } |
Adam Langley | 7247dca | 2012-04-11 12:55:57 -0400 | [diff] [blame] | 258 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 259 | func runClientTestTLS10(t *testing.T, template *clientTest) { |
| 260 | runClientTestForVersion(t, template, "TLSv10-", "-tls1") |
Adam Langley | 7247dca | 2012-04-11 12:55:57 -0400 | [diff] [blame] | 261 | } |
Adam Langley | a1dbfee | 2013-05-15 10:25:54 -0400 | [diff] [blame] | 262 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 263 | func runClientTestTLS11(t *testing.T, template *clientTest) { |
| 264 | runClientTestForVersion(t, template, "TLSv11-", "-tls1_1") |
Adam Langley | a1dbfee | 2013-05-15 10:25:54 -0400 | [diff] [blame] | 265 | } |
John Shahid | ca986a2 | 2013-05-29 11:21:32 -0400 | [diff] [blame] | 266 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 267 | func runClientTestTLS12(t *testing.T, template *clientTest) { |
| 268 | runClientTestForVersion(t, template, "TLSv12-", "-tls1_2") |
Adam Langley | 2112fed | 2013-06-04 20:02:22 -0400 | [diff] [blame] | 269 | } |
| 270 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 271 | func TestHandshakeClientRSARC4(t *testing.T) { |
| 272 | test := &clientTest{ |
| 273 | name: "RSA-RC4", |
| 274 | command: []string{"openssl", "s_server", "-cipher", "RC4-SHA"}, |
| 275 | } |
| 276 | runClientTestTLS10(t, test) |
| 277 | runClientTestTLS11(t, test) |
| 278 | runClientTestTLS12(t, test) |
John Shahid | ca986a2 | 2013-05-29 11:21:32 -0400 | [diff] [blame] | 279 | } |
| 280 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 281 | func TestHandshakeClientECDHERSAAES(t *testing.T) { |
| 282 | test := &clientTest{ |
| 283 | name: "ECDHE-RSA-AES", |
| 284 | command: []string{"openssl", "s_server", "-cipher", "ECDHE-RSA-AES128-SHA"}, |
| 285 | } |
| 286 | runClientTestTLS10(t, test) |
| 287 | runClientTestTLS11(t, test) |
| 288 | runClientTestTLS12(t, test) |
Adam Langley | 7e76779 | 2013-07-02 19:58:56 -0400 | [diff] [blame] | 289 | } |
| 290 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 291 | func TestHandshakeClientECDHEECDSAAES(t *testing.T) { |
| 292 | test := &clientTest{ |
| 293 | name: "ECDHE-ECDSA-AES", |
| 294 | command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA"}, |
| 295 | cert: testECDSACertificate, |
| 296 | key: testECDSAPrivateKey, |
| 297 | } |
| 298 | runClientTestTLS10(t, test) |
| 299 | runClientTestTLS11(t, test) |
| 300 | runClientTestTLS12(t, test) |
Adam Langley | 6a1022a | 2013-09-16 16:39:42 -0400 | [diff] [blame] | 301 | } |
| 302 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 303 | func TestHandshakeClientECDHEECDSAAESGCM(t *testing.T) { |
| 304 | test := &clientTest{ |
| 305 | name: "ECDHE-ECDSA-AES-GCM", |
| 306 | command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-GCM-SHA256"}, |
| 307 | cert: testECDSACertificate, |
| 308 | key: testECDSAPrivateKey, |
| 309 | } |
| 310 | runClientTestTLS12(t, test) |
| 311 | } |
Joel Sing | 7b7dac5 | 2013-07-17 12:33:16 -0400 | [diff] [blame] | 312 | |
Jacob H. Haven | f1d669a | 2015-02-03 16:15:18 -0800 | [diff] [blame^] | 313 | func TestHandshakeClientAES256GCMSHA384(t *testing.T) { |
| 314 | test := &clientTest{ |
| 315 | name: "ECDHE-ECDSA-AES256-GCM-SHA384", |
| 316 | command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES256-GCM-SHA384"}, |
| 317 | cert: testECDSACertificate, |
| 318 | key: testECDSAPrivateKey, |
| 319 | } |
| 320 | runClientTestTLS12(t, test) |
| 321 | } |
| 322 | |
Adam Langley | 6f14949 | 2013-12-20 11:37:05 -0500 | [diff] [blame] | 323 | func TestHandshakeClientCertRSA(t *testing.T) { |
| 324 | config := *testConfig |
| 325 | cert, _ := X509KeyPair([]byte(clientCertificatePEM), []byte(clientKeyPEM)) |
| 326 | config.Certificates = []Certificate{cert} |
| 327 | |
| 328 | test := &clientTest{ |
| 329 | name: "ClientCert-RSA-RSA", |
| 330 | command: []string{"openssl", "s_server", "-cipher", "RC4-SHA", "-verify", "1"}, |
| 331 | config: &config, |
| 332 | } |
| 333 | |
| 334 | runClientTestTLS10(t, test) |
| 335 | runClientTestTLS12(t, test) |
| 336 | |
| 337 | test = &clientTest{ |
| 338 | name: "ClientCert-RSA-ECDSA", |
| 339 | command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA", "-verify", "1"}, |
| 340 | config: &config, |
| 341 | cert: testECDSACertificate, |
| 342 | key: testECDSAPrivateKey, |
| 343 | } |
| 344 | |
| 345 | runClientTestTLS10(t, test) |
| 346 | runClientTestTLS12(t, test) |
| 347 | } |
| 348 | |
| 349 | func TestHandshakeClientCertECDSA(t *testing.T) { |
| 350 | config := *testConfig |
| 351 | cert, _ := X509KeyPair([]byte(clientECDSACertificatePEM), []byte(clientECDSAKeyPEM)) |
| 352 | config.Certificates = []Certificate{cert} |
| 353 | |
| 354 | test := &clientTest{ |
| 355 | name: "ClientCert-ECDSA-RSA", |
| 356 | command: []string{"openssl", "s_server", "-cipher", "RC4-SHA", "-verify", "1"}, |
| 357 | config: &config, |
| 358 | } |
| 359 | |
| 360 | runClientTestTLS10(t, test) |
| 361 | runClientTestTLS12(t, test) |
| 362 | |
| 363 | test = &clientTest{ |
| 364 | name: "ClientCert-ECDSA-ECDSA", |
| 365 | command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA", "-verify", "1"}, |
| 366 | config: &config, |
| 367 | cert: testECDSACertificate, |
| 368 | key: testECDSAPrivateKey, |
| 369 | } |
| 370 | |
| 371 | runClientTestTLS10(t, test) |
| 372 | runClientTestTLS12(t, test) |
Joel Sing | 7b7dac5 | 2013-07-17 12:33:16 -0400 | [diff] [blame] | 373 | } |
Gautham Thambidorai | 988ffc0 | 2014-01-22 18:24:03 -0500 | [diff] [blame] | 374 | |
| 375 | func TestClientResumption(t *testing.T) { |
| 376 | serverConfig := &Config{ |
| 377 | CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA}, |
| 378 | Certificates: testConfig.Certificates, |
| 379 | } |
| 380 | clientConfig := &Config{ |
| 381 | CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, |
| 382 | InsecureSkipVerify: true, |
| 383 | ClientSessionCache: NewLRUClientSessionCache(32), |
| 384 | } |
| 385 | |
| 386 | testResumeState := func(test string, didResume bool) { |
| 387 | hs, err := testHandshake(clientConfig, serverConfig) |
| 388 | if err != nil { |
| 389 | t.Fatalf("%s: handshake failed: %s", test, err) |
| 390 | } |
| 391 | if hs.DidResume != didResume { |
| 392 | t.Fatalf("%s resumed: %v, expected: %v", test, hs.DidResume, didResume) |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | testResumeState("Handshake", false) |
| 397 | testResumeState("Resume", true) |
| 398 | |
| 399 | if _, err := io.ReadFull(serverConfig.rand(), serverConfig.SessionTicketKey[:]); err != nil { |
| 400 | t.Fatalf("Failed to invalidate SessionTicketKey") |
| 401 | } |
| 402 | testResumeState("InvalidSessionTicketKey", false) |
| 403 | testResumeState("ResumeAfterInvalidSessionTicketKey", true) |
| 404 | |
| 405 | clientConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA} |
| 406 | testResumeState("DifferentCipherSuite", false) |
| 407 | testResumeState("DifferentCipherSuiteRecovers", true) |
| 408 | |
| 409 | clientConfig.ClientSessionCache = nil |
| 410 | testResumeState("WithoutSessionCache", false) |
| 411 | } |
| 412 | |
| 413 | func TestLRUClientSessionCache(t *testing.T) { |
| 414 | // Initialize cache of capacity 4. |
| 415 | cache := NewLRUClientSessionCache(4) |
| 416 | cs := make([]ClientSessionState, 6) |
| 417 | keys := []string{"0", "1", "2", "3", "4", "5", "6"} |
| 418 | |
| 419 | // Add 4 entries to the cache and look them up. |
| 420 | for i := 0; i < 4; i++ { |
| 421 | cache.Put(keys[i], &cs[i]) |
| 422 | } |
| 423 | for i := 0; i < 4; i++ { |
| 424 | if s, ok := cache.Get(keys[i]); !ok || s != &cs[i] { |
| 425 | t.Fatalf("session cache failed lookup for added key: %s", keys[i]) |
| 426 | } |
| 427 | } |
| 428 | |
| 429 | // Add 2 more entries to the cache. First 2 should be evicted. |
| 430 | for i := 4; i < 6; i++ { |
| 431 | cache.Put(keys[i], &cs[i]) |
| 432 | } |
| 433 | for i := 0; i < 2; i++ { |
| 434 | if s, ok := cache.Get(keys[i]); ok || s != nil { |
| 435 | t.Fatalf("session cache should have evicted key: %s", keys[i]) |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | // Touch entry 2. LRU should evict 3 next. |
| 440 | cache.Get(keys[2]) |
| 441 | cache.Put(keys[0], &cs[0]) |
| 442 | if s, ok := cache.Get(keys[3]); ok || s != nil { |
| 443 | t.Fatalf("session cache should have evicted key 3") |
| 444 | } |
| 445 | |
| 446 | // Update entry 0 in place. |
| 447 | cache.Put(keys[0], &cs[3]) |
| 448 | if s, ok := cache.Get(keys[0]); !ok || s != &cs[3] { |
| 449 | t.Fatalf("session cache failed update for key 0") |
| 450 | } |
| 451 | |
| 452 | // Adding a nil entry is valid. |
| 453 | cache.Put(keys[0], nil) |
| 454 | if s, ok := cache.Get(keys[0]); !ok || s != nil { |
| 455 | t.Fatalf("failed to add nil entry to cache") |
| 456 | } |
| 457 | } |
Adam Langley | d0e255f | 2014-08-05 11:36:20 -0700 | [diff] [blame] | 458 | |
| 459 | func TestHandshakeClientALPNMatch(t *testing.T) { |
| 460 | config := *testConfig |
| 461 | config.NextProtos = []string{"proto2", "proto1"} |
| 462 | |
| 463 | test := &clientTest{ |
| 464 | name: "ALPN", |
| 465 | // Note that this needs OpenSSL 1.0.2 because that is the first |
| 466 | // version that supports the -alpn flag. |
| 467 | command: []string{"openssl", "s_server", "-alpn", "proto1,proto2"}, |
| 468 | config: &config, |
| 469 | validate: func(state ConnectionState) error { |
| 470 | // The server's preferences should override the client. |
| 471 | if state.NegotiatedProtocol != "proto1" { |
| 472 | return fmt.Errorf("Got protocol %q, wanted proto1", state.NegotiatedProtocol) |
| 473 | } |
| 474 | return nil |
| 475 | }, |
| 476 | } |
| 477 | runClientTestTLS12(t, test) |
| 478 | } |
| 479 | |
| 480 | func TestHandshakeClientALPNNoMatch(t *testing.T) { |
| 481 | config := *testConfig |
| 482 | config.NextProtos = []string{"proto3"} |
| 483 | |
| 484 | test := &clientTest{ |
| 485 | name: "ALPN-NoMatch", |
| 486 | // Note that this needs OpenSSL 1.0.2 because that is the first |
| 487 | // version that supports the -alpn flag. |
| 488 | command: []string{"openssl", "s_server", "-alpn", "proto1,proto2"}, |
| 489 | config: &config, |
| 490 | validate: func(state ConnectionState) error { |
| 491 | // There's no overlap so OpenSSL will not select a protocol. |
| 492 | if state.NegotiatedProtocol != "" { |
| 493 | return fmt.Errorf("Got protocol %q, wanted ''", state.NegotiatedProtocol) |
| 494 | } |
| 495 | return nil |
| 496 | }, |
| 497 | } |
| 498 | runClientTestTLS12(t, test) |
| 499 | } |