blob: 79bf99c8420382e5654a9b7ee88b41ea10590381 [file] [log] [blame]
Adam Langley95c29f32014-06-20 12:00:00 -07001package main
2
3import (
4 "bytes"
David Benjamina08e49d2014-08-24 01:46:07 -04005 "crypto/ecdsa"
6 "crypto/elliptic"
David Benjamin407a10c2014-07-16 12:58:59 -04007 "crypto/x509"
David Benjamin2561dc32014-08-24 01:25:27 -04008 "encoding/base64"
David Benjamina08e49d2014-08-24 01:46:07 -04009 "encoding/pem"
Adam Langley95c29f32014-06-20 12:00:00 -070010 "flag"
11 "fmt"
12 "io"
Kenny Root7fdeaf12014-08-05 15:23:37 -070013 "io/ioutil"
Adam Langley95c29f32014-06-20 12:00:00 -070014 "net"
15 "os"
16 "os/exec"
David Benjamin884fdf12014-08-02 15:28:23 -040017 "path"
David Benjamin2bc8e6f2014-08-02 15:22:37 -040018 "runtime"
Adam Langley95c29f32014-06-20 12:00:00 -070019 "strings"
20 "sync"
21 "syscall"
22)
23
24var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
25
David Benjamin025b3d32014-07-01 19:53:04 -040026const (
27 rsaCertificateFile = "cert.pem"
28 ecdsaCertificateFile = "ecdsa_cert.pem"
29)
30
31const (
David Benjamina08e49d2014-08-24 01:46:07 -040032 rsaKeyFile = "key.pem"
33 ecdsaKeyFile = "ecdsa_key.pem"
34 channelIDKeyFile = "channel_id_key.pem"
David Benjamin025b3d32014-07-01 19:53:04 -040035)
36
Adam Langley95c29f32014-06-20 12:00:00 -070037var rsaCertificate, ecdsaCertificate Certificate
David Benjamina08e49d2014-08-24 01:46:07 -040038var channelIDKey *ecdsa.PrivateKey
39var channelIDBytes []byte
Adam Langley95c29f32014-06-20 12:00:00 -070040
41func initCertificates() {
42 var err error
David Benjamin025b3d32014-07-01 19:53:04 -040043 rsaCertificate, err = LoadX509KeyPair(rsaCertificateFile, rsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070044 if err != nil {
45 panic(err)
46 }
47
David Benjamin025b3d32014-07-01 19:53:04 -040048 ecdsaCertificate, err = LoadX509KeyPair(ecdsaCertificateFile, ecdsaKeyFile)
Adam Langley95c29f32014-06-20 12:00:00 -070049 if err != nil {
50 panic(err)
51 }
David Benjamina08e49d2014-08-24 01:46:07 -040052
53 channelIDPEMBlock, err := ioutil.ReadFile(channelIDKeyFile)
54 if err != nil {
55 panic(err)
56 }
57 channelIDDERBlock, _ := pem.Decode(channelIDPEMBlock)
58 if channelIDDERBlock.Type != "EC PRIVATE KEY" {
59 panic("bad key type")
60 }
61 channelIDKey, err = x509.ParseECPrivateKey(channelIDDERBlock.Bytes)
62 if err != nil {
63 panic(err)
64 }
65 if channelIDKey.Curve != elliptic.P256() {
66 panic("bad curve")
67 }
68
69 channelIDBytes = make([]byte, 64)
70 writeIntPadded(channelIDBytes[:32], channelIDKey.X)
71 writeIntPadded(channelIDBytes[32:], channelIDKey.Y)
Adam Langley95c29f32014-06-20 12:00:00 -070072}
73
74var certificateOnce sync.Once
75
76func getRSACertificate() Certificate {
77 certificateOnce.Do(initCertificates)
78 return rsaCertificate
79}
80
81func getECDSACertificate() Certificate {
82 certificateOnce.Do(initCertificates)
83 return ecdsaCertificate
84}
85
David Benjamin025b3d32014-07-01 19:53:04 -040086type testType int
87
88const (
89 clientTest testType = iota
90 serverTest
91)
92
David Benjamin6fd297b2014-08-11 18:43:38 -040093type protocol int
94
95const (
96 tls protocol = iota
97 dtls
98)
99
Adam Langley95c29f32014-06-20 12:00:00 -0700100type testCase struct {
David Benjamin025b3d32014-07-01 19:53:04 -0400101 testType testType
David Benjamin6fd297b2014-08-11 18:43:38 -0400102 protocol protocol
Adam Langley95c29f32014-06-20 12:00:00 -0700103 name string
104 config Config
105 shouldFail bool
106 expectedError string
Adam Langleyac61fa32014-06-23 12:03:11 -0700107 // expectedLocalError, if not empty, contains a substring that must be
108 // found in the local error.
109 expectedLocalError string
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400110 // expectedVersion, if non-zero, specifies the TLS version that must be
111 // negotiated.
112 expectedVersion uint16
David Benjamina08e49d2014-08-24 01:46:07 -0400113 // expectChannelID controls whether the connection should have
114 // negotiated a Channel ID with channelIDKey.
115 expectChannelID bool
Adam Langley80842bd2014-06-20 12:00:00 -0700116 // messageLen is the length, in bytes, of the test message that will be
117 // sent.
118 messageLen int
David Benjamin025b3d32014-07-01 19:53:04 -0400119 // certFile is the path to the certificate to use for the server.
120 certFile string
121 // keyFile is the path to the private key to use for the server.
122 keyFile string
David Benjamin1d5c83e2014-07-22 19:20:02 -0400123 // resumeSession controls whether a second connection should be tested
124 // which resumes the first session.
125 resumeSession bool
David Benjamin98e882e2014-08-08 13:24:34 -0400126 // sendPrefix sends a prefix on the socket before actually performing a
127 // handshake.
128 sendPrefix string
David Benjamine58c4f52014-08-24 03:47:07 -0400129 // shimWritesFirst controls whether the shim sends an initial "hello"
130 // message before doing a roundtrip with the runner.
131 shimWritesFirst bool
David Benjamin325b5c32014-07-01 19:40:31 -0400132 // flags, if not empty, contains a list of command-line flags that will
133 // be passed to the shim program.
134 flags []string
Adam Langley95c29f32014-06-20 12:00:00 -0700135}
136
David Benjamin025b3d32014-07-01 19:53:04 -0400137var testCases = []testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700138 {
139 name: "BadRSASignature",
140 config: Config{
141 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
142 Bugs: ProtocolBugs{
143 InvalidSKXSignature: true,
144 },
145 },
146 shouldFail: true,
147 expectedError: ":BAD_SIGNATURE:",
148 },
149 {
150 name: "BadECDSASignature",
151 config: Config{
152 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
153 Bugs: ProtocolBugs{
154 InvalidSKXSignature: true,
155 },
156 Certificates: []Certificate{getECDSACertificate()},
157 },
158 shouldFail: true,
159 expectedError: ":BAD_SIGNATURE:",
160 },
161 {
162 name: "BadECDSACurve",
163 config: Config{
164 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
165 Bugs: ProtocolBugs{
166 InvalidSKXCurve: true,
167 },
168 Certificates: []Certificate{getECDSACertificate()},
169 },
170 shouldFail: true,
171 expectedError: ":WRONG_CURVE:",
172 },
Adam Langleyac61fa32014-06-23 12:03:11 -0700173 {
David Benjamina8e3e0e2014-08-06 22:11:10 -0400174 testType: serverTest,
175 name: "BadRSAVersion",
176 config: Config{
177 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
178 Bugs: ProtocolBugs{
179 RsaClientKeyExchangeVersion: VersionTLS11,
180 },
181 },
182 shouldFail: true,
183 expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
184 },
185 {
David Benjamin325b5c32014-07-01 19:40:31 -0400186 name: "NoFallbackSCSV",
Adam Langleyac61fa32014-06-23 12:03:11 -0700187 config: Config{
188 Bugs: ProtocolBugs{
189 FailIfNotFallbackSCSV: true,
190 },
191 },
192 shouldFail: true,
193 expectedLocalError: "no fallback SCSV found",
194 },
David Benjamin325b5c32014-07-01 19:40:31 -0400195 {
David Benjamin2a0c4962014-08-22 23:46:35 -0400196 name: "SendFallbackSCSV",
David Benjamin325b5c32014-07-01 19:40:31 -0400197 config: Config{
198 Bugs: ProtocolBugs{
199 FailIfNotFallbackSCSV: true,
200 },
201 },
202 flags: []string{"-fallback-scsv"},
203 },
David Benjamin197b3ab2014-07-02 18:37:33 -0400204 {
205 testType: serverTest,
David Benjamin35a7a442014-07-05 00:23:20 -0400206 name: "ServerNameExtension",
David Benjamin197b3ab2014-07-02 18:37:33 -0400207 config: Config{
208 ServerName: "example.com",
209 },
210 flags: []string{"-expect-server-name", "example.com"},
211 },
David Benjamin35a7a442014-07-05 00:23:20 -0400212 {
213 testType: clientTest,
214 name: "DuplicateExtensionClient",
215 config: Config{
216 Bugs: ProtocolBugs{
217 DuplicateExtension: true,
218 },
219 },
220 shouldFail: true,
221 expectedLocalError: "remote error: error decoding message",
222 },
223 {
224 testType: serverTest,
225 name: "DuplicateExtensionServer",
226 config: Config{
227 Bugs: ProtocolBugs{
228 DuplicateExtension: true,
229 },
230 },
231 shouldFail: true,
232 expectedLocalError: "remote error: error decoding message",
233 },
David Benjamin7b030512014-07-08 17:30:11 -0400234 {
235 name: "ClientCertificateTypes",
236 config: Config{
237 ClientAuth: RequestClientCert,
238 ClientCertificateTypes: []byte{
239 CertTypeDSSSign,
240 CertTypeRSASign,
241 CertTypeECDSASign,
242 },
243 },
David Benjamin2561dc32014-08-24 01:25:27 -0400244 flags: []string{
245 "-expect-certificate-types",
246 base64.StdEncoding.EncodeToString([]byte{
247 CertTypeDSSSign,
248 CertTypeRSASign,
249 CertTypeECDSASign,
250 }),
251 },
David Benjamin7b030512014-07-08 17:30:11 -0400252 },
David Benjamin636293b2014-07-08 17:59:18 -0400253 {
254 name: "NoClientCertificate",
255 config: Config{
256 ClientAuth: RequireAnyClientCert,
257 },
258 shouldFail: true,
259 expectedLocalError: "client didn't provide a certificate",
260 },
David Benjamin1c375dd2014-07-12 00:48:23 -0400261 {
262 name: "UnauthenticatedECDH",
263 config: Config{
264 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
265 Bugs: ProtocolBugs{
266 UnauthenticatedECDH: true,
267 },
268 },
269 shouldFail: true,
David Benjamine8f3d662014-07-12 01:10:19 -0400270 expectedError: ":UNEXPECTED_MESSAGE:",
David Benjamin1c375dd2014-07-12 00:48:23 -0400271 },
David Benjamin9c651c92014-07-12 13:27:45 -0400272 {
273 name: "SkipServerKeyExchange",
274 config: Config{
275 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
276 Bugs: ProtocolBugs{
277 SkipServerKeyExchange: true,
278 },
279 },
280 shouldFail: true,
281 expectedError: ":UNEXPECTED_MESSAGE:",
282 },
David Benjamin1f5f62b2014-07-12 16:18:02 -0400283 {
David Benjamina0e52232014-07-19 17:39:58 -0400284 name: "SkipChangeCipherSpec-Client",
285 config: Config{
286 Bugs: ProtocolBugs{
287 SkipChangeCipherSpec: true,
288 },
289 },
290 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400291 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400292 },
293 {
294 testType: serverTest,
295 name: "SkipChangeCipherSpec-Server",
296 config: Config{
297 Bugs: ProtocolBugs{
298 SkipChangeCipherSpec: true,
299 },
300 },
301 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400302 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamina0e52232014-07-19 17:39:58 -0400303 },
David Benjamin42be6452014-07-21 14:50:23 -0400304 {
305 testType: serverTest,
306 name: "SkipChangeCipherSpec-Server-NPN",
307 config: Config{
308 NextProtos: []string{"bar"},
309 Bugs: ProtocolBugs{
310 SkipChangeCipherSpec: true,
311 },
312 },
313 flags: []string{
314 "-advertise-npn", "\x03foo\x03bar\x03baz",
315 },
316 shouldFail: true,
David Benjamin86271ee2014-07-21 16:14:03 -0400317 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
318 },
319 {
320 name: "FragmentAcrossChangeCipherSpec-Client",
321 config: Config{
322 Bugs: ProtocolBugs{
323 FragmentAcrossChangeCipherSpec: true,
324 },
325 },
326 shouldFail: true,
327 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
328 },
329 {
330 testType: serverTest,
331 name: "FragmentAcrossChangeCipherSpec-Server",
332 config: Config{
333 Bugs: ProtocolBugs{
334 FragmentAcrossChangeCipherSpec: true,
335 },
336 },
337 shouldFail: true,
338 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
339 },
340 {
341 testType: serverTest,
342 name: "FragmentAcrossChangeCipherSpec-Server-NPN",
343 config: Config{
344 NextProtos: []string{"bar"},
345 Bugs: ProtocolBugs{
346 FragmentAcrossChangeCipherSpec: true,
347 },
348 },
349 flags: []string{
350 "-advertise-npn", "\x03foo\x03bar\x03baz",
351 },
352 shouldFail: true,
353 expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
David Benjamin42be6452014-07-21 14:50:23 -0400354 },
David Benjaminf3ec83d2014-07-21 22:42:34 -0400355 {
356 testType: serverTest,
357 name: "EarlyChangeCipherSpec-server-1",
358 config: Config{
359 Bugs: ProtocolBugs{
360 EarlyChangeCipherSpec: 1,
361 },
362 },
363 shouldFail: true,
364 expectedError: ":CCS_RECEIVED_EARLY:",
365 },
366 {
367 testType: serverTest,
368 name: "EarlyChangeCipherSpec-server-2",
369 config: Config{
370 Bugs: ProtocolBugs{
371 EarlyChangeCipherSpec: 2,
372 },
373 },
374 shouldFail: true,
375 expectedError: ":CCS_RECEIVED_EARLY:",
376 },
David Benjamind23f4122014-07-23 15:09:48 -0400377 {
David Benjamind23f4122014-07-23 15:09:48 -0400378 name: "SkipNewSessionTicket",
379 config: Config{
380 Bugs: ProtocolBugs{
381 SkipNewSessionTicket: true,
382 },
383 },
384 shouldFail: true,
385 expectedError: ":CCS_RECEIVED_EARLY:",
386 },
David Benjamin7e3305e2014-07-28 14:52:32 -0400387 {
David Benjamind86c7672014-08-02 04:07:12 -0400388 testType: serverTest,
David Benjaminbef270a2014-08-02 04:22:02 -0400389 name: "FallbackSCSV",
390 config: Config{
391 MaxVersion: VersionTLS11,
392 Bugs: ProtocolBugs{
393 SendFallbackSCSV: true,
394 },
395 },
396 shouldFail: true,
397 expectedError: ":INAPPROPRIATE_FALLBACK:",
398 },
399 {
400 testType: serverTest,
401 name: "FallbackSCSV-VersionMatch",
402 config: Config{
403 Bugs: ProtocolBugs{
404 SendFallbackSCSV: true,
405 },
406 },
407 },
David Benjamin98214542014-08-07 18:02:39 -0400408 {
409 testType: serverTest,
410 name: "FragmentedClientVersion",
411 config: Config{
412 Bugs: ProtocolBugs{
413 MaxHandshakeRecordLength: 1,
414 FragmentClientVersion: true,
415 },
416 },
417 shouldFail: true,
418 expectedError: ":RECORD_TOO_SMALL:",
419 },
David Benjamin98e882e2014-08-08 13:24:34 -0400420 {
421 testType: serverTest,
422 name: "MinorVersionTolerance",
423 config: Config{
424 Bugs: ProtocolBugs{
425 SendClientVersion: 0x03ff,
426 },
427 },
428 expectedVersion: VersionTLS12,
429 },
430 {
431 testType: serverTest,
432 name: "MajorVersionTolerance",
433 config: Config{
434 Bugs: ProtocolBugs{
435 SendClientVersion: 0x0400,
436 },
437 },
438 expectedVersion: VersionTLS12,
439 },
440 {
441 testType: serverTest,
442 name: "VersionTooLow",
443 config: Config{
444 Bugs: ProtocolBugs{
445 SendClientVersion: 0x0200,
446 },
447 },
448 shouldFail: true,
449 expectedError: ":UNSUPPORTED_PROTOCOL:",
450 },
451 {
452 testType: serverTest,
453 name: "HttpGET",
454 sendPrefix: "GET / HTTP/1.0\n",
455 shouldFail: true,
456 expectedError: ":HTTP_REQUEST:",
457 },
458 {
459 testType: serverTest,
460 name: "HttpPOST",
461 sendPrefix: "POST / HTTP/1.0\n",
462 shouldFail: true,
463 expectedError: ":HTTP_REQUEST:",
464 },
465 {
466 testType: serverTest,
467 name: "HttpHEAD",
468 sendPrefix: "HEAD / HTTP/1.0\n",
469 shouldFail: true,
470 expectedError: ":HTTP_REQUEST:",
471 },
472 {
473 testType: serverTest,
474 name: "HttpPUT",
475 sendPrefix: "PUT / HTTP/1.0\n",
476 shouldFail: true,
477 expectedError: ":HTTP_REQUEST:",
478 },
479 {
480 testType: serverTest,
481 name: "HttpCONNECT",
482 sendPrefix: "CONNECT www.google.com:443 HTTP/1.0\n",
483 shouldFail: true,
484 expectedError: ":HTTPS_PROXY_REQUEST:",
485 },
Adam Langley95c29f32014-06-20 12:00:00 -0700486}
487
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400488func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int) error {
David Benjamin6fd297b2014-08-11 18:43:38 -0400489 if test.protocol == dtls {
490 conn = newPacketAdaptor(conn)
491 }
492
493 if test.sendPrefix != "" {
494 if _, err := conn.Write([]byte(test.sendPrefix)); err != nil {
495 return err
496 }
David Benjamin98e882e2014-08-08 13:24:34 -0400497 }
498
David Benjamin1d5c83e2014-07-22 19:20:02 -0400499 var tlsConn *Conn
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400500 if test.testType == clientTest {
David Benjamin6fd297b2014-08-11 18:43:38 -0400501 if test.protocol == dtls {
502 tlsConn = DTLSServer(conn, config)
503 } else {
504 tlsConn = Server(conn, config)
505 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400506 } else {
507 config.InsecureSkipVerify = true
David Benjamin6fd297b2014-08-11 18:43:38 -0400508 if test.protocol == dtls {
509 tlsConn = DTLSClient(conn, config)
510 } else {
511 tlsConn = Client(conn, config)
512 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400513 }
514
Adam Langley95c29f32014-06-20 12:00:00 -0700515 if err := tlsConn.Handshake(); err != nil {
516 return err
517 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700518
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400519 if vers := tlsConn.ConnectionState().Version; test.expectedVersion != 0 && vers != test.expectedVersion {
520 return fmt.Errorf("got version %x, expected %x", vers, test.expectedVersion)
521 }
522
David Benjamina08e49d2014-08-24 01:46:07 -0400523 if test.expectChannelID {
524 channelID := tlsConn.ConnectionState().ChannelID
525 if channelID == nil {
526 return fmt.Errorf("no channel ID negotiated")
527 }
528 if channelID.Curve != channelIDKey.Curve ||
529 channelIDKey.X.Cmp(channelIDKey.X) != 0 ||
530 channelIDKey.Y.Cmp(channelIDKey.Y) != 0 {
531 return fmt.Errorf("incorrect channel ID")
532 }
533 }
534
David Benjamine58c4f52014-08-24 03:47:07 -0400535 if test.shimWritesFirst {
536 var buf [5]byte
537 _, err := io.ReadFull(tlsConn, buf[:])
538 if err != nil {
539 return err
540 }
541 if string(buf[:]) != "hello" {
542 return fmt.Errorf("bad initial message")
543 }
544 }
545
Kenny Root7fdeaf12014-08-05 15:23:37 -0700546 if messageLen < 0 {
David Benjamin6fd297b2014-08-11 18:43:38 -0400547 if test.protocol == dtls {
548 return fmt.Errorf("messageLen < 0 not supported for DTLS tests")
549 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700550 // Read until EOF.
551 _, err := io.Copy(ioutil.Discard, tlsConn)
552 return err
553 }
554
Adam Langley80842bd2014-06-20 12:00:00 -0700555 if messageLen == 0 {
556 messageLen = 32
557 }
558 testMessage := make([]byte, messageLen)
559 for i := range testMessage {
560 testMessage[i] = 0x42
561 }
Adam Langley95c29f32014-06-20 12:00:00 -0700562 tlsConn.Write(testMessage)
563
564 buf := make([]byte, len(testMessage))
David Benjamin6fd297b2014-08-11 18:43:38 -0400565 if test.protocol == dtls {
566 bufTmp := make([]byte, len(buf)+1)
567 n, err := tlsConn.Read(bufTmp)
568 if err != nil {
569 return err
570 }
571 if n != len(buf) {
572 return fmt.Errorf("bad reply; length mismatch (%d vs %d)", n, len(buf))
573 }
574 copy(buf, bufTmp)
575 } else {
576 _, err := io.ReadFull(tlsConn, buf)
577 if err != nil {
578 return err
579 }
Adam Langley95c29f32014-06-20 12:00:00 -0700580 }
581
582 for i, v := range buf {
583 if v != testMessage[i]^0xff {
584 return fmt.Errorf("bad reply contents at byte %d", i)
585 }
586 }
587
588 return nil
589}
590
David Benjamin325b5c32014-07-01 19:40:31 -0400591func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
592 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700593 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400594 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700595 }
David Benjamin325b5c32014-07-01 19:40:31 -0400596 valgrindArgs = append(valgrindArgs, path)
597 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700598
David Benjamin325b5c32014-07-01 19:40:31 -0400599 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700600}
601
David Benjamin325b5c32014-07-01 19:40:31 -0400602func gdbOf(path string, args ...string) *exec.Cmd {
603 xtermArgs := []string{"-e", "gdb", "--args"}
604 xtermArgs = append(xtermArgs, path)
605 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700606
David Benjamin325b5c32014-07-01 19:40:31 -0400607 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700608}
609
David Benjamin1d5c83e2014-07-22 19:20:02 -0400610func openSocketPair() (shimEnd *os.File, conn net.Conn) {
Adam Langley95c29f32014-06-20 12:00:00 -0700611 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
612 if err != nil {
613 panic(err)
614 }
615
616 syscall.CloseOnExec(socks[0])
617 syscall.CloseOnExec(socks[1])
David Benjamin1d5c83e2014-07-22 19:20:02 -0400618 shimEnd = os.NewFile(uintptr(socks[0]), "shim end")
Adam Langley95c29f32014-06-20 12:00:00 -0700619 connFile := os.NewFile(uintptr(socks[1]), "our end")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400620 conn, err = net.FileConn(connFile)
621 if err != nil {
622 panic(err)
623 }
Adam Langley95c29f32014-06-20 12:00:00 -0700624 connFile.Close()
625 if err != nil {
626 panic(err)
627 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400628 return shimEnd, conn
629}
630
David Benjamin884fdf12014-08-02 15:28:23 -0400631func runTest(test *testCase, buildDir string) error {
David Benjamin1d5c83e2014-07-22 19:20:02 -0400632 shimEnd, conn := openSocketPair()
633 shimEndResume, connResume := openSocketPair()
Adam Langley95c29f32014-06-20 12:00:00 -0700634
David Benjamin884fdf12014-08-02 15:28:23 -0400635 shim_path := path.Join(buildDir, "ssl/test/bssl_shim")
David Benjamin5a593af2014-08-11 19:51:50 -0400636 var flags []string
David Benjamin1d5c83e2014-07-22 19:20:02 -0400637 if test.testType == serverTest {
David Benjamin5a593af2014-08-11 19:51:50 -0400638 flags = append(flags, "-server")
639
David Benjamin025b3d32014-07-01 19:53:04 -0400640 flags = append(flags, "-key-file")
641 if test.keyFile == "" {
642 flags = append(flags, rsaKeyFile)
643 } else {
644 flags = append(flags, test.keyFile)
645 }
646
647 flags = append(flags, "-cert-file")
648 if test.certFile == "" {
649 flags = append(flags, rsaCertificateFile)
650 } else {
651 flags = append(flags, test.certFile)
652 }
653 }
David Benjamin5a593af2014-08-11 19:51:50 -0400654
David Benjamin6fd297b2014-08-11 18:43:38 -0400655 if test.protocol == dtls {
656 flags = append(flags, "-dtls")
657 }
658
David Benjamin5a593af2014-08-11 19:51:50 -0400659 if test.resumeSession {
660 flags = append(flags, "-resume")
661 }
662
David Benjamine58c4f52014-08-24 03:47:07 -0400663 if test.shimWritesFirst {
664 flags = append(flags, "-shim-writes-first")
665 }
666
David Benjamin025b3d32014-07-01 19:53:04 -0400667 flags = append(flags, test.flags...)
668
669 var shim *exec.Cmd
670 if *useValgrind {
671 shim = valgrindOf(false, shim_path, flags...)
672 } else {
673 shim = exec.Command(shim_path, flags...)
674 }
675 // shim = gdbOf(shim_path, flags...)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400676 shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
David Benjamin025b3d32014-07-01 19:53:04 -0400677 shim.Stdin = os.Stdin
678 var stdoutBuf, stderrBuf bytes.Buffer
679 shim.Stdout = &stdoutBuf
680 shim.Stderr = &stderrBuf
681
682 if err := shim.Start(); err != nil {
Adam Langley95c29f32014-06-20 12:00:00 -0700683 panic(err)
684 }
David Benjamin025b3d32014-07-01 19:53:04 -0400685 shimEnd.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400686 shimEndResume.Close()
Adam Langley95c29f32014-06-20 12:00:00 -0700687
688 config := test.config
David Benjamin1d5c83e2014-07-22 19:20:02 -0400689 config.ClientSessionCache = NewLRUClientSessionCache(1)
David Benjamin025b3d32014-07-01 19:53:04 -0400690 if test.testType == clientTest {
691 if len(config.Certificates) == 0 {
692 config.Certificates = []Certificate{getRSACertificate()}
693 }
David Benjamin025b3d32014-07-01 19:53:04 -0400694 }
Adam Langley95c29f32014-06-20 12:00:00 -0700695
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400696 err := doExchange(test, &config, conn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700697 conn.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400698 if err == nil && test.resumeSession {
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400699 err = doExchange(test, &config, connResume, test.messageLen)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400700 connResume.Close()
701 }
702
David Benjamin025b3d32014-07-01 19:53:04 -0400703 childErr := shim.Wait()
Adam Langley95c29f32014-06-20 12:00:00 -0700704
705 stdout := string(stdoutBuf.Bytes())
706 stderr := string(stderrBuf.Bytes())
707 failed := err != nil || childErr != nil
708 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700709 localError := "none"
710 if err != nil {
711 localError = err.Error()
712 }
713 if len(test.expectedLocalError) != 0 {
714 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
715 }
Adam Langley95c29f32014-06-20 12:00:00 -0700716
717 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700718 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700719 if childErr != nil {
720 childError = childErr.Error()
721 }
722
723 var msg string
724 switch {
725 case failed && !test.shouldFail:
726 msg = "unexpected failure"
727 case !failed && test.shouldFail:
728 msg = "unexpected success"
729 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700730 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700731 default:
732 panic("internal error")
733 }
734
735 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
736 }
737
738 if !*useValgrind && len(stderr) > 0 {
739 println(stderr)
740 }
741
742 return nil
743}
744
745var tlsVersions = []struct {
746 name string
747 version uint16
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400748 flag string
Adam Langley95c29f32014-06-20 12:00:00 -0700749}{
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400750 {"SSL3", VersionSSL30, "-no-ssl3"},
751 {"TLS1", VersionTLS10, "-no-tls1"},
752 {"TLS11", VersionTLS11, "-no-tls11"},
753 {"TLS12", VersionTLS12, "-no-tls12"},
Adam Langley95c29f32014-06-20 12:00:00 -0700754}
755
756var testCipherSuites = []struct {
757 name string
758 id uint16
759}{
760 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400761 {"AES128-GCM", TLS_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700762 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400763 {"AES256-GCM", TLS_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700764 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400765 {"DHE-RSA-AES128-GCM", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
766 {"DHE-RSA-AES128-SHA", TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
767 {"DHE-RSA-AES256-GCM", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384},
768 {"DHE-RSA-AES256-SHA", TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700769 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
770 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
771 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
772 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700773 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700774 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400775 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700776 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
777 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700778 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400779 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700780}
781
782func addCipherSuiteTests() {
783 for _, suite := range testCipherSuites {
784 var cert Certificate
David Benjamin025b3d32014-07-01 19:53:04 -0400785 var certFile string
786 var keyFile string
Adam Langley95c29f32014-06-20 12:00:00 -0700787 if strings.Contains(suite.name, "ECDSA") {
788 cert = getECDSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400789 certFile = ecdsaCertificateFile
790 keyFile = ecdsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700791 } else {
792 cert = getRSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400793 certFile = rsaCertificateFile
794 keyFile = rsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700795 }
796
797 for _, ver := range tlsVersions {
798 if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
799 continue
800 }
801
David Benjamin1d5c83e2014-07-22 19:20:02 -0400802 // Go's TLS implementation only implements session
803 // resumption with tickets, so SSLv3 cannot resume
804 // sessions.
805 resumeSession := ver.version != VersionSSL30
806
David Benjamin025b3d32014-07-01 19:53:04 -0400807 testCases = append(testCases, testCase{
808 testType: clientTest,
809 name: ver.name + "-" + suite.name + "-client",
Adam Langley95c29f32014-06-20 12:00:00 -0700810 config: Config{
811 MinVersion: ver.version,
812 MaxVersion: ver.version,
813 CipherSuites: []uint16{suite.id},
814 Certificates: []Certificate{cert},
815 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400816 resumeSession: resumeSession,
Adam Langley95c29f32014-06-20 12:00:00 -0700817 })
David Benjamin025b3d32014-07-01 19:53:04 -0400818
David Benjamin76d8abe2014-08-14 16:25:34 -0400819 testCases = append(testCases, testCase{
820 testType: serverTest,
821 name: ver.name + "-" + suite.name + "-server",
822 config: Config{
823 MinVersion: ver.version,
824 MaxVersion: ver.version,
825 CipherSuites: []uint16{suite.id},
826 Certificates: []Certificate{cert},
827 },
828 certFile: certFile,
829 keyFile: keyFile,
830 resumeSession: resumeSession,
831 })
David Benjamin6fd297b2014-08-11 18:43:38 -0400832
833 // TODO(davidben): Fix DTLS 1.2 support and test that.
834 if ver.version == VersionTLS10 && strings.Index(suite.name, "RC4") == -1 {
835 testCases = append(testCases, testCase{
836 testType: clientTest,
837 protocol: dtls,
838 name: "D" + ver.name + "-" + suite.name + "-client",
839 config: Config{
840 MinVersion: ver.version,
841 MaxVersion: ver.version,
842 CipherSuites: []uint16{suite.id},
843 Certificates: []Certificate{cert},
844 },
845 resumeSession: resumeSession,
846 })
847 testCases = append(testCases, testCase{
848 testType: serverTest,
849 protocol: dtls,
850 name: "D" + ver.name + "-" + suite.name + "-server",
851 config: Config{
852 MinVersion: ver.version,
853 MaxVersion: ver.version,
854 CipherSuites: []uint16{suite.id},
855 Certificates: []Certificate{cert},
856 },
857 certFile: certFile,
858 keyFile: keyFile,
859 resumeSession: resumeSession,
860 })
861 }
Adam Langley95c29f32014-06-20 12:00:00 -0700862 }
863 }
864}
865
866func addBadECDSASignatureTests() {
867 for badR := BadValue(1); badR < NumBadValues; badR++ {
868 for badS := BadValue(1); badS < NumBadValues; badS++ {
David Benjamin025b3d32014-07-01 19:53:04 -0400869 testCases = append(testCases, testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700870 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
871 config: Config{
872 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
873 Certificates: []Certificate{getECDSACertificate()},
874 Bugs: ProtocolBugs{
875 BadECDSAR: badR,
876 BadECDSAS: badS,
877 },
878 },
879 shouldFail: true,
880 expectedError: "SIGNATURE",
881 })
882 }
883 }
884}
885
Adam Langley80842bd2014-06-20 12:00:00 -0700886func addCBCPaddingTests() {
David Benjamin025b3d32014-07-01 19:53:04 -0400887 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700888 name: "MaxCBCPadding",
889 config: Config{
890 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
891 Bugs: ProtocolBugs{
892 MaxPadding: true,
893 },
894 },
895 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
896 })
David Benjamin025b3d32014-07-01 19:53:04 -0400897 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700898 name: "BadCBCPadding",
899 config: Config{
900 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
901 Bugs: ProtocolBugs{
902 PaddingFirstByteBad: true,
903 },
904 },
905 shouldFail: true,
906 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
907 })
908 // OpenSSL previously had an issue where the first byte of padding in
909 // 255 bytes of padding wasn't checked.
David Benjamin025b3d32014-07-01 19:53:04 -0400910 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700911 name: "BadCBCPadding255",
912 config: Config{
913 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
914 Bugs: ProtocolBugs{
915 MaxPadding: true,
916 PaddingFirstByteBadIf255: true,
917 },
918 },
919 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
920 shouldFail: true,
921 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
922 })
923}
924
Kenny Root7fdeaf12014-08-05 15:23:37 -0700925func addCBCSplittingTests() {
926 testCases = append(testCases, testCase{
927 name: "CBCRecordSplitting",
928 config: Config{
929 MaxVersion: VersionTLS10,
930 MinVersion: VersionTLS10,
931 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
932 },
933 messageLen: -1, // read until EOF
934 flags: []string{
935 "-async",
936 "-write-different-record-sizes",
937 "-cbc-record-splitting",
938 },
David Benjamina8e3e0e2014-08-06 22:11:10 -0400939 })
940 testCases = append(testCases, testCase{
Kenny Root7fdeaf12014-08-05 15:23:37 -0700941 name: "CBCRecordSplittingPartialWrite",
942 config: Config{
943 MaxVersion: VersionTLS10,
944 MinVersion: VersionTLS10,
945 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
946 },
947 messageLen: -1, // read until EOF
948 flags: []string{
949 "-async",
950 "-write-different-record-sizes",
951 "-cbc-record-splitting",
952 "-partial-write",
953 },
954 })
955}
956
David Benjamin636293b2014-07-08 17:59:18 -0400957func addClientAuthTests() {
David Benjamin407a10c2014-07-16 12:58:59 -0400958 // Add a dummy cert pool to stress certificate authority parsing.
959 // TODO(davidben): Add tests that those values parse out correctly.
960 certPool := x509.NewCertPool()
961 cert, err := x509.ParseCertificate(rsaCertificate.Certificate[0])
962 if err != nil {
963 panic(err)
964 }
965 certPool.AddCert(cert)
966
David Benjamin636293b2014-07-08 17:59:18 -0400967 for _, ver := range tlsVersions {
David Benjamin636293b2014-07-08 17:59:18 -0400968 testCases = append(testCases, testCase{
969 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400970 name: ver.name + "-Client-ClientAuth-RSA",
David Benjamin636293b2014-07-08 17:59:18 -0400971 config: Config{
David Benjamine098ec22014-08-27 23:13:20 -0400972 MinVersion: ver.version,
973 MaxVersion: ver.version,
974 ClientAuth: RequireAnyClientCert,
975 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -0400976 },
977 flags: []string{
978 "-cert-file", rsaCertificateFile,
979 "-key-file", rsaKeyFile,
980 },
981 })
982 testCases = append(testCases, testCase{
David Benjamin67666e72014-07-12 15:47:52 -0400983 testType: serverTest,
984 name: ver.name + "-Server-ClientAuth-RSA",
985 config: Config{
David Benjamine098ec22014-08-27 23:13:20 -0400986 MinVersion: ver.version,
987 MaxVersion: ver.version,
David Benjamin67666e72014-07-12 15:47:52 -0400988 Certificates: []Certificate{rsaCertificate},
989 },
990 flags: []string{"-require-any-client-certificate"},
991 })
David Benjamine098ec22014-08-27 23:13:20 -0400992 if ver.version != VersionSSL30 {
993 testCases = append(testCases, testCase{
994 testType: serverTest,
995 name: ver.name + "-Server-ClientAuth-ECDSA",
996 config: Config{
997 MinVersion: ver.version,
998 MaxVersion: ver.version,
999 Certificates: []Certificate{ecdsaCertificate},
1000 },
1001 flags: []string{"-require-any-client-certificate"},
1002 })
1003 testCases = append(testCases, testCase{
1004 testType: clientTest,
1005 name: ver.name + "-Client-ClientAuth-ECDSA",
1006 config: Config{
1007 MinVersion: ver.version,
1008 MaxVersion: ver.version,
1009 ClientAuth: RequireAnyClientCert,
1010 ClientCAs: certPool,
1011 },
1012 flags: []string{
1013 "-cert-file", ecdsaCertificateFile,
1014 "-key-file", ecdsaKeyFile,
1015 },
1016 })
1017 }
David Benjamin636293b2014-07-08 17:59:18 -04001018 }
1019}
1020
David Benjamin43ec06f2014-08-05 02:28:57 -04001021// Adds tests that try to cover the range of the handshake state machine, under
1022// various conditions. Some of these are redundant with other tests, but they
1023// only cover the synchronous case.
David Benjamin6fd297b2014-08-11 18:43:38 -04001024func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) {
David Benjamin43ec06f2014-08-05 02:28:57 -04001025 var suffix string
1026 var flags []string
1027 var maxHandshakeRecordLength int
David Benjamin6fd297b2014-08-11 18:43:38 -04001028 if protocol == dtls {
1029 suffix = "-DTLS"
1030 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001031 if async {
David Benjamin6fd297b2014-08-11 18:43:38 -04001032 suffix += "-Async"
David Benjamin43ec06f2014-08-05 02:28:57 -04001033 flags = append(flags, "-async")
1034 } else {
David Benjamin6fd297b2014-08-11 18:43:38 -04001035 suffix += "-Sync"
David Benjamin43ec06f2014-08-05 02:28:57 -04001036 }
1037 if splitHandshake {
1038 suffix += "-SplitHandshakeRecords"
David Benjamin98214542014-08-07 18:02:39 -04001039 maxHandshakeRecordLength = 1
David Benjamin43ec06f2014-08-05 02:28:57 -04001040 }
1041
1042 // Basic handshake, with resumption. Client and server.
1043 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001044 protocol: protocol,
1045 name: "Basic-Client" + suffix,
David Benjamin43ec06f2014-08-05 02:28:57 -04001046 config: Config{
1047 Bugs: ProtocolBugs{
1048 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1049 },
1050 },
David Benjaminbed9aae2014-08-07 19:13:38 -04001051 flags: flags,
1052 resumeSession: true,
1053 })
1054 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001055 protocol: protocol,
1056 name: "Basic-Client-RenewTicket" + suffix,
David Benjaminbed9aae2014-08-07 19:13:38 -04001057 config: Config{
1058 Bugs: ProtocolBugs{
1059 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1060 RenewTicketOnResume: true,
1061 },
1062 },
1063 flags: flags,
1064 resumeSession: true,
David Benjamin43ec06f2014-08-05 02:28:57 -04001065 })
1066 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001067 protocol: protocol,
David Benjamin43ec06f2014-08-05 02:28:57 -04001068 testType: serverTest,
1069 name: "Basic-Server" + suffix,
1070 config: Config{
1071 Bugs: ProtocolBugs{
1072 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1073 },
1074 },
David Benjaminbed9aae2014-08-07 19:13:38 -04001075 flags: flags,
1076 resumeSession: true,
David Benjamin43ec06f2014-08-05 02:28:57 -04001077 })
1078
David Benjamin6fd297b2014-08-11 18:43:38 -04001079 // TLS client auth.
1080 testCases = append(testCases, testCase{
1081 protocol: protocol,
1082 testType: clientTest,
1083 name: "ClientAuth-Client" + suffix,
1084 config: Config{
David Benjamine098ec22014-08-27 23:13:20 -04001085 ClientAuth: RequireAnyClientCert,
David Benjamin6fd297b2014-08-11 18:43:38 -04001086 Bugs: ProtocolBugs{
1087 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1088 },
1089 },
1090 flags: append(flags,
1091 "-cert-file", rsaCertificateFile,
1092 "-key-file", rsaKeyFile),
1093 })
1094 testCases = append(testCases, testCase{
1095 protocol: protocol,
1096 testType: serverTest,
1097 name: "ClientAuth-Server" + suffix,
1098 config: Config{
1099 Certificates: []Certificate{rsaCertificate},
1100 },
1101 flags: append(flags, "-require-any-client-certificate"),
1102 })
1103
David Benjamin43ec06f2014-08-05 02:28:57 -04001104 // No session ticket support; server doesn't send NewSessionTicket.
1105 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001106 protocol: protocol,
1107 name: "SessionTicketsDisabled-Client" + suffix,
David Benjamin43ec06f2014-08-05 02:28:57 -04001108 config: Config{
1109 SessionTicketsDisabled: true,
1110 Bugs: ProtocolBugs{
1111 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1112 },
1113 },
1114 flags: flags,
1115 })
1116 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001117 protocol: protocol,
David Benjamin43ec06f2014-08-05 02:28:57 -04001118 testType: serverTest,
1119 name: "SessionTicketsDisabled-Server" + suffix,
1120 config: Config{
1121 SessionTicketsDisabled: true,
1122 Bugs: ProtocolBugs{
1123 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1124 },
1125 },
1126 flags: flags,
1127 })
1128
David Benjamin6fd297b2014-08-11 18:43:38 -04001129 if protocol == tls {
1130 // NPN on client and server; results in post-handshake message.
1131 testCases = append(testCases, testCase{
1132 protocol: protocol,
1133 name: "NPN-Client" + suffix,
1134 config: Config{
1135 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1136 NextProtos: []string{"foo"},
1137 Bugs: ProtocolBugs{
1138 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1139 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001140 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001141 flags: append(flags, "-select-next-proto", "foo"),
1142 })
1143 testCases = append(testCases, testCase{
1144 protocol: protocol,
1145 testType: serverTest,
1146 name: "NPN-Server" + suffix,
1147 config: Config{
1148 NextProtos: []string{"bar"},
1149 Bugs: ProtocolBugs{
1150 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1151 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001152 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001153 flags: append(flags,
1154 "-advertise-npn", "\x03foo\x03bar\x03baz",
1155 "-expect-next-proto", "bar"),
1156 })
David Benjamin43ec06f2014-08-05 02:28:57 -04001157
David Benjamin6fd297b2014-08-11 18:43:38 -04001158 // Client does False Start and negotiates NPN.
1159 testCases = append(testCases, testCase{
1160 protocol: protocol,
1161 name: "FalseStart" + suffix,
1162 config: Config{
1163 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1164 NextProtos: []string{"foo"},
1165 Bugs: ProtocolBugs{
David Benjamine58c4f52014-08-24 03:47:07 -04001166 ExpectFalseStart: true,
David Benjamin6fd297b2014-08-11 18:43:38 -04001167 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1168 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001169 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001170 flags: append(flags,
1171 "-false-start",
1172 "-select-next-proto", "foo"),
David Benjamine58c4f52014-08-24 03:47:07 -04001173 shimWritesFirst: true,
1174 resumeSession: true,
David Benjamin6fd297b2014-08-11 18:43:38 -04001175 })
David Benjamin43ec06f2014-08-05 02:28:57 -04001176
David Benjamin6fd297b2014-08-11 18:43:38 -04001177 // False Start without session tickets.
1178 testCases = append(testCases, testCase{
1179 name: "FalseStart-SessionTicketsDisabled",
1180 config: Config{
1181 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1182 NextProtos: []string{"foo"},
1183 SessionTicketsDisabled: true,
David Benjamin4e99c522014-08-24 01:45:30 -04001184 Bugs: ProtocolBugs{
David Benjamine58c4f52014-08-24 03:47:07 -04001185 ExpectFalseStart: true,
David Benjamin4e99c522014-08-24 01:45:30 -04001186 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1187 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001188 },
David Benjamin4e99c522014-08-24 01:45:30 -04001189 flags: append(flags,
David Benjamin6fd297b2014-08-11 18:43:38 -04001190 "-false-start",
1191 "-select-next-proto", "foo",
David Benjamin4e99c522014-08-24 01:45:30 -04001192 ),
David Benjamine58c4f52014-08-24 03:47:07 -04001193 shimWritesFirst: true,
David Benjamin6fd297b2014-08-11 18:43:38 -04001194 })
David Benjamin1e7f8d72014-08-08 12:27:04 -04001195
David Benjamina08e49d2014-08-24 01:46:07 -04001196 // Server parses a V2ClientHello.
David Benjamin6fd297b2014-08-11 18:43:38 -04001197 testCases = append(testCases, testCase{
1198 protocol: protocol,
1199 testType: serverTest,
1200 name: "SendV2ClientHello" + suffix,
1201 config: Config{
1202 // Choose a cipher suite that does not involve
1203 // elliptic curves, so no extensions are
1204 // involved.
1205 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
1206 Bugs: ProtocolBugs{
1207 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1208 SendV2ClientHello: true,
1209 },
David Benjamin1e7f8d72014-08-08 12:27:04 -04001210 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001211 flags: flags,
1212 })
David Benjamina08e49d2014-08-24 01:46:07 -04001213
1214 // Client sends a Channel ID.
1215 testCases = append(testCases, testCase{
1216 protocol: protocol,
1217 name: "ChannelID-Client" + suffix,
1218 config: Config{
1219 RequestChannelID: true,
1220 Bugs: ProtocolBugs{
1221 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1222 },
1223 },
1224 flags: append(flags,
1225 "-send-channel-id", channelIDKeyFile,
1226 ),
1227 resumeSession: true,
1228 expectChannelID: true,
1229 })
1230
1231 // Server accepts a Channel ID.
1232 testCases = append(testCases, testCase{
1233 protocol: protocol,
1234 testType: serverTest,
1235 name: "ChannelID-Server" + suffix,
1236 config: Config{
1237 ChannelID: channelIDKey,
1238 Bugs: ProtocolBugs{
1239 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1240 },
1241 },
1242 flags: append(flags,
1243 "-expect-channel-id",
1244 base64.StdEncoding.EncodeToString(channelIDBytes),
1245 ),
1246 resumeSession: true,
1247 expectChannelID: true,
1248 })
David Benjamin6fd297b2014-08-11 18:43:38 -04001249 } else {
1250 testCases = append(testCases, testCase{
1251 protocol: protocol,
1252 name: "SkipHelloVerifyRequest" + suffix,
1253 config: Config{
1254 Bugs: ProtocolBugs{
1255 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1256 SkipHelloVerifyRequest: true,
1257 },
1258 },
1259 flags: flags,
1260 })
1261
1262 testCases = append(testCases, testCase{
1263 testType: serverTest,
1264 protocol: protocol,
1265 name: "CookieExchange" + suffix,
1266 config: Config{
1267 Bugs: ProtocolBugs{
1268 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1269 },
1270 },
1271 flags: append(flags, "-cookie-exchange"),
1272 })
1273 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001274}
1275
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001276func addVersionNegotiationTests() {
1277 for i, shimVers := range tlsVersions {
1278 // Assemble flags to disable all newer versions on the shim.
1279 var flags []string
1280 for _, vers := range tlsVersions[i+1:] {
1281 flags = append(flags, vers.flag)
1282 }
1283
1284 for _, runnerVers := range tlsVersions {
1285 expectedVersion := shimVers.version
1286 if runnerVers.version < shimVers.version {
1287 expectedVersion = runnerVers.version
1288 }
1289 suffix := shimVers.name + "-" + runnerVers.name
1290
1291 testCases = append(testCases, testCase{
1292 testType: clientTest,
1293 name: "VersionNegotiation-Client-" + suffix,
1294 config: Config{
1295 MaxVersion: runnerVers.version,
1296 },
1297 flags: flags,
1298 expectedVersion: expectedVersion,
1299 })
1300
David Benjamin76d8abe2014-08-14 16:25:34 -04001301 testCases = append(testCases, testCase{
1302 testType: serverTest,
1303 name: "VersionNegotiation-Server-" + suffix,
1304 config: Config{
1305 MaxVersion: runnerVers.version,
1306 },
1307 flags: flags,
1308 expectedVersion: expectedVersion,
1309 })
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001310 }
1311 }
1312}
1313
David Benjamin884fdf12014-08-02 15:28:23 -04001314func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
Adam Langley95c29f32014-06-20 12:00:00 -07001315 defer wg.Done()
1316
1317 for test := range c {
1318 statusChan <- statusMsg{test: test, started: true}
David Benjamin884fdf12014-08-02 15:28:23 -04001319 err := runTest(test, buildDir)
Adam Langley95c29f32014-06-20 12:00:00 -07001320 statusChan <- statusMsg{test: test, err: err}
1321 }
1322}
1323
1324type statusMsg struct {
1325 test *testCase
1326 started bool
1327 err error
1328}
1329
1330func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
1331 var started, done, failed, lineLen int
1332 defer close(doneChan)
1333
1334 for msg := range statusChan {
1335 if msg.started {
1336 started++
1337 } else {
1338 done++
1339 }
1340
1341 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
1342
1343 if msg.err != nil {
1344 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
1345 failed++
1346 }
1347 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
1348 lineLen = len(line)
1349 os.Stdout.WriteString(line)
1350 }
1351}
1352
1353func main() {
1354 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 -04001355 var flagNumWorkers *int = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
David Benjamin884fdf12014-08-02 15:28:23 -04001356 var flagBuildDir *string = flag.String("build-dir", "../../../build", "The build directory to run the shim from.")
Adam Langley95c29f32014-06-20 12:00:00 -07001357
1358 flag.Parse()
1359
1360 addCipherSuiteTests()
1361 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -07001362 addCBCPaddingTests()
Kenny Root7fdeaf12014-08-05 15:23:37 -07001363 addCBCSplittingTests()
David Benjamin636293b2014-07-08 17:59:18 -04001364 addClientAuthTests()
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001365 addVersionNegotiationTests()
David Benjamin43ec06f2014-08-05 02:28:57 -04001366 for _, async := range []bool{false, true} {
1367 for _, splitHandshake := range []bool{false, true} {
David Benjamin6fd297b2014-08-11 18:43:38 -04001368 for _, protocol := range []protocol{tls, dtls} {
1369 addStateMachineCoverageTests(async, splitHandshake, protocol)
1370 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001371 }
1372 }
Adam Langley95c29f32014-06-20 12:00:00 -07001373
1374 var wg sync.WaitGroup
1375
David Benjamin2bc8e6f2014-08-02 15:22:37 -04001376 numWorkers := *flagNumWorkers
Adam Langley95c29f32014-06-20 12:00:00 -07001377
1378 statusChan := make(chan statusMsg, numWorkers)
1379 testChan := make(chan *testCase, numWorkers)
1380 doneChan := make(chan struct{})
1381
David Benjamin025b3d32014-07-01 19:53:04 -04001382 go statusPrinter(doneChan, statusChan, len(testCases))
Adam Langley95c29f32014-06-20 12:00:00 -07001383
1384 for i := 0; i < numWorkers; i++ {
1385 wg.Add(1)
David Benjamin884fdf12014-08-02 15:28:23 -04001386 go worker(statusChan, testChan, *flagBuildDir, &wg)
Adam Langley95c29f32014-06-20 12:00:00 -07001387 }
1388
David Benjamin025b3d32014-07-01 19:53:04 -04001389 for i := range testCases {
1390 if len(*flagTest) == 0 || *flagTest == testCases[i].name {
1391 testChan <- &testCases[i]
Adam Langley95c29f32014-06-20 12:00:00 -07001392 }
1393 }
1394
1395 close(testChan)
1396 wg.Wait()
1397 close(statusChan)
1398 <-doneChan
1399
1400 fmt.Printf("\n")
1401}