blob: 701de81de570090deef0141b175ff5fa75680271 [file] [log] [blame]
Adam Langley95c29f32014-06-20 12:00:00 -07001package main
2
3import (
4 "bytes"
David Benjamin407a10c2014-07-16 12:58:59 -04005 "crypto/x509"
Adam Langley95c29f32014-06-20 12:00:00 -07006 "flag"
7 "fmt"
8 "io"
Kenny Root7fdeaf12014-08-05 15:23:37 -07009 "io/ioutil"
Adam Langley95c29f32014-06-20 12:00:00 -070010 "net"
11 "os"
12 "os/exec"
David Benjamin884fdf12014-08-02 15:28:23 -040013 "path"
David Benjamin2bc8e6f2014-08-02 15:22:37 -040014 "runtime"
Adam Langley95c29f32014-06-20 12:00:00 -070015 "strings"
16 "sync"
17 "syscall"
18)
19
20var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
21
David Benjamin025b3d32014-07-01 19:53:04 -040022const (
23 rsaCertificateFile = "cert.pem"
24 ecdsaCertificateFile = "ecdsa_cert.pem"
25)
26
27const (
28 rsaKeyFile = "key.pem"
29 ecdsaKeyFile = "ecdsa_key.pem"
30)
31
Adam Langley95c29f32014-06-20 12:00:00 -070032var rsaCertificate, ecdsaCertificate Certificate
33
34func initCertificates() {
35 var err error
David Benjamin025b3d32014-07-01 19:53:04 -040036 rsaCertificate, err = LoadX509KeyPair(rsaCertificateFile, rsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070037 if err != nil {
38 panic(err)
39 }
40
David Benjamin025b3d32014-07-01 19:53:04 -040041 ecdsaCertificate, err = LoadX509KeyPair(ecdsaCertificateFile, ecdsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070042 if err != nil {
43 panic(err)
44 }
45}
46
47var certificateOnce sync.Once
48
49func getRSACertificate() Certificate {
50 certificateOnce.Do(initCertificates)
51 return rsaCertificate
52}
53
54func getECDSACertificate() Certificate {
55 certificateOnce.Do(initCertificates)
56 return ecdsaCertificate
57}
58
David Benjamin025b3d32014-07-01 19:53:04 -040059type testType int
60
61const (
62 clientTest testType = iota
63 serverTest
64)
65
Adam Langley95c29f32014-06-20 12:00:00 -070066type testCase struct {
David Benjamin025b3d32014-07-01 19:53:04 -040067 testType testType
Adam Langley95c29f32014-06-20 12:00:00 -070068 name string
69 config Config
70 shouldFail bool
71 expectedError string
Adam Langleyac61fa32014-06-23 12:03:11 -070072 // expectedLocalError, if not empty, contains a substring that must be
73 // found in the local error.
74 expectedLocalError string
David Benjamin7e2e6cf2014-08-07 17:44:24 -040075 // expectedVersion, if non-zero, specifies the TLS version that must be
76 // negotiated.
77 expectedVersion uint16
Adam Langley80842bd2014-06-20 12:00:00 -070078 // messageLen is the length, in bytes, of the test message that will be
79 // sent.
80 messageLen int
David Benjamin025b3d32014-07-01 19:53:04 -040081 // certFile is the path to the certificate to use for the server.
82 certFile string
83 // keyFile is the path to the private key to use for the server.
84 keyFile string
David Benjamin1d5c83e2014-07-22 19:20:02 -040085 // resumeSession controls whether a second connection should be tested
86 // which resumes the first session.
87 resumeSession bool
David Benjamin325b5c32014-07-01 19:40:31 -040088 // flags, if not empty, contains a list of command-line flags that will
89 // be passed to the shim program.
90 flags []string
Adam Langley95c29f32014-06-20 12:00:00 -070091}
92
David Benjamin025b3d32014-07-01 19:53:04 -040093var testCases = []testCase{
Adam Langley95c29f32014-06-20 12:00:00 -070094 {
95 name: "BadRSASignature",
96 config: Config{
97 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
98 Bugs: ProtocolBugs{
99 InvalidSKXSignature: true,
100 },
101 },
102 shouldFail: true,
103 expectedError: ":BAD_SIGNATURE:",
104 },
105 {
106 name: "BadECDSASignature",
107 config: Config{
108 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
109 Bugs: ProtocolBugs{
110 InvalidSKXSignature: true,
111 },
112 Certificates: []Certificate{getECDSACertificate()},
113 },
114 shouldFail: true,
115 expectedError: ":BAD_SIGNATURE:",
116 },
117 {
118 name: "BadECDSACurve",
119 config: Config{
120 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
121 Bugs: ProtocolBugs{
122 InvalidSKXCurve: true,
123 },
124 Certificates: []Certificate{getECDSACertificate()},
125 },
126 shouldFail: true,
127 expectedError: ":WRONG_CURVE:",
128 },
Adam Langleyac61fa32014-06-23 12:03:11 -0700129 {
David Benjamina8e3e0e2014-08-06 22:11:10 -0400130 testType: serverTest,
131 name: "BadRSAVersion",
132 config: Config{
133 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
134 Bugs: ProtocolBugs{
135 RsaClientKeyExchangeVersion: VersionTLS11,
136 },
137 },
138 shouldFail: true,
139 expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
140 },
141 {
David Benjamin325b5c32014-07-01 19:40:31 -0400142 name: "NoFallbackSCSV",
Adam Langleyac61fa32014-06-23 12:03:11 -0700143 config: Config{
144 Bugs: ProtocolBugs{
145 FailIfNotFallbackSCSV: true,
146 },
147 },
148 shouldFail: true,
149 expectedLocalError: "no fallback SCSV found",
150 },
David Benjamin325b5c32014-07-01 19:40:31 -0400151 {
152 name: "FallbackSCSV",
153 config: Config{
154 Bugs: ProtocolBugs{
155 FailIfNotFallbackSCSV: true,
156 },
157 },
158 flags: []string{"-fallback-scsv"},
159 },
David Benjamin197b3ab2014-07-02 18:37:33 -0400160 {
161 testType: serverTest,
David Benjamin35a7a442014-07-05 00:23:20 -0400162 name: "ServerNameExtension",
David Benjamin197b3ab2014-07-02 18:37:33 -0400163 config: Config{
164 ServerName: "example.com",
165 },
166 flags: []string{"-expect-server-name", "example.com"},
167 },
David Benjamin35a7a442014-07-05 00:23:20 -0400168 {
169 testType: clientTest,
170 name: "DuplicateExtensionClient",
171 config: Config{
172 Bugs: ProtocolBugs{
173 DuplicateExtension: true,
174 },
175 },
176 shouldFail: true,
177 expectedLocalError: "remote error: error decoding message",
178 },
179 {
180 testType: serverTest,
181 name: "DuplicateExtensionServer",
182 config: Config{
183 Bugs: ProtocolBugs{
184 DuplicateExtension: true,
185 },
186 },
187 shouldFail: true,
188 expectedLocalError: "remote error: error decoding message",
189 },
David Benjamin7b030512014-07-08 17:30:11 -0400190 {
191 name: "ClientCertificateTypes",
192 config: Config{
193 ClientAuth: RequestClientCert,
194 ClientCertificateTypes: []byte{
195 CertTypeDSSSign,
196 CertTypeRSASign,
197 CertTypeECDSASign,
198 },
199 },
200 flags: []string{"-expect-certificate-types", string([]byte{
201 CertTypeDSSSign,
202 CertTypeRSASign,
203 CertTypeECDSASign,
204 })},
205 },
David Benjamin636293b2014-07-08 17:59:18 -0400206 {
207 name: "NoClientCertificate",
208 config: Config{
209 ClientAuth: RequireAnyClientCert,
210 },
211 shouldFail: true,
212 expectedLocalError: "client didn't provide a certificate",
213 },
David Benjamin1c375dd2014-07-12 00:48:23 -0400214 {
215 name: "UnauthenticatedECDH",
216 config: Config{
217 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
218 Bugs: ProtocolBugs{
219 UnauthenticatedECDH: true,
220 },
221 },
222 shouldFail: true,
David Benjamine8f3d662014-07-12 01:10:19 -0400223 expectedError: ":UNEXPECTED_MESSAGE:",
David Benjamin1c375dd2014-07-12 00:48:23 -0400224 },
David Benjamin9c651c92014-07-12 13:27:45 -0400225 {
226 name: "SkipServerKeyExchange",
227 config: Config{
228 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
229 Bugs: ProtocolBugs{
230 SkipServerKeyExchange: true,
231 },
232 },
233 shouldFail: true,
234 expectedError: ":UNEXPECTED_MESSAGE:",
235 },
David Benjamin1f5f62b2014-07-12 16:18:02 -0400236 {
David Benjamina0e52232014-07-19 17:39:58 -0400237 name: "SkipChangeCipherSpec-Client",
238 config: Config{
239 Bugs: ProtocolBugs{
240 SkipChangeCipherSpec: true,
241 },
242 },
243 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400244 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400245 },
246 {
247 testType: serverTest,
248 name: "SkipChangeCipherSpec-Server",
249 config: Config{
250 Bugs: ProtocolBugs{
251 SkipChangeCipherSpec: true,
252 },
253 },
254 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400255 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400256 },
David Benjamin42be6452014-07-21 14:50:23 -0400257 {
258 testType: serverTest,
259 name: "SkipChangeCipherSpec-Server-NPN",
260 config: Config{
261 NextProtos: []string{"bar"},
262 Bugs: ProtocolBugs{
263 SkipChangeCipherSpec: true,
264 },
265 },
266 flags: []string{
267 "-advertise-npn", "\x03foo\x03bar\x03baz",
268 },
269 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400270 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
271 },
272 {
273 name: "FragmentAcrossChangeCipherSpec-Client",
274 config: Config{
275 Bugs: ProtocolBugs{
276 FragmentAcrossChangeCipherSpec: true,
277 },
278 },
279 shouldFail: true,
280 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
281 },
282 {
283 testType: serverTest,
284 name: "FragmentAcrossChangeCipherSpec-Server",
285 config: Config{
286 Bugs: ProtocolBugs{
287 FragmentAcrossChangeCipherSpec: true,
288 },
289 },
290 shouldFail: true,
291 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
292 },
293 {
294 testType: serverTest,
295 name: "FragmentAcrossChangeCipherSpec-Server-NPN",
296 config: Config{
297 NextProtos: []string{"bar"},
298 Bugs: ProtocolBugs{
299 FragmentAcrossChangeCipherSpec: true,
300 },
301 },
302 flags: []string{
303 "-advertise-npn", "\x03foo\x03bar\x03baz",
304 },
305 shouldFail: true,
306 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamin42be6452014-07-21 14:50:23 -0400307 },
David Benjaminf3ec83d2014-07-21 22:42:34 -0400308 {
309 testType: serverTest,
310 name: "EarlyChangeCipherSpec-server-1",
311 config: Config{
312 Bugs: ProtocolBugs{
313 EarlyChangeCipherSpec: 1,
314 },
315 },
316 shouldFail: true,
317 expectedError: ":CCS_RECEIVED_EARLY:",
318 },
319 {
320 testType: serverTest,
321 name: "EarlyChangeCipherSpec-server-2",
322 config: Config{
323 Bugs: ProtocolBugs{
324 EarlyChangeCipherSpec: 2,
325 },
326 },
327 shouldFail: true,
328 expectedError: ":CCS_RECEIVED_EARLY:",
329 },
David Benjamind23f4122014-07-23 15:09:48 -0400330 {
David Benjamind23f4122014-07-23 15:09:48 -0400331 name: "SkipNewSessionTicket",
332 config: Config{
333 Bugs: ProtocolBugs{
334 SkipNewSessionTicket: true,
335 },
336 },
337 shouldFail: true,
338 expectedError: ":CCS_RECEIVED_EARLY:",
339 },
David Benjamin7e3305e2014-07-28 14:52:32 -0400340 {
David Benjamin7e3305e2014-07-28 14:52:32 -0400341 name: "FalseStart-SessionTicketsDisabled",
342 config: Config{
David Benjamind86c7672014-08-02 04:07:12 -0400343 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
344 NextProtos: []string{"foo"},
David Benjamin7e3305e2014-07-28 14:52:32 -0400345 SessionTicketsDisabled: true,
346 },
347 flags: []string{
348 "-false-start",
349 "-select-next-proto", "foo",
350 },
351 },
David Benjamind86c7672014-08-02 04:07:12 -0400352 {
353 testType: serverTest,
354 name: "SendV2ClientHello",
355 config: Config{
356 // Choose a cipher suite that does not involve
357 // elliptic curves, so no extensions are
358 // involved.
359 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
360 Bugs: ProtocolBugs{
361 SendV2ClientHello: true,
362 },
363 },
364 },
David Benjaminbef270a2014-08-02 04:22:02 -0400365 {
366 testType: serverTest,
367 name: "FallbackSCSV",
368 config: Config{
369 MaxVersion: VersionTLS11,
370 Bugs: ProtocolBugs{
371 SendFallbackSCSV: true,
372 },
373 },
374 shouldFail: true,
375 expectedError: ":INAPPROPRIATE_FALLBACK:",
376 },
377 {
378 testType: serverTest,
379 name: "FallbackSCSV-VersionMatch",
380 config: Config{
381 Bugs: ProtocolBugs{
382 SendFallbackSCSV: true,
383 },
384 },
385 },
Adam Langley95c29f32014-06-20 12:00:00 -0700386}
387
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400388func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int) error {
David Benjamin1d5c83e2014-07-22 19:20:02 -0400389 var tlsConn *Conn
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400390 if test.testType == clientTest {
David Benjamin1d5c83e2014-07-22 19:20:02 -0400391 tlsConn = Server(conn, config)
392 } else {
393 config.InsecureSkipVerify = true
394 tlsConn = Client(conn, config)
395 }
396
Adam Langley95c29f32014-06-20 12:00:00 -0700397 if err := tlsConn.Handshake(); err != nil {
398 return err
399 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700400
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400401 if vers := tlsConn.ConnectionState().Version; test.expectedVersion != 0 && vers != test.expectedVersion {
402 return fmt.Errorf("got version %x, expected %x", vers, test.expectedVersion)
403 }
404
Kenny Root7fdeaf12014-08-05 15:23:37 -0700405 if messageLen < 0 {
406 // Read until EOF.
407 _, err := io.Copy(ioutil.Discard, tlsConn)
408 return err
409 }
410
Adam Langley80842bd2014-06-20 12:00:00 -0700411 if messageLen == 0 {
412 messageLen = 32
413 }
414 testMessage := make([]byte, messageLen)
415 for i := range testMessage {
416 testMessage[i] = 0x42
417 }
Adam Langley95c29f32014-06-20 12:00:00 -0700418 tlsConn.Write(testMessage)
419
420 buf := make([]byte, len(testMessage))
421 _, err := io.ReadFull(tlsConn, buf)
422 if err != nil {
423 return err
424 }
425
426 for i, v := range buf {
427 if v != testMessage[i]^0xff {
428 return fmt.Errorf("bad reply contents at byte %d", i)
429 }
430 }
431
432 return nil
433}
434
David Benjamin325b5c32014-07-01 19:40:31 -0400435func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
436 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700437 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400438 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700439 }
David Benjamin325b5c32014-07-01 19:40:31 -0400440 valgrindArgs = append(valgrindArgs, path)
441 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700442
David Benjamin325b5c32014-07-01 19:40:31 -0400443 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700444}
445
David Benjamin325b5c32014-07-01 19:40:31 -0400446func gdbOf(path string, args ...string) *exec.Cmd {
447 xtermArgs := []string{"-e", "gdb", "--args"}
448 xtermArgs = append(xtermArgs, path)
449 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700450
David Benjamin325b5c32014-07-01 19:40:31 -0400451 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700452}
453
David Benjamin1d5c83e2014-07-22 19:20:02 -0400454func openSocketPair() (shimEnd *os.File, conn net.Conn) {
Adam Langley95c29f32014-06-20 12:00:00 -0700455 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
456 if err != nil {
457 panic(err)
458 }
459
460 syscall.CloseOnExec(socks[0])
461 syscall.CloseOnExec(socks[1])
David Benjamin1d5c83e2014-07-22 19:20:02 -0400462 shimEnd = os.NewFile(uintptr(socks[0]), "shim end")
Adam Langley95c29f32014-06-20 12:00:00 -0700463 connFile := os.NewFile(uintptr(socks[1]), "our end")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400464 conn, err = net.FileConn(connFile)
465 if err != nil {
466 panic(err)
467 }
Adam Langley95c29f32014-06-20 12:00:00 -0700468 connFile.Close()
469 if err != nil {
470 panic(err)
471 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400472 return shimEnd, conn
473}
474
David Benjamin884fdf12014-08-02 15:28:23 -0400475func runTest(test *testCase, buildDir string) error {
David Benjamin1d5c83e2014-07-22 19:20:02 -0400476 shimEnd, conn := openSocketPair()
477 shimEndResume, connResume := openSocketPair()
Adam Langley95c29f32014-06-20 12:00:00 -0700478
David Benjamin884fdf12014-08-02 15:28:23 -0400479 shim_path := path.Join(buildDir, "ssl/test/bssl_shim")
David Benjamin025b3d32014-07-01 19:53:04 -0400480 flags := []string{}
481 if test.testType == clientTest {
482 flags = append(flags, "client")
Adam Langley95c29f32014-06-20 12:00:00 -0700483 } else {
David Benjamin025b3d32014-07-01 19:53:04 -0400484 flags = append(flags, "server")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400485 }
Adam Langley95c29f32014-06-20 12:00:00 -0700486
David Benjamin1d5c83e2014-07-22 19:20:02 -0400487 if test.resumeSession {
488 flags = append(flags, "resume")
489 } else {
490 flags = append(flags, "normal")
491 }
492
493 if test.testType == serverTest {
David Benjamin025b3d32014-07-01 19:53:04 -0400494 flags = append(flags, "-key-file")
495 if test.keyFile == "" {
496 flags = append(flags, rsaKeyFile)
497 } else {
498 flags = append(flags, test.keyFile)
499 }
500
501 flags = append(flags, "-cert-file")
502 if test.certFile == "" {
503 flags = append(flags, rsaCertificateFile)
504 } else {
505 flags = append(flags, test.certFile)
506 }
507 }
508 flags = append(flags, test.flags...)
509
510 var shim *exec.Cmd
511 if *useValgrind {
512 shim = valgrindOf(false, shim_path, flags...)
513 } else {
514 shim = exec.Command(shim_path, flags...)
515 }
516 // shim = gdbOf(shim_path, flags...)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400517 shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
David Benjamin025b3d32014-07-01 19:53:04 -0400518 shim.Stdin = os.Stdin
519 var stdoutBuf, stderrBuf bytes.Buffer
520 shim.Stdout = &stdoutBuf
521 shim.Stderr = &stderrBuf
522
523 if err := shim.Start(); err != nil {
Adam Langley95c29f32014-06-20 12:00:00 -0700524 panic(err)
525 }
David Benjamin025b3d32014-07-01 19:53:04 -0400526 shimEnd.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400527 shimEndResume.Close()
Adam Langley95c29f32014-06-20 12:00:00 -0700528
529 config := test.config
David Benjamin1d5c83e2014-07-22 19:20:02 -0400530 config.ClientSessionCache = NewLRUClientSessionCache(1)
David Benjamin025b3d32014-07-01 19:53:04 -0400531 if test.testType == clientTest {
532 if len(config.Certificates) == 0 {
533 config.Certificates = []Certificate{getRSACertificate()}
534 }
David Benjamin025b3d32014-07-01 19:53:04 -0400535 }
Adam Langley95c29f32014-06-20 12:00:00 -0700536
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400537 err := doExchange(test, &config, conn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700538 conn.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400539 if err == nil && test.resumeSession {
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400540 err = doExchange(test, &config, connResume, test.messageLen)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400541 connResume.Close()
542 }
543
David Benjamin025b3d32014-07-01 19:53:04 -0400544 childErr := shim.Wait()
Adam Langley95c29f32014-06-20 12:00:00 -0700545
546 stdout := string(stdoutBuf.Bytes())
547 stderr := string(stderrBuf.Bytes())
548 failed := err != nil || childErr != nil
549 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700550 localError := "none"
551 if err != nil {
552 localError = err.Error()
553 }
554 if len(test.expectedLocalError) != 0 {
555 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
556 }
Adam Langley95c29f32014-06-20 12:00:00 -0700557
558 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700559 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700560 if childErr != nil {
561 childError = childErr.Error()
562 }
563
564 var msg string
565 switch {
566 case failed && !test.shouldFail:
567 msg = "unexpected failure"
568 case !failed && test.shouldFail:
569 msg = "unexpected success"
570 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700571 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700572 default:
573 panic("internal error")
574 }
575
576 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
577 }
578
579 if !*useValgrind && len(stderr) > 0 {
580 println(stderr)
581 }
582
583 return nil
584}
585
586var tlsVersions = []struct {
587 name string
588 version uint16
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400589 flag string
Adam Langley95c29f32014-06-20 12:00:00 -0700590}{
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400591 {"SSL3", VersionSSL30, "-no-ssl3"},
592 {"TLS1", VersionTLS10, "-no-tls1"},
593 {"TLS11", VersionTLS11, "-no-tls11"},
594 {"TLS12", VersionTLS12, "-no-tls12"},
Adam Langley95c29f32014-06-20 12:00:00 -0700595}
596
597var testCipherSuites = []struct {
598 name string
599 id uint16
600}{
601 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400602 {"AES128-GCM", TLS_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700603 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400604 {"AES256-GCM", TLS_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700605 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400606 {"DHE-RSA-3DES-SHA", TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA},
607 {"DHE-RSA-AES128-GCM", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
608 {"DHE-RSA-AES128-SHA", TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
609 {"DHE-RSA-AES256-GCM", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384},
610 {"DHE-RSA-AES256-SHA", TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700611 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
612 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
613 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
614 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
615 {"ECDHE-RSA-3DES-SHA", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
616 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700617 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400618 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700619 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
620 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700621 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400622 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700623}
624
625func addCipherSuiteTests() {
626 for _, suite := range testCipherSuites {
627 var cert Certificate
David Benjamin025b3d32014-07-01 19:53:04 -0400628 var certFile string
629 var keyFile string
Adam Langley95c29f32014-06-20 12:00:00 -0700630 if strings.Contains(suite.name, "ECDSA") {
631 cert = getECDSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400632 certFile = ecdsaCertificateFile
633 keyFile = ecdsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700634 } else {
635 cert = getRSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400636 certFile = rsaCertificateFile
637 keyFile = rsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700638 }
639
640 for _, ver := range tlsVersions {
641 if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
642 continue
643 }
644
David Benjamin1d5c83e2014-07-22 19:20:02 -0400645 // Go's TLS implementation only implements session
646 // resumption with tickets, so SSLv3 cannot resume
647 // sessions.
648 resumeSession := ver.version != VersionSSL30
649
David Benjamin025b3d32014-07-01 19:53:04 -0400650 testCases = append(testCases, testCase{
651 testType: clientTest,
652 name: ver.name + "-" + suite.name + "-client",
Adam Langley95c29f32014-06-20 12:00:00 -0700653 config: Config{
654 MinVersion: ver.version,
655 MaxVersion: ver.version,
656 CipherSuites: []uint16{suite.id},
657 Certificates: []Certificate{cert},
658 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400659 resumeSession: resumeSession,
Adam Langley95c29f32014-06-20 12:00:00 -0700660 })
David Benjamin025b3d32014-07-01 19:53:04 -0400661
662 // Go's TLS implementation implements SSLv3 as a server,
663 // but not as a client.
664 //
665 // TODO(davidben): Implement SSLv3 as a client too to
666 // exercise that code.
667 if ver.version != VersionSSL30 {
668 testCases = append(testCases, testCase{
669 testType: serverTest,
670 name: ver.name + "-" + suite.name + "-server",
671 config: Config{
672 MinVersion: ver.version,
673 MaxVersion: ver.version,
674 CipherSuites: []uint16{suite.id},
675 Certificates: []Certificate{cert},
676 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400677 certFile: certFile,
678 keyFile: keyFile,
679 resumeSession: resumeSession,
David Benjamin025b3d32014-07-01 19:53:04 -0400680 })
681 }
Adam Langley95c29f32014-06-20 12:00:00 -0700682 }
683 }
684}
685
686func addBadECDSASignatureTests() {
687 for badR := BadValue(1); badR < NumBadValues; badR++ {
688 for badS := BadValue(1); badS < NumBadValues; badS++ {
David Benjamin025b3d32014-07-01 19:53:04 -0400689 testCases = append(testCases, testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700690 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
691 config: Config{
692 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
693 Certificates: []Certificate{getECDSACertificate()},
694 Bugs: ProtocolBugs{
695 BadECDSAR: badR,
696 BadECDSAS: badS,
697 },
698 },
699 shouldFail: true,
700 expectedError: "SIGNATURE",
701 })
702 }
703 }
704}
705
Adam Langley80842bd2014-06-20 12:00:00 -0700706func addCBCPaddingTests() {
David Benjamin025b3d32014-07-01 19:53:04 -0400707 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700708 name: "MaxCBCPadding",
709 config: Config{
710 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
711 Bugs: ProtocolBugs{
712 MaxPadding: true,
713 },
714 },
715 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
716 })
David Benjamin025b3d32014-07-01 19:53:04 -0400717 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700718 name: "BadCBCPadding",
719 config: Config{
720 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
721 Bugs: ProtocolBugs{
722 PaddingFirstByteBad: true,
723 },
724 },
725 shouldFail: true,
726 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
727 })
728 // OpenSSL previously had an issue where the first byte of padding in
729 // 255 bytes of padding wasn't checked.
David Benjamin025b3d32014-07-01 19:53:04 -0400730 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700731 name: "BadCBCPadding255",
732 config: Config{
733 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
734 Bugs: ProtocolBugs{
735 MaxPadding: true,
736 PaddingFirstByteBadIf255: true,
737 },
738 },
739 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
740 shouldFail: true,
741 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
742 })
743}
744
Kenny Root7fdeaf12014-08-05 15:23:37 -0700745func addCBCSplittingTests() {
746 testCases = append(testCases, testCase{
747 name: "CBCRecordSplitting",
748 config: Config{
749 MaxVersion: VersionTLS10,
750 MinVersion: VersionTLS10,
751 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
752 },
753 messageLen: -1, // read until EOF
754 flags: []string{
755 "-async",
756 "-write-different-record-sizes",
757 "-cbc-record-splitting",
758 },
David Benjamina8e3e0e2014-08-06 22:11:10 -0400759 })
760 testCases = append(testCases, testCase{
Kenny Root7fdeaf12014-08-05 15:23:37 -0700761 name: "CBCRecordSplittingPartialWrite",
762 config: Config{
763 MaxVersion: VersionTLS10,
764 MinVersion: VersionTLS10,
765 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
766 },
767 messageLen: -1, // read until EOF
768 flags: []string{
769 "-async",
770 "-write-different-record-sizes",
771 "-cbc-record-splitting",
772 "-partial-write",
773 },
774 })
775}
776
David Benjamin636293b2014-07-08 17:59:18 -0400777func addClientAuthTests() {
David Benjamin407a10c2014-07-16 12:58:59 -0400778 // Add a dummy cert pool to stress certificate authority parsing.
779 // TODO(davidben): Add tests that those values parse out correctly.
780 certPool := x509.NewCertPool()
781 cert, err := x509.ParseCertificate(rsaCertificate.Certificate[0])
782 if err != nil {
783 panic(err)
784 }
785 certPool.AddCert(cert)
786
David Benjamin636293b2014-07-08 17:59:18 -0400787 for _, ver := range tlsVersions {
788 if ver.version == VersionSSL30 {
789 // TODO(davidben): The Go implementation does not
790 // correctly compute CertificateVerify hashes for SSLv3.
791 continue
792 }
793
794 var cipherSuites []uint16
795 if ver.version >= VersionTLS12 {
796 // Pick a SHA-256 cipher suite. The Go implementation
797 // does not correctly handle client auth with a SHA-384
798 // cipher suite.
799 cipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}
800 }
801
802 testCases = append(testCases, testCase{
803 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400804 name: ver.name + "-Client-ClientAuth-RSA",
David Benjamin636293b2014-07-08 17:59:18 -0400805 config: Config{
806 MinVersion: ver.version,
807 MaxVersion: ver.version,
808 CipherSuites: cipherSuites,
809 ClientAuth: RequireAnyClientCert,
David Benjamin407a10c2014-07-16 12:58:59 -0400810 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400811 },
812 flags: []string{
813 "-cert-file", rsaCertificateFile,
814 "-key-file", rsaKeyFile,
815 },
816 })
817 testCases = append(testCases, testCase{
818 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400819 name: ver.name + "-Client-ClientAuth-ECDSA",
David Benjamin636293b2014-07-08 17:59:18 -0400820 config: Config{
821 MinVersion: ver.version,
822 MaxVersion: ver.version,
823 CipherSuites: cipherSuites,
824 ClientAuth: RequireAnyClientCert,
David Benjamin407a10c2014-07-16 12:58:59 -0400825 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400826 },
827 flags: []string{
828 "-cert-file", ecdsaCertificateFile,
829 "-key-file", ecdsaKeyFile,
830 },
831 })
David Benjamin67666e72014-07-12 15:47:52 -0400832 testCases = append(testCases, testCase{
833 testType: serverTest,
834 name: ver.name + "-Server-ClientAuth-RSA",
835 config: Config{
836 Certificates: []Certificate{rsaCertificate},
837 },
838 flags: []string{"-require-any-client-certificate"},
839 })
840 testCases = append(testCases, testCase{
841 testType: serverTest,
842 name: ver.name + "-Server-ClientAuth-ECDSA",
843 config: Config{
844 Certificates: []Certificate{ecdsaCertificate},
845 },
846 flags: []string{"-require-any-client-certificate"},
847 })
David Benjamin636293b2014-07-08 17:59:18 -0400848 }
849}
850
David Benjamin43ec06f2014-08-05 02:28:57 -0400851// Adds tests that try to cover the range of the handshake state machine, under
852// various conditions. Some of these are redundant with other tests, but they
853// only cover the synchronous case.
854func addStateMachineCoverageTests(async bool, splitHandshake bool) {
855 var suffix string
856 var flags []string
857 var maxHandshakeRecordLength int
858 if async {
859 suffix = "-Async"
860 flags = append(flags, "-async")
861 } else {
862 suffix = "-Sync"
863 }
864 if splitHandshake {
865 suffix += "-SplitHandshakeRecords"
866 maxHandshakeRecordLength = 10
867 }
868
869 // Basic handshake, with resumption. Client and server.
870 testCases = append(testCases, testCase{
871 name: "Basic-Client" + suffix,
872 config: Config{
873 Bugs: ProtocolBugs{
874 MaxHandshakeRecordLength: maxHandshakeRecordLength,
875 },
876 },
877 flags: flags,
878 })
879 testCases = append(testCases, testCase{
880 testType: serverTest,
881 name: "Basic-Server" + suffix,
882 config: Config{
883 Bugs: ProtocolBugs{
884 MaxHandshakeRecordLength: maxHandshakeRecordLength,
885 },
886 },
887 flags: flags,
888 })
889
890 // No session ticket support; server doesn't send NewSessionTicket.
891 testCases = append(testCases, testCase{
892 name: "SessionTicketsDisabled-Client" + suffix,
893 config: Config{
894 SessionTicketsDisabled: true,
895 Bugs: ProtocolBugs{
896 MaxHandshakeRecordLength: maxHandshakeRecordLength,
897 },
898 },
899 flags: flags,
900 })
901 testCases = append(testCases, testCase{
902 testType: serverTest,
903 name: "SessionTicketsDisabled-Server" + suffix,
904 config: Config{
905 SessionTicketsDisabled: true,
906 Bugs: ProtocolBugs{
907 MaxHandshakeRecordLength: maxHandshakeRecordLength,
908 },
909 },
910 flags: flags,
911 })
912
913 // NPN on client and server; results in post-handshake message.
914 testCases = append(testCases, testCase{
915 name: "NPN-Client" + suffix,
916 config: Config{
917 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
918 NextProtos: []string{"foo"},
919 Bugs: ProtocolBugs{
920 MaxHandshakeRecordLength: maxHandshakeRecordLength,
921 },
922 },
923 flags: append(flags, "-select-next-proto", "foo"),
924 })
925 testCases = append(testCases, testCase{
926 testType: serverTest,
927 name: "NPN-Server" + suffix,
928 config: Config{
929 NextProtos: []string{"bar"},
930 Bugs: ProtocolBugs{
931 MaxHandshakeRecordLength: maxHandshakeRecordLength,
932 },
933 },
934 flags: append(flags,
935 "-advertise-npn", "\x03foo\x03bar\x03baz",
936 "-expect-next-proto", "bar"),
937 })
938
939 // Client does False Start and negotiates NPN.
940 testCases = append(testCases, testCase{
941 name: "FalseStart" + suffix,
942 config: Config{
943 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
944 NextProtos: []string{"foo"},
945 Bugs: ProtocolBugs{
946 MaxHandshakeRecordLength: maxHandshakeRecordLength,
947 },
948 },
949 flags: append(flags,
950 "-false-start",
951 "-select-next-proto", "foo"),
952 resumeSession: true,
953 })
954
955 // TLS client auth.
956 testCases = append(testCases, testCase{
957 testType: clientTest,
958 name: "ClientAuth-Client" + suffix,
959 config: Config{
960 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
961 ClientAuth: RequireAnyClientCert,
962 Bugs: ProtocolBugs{
963 MaxHandshakeRecordLength: maxHandshakeRecordLength,
964 },
965 },
966 flags: append(flags,
967 "-cert-file", rsaCertificateFile,
968 "-key-file", rsaKeyFile),
969 })
970 testCases = append(testCases, testCase{
971 testType: serverTest,
972 name: "ClientAuth-Server" + suffix,
973 config: Config{
974 Certificates: []Certificate{rsaCertificate},
975 },
976 flags: append(flags, "-require-any-client-certificate"),
977 })
978}
979
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400980func addVersionNegotiationTests() {
981 for i, shimVers := range tlsVersions {
982 // Assemble flags to disable all newer versions on the shim.
983 var flags []string
984 for _, vers := range tlsVersions[i+1:] {
985 flags = append(flags, vers.flag)
986 }
987
988 for _, runnerVers := range tlsVersions {
989 expectedVersion := shimVers.version
990 if runnerVers.version < shimVers.version {
991 expectedVersion = runnerVers.version
992 }
993 suffix := shimVers.name + "-" + runnerVers.name
994
995 testCases = append(testCases, testCase{
996 testType: clientTest,
997 name: "VersionNegotiation-Client-" + suffix,
998 config: Config{
999 MaxVersion: runnerVers.version,
1000 },
1001 flags: flags,
1002 expectedVersion: expectedVersion,
1003 })
1004
1005 // TODO(davidben): Implement SSLv3 as a client in the runner.
1006 if expectedVersion > VersionSSL30 {
1007 testCases = append(testCases, testCase{
1008 testType: serverTest,
1009 name: "VersionNegotiation-Server-" + suffix,
1010 config: Config{
1011 MaxVersion: runnerVers.version,
1012 },
1013 flags: flags,
1014 expectedVersion: expectedVersion,
1015 })
1016 }
1017 }
1018 }
1019}
1020
David Benjamin884fdf12014-08-02 15:28:23 -04001021func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
Adam Langley95c29f32014-06-20 12:00:00 -07001022 defer wg.Done()
1023
1024 for test := range c {
1025 statusChan <- statusMsg{test: test, started: true}
David Benjamin884fdf12014-08-02 15:28:23 -04001026 err := runTest(test, buildDir)
Adam Langley95c29f32014-06-20 12:00:00 -07001027 statusChan <- statusMsg{test: test, err: err}
1028 }
1029}
1030
1031type statusMsg struct {
1032 test *testCase
1033 started bool
1034 err error
1035}
1036
1037func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
1038 var started, done, failed, lineLen int
1039 defer close(doneChan)
1040
1041 for msg := range statusChan {
1042 if msg.started {
1043 started++
1044 } else {
1045 done++
1046 }
1047
1048 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
1049
1050 if msg.err != nil {
1051 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
1052 failed++
1053 }
1054 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
1055 lineLen = len(line)
1056 os.Stdout.WriteString(line)
1057 }
1058}
1059
1060func main() {
1061 var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests")
David Benjamin2bc8e6f2014-08-02 15:22:37 -04001062 var flagNumWorkers *int = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
David Benjamin884fdf12014-08-02 15:28:23 -04001063 var flagBuildDir *string = flag.String("build-dir", "../../../build", "The build directory to run the shim from.")
Adam Langley95c29f32014-06-20 12:00:00 -07001064
1065 flag.Parse()
1066
1067 addCipherSuiteTests()
1068 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -07001069 addCBCPaddingTests()
Kenny Root7fdeaf12014-08-05 15:23:37 -07001070 addCBCSplittingTests()
David Benjamin636293b2014-07-08 17:59:18 -04001071 addClientAuthTests()
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001072 addVersionNegotiationTests()
David Benjamin43ec06f2014-08-05 02:28:57 -04001073 for _, async := range []bool{false, true} {
1074 for _, splitHandshake := range []bool{false, true} {
1075 addStateMachineCoverageTests(async, splitHandshake)
1076 }
1077 }
Adam Langley95c29f32014-06-20 12:00:00 -07001078
1079 var wg sync.WaitGroup
1080
David Benjamin2bc8e6f2014-08-02 15:22:37 -04001081 numWorkers := *flagNumWorkers
Adam Langley95c29f32014-06-20 12:00:00 -07001082
1083 statusChan := make(chan statusMsg, numWorkers)
1084 testChan := make(chan *testCase, numWorkers)
1085 doneChan := make(chan struct{})
1086
David Benjamin025b3d32014-07-01 19:53:04 -04001087 go statusPrinter(doneChan, statusChan, len(testCases))
Adam Langley95c29f32014-06-20 12:00:00 -07001088
1089 for i := 0; i < numWorkers; i++ {
1090 wg.Add(1)
David Benjamin884fdf12014-08-02 15:28:23 -04001091 go worker(statusChan, testChan, *flagBuildDir, &wg)
Adam Langley95c29f32014-06-20 12:00:00 -07001092 }
1093
David Benjamin025b3d32014-07-01 19:53:04 -04001094 for i := range testCases {
1095 if len(*flagTest) == 0 || *flagTest == testCases[i].name {
1096 testChan <- &testCases[i]
Adam Langley95c29f32014-06-20 12:00:00 -07001097 }
1098 }
1099
1100 close(testChan)
1101 wg.Wait()
1102 close(statusChan)
1103 <-doneChan
1104
1105 fmt.Printf("\n")
1106}