blob: 7b1462a3ac9e536c0b662565b357ee4fbc1d3fe4 [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 },
David Benjamin7b030512014-07-08 17:30:11 -0400168 {
169 name: "ClientCertificateTypes",
170 config: Config{
171 ClientAuth: RequestClientCert,
172 ClientCertificateTypes: []byte{
173 CertTypeDSSSign,
174 CertTypeRSASign,
175 CertTypeECDSASign,
176 },
177 },
178 flags: []string{"-expect-certificate-types", string([]byte{
179 CertTypeDSSSign,
180 CertTypeRSASign,
181 CertTypeECDSASign,
182 })},
183 },
Adam Langley95c29f32014-06-20 12:00:00 -0700184}
185
Adam Langley80842bd2014-06-20 12:00:00 -0700186func doExchange(tlsConn *Conn, messageLen int) error {
Adam Langley95c29f32014-06-20 12:00:00 -0700187 if err := tlsConn.Handshake(); err != nil {
188 return err
189 }
Adam Langley80842bd2014-06-20 12:00:00 -0700190 if messageLen == 0 {
191 messageLen = 32
192 }
193 testMessage := make([]byte, messageLen)
194 for i := range testMessage {
195 testMessage[i] = 0x42
196 }
Adam Langley95c29f32014-06-20 12:00:00 -0700197 tlsConn.Write(testMessage)
198
199 buf := make([]byte, len(testMessage))
200 _, err := io.ReadFull(tlsConn, buf)
201 if err != nil {
202 return err
203 }
204
205 for i, v := range buf {
206 if v != testMessage[i]^0xff {
207 return fmt.Errorf("bad reply contents at byte %d", i)
208 }
209 }
210
211 return nil
212}
213
David Benjamin325b5c32014-07-01 19:40:31 -0400214func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
215 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700216 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400217 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700218 }
David Benjamin325b5c32014-07-01 19:40:31 -0400219 valgrindArgs = append(valgrindArgs, path)
220 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700221
David Benjamin325b5c32014-07-01 19:40:31 -0400222 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700223}
224
David Benjamin325b5c32014-07-01 19:40:31 -0400225func gdbOf(path string, args ...string) *exec.Cmd {
226 xtermArgs := []string{"-e", "gdb", "--args"}
227 xtermArgs = append(xtermArgs, path)
228 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700229
David Benjamin325b5c32014-07-01 19:40:31 -0400230 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700231}
232
233func runTest(test *testCase) error {
234 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
235 if err != nil {
236 panic(err)
237 }
238
239 syscall.CloseOnExec(socks[0])
240 syscall.CloseOnExec(socks[1])
David Benjamin025b3d32014-07-01 19:53:04 -0400241 shimEnd := os.NewFile(uintptr(socks[0]), "shim end")
Adam Langley95c29f32014-06-20 12:00:00 -0700242 connFile := os.NewFile(uintptr(socks[1]), "our end")
243 conn, err := net.FileConn(connFile)
244 connFile.Close()
245 if err != nil {
246 panic(err)
247 }
248
David Benjamin025b3d32014-07-01 19:53:04 -0400249 const shim_path = "../../../build/ssl/test/bssl_shim"
250 flags := []string{}
251 if test.testType == clientTest {
252 flags = append(flags, "client")
Adam Langley95c29f32014-06-20 12:00:00 -0700253 } else {
David Benjamin025b3d32014-07-01 19:53:04 -0400254 flags = append(flags, "server")
Adam Langley95c29f32014-06-20 12:00:00 -0700255
David Benjamin025b3d32014-07-01 19:53:04 -0400256 flags = append(flags, "-key-file")
257 if test.keyFile == "" {
258 flags = append(flags, rsaKeyFile)
259 } else {
260 flags = append(flags, test.keyFile)
261 }
262
263 flags = append(flags, "-cert-file")
264 if test.certFile == "" {
265 flags = append(flags, rsaCertificateFile)
266 } else {
267 flags = append(flags, test.certFile)
268 }
269 }
270 flags = append(flags, test.flags...)
271
272 var shim *exec.Cmd
273 if *useValgrind {
274 shim = valgrindOf(false, shim_path, flags...)
275 } else {
276 shim = exec.Command(shim_path, flags...)
277 }
278 // shim = gdbOf(shim_path, flags...)
279 shim.ExtraFiles = []*os.File{shimEnd}
280 shim.Stdin = os.Stdin
281 var stdoutBuf, stderrBuf bytes.Buffer
282 shim.Stdout = &stdoutBuf
283 shim.Stderr = &stderrBuf
284
285 if err := shim.Start(); err != nil {
Adam Langley95c29f32014-06-20 12:00:00 -0700286 panic(err)
287 }
David Benjamin025b3d32014-07-01 19:53:04 -0400288 shimEnd.Close()
Adam Langley95c29f32014-06-20 12:00:00 -0700289
290 config := test.config
Adam Langley95c29f32014-06-20 12:00:00 -0700291
David Benjamin025b3d32014-07-01 19:53:04 -0400292 var tlsConn *Conn
293 if test.testType == clientTest {
294 if len(config.Certificates) == 0 {
295 config.Certificates = []Certificate{getRSACertificate()}
296 }
297 tlsConn = Server(conn, &config)
298 } else {
299 config.InsecureSkipVerify = true
300 tlsConn = Client(conn, &config)
301 }
Adam Langley80842bd2014-06-20 12:00:00 -0700302 err = doExchange(tlsConn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700303
304 conn.Close()
David Benjamin025b3d32014-07-01 19:53:04 -0400305 childErr := shim.Wait()
Adam Langley95c29f32014-06-20 12:00:00 -0700306
307 stdout := string(stdoutBuf.Bytes())
308 stderr := string(stderrBuf.Bytes())
309 failed := err != nil || childErr != nil
310 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700311 localError := "none"
312 if err != nil {
313 localError = err.Error()
314 }
315 if len(test.expectedLocalError) != 0 {
316 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
317 }
Adam Langley95c29f32014-06-20 12:00:00 -0700318
319 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700320 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700321 if childErr != nil {
322 childError = childErr.Error()
323 }
324
325 var msg string
326 switch {
327 case failed && !test.shouldFail:
328 msg = "unexpected failure"
329 case !failed && test.shouldFail:
330 msg = "unexpected success"
331 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700332 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700333 default:
334 panic("internal error")
335 }
336
337 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
338 }
339
340 if !*useValgrind && len(stderr) > 0 {
341 println(stderr)
342 }
343
344 return nil
345}
346
347var tlsVersions = []struct {
348 name string
349 version uint16
350}{
351 {"SSL3", VersionSSL30},
352 {"TLS1", VersionTLS10},
353 {"TLS11", VersionTLS11},
354 {"TLS12", VersionTLS12},
355}
356
357var testCipherSuites = []struct {
358 name string
359 id uint16
360}{
361 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
362 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
363 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
364 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
365 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
366 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
367 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
368 {"ECDHE-RSA-3DES-SHA", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
369 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
370 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
371 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
372 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
373 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
374 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
375 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
376}
377
378func addCipherSuiteTests() {
379 for _, suite := range testCipherSuites {
380 var cert Certificate
David Benjamin025b3d32014-07-01 19:53:04 -0400381 var certFile string
382 var keyFile string
Adam Langley95c29f32014-06-20 12:00:00 -0700383 if strings.Contains(suite.name, "ECDSA") {
384 cert = getECDSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400385 certFile = ecdsaCertificateFile
386 keyFile = ecdsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700387 } else {
388 cert = getRSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400389 certFile = rsaCertificateFile
390 keyFile = rsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700391 }
392
393 for _, ver := range tlsVersions {
394 if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
395 continue
396 }
397
David Benjamin025b3d32014-07-01 19:53:04 -0400398 testCases = append(testCases, testCase{
399 testType: clientTest,
400 name: ver.name + "-" + suite.name + "-client",
Adam Langley95c29f32014-06-20 12:00:00 -0700401 config: Config{
402 MinVersion: ver.version,
403 MaxVersion: ver.version,
404 CipherSuites: []uint16{suite.id},
405 Certificates: []Certificate{cert},
406 },
407 })
David Benjamin025b3d32014-07-01 19:53:04 -0400408
409 // Go's TLS implementation implements SSLv3 as a server,
410 // but not as a client.
411 //
412 // TODO(davidben): Implement SSLv3 as a client too to
413 // exercise that code.
414 if ver.version != VersionSSL30 {
415 testCases = append(testCases, testCase{
416 testType: serverTest,
417 name: ver.name + "-" + suite.name + "-server",
418 config: Config{
419 MinVersion: ver.version,
420 MaxVersion: ver.version,
421 CipherSuites: []uint16{suite.id},
422 Certificates: []Certificate{cert},
423 },
424 certFile: certFile,
425 keyFile: keyFile,
426 })
427 }
Adam Langley95c29f32014-06-20 12:00:00 -0700428 }
429 }
430}
431
432func addBadECDSASignatureTests() {
433 for badR := BadValue(1); badR < NumBadValues; badR++ {
434 for badS := BadValue(1); badS < NumBadValues; badS++ {
David Benjamin025b3d32014-07-01 19:53:04 -0400435 testCases = append(testCases, testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700436 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
437 config: Config{
438 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
439 Certificates: []Certificate{getECDSACertificate()},
440 Bugs: ProtocolBugs{
441 BadECDSAR: badR,
442 BadECDSAS: badS,
443 },
444 },
445 shouldFail: true,
446 expectedError: "SIGNATURE",
447 })
448 }
449 }
450}
451
Adam Langley80842bd2014-06-20 12:00:00 -0700452func addCBCPaddingTests() {
David Benjamin025b3d32014-07-01 19:53:04 -0400453 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700454 name: "MaxCBCPadding",
455 config: Config{
456 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
457 Bugs: ProtocolBugs{
458 MaxPadding: true,
459 },
460 },
461 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
462 })
David Benjamin025b3d32014-07-01 19:53:04 -0400463 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700464 name: "BadCBCPadding",
465 config: Config{
466 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
467 Bugs: ProtocolBugs{
468 PaddingFirstByteBad: true,
469 },
470 },
471 shouldFail: true,
472 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
473 })
474 // OpenSSL previously had an issue where the first byte of padding in
475 // 255 bytes of padding wasn't checked.
David Benjamin025b3d32014-07-01 19:53:04 -0400476 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700477 name: "BadCBCPadding255",
478 config: Config{
479 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
480 Bugs: ProtocolBugs{
481 MaxPadding: true,
482 PaddingFirstByteBadIf255: true,
483 },
484 },
485 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
486 shouldFail: true,
487 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
488 })
489}
490
Adam Langley95c29f32014-06-20 12:00:00 -0700491func worker(statusChan chan statusMsg, c chan *testCase, wg *sync.WaitGroup) {
492 defer wg.Done()
493
494 for test := range c {
495 statusChan <- statusMsg{test: test, started: true}
496 err := runTest(test)
497 statusChan <- statusMsg{test: test, err: err}
498 }
499}
500
501type statusMsg struct {
502 test *testCase
503 started bool
504 err error
505}
506
507func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
508 var started, done, failed, lineLen int
509 defer close(doneChan)
510
511 for msg := range statusChan {
512 if msg.started {
513 started++
514 } else {
515 done++
516 }
517
518 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
519
520 if msg.err != nil {
521 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
522 failed++
523 }
524 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
525 lineLen = len(line)
526 os.Stdout.WriteString(line)
527 }
528}
529
530func main() {
531 var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests")
532
533 flag.Parse()
534
535 addCipherSuiteTests()
536 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -0700537 addCBCPaddingTests()
Adam Langley95c29f32014-06-20 12:00:00 -0700538
539 var wg sync.WaitGroup
540
541 const numWorkers = 64
542
543 statusChan := make(chan statusMsg, numWorkers)
544 testChan := make(chan *testCase, numWorkers)
545 doneChan := make(chan struct{})
546
David Benjamin025b3d32014-07-01 19:53:04 -0400547 go statusPrinter(doneChan, statusChan, len(testCases))
Adam Langley95c29f32014-06-20 12:00:00 -0700548
549 for i := 0; i < numWorkers; i++ {
550 wg.Add(1)
551 go worker(statusChan, testChan, &wg)
552 }
553
David Benjamin025b3d32014-07-01 19:53:04 -0400554 for i := range testCases {
555 if len(*flagTest) == 0 || *flagTest == testCases[i].name {
556 testChan <- &testCases[i]
Adam Langley95c29f32014-06-20 12:00:00 -0700557 }
558 }
559
560 close(testChan)
561 wg.Wait()
562 close(statusChan)
563 <-doneChan
564
565 fmt.Printf("\n")
566}