blob: 7a87b91e25ca3e03d52bbc20bb1e5c5deec9305b [file] [log] [blame]
Adam Langley95c29f32014-06-20 12:00:00 -07001package main
2
3import (
4 "bytes"
David Benjamina08e49d2014-08-24 01:46:07 -04005 "crypto/ecdsa"
6 "crypto/elliptic"
David Benjamin407a10c2014-07-16 12:58:59 -04007 "crypto/x509"
David Benjamin2561dc32014-08-24 01:25:27 -04008 "encoding/base64"
David Benjamina08e49d2014-08-24 01:46:07 -04009 "encoding/pem"
Adam Langley95c29f32014-06-20 12:00:00 -070010 "flag"
11 "fmt"
12 "io"
Kenny Root7fdeaf12014-08-05 15:23:37 -070013 "io/ioutil"
Adam Langley95c29f32014-06-20 12:00:00 -070014 "net"
15 "os"
16 "os/exec"
David Benjamin884fdf12014-08-02 15:28:23 -040017 "path"
David Benjamin2bc8e6f2014-08-02 15:22:37 -040018 "runtime"
Adam Langley95c29f32014-06-20 12:00:00 -070019 "strings"
20 "sync"
21 "syscall"
22)
23
24var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
25
David Benjamin025b3d32014-07-01 19:53:04 -040026const (
27 rsaCertificateFile = "cert.pem"
28 ecdsaCertificateFile = "ecdsa_cert.pem"
29)
30
31const (
David Benjamina08e49d2014-08-24 01:46:07 -040032 rsaKeyFile = "key.pem"
33 ecdsaKeyFile = "ecdsa_key.pem"
34 channelIDKeyFile = "channel_id_key.pem"
David Benjamin025b3d32014-07-01 19:53:04 -040035)
36
Adam Langley95c29f32014-06-20 12:00:00 -070037var rsaCertificate, ecdsaCertificate Certificate
David Benjamina08e49d2014-08-24 01:46:07 -040038var channelIDKey *ecdsa.PrivateKey
39var channelIDBytes []byte
Adam Langley95c29f32014-06-20 12:00:00 -070040
41func initCertificates() {
42 var err error
David Benjamin025b3d32014-07-01 19:53:04 -040043 rsaCertificate, err = LoadX509KeyPair(rsaCertificateFile, rsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070044 if err != nil {
45 panic(err)
46 }
47
David Benjamin025b3d32014-07-01 19:53:04 -040048 ecdsaCertificate, err = LoadX509KeyPair(ecdsaCertificateFile, ecdsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070049 if err != nil {
50 panic(err)
51 }
David Benjamina08e49d2014-08-24 01:46:07 -040052
53 channelIDPEMBlock, err := ioutil.ReadFile(channelIDKeyFile)
54 if err != nil {
55 panic(err)
56 }
57 channelIDDERBlock, _ := pem.Decode(channelIDPEMBlock)
58 if channelIDDERBlock.Type != "EC PRIVATE KEY" {
59 panic("bad key type")
60 }
61 channelIDKey, err = x509.ParseECPrivateKey(channelIDDERBlock.Bytes)
62 if err != nil {
63 panic(err)
64 }
65 if channelIDKey.Curve != elliptic.P256() {
66 panic("bad curve")
67 }
68
69 channelIDBytes = make([]byte, 64)
70 writeIntPadded(channelIDBytes[:32], channelIDKey.X)
71 writeIntPadded(channelIDBytes[32:], channelIDKey.Y)
Adam Langley95c29f32014-06-20 12:00:00 -070072}
73
74var certificateOnce sync.Once
75
76func getRSACertificate() Certificate {
77 certificateOnce.Do(initCertificates)
78 return rsaCertificate
79}
80
81func getECDSACertificate() Certificate {
82 certificateOnce.Do(initCertificates)
83 return ecdsaCertificate
84}
85
David Benjamin025b3d32014-07-01 19:53:04 -040086type testType int
87
88const (
89 clientTest testType = iota
90 serverTest
91)
92
David Benjamin6fd297b2014-08-11 18:43:38 -040093type protocol int
94
95const (
96 tls protocol = iota
97 dtls
98)
99
David Benjaminfc7b0862014-09-06 13:21:53 -0400100const (
101 alpn = 1
102 npn = 2
103)
104
Adam Langley95c29f32014-06-20 12:00:00 -0700105type testCase struct {
David Benjamin025b3d32014-07-01 19:53:04 -0400106 testType testType
David Benjamin6fd297b2014-08-11 18:43:38 -0400107 protocol protocol
Adam Langley95c29f32014-06-20 12:00:00 -0700108 name string
109 config Config
110 shouldFail bool
111 expectedError string
Adam Langleyac61fa32014-06-23 12:03:11 -0700112 // expectedLocalError, if not empty, contains a substring that must be
113 // found in the local error.
114 expectedLocalError string
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400115 // expectedVersion, if non-zero, specifies the TLS version that must be
116 // negotiated.
117 expectedVersion uint16
David Benjamina08e49d2014-08-24 01:46:07 -0400118 // expectChannelID controls whether the connection should have
119 // negotiated a Channel ID with channelIDKey.
120 expectChannelID bool
David Benjaminae2888f2014-09-06 12:58:58 -0400121 // expectedNextProto controls whether the connection should
122 // negotiate a next protocol via NPN or ALPN.
123 expectedNextProto string
David Benjaminfc7b0862014-09-06 13:21:53 -0400124 // expectedNextProtoType, if non-zero, is the expected next
125 // protocol negotiation mechanism.
126 expectedNextProtoType int
Adam Langley80842bd2014-06-20 12:00:00 -0700127 // messageLen is the length, in bytes, of the test message that will be
128 // sent.
129 messageLen int
David Benjamin025b3d32014-07-01 19:53:04 -0400130 // certFile is the path to the certificate to use for the server.
131 certFile string
132 // keyFile is the path to the private key to use for the server.
133 keyFile string
David Benjamin1d5c83e2014-07-22 19:20:02 -0400134 // resumeSession controls whether a second connection should be tested
135 // which resumes the first session.
136 resumeSession bool
David Benjamin98e882e2014-08-08 13:24:34 -0400137 // sendPrefix sends a prefix on the socket before actually performing a
138 // handshake.
139 sendPrefix string
David Benjamine58c4f52014-08-24 03:47:07 -0400140 // shimWritesFirst controls whether the shim sends an initial "hello"
141 // message before doing a roundtrip with the runner.
142 shimWritesFirst bool
David Benjamin325b5c32014-07-01 19:40:31 -0400143 // flags, if not empty, contains a list of command-line flags that will
144 // be passed to the shim program.
145 flags []string
Adam Langley95c29f32014-06-20 12:00:00 -0700146}
147
David Benjamin025b3d32014-07-01 19:53:04 -0400148var testCases = []testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700149 {
150 name: "BadRSASignature",
151 config: Config{
152 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
153 Bugs: ProtocolBugs{
154 InvalidSKXSignature: true,
155 },
156 },
157 shouldFail: true,
158 expectedError: ":BAD_SIGNATURE:",
159 },
160 {
161 name: "BadECDSASignature",
162 config: Config{
163 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
164 Bugs: ProtocolBugs{
165 InvalidSKXSignature: true,
166 },
167 Certificates: []Certificate{getECDSACertificate()},
168 },
169 shouldFail: true,
170 expectedError: ":BAD_SIGNATURE:",
171 },
172 {
173 name: "BadECDSACurve",
174 config: Config{
175 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
176 Bugs: ProtocolBugs{
177 InvalidSKXCurve: true,
178 },
179 Certificates: []Certificate{getECDSACertificate()},
180 },
181 shouldFail: true,
182 expectedError: ":WRONG_CURVE:",
183 },
Adam Langleyac61fa32014-06-23 12:03:11 -0700184 {
David Benjamina8e3e0e2014-08-06 22:11:10 -0400185 testType: serverTest,
186 name: "BadRSAVersion",
187 config: Config{
188 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
189 Bugs: ProtocolBugs{
190 RsaClientKeyExchangeVersion: VersionTLS11,
191 },
192 },
193 shouldFail: true,
194 expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
195 },
196 {
David Benjamin325b5c32014-07-01 19:40:31 -0400197 name: "NoFallbackSCSV",
Adam Langleyac61fa32014-06-23 12:03:11 -0700198 config: Config{
199 Bugs: ProtocolBugs{
200 FailIfNotFallbackSCSV: true,
201 },
202 },
203 shouldFail: true,
204 expectedLocalError: "no fallback SCSV found",
205 },
David Benjamin325b5c32014-07-01 19:40:31 -0400206 {
David Benjamin2a0c4962014-08-22 23:46:35 -0400207 name: "SendFallbackSCSV",
David Benjamin325b5c32014-07-01 19:40:31 -0400208 config: Config{
209 Bugs: ProtocolBugs{
210 FailIfNotFallbackSCSV: true,
211 },
212 },
213 flags: []string{"-fallback-scsv"},
214 },
David Benjamin197b3ab2014-07-02 18:37:33 -0400215 {
David Benjamin7b030512014-07-08 17:30:11 -0400216 name: "ClientCertificateTypes",
217 config: Config{
218 ClientAuth: RequestClientCert,
219 ClientCertificateTypes: []byte{
220 CertTypeDSSSign,
221 CertTypeRSASign,
222 CertTypeECDSASign,
223 },
224 },
David Benjamin2561dc32014-08-24 01:25:27 -0400225 flags: []string{
226 "-expect-certificate-types",
227 base64.StdEncoding.EncodeToString([]byte{
228 CertTypeDSSSign,
229 CertTypeRSASign,
230 CertTypeECDSASign,
231 }),
232 },
David Benjamin7b030512014-07-08 17:30:11 -0400233 },
David Benjamin636293b2014-07-08 17:59:18 -0400234 {
235 name: "NoClientCertificate",
236 config: Config{
237 ClientAuth: RequireAnyClientCert,
238 },
239 shouldFail: true,
240 expectedLocalError: "client didn't provide a certificate",
241 },
David Benjamin1c375dd2014-07-12 00:48:23 -0400242 {
243 name: "UnauthenticatedECDH",
244 config: Config{
245 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
246 Bugs: ProtocolBugs{
247 UnauthenticatedECDH: true,
248 },
249 },
250 shouldFail: true,
David Benjamine8f3d662014-07-12 01:10:19 -0400251 expectedError: ":UNEXPECTED_MESSAGE:",
David Benjamin1c375dd2014-07-12 00:48:23 -0400252 },
David Benjamin9c651c92014-07-12 13:27:45 -0400253 {
254 name: "SkipServerKeyExchange",
255 config: Config{
256 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
257 Bugs: ProtocolBugs{
258 SkipServerKeyExchange: true,
259 },
260 },
261 shouldFail: true,
262 expectedError: ":UNEXPECTED_MESSAGE:",
263 },
David Benjamin1f5f62b2014-07-12 16:18:02 -0400264 {
David Benjamina0e52232014-07-19 17:39:58 -0400265 name: "SkipChangeCipherSpec-Client",
266 config: Config{
267 Bugs: ProtocolBugs{
268 SkipChangeCipherSpec: true,
269 },
270 },
271 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400272 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400273 },
274 {
275 testType: serverTest,
276 name: "SkipChangeCipherSpec-Server",
277 config: Config{
278 Bugs: ProtocolBugs{
279 SkipChangeCipherSpec: true,
280 },
281 },
282 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400283 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400284 },
David Benjamin42be6452014-07-21 14:50:23 -0400285 {
286 testType: serverTest,
287 name: "SkipChangeCipherSpec-Server-NPN",
288 config: Config{
289 NextProtos: []string{"bar"},
290 Bugs: ProtocolBugs{
291 SkipChangeCipherSpec: true,
292 },
293 },
294 flags: []string{
295 "-advertise-npn", "\x03foo\x03bar\x03baz",
296 },
297 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400298 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
299 },
300 {
301 name: "FragmentAcrossChangeCipherSpec-Client",
302 config: Config{
303 Bugs: ProtocolBugs{
304 FragmentAcrossChangeCipherSpec: true,
305 },
306 },
307 shouldFail: true,
308 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
309 },
310 {
311 testType: serverTest,
312 name: "FragmentAcrossChangeCipherSpec-Server",
313 config: Config{
314 Bugs: ProtocolBugs{
315 FragmentAcrossChangeCipherSpec: true,
316 },
317 },
318 shouldFail: true,
319 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
320 },
321 {
322 testType: serverTest,
323 name: "FragmentAcrossChangeCipherSpec-Server-NPN",
324 config: Config{
325 NextProtos: []string{"bar"},
326 Bugs: ProtocolBugs{
327 FragmentAcrossChangeCipherSpec: true,
328 },
329 },
330 flags: []string{
331 "-advertise-npn", "\x03foo\x03bar\x03baz",
332 },
333 shouldFail: true,
334 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamin42be6452014-07-21 14:50:23 -0400335 },
David Benjaminf3ec83d2014-07-21 22:42:34 -0400336 {
337 testType: serverTest,
338 name: "EarlyChangeCipherSpec-server-1",
339 config: Config{
340 Bugs: ProtocolBugs{
341 EarlyChangeCipherSpec: 1,
342 },
343 },
344 shouldFail: true,
345 expectedError: ":CCS_RECEIVED_EARLY:",
346 },
347 {
348 testType: serverTest,
349 name: "EarlyChangeCipherSpec-server-2",
350 config: Config{
351 Bugs: ProtocolBugs{
352 EarlyChangeCipherSpec: 2,
353 },
354 },
355 shouldFail: true,
356 expectedError: ":CCS_RECEIVED_EARLY:",
357 },
David Benjamind23f4122014-07-23 15:09:48 -0400358 {
David Benjamind23f4122014-07-23 15:09:48 -0400359 name: "SkipNewSessionTicket",
360 config: Config{
361 Bugs: ProtocolBugs{
362 SkipNewSessionTicket: true,
363 },
364 },
365 shouldFail: true,
366 expectedError: ":CCS_RECEIVED_EARLY:",
367 },
David Benjamin7e3305e2014-07-28 14:52:32 -0400368 {
David Benjamind86c7672014-08-02 04:07:12 -0400369 testType: serverTest,
David Benjaminbef270a2014-08-02 04:22:02 -0400370 name: "FallbackSCSV",
371 config: Config{
372 MaxVersion: VersionTLS11,
373 Bugs: ProtocolBugs{
374 SendFallbackSCSV: true,
375 },
376 },
377 shouldFail: true,
378 expectedError: ":INAPPROPRIATE_FALLBACK:",
379 },
380 {
381 testType: serverTest,
382 name: "FallbackSCSV-VersionMatch",
383 config: Config{
384 Bugs: ProtocolBugs{
385 SendFallbackSCSV: true,
386 },
387 },
388 },
David Benjamin98214542014-08-07 18:02:39 -0400389 {
390 testType: serverTest,
391 name: "FragmentedClientVersion",
392 config: Config{
393 Bugs: ProtocolBugs{
394 MaxHandshakeRecordLength: 1,
395 FragmentClientVersion: true,
396 },
397 },
398 shouldFail: true,
399 expectedError: ":RECORD_TOO_SMALL:",
400 },
David Benjamin98e882e2014-08-08 13:24:34 -0400401 {
402 testType: serverTest,
403 name: "MinorVersionTolerance",
404 config: Config{
405 Bugs: ProtocolBugs{
406 SendClientVersion: 0x03ff,
407 },
408 },
409 expectedVersion: VersionTLS12,
410 },
411 {
412 testType: serverTest,
413 name: "MajorVersionTolerance",
414 config: Config{
415 Bugs: ProtocolBugs{
416 SendClientVersion: 0x0400,
417 },
418 },
419 expectedVersion: VersionTLS12,
420 },
421 {
422 testType: serverTest,
423 name: "VersionTooLow",
424 config: Config{
425 Bugs: ProtocolBugs{
426 SendClientVersion: 0x0200,
427 },
428 },
429 shouldFail: true,
430 expectedError: ":UNSUPPORTED_PROTOCOL:",
431 },
432 {
433 testType: serverTest,
434 name: "HttpGET",
435 sendPrefix: "GET / HTTP/1.0\n",
436 shouldFail: true,
437 expectedError: ":HTTP_REQUEST:",
438 },
439 {
440 testType: serverTest,
441 name: "HttpPOST",
442 sendPrefix: "POST / HTTP/1.0\n",
443 shouldFail: true,
444 expectedError: ":HTTP_REQUEST:",
445 },
446 {
447 testType: serverTest,
448 name: "HttpHEAD",
449 sendPrefix: "HEAD / HTTP/1.0\n",
450 shouldFail: true,
451 expectedError: ":HTTP_REQUEST:",
452 },
453 {
454 testType: serverTest,
455 name: "HttpPUT",
456 sendPrefix: "PUT / HTTP/1.0\n",
457 shouldFail: true,
458 expectedError: ":HTTP_REQUEST:",
459 },
460 {
461 testType: serverTest,
462 name: "HttpCONNECT",
463 sendPrefix: "CONNECT www.google.com:443 HTTP/1.0\n",
464 shouldFail: true,
465 expectedError: ":HTTPS_PROXY_REQUEST:",
466 },
David Benjamin39ebf532014-08-31 02:23:49 -0400467 {
468 name: "SkipCipherVersionCheck",
469 config: Config{
470 CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
471 MaxVersion: VersionTLS11,
472 Bugs: ProtocolBugs{
473 SkipCipherVersionCheck: true,
474 },
475 },
476 shouldFail: true,
477 expectedError: ":WRONG_CIPHER_RETURNED:",
478 },
Adam Langley95c29f32014-06-20 12:00:00 -0700479}
480
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400481func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int) error {
David Benjamin6fd297b2014-08-11 18:43:38 -0400482 if test.protocol == dtls {
483 conn = newPacketAdaptor(conn)
484 }
485
486 if test.sendPrefix != "" {
487 if _, err := conn.Write([]byte(test.sendPrefix)); err != nil {
488 return err
489 }
David Benjamin98e882e2014-08-08 13:24:34 -0400490 }
491
David Benjamin1d5c83e2014-07-22 19:20:02 -0400492 var tlsConn *Conn
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400493 if test.testType == clientTest {
David Benjamin6fd297b2014-08-11 18:43:38 -0400494 if test.protocol == dtls {
495 tlsConn = DTLSServer(conn, config)
496 } else {
497 tlsConn = Server(conn, config)
498 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400499 } else {
500 config.InsecureSkipVerify = true
David Benjamin6fd297b2014-08-11 18:43:38 -0400501 if test.protocol == dtls {
502 tlsConn = DTLSClient(conn, config)
503 } else {
504 tlsConn = Client(conn, config)
505 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400506 }
507
Adam Langley95c29f32014-06-20 12:00:00 -0700508 if err := tlsConn.Handshake(); err != nil {
509 return err
510 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700511
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400512 if vers := tlsConn.ConnectionState().Version; test.expectedVersion != 0 && vers != test.expectedVersion {
513 return fmt.Errorf("got version %x, expected %x", vers, test.expectedVersion)
514 }
515
David Benjamina08e49d2014-08-24 01:46:07 -0400516 if test.expectChannelID {
517 channelID := tlsConn.ConnectionState().ChannelID
518 if channelID == nil {
519 return fmt.Errorf("no channel ID negotiated")
520 }
521 if channelID.Curve != channelIDKey.Curve ||
522 channelIDKey.X.Cmp(channelIDKey.X) != 0 ||
523 channelIDKey.Y.Cmp(channelIDKey.Y) != 0 {
524 return fmt.Errorf("incorrect channel ID")
525 }
526 }
527
David Benjaminae2888f2014-09-06 12:58:58 -0400528 if expected := test.expectedNextProto; expected != "" {
529 if actual := tlsConn.ConnectionState().NegotiatedProtocol; actual != expected {
530 return fmt.Errorf("next proto mismatch: got %s, wanted %s", actual, expected)
531 }
532 }
533
David Benjaminfc7b0862014-09-06 13:21:53 -0400534 if test.expectedNextProtoType != 0 {
535 if (test.expectedNextProtoType == alpn) != tlsConn.ConnectionState().NegotiatedProtocolFromALPN {
536 return fmt.Errorf("next proto type mismatch")
537 }
538 }
539
David Benjamine58c4f52014-08-24 03:47:07 -0400540 if test.shimWritesFirst {
541 var buf [5]byte
542 _, err := io.ReadFull(tlsConn, buf[:])
543 if err != nil {
544 return err
545 }
546 if string(buf[:]) != "hello" {
547 return fmt.Errorf("bad initial message")
548 }
549 }
550
Kenny Root7fdeaf12014-08-05 15:23:37 -0700551 if messageLen < 0 {
David Benjamin6fd297b2014-08-11 18:43:38 -0400552 if test.protocol == dtls {
553 return fmt.Errorf("messageLen < 0 not supported for DTLS tests")
554 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700555 // Read until EOF.
556 _, err := io.Copy(ioutil.Discard, tlsConn)
557 return err
558 }
559
Adam Langley80842bd2014-06-20 12:00:00 -0700560 if messageLen == 0 {
561 messageLen = 32
562 }
563 testMessage := make([]byte, messageLen)
564 for i := range testMessage {
565 testMessage[i] = 0x42
566 }
Adam Langley95c29f32014-06-20 12:00:00 -0700567 tlsConn.Write(testMessage)
568
569 buf := make([]byte, len(testMessage))
David Benjamin6fd297b2014-08-11 18:43:38 -0400570 if test.protocol == dtls {
571 bufTmp := make([]byte, len(buf)+1)
572 n, err := tlsConn.Read(bufTmp)
573 if err != nil {
574 return err
575 }
576 if n != len(buf) {
577 return fmt.Errorf("bad reply; length mismatch (%d vs %d)", n, len(buf))
578 }
579 copy(buf, bufTmp)
580 } else {
581 _, err := io.ReadFull(tlsConn, buf)
582 if err != nil {
583 return err
584 }
Adam Langley95c29f32014-06-20 12:00:00 -0700585 }
586
587 for i, v := range buf {
588 if v != testMessage[i]^0xff {
589 return fmt.Errorf("bad reply contents at byte %d", i)
590 }
591 }
592
593 return nil
594}
595
David Benjamin325b5c32014-07-01 19:40:31 -0400596func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
597 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700598 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400599 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700600 }
David Benjamin325b5c32014-07-01 19:40:31 -0400601 valgrindArgs = append(valgrindArgs, path)
602 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700603
David Benjamin325b5c32014-07-01 19:40:31 -0400604 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700605}
606
David Benjamin325b5c32014-07-01 19:40:31 -0400607func gdbOf(path string, args ...string) *exec.Cmd {
608 xtermArgs := []string{"-e", "gdb", "--args"}
609 xtermArgs = append(xtermArgs, path)
610 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700611
David Benjamin325b5c32014-07-01 19:40:31 -0400612 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700613}
614
David Benjamin1d5c83e2014-07-22 19:20:02 -0400615func openSocketPair() (shimEnd *os.File, conn net.Conn) {
Adam Langley95c29f32014-06-20 12:00:00 -0700616 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
617 if err != nil {
618 panic(err)
619 }
620
621 syscall.CloseOnExec(socks[0])
622 syscall.CloseOnExec(socks[1])
David Benjamin1d5c83e2014-07-22 19:20:02 -0400623 shimEnd = os.NewFile(uintptr(socks[0]), "shim end")
Adam Langley95c29f32014-06-20 12:00:00 -0700624 connFile := os.NewFile(uintptr(socks[1]), "our end")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400625 conn, err = net.FileConn(connFile)
626 if err != nil {
627 panic(err)
628 }
Adam Langley95c29f32014-06-20 12:00:00 -0700629 connFile.Close()
630 if err != nil {
631 panic(err)
632 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400633 return shimEnd, conn
634}
635
David Benjamin884fdf12014-08-02 15:28:23 -0400636func runTest(test *testCase, buildDir string) error {
David Benjamin1d5c83e2014-07-22 19:20:02 -0400637 shimEnd, conn := openSocketPair()
638 shimEndResume, connResume := openSocketPair()
Adam Langley95c29f32014-06-20 12:00:00 -0700639
David Benjamin884fdf12014-08-02 15:28:23 -0400640 shim_path := path.Join(buildDir, "ssl/test/bssl_shim")
David Benjamin5a593af2014-08-11 19:51:50 -0400641 var flags []string
David Benjamin1d5c83e2014-07-22 19:20:02 -0400642 if test.testType == serverTest {
David Benjamin5a593af2014-08-11 19:51:50 -0400643 flags = append(flags, "-server")
644
David Benjamin025b3d32014-07-01 19:53:04 -0400645 flags = append(flags, "-key-file")
646 if test.keyFile == "" {
647 flags = append(flags, rsaKeyFile)
648 } else {
649 flags = append(flags, test.keyFile)
650 }
651
652 flags = append(flags, "-cert-file")
653 if test.certFile == "" {
654 flags = append(flags, rsaCertificateFile)
655 } else {
656 flags = append(flags, test.certFile)
657 }
658 }
David Benjamin5a593af2014-08-11 19:51:50 -0400659
David Benjamin6fd297b2014-08-11 18:43:38 -0400660 if test.protocol == dtls {
661 flags = append(flags, "-dtls")
662 }
663
David Benjamin5a593af2014-08-11 19:51:50 -0400664 if test.resumeSession {
665 flags = append(flags, "-resume")
666 }
667
David Benjamine58c4f52014-08-24 03:47:07 -0400668 if test.shimWritesFirst {
669 flags = append(flags, "-shim-writes-first")
670 }
671
David Benjamin025b3d32014-07-01 19:53:04 -0400672 flags = append(flags, test.flags...)
673
674 var shim *exec.Cmd
675 if *useValgrind {
676 shim = valgrindOf(false, shim_path, flags...)
677 } else {
678 shim = exec.Command(shim_path, flags...)
679 }
680 // shim = gdbOf(shim_path, flags...)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400681 shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
David Benjamin025b3d32014-07-01 19:53:04 -0400682 shim.Stdin = os.Stdin
683 var stdoutBuf, stderrBuf bytes.Buffer
684 shim.Stdout = &stdoutBuf
685 shim.Stderr = &stderrBuf
686
687 if err := shim.Start(); err != nil {
Adam Langley95c29f32014-06-20 12:00:00 -0700688 panic(err)
689 }
David Benjamin025b3d32014-07-01 19:53:04 -0400690 shimEnd.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400691 shimEndResume.Close()
Adam Langley95c29f32014-06-20 12:00:00 -0700692
693 config := test.config
David Benjamin1d5c83e2014-07-22 19:20:02 -0400694 config.ClientSessionCache = NewLRUClientSessionCache(1)
David Benjamin025b3d32014-07-01 19:53:04 -0400695 if test.testType == clientTest {
696 if len(config.Certificates) == 0 {
697 config.Certificates = []Certificate{getRSACertificate()}
698 }
David Benjamin025b3d32014-07-01 19:53:04 -0400699 }
Adam Langley95c29f32014-06-20 12:00:00 -0700700
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400701 err := doExchange(test, &config, conn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700702 conn.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400703 if err == nil && test.resumeSession {
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400704 err = doExchange(test, &config, connResume, test.messageLen)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400705 }
David Benjamin812152a2014-09-06 12:49:07 -0400706 connResume.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400707
David Benjamin025b3d32014-07-01 19:53:04 -0400708 childErr := shim.Wait()
Adam Langley95c29f32014-06-20 12:00:00 -0700709
710 stdout := string(stdoutBuf.Bytes())
711 stderr := string(stderrBuf.Bytes())
712 failed := err != nil || childErr != nil
713 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700714 localError := "none"
715 if err != nil {
716 localError = err.Error()
717 }
718 if len(test.expectedLocalError) != 0 {
719 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
720 }
Adam Langley95c29f32014-06-20 12:00:00 -0700721
722 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700723 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700724 if childErr != nil {
725 childError = childErr.Error()
726 }
727
728 var msg string
729 switch {
730 case failed && !test.shouldFail:
731 msg = "unexpected failure"
732 case !failed && test.shouldFail:
733 msg = "unexpected success"
734 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700735 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700736 default:
737 panic("internal error")
738 }
739
740 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
741 }
742
743 if !*useValgrind && len(stderr) > 0 {
744 println(stderr)
745 }
746
747 return nil
748}
749
750var tlsVersions = []struct {
751 name string
752 version uint16
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400753 flag string
Adam Langley95c29f32014-06-20 12:00:00 -0700754}{
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400755 {"SSL3", VersionSSL30, "-no-ssl3"},
756 {"TLS1", VersionTLS10, "-no-tls1"},
757 {"TLS11", VersionTLS11, "-no-tls11"},
758 {"TLS12", VersionTLS12, "-no-tls12"},
Adam Langley95c29f32014-06-20 12:00:00 -0700759}
760
761var testCipherSuites = []struct {
762 name string
763 id uint16
764}{
765 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400766 {"AES128-GCM", TLS_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700767 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400768 {"AES128-SHA256", TLS_RSA_WITH_AES_128_CBC_SHA256},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400769 {"AES256-GCM", TLS_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700770 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400771 {"AES256-SHA256", TLS_RSA_WITH_AES_256_CBC_SHA256},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400772 {"DHE-RSA-AES128-GCM", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
773 {"DHE-RSA-AES128-SHA", TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400774 {"DHE-RSA-AES128-SHA256", TLS_DHE_RSA_WITH_AES_128_CBC_SHA256},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400775 {"DHE-RSA-AES256-GCM", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384},
776 {"DHE-RSA-AES256-SHA", TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400777 {"DHE-RSA-AES256-SHA256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700778 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
779 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400780 {"ECDHE-ECDSA-AES128-SHA256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
781 {"ECDHE-ECDSA-AES256-GCM", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700782 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400783 {"ECDHE-ECDSA-AES256-SHA384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700784 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700785 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700786 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400787 {"ECDHE-RSA-AES128-SHA256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400788 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700789 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400790 {"ECDHE-RSA-AES256-SHA384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700791 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700792 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400793 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700794}
795
David Benjaminf7768e42014-08-31 02:06:47 -0400796func isTLS12Only(suiteName string) bool {
797 return strings.HasSuffix(suiteName, "-GCM") ||
798 strings.HasSuffix(suiteName, "-SHA256") ||
799 strings.HasSuffix(suiteName, "-SHA384")
800}
801
Adam Langley95c29f32014-06-20 12:00:00 -0700802func addCipherSuiteTests() {
803 for _, suite := range testCipherSuites {
804 var cert Certificate
David Benjamin025b3d32014-07-01 19:53:04 -0400805 var certFile string
806 var keyFile string
Adam Langley95c29f32014-06-20 12:00:00 -0700807 if strings.Contains(suite.name, "ECDSA") {
808 cert = getECDSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400809 certFile = ecdsaCertificateFile
810 keyFile = ecdsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700811 } else {
812 cert = getRSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400813 certFile = rsaCertificateFile
814 keyFile = rsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700815 }
816
817 for _, ver := range tlsVersions {
David Benjaminf7768e42014-08-31 02:06:47 -0400818 if ver.version < VersionTLS12 && isTLS12Only(suite.name) {
Adam Langley95c29f32014-06-20 12:00:00 -0700819 continue
820 }
821
David Benjamin1d5c83e2014-07-22 19:20:02 -0400822 // Go's TLS implementation only implements session
823 // resumption with tickets, so SSLv3 cannot resume
824 // sessions.
825 resumeSession := ver.version != VersionSSL30
826
David Benjamin025b3d32014-07-01 19:53:04 -0400827 testCases = append(testCases, testCase{
828 testType: clientTest,
829 name: ver.name + "-" + suite.name + "-client",
Adam Langley95c29f32014-06-20 12:00:00 -0700830 config: Config{
831 MinVersion: ver.version,
832 MaxVersion: ver.version,
833 CipherSuites: []uint16{suite.id},
834 Certificates: []Certificate{cert},
835 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400836 resumeSession: resumeSession,
Adam Langley95c29f32014-06-20 12:00:00 -0700837 })
David Benjamin025b3d32014-07-01 19:53:04 -0400838
David Benjamin76d8abe2014-08-14 16:25:34 -0400839 testCases = append(testCases, testCase{
840 testType: serverTest,
841 name: ver.name + "-" + suite.name + "-server",
842 config: Config{
843 MinVersion: ver.version,
844 MaxVersion: ver.version,
845 CipherSuites: []uint16{suite.id},
846 Certificates: []Certificate{cert},
847 },
848 certFile: certFile,
849 keyFile: keyFile,
850 resumeSession: resumeSession,
851 })
David Benjamin6fd297b2014-08-11 18:43:38 -0400852
853 // TODO(davidben): Fix DTLS 1.2 support and test that.
854 if ver.version == VersionTLS10 && strings.Index(suite.name, "RC4") == -1 {
855 testCases = append(testCases, testCase{
856 testType: clientTest,
857 protocol: dtls,
858 name: "D" + ver.name + "-" + suite.name + "-client",
859 config: Config{
860 MinVersion: ver.version,
861 MaxVersion: ver.version,
862 CipherSuites: []uint16{suite.id},
863 Certificates: []Certificate{cert},
864 },
865 resumeSession: resumeSession,
866 })
867 testCases = append(testCases, testCase{
868 testType: serverTest,
869 protocol: dtls,
870 name: "D" + ver.name + "-" + suite.name + "-server",
871 config: Config{
872 MinVersion: ver.version,
873 MaxVersion: ver.version,
874 CipherSuites: []uint16{suite.id},
875 Certificates: []Certificate{cert},
876 },
877 certFile: certFile,
878 keyFile: keyFile,
879 resumeSession: resumeSession,
880 })
881 }
Adam Langley95c29f32014-06-20 12:00:00 -0700882 }
883 }
884}
885
886func addBadECDSASignatureTests() {
887 for badR := BadValue(1); badR < NumBadValues; badR++ {
888 for badS := BadValue(1); badS < NumBadValues; badS++ {
David Benjamin025b3d32014-07-01 19:53:04 -0400889 testCases = append(testCases, testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700890 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
891 config: Config{
892 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
893 Certificates: []Certificate{getECDSACertificate()},
894 Bugs: ProtocolBugs{
895 BadECDSAR: badR,
896 BadECDSAS: badS,
897 },
898 },
899 shouldFail: true,
900 expectedError: "SIGNATURE",
901 })
902 }
903 }
904}
905
Adam Langley80842bd2014-06-20 12:00:00 -0700906func addCBCPaddingTests() {
David Benjamin025b3d32014-07-01 19:53:04 -0400907 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700908 name: "MaxCBCPadding",
909 config: Config{
910 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
911 Bugs: ProtocolBugs{
912 MaxPadding: true,
913 },
914 },
915 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
916 })
David Benjamin025b3d32014-07-01 19:53:04 -0400917 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700918 name: "BadCBCPadding",
919 config: Config{
920 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
921 Bugs: ProtocolBugs{
922 PaddingFirstByteBad: true,
923 },
924 },
925 shouldFail: true,
926 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
927 })
928 // OpenSSL previously had an issue where the first byte of padding in
929 // 255 bytes of padding wasn't checked.
David Benjamin025b3d32014-07-01 19:53:04 -0400930 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700931 name: "BadCBCPadding255",
932 config: Config{
933 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
934 Bugs: ProtocolBugs{
935 MaxPadding: true,
936 PaddingFirstByteBadIf255: true,
937 },
938 },
939 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
940 shouldFail: true,
941 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
942 })
943}
944
Kenny Root7fdeaf12014-08-05 15:23:37 -0700945func addCBCSplittingTests() {
946 testCases = append(testCases, testCase{
947 name: "CBCRecordSplitting",
948 config: Config{
949 MaxVersion: VersionTLS10,
950 MinVersion: VersionTLS10,
951 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
952 },
953 messageLen: -1, // read until EOF
954 flags: []string{
955 "-async",
956 "-write-different-record-sizes",
957 "-cbc-record-splitting",
958 },
David Benjamina8e3e0e2014-08-06 22:11:10 -0400959 })
960 testCases = append(testCases, testCase{
Kenny Root7fdeaf12014-08-05 15:23:37 -0700961 name: "CBCRecordSplittingPartialWrite",
962 config: Config{
963 MaxVersion: VersionTLS10,
964 MinVersion: VersionTLS10,
965 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
966 },
967 messageLen: -1, // read until EOF
968 flags: []string{
969 "-async",
970 "-write-different-record-sizes",
971 "-cbc-record-splitting",
972 "-partial-write",
973 },
974 })
975}
976
David Benjamin636293b2014-07-08 17:59:18 -0400977func addClientAuthTests() {
David Benjamin407a10c2014-07-16 12:58:59 -0400978 // Add a dummy cert pool to stress certificate authority parsing.
979 // TODO(davidben): Add tests that those values parse out correctly.
980 certPool := x509.NewCertPool()
981 cert, err := x509.ParseCertificate(rsaCertificate.Certificate[0])
982 if err != nil {
983 panic(err)
984 }
985 certPool.AddCert(cert)
986
David Benjamin636293b2014-07-08 17:59:18 -0400987 for _, ver := range tlsVersions {
David Benjamin636293b2014-07-08 17:59:18 -0400988 testCases = append(testCases, testCase{
989 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400990 name: ver.name + "-Client-ClientAuth-RSA",
David Benjamin636293b2014-07-08 17:59:18 -0400991 config: Config{
David Benjamine098ec22014-08-27 23:13:20 -0400992 MinVersion: ver.version,
993 MaxVersion: ver.version,
994 ClientAuth: RequireAnyClientCert,
995 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400996 },
997 flags: []string{
998 "-cert-file", rsaCertificateFile,
999 "-key-file", rsaKeyFile,
1000 },
1001 })
1002 testCases = append(testCases, testCase{
David Benjamin67666e72014-07-12 15:47:52 -04001003 testType: serverTest,
1004 name: ver.name + "-Server-ClientAuth-RSA",
1005 config: Config{
David Benjamine098ec22014-08-27 23:13:20 -04001006 MinVersion: ver.version,
1007 MaxVersion: ver.version,
David Benjamin67666e72014-07-12 15:47:52 -04001008 Certificates: []Certificate{rsaCertificate},
1009 },
1010 flags: []string{"-require-any-client-certificate"},
1011 })
David Benjamine098ec22014-08-27 23:13:20 -04001012 if ver.version != VersionSSL30 {
1013 testCases = append(testCases, testCase{
1014 testType: serverTest,
1015 name: ver.name + "-Server-ClientAuth-ECDSA",
1016 config: Config{
1017 MinVersion: ver.version,
1018 MaxVersion: ver.version,
1019 Certificates: []Certificate{ecdsaCertificate},
1020 },
1021 flags: []string{"-require-any-client-certificate"},
1022 })
1023 testCases = append(testCases, testCase{
1024 testType: clientTest,
1025 name: ver.name + "-Client-ClientAuth-ECDSA",
1026 config: Config{
1027 MinVersion: ver.version,
1028 MaxVersion: ver.version,
1029 ClientAuth: RequireAnyClientCert,
1030 ClientCAs: certPool,
1031 },
1032 flags: []string{
1033 "-cert-file", ecdsaCertificateFile,
1034 "-key-file", ecdsaKeyFile,
1035 },
1036 })
1037 }
David Benjamin636293b2014-07-08 17:59:18 -04001038 }
1039}
1040
David Benjamin43ec06f2014-08-05 02:28:57 -04001041// Adds tests that try to cover the range of the handshake state machine, under
1042// various conditions. Some of these are redundant with other tests, but they
1043// only cover the synchronous case.
David Benjamin6fd297b2014-08-11 18:43:38 -04001044func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) {
David Benjamin43ec06f2014-08-05 02:28:57 -04001045 var suffix string
1046 var flags []string
1047 var maxHandshakeRecordLength int
David Benjamin6fd297b2014-08-11 18:43:38 -04001048 if protocol == dtls {
1049 suffix = "-DTLS"
1050 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001051 if async {
David Benjamin6fd297b2014-08-11 18:43:38 -04001052 suffix += "-Async"
David Benjamin43ec06f2014-08-05 02:28:57 -04001053 flags = append(flags, "-async")
1054 } else {
David Benjamin6fd297b2014-08-11 18:43:38 -04001055 suffix += "-Sync"
David Benjamin43ec06f2014-08-05 02:28:57 -04001056 }
1057 if splitHandshake {
1058 suffix += "-SplitHandshakeRecords"
David Benjamin98214542014-08-07 18:02:39 -04001059 maxHandshakeRecordLength = 1
David Benjamin43ec06f2014-08-05 02:28:57 -04001060 }
1061
1062 // Basic handshake, with resumption. Client and server.
1063 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001064 protocol: protocol,
1065 name: "Basic-Client" + suffix,
David Benjamin43ec06f2014-08-05 02:28:57 -04001066 config: Config{
1067 Bugs: ProtocolBugs{
1068 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1069 },
1070 },
David Benjaminbed9aae2014-08-07 19:13:38 -04001071 flags: flags,
1072 resumeSession: true,
1073 })
1074 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001075 protocol: protocol,
1076 name: "Basic-Client-RenewTicket" + suffix,
David Benjaminbed9aae2014-08-07 19:13:38 -04001077 config: Config{
1078 Bugs: ProtocolBugs{
1079 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1080 RenewTicketOnResume: true,
1081 },
1082 },
1083 flags: flags,
1084 resumeSession: true,
David Benjamin43ec06f2014-08-05 02:28:57 -04001085 })
1086 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001087 protocol: protocol,
David Benjamin43ec06f2014-08-05 02:28:57 -04001088 testType: serverTest,
1089 name: "Basic-Server" + suffix,
1090 config: Config{
1091 Bugs: ProtocolBugs{
1092 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1093 },
1094 },
David Benjaminbed9aae2014-08-07 19:13:38 -04001095 flags: flags,
1096 resumeSession: true,
David Benjamin43ec06f2014-08-05 02:28:57 -04001097 })
1098
David Benjamin6fd297b2014-08-11 18:43:38 -04001099 // TLS client auth.
1100 testCases = append(testCases, testCase{
1101 protocol: protocol,
1102 testType: clientTest,
1103 name: "ClientAuth-Client" + suffix,
1104 config: Config{
David Benjamine098ec22014-08-27 23:13:20 -04001105 ClientAuth: RequireAnyClientCert,
David Benjamin6fd297b2014-08-11 18:43:38 -04001106 Bugs: ProtocolBugs{
1107 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1108 },
1109 },
1110 flags: append(flags,
1111 "-cert-file", rsaCertificateFile,
1112 "-key-file", rsaKeyFile),
1113 })
1114 testCases = append(testCases, testCase{
1115 protocol: protocol,
1116 testType: serverTest,
1117 name: "ClientAuth-Server" + suffix,
1118 config: Config{
1119 Certificates: []Certificate{rsaCertificate},
1120 },
1121 flags: append(flags, "-require-any-client-certificate"),
1122 })
1123
David Benjamin43ec06f2014-08-05 02:28:57 -04001124 // No session ticket support; server doesn't send NewSessionTicket.
1125 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001126 protocol: protocol,
1127 name: "SessionTicketsDisabled-Client" + suffix,
David Benjamin43ec06f2014-08-05 02:28:57 -04001128 config: Config{
1129 SessionTicketsDisabled: true,
1130 Bugs: ProtocolBugs{
1131 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1132 },
1133 },
1134 flags: flags,
1135 })
1136 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001137 protocol: protocol,
David Benjamin43ec06f2014-08-05 02:28:57 -04001138 testType: serverTest,
1139 name: "SessionTicketsDisabled-Server" + suffix,
1140 config: Config{
1141 SessionTicketsDisabled: true,
1142 Bugs: ProtocolBugs{
1143 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1144 },
1145 },
1146 flags: flags,
1147 })
1148
David Benjamin6fd297b2014-08-11 18:43:38 -04001149 if protocol == tls {
1150 // NPN on client and server; results in post-handshake message.
1151 testCases = append(testCases, testCase{
1152 protocol: protocol,
1153 name: "NPN-Client" + suffix,
1154 config: Config{
David Benjaminae2888f2014-09-06 12:58:58 -04001155 NextProtos: []string{"foo"},
David Benjamin6fd297b2014-08-11 18:43:38 -04001156 Bugs: ProtocolBugs{
1157 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1158 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001159 },
David Benjaminfc7b0862014-09-06 13:21:53 -04001160 flags: append(flags, "-select-next-proto", "foo"),
1161 expectedNextProto: "foo",
1162 expectedNextProtoType: npn,
David Benjamin6fd297b2014-08-11 18:43:38 -04001163 })
1164 testCases = append(testCases, testCase{
1165 protocol: protocol,
1166 testType: serverTest,
1167 name: "NPN-Server" + suffix,
1168 config: Config{
1169 NextProtos: []string{"bar"},
1170 Bugs: ProtocolBugs{
1171 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1172 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001173 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001174 flags: append(flags,
1175 "-advertise-npn", "\x03foo\x03bar\x03baz",
1176 "-expect-next-proto", "bar"),
David Benjaminfc7b0862014-09-06 13:21:53 -04001177 expectedNextProto: "bar",
1178 expectedNextProtoType: npn,
David Benjamin6fd297b2014-08-11 18:43:38 -04001179 })
David Benjamin43ec06f2014-08-05 02:28:57 -04001180
David Benjamin6fd297b2014-08-11 18:43:38 -04001181 // Client does False Start and negotiates NPN.
1182 testCases = append(testCases, testCase{
1183 protocol: protocol,
1184 name: "FalseStart" + suffix,
1185 config: Config{
1186 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1187 NextProtos: []string{"foo"},
1188 Bugs: ProtocolBugs{
David Benjamine58c4f52014-08-24 03:47:07 -04001189 ExpectFalseStart: true,
David Benjamin6fd297b2014-08-11 18:43:38 -04001190 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1191 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001192 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001193 flags: append(flags,
1194 "-false-start",
1195 "-select-next-proto", "foo"),
David Benjamine58c4f52014-08-24 03:47:07 -04001196 shimWritesFirst: true,
1197 resumeSession: true,
David Benjamin6fd297b2014-08-11 18:43:38 -04001198 })
David Benjamin43ec06f2014-08-05 02:28:57 -04001199
David Benjaminae2888f2014-09-06 12:58:58 -04001200 // Client does False Start and negotiates ALPN.
1201 testCases = append(testCases, testCase{
1202 protocol: protocol,
1203 name: "FalseStart-ALPN" + suffix,
1204 config: Config{
1205 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1206 NextProtos: []string{"foo"},
1207 Bugs: ProtocolBugs{
1208 ExpectFalseStart: true,
1209 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1210 },
1211 },
1212 flags: append(flags,
1213 "-false-start",
1214 "-advertise-alpn", "\x03foo"),
1215 shimWritesFirst: true,
1216 resumeSession: true,
1217 })
1218
David Benjamin6fd297b2014-08-11 18:43:38 -04001219 // False Start without session tickets.
1220 testCases = append(testCases, testCase{
1221 name: "FalseStart-SessionTicketsDisabled",
1222 config: Config{
1223 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1224 NextProtos: []string{"foo"},
1225 SessionTicketsDisabled: true,
David Benjamin4e99c522014-08-24 01:45:30 -04001226 Bugs: ProtocolBugs{
David Benjamine58c4f52014-08-24 03:47:07 -04001227 ExpectFalseStart: true,
David Benjamin4e99c522014-08-24 01:45:30 -04001228 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1229 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001230 },
David Benjamin4e99c522014-08-24 01:45:30 -04001231 flags: append(flags,
David Benjamin6fd297b2014-08-11 18:43:38 -04001232 "-false-start",
1233 "-select-next-proto", "foo",
David Benjamin4e99c522014-08-24 01:45:30 -04001234 ),
David Benjamine58c4f52014-08-24 03:47:07 -04001235 shimWritesFirst: true,
David Benjamin6fd297b2014-08-11 18:43:38 -04001236 })
David Benjamin1e7f8d72014-08-08 12:27:04 -04001237
David Benjamina08e49d2014-08-24 01:46:07 -04001238 // Server parses a V2ClientHello.
David Benjamin6fd297b2014-08-11 18:43:38 -04001239 testCases = append(testCases, testCase{
1240 protocol: protocol,
1241 testType: serverTest,
1242 name: "SendV2ClientHello" + suffix,
1243 config: Config{
1244 // Choose a cipher suite that does not involve
1245 // elliptic curves, so no extensions are
1246 // involved.
1247 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
1248 Bugs: ProtocolBugs{
1249 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1250 SendV2ClientHello: true,
1251 },
David Benjamin1e7f8d72014-08-08 12:27:04 -04001252 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001253 flags: flags,
1254 })
David Benjamina08e49d2014-08-24 01:46:07 -04001255
1256 // Client sends a Channel ID.
1257 testCases = append(testCases, testCase{
1258 protocol: protocol,
1259 name: "ChannelID-Client" + suffix,
1260 config: Config{
1261 RequestChannelID: true,
1262 Bugs: ProtocolBugs{
1263 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1264 },
1265 },
1266 flags: append(flags,
1267 "-send-channel-id", channelIDKeyFile,
1268 ),
1269 resumeSession: true,
1270 expectChannelID: true,
1271 })
1272
1273 // Server accepts a Channel ID.
1274 testCases = append(testCases, testCase{
1275 protocol: protocol,
1276 testType: serverTest,
1277 name: "ChannelID-Server" + suffix,
1278 config: Config{
1279 ChannelID: channelIDKey,
1280 Bugs: ProtocolBugs{
1281 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1282 },
1283 },
1284 flags: append(flags,
1285 "-expect-channel-id",
1286 base64.StdEncoding.EncodeToString(channelIDBytes),
1287 ),
1288 resumeSession: true,
1289 expectChannelID: true,
1290 })
David Benjamin6fd297b2014-08-11 18:43:38 -04001291 } else {
1292 testCases = append(testCases, testCase{
1293 protocol: protocol,
1294 name: "SkipHelloVerifyRequest" + suffix,
1295 config: Config{
1296 Bugs: ProtocolBugs{
1297 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1298 SkipHelloVerifyRequest: true,
1299 },
1300 },
1301 flags: flags,
1302 })
1303
1304 testCases = append(testCases, testCase{
1305 testType: serverTest,
1306 protocol: protocol,
1307 name: "CookieExchange" + suffix,
1308 config: Config{
1309 Bugs: ProtocolBugs{
1310 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1311 },
1312 },
1313 flags: append(flags, "-cookie-exchange"),
1314 })
1315 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001316}
1317
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001318func addVersionNegotiationTests() {
1319 for i, shimVers := range tlsVersions {
1320 // Assemble flags to disable all newer versions on the shim.
1321 var flags []string
1322 for _, vers := range tlsVersions[i+1:] {
1323 flags = append(flags, vers.flag)
1324 }
1325
1326 for _, runnerVers := range tlsVersions {
1327 expectedVersion := shimVers.version
1328 if runnerVers.version < shimVers.version {
1329 expectedVersion = runnerVers.version
1330 }
1331 suffix := shimVers.name + "-" + runnerVers.name
1332
1333 testCases = append(testCases, testCase{
1334 testType: clientTest,
1335 name: "VersionNegotiation-Client-" + suffix,
1336 config: Config{
1337 MaxVersion: runnerVers.version,
1338 },
1339 flags: flags,
1340 expectedVersion: expectedVersion,
1341 })
1342
David Benjamin76d8abe2014-08-14 16:25:34 -04001343 testCases = append(testCases, testCase{
1344 testType: serverTest,
1345 name: "VersionNegotiation-Server-" + suffix,
1346 config: Config{
1347 MaxVersion: runnerVers.version,
1348 },
1349 flags: flags,
1350 expectedVersion: expectedVersion,
1351 })
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001352 }
1353 }
1354}
1355
David Benjamin5c24a1d2014-08-31 00:59:27 -04001356func addD5BugTests() {
1357 testCases = append(testCases, testCase{
1358 testType: serverTest,
1359 name: "D5Bug-NoQuirk-Reject",
1360 config: Config{
1361 CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
1362 Bugs: ProtocolBugs{
1363 SSL3RSAKeyExchange: true,
1364 },
1365 },
1366 shouldFail: true,
1367 expectedError: ":TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG:",
1368 })
1369 testCases = append(testCases, testCase{
1370 testType: serverTest,
1371 name: "D5Bug-Quirk-Normal",
1372 config: Config{
1373 CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
1374 },
1375 flags: []string{"-tls-d5-bug"},
1376 })
1377 testCases = append(testCases, testCase{
1378 testType: serverTest,
1379 name: "D5Bug-Quirk-Bug",
1380 config: Config{
1381 CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
1382 Bugs: ProtocolBugs{
1383 SSL3RSAKeyExchange: true,
1384 },
1385 },
1386 flags: []string{"-tls-d5-bug"},
1387 })
1388}
1389
David Benjamine78bfde2014-09-06 12:45:15 -04001390func addExtensionTests() {
1391 testCases = append(testCases, testCase{
1392 testType: clientTest,
1393 name: "DuplicateExtensionClient",
1394 config: Config{
1395 Bugs: ProtocolBugs{
1396 DuplicateExtension: true,
1397 },
1398 },
1399 shouldFail: true,
1400 expectedLocalError: "remote error: error decoding message",
1401 })
1402 testCases = append(testCases, testCase{
1403 testType: serverTest,
1404 name: "DuplicateExtensionServer",
1405 config: Config{
1406 Bugs: ProtocolBugs{
1407 DuplicateExtension: true,
1408 },
1409 },
1410 shouldFail: true,
1411 expectedLocalError: "remote error: error decoding message",
1412 })
1413 testCases = append(testCases, testCase{
1414 testType: clientTest,
1415 name: "ServerNameExtensionClient",
1416 config: Config{
1417 Bugs: ProtocolBugs{
1418 ExpectServerName: "example.com",
1419 },
1420 },
1421 flags: []string{"-host-name", "example.com"},
1422 })
1423 testCases = append(testCases, testCase{
1424 testType: clientTest,
1425 name: "ServerNameExtensionClient",
1426 config: Config{
1427 Bugs: ProtocolBugs{
1428 ExpectServerName: "mismatch.com",
1429 },
1430 },
1431 flags: []string{"-host-name", "example.com"},
1432 shouldFail: true,
1433 expectedLocalError: "tls: unexpected server name",
1434 })
1435 testCases = append(testCases, testCase{
1436 testType: clientTest,
1437 name: "ServerNameExtensionClient",
1438 config: Config{
1439 Bugs: ProtocolBugs{
1440 ExpectServerName: "missing.com",
1441 },
1442 },
1443 shouldFail: true,
1444 expectedLocalError: "tls: unexpected server name",
1445 })
1446 testCases = append(testCases, testCase{
1447 testType: serverTest,
1448 name: "ServerNameExtensionServer",
1449 config: Config{
1450 ServerName: "example.com",
1451 },
1452 flags: []string{"-expect-server-name", "example.com"},
1453 resumeSession: true,
1454 })
David Benjaminae2888f2014-09-06 12:58:58 -04001455 testCases = append(testCases, testCase{
1456 testType: clientTest,
1457 name: "ALPNClient",
1458 config: Config{
1459 NextProtos: []string{"foo"},
1460 },
1461 flags: []string{
1462 "-advertise-alpn", "\x03foo\x03bar\x03baz",
1463 "-expect-alpn", "foo",
1464 },
David Benjaminfc7b0862014-09-06 13:21:53 -04001465 expectedNextProto: "foo",
1466 expectedNextProtoType: alpn,
1467 resumeSession: true,
David Benjaminae2888f2014-09-06 12:58:58 -04001468 })
1469 testCases = append(testCases, testCase{
1470 testType: serverTest,
1471 name: "ALPNServer",
1472 config: Config{
1473 NextProtos: []string{"foo", "bar", "baz"},
1474 },
1475 flags: []string{
1476 "-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
1477 "-select-alpn", "foo",
1478 },
David Benjaminfc7b0862014-09-06 13:21:53 -04001479 expectedNextProto: "foo",
1480 expectedNextProtoType: alpn,
1481 resumeSession: true,
1482 })
1483 // Test that the server prefers ALPN over NPN.
1484 testCases = append(testCases, testCase{
1485 testType: serverTest,
1486 name: "ALPNServer-Preferred",
1487 config: Config{
1488 NextProtos: []string{"foo", "bar", "baz"},
1489 },
1490 flags: []string{
1491 "-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
1492 "-select-alpn", "foo",
1493 "-advertise-npn", "\x03foo\x03bar\x03baz",
1494 },
1495 expectedNextProto: "foo",
1496 expectedNextProtoType: alpn,
1497 resumeSession: true,
1498 })
1499 testCases = append(testCases, testCase{
1500 testType: serverTest,
1501 name: "ALPNServer-Preferred-Swapped",
1502 config: Config{
1503 NextProtos: []string{"foo", "bar", "baz"},
1504 Bugs: ProtocolBugs{
1505 SwapNPNAndALPN: true,
1506 },
1507 },
1508 flags: []string{
1509 "-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
1510 "-select-alpn", "foo",
1511 "-advertise-npn", "\x03foo\x03bar\x03baz",
1512 },
1513 expectedNextProto: "foo",
1514 expectedNextProtoType: alpn,
1515 resumeSession: true,
David Benjaminae2888f2014-09-06 12:58:58 -04001516 })
David Benjamine78bfde2014-09-06 12:45:15 -04001517}
1518
David Benjamin884fdf12014-08-02 15:28:23 -04001519func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
Adam Langley95c29f32014-06-20 12:00:00 -07001520 defer wg.Done()
1521
1522 for test := range c {
1523 statusChan <- statusMsg{test: test, started: true}
David Benjamin884fdf12014-08-02 15:28:23 -04001524 err := runTest(test, buildDir)
Adam Langley95c29f32014-06-20 12:00:00 -07001525 statusChan <- statusMsg{test: test, err: err}
1526 }
1527}
1528
1529type statusMsg struct {
1530 test *testCase
1531 started bool
1532 err error
1533}
1534
1535func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
1536 var started, done, failed, lineLen int
1537 defer close(doneChan)
1538
1539 for msg := range statusChan {
1540 if msg.started {
1541 started++
1542 } else {
1543 done++
1544 }
1545
1546 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
1547
1548 if msg.err != nil {
1549 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
1550 failed++
1551 }
1552 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
1553 lineLen = len(line)
1554 os.Stdout.WriteString(line)
1555 }
1556}
1557
1558func main() {
1559 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 -04001560 var flagNumWorkers *int = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
David Benjamin884fdf12014-08-02 15:28:23 -04001561 var flagBuildDir *string = flag.String("build-dir", "../../../build", "The build directory to run the shim from.")
Adam Langley95c29f32014-06-20 12:00:00 -07001562
1563 flag.Parse()
1564
1565 addCipherSuiteTests()
1566 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -07001567 addCBCPaddingTests()
Kenny Root7fdeaf12014-08-05 15:23:37 -07001568 addCBCSplittingTests()
David Benjamin636293b2014-07-08 17:59:18 -04001569 addClientAuthTests()
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001570 addVersionNegotiationTests()
David Benjamin5c24a1d2014-08-31 00:59:27 -04001571 addD5BugTests()
David Benjamine78bfde2014-09-06 12:45:15 -04001572 addExtensionTests()
David Benjamin43ec06f2014-08-05 02:28:57 -04001573 for _, async := range []bool{false, true} {
1574 for _, splitHandshake := range []bool{false, true} {
David Benjamin6fd297b2014-08-11 18:43:38 -04001575 for _, protocol := range []protocol{tls, dtls} {
1576 addStateMachineCoverageTests(async, splitHandshake, protocol)
1577 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001578 }
1579 }
Adam Langley95c29f32014-06-20 12:00:00 -07001580
1581 var wg sync.WaitGroup
1582
David Benjamin2bc8e6f2014-08-02 15:22:37 -04001583 numWorkers := *flagNumWorkers
Adam Langley95c29f32014-06-20 12:00:00 -07001584
1585 statusChan := make(chan statusMsg, numWorkers)
1586 testChan := make(chan *testCase, numWorkers)
1587 doneChan := make(chan struct{})
1588
David Benjamin025b3d32014-07-01 19:53:04 -04001589 go statusPrinter(doneChan, statusChan, len(testCases))
Adam Langley95c29f32014-06-20 12:00:00 -07001590
1591 for i := 0; i < numWorkers; i++ {
1592 wg.Add(1)
David Benjamin884fdf12014-08-02 15:28:23 -04001593 go worker(statusChan, testChan, *flagBuildDir, &wg)
Adam Langley95c29f32014-06-20 12:00:00 -07001594 }
1595
David Benjamin025b3d32014-07-01 19:53:04 -04001596 for i := range testCases {
1597 if len(*flagTest) == 0 || *flagTest == testCases[i].name {
1598 testChan <- &testCases[i]
Adam Langley95c29f32014-06-20 12:00:00 -07001599 }
1600 }
1601
1602 close(testChan)
1603 wg.Wait()
1604 close(statusChan)
1605 <-doneChan
1606
1607 fmt.Printf("\n")
1608}