blob: bec89b164ff8b0cf7be9fc9639e4d9203f35a8c3 [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"
9 "net"
10 "os"
11 "os/exec"
12 "strings"
13 "sync"
14 "syscall"
15)
16
17var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
18
David Benjamin025b3d32014-07-01 19:53:04 -040019const (
20 rsaCertificateFile = "cert.pem"
21 ecdsaCertificateFile = "ecdsa_cert.pem"
22)
23
24const (
25 rsaKeyFile = "key.pem"
26 ecdsaKeyFile = "ecdsa_key.pem"
27)
28
Adam Langley95c29f32014-06-20 12:00:00 -070029var rsaCertificate, ecdsaCertificate Certificate
30
31func initCertificates() {
32 var err error
David Benjamin025b3d32014-07-01 19:53:04 -040033 rsaCertificate, err = LoadX509KeyPair(rsaCertificateFile, rsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070034 if err != nil {
35 panic(err)
36 }
37
David Benjamin025b3d32014-07-01 19:53:04 -040038 ecdsaCertificate, err = LoadX509KeyPair(ecdsaCertificateFile, ecdsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070039 if err != nil {
40 panic(err)
41 }
42}
43
44var certificateOnce sync.Once
45
46func getRSACertificate() Certificate {
47 certificateOnce.Do(initCertificates)
48 return rsaCertificate
49}
50
51func getECDSACertificate() Certificate {
52 certificateOnce.Do(initCertificates)
53 return ecdsaCertificate
54}
55
David Benjamin025b3d32014-07-01 19:53:04 -040056type testType int
57
58const (
59 clientTest testType = iota
60 serverTest
61)
62
Adam Langley95c29f32014-06-20 12:00:00 -070063type testCase struct {
David Benjamin025b3d32014-07-01 19:53:04 -040064 testType testType
Adam Langley95c29f32014-06-20 12:00:00 -070065 name string
66 config Config
67 shouldFail bool
68 expectedError string
Adam Langleyac61fa32014-06-23 12:03:11 -070069 // expectedLocalError, if not empty, contains a substring that must be
70 // found in the local error.
71 expectedLocalError string
Adam Langley80842bd2014-06-20 12:00:00 -070072 // messageLen is the length, in bytes, of the test message that will be
73 // sent.
74 messageLen int
David Benjamin025b3d32014-07-01 19:53:04 -040075 // certFile is the path to the certificate to use for the server.
76 certFile string
77 // keyFile is the path to the private key to use for the server.
78 keyFile string
David Benjamin1d5c83e2014-07-22 19:20:02 -040079 // resumeSession controls whether a second connection should be tested
80 // which resumes the first session.
81 resumeSession bool
David Benjamin325b5c32014-07-01 19:40:31 -040082 // flags, if not empty, contains a list of command-line flags that will
83 // be passed to the shim program.
84 flags []string
Adam Langley95c29f32014-06-20 12:00:00 -070085}
86
David Benjamin025b3d32014-07-01 19:53:04 -040087var testCases = []testCase{
Adam Langley95c29f32014-06-20 12:00:00 -070088 {
89 name: "BadRSASignature",
90 config: Config{
91 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
92 Bugs: ProtocolBugs{
93 InvalidSKXSignature: true,
94 },
95 },
96 shouldFail: true,
97 expectedError: ":BAD_SIGNATURE:",
98 },
99 {
100 name: "BadECDSASignature",
101 config: Config{
102 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
103 Bugs: ProtocolBugs{
104 InvalidSKXSignature: true,
105 },
106 Certificates: []Certificate{getECDSACertificate()},
107 },
108 shouldFail: true,
109 expectedError: ":BAD_SIGNATURE:",
110 },
111 {
112 name: "BadECDSACurve",
113 config: Config{
114 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
115 Bugs: ProtocolBugs{
116 InvalidSKXCurve: true,
117 },
118 Certificates: []Certificate{getECDSACertificate()},
119 },
120 shouldFail: true,
121 expectedError: ":WRONG_CURVE:",
122 },
Adam Langleyac61fa32014-06-23 12:03:11 -0700123 {
David Benjamin325b5c32014-07-01 19:40:31 -0400124 name: "NoFallbackSCSV",
Adam Langleyac61fa32014-06-23 12:03:11 -0700125 config: Config{
126 Bugs: ProtocolBugs{
127 FailIfNotFallbackSCSV: true,
128 },
129 },
130 shouldFail: true,
131 expectedLocalError: "no fallback SCSV found",
132 },
David Benjamin325b5c32014-07-01 19:40:31 -0400133 {
134 name: "FallbackSCSV",
135 config: Config{
136 Bugs: ProtocolBugs{
137 FailIfNotFallbackSCSV: true,
138 },
139 },
140 flags: []string{"-fallback-scsv"},
141 },
David Benjamin197b3ab2014-07-02 18:37:33 -0400142 {
143 testType: serverTest,
David Benjamin35a7a442014-07-05 00:23:20 -0400144 name: "ServerNameExtension",
David Benjamin197b3ab2014-07-02 18:37:33 -0400145 config: Config{
146 ServerName: "example.com",
147 },
148 flags: []string{"-expect-server-name", "example.com"},
149 },
David Benjamin35a7a442014-07-05 00:23:20 -0400150 {
151 testType: clientTest,
152 name: "DuplicateExtensionClient",
153 config: Config{
154 Bugs: ProtocolBugs{
155 DuplicateExtension: true,
156 },
157 },
158 shouldFail: true,
159 expectedLocalError: "remote error: error decoding message",
160 },
161 {
162 testType: serverTest,
163 name: "DuplicateExtensionServer",
164 config: Config{
165 Bugs: ProtocolBugs{
166 DuplicateExtension: true,
167 },
168 },
169 shouldFail: true,
170 expectedLocalError: "remote error: error decoding message",
171 },
David Benjamin7b030512014-07-08 17:30:11 -0400172 {
173 name: "ClientCertificateTypes",
174 config: Config{
175 ClientAuth: RequestClientCert,
176 ClientCertificateTypes: []byte{
177 CertTypeDSSSign,
178 CertTypeRSASign,
179 CertTypeECDSASign,
180 },
181 },
182 flags: []string{"-expect-certificate-types", string([]byte{
183 CertTypeDSSSign,
184 CertTypeRSASign,
185 CertTypeECDSASign,
186 })},
187 },
David Benjamin636293b2014-07-08 17:59:18 -0400188 {
189 name: "NoClientCertificate",
190 config: Config{
191 ClientAuth: RequireAnyClientCert,
192 },
193 shouldFail: true,
194 expectedLocalError: "client didn't provide a certificate",
195 },
David Benjamin1c375dd2014-07-12 00:48:23 -0400196 {
197 name: "UnauthenticatedECDH",
198 config: Config{
199 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
200 Bugs: ProtocolBugs{
201 UnauthenticatedECDH: true,
202 },
203 },
204 shouldFail: true,
David Benjamine8f3d662014-07-12 01:10:19 -0400205 expectedError: ":UNEXPECTED_MESSAGE:",
David Benjamin1c375dd2014-07-12 00:48:23 -0400206 },
David Benjamin9c651c92014-07-12 13:27:45 -0400207 {
208 name: "SkipServerKeyExchange",
209 config: Config{
210 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
211 Bugs: ProtocolBugs{
212 SkipServerKeyExchange: true,
213 },
214 },
215 shouldFail: true,
216 expectedError: ":UNEXPECTED_MESSAGE:",
217 },
David Benjamin1f5f62b2014-07-12 16:18:02 -0400218 {
219 testType: serverTest,
220 name: "NPNServerTest",
221 config: Config{
222 NextProtos: []string{"bar"},
223 },
224 flags: []string{
225 "-advertise-npn", "\x03foo\x03bar\x03baz",
226 "-expect-next-proto", "bar",
227 },
228 },
David Benjamina0e52232014-07-19 17:39:58 -0400229 {
230 name: "SkipChangeCipherSpec-Client",
231 config: Config{
232 Bugs: ProtocolBugs{
233 SkipChangeCipherSpec: true,
234 },
235 },
236 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400237 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400238 },
239 {
240 testType: serverTest,
241 name: "SkipChangeCipherSpec-Server",
242 config: Config{
243 Bugs: ProtocolBugs{
244 SkipChangeCipherSpec: true,
245 },
246 },
247 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400248 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400249 },
David Benjamin42be6452014-07-21 14:50:23 -0400250 {
251 testType: serverTest,
252 name: "SkipChangeCipherSpec-Server-NPN",
253 config: Config{
254 NextProtos: []string{"bar"},
255 Bugs: ProtocolBugs{
256 SkipChangeCipherSpec: true,
257 },
258 },
259 flags: []string{
260 "-advertise-npn", "\x03foo\x03bar\x03baz",
261 },
262 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400263 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
264 },
265 {
266 name: "FragmentAcrossChangeCipherSpec-Client",
267 config: Config{
268 Bugs: ProtocolBugs{
269 FragmentAcrossChangeCipherSpec: true,
270 },
271 },
272 shouldFail: true,
273 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
274 },
275 {
276 testType: serverTest,
277 name: "FragmentAcrossChangeCipherSpec-Server",
278 config: Config{
279 Bugs: ProtocolBugs{
280 FragmentAcrossChangeCipherSpec: true,
281 },
282 },
283 shouldFail: true,
284 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
285 },
286 {
287 testType: serverTest,
288 name: "FragmentAcrossChangeCipherSpec-Server-NPN",
289 config: Config{
290 NextProtos: []string{"bar"},
291 Bugs: ProtocolBugs{
292 FragmentAcrossChangeCipherSpec: true,
293 },
294 },
295 flags: []string{
296 "-advertise-npn", "\x03foo\x03bar\x03baz",
297 },
298 shouldFail: true,
299 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamin42be6452014-07-21 14:50:23 -0400300 },
David Benjaminf3ec83d2014-07-21 22:42:34 -0400301 {
302 testType: serverTest,
303 name: "EarlyChangeCipherSpec-server-1",
304 config: Config{
305 Bugs: ProtocolBugs{
306 EarlyChangeCipherSpec: 1,
307 },
308 },
309 shouldFail: true,
310 expectedError: ":CCS_RECEIVED_EARLY:",
311 },
312 {
313 testType: serverTest,
314 name: "EarlyChangeCipherSpec-server-2",
315 config: Config{
316 Bugs: ProtocolBugs{
317 EarlyChangeCipherSpec: 2,
318 },
319 },
320 shouldFail: true,
321 expectedError: ":CCS_RECEIVED_EARLY:",
322 },
David Benjamind23f4122014-07-23 15:09:48 -0400323 {
324 name: "SessionTicketsDisabled-Client",
325 config: Config{
326 SessionTicketsDisabled: true,
327 },
328 },
329 {
330 testType: serverTest,
331 name: "SessionTicketsDisabled-Server",
332 config: Config{
333 SessionTicketsDisabled: true,
334 },
335 },
336 {
337 name: "SkipNewSessionTicket",
338 config: Config{
339 Bugs: ProtocolBugs{
340 SkipNewSessionTicket: true,
341 },
342 },
343 shouldFail: true,
344 expectedError: ":CCS_RECEIVED_EARLY:",
345 },
David Benjamin7e3305e2014-07-28 14:52:32 -0400346 {
347 name: "FalseStart",
348 config: Config{
349 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
David Benjamind86c7672014-08-02 04:07:12 -0400350 NextProtos: []string{"foo"},
David Benjamin7e3305e2014-07-28 14:52:32 -0400351 },
352 flags: []string{
353 "-false-start",
354 "-select-next-proto", "foo",
355 },
356 resumeSession: true,
357 },
358 {
359 name: "FalseStart-SessionTicketsDisabled",
360 config: Config{
David Benjamind86c7672014-08-02 04:07:12 -0400361 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
362 NextProtos: []string{"foo"},
David Benjamin7e3305e2014-07-28 14:52:32 -0400363 SessionTicketsDisabled: true,
364 },
365 flags: []string{
366 "-false-start",
367 "-select-next-proto", "foo",
368 },
369 },
David Benjamind86c7672014-08-02 04:07:12 -0400370 {
371 testType: serverTest,
372 name: "SendV2ClientHello",
373 config: Config{
374 // Choose a cipher suite that does not involve
375 // elliptic curves, so no extensions are
376 // involved.
377 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
378 Bugs: ProtocolBugs{
379 SendV2ClientHello: true,
380 },
381 },
382 },
Adam Langley95c29f32014-06-20 12:00:00 -0700383}
384
David Benjamin1d5c83e2014-07-22 19:20:02 -0400385func doExchange(testType testType, config *Config, conn net.Conn, messageLen int) error {
386 var tlsConn *Conn
387 if testType == clientTest {
388 tlsConn = Server(conn, config)
389 } else {
390 config.InsecureSkipVerify = true
391 tlsConn = Client(conn, config)
392 }
393
Adam Langley95c29f32014-06-20 12:00:00 -0700394 if err := tlsConn.Handshake(); err != nil {
395 return err
396 }
Adam Langley80842bd2014-06-20 12:00:00 -0700397 if messageLen == 0 {
398 messageLen = 32
399 }
400 testMessage := make([]byte, messageLen)
401 for i := range testMessage {
402 testMessage[i] = 0x42
403 }
Adam Langley95c29f32014-06-20 12:00:00 -0700404 tlsConn.Write(testMessage)
405
406 buf := make([]byte, len(testMessage))
407 _, err := io.ReadFull(tlsConn, buf)
408 if err != nil {
409 return err
410 }
411
412 for i, v := range buf {
413 if v != testMessage[i]^0xff {
414 return fmt.Errorf("bad reply contents at byte %d", i)
415 }
416 }
417
418 return nil
419}
420
David Benjamin325b5c32014-07-01 19:40:31 -0400421func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
422 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700423 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400424 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700425 }
David Benjamin325b5c32014-07-01 19:40:31 -0400426 valgrindArgs = append(valgrindArgs, path)
427 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700428
David Benjamin325b5c32014-07-01 19:40:31 -0400429 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700430}
431
David Benjamin325b5c32014-07-01 19:40:31 -0400432func gdbOf(path string, args ...string) *exec.Cmd {
433 xtermArgs := []string{"-e", "gdb", "--args"}
434 xtermArgs = append(xtermArgs, path)
435 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700436
David Benjamin325b5c32014-07-01 19:40:31 -0400437 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700438}
439
David Benjamin1d5c83e2014-07-22 19:20:02 -0400440func openSocketPair() (shimEnd *os.File, conn net.Conn) {
Adam Langley95c29f32014-06-20 12:00:00 -0700441 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
442 if err != nil {
443 panic(err)
444 }
445
446 syscall.CloseOnExec(socks[0])
447 syscall.CloseOnExec(socks[1])
David Benjamin1d5c83e2014-07-22 19:20:02 -0400448 shimEnd = os.NewFile(uintptr(socks[0]), "shim end")
Adam Langley95c29f32014-06-20 12:00:00 -0700449 connFile := os.NewFile(uintptr(socks[1]), "our end")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400450 conn, err = net.FileConn(connFile)
451 if err != nil {
452 panic(err)
453 }
Adam Langley95c29f32014-06-20 12:00:00 -0700454 connFile.Close()
455 if err != nil {
456 panic(err)
457 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400458 return shimEnd, conn
459}
460
461func runTest(test *testCase) error {
462 shimEnd, conn := openSocketPair()
463 shimEndResume, connResume := openSocketPair()
Adam Langley95c29f32014-06-20 12:00:00 -0700464
David Benjamin025b3d32014-07-01 19:53:04 -0400465 const shim_path = "../../../build/ssl/test/bssl_shim"
466 flags := []string{}
467 if test.testType == clientTest {
468 flags = append(flags, "client")
Adam Langley95c29f32014-06-20 12:00:00 -0700469 } else {
David Benjamin025b3d32014-07-01 19:53:04 -0400470 flags = append(flags, "server")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400471 }
Adam Langley95c29f32014-06-20 12:00:00 -0700472
David Benjamin1d5c83e2014-07-22 19:20:02 -0400473 if test.resumeSession {
474 flags = append(flags, "resume")
475 } else {
476 flags = append(flags, "normal")
477 }
478
479 if test.testType == serverTest {
David Benjamin025b3d32014-07-01 19:53:04 -0400480 flags = append(flags, "-key-file")
481 if test.keyFile == "" {
482 flags = append(flags, rsaKeyFile)
483 } else {
484 flags = append(flags, test.keyFile)
485 }
486
487 flags = append(flags, "-cert-file")
488 if test.certFile == "" {
489 flags = append(flags, rsaCertificateFile)
490 } else {
491 flags = append(flags, test.certFile)
492 }
493 }
494 flags = append(flags, test.flags...)
495
496 var shim *exec.Cmd
497 if *useValgrind {
498 shim = valgrindOf(false, shim_path, flags...)
499 } else {
500 shim = exec.Command(shim_path, flags...)
501 }
502 // shim = gdbOf(shim_path, flags...)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400503 shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
David Benjamin025b3d32014-07-01 19:53:04 -0400504 shim.Stdin = os.Stdin
505 var stdoutBuf, stderrBuf bytes.Buffer
506 shim.Stdout = &stdoutBuf
507 shim.Stderr = &stderrBuf
508
509 if err := shim.Start(); err != nil {
Adam Langley95c29f32014-06-20 12:00:00 -0700510 panic(err)
511 }
David Benjamin025b3d32014-07-01 19:53:04 -0400512 shimEnd.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400513 shimEndResume.Close()
Adam Langley95c29f32014-06-20 12:00:00 -0700514
515 config := test.config
David Benjamin1d5c83e2014-07-22 19:20:02 -0400516 config.ClientSessionCache = NewLRUClientSessionCache(1)
David Benjamin025b3d32014-07-01 19:53:04 -0400517 if test.testType == clientTest {
518 if len(config.Certificates) == 0 {
519 config.Certificates = []Certificate{getRSACertificate()}
520 }
David Benjamin025b3d32014-07-01 19:53:04 -0400521 }
Adam Langley95c29f32014-06-20 12:00:00 -0700522
David Benjamin1d5c83e2014-07-22 19:20:02 -0400523 err := doExchange(test.testType, &config, conn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700524 conn.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400525 if err == nil && test.resumeSession {
526 err = doExchange(test.testType, &config, connResume, test.messageLen)
527 connResume.Close()
528 }
529
David Benjamin025b3d32014-07-01 19:53:04 -0400530 childErr := shim.Wait()
Adam Langley95c29f32014-06-20 12:00:00 -0700531
532 stdout := string(stdoutBuf.Bytes())
533 stderr := string(stderrBuf.Bytes())
534 failed := err != nil || childErr != nil
535 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700536 localError := "none"
537 if err != nil {
538 localError = err.Error()
539 }
540 if len(test.expectedLocalError) != 0 {
541 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
542 }
Adam Langley95c29f32014-06-20 12:00:00 -0700543
544 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700545 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700546 if childErr != nil {
547 childError = childErr.Error()
548 }
549
550 var msg string
551 switch {
552 case failed && !test.shouldFail:
553 msg = "unexpected failure"
554 case !failed && test.shouldFail:
555 msg = "unexpected success"
556 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700557 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700558 default:
559 panic("internal error")
560 }
561
562 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
563 }
564
565 if !*useValgrind && len(stderr) > 0 {
566 println(stderr)
567 }
568
569 return nil
570}
571
572var tlsVersions = []struct {
573 name string
574 version uint16
575}{
576 {"SSL3", VersionSSL30},
577 {"TLS1", VersionTLS10},
578 {"TLS11", VersionTLS11},
579 {"TLS12", VersionTLS12},
580}
581
582var testCipherSuites = []struct {
583 name string
584 id uint16
585}{
586 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
587 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
588 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
589 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
590 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
591 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
592 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
593 {"ECDHE-RSA-3DES-SHA", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
594 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
595 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
596 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
597 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
598 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
599 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
600 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
601}
602
603func addCipherSuiteTests() {
604 for _, suite := range testCipherSuites {
605 var cert Certificate
David Benjamin025b3d32014-07-01 19:53:04 -0400606 var certFile string
607 var keyFile string
Adam Langley95c29f32014-06-20 12:00:00 -0700608 if strings.Contains(suite.name, "ECDSA") {
609 cert = getECDSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400610 certFile = ecdsaCertificateFile
611 keyFile = ecdsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700612 } else {
613 cert = getRSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400614 certFile = rsaCertificateFile
615 keyFile = rsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700616 }
617
618 for _, ver := range tlsVersions {
619 if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
620 continue
621 }
622
David Benjamin1d5c83e2014-07-22 19:20:02 -0400623 // Go's TLS implementation only implements session
624 // resumption with tickets, so SSLv3 cannot resume
625 // sessions.
626 resumeSession := ver.version != VersionSSL30
627
David Benjamin025b3d32014-07-01 19:53:04 -0400628 testCases = append(testCases, testCase{
629 testType: clientTest,
630 name: ver.name + "-" + suite.name + "-client",
Adam Langley95c29f32014-06-20 12:00:00 -0700631 config: Config{
632 MinVersion: ver.version,
633 MaxVersion: ver.version,
634 CipherSuites: []uint16{suite.id},
635 Certificates: []Certificate{cert},
636 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400637 resumeSession: resumeSession,
Adam Langley95c29f32014-06-20 12:00:00 -0700638 })
David Benjamin025b3d32014-07-01 19:53:04 -0400639
640 // Go's TLS implementation implements SSLv3 as a server,
641 // but not as a client.
642 //
643 // TODO(davidben): Implement SSLv3 as a client too to
644 // exercise that code.
645 if ver.version != VersionSSL30 {
646 testCases = append(testCases, testCase{
647 testType: serverTest,
648 name: ver.name + "-" + suite.name + "-server",
649 config: Config{
650 MinVersion: ver.version,
651 MaxVersion: ver.version,
652 CipherSuites: []uint16{suite.id},
653 Certificates: []Certificate{cert},
654 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400655 certFile: certFile,
656 keyFile: keyFile,
657 resumeSession: resumeSession,
David Benjamin025b3d32014-07-01 19:53:04 -0400658 })
659 }
Adam Langley95c29f32014-06-20 12:00:00 -0700660 }
661 }
662}
663
664func addBadECDSASignatureTests() {
665 for badR := BadValue(1); badR < NumBadValues; badR++ {
666 for badS := BadValue(1); badS < NumBadValues; badS++ {
David Benjamin025b3d32014-07-01 19:53:04 -0400667 testCases = append(testCases, testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700668 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
669 config: Config{
670 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
671 Certificates: []Certificate{getECDSACertificate()},
672 Bugs: ProtocolBugs{
673 BadECDSAR: badR,
674 BadECDSAS: badS,
675 },
676 },
677 shouldFail: true,
678 expectedError: "SIGNATURE",
679 })
680 }
681 }
682}
683
Adam Langley80842bd2014-06-20 12:00:00 -0700684func addCBCPaddingTests() {
David Benjamin025b3d32014-07-01 19:53:04 -0400685 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700686 name: "MaxCBCPadding",
687 config: Config{
688 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
689 Bugs: ProtocolBugs{
690 MaxPadding: true,
691 },
692 },
693 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
694 })
David Benjamin025b3d32014-07-01 19:53:04 -0400695 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700696 name: "BadCBCPadding",
697 config: Config{
698 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
699 Bugs: ProtocolBugs{
700 PaddingFirstByteBad: true,
701 },
702 },
703 shouldFail: true,
704 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
705 })
706 // OpenSSL previously had an issue where the first byte of padding in
707 // 255 bytes of padding wasn't checked.
David Benjamin025b3d32014-07-01 19:53:04 -0400708 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700709 name: "BadCBCPadding255",
710 config: Config{
711 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
712 Bugs: ProtocolBugs{
713 MaxPadding: true,
714 PaddingFirstByteBadIf255: true,
715 },
716 },
717 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
718 shouldFail: true,
719 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
720 })
721}
722
David Benjamin636293b2014-07-08 17:59:18 -0400723func addClientAuthTests() {
David Benjamin407a10c2014-07-16 12:58:59 -0400724 // Add a dummy cert pool to stress certificate authority parsing.
725 // TODO(davidben): Add tests that those values parse out correctly.
726 certPool := x509.NewCertPool()
727 cert, err := x509.ParseCertificate(rsaCertificate.Certificate[0])
728 if err != nil {
729 panic(err)
730 }
731 certPool.AddCert(cert)
732
David Benjamin636293b2014-07-08 17:59:18 -0400733 for _, ver := range tlsVersions {
734 if ver.version == VersionSSL30 {
735 // TODO(davidben): The Go implementation does not
736 // correctly compute CertificateVerify hashes for SSLv3.
737 continue
738 }
739
740 var cipherSuites []uint16
741 if ver.version >= VersionTLS12 {
742 // Pick a SHA-256 cipher suite. The Go implementation
743 // does not correctly handle client auth with a SHA-384
744 // cipher suite.
745 cipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}
746 }
747
748 testCases = append(testCases, testCase{
749 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400750 name: ver.name + "-Client-ClientAuth-RSA",
David Benjamin636293b2014-07-08 17:59:18 -0400751 config: Config{
752 MinVersion: ver.version,
753 MaxVersion: ver.version,
754 CipherSuites: cipherSuites,
755 ClientAuth: RequireAnyClientCert,
David Benjamin407a10c2014-07-16 12:58:59 -0400756 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400757 },
758 flags: []string{
759 "-cert-file", rsaCertificateFile,
760 "-key-file", rsaKeyFile,
761 },
762 })
763 testCases = append(testCases, testCase{
764 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400765 name: ver.name + "-Client-ClientAuth-ECDSA",
David Benjamin636293b2014-07-08 17:59:18 -0400766 config: Config{
767 MinVersion: ver.version,
768 MaxVersion: ver.version,
769 CipherSuites: cipherSuites,
770 ClientAuth: RequireAnyClientCert,
David Benjamin407a10c2014-07-16 12:58:59 -0400771 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400772 },
773 flags: []string{
774 "-cert-file", ecdsaCertificateFile,
775 "-key-file", ecdsaKeyFile,
776 },
777 })
David Benjamin67666e72014-07-12 15:47:52 -0400778 testCases = append(testCases, testCase{
779 testType: serverTest,
780 name: ver.name + "-Server-ClientAuth-RSA",
781 config: Config{
782 Certificates: []Certificate{rsaCertificate},
783 },
784 flags: []string{"-require-any-client-certificate"},
785 })
786 testCases = append(testCases, testCase{
787 testType: serverTest,
788 name: ver.name + "-Server-ClientAuth-ECDSA",
789 config: Config{
790 Certificates: []Certificate{ecdsaCertificate},
791 },
792 flags: []string{"-require-any-client-certificate"},
793 })
David Benjamin636293b2014-07-08 17:59:18 -0400794 }
795}
796
Adam Langley95c29f32014-06-20 12:00:00 -0700797func worker(statusChan chan statusMsg, c chan *testCase, wg *sync.WaitGroup) {
798 defer wg.Done()
799
800 for test := range c {
801 statusChan <- statusMsg{test: test, started: true}
802 err := runTest(test)
803 statusChan <- statusMsg{test: test, err: err}
804 }
805}
806
807type statusMsg struct {
808 test *testCase
809 started bool
810 err error
811}
812
813func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
814 var started, done, failed, lineLen int
815 defer close(doneChan)
816
817 for msg := range statusChan {
818 if msg.started {
819 started++
820 } else {
821 done++
822 }
823
824 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
825
826 if msg.err != nil {
827 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
828 failed++
829 }
830 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
831 lineLen = len(line)
832 os.Stdout.WriteString(line)
833 }
834}
835
836func main() {
837 var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests")
838
839 flag.Parse()
840
841 addCipherSuiteTests()
842 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -0700843 addCBCPaddingTests()
David Benjamin636293b2014-07-08 17:59:18 -0400844 addClientAuthTests()
Adam Langley95c29f32014-06-20 12:00:00 -0700845
846 var wg sync.WaitGroup
847
848 const numWorkers = 64
849
850 statusChan := make(chan statusMsg, numWorkers)
851 testChan := make(chan *testCase, numWorkers)
852 doneChan := make(chan struct{})
853
David Benjamin025b3d32014-07-01 19:53:04 -0400854 go statusPrinter(doneChan, statusChan, len(testCases))
Adam Langley95c29f32014-06-20 12:00:00 -0700855
856 for i := 0; i < numWorkers; i++ {
857 wg.Add(1)
858 go worker(statusChan, testChan, &wg)
859 }
860
David Benjamin025b3d32014-07-01 19:53:04 -0400861 for i := range testCases {
862 if len(*flagTest) == 0 || *flagTest == testCases[i].name {
863 testChan <- &testCases[i]
Adam Langley95c29f32014-06-20 12:00:00 -0700864 }
865 }
866
867 close(testChan)
868 wg.Wait()
869 close(statusChan)
870 <-doneChan
871
872 fmt.Printf("\n")
873}