blob: 539a7464e8be5f492f561d2e92a807054d82175b [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,
237 expectedError: ":GOT_A_FIN_BEFORE_A_CCS:",
238 },
239 {
240 testType: serverTest,
241 name: "SkipChangeCipherSpec-Server",
242 config: Config{
243 Bugs: ProtocolBugs{
244 SkipChangeCipherSpec: true,
245 },
246 },
247 shouldFail: true,
248 expectedError: ":GOT_A_FIN_BEFORE_A_CCS:",
249 },
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,
263 expectedError: ":GOT_NEXT_PROTO_BEFORE_A_CCS:",
264 },
Adam Langley95c29f32014-06-20 12:00:00 -0700265}
266
David Benjamin1d5c83e2014-07-22 19:20:02 -0400267func doExchange(testType testType, config *Config, conn net.Conn, messageLen int) error {
268 var tlsConn *Conn
269 if testType == clientTest {
270 tlsConn = Server(conn, config)
271 } else {
272 config.InsecureSkipVerify = true
273 tlsConn = Client(conn, config)
274 }
275
Adam Langley95c29f32014-06-20 12:00:00 -0700276 if err := tlsConn.Handshake(); err != nil {
277 return err
278 }
Adam Langley80842bd2014-06-20 12:00:00 -0700279 if messageLen == 0 {
280 messageLen = 32
281 }
282 testMessage := make([]byte, messageLen)
283 for i := range testMessage {
284 testMessage[i] = 0x42
285 }
Adam Langley95c29f32014-06-20 12:00:00 -0700286 tlsConn.Write(testMessage)
287
288 buf := make([]byte, len(testMessage))
289 _, err := io.ReadFull(tlsConn, buf)
290 if err != nil {
291 return err
292 }
293
294 for i, v := range buf {
295 if v != testMessage[i]^0xff {
296 return fmt.Errorf("bad reply contents at byte %d", i)
297 }
298 }
299
300 return nil
301}
302
David Benjamin325b5c32014-07-01 19:40:31 -0400303func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
304 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700305 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400306 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700307 }
David Benjamin325b5c32014-07-01 19:40:31 -0400308 valgrindArgs = append(valgrindArgs, path)
309 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700310
David Benjamin325b5c32014-07-01 19:40:31 -0400311 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700312}
313
David Benjamin325b5c32014-07-01 19:40:31 -0400314func gdbOf(path string, args ...string) *exec.Cmd {
315 xtermArgs := []string{"-e", "gdb", "--args"}
316 xtermArgs = append(xtermArgs, path)
317 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700318
David Benjamin325b5c32014-07-01 19:40:31 -0400319 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700320}
321
David Benjamin1d5c83e2014-07-22 19:20:02 -0400322func openSocketPair() (shimEnd *os.File, conn net.Conn) {
Adam Langley95c29f32014-06-20 12:00:00 -0700323 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
324 if err != nil {
325 panic(err)
326 }
327
328 syscall.CloseOnExec(socks[0])
329 syscall.CloseOnExec(socks[1])
David Benjamin1d5c83e2014-07-22 19:20:02 -0400330 shimEnd = os.NewFile(uintptr(socks[0]), "shim end")
Adam Langley95c29f32014-06-20 12:00:00 -0700331 connFile := os.NewFile(uintptr(socks[1]), "our end")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400332 conn, err = net.FileConn(connFile)
333 if err != nil {
334 panic(err)
335 }
Adam Langley95c29f32014-06-20 12:00:00 -0700336 connFile.Close()
337 if err != nil {
338 panic(err)
339 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400340 return shimEnd, conn
341}
342
343func runTest(test *testCase) error {
344 shimEnd, conn := openSocketPair()
345 shimEndResume, connResume := openSocketPair()
Adam Langley95c29f32014-06-20 12:00:00 -0700346
David Benjamin025b3d32014-07-01 19:53:04 -0400347 const shim_path = "../../../build/ssl/test/bssl_shim"
348 flags := []string{}
349 if test.testType == clientTest {
350 flags = append(flags, "client")
Adam Langley95c29f32014-06-20 12:00:00 -0700351 } else {
David Benjamin025b3d32014-07-01 19:53:04 -0400352 flags = append(flags, "server")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400353 }
Adam Langley95c29f32014-06-20 12:00:00 -0700354
David Benjamin1d5c83e2014-07-22 19:20:02 -0400355 if test.resumeSession {
356 flags = append(flags, "resume")
357 } else {
358 flags = append(flags, "normal")
359 }
360
361 if test.testType == serverTest {
David Benjamin025b3d32014-07-01 19:53:04 -0400362 flags = append(flags, "-key-file")
363 if test.keyFile == "" {
364 flags = append(flags, rsaKeyFile)
365 } else {
366 flags = append(flags, test.keyFile)
367 }
368
369 flags = append(flags, "-cert-file")
370 if test.certFile == "" {
371 flags = append(flags, rsaCertificateFile)
372 } else {
373 flags = append(flags, test.certFile)
374 }
375 }
376 flags = append(flags, test.flags...)
377
378 var shim *exec.Cmd
379 if *useValgrind {
380 shim = valgrindOf(false, shim_path, flags...)
381 } else {
382 shim = exec.Command(shim_path, flags...)
383 }
384 // shim = gdbOf(shim_path, flags...)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400385 shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
David Benjamin025b3d32014-07-01 19:53:04 -0400386 shim.Stdin = os.Stdin
387 var stdoutBuf, stderrBuf bytes.Buffer
388 shim.Stdout = &stdoutBuf
389 shim.Stderr = &stderrBuf
390
391 if err := shim.Start(); err != nil {
Adam Langley95c29f32014-06-20 12:00:00 -0700392 panic(err)
393 }
David Benjamin025b3d32014-07-01 19:53:04 -0400394 shimEnd.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400395 shimEndResume.Close()
Adam Langley95c29f32014-06-20 12:00:00 -0700396
397 config := test.config
David Benjamin1d5c83e2014-07-22 19:20:02 -0400398 config.ClientSessionCache = NewLRUClientSessionCache(1)
David Benjamin025b3d32014-07-01 19:53:04 -0400399 if test.testType == clientTest {
400 if len(config.Certificates) == 0 {
401 config.Certificates = []Certificate{getRSACertificate()}
402 }
David Benjamin025b3d32014-07-01 19:53:04 -0400403 }
Adam Langley95c29f32014-06-20 12:00:00 -0700404
David Benjamin1d5c83e2014-07-22 19:20:02 -0400405 err := doExchange(test.testType, &config, conn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700406 conn.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400407 if err == nil && test.resumeSession {
408 err = doExchange(test.testType, &config, connResume, test.messageLen)
409 connResume.Close()
410 }
411
David Benjamin025b3d32014-07-01 19:53:04 -0400412 childErr := shim.Wait()
Adam Langley95c29f32014-06-20 12:00:00 -0700413
414 stdout := string(stdoutBuf.Bytes())
415 stderr := string(stderrBuf.Bytes())
416 failed := err != nil || childErr != nil
417 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700418 localError := "none"
419 if err != nil {
420 localError = err.Error()
421 }
422 if len(test.expectedLocalError) != 0 {
423 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
424 }
Adam Langley95c29f32014-06-20 12:00:00 -0700425
426 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700427 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700428 if childErr != nil {
429 childError = childErr.Error()
430 }
431
432 var msg string
433 switch {
434 case failed && !test.shouldFail:
435 msg = "unexpected failure"
436 case !failed && test.shouldFail:
437 msg = "unexpected success"
438 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700439 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700440 default:
441 panic("internal error")
442 }
443
444 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
445 }
446
447 if !*useValgrind && len(stderr) > 0 {
448 println(stderr)
449 }
450
451 return nil
452}
453
454var tlsVersions = []struct {
455 name string
456 version uint16
457}{
458 {"SSL3", VersionSSL30},
459 {"TLS1", VersionTLS10},
460 {"TLS11", VersionTLS11},
461 {"TLS12", VersionTLS12},
462}
463
464var testCipherSuites = []struct {
465 name string
466 id uint16
467}{
468 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
469 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
470 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
471 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
472 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
473 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
474 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
475 {"ECDHE-RSA-3DES-SHA", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
476 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
477 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
478 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
479 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
480 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
481 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
482 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
483}
484
485func addCipherSuiteTests() {
486 for _, suite := range testCipherSuites {
487 var cert Certificate
David Benjamin025b3d32014-07-01 19:53:04 -0400488 var certFile string
489 var keyFile string
Adam Langley95c29f32014-06-20 12:00:00 -0700490 if strings.Contains(suite.name, "ECDSA") {
491 cert = getECDSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400492 certFile = ecdsaCertificateFile
493 keyFile = ecdsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700494 } else {
495 cert = getRSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400496 certFile = rsaCertificateFile
497 keyFile = rsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700498 }
499
500 for _, ver := range tlsVersions {
501 if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
502 continue
503 }
504
David Benjamin1d5c83e2014-07-22 19:20:02 -0400505 // Go's TLS implementation only implements session
506 // resumption with tickets, so SSLv3 cannot resume
507 // sessions.
508 resumeSession := ver.version != VersionSSL30
509
David Benjamin025b3d32014-07-01 19:53:04 -0400510 testCases = append(testCases, testCase{
511 testType: clientTest,
512 name: ver.name + "-" + suite.name + "-client",
Adam Langley95c29f32014-06-20 12:00:00 -0700513 config: Config{
514 MinVersion: ver.version,
515 MaxVersion: ver.version,
516 CipherSuites: []uint16{suite.id},
517 Certificates: []Certificate{cert},
518 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400519 resumeSession: resumeSession,
Adam Langley95c29f32014-06-20 12:00:00 -0700520 })
David Benjamin025b3d32014-07-01 19:53:04 -0400521
522 // Go's TLS implementation implements SSLv3 as a server,
523 // but not as a client.
524 //
525 // TODO(davidben): Implement SSLv3 as a client too to
526 // exercise that code.
527 if ver.version != VersionSSL30 {
528 testCases = append(testCases, testCase{
529 testType: serverTest,
530 name: ver.name + "-" + suite.name + "-server",
531 config: Config{
532 MinVersion: ver.version,
533 MaxVersion: ver.version,
534 CipherSuites: []uint16{suite.id},
535 Certificates: []Certificate{cert},
536 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400537 certFile: certFile,
538 keyFile: keyFile,
539 resumeSession: resumeSession,
David Benjamin025b3d32014-07-01 19:53:04 -0400540 })
541 }
Adam Langley95c29f32014-06-20 12:00:00 -0700542 }
543 }
544}
545
546func addBadECDSASignatureTests() {
547 for badR := BadValue(1); badR < NumBadValues; badR++ {
548 for badS := BadValue(1); badS < NumBadValues; badS++ {
David Benjamin025b3d32014-07-01 19:53:04 -0400549 testCases = append(testCases, testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700550 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
551 config: Config{
552 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
553 Certificates: []Certificate{getECDSACertificate()},
554 Bugs: ProtocolBugs{
555 BadECDSAR: badR,
556 BadECDSAS: badS,
557 },
558 },
559 shouldFail: true,
560 expectedError: "SIGNATURE",
561 })
562 }
563 }
564}
565
Adam Langley80842bd2014-06-20 12:00:00 -0700566func addCBCPaddingTests() {
David Benjamin025b3d32014-07-01 19:53:04 -0400567 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700568 name: "MaxCBCPadding",
569 config: Config{
570 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
571 Bugs: ProtocolBugs{
572 MaxPadding: true,
573 },
574 },
575 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
576 })
David Benjamin025b3d32014-07-01 19:53:04 -0400577 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700578 name: "BadCBCPadding",
579 config: Config{
580 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
581 Bugs: ProtocolBugs{
582 PaddingFirstByteBad: true,
583 },
584 },
585 shouldFail: true,
586 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
587 })
588 // OpenSSL previously had an issue where the first byte of padding in
589 // 255 bytes of padding wasn't checked.
David Benjamin025b3d32014-07-01 19:53:04 -0400590 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700591 name: "BadCBCPadding255",
592 config: Config{
593 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
594 Bugs: ProtocolBugs{
595 MaxPadding: true,
596 PaddingFirstByteBadIf255: true,
597 },
598 },
599 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
600 shouldFail: true,
601 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
602 })
603}
604
David Benjamin636293b2014-07-08 17:59:18 -0400605func addClientAuthTests() {
David Benjamin407a10c2014-07-16 12:58:59 -0400606 // Add a dummy cert pool to stress certificate authority parsing.
607 // TODO(davidben): Add tests that those values parse out correctly.
608 certPool := x509.NewCertPool()
609 cert, err := x509.ParseCertificate(rsaCertificate.Certificate[0])
610 if err != nil {
611 panic(err)
612 }
613 certPool.AddCert(cert)
614
David Benjamin636293b2014-07-08 17:59:18 -0400615 for _, ver := range tlsVersions {
616 if ver.version == VersionSSL30 {
617 // TODO(davidben): The Go implementation does not
618 // correctly compute CertificateVerify hashes for SSLv3.
619 continue
620 }
621
622 var cipherSuites []uint16
623 if ver.version >= VersionTLS12 {
624 // Pick a SHA-256 cipher suite. The Go implementation
625 // does not correctly handle client auth with a SHA-384
626 // cipher suite.
627 cipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}
628 }
629
630 testCases = append(testCases, testCase{
631 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400632 name: ver.name + "-Client-ClientAuth-RSA",
David Benjamin636293b2014-07-08 17:59:18 -0400633 config: Config{
634 MinVersion: ver.version,
635 MaxVersion: ver.version,
636 CipherSuites: cipherSuites,
637 ClientAuth: RequireAnyClientCert,
David Benjamin407a10c2014-07-16 12:58:59 -0400638 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400639 },
640 flags: []string{
641 "-cert-file", rsaCertificateFile,
642 "-key-file", rsaKeyFile,
643 },
644 })
645 testCases = append(testCases, testCase{
646 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400647 name: ver.name + "-Client-ClientAuth-ECDSA",
David Benjamin636293b2014-07-08 17:59:18 -0400648 config: Config{
649 MinVersion: ver.version,
650 MaxVersion: ver.version,
651 CipherSuites: cipherSuites,
652 ClientAuth: RequireAnyClientCert,
David Benjamin407a10c2014-07-16 12:58:59 -0400653 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400654 },
655 flags: []string{
656 "-cert-file", ecdsaCertificateFile,
657 "-key-file", ecdsaKeyFile,
658 },
659 })
David Benjamin67666e72014-07-12 15:47:52 -0400660 testCases = append(testCases, testCase{
661 testType: serverTest,
662 name: ver.name + "-Server-ClientAuth-RSA",
663 config: Config{
664 Certificates: []Certificate{rsaCertificate},
665 },
666 flags: []string{"-require-any-client-certificate"},
667 })
668 testCases = append(testCases, testCase{
669 testType: serverTest,
670 name: ver.name + "-Server-ClientAuth-ECDSA",
671 config: Config{
672 Certificates: []Certificate{ecdsaCertificate},
673 },
674 flags: []string{"-require-any-client-certificate"},
675 })
David Benjamin636293b2014-07-08 17:59:18 -0400676 }
677}
678
Adam Langley95c29f32014-06-20 12:00:00 -0700679func worker(statusChan chan statusMsg, c chan *testCase, wg *sync.WaitGroup) {
680 defer wg.Done()
681
682 for test := range c {
683 statusChan <- statusMsg{test: test, started: true}
684 err := runTest(test)
685 statusChan <- statusMsg{test: test, err: err}
686 }
687}
688
689type statusMsg struct {
690 test *testCase
691 started bool
692 err error
693}
694
695func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
696 var started, done, failed, lineLen int
697 defer close(doneChan)
698
699 for msg := range statusChan {
700 if msg.started {
701 started++
702 } else {
703 done++
704 }
705
706 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
707
708 if msg.err != nil {
709 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
710 failed++
711 }
712 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
713 lineLen = len(line)
714 os.Stdout.WriteString(line)
715 }
716}
717
718func main() {
719 var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests")
720
721 flag.Parse()
722
723 addCipherSuiteTests()
724 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -0700725 addCBCPaddingTests()
David Benjamin636293b2014-07-08 17:59:18 -0400726 addClientAuthTests()
Adam Langley95c29f32014-06-20 12:00:00 -0700727
728 var wg sync.WaitGroup
729
730 const numWorkers = 64
731
732 statusChan := make(chan statusMsg, numWorkers)
733 testChan := make(chan *testCase, numWorkers)
734 doneChan := make(chan struct{})
735
David Benjamin025b3d32014-07-01 19:53:04 -0400736 go statusPrinter(doneChan, statusChan, len(testCases))
Adam Langley95c29f32014-06-20 12:00:00 -0700737
738 for i := 0; i < numWorkers; i++ {
739 wg.Add(1)
740 go worker(statusChan, testChan, &wg)
741 }
742
David Benjamin025b3d32014-07-01 19:53:04 -0400743 for i := range testCases {
744 if len(*flagTest) == 0 || *flagTest == testCases[i].name {
745 testChan <- &testCases[i]
Adam Langley95c29f32014-06-20 12:00:00 -0700746 }
747 }
748
749 close(testChan)
750 wg.Wait()
751 close(statusChan)
752 <-doneChan
753
754 fmt.Printf("\n")
755}