blob: 3edbd8bf3c195cd0447be5c3dc798d39a169e5e8 [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"
Kenny Root7fdeaf12014-08-05 15:23:37 -07009 "io/ioutil"
Adam Langley95c29f32014-06-20 12:00:00 -070010 "net"
11 "os"
12 "os/exec"
David Benjamin884fdf12014-08-02 15:28:23 -040013 "path"
David Benjamin2bc8e6f2014-08-02 15:22:37 -040014 "runtime"
Adam Langley95c29f32014-06-20 12:00:00 -070015 "strings"
16 "sync"
17 "syscall"
18)
19
20var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
21
David Benjamin025b3d32014-07-01 19:53:04 -040022const (
23 rsaCertificateFile = "cert.pem"
24 ecdsaCertificateFile = "ecdsa_cert.pem"
25)
26
27const (
28 rsaKeyFile = "key.pem"
29 ecdsaKeyFile = "ecdsa_key.pem"
30)
31
Adam Langley95c29f32014-06-20 12:00:00 -070032var rsaCertificate, ecdsaCertificate Certificate
33
34func initCertificates() {
35 var err error
David Benjamin025b3d32014-07-01 19:53:04 -040036 rsaCertificate, err = LoadX509KeyPair(rsaCertificateFile, rsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070037 if err != nil {
38 panic(err)
39 }
40
David Benjamin025b3d32014-07-01 19:53:04 -040041 ecdsaCertificate, err = LoadX509KeyPair(ecdsaCertificateFile, ecdsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070042 if err != nil {
43 panic(err)
44 }
45}
46
47var certificateOnce sync.Once
48
49func getRSACertificate() Certificate {
50 certificateOnce.Do(initCertificates)
51 return rsaCertificate
52}
53
54func getECDSACertificate() Certificate {
55 certificateOnce.Do(initCertificates)
56 return ecdsaCertificate
57}
58
David Benjamin025b3d32014-07-01 19:53:04 -040059type testType int
60
61const (
62 clientTest testType = iota
63 serverTest
64)
65
Adam Langley95c29f32014-06-20 12:00:00 -070066type testCase struct {
David Benjamin025b3d32014-07-01 19:53:04 -040067 testType testType
Adam Langley95c29f32014-06-20 12:00:00 -070068 name string
69 config Config
70 shouldFail bool
71 expectedError string
Adam Langleyac61fa32014-06-23 12:03:11 -070072 // expectedLocalError, if not empty, contains a substring that must be
73 // found in the local error.
74 expectedLocalError string
Adam Langley80842bd2014-06-20 12:00:00 -070075 // messageLen is the length, in bytes, of the test message that will be
76 // sent.
77 messageLen int
David Benjamin025b3d32014-07-01 19:53:04 -040078 // certFile is the path to the certificate to use for the server.
79 certFile string
80 // keyFile is the path to the private key to use for the server.
81 keyFile string
David Benjamin1d5c83e2014-07-22 19:20:02 -040082 // resumeSession controls whether a second connection should be tested
83 // which resumes the first session.
84 resumeSession bool
David Benjamin325b5c32014-07-01 19:40:31 -040085 // flags, if not empty, contains a list of command-line flags that will
86 // be passed to the shim program.
87 flags []string
Adam Langley95c29f32014-06-20 12:00:00 -070088}
89
David Benjamin025b3d32014-07-01 19:53:04 -040090var testCases = []testCase{
Adam Langley95c29f32014-06-20 12:00:00 -070091 {
92 name: "BadRSASignature",
93 config: Config{
94 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
95 Bugs: ProtocolBugs{
96 InvalidSKXSignature: true,
97 },
98 },
99 shouldFail: true,
100 expectedError: ":BAD_SIGNATURE:",
101 },
102 {
103 name: "BadECDSASignature",
104 config: Config{
105 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
106 Bugs: ProtocolBugs{
107 InvalidSKXSignature: true,
108 },
109 Certificates: []Certificate{getECDSACertificate()},
110 },
111 shouldFail: true,
112 expectedError: ":BAD_SIGNATURE:",
113 },
114 {
115 name: "BadECDSACurve",
116 config: Config{
117 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
118 Bugs: ProtocolBugs{
119 InvalidSKXCurve: true,
120 },
121 Certificates: []Certificate{getECDSACertificate()},
122 },
123 shouldFail: true,
124 expectedError: ":WRONG_CURVE:",
125 },
Adam Langleyac61fa32014-06-23 12:03:11 -0700126 {
David Benjamin325b5c32014-07-01 19:40:31 -0400127 name: "NoFallbackSCSV",
Adam Langleyac61fa32014-06-23 12:03:11 -0700128 config: Config{
129 Bugs: ProtocolBugs{
130 FailIfNotFallbackSCSV: true,
131 },
132 },
133 shouldFail: true,
134 expectedLocalError: "no fallback SCSV found",
135 },
David Benjamin325b5c32014-07-01 19:40:31 -0400136 {
137 name: "FallbackSCSV",
138 config: Config{
139 Bugs: ProtocolBugs{
140 FailIfNotFallbackSCSV: true,
141 },
142 },
143 flags: []string{"-fallback-scsv"},
144 },
David Benjamin197b3ab2014-07-02 18:37:33 -0400145 {
146 testType: serverTest,
David Benjamin35a7a442014-07-05 00:23:20 -0400147 name: "ServerNameExtension",
David Benjamin197b3ab2014-07-02 18:37:33 -0400148 config: Config{
149 ServerName: "example.com",
150 },
151 flags: []string{"-expect-server-name", "example.com"},
152 },
David Benjamin35a7a442014-07-05 00:23:20 -0400153 {
154 testType: clientTest,
155 name: "DuplicateExtensionClient",
156 config: Config{
157 Bugs: ProtocolBugs{
158 DuplicateExtension: true,
159 },
160 },
161 shouldFail: true,
162 expectedLocalError: "remote error: error decoding message",
163 },
164 {
165 testType: serverTest,
166 name: "DuplicateExtensionServer",
167 config: Config{
168 Bugs: ProtocolBugs{
169 DuplicateExtension: true,
170 },
171 },
172 shouldFail: true,
173 expectedLocalError: "remote error: error decoding message",
174 },
David Benjamin7b030512014-07-08 17:30:11 -0400175 {
176 name: "ClientCertificateTypes",
177 config: Config{
178 ClientAuth: RequestClientCert,
179 ClientCertificateTypes: []byte{
180 CertTypeDSSSign,
181 CertTypeRSASign,
182 CertTypeECDSASign,
183 },
184 },
185 flags: []string{"-expect-certificate-types", string([]byte{
186 CertTypeDSSSign,
187 CertTypeRSASign,
188 CertTypeECDSASign,
189 })},
190 },
David Benjamin636293b2014-07-08 17:59:18 -0400191 {
192 name: "NoClientCertificate",
193 config: Config{
194 ClientAuth: RequireAnyClientCert,
195 },
196 shouldFail: true,
197 expectedLocalError: "client didn't provide a certificate",
198 },
David Benjamin1c375dd2014-07-12 00:48:23 -0400199 {
200 name: "UnauthenticatedECDH",
201 config: Config{
202 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
203 Bugs: ProtocolBugs{
204 UnauthenticatedECDH: true,
205 },
206 },
207 shouldFail: true,
David Benjamine8f3d662014-07-12 01:10:19 -0400208 expectedError: ":UNEXPECTED_MESSAGE:",
David Benjamin1c375dd2014-07-12 00:48:23 -0400209 },
David Benjamin9c651c92014-07-12 13:27:45 -0400210 {
211 name: "SkipServerKeyExchange",
212 config: Config{
213 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
214 Bugs: ProtocolBugs{
215 SkipServerKeyExchange: true,
216 },
217 },
218 shouldFail: true,
219 expectedError: ":UNEXPECTED_MESSAGE:",
220 },
David Benjamin1f5f62b2014-07-12 16:18:02 -0400221 {
David Benjamina0e52232014-07-19 17:39:58 -0400222 name: "SkipChangeCipherSpec-Client",
223 config: Config{
224 Bugs: ProtocolBugs{
225 SkipChangeCipherSpec: true,
226 },
227 },
228 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400229 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400230 },
231 {
232 testType: serverTest,
233 name: "SkipChangeCipherSpec-Server",
234 config: Config{
235 Bugs: ProtocolBugs{
236 SkipChangeCipherSpec: true,
237 },
238 },
239 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400240 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400241 },
David Benjamin42be6452014-07-21 14:50:23 -0400242 {
243 testType: serverTest,
244 name: "SkipChangeCipherSpec-Server-NPN",
245 config: Config{
246 NextProtos: []string{"bar"},
247 Bugs: ProtocolBugs{
248 SkipChangeCipherSpec: true,
249 },
250 },
251 flags: []string{
252 "-advertise-npn", "\x03foo\x03bar\x03baz",
253 },
254 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400255 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
256 },
257 {
258 name: "FragmentAcrossChangeCipherSpec-Client",
259 config: Config{
260 Bugs: ProtocolBugs{
261 FragmentAcrossChangeCipherSpec: true,
262 },
263 },
264 shouldFail: true,
265 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
266 },
267 {
268 testType: serverTest,
269 name: "FragmentAcrossChangeCipherSpec-Server",
270 config: Config{
271 Bugs: ProtocolBugs{
272 FragmentAcrossChangeCipherSpec: true,
273 },
274 },
275 shouldFail: true,
276 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
277 },
278 {
279 testType: serverTest,
280 name: "FragmentAcrossChangeCipherSpec-Server-NPN",
281 config: Config{
282 NextProtos: []string{"bar"},
283 Bugs: ProtocolBugs{
284 FragmentAcrossChangeCipherSpec: true,
285 },
286 },
287 flags: []string{
288 "-advertise-npn", "\x03foo\x03bar\x03baz",
289 },
290 shouldFail: true,
291 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamin42be6452014-07-21 14:50:23 -0400292 },
David Benjaminf3ec83d2014-07-21 22:42:34 -0400293 {
294 testType: serverTest,
295 name: "EarlyChangeCipherSpec-server-1",
296 config: Config{
297 Bugs: ProtocolBugs{
298 EarlyChangeCipherSpec: 1,
299 },
300 },
301 shouldFail: true,
302 expectedError: ":CCS_RECEIVED_EARLY:",
303 },
304 {
305 testType: serverTest,
306 name: "EarlyChangeCipherSpec-server-2",
307 config: Config{
308 Bugs: ProtocolBugs{
309 EarlyChangeCipherSpec: 2,
310 },
311 },
312 shouldFail: true,
313 expectedError: ":CCS_RECEIVED_EARLY:",
314 },
David Benjamind23f4122014-07-23 15:09:48 -0400315 {
David Benjamind23f4122014-07-23 15:09:48 -0400316 name: "SkipNewSessionTicket",
317 config: Config{
318 Bugs: ProtocolBugs{
319 SkipNewSessionTicket: true,
320 },
321 },
322 shouldFail: true,
323 expectedError: ":CCS_RECEIVED_EARLY:",
324 },
David Benjamin7e3305e2014-07-28 14:52:32 -0400325 {
David Benjamin7e3305e2014-07-28 14:52:32 -0400326 name: "FalseStart-SessionTicketsDisabled",
327 config: Config{
David Benjamind86c7672014-08-02 04:07:12 -0400328 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
329 NextProtos: []string{"foo"},
David Benjamin7e3305e2014-07-28 14:52:32 -0400330 SessionTicketsDisabled: true,
331 },
332 flags: []string{
333 "-false-start",
334 "-select-next-proto", "foo",
335 },
336 },
David Benjamind86c7672014-08-02 04:07:12 -0400337 {
338 testType: serverTest,
339 name: "SendV2ClientHello",
340 config: Config{
341 // Choose a cipher suite that does not involve
342 // elliptic curves, so no extensions are
343 // involved.
344 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
345 Bugs: ProtocolBugs{
346 SendV2ClientHello: true,
347 },
348 },
349 },
David Benjaminbef270a2014-08-02 04:22:02 -0400350 {
351 testType: serverTest,
352 name: "FallbackSCSV",
353 config: Config{
354 MaxVersion: VersionTLS11,
355 Bugs: ProtocolBugs{
356 SendFallbackSCSV: true,
357 },
358 },
359 shouldFail: true,
360 expectedError: ":INAPPROPRIATE_FALLBACK:",
361 },
362 {
363 testType: serverTest,
364 name: "FallbackSCSV-VersionMatch",
365 config: Config{
366 Bugs: ProtocolBugs{
367 SendFallbackSCSV: true,
368 },
369 },
370 },
Adam Langley95c29f32014-06-20 12:00:00 -0700371}
372
David Benjamin1d5c83e2014-07-22 19:20:02 -0400373func doExchange(testType testType, config *Config, conn net.Conn, messageLen int) error {
374 var tlsConn *Conn
375 if testType == clientTest {
376 tlsConn = Server(conn, config)
377 } else {
378 config.InsecureSkipVerify = true
379 tlsConn = Client(conn, config)
380 }
381
Adam Langley95c29f32014-06-20 12:00:00 -0700382 if err := tlsConn.Handshake(); err != nil {
383 return err
384 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700385
386 if messageLen < 0 {
387 // Read until EOF.
388 _, err := io.Copy(ioutil.Discard, tlsConn)
389 return err
390 }
391
Adam Langley80842bd2014-06-20 12:00:00 -0700392 if messageLen == 0 {
393 messageLen = 32
394 }
395 testMessage := make([]byte, messageLen)
396 for i := range testMessage {
397 testMessage[i] = 0x42
398 }
Adam Langley95c29f32014-06-20 12:00:00 -0700399 tlsConn.Write(testMessage)
400
401 buf := make([]byte, len(testMessage))
402 _, err := io.ReadFull(tlsConn, buf)
403 if err != nil {
404 return err
405 }
406
407 for i, v := range buf {
408 if v != testMessage[i]^0xff {
409 return fmt.Errorf("bad reply contents at byte %d", i)
410 }
411 }
412
413 return nil
414}
415
David Benjamin325b5c32014-07-01 19:40:31 -0400416func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
417 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700418 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400419 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700420 }
David Benjamin325b5c32014-07-01 19:40:31 -0400421 valgrindArgs = append(valgrindArgs, path)
422 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700423
David Benjamin325b5c32014-07-01 19:40:31 -0400424 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700425}
426
David Benjamin325b5c32014-07-01 19:40:31 -0400427func gdbOf(path string, args ...string) *exec.Cmd {
428 xtermArgs := []string{"-e", "gdb", "--args"}
429 xtermArgs = append(xtermArgs, path)
430 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700431
David Benjamin325b5c32014-07-01 19:40:31 -0400432 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700433}
434
David Benjamin1d5c83e2014-07-22 19:20:02 -0400435func openSocketPair() (shimEnd *os.File, conn net.Conn) {
Adam Langley95c29f32014-06-20 12:00:00 -0700436 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
437 if err != nil {
438 panic(err)
439 }
440
441 syscall.CloseOnExec(socks[0])
442 syscall.CloseOnExec(socks[1])
David Benjamin1d5c83e2014-07-22 19:20:02 -0400443 shimEnd = os.NewFile(uintptr(socks[0]), "shim end")
Adam Langley95c29f32014-06-20 12:00:00 -0700444 connFile := os.NewFile(uintptr(socks[1]), "our end")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400445 conn, err = net.FileConn(connFile)
446 if err != nil {
447 panic(err)
448 }
Adam Langley95c29f32014-06-20 12:00:00 -0700449 connFile.Close()
450 if err != nil {
451 panic(err)
452 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400453 return shimEnd, conn
454}
455
David Benjamin884fdf12014-08-02 15:28:23 -0400456func runTest(test *testCase, buildDir string) error {
David Benjamin1d5c83e2014-07-22 19:20:02 -0400457 shimEnd, conn := openSocketPair()
458 shimEndResume, connResume := openSocketPair()
Adam Langley95c29f32014-06-20 12:00:00 -0700459
David Benjamin884fdf12014-08-02 15:28:23 -0400460 shim_path := path.Join(buildDir, "ssl/test/bssl_shim")
David Benjamin025b3d32014-07-01 19:53:04 -0400461 flags := []string{}
462 if test.testType == clientTest {
463 flags = append(flags, "client")
Adam Langley95c29f32014-06-20 12:00:00 -0700464 } else {
David Benjamin025b3d32014-07-01 19:53:04 -0400465 flags = append(flags, "server")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400466 }
Adam Langley95c29f32014-06-20 12:00:00 -0700467
David Benjamin1d5c83e2014-07-22 19:20:02 -0400468 if test.resumeSession {
469 flags = append(flags, "resume")
470 } else {
471 flags = append(flags, "normal")
472 }
473
474 if test.testType == serverTest {
David Benjamin025b3d32014-07-01 19:53:04 -0400475 flags = append(flags, "-key-file")
476 if test.keyFile == "" {
477 flags = append(flags, rsaKeyFile)
478 } else {
479 flags = append(flags, test.keyFile)
480 }
481
482 flags = append(flags, "-cert-file")
483 if test.certFile == "" {
484 flags = append(flags, rsaCertificateFile)
485 } else {
486 flags = append(flags, test.certFile)
487 }
488 }
489 flags = append(flags, test.flags...)
490
491 var shim *exec.Cmd
492 if *useValgrind {
493 shim = valgrindOf(false, shim_path, flags...)
494 } else {
495 shim = exec.Command(shim_path, flags...)
496 }
497 // shim = gdbOf(shim_path, flags...)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400498 shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
David Benjamin025b3d32014-07-01 19:53:04 -0400499 shim.Stdin = os.Stdin
500 var stdoutBuf, stderrBuf bytes.Buffer
501 shim.Stdout = &stdoutBuf
502 shim.Stderr = &stderrBuf
503
504 if err := shim.Start(); err != nil {
Adam Langley95c29f32014-06-20 12:00:00 -0700505 panic(err)
506 }
David Benjamin025b3d32014-07-01 19:53:04 -0400507 shimEnd.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400508 shimEndResume.Close()
Adam Langley95c29f32014-06-20 12:00:00 -0700509
510 config := test.config
David Benjamin1d5c83e2014-07-22 19:20:02 -0400511 config.ClientSessionCache = NewLRUClientSessionCache(1)
David Benjamin025b3d32014-07-01 19:53:04 -0400512 if test.testType == clientTest {
513 if len(config.Certificates) == 0 {
514 config.Certificates = []Certificate{getRSACertificate()}
515 }
David Benjamin025b3d32014-07-01 19:53:04 -0400516 }
Adam Langley95c29f32014-06-20 12:00:00 -0700517
David Benjamin1d5c83e2014-07-22 19:20:02 -0400518 err := doExchange(test.testType, &config, conn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700519 conn.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400520 if err == nil && test.resumeSession {
521 err = doExchange(test.testType, &config, connResume, test.messageLen)
522 connResume.Close()
523 }
524
David Benjamin025b3d32014-07-01 19:53:04 -0400525 childErr := shim.Wait()
Adam Langley95c29f32014-06-20 12:00:00 -0700526
527 stdout := string(stdoutBuf.Bytes())
528 stderr := string(stderrBuf.Bytes())
529 failed := err != nil || childErr != nil
530 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700531 localError := "none"
532 if err != nil {
533 localError = err.Error()
534 }
535 if len(test.expectedLocalError) != 0 {
536 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
537 }
Adam Langley95c29f32014-06-20 12:00:00 -0700538
539 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700540 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700541 if childErr != nil {
542 childError = childErr.Error()
543 }
544
545 var msg string
546 switch {
547 case failed && !test.shouldFail:
548 msg = "unexpected failure"
549 case !failed && test.shouldFail:
550 msg = "unexpected success"
551 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700552 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700553 default:
554 panic("internal error")
555 }
556
557 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
558 }
559
560 if !*useValgrind && len(stderr) > 0 {
561 println(stderr)
562 }
563
564 return nil
565}
566
567var tlsVersions = []struct {
568 name string
569 version uint16
570}{
571 {"SSL3", VersionSSL30},
572 {"TLS1", VersionTLS10},
573 {"TLS11", VersionTLS11},
574 {"TLS12", VersionTLS12},
575}
576
577var testCipherSuites = []struct {
578 name string
579 id uint16
580}{
581 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400582 {"AES128-GCM", TLS_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700583 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400584 {"AES256-GCM", TLS_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700585 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400586 {"DHE-RSA-3DES-SHA", TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA},
587 {"DHE-RSA-AES128-GCM", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
588 {"DHE-RSA-AES128-SHA", TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
589 {"DHE-RSA-AES256-GCM", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384},
590 {"DHE-RSA-AES256-SHA", TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700591 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
592 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
593 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
594 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
595 {"ECDHE-RSA-3DES-SHA", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
596 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700597 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400598 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700599 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
600 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700601 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400602 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700603}
604
605func addCipherSuiteTests() {
606 for _, suite := range testCipherSuites {
607 var cert Certificate
David Benjamin025b3d32014-07-01 19:53:04 -0400608 var certFile string
609 var keyFile string
Adam Langley95c29f32014-06-20 12:00:00 -0700610 if strings.Contains(suite.name, "ECDSA") {
611 cert = getECDSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400612 certFile = ecdsaCertificateFile
613 keyFile = ecdsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700614 } else {
615 cert = getRSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400616 certFile = rsaCertificateFile
617 keyFile = rsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700618 }
619
620 for _, ver := range tlsVersions {
621 if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
622 continue
623 }
624
David Benjamin1d5c83e2014-07-22 19:20:02 -0400625 // Go's TLS implementation only implements session
626 // resumption with tickets, so SSLv3 cannot resume
627 // sessions.
628 resumeSession := ver.version != VersionSSL30
629
David Benjamin025b3d32014-07-01 19:53:04 -0400630 testCases = append(testCases, testCase{
631 testType: clientTest,
632 name: ver.name + "-" + suite.name + "-client",
Adam Langley95c29f32014-06-20 12:00:00 -0700633 config: Config{
634 MinVersion: ver.version,
635 MaxVersion: ver.version,
636 CipherSuites: []uint16{suite.id},
637 Certificates: []Certificate{cert},
638 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400639 resumeSession: resumeSession,
Adam Langley95c29f32014-06-20 12:00:00 -0700640 })
David Benjamin025b3d32014-07-01 19:53:04 -0400641
642 // Go's TLS implementation implements SSLv3 as a server,
643 // but not as a client.
644 //
645 // TODO(davidben): Implement SSLv3 as a client too to
646 // exercise that code.
647 if ver.version != VersionSSL30 {
648 testCases = append(testCases, testCase{
649 testType: serverTest,
650 name: ver.name + "-" + suite.name + "-server",
651 config: Config{
652 MinVersion: ver.version,
653 MaxVersion: ver.version,
654 CipherSuites: []uint16{suite.id},
655 Certificates: []Certificate{cert},
656 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400657 certFile: certFile,
658 keyFile: keyFile,
659 resumeSession: resumeSession,
David Benjamin025b3d32014-07-01 19:53:04 -0400660 })
661 }
Adam Langley95c29f32014-06-20 12:00:00 -0700662 }
663 }
664}
665
666func addBadECDSASignatureTests() {
667 for badR := BadValue(1); badR < NumBadValues; badR++ {
668 for badS := BadValue(1); badS < NumBadValues; badS++ {
David Benjamin025b3d32014-07-01 19:53:04 -0400669 testCases = append(testCases, testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700670 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
671 config: Config{
672 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
673 Certificates: []Certificate{getECDSACertificate()},
674 Bugs: ProtocolBugs{
675 BadECDSAR: badR,
676 BadECDSAS: badS,
677 },
678 },
679 shouldFail: true,
680 expectedError: "SIGNATURE",
681 })
682 }
683 }
684}
685
Adam Langley80842bd2014-06-20 12:00:00 -0700686func addCBCPaddingTests() {
David Benjamin025b3d32014-07-01 19:53:04 -0400687 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700688 name: "MaxCBCPadding",
689 config: Config{
690 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
691 Bugs: ProtocolBugs{
692 MaxPadding: true,
693 },
694 },
695 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
696 })
David Benjamin025b3d32014-07-01 19:53:04 -0400697 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700698 name: "BadCBCPadding",
699 config: Config{
700 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
701 Bugs: ProtocolBugs{
702 PaddingFirstByteBad: true,
703 },
704 },
705 shouldFail: true,
706 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
707 })
708 // OpenSSL previously had an issue where the first byte of padding in
709 // 255 bytes of padding wasn't checked.
David Benjamin025b3d32014-07-01 19:53:04 -0400710 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700711 name: "BadCBCPadding255",
712 config: Config{
713 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
714 Bugs: ProtocolBugs{
715 MaxPadding: true,
716 PaddingFirstByteBadIf255: true,
717 },
718 },
719 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
720 shouldFail: true,
721 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
722 })
723}
724
Kenny Root7fdeaf12014-08-05 15:23:37 -0700725func addCBCSplittingTests() {
726 testCases = append(testCases, testCase{
727 name: "CBCRecordSplitting",
728 config: Config{
729 MaxVersion: VersionTLS10,
730 MinVersion: VersionTLS10,
731 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
732 },
733 messageLen: -1, // read until EOF
734 flags: []string{
735 "-async",
736 "-write-different-record-sizes",
737 "-cbc-record-splitting",
738 },
739 },
740 testCase{
741 name: "CBCRecordSplittingPartialWrite",
742 config: Config{
743 MaxVersion: VersionTLS10,
744 MinVersion: VersionTLS10,
745 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
746 },
747 messageLen: -1, // read until EOF
748 flags: []string{
749 "-async",
750 "-write-different-record-sizes",
751 "-cbc-record-splitting",
752 "-partial-write",
753 },
754 })
755}
756
David Benjamin636293b2014-07-08 17:59:18 -0400757func addClientAuthTests() {
David Benjamin407a10c2014-07-16 12:58:59 -0400758 // Add a dummy cert pool to stress certificate authority parsing.
759 // TODO(davidben): Add tests that those values parse out correctly.
760 certPool := x509.NewCertPool()
761 cert, err := x509.ParseCertificate(rsaCertificate.Certificate[0])
762 if err != nil {
763 panic(err)
764 }
765 certPool.AddCert(cert)
766
David Benjamin636293b2014-07-08 17:59:18 -0400767 for _, ver := range tlsVersions {
768 if ver.version == VersionSSL30 {
769 // TODO(davidben): The Go implementation does not
770 // correctly compute CertificateVerify hashes for SSLv3.
771 continue
772 }
773
774 var cipherSuites []uint16
775 if ver.version >= VersionTLS12 {
776 // Pick a SHA-256 cipher suite. The Go implementation
777 // does not correctly handle client auth with a SHA-384
778 // cipher suite.
779 cipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}
780 }
781
782 testCases = append(testCases, testCase{
783 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400784 name: ver.name + "-Client-ClientAuth-RSA",
David Benjamin636293b2014-07-08 17:59:18 -0400785 config: Config{
786 MinVersion: ver.version,
787 MaxVersion: ver.version,
788 CipherSuites: cipherSuites,
789 ClientAuth: RequireAnyClientCert,
David Benjamin407a10c2014-07-16 12:58:59 -0400790 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400791 },
792 flags: []string{
793 "-cert-file", rsaCertificateFile,
794 "-key-file", rsaKeyFile,
795 },
796 })
797 testCases = append(testCases, testCase{
798 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400799 name: ver.name + "-Client-ClientAuth-ECDSA",
David Benjamin636293b2014-07-08 17:59:18 -0400800 config: Config{
801 MinVersion: ver.version,
802 MaxVersion: ver.version,
803 CipherSuites: cipherSuites,
804 ClientAuth: RequireAnyClientCert,
David Benjamin407a10c2014-07-16 12:58:59 -0400805 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400806 },
807 flags: []string{
808 "-cert-file", ecdsaCertificateFile,
809 "-key-file", ecdsaKeyFile,
810 },
811 })
David Benjamin67666e72014-07-12 15:47:52 -0400812 testCases = append(testCases, testCase{
813 testType: serverTest,
814 name: ver.name + "-Server-ClientAuth-RSA",
815 config: Config{
816 Certificates: []Certificate{rsaCertificate},
817 },
818 flags: []string{"-require-any-client-certificate"},
819 })
820 testCases = append(testCases, testCase{
821 testType: serverTest,
822 name: ver.name + "-Server-ClientAuth-ECDSA",
823 config: Config{
824 Certificates: []Certificate{ecdsaCertificate},
825 },
826 flags: []string{"-require-any-client-certificate"},
827 })
David Benjamin636293b2014-07-08 17:59:18 -0400828 }
829}
830
David Benjamin43ec06f2014-08-05 02:28:57 -0400831// Adds tests that try to cover the range of the handshake state machine, under
832// various conditions. Some of these are redundant with other tests, but they
833// only cover the synchronous case.
834func addStateMachineCoverageTests(async bool, splitHandshake bool) {
835 var suffix string
836 var flags []string
837 var maxHandshakeRecordLength int
838 if async {
839 suffix = "-Async"
840 flags = append(flags, "-async")
841 } else {
842 suffix = "-Sync"
843 }
844 if splitHandshake {
845 suffix += "-SplitHandshakeRecords"
846 maxHandshakeRecordLength = 10
847 }
848
849 // Basic handshake, with resumption. Client and server.
850 testCases = append(testCases, testCase{
851 name: "Basic-Client" + suffix,
852 config: Config{
853 Bugs: ProtocolBugs{
854 MaxHandshakeRecordLength: maxHandshakeRecordLength,
855 },
856 },
857 flags: flags,
858 })
859 testCases = append(testCases, testCase{
860 testType: serverTest,
861 name: "Basic-Server" + suffix,
862 config: Config{
863 Bugs: ProtocolBugs{
864 MaxHandshakeRecordLength: maxHandshakeRecordLength,
865 },
866 },
867 flags: flags,
868 })
869
870 // No session ticket support; server doesn't send NewSessionTicket.
871 testCases = append(testCases, testCase{
872 name: "SessionTicketsDisabled-Client" + suffix,
873 config: Config{
874 SessionTicketsDisabled: true,
875 Bugs: ProtocolBugs{
876 MaxHandshakeRecordLength: maxHandshakeRecordLength,
877 },
878 },
879 flags: flags,
880 })
881 testCases = append(testCases, testCase{
882 testType: serverTest,
883 name: "SessionTicketsDisabled-Server" + suffix,
884 config: Config{
885 SessionTicketsDisabled: true,
886 Bugs: ProtocolBugs{
887 MaxHandshakeRecordLength: maxHandshakeRecordLength,
888 },
889 },
890 flags: flags,
891 })
892
893 // NPN on client and server; results in post-handshake message.
894 testCases = append(testCases, testCase{
895 name: "NPN-Client" + suffix,
896 config: Config{
897 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
898 NextProtos: []string{"foo"},
899 Bugs: ProtocolBugs{
900 MaxHandshakeRecordLength: maxHandshakeRecordLength,
901 },
902 },
903 flags: append(flags, "-select-next-proto", "foo"),
904 })
905 testCases = append(testCases, testCase{
906 testType: serverTest,
907 name: "NPN-Server" + suffix,
908 config: Config{
909 NextProtos: []string{"bar"},
910 Bugs: ProtocolBugs{
911 MaxHandshakeRecordLength: maxHandshakeRecordLength,
912 },
913 },
914 flags: append(flags,
915 "-advertise-npn", "\x03foo\x03bar\x03baz",
916 "-expect-next-proto", "bar"),
917 })
918
919 // Client does False Start and negotiates NPN.
920 testCases = append(testCases, testCase{
921 name: "FalseStart" + suffix,
922 config: Config{
923 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
924 NextProtos: []string{"foo"},
925 Bugs: ProtocolBugs{
926 MaxHandshakeRecordLength: maxHandshakeRecordLength,
927 },
928 },
929 flags: append(flags,
930 "-false-start",
931 "-select-next-proto", "foo"),
932 resumeSession: true,
933 })
934
935 // TLS client auth.
936 testCases = append(testCases, testCase{
937 testType: clientTest,
938 name: "ClientAuth-Client" + suffix,
939 config: Config{
940 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
941 ClientAuth: RequireAnyClientCert,
942 Bugs: ProtocolBugs{
943 MaxHandshakeRecordLength: maxHandshakeRecordLength,
944 },
945 },
946 flags: append(flags,
947 "-cert-file", rsaCertificateFile,
948 "-key-file", rsaKeyFile),
949 })
950 testCases = append(testCases, testCase{
951 testType: serverTest,
952 name: "ClientAuth-Server" + suffix,
953 config: Config{
954 Certificates: []Certificate{rsaCertificate},
955 },
956 flags: append(flags, "-require-any-client-certificate"),
957 })
958}
959
David Benjamin884fdf12014-08-02 15:28:23 -0400960func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
Adam Langley95c29f32014-06-20 12:00:00 -0700961 defer wg.Done()
962
963 for test := range c {
964 statusChan <- statusMsg{test: test, started: true}
David Benjamin884fdf12014-08-02 15:28:23 -0400965 err := runTest(test, buildDir)
Adam Langley95c29f32014-06-20 12:00:00 -0700966 statusChan <- statusMsg{test: test, err: err}
967 }
968}
969
970type statusMsg struct {
971 test *testCase
972 started bool
973 err error
974}
975
976func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
977 var started, done, failed, lineLen int
978 defer close(doneChan)
979
980 for msg := range statusChan {
981 if msg.started {
982 started++
983 } else {
984 done++
985 }
986
987 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
988
989 if msg.err != nil {
990 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
991 failed++
992 }
993 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
994 lineLen = len(line)
995 os.Stdout.WriteString(line)
996 }
997}
998
999func main() {
1000 var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests")
David Benjamin2bc8e6f2014-08-02 15:22:37 -04001001 var flagNumWorkers *int = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
David Benjamin884fdf12014-08-02 15:28:23 -04001002 var flagBuildDir *string = flag.String("build-dir", "../../../build", "The build directory to run the shim from.")
Adam Langley95c29f32014-06-20 12:00:00 -07001003
1004 flag.Parse()
1005
1006 addCipherSuiteTests()
1007 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -07001008 addCBCPaddingTests()
Kenny Root7fdeaf12014-08-05 15:23:37 -07001009 addCBCSplittingTests()
David Benjamin636293b2014-07-08 17:59:18 -04001010 addClientAuthTests()
David Benjamin43ec06f2014-08-05 02:28:57 -04001011 for _, async := range []bool{false, true} {
1012 for _, splitHandshake := range []bool{false, true} {
1013 addStateMachineCoverageTests(async, splitHandshake)
1014 }
1015 }
Adam Langley95c29f32014-06-20 12:00:00 -07001016
1017 var wg sync.WaitGroup
1018
David Benjamin2bc8e6f2014-08-02 15:22:37 -04001019 numWorkers := *flagNumWorkers
Adam Langley95c29f32014-06-20 12:00:00 -07001020
1021 statusChan := make(chan statusMsg, numWorkers)
1022 testChan := make(chan *testCase, numWorkers)
1023 doneChan := make(chan struct{})
1024
David Benjamin025b3d32014-07-01 19:53:04 -04001025 go statusPrinter(doneChan, statusChan, len(testCases))
Adam Langley95c29f32014-06-20 12:00:00 -07001026
1027 for i := 0; i < numWorkers; i++ {
1028 wg.Add(1)
David Benjamin884fdf12014-08-02 15:28:23 -04001029 go worker(statusChan, testChan, *flagBuildDir, &wg)
Adam Langley95c29f32014-06-20 12:00:00 -07001030 }
1031
David Benjamin025b3d32014-07-01 19:53:04 -04001032 for i := range testCases {
1033 if len(*flagTest) == 0 || *flagTest == testCases[i].name {
1034 testChan <- &testCases[i]
Adam Langley95c29f32014-06-20 12:00:00 -07001035 }
1036 }
1037
1038 close(testChan)
1039 wg.Wait()
1040 close(statusChan)
1041 <-doneChan
1042
1043 fmt.Printf("\n")
1044}