blob: 96b52fa47e727a25dc964dfad6b2dd81ab661306 [file] [log] [blame]
Adam Langley95c29f32014-06-20 12:00:00 -07001package main
2
3import (
4 "bytes"
5 "flag"
6 "fmt"
7 "io"
8 "net"
9 "os"
10 "os/exec"
11 "strings"
12 "sync"
13 "syscall"
14)
15
16var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
17
David Benjamin025b3d32014-07-01 19:53:04 -040018const (
19 rsaCertificateFile = "cert.pem"
20 ecdsaCertificateFile = "ecdsa_cert.pem"
21)
22
23const (
24 rsaKeyFile = "key.pem"
25 ecdsaKeyFile = "ecdsa_key.pem"
26)
27
Adam Langley95c29f32014-06-20 12:00:00 -070028var rsaCertificate, ecdsaCertificate Certificate
29
30func initCertificates() {
31 var err error
David Benjamin025b3d32014-07-01 19:53:04 -040032 rsaCertificate, err = LoadX509KeyPair(rsaCertificateFile, rsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070033 if err != nil {
34 panic(err)
35 }
36
David Benjamin025b3d32014-07-01 19:53:04 -040037 ecdsaCertificate, err = LoadX509KeyPair(ecdsaCertificateFile, ecdsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070038 if err != nil {
39 panic(err)
40 }
41}
42
43var certificateOnce sync.Once
44
45func getRSACertificate() Certificate {
46 certificateOnce.Do(initCertificates)
47 return rsaCertificate
48}
49
50func getECDSACertificate() Certificate {
51 certificateOnce.Do(initCertificates)
52 return ecdsaCertificate
53}
54
David Benjamin025b3d32014-07-01 19:53:04 -040055type testType int
56
57const (
58 clientTest testType = iota
59 serverTest
60)
61
Adam Langley95c29f32014-06-20 12:00:00 -070062type testCase struct {
David Benjamin025b3d32014-07-01 19:53:04 -040063 testType testType
Adam Langley95c29f32014-06-20 12:00:00 -070064 name string
65 config Config
66 shouldFail bool
67 expectedError string
Adam Langleyac61fa32014-06-23 12:03:11 -070068 // expectedLocalError, if not empty, contains a substring that must be
69 // found in the local error.
70 expectedLocalError string
Adam Langley80842bd2014-06-20 12:00:00 -070071 // messageLen is the length, in bytes, of the test message that will be
72 // sent.
73 messageLen int
David Benjamin025b3d32014-07-01 19:53:04 -040074 // certFile is the path to the certificate to use for the server.
75 certFile string
76 // keyFile is the path to the private key to use for the server.
77 keyFile string
David Benjamin325b5c32014-07-01 19:40:31 -040078 // flags, if not empty, contains a list of command-line flags that will
79 // be passed to the shim program.
80 flags []string
Adam Langley95c29f32014-06-20 12:00:00 -070081}
82
David Benjamin025b3d32014-07-01 19:53:04 -040083var testCases = []testCase{
Adam Langley95c29f32014-06-20 12:00:00 -070084 {
85 name: "BadRSASignature",
86 config: Config{
87 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
88 Bugs: ProtocolBugs{
89 InvalidSKXSignature: true,
90 },
91 },
92 shouldFail: true,
93 expectedError: ":BAD_SIGNATURE:",
94 },
95 {
96 name: "BadECDSASignature",
97 config: Config{
98 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
99 Bugs: ProtocolBugs{
100 InvalidSKXSignature: true,
101 },
102 Certificates: []Certificate{getECDSACertificate()},
103 },
104 shouldFail: true,
105 expectedError: ":BAD_SIGNATURE:",
106 },
107 {
108 name: "BadECDSACurve",
109 config: Config{
110 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
111 Bugs: ProtocolBugs{
112 InvalidSKXCurve: true,
113 },
114 Certificates: []Certificate{getECDSACertificate()},
115 },
116 shouldFail: true,
117 expectedError: ":WRONG_CURVE:",
118 },
Adam Langleyac61fa32014-06-23 12:03:11 -0700119 {
David Benjamin325b5c32014-07-01 19:40:31 -0400120 name: "NoFallbackSCSV",
Adam Langleyac61fa32014-06-23 12:03:11 -0700121 config: Config{
122 Bugs: ProtocolBugs{
123 FailIfNotFallbackSCSV: true,
124 },
125 },
126 shouldFail: true,
127 expectedLocalError: "no fallback SCSV found",
128 },
David Benjamin325b5c32014-07-01 19:40:31 -0400129 {
130 name: "FallbackSCSV",
131 config: Config{
132 Bugs: ProtocolBugs{
133 FailIfNotFallbackSCSV: true,
134 },
135 },
136 flags: []string{"-fallback-scsv"},
137 },
David Benjamin197b3ab2014-07-02 18:37:33 -0400138 {
139 testType: serverTest,
David Benjamin35a7a442014-07-05 00:23:20 -0400140 name: "ServerNameExtension",
David Benjamin197b3ab2014-07-02 18:37:33 -0400141 config: Config{
142 ServerName: "example.com",
143 },
144 flags: []string{"-expect-server-name", "example.com"},
145 },
David Benjamin35a7a442014-07-05 00:23:20 -0400146 {
147 testType: clientTest,
148 name: "DuplicateExtensionClient",
149 config: Config{
150 Bugs: ProtocolBugs{
151 DuplicateExtension: true,
152 },
153 },
154 shouldFail: true,
155 expectedLocalError: "remote error: error decoding message",
156 },
157 {
158 testType: serverTest,
159 name: "DuplicateExtensionServer",
160 config: Config{
161 Bugs: ProtocolBugs{
162 DuplicateExtension: true,
163 },
164 },
165 shouldFail: true,
166 expectedLocalError: "remote error: error decoding message",
167 },
Adam Langley95c29f32014-06-20 12:00:00 -0700168}
169
Adam Langley80842bd2014-06-20 12:00:00 -0700170func doExchange(tlsConn *Conn, messageLen int) error {
Adam Langley95c29f32014-06-20 12:00:00 -0700171 if err := tlsConn.Handshake(); err != nil {
172 return err
173 }
Adam Langley80842bd2014-06-20 12:00:00 -0700174 if messageLen == 0 {
175 messageLen = 32
176 }
177 testMessage := make([]byte, messageLen)
178 for i := range testMessage {
179 testMessage[i] = 0x42
180 }
Adam Langley95c29f32014-06-20 12:00:00 -0700181 tlsConn.Write(testMessage)
182
183 buf := make([]byte, len(testMessage))
184 _, err := io.ReadFull(tlsConn, buf)
185 if err != nil {
186 return err
187 }
188
189 for i, v := range buf {
190 if v != testMessage[i]^0xff {
191 return fmt.Errorf("bad reply contents at byte %d", i)
192 }
193 }
194
195 return nil
196}
197
David Benjamin325b5c32014-07-01 19:40:31 -0400198func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
199 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700200 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400201 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700202 }
David Benjamin325b5c32014-07-01 19:40:31 -0400203 valgrindArgs = append(valgrindArgs, path)
204 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700205
David Benjamin325b5c32014-07-01 19:40:31 -0400206 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700207}
208
David Benjamin325b5c32014-07-01 19:40:31 -0400209func gdbOf(path string, args ...string) *exec.Cmd {
210 xtermArgs := []string{"-e", "gdb", "--args"}
211 xtermArgs = append(xtermArgs, path)
212 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700213
David Benjamin325b5c32014-07-01 19:40:31 -0400214 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700215}
216
217func runTest(test *testCase) error {
218 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
219 if err != nil {
220 panic(err)
221 }
222
223 syscall.CloseOnExec(socks[0])
224 syscall.CloseOnExec(socks[1])
David Benjamin025b3d32014-07-01 19:53:04 -0400225 shimEnd := os.NewFile(uintptr(socks[0]), "shim end")
Adam Langley95c29f32014-06-20 12:00:00 -0700226 connFile := os.NewFile(uintptr(socks[1]), "our end")
227 conn, err := net.FileConn(connFile)
228 connFile.Close()
229 if err != nil {
230 panic(err)
231 }
232
David Benjamin025b3d32014-07-01 19:53:04 -0400233 const shim_path = "../../../build/ssl/test/bssl_shim"
234 flags := []string{}
235 if test.testType == clientTest {
236 flags = append(flags, "client")
Adam Langley95c29f32014-06-20 12:00:00 -0700237 } else {
David Benjamin025b3d32014-07-01 19:53:04 -0400238 flags = append(flags, "server")
Adam Langley95c29f32014-06-20 12:00:00 -0700239
David Benjamin025b3d32014-07-01 19:53:04 -0400240 flags = append(flags, "-key-file")
241 if test.keyFile == "" {
242 flags = append(flags, rsaKeyFile)
243 } else {
244 flags = append(flags, test.keyFile)
245 }
246
247 flags = append(flags, "-cert-file")
248 if test.certFile == "" {
249 flags = append(flags, rsaCertificateFile)
250 } else {
251 flags = append(flags, test.certFile)
252 }
253 }
254 flags = append(flags, test.flags...)
255
256 var shim *exec.Cmd
257 if *useValgrind {
258 shim = valgrindOf(false, shim_path, flags...)
259 } else {
260 shim = exec.Command(shim_path, flags...)
261 }
262 // shim = gdbOf(shim_path, flags...)
263 shim.ExtraFiles = []*os.File{shimEnd}
264 shim.Stdin = os.Stdin
265 var stdoutBuf, stderrBuf bytes.Buffer
266 shim.Stdout = &stdoutBuf
267 shim.Stderr = &stderrBuf
268
269 if err := shim.Start(); err != nil {
Adam Langley95c29f32014-06-20 12:00:00 -0700270 panic(err)
271 }
David Benjamin025b3d32014-07-01 19:53:04 -0400272 shimEnd.Close()
Adam Langley95c29f32014-06-20 12:00:00 -0700273
274 config := test.config
Adam Langley95c29f32014-06-20 12:00:00 -0700275
David Benjamin025b3d32014-07-01 19:53:04 -0400276 var tlsConn *Conn
277 if test.testType == clientTest {
278 if len(config.Certificates) == 0 {
279 config.Certificates = []Certificate{getRSACertificate()}
280 }
281 tlsConn = Server(conn, &config)
282 } else {
283 config.InsecureSkipVerify = true
284 tlsConn = Client(conn, &config)
285 }
Adam Langley80842bd2014-06-20 12:00:00 -0700286 err = doExchange(tlsConn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700287
288 conn.Close()
David Benjamin025b3d32014-07-01 19:53:04 -0400289 childErr := shim.Wait()
Adam Langley95c29f32014-06-20 12:00:00 -0700290
291 stdout := string(stdoutBuf.Bytes())
292 stderr := string(stderrBuf.Bytes())
293 failed := err != nil || childErr != nil
294 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700295 localError := "none"
296 if err != nil {
297 localError = err.Error()
298 }
299 if len(test.expectedLocalError) != 0 {
300 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
301 }
Adam Langley95c29f32014-06-20 12:00:00 -0700302
303 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700304 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700305 if childErr != nil {
306 childError = childErr.Error()
307 }
308
309 var msg string
310 switch {
311 case failed && !test.shouldFail:
312 msg = "unexpected failure"
313 case !failed && test.shouldFail:
314 msg = "unexpected success"
315 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700316 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700317 default:
318 panic("internal error")
319 }
320
321 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
322 }
323
324 if !*useValgrind && len(stderr) > 0 {
325 println(stderr)
326 }
327
328 return nil
329}
330
331var tlsVersions = []struct {
332 name string
333 version uint16
334}{
335 {"SSL3", VersionSSL30},
336 {"TLS1", VersionTLS10},
337 {"TLS11", VersionTLS11},
338 {"TLS12", VersionTLS12},
339}
340
341var testCipherSuites = []struct {
342 name string
343 id uint16
344}{
345 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
346 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
347 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
348 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
349 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
350 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
351 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
352 {"ECDHE-RSA-3DES-SHA", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
353 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
354 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
355 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
356 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
357 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
358 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
359 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
360}
361
362func addCipherSuiteTests() {
363 for _, suite := range testCipherSuites {
364 var cert Certificate
David Benjamin025b3d32014-07-01 19:53:04 -0400365 var certFile string
366 var keyFile string
Adam Langley95c29f32014-06-20 12:00:00 -0700367 if strings.Contains(suite.name, "ECDSA") {
368 cert = getECDSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400369 certFile = ecdsaCertificateFile
370 keyFile = ecdsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700371 } else {
372 cert = getRSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400373 certFile = rsaCertificateFile
374 keyFile = rsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700375 }
376
377 for _, ver := range tlsVersions {
378 if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
379 continue
380 }
381
David Benjamin025b3d32014-07-01 19:53:04 -0400382 testCases = append(testCases, testCase{
383 testType: clientTest,
384 name: ver.name + "-" + suite.name + "-client",
Adam Langley95c29f32014-06-20 12:00:00 -0700385 config: Config{
386 MinVersion: ver.version,
387 MaxVersion: ver.version,
388 CipherSuites: []uint16{suite.id},
389 Certificates: []Certificate{cert},
390 },
391 })
David Benjamin025b3d32014-07-01 19:53:04 -0400392
393 // Go's TLS implementation implements SSLv3 as a server,
394 // but not as a client.
395 //
396 // TODO(davidben): Implement SSLv3 as a client too to
397 // exercise that code.
398 if ver.version != VersionSSL30 {
399 testCases = append(testCases, testCase{
400 testType: serverTest,
401 name: ver.name + "-" + suite.name + "-server",
402 config: Config{
403 MinVersion: ver.version,
404 MaxVersion: ver.version,
405 CipherSuites: []uint16{suite.id},
406 Certificates: []Certificate{cert},
407 },
408 certFile: certFile,
409 keyFile: keyFile,
410 })
411 }
Adam Langley95c29f32014-06-20 12:00:00 -0700412 }
413 }
414}
415
416func addBadECDSASignatureTests() {
417 for badR := BadValue(1); badR < NumBadValues; badR++ {
418 for badS := BadValue(1); badS < NumBadValues; badS++ {
David Benjamin025b3d32014-07-01 19:53:04 -0400419 testCases = append(testCases, testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700420 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
421 config: Config{
422 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
423 Certificates: []Certificate{getECDSACertificate()},
424 Bugs: ProtocolBugs{
425 BadECDSAR: badR,
426 BadECDSAS: badS,
427 },
428 },
429 shouldFail: true,
430 expectedError: "SIGNATURE",
431 })
432 }
433 }
434}
435
Adam Langley80842bd2014-06-20 12:00:00 -0700436func addCBCPaddingTests() {
David Benjamin025b3d32014-07-01 19:53:04 -0400437 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700438 name: "MaxCBCPadding",
439 config: Config{
440 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
441 Bugs: ProtocolBugs{
442 MaxPadding: true,
443 },
444 },
445 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
446 })
David Benjamin025b3d32014-07-01 19:53:04 -0400447 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700448 name: "BadCBCPadding",
449 config: Config{
450 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
451 Bugs: ProtocolBugs{
452 PaddingFirstByteBad: true,
453 },
454 },
455 shouldFail: true,
456 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
457 })
458 // OpenSSL previously had an issue where the first byte of padding in
459 // 255 bytes of padding wasn't checked.
David Benjamin025b3d32014-07-01 19:53:04 -0400460 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700461 name: "BadCBCPadding255",
462 config: Config{
463 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
464 Bugs: ProtocolBugs{
465 MaxPadding: true,
466 PaddingFirstByteBadIf255: true,
467 },
468 },
469 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
470 shouldFail: true,
471 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
472 })
473}
474
Adam Langley95c29f32014-06-20 12:00:00 -0700475func worker(statusChan chan statusMsg, c chan *testCase, wg *sync.WaitGroup) {
476 defer wg.Done()
477
478 for test := range c {
479 statusChan <- statusMsg{test: test, started: true}
480 err := runTest(test)
481 statusChan <- statusMsg{test: test, err: err}
482 }
483}
484
485type statusMsg struct {
486 test *testCase
487 started bool
488 err error
489}
490
491func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
492 var started, done, failed, lineLen int
493 defer close(doneChan)
494
495 for msg := range statusChan {
496 if msg.started {
497 started++
498 } else {
499 done++
500 }
501
502 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
503
504 if msg.err != nil {
505 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
506 failed++
507 }
508 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
509 lineLen = len(line)
510 os.Stdout.WriteString(line)
511 }
512}
513
514func main() {
515 var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests")
516
517 flag.Parse()
518
519 addCipherSuiteTests()
520 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -0700521 addCBCPaddingTests()
Adam Langley95c29f32014-06-20 12:00:00 -0700522
523 var wg sync.WaitGroup
524
525 const numWorkers = 64
526
527 statusChan := make(chan statusMsg, numWorkers)
528 testChan := make(chan *testCase, numWorkers)
529 doneChan := make(chan struct{})
530
David Benjamin025b3d32014-07-01 19:53:04 -0400531 go statusPrinter(doneChan, statusChan, len(testCases))
Adam Langley95c29f32014-06-20 12:00:00 -0700532
533 for i := 0; i < numWorkers; i++ {
534 wg.Add(1)
535 go worker(statusChan, testChan, &wg)
536 }
537
David Benjamin025b3d32014-07-01 19:53:04 -0400538 for i := range testCases {
539 if len(*flagTest) == 0 || *flagTest == testCases[i].name {
540 testChan <- &testCases[i]
Adam Langley95c29f32014-06-20 12:00:00 -0700541 }
542 }
543
544 close(testChan)
545 wg.Wait()
546 close(statusChan)
547 <-doneChan
548
549 fmt.Printf("\n")
550}