blob: 7958a77be985283fc26580eabbe0e988118ee5b8 [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"
David Benjamin2bc8e6f2014-08-02 15:22:37 -040012 "runtime"
Adam Langley95c29f32014-06-20 12:00:00 -070013 "strings"
14 "sync"
15 "syscall"
16)
17
18var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
19
David Benjamin025b3d32014-07-01 19:53:04 -040020const (
21 rsaCertificateFile = "cert.pem"
22 ecdsaCertificateFile = "ecdsa_cert.pem"
23)
24
25const (
26 rsaKeyFile = "key.pem"
27 ecdsaKeyFile = "ecdsa_key.pem"
28)
29
Adam Langley95c29f32014-06-20 12:00:00 -070030var rsaCertificate, ecdsaCertificate Certificate
31
32func initCertificates() {
33 var err error
David Benjamin025b3d32014-07-01 19:53:04 -040034 rsaCertificate, err = LoadX509KeyPair(rsaCertificateFile, rsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070035 if err != nil {
36 panic(err)
37 }
38
David Benjamin025b3d32014-07-01 19:53:04 -040039 ecdsaCertificate, err = LoadX509KeyPair(ecdsaCertificateFile, ecdsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070040 if err != nil {
41 panic(err)
42 }
43}
44
45var certificateOnce sync.Once
46
47func getRSACertificate() Certificate {
48 certificateOnce.Do(initCertificates)
49 return rsaCertificate
50}
51
52func getECDSACertificate() Certificate {
53 certificateOnce.Do(initCertificates)
54 return ecdsaCertificate
55}
56
David Benjamin025b3d32014-07-01 19:53:04 -040057type testType int
58
59const (
60 clientTest testType = iota
61 serverTest
62)
63
Adam Langley95c29f32014-06-20 12:00:00 -070064type testCase struct {
David Benjamin025b3d32014-07-01 19:53:04 -040065 testType testType
Adam Langley95c29f32014-06-20 12:00:00 -070066 name string
67 config Config
68 shouldFail bool
69 expectedError string
Adam Langleyac61fa32014-06-23 12:03:11 -070070 // expectedLocalError, if not empty, contains a substring that must be
71 // found in the local error.
72 expectedLocalError string
Adam Langley80842bd2014-06-20 12:00:00 -070073 // messageLen is the length, in bytes, of the test message that will be
74 // sent.
75 messageLen int
David Benjamin025b3d32014-07-01 19:53:04 -040076 // certFile is the path to the certificate to use for the server.
77 certFile string
78 // keyFile is the path to the private key to use for the server.
79 keyFile string
David Benjamin1d5c83e2014-07-22 19:20:02 -040080 // resumeSession controls whether a second connection should be tested
81 // which resumes the first session.
82 resumeSession bool
David Benjamin325b5c32014-07-01 19:40:31 -040083 // flags, if not empty, contains a list of command-line flags that will
84 // be passed to the shim program.
85 flags []string
Adam Langley95c29f32014-06-20 12:00:00 -070086}
87
David Benjamin025b3d32014-07-01 19:53:04 -040088var testCases = []testCase{
Adam Langley95c29f32014-06-20 12:00:00 -070089 {
90 name: "BadRSASignature",
91 config: Config{
92 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
93 Bugs: ProtocolBugs{
94 InvalidSKXSignature: true,
95 },
96 },
97 shouldFail: true,
98 expectedError: ":BAD_SIGNATURE:",
99 },
100 {
101 name: "BadECDSASignature",
102 config: Config{
103 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
104 Bugs: ProtocolBugs{
105 InvalidSKXSignature: true,
106 },
107 Certificates: []Certificate{getECDSACertificate()},
108 },
109 shouldFail: true,
110 expectedError: ":BAD_SIGNATURE:",
111 },
112 {
113 name: "BadECDSACurve",
114 config: Config{
115 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
116 Bugs: ProtocolBugs{
117 InvalidSKXCurve: true,
118 },
119 Certificates: []Certificate{getECDSACertificate()},
120 },
121 shouldFail: true,
122 expectedError: ":WRONG_CURVE:",
123 },
Adam Langleyac61fa32014-06-23 12:03:11 -0700124 {
David Benjamin325b5c32014-07-01 19:40:31 -0400125 name: "NoFallbackSCSV",
Adam Langleyac61fa32014-06-23 12:03:11 -0700126 config: Config{
127 Bugs: ProtocolBugs{
128 FailIfNotFallbackSCSV: true,
129 },
130 },
131 shouldFail: true,
132 expectedLocalError: "no fallback SCSV found",
133 },
David Benjamin325b5c32014-07-01 19:40:31 -0400134 {
135 name: "FallbackSCSV",
136 config: Config{
137 Bugs: ProtocolBugs{
138 FailIfNotFallbackSCSV: true,
139 },
140 },
141 flags: []string{"-fallback-scsv"},
142 },
David Benjamin197b3ab2014-07-02 18:37:33 -0400143 {
144 testType: serverTest,
David Benjamin35a7a442014-07-05 00:23:20 -0400145 name: "ServerNameExtension",
David Benjamin197b3ab2014-07-02 18:37:33 -0400146 config: Config{
147 ServerName: "example.com",
148 },
149 flags: []string{"-expect-server-name", "example.com"},
150 },
David Benjamin35a7a442014-07-05 00:23:20 -0400151 {
152 testType: clientTest,
153 name: "DuplicateExtensionClient",
154 config: Config{
155 Bugs: ProtocolBugs{
156 DuplicateExtension: true,
157 },
158 },
159 shouldFail: true,
160 expectedLocalError: "remote error: error decoding message",
161 },
162 {
163 testType: serverTest,
164 name: "DuplicateExtensionServer",
165 config: Config{
166 Bugs: ProtocolBugs{
167 DuplicateExtension: true,
168 },
169 },
170 shouldFail: true,
171 expectedLocalError: "remote error: error decoding message",
172 },
David Benjamin7b030512014-07-08 17:30:11 -0400173 {
174 name: "ClientCertificateTypes",
175 config: Config{
176 ClientAuth: RequestClientCert,
177 ClientCertificateTypes: []byte{
178 CertTypeDSSSign,
179 CertTypeRSASign,
180 CertTypeECDSASign,
181 },
182 },
183 flags: []string{"-expect-certificate-types", string([]byte{
184 CertTypeDSSSign,
185 CertTypeRSASign,
186 CertTypeECDSASign,
187 })},
188 },
David Benjamin636293b2014-07-08 17:59:18 -0400189 {
190 name: "NoClientCertificate",
191 config: Config{
192 ClientAuth: RequireAnyClientCert,
193 },
194 shouldFail: true,
195 expectedLocalError: "client didn't provide a certificate",
196 },
David Benjamin1c375dd2014-07-12 00:48:23 -0400197 {
198 name: "UnauthenticatedECDH",
199 config: Config{
200 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
201 Bugs: ProtocolBugs{
202 UnauthenticatedECDH: true,
203 },
204 },
205 shouldFail: true,
David Benjamine8f3d662014-07-12 01:10:19 -0400206 expectedError: ":UNEXPECTED_MESSAGE:",
David Benjamin1c375dd2014-07-12 00:48:23 -0400207 },
David Benjamin9c651c92014-07-12 13:27:45 -0400208 {
209 name: "SkipServerKeyExchange",
210 config: Config{
211 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
212 Bugs: ProtocolBugs{
213 SkipServerKeyExchange: true,
214 },
215 },
216 shouldFail: true,
217 expectedError: ":UNEXPECTED_MESSAGE:",
218 },
David Benjamin1f5f62b2014-07-12 16:18:02 -0400219 {
220 testType: serverTest,
221 name: "NPNServerTest",
222 config: Config{
223 NextProtos: []string{"bar"},
224 },
225 flags: []string{
226 "-advertise-npn", "\x03foo\x03bar\x03baz",
227 "-expect-next-proto", "bar",
228 },
229 },
David Benjamina0e52232014-07-19 17:39:58 -0400230 {
231 name: "SkipChangeCipherSpec-Client",
232 config: Config{
233 Bugs: ProtocolBugs{
234 SkipChangeCipherSpec: true,
235 },
236 },
237 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400238 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400239 },
240 {
241 testType: serverTest,
242 name: "SkipChangeCipherSpec-Server",
243 config: Config{
244 Bugs: ProtocolBugs{
245 SkipChangeCipherSpec: true,
246 },
247 },
248 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400249 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400250 },
David Benjamin42be6452014-07-21 14:50:23 -0400251 {
252 testType: serverTest,
253 name: "SkipChangeCipherSpec-Server-NPN",
254 config: Config{
255 NextProtos: []string{"bar"},
256 Bugs: ProtocolBugs{
257 SkipChangeCipherSpec: true,
258 },
259 },
260 flags: []string{
261 "-advertise-npn", "\x03foo\x03bar\x03baz",
262 },
263 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400264 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
265 },
266 {
267 name: "FragmentAcrossChangeCipherSpec-Client",
268 config: Config{
269 Bugs: ProtocolBugs{
270 FragmentAcrossChangeCipherSpec: true,
271 },
272 },
273 shouldFail: true,
274 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
275 },
276 {
277 testType: serverTest,
278 name: "FragmentAcrossChangeCipherSpec-Server",
279 config: Config{
280 Bugs: ProtocolBugs{
281 FragmentAcrossChangeCipherSpec: true,
282 },
283 },
284 shouldFail: true,
285 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
286 },
287 {
288 testType: serverTest,
289 name: "FragmentAcrossChangeCipherSpec-Server-NPN",
290 config: Config{
291 NextProtos: []string{"bar"},
292 Bugs: ProtocolBugs{
293 FragmentAcrossChangeCipherSpec: true,
294 },
295 },
296 flags: []string{
297 "-advertise-npn", "\x03foo\x03bar\x03baz",
298 },
299 shouldFail: true,
300 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamin42be6452014-07-21 14:50:23 -0400301 },
David Benjaminf3ec83d2014-07-21 22:42:34 -0400302 {
303 testType: serverTest,
304 name: "EarlyChangeCipherSpec-server-1",
305 config: Config{
306 Bugs: ProtocolBugs{
307 EarlyChangeCipherSpec: 1,
308 },
309 },
310 shouldFail: true,
311 expectedError: ":CCS_RECEIVED_EARLY:",
312 },
313 {
314 testType: serverTest,
315 name: "EarlyChangeCipherSpec-server-2",
316 config: Config{
317 Bugs: ProtocolBugs{
318 EarlyChangeCipherSpec: 2,
319 },
320 },
321 shouldFail: true,
322 expectedError: ":CCS_RECEIVED_EARLY:",
323 },
David Benjamind23f4122014-07-23 15:09:48 -0400324 {
325 name: "SessionTicketsDisabled-Client",
326 config: Config{
327 SessionTicketsDisabled: true,
328 },
329 },
330 {
331 testType: serverTest,
332 name: "SessionTicketsDisabled-Server",
333 config: Config{
334 SessionTicketsDisabled: true,
335 },
336 },
337 {
338 name: "SkipNewSessionTicket",
339 config: Config{
340 Bugs: ProtocolBugs{
341 SkipNewSessionTicket: true,
342 },
343 },
344 shouldFail: true,
345 expectedError: ":CCS_RECEIVED_EARLY:",
346 },
David Benjamin7e3305e2014-07-28 14:52:32 -0400347 {
348 name: "FalseStart",
349 config: Config{
350 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
David Benjamind86c7672014-08-02 04:07:12 -0400351 NextProtos: []string{"foo"},
David Benjamin7e3305e2014-07-28 14:52:32 -0400352 },
353 flags: []string{
354 "-false-start",
355 "-select-next-proto", "foo",
356 },
357 resumeSession: true,
358 },
359 {
360 name: "FalseStart-SessionTicketsDisabled",
361 config: Config{
David Benjamind86c7672014-08-02 04:07:12 -0400362 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
363 NextProtos: []string{"foo"},
David Benjamin7e3305e2014-07-28 14:52:32 -0400364 SessionTicketsDisabled: true,
365 },
366 flags: []string{
367 "-false-start",
368 "-select-next-proto", "foo",
369 },
370 },
David Benjamind86c7672014-08-02 04:07:12 -0400371 {
372 testType: serverTest,
373 name: "SendV2ClientHello",
374 config: Config{
375 // Choose a cipher suite that does not involve
376 // elliptic curves, so no extensions are
377 // involved.
378 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
379 Bugs: ProtocolBugs{
380 SendV2ClientHello: true,
381 },
382 },
383 },
David Benjaminbef270a2014-08-02 04:22:02 -0400384 {
385 testType: serverTest,
386 name: "FallbackSCSV",
387 config: Config{
388 MaxVersion: VersionTLS11,
389 Bugs: ProtocolBugs{
390 SendFallbackSCSV: true,
391 },
392 },
393 shouldFail: true,
394 expectedError: ":INAPPROPRIATE_FALLBACK:",
395 },
396 {
397 testType: serverTest,
398 name: "FallbackSCSV-VersionMatch",
399 config: Config{
400 Bugs: ProtocolBugs{
401 SendFallbackSCSV: true,
402 },
403 },
404 },
Adam Langley95c29f32014-06-20 12:00:00 -0700405}
406
David Benjamin1d5c83e2014-07-22 19:20:02 -0400407func doExchange(testType testType, config *Config, conn net.Conn, messageLen int) error {
408 var tlsConn *Conn
409 if testType == clientTest {
410 tlsConn = Server(conn, config)
411 } else {
412 config.InsecureSkipVerify = true
413 tlsConn = Client(conn, config)
414 }
415
Adam Langley95c29f32014-06-20 12:00:00 -0700416 if err := tlsConn.Handshake(); err != nil {
417 return err
418 }
Adam Langley80842bd2014-06-20 12:00:00 -0700419 if messageLen == 0 {
420 messageLen = 32
421 }
422 testMessage := make([]byte, messageLen)
423 for i := range testMessage {
424 testMessage[i] = 0x42
425 }
Adam Langley95c29f32014-06-20 12:00:00 -0700426 tlsConn.Write(testMessage)
427
428 buf := make([]byte, len(testMessage))
429 _, err := io.ReadFull(tlsConn, buf)
430 if err != nil {
431 return err
432 }
433
434 for i, v := range buf {
435 if v != testMessage[i]^0xff {
436 return fmt.Errorf("bad reply contents at byte %d", i)
437 }
438 }
439
440 return nil
441}
442
David Benjamin325b5c32014-07-01 19:40:31 -0400443func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
444 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700445 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400446 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700447 }
David Benjamin325b5c32014-07-01 19:40:31 -0400448 valgrindArgs = append(valgrindArgs, path)
449 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700450
David Benjamin325b5c32014-07-01 19:40:31 -0400451 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700452}
453
David Benjamin325b5c32014-07-01 19:40:31 -0400454func gdbOf(path string, args ...string) *exec.Cmd {
455 xtermArgs := []string{"-e", "gdb", "--args"}
456 xtermArgs = append(xtermArgs, path)
457 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700458
David Benjamin325b5c32014-07-01 19:40:31 -0400459 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700460}
461
David Benjamin1d5c83e2014-07-22 19:20:02 -0400462func openSocketPair() (shimEnd *os.File, conn net.Conn) {
Adam Langley95c29f32014-06-20 12:00:00 -0700463 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
464 if err != nil {
465 panic(err)
466 }
467
468 syscall.CloseOnExec(socks[0])
469 syscall.CloseOnExec(socks[1])
David Benjamin1d5c83e2014-07-22 19:20:02 -0400470 shimEnd = os.NewFile(uintptr(socks[0]), "shim end")
Adam Langley95c29f32014-06-20 12:00:00 -0700471 connFile := os.NewFile(uintptr(socks[1]), "our end")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400472 conn, err = net.FileConn(connFile)
473 if err != nil {
474 panic(err)
475 }
Adam Langley95c29f32014-06-20 12:00:00 -0700476 connFile.Close()
477 if err != nil {
478 panic(err)
479 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400480 return shimEnd, conn
481}
482
483func runTest(test *testCase) error {
484 shimEnd, conn := openSocketPair()
485 shimEndResume, connResume := openSocketPair()
Adam Langley95c29f32014-06-20 12:00:00 -0700486
David Benjamin025b3d32014-07-01 19:53:04 -0400487 const shim_path = "../../../build/ssl/test/bssl_shim"
488 flags := []string{}
489 if test.testType == clientTest {
490 flags = append(flags, "client")
Adam Langley95c29f32014-06-20 12:00:00 -0700491 } else {
David Benjamin025b3d32014-07-01 19:53:04 -0400492 flags = append(flags, "server")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400493 }
Adam Langley95c29f32014-06-20 12:00:00 -0700494
David Benjamin1d5c83e2014-07-22 19:20:02 -0400495 if test.resumeSession {
496 flags = append(flags, "resume")
497 } else {
498 flags = append(flags, "normal")
499 }
500
501 if test.testType == serverTest {
David Benjamin025b3d32014-07-01 19:53:04 -0400502 flags = append(flags, "-key-file")
503 if test.keyFile == "" {
504 flags = append(flags, rsaKeyFile)
505 } else {
506 flags = append(flags, test.keyFile)
507 }
508
509 flags = append(flags, "-cert-file")
510 if test.certFile == "" {
511 flags = append(flags, rsaCertificateFile)
512 } else {
513 flags = append(flags, test.certFile)
514 }
515 }
516 flags = append(flags, test.flags...)
517
518 var shim *exec.Cmd
519 if *useValgrind {
520 shim = valgrindOf(false, shim_path, flags...)
521 } else {
522 shim = exec.Command(shim_path, flags...)
523 }
524 // shim = gdbOf(shim_path, flags...)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400525 shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
David Benjamin025b3d32014-07-01 19:53:04 -0400526 shim.Stdin = os.Stdin
527 var stdoutBuf, stderrBuf bytes.Buffer
528 shim.Stdout = &stdoutBuf
529 shim.Stderr = &stderrBuf
530
531 if err := shim.Start(); err != nil {
Adam Langley95c29f32014-06-20 12:00:00 -0700532 panic(err)
533 }
David Benjamin025b3d32014-07-01 19:53:04 -0400534 shimEnd.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400535 shimEndResume.Close()
Adam Langley95c29f32014-06-20 12:00:00 -0700536
537 config := test.config
David Benjamin1d5c83e2014-07-22 19:20:02 -0400538 config.ClientSessionCache = NewLRUClientSessionCache(1)
David Benjamin025b3d32014-07-01 19:53:04 -0400539 if test.testType == clientTest {
540 if len(config.Certificates) == 0 {
541 config.Certificates = []Certificate{getRSACertificate()}
542 }
David Benjamin025b3d32014-07-01 19:53:04 -0400543 }
Adam Langley95c29f32014-06-20 12:00:00 -0700544
David Benjamin1d5c83e2014-07-22 19:20:02 -0400545 err := doExchange(test.testType, &config, conn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700546 conn.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400547 if err == nil && test.resumeSession {
548 err = doExchange(test.testType, &config, connResume, test.messageLen)
549 connResume.Close()
550 }
551
David Benjamin025b3d32014-07-01 19:53:04 -0400552 childErr := shim.Wait()
Adam Langley95c29f32014-06-20 12:00:00 -0700553
554 stdout := string(stdoutBuf.Bytes())
555 stderr := string(stderrBuf.Bytes())
556 failed := err != nil || childErr != nil
557 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700558 localError := "none"
559 if err != nil {
560 localError = err.Error()
561 }
562 if len(test.expectedLocalError) != 0 {
563 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
564 }
Adam Langley95c29f32014-06-20 12:00:00 -0700565
566 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700567 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700568 if childErr != nil {
569 childError = childErr.Error()
570 }
571
572 var msg string
573 switch {
574 case failed && !test.shouldFail:
575 msg = "unexpected failure"
576 case !failed && test.shouldFail:
577 msg = "unexpected success"
578 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700579 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700580 default:
581 panic("internal error")
582 }
583
584 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
585 }
586
587 if !*useValgrind && len(stderr) > 0 {
588 println(stderr)
589 }
590
591 return nil
592}
593
594var tlsVersions = []struct {
595 name string
596 version uint16
597}{
598 {"SSL3", VersionSSL30},
599 {"TLS1", VersionTLS10},
600 {"TLS11", VersionTLS11},
601 {"TLS12", VersionTLS12},
602}
603
604var testCipherSuites = []struct {
605 name string
606 id uint16
607}{
608 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
609 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
610 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
611 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
612 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
613 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
614 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
615 {"ECDHE-RSA-3DES-SHA", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
616 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
617 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
618 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
619 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
620 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
621 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
622 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
623}
624
625func addCipherSuiteTests() {
626 for _, suite := range testCipherSuites {
627 var cert Certificate
David Benjamin025b3d32014-07-01 19:53:04 -0400628 var certFile string
629 var keyFile string
Adam Langley95c29f32014-06-20 12:00:00 -0700630 if strings.Contains(suite.name, "ECDSA") {
631 cert = getECDSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400632 certFile = ecdsaCertificateFile
633 keyFile = ecdsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700634 } else {
635 cert = getRSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400636 certFile = rsaCertificateFile
637 keyFile = rsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700638 }
639
640 for _, ver := range tlsVersions {
641 if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
642 continue
643 }
644
David Benjamin1d5c83e2014-07-22 19:20:02 -0400645 // Go's TLS implementation only implements session
646 // resumption with tickets, so SSLv3 cannot resume
647 // sessions.
648 resumeSession := ver.version != VersionSSL30
649
David Benjamin025b3d32014-07-01 19:53:04 -0400650 testCases = append(testCases, testCase{
651 testType: clientTest,
652 name: ver.name + "-" + suite.name + "-client",
Adam Langley95c29f32014-06-20 12:00:00 -0700653 config: Config{
654 MinVersion: ver.version,
655 MaxVersion: ver.version,
656 CipherSuites: []uint16{suite.id},
657 Certificates: []Certificate{cert},
658 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400659 resumeSession: resumeSession,
Adam Langley95c29f32014-06-20 12:00:00 -0700660 })
David Benjamin025b3d32014-07-01 19:53:04 -0400661
662 // Go's TLS implementation implements SSLv3 as a server,
663 // but not as a client.
664 //
665 // TODO(davidben): Implement SSLv3 as a client too to
666 // exercise that code.
667 if ver.version != VersionSSL30 {
668 testCases = append(testCases, testCase{
669 testType: serverTest,
670 name: ver.name + "-" + suite.name + "-server",
671 config: Config{
672 MinVersion: ver.version,
673 MaxVersion: ver.version,
674 CipherSuites: []uint16{suite.id},
675 Certificates: []Certificate{cert},
676 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400677 certFile: certFile,
678 keyFile: keyFile,
679 resumeSession: resumeSession,
David Benjamin025b3d32014-07-01 19:53:04 -0400680 })
681 }
Adam Langley95c29f32014-06-20 12:00:00 -0700682 }
683 }
684}
685
686func addBadECDSASignatureTests() {
687 for badR := BadValue(1); badR < NumBadValues; badR++ {
688 for badS := BadValue(1); badS < NumBadValues; badS++ {
David Benjamin025b3d32014-07-01 19:53:04 -0400689 testCases = append(testCases, testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700690 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
691 config: Config{
692 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
693 Certificates: []Certificate{getECDSACertificate()},
694 Bugs: ProtocolBugs{
695 BadECDSAR: badR,
696 BadECDSAS: badS,
697 },
698 },
699 shouldFail: true,
700 expectedError: "SIGNATURE",
701 })
702 }
703 }
704}
705
Adam Langley80842bd2014-06-20 12:00:00 -0700706func addCBCPaddingTests() {
David Benjamin025b3d32014-07-01 19:53:04 -0400707 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700708 name: "MaxCBCPadding",
709 config: Config{
710 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
711 Bugs: ProtocolBugs{
712 MaxPadding: true,
713 },
714 },
715 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
716 })
David Benjamin025b3d32014-07-01 19:53:04 -0400717 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700718 name: "BadCBCPadding",
719 config: Config{
720 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
721 Bugs: ProtocolBugs{
722 PaddingFirstByteBad: true,
723 },
724 },
725 shouldFail: true,
726 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
727 })
728 // OpenSSL previously had an issue where the first byte of padding in
729 // 255 bytes of padding wasn't checked.
David Benjamin025b3d32014-07-01 19:53:04 -0400730 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700731 name: "BadCBCPadding255",
732 config: Config{
733 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
734 Bugs: ProtocolBugs{
735 MaxPadding: true,
736 PaddingFirstByteBadIf255: true,
737 },
738 },
739 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
740 shouldFail: true,
741 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
742 })
743}
744
David Benjamin636293b2014-07-08 17:59:18 -0400745func addClientAuthTests() {
David Benjamin407a10c2014-07-16 12:58:59 -0400746 // Add a dummy cert pool to stress certificate authority parsing.
747 // TODO(davidben): Add tests that those values parse out correctly.
748 certPool := x509.NewCertPool()
749 cert, err := x509.ParseCertificate(rsaCertificate.Certificate[0])
750 if err != nil {
751 panic(err)
752 }
753 certPool.AddCert(cert)
754
David Benjamin636293b2014-07-08 17:59:18 -0400755 for _, ver := range tlsVersions {
756 if ver.version == VersionSSL30 {
757 // TODO(davidben): The Go implementation does not
758 // correctly compute CertificateVerify hashes for SSLv3.
759 continue
760 }
761
762 var cipherSuites []uint16
763 if ver.version >= VersionTLS12 {
764 // Pick a SHA-256 cipher suite. The Go implementation
765 // does not correctly handle client auth with a SHA-384
766 // cipher suite.
767 cipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}
768 }
769
770 testCases = append(testCases, testCase{
771 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400772 name: ver.name + "-Client-ClientAuth-RSA",
David Benjamin636293b2014-07-08 17:59:18 -0400773 config: Config{
774 MinVersion: ver.version,
775 MaxVersion: ver.version,
776 CipherSuites: cipherSuites,
777 ClientAuth: RequireAnyClientCert,
David Benjamin407a10c2014-07-16 12:58:59 -0400778 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400779 },
780 flags: []string{
781 "-cert-file", rsaCertificateFile,
782 "-key-file", rsaKeyFile,
783 },
784 })
785 testCases = append(testCases, testCase{
786 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400787 name: ver.name + "-Client-ClientAuth-ECDSA",
David Benjamin636293b2014-07-08 17:59:18 -0400788 config: Config{
789 MinVersion: ver.version,
790 MaxVersion: ver.version,
791 CipherSuites: cipherSuites,
792 ClientAuth: RequireAnyClientCert,
David Benjamin407a10c2014-07-16 12:58:59 -0400793 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400794 },
795 flags: []string{
796 "-cert-file", ecdsaCertificateFile,
797 "-key-file", ecdsaKeyFile,
798 },
799 })
David Benjamin67666e72014-07-12 15:47:52 -0400800 testCases = append(testCases, testCase{
801 testType: serverTest,
802 name: ver.name + "-Server-ClientAuth-RSA",
803 config: Config{
804 Certificates: []Certificate{rsaCertificate},
805 },
806 flags: []string{"-require-any-client-certificate"},
807 })
808 testCases = append(testCases, testCase{
809 testType: serverTest,
810 name: ver.name + "-Server-ClientAuth-ECDSA",
811 config: Config{
812 Certificates: []Certificate{ecdsaCertificate},
813 },
814 flags: []string{"-require-any-client-certificate"},
815 })
David Benjamin636293b2014-07-08 17:59:18 -0400816 }
817}
818
Adam Langley95c29f32014-06-20 12:00:00 -0700819func worker(statusChan chan statusMsg, c chan *testCase, wg *sync.WaitGroup) {
820 defer wg.Done()
821
822 for test := range c {
823 statusChan <- statusMsg{test: test, started: true}
824 err := runTest(test)
825 statusChan <- statusMsg{test: test, err: err}
826 }
827}
828
829type statusMsg struct {
830 test *testCase
831 started bool
832 err error
833}
834
835func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
836 var started, done, failed, lineLen int
837 defer close(doneChan)
838
839 for msg := range statusChan {
840 if msg.started {
841 started++
842 } else {
843 done++
844 }
845
846 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
847
848 if msg.err != nil {
849 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
850 failed++
851 }
852 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
853 lineLen = len(line)
854 os.Stdout.WriteString(line)
855 }
856}
857
858func main() {
859 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 -0400860 var flagNumWorkers *int = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
Adam Langley95c29f32014-06-20 12:00:00 -0700861
862 flag.Parse()
863
864 addCipherSuiteTests()
865 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -0700866 addCBCPaddingTests()
David Benjamin636293b2014-07-08 17:59:18 -0400867 addClientAuthTests()
Adam Langley95c29f32014-06-20 12:00:00 -0700868
869 var wg sync.WaitGroup
870
David Benjamin2bc8e6f2014-08-02 15:22:37 -0400871 numWorkers := *flagNumWorkers
Adam Langley95c29f32014-06-20 12:00:00 -0700872
873 statusChan := make(chan statusMsg, numWorkers)
874 testChan := make(chan *testCase, numWorkers)
875 doneChan := make(chan struct{})
876
David Benjamin025b3d32014-07-01 19:53:04 -0400877 go statusPrinter(doneChan, statusChan, len(testCases))
Adam Langley95c29f32014-06-20 12:00:00 -0700878
879 for i := 0; i < numWorkers; i++ {
880 wg.Add(1)
881 go worker(statusChan, testChan, &wg)
882 }
883
David Benjamin025b3d32014-07-01 19:53:04 -0400884 for i := range testCases {
885 if len(*flagTest) == 0 || *flagTest == testCases[i].name {
886 testChan <- &testCases[i]
Adam Langley95c29f32014-06-20 12:00:00 -0700887 }
888 }
889
890 close(testChan)
891 wg.Wait()
892 close(statusChan)
893 <-doneChan
894
895 fmt.Printf("\n")
896}