blob: 64df21df8d16f8c88d9a66d8d5bb8724220cb9b9 [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 },
David Benjamin39ebf532014-08-31 02:23:49 -0400486 {
487 name: "SkipCipherVersionCheck",
488 config: Config{
489 CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
490 MaxVersion: VersionTLS11,
491 Bugs: ProtocolBugs{
492 SkipCipherVersionCheck: true,
493 },
494 },
495 shouldFail: true,
496 expectedError: ":WRONG_CIPHER_RETURNED:",
497 },
Adam Langley95c29f32014-06-20 12:00:00 -0700498}
499
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400500func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int) error {
David Benjamin6fd297b2014-08-11 18:43:38 -0400501 if test.protocol == dtls {
502 conn = newPacketAdaptor(conn)
503 }
504
505 if test.sendPrefix != "" {
506 if _, err := conn.Write([]byte(test.sendPrefix)); err != nil {
507 return err
508 }
David Benjamin98e882e2014-08-08 13:24:34 -0400509 }
510
David Benjamin1d5c83e2014-07-22 19:20:02 -0400511 var tlsConn *Conn
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400512 if test.testType == clientTest {
David Benjamin6fd297b2014-08-11 18:43:38 -0400513 if test.protocol == dtls {
514 tlsConn = DTLSServer(conn, config)
515 } else {
516 tlsConn = Server(conn, config)
517 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400518 } else {
519 config.InsecureSkipVerify = true
David Benjamin6fd297b2014-08-11 18:43:38 -0400520 if test.protocol == dtls {
521 tlsConn = DTLSClient(conn, config)
522 } else {
523 tlsConn = Client(conn, config)
524 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400525 }
526
Adam Langley95c29f32014-06-20 12:00:00 -0700527 if err := tlsConn.Handshake(); err != nil {
528 return err
529 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700530
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400531 if vers := tlsConn.ConnectionState().Version; test.expectedVersion != 0 && vers != test.expectedVersion {
532 return fmt.Errorf("got version %x, expected %x", vers, test.expectedVersion)
533 }
534
David Benjamina08e49d2014-08-24 01:46:07 -0400535 if test.expectChannelID {
536 channelID := tlsConn.ConnectionState().ChannelID
537 if channelID == nil {
538 return fmt.Errorf("no channel ID negotiated")
539 }
540 if channelID.Curve != channelIDKey.Curve ||
541 channelIDKey.X.Cmp(channelIDKey.X) != 0 ||
542 channelIDKey.Y.Cmp(channelIDKey.Y) != 0 {
543 return fmt.Errorf("incorrect channel ID")
544 }
545 }
546
David Benjamine58c4f52014-08-24 03:47:07 -0400547 if test.shimWritesFirst {
548 var buf [5]byte
549 _, err := io.ReadFull(tlsConn, buf[:])
550 if err != nil {
551 return err
552 }
553 if string(buf[:]) != "hello" {
554 return fmt.Errorf("bad initial message")
555 }
556 }
557
Kenny Root7fdeaf12014-08-05 15:23:37 -0700558 if messageLen < 0 {
David Benjamin6fd297b2014-08-11 18:43:38 -0400559 if test.protocol == dtls {
560 return fmt.Errorf("messageLen < 0 not supported for DTLS tests")
561 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700562 // Read until EOF.
563 _, err := io.Copy(ioutil.Discard, tlsConn)
564 return err
565 }
566
Adam Langley80842bd2014-06-20 12:00:00 -0700567 if messageLen == 0 {
568 messageLen = 32
569 }
570 testMessage := make([]byte, messageLen)
571 for i := range testMessage {
572 testMessage[i] = 0x42
573 }
Adam Langley95c29f32014-06-20 12:00:00 -0700574 tlsConn.Write(testMessage)
575
576 buf := make([]byte, len(testMessage))
David Benjamin6fd297b2014-08-11 18:43:38 -0400577 if test.protocol == dtls {
578 bufTmp := make([]byte, len(buf)+1)
579 n, err := tlsConn.Read(bufTmp)
580 if err != nil {
581 return err
582 }
583 if n != len(buf) {
584 return fmt.Errorf("bad reply; length mismatch (%d vs %d)", n, len(buf))
585 }
586 copy(buf, bufTmp)
587 } else {
588 _, err := io.ReadFull(tlsConn, buf)
589 if err != nil {
590 return err
591 }
Adam Langley95c29f32014-06-20 12:00:00 -0700592 }
593
594 for i, v := range buf {
595 if v != testMessage[i]^0xff {
596 return fmt.Errorf("bad reply contents at byte %d", i)
597 }
598 }
599
600 return nil
601}
602
David Benjamin325b5c32014-07-01 19:40:31 -0400603func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd {
604 valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
Adam Langley95c29f32014-06-20 12:00:00 -0700605 if dbAttach {
David Benjamin325b5c32014-07-01 19:40:31 -0400606 valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
Adam Langley95c29f32014-06-20 12:00:00 -0700607 }
David Benjamin325b5c32014-07-01 19:40:31 -0400608 valgrindArgs = append(valgrindArgs, path)
609 valgrindArgs = append(valgrindArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700610
David Benjamin325b5c32014-07-01 19:40:31 -0400611 return exec.Command("valgrind", valgrindArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700612}
613
David Benjamin325b5c32014-07-01 19:40:31 -0400614func gdbOf(path string, args ...string) *exec.Cmd {
615 xtermArgs := []string{"-e", "gdb", "--args"}
616 xtermArgs = append(xtermArgs, path)
617 xtermArgs = append(xtermArgs, args...)
Adam Langley95c29f32014-06-20 12:00:00 -0700618
David Benjamin325b5c32014-07-01 19:40:31 -0400619 return exec.Command("xterm", xtermArgs...)
Adam Langley95c29f32014-06-20 12:00:00 -0700620}
621
David Benjamin1d5c83e2014-07-22 19:20:02 -0400622func openSocketPair() (shimEnd *os.File, conn net.Conn) {
Adam Langley95c29f32014-06-20 12:00:00 -0700623 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
624 if err != nil {
625 panic(err)
626 }
627
628 syscall.CloseOnExec(socks[0])
629 syscall.CloseOnExec(socks[1])
David Benjamin1d5c83e2014-07-22 19:20:02 -0400630 shimEnd = os.NewFile(uintptr(socks[0]), "shim end")
Adam Langley95c29f32014-06-20 12:00:00 -0700631 connFile := os.NewFile(uintptr(socks[1]), "our end")
David Benjamin1d5c83e2014-07-22 19:20:02 -0400632 conn, err = net.FileConn(connFile)
633 if err != nil {
634 panic(err)
635 }
Adam Langley95c29f32014-06-20 12:00:00 -0700636 connFile.Close()
637 if err != nil {
638 panic(err)
639 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400640 return shimEnd, conn
641}
642
David Benjamin884fdf12014-08-02 15:28:23 -0400643func runTest(test *testCase, buildDir string) error {
David Benjamin1d5c83e2014-07-22 19:20:02 -0400644 shimEnd, conn := openSocketPair()
645 shimEndResume, connResume := openSocketPair()
Adam Langley95c29f32014-06-20 12:00:00 -0700646
David Benjamin884fdf12014-08-02 15:28:23 -0400647 shim_path := path.Join(buildDir, "ssl/test/bssl_shim")
David Benjamin5a593af2014-08-11 19:51:50 -0400648 var flags []string
David Benjamin1d5c83e2014-07-22 19:20:02 -0400649 if test.testType == serverTest {
David Benjamin5a593af2014-08-11 19:51:50 -0400650 flags = append(flags, "-server")
651
David Benjamin025b3d32014-07-01 19:53:04 -0400652 flags = append(flags, "-key-file")
653 if test.keyFile == "" {
654 flags = append(flags, rsaKeyFile)
655 } else {
656 flags = append(flags, test.keyFile)
657 }
658
659 flags = append(flags, "-cert-file")
660 if test.certFile == "" {
661 flags = append(flags, rsaCertificateFile)
662 } else {
663 flags = append(flags, test.certFile)
664 }
665 }
David Benjamin5a593af2014-08-11 19:51:50 -0400666
David Benjamin6fd297b2014-08-11 18:43:38 -0400667 if test.protocol == dtls {
668 flags = append(flags, "-dtls")
669 }
670
David Benjamin5a593af2014-08-11 19:51:50 -0400671 if test.resumeSession {
672 flags = append(flags, "-resume")
673 }
674
David Benjamine58c4f52014-08-24 03:47:07 -0400675 if test.shimWritesFirst {
676 flags = append(flags, "-shim-writes-first")
677 }
678
David Benjamin025b3d32014-07-01 19:53:04 -0400679 flags = append(flags, test.flags...)
680
681 var shim *exec.Cmd
682 if *useValgrind {
683 shim = valgrindOf(false, shim_path, flags...)
684 } else {
685 shim = exec.Command(shim_path, flags...)
686 }
687 // shim = gdbOf(shim_path, flags...)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400688 shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
David Benjamin025b3d32014-07-01 19:53:04 -0400689 shim.Stdin = os.Stdin
690 var stdoutBuf, stderrBuf bytes.Buffer
691 shim.Stdout = &stdoutBuf
692 shim.Stderr = &stderrBuf
693
694 if err := shim.Start(); err != nil {
Adam Langley95c29f32014-06-20 12:00:00 -0700695 panic(err)
696 }
David Benjamin025b3d32014-07-01 19:53:04 -0400697 shimEnd.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400698 shimEndResume.Close()
Adam Langley95c29f32014-06-20 12:00:00 -0700699
700 config := test.config
David Benjamin1d5c83e2014-07-22 19:20:02 -0400701 config.ClientSessionCache = NewLRUClientSessionCache(1)
David Benjamin025b3d32014-07-01 19:53:04 -0400702 if test.testType == clientTest {
703 if len(config.Certificates) == 0 {
704 config.Certificates = []Certificate{getRSACertificate()}
705 }
David Benjamin025b3d32014-07-01 19:53:04 -0400706 }
Adam Langley95c29f32014-06-20 12:00:00 -0700707
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400708 err := doExchange(test, &config, conn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700709 conn.Close()
David Benjamin1d5c83e2014-07-22 19:20:02 -0400710 if err == nil && test.resumeSession {
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400711 err = doExchange(test, &config, connResume, test.messageLen)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400712 connResume.Close()
713 }
714
David Benjamin025b3d32014-07-01 19:53:04 -0400715 childErr := shim.Wait()
Adam Langley95c29f32014-06-20 12:00:00 -0700716
717 stdout := string(stdoutBuf.Bytes())
718 stderr := string(stderrBuf.Bytes())
719 failed := err != nil || childErr != nil
720 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700721 localError := "none"
722 if err != nil {
723 localError = err.Error()
724 }
725 if len(test.expectedLocalError) != 0 {
726 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
727 }
Adam Langley95c29f32014-06-20 12:00:00 -0700728
729 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700730 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700731 if childErr != nil {
732 childError = childErr.Error()
733 }
734
735 var msg string
736 switch {
737 case failed && !test.shouldFail:
738 msg = "unexpected failure"
739 case !failed && test.shouldFail:
740 msg = "unexpected success"
741 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700742 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700743 default:
744 panic("internal error")
745 }
746
747 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
748 }
749
750 if !*useValgrind && len(stderr) > 0 {
751 println(stderr)
752 }
753
754 return nil
755}
756
757var tlsVersions = []struct {
758 name string
759 version uint16
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400760 flag string
Adam Langley95c29f32014-06-20 12:00:00 -0700761}{
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400762 {"SSL3", VersionSSL30, "-no-ssl3"},
763 {"TLS1", VersionTLS10, "-no-tls1"},
764 {"TLS11", VersionTLS11, "-no-tls11"},
765 {"TLS12", VersionTLS12, "-no-tls12"},
Adam Langley95c29f32014-06-20 12:00:00 -0700766}
767
768var testCipherSuites = []struct {
769 name string
770 id uint16
771}{
772 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400773 {"AES128-GCM", TLS_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700774 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400775 {"AES128-SHA256", TLS_RSA_WITH_AES_128_CBC_SHA256},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400776 {"AES256-GCM", TLS_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700777 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400778 {"AES256-SHA256", TLS_RSA_WITH_AES_256_CBC_SHA256},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400779 {"DHE-RSA-AES128-GCM", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
780 {"DHE-RSA-AES128-SHA", TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400781 {"DHE-RSA-AES128-SHA256", TLS_DHE_RSA_WITH_AES_128_CBC_SHA256},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400782 {"DHE-RSA-AES256-GCM", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384},
783 {"DHE-RSA-AES256-SHA", TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400784 {"DHE-RSA-AES256-SHA256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700785 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
786 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400787 {"ECDHE-ECDSA-AES128-SHA256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
788 {"ECDHE-ECDSA-AES256-GCM", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700789 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400790 {"ECDHE-ECDSA-AES256-SHA384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700791 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700792 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
Adam Langley95c29f32014-06-20 12:00:00 -0700793 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400794 {"ECDHE-RSA-AES128-SHA256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400795 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700796 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
David Benjaminf7768e42014-08-31 02:06:47 -0400797 {"ECDHE-RSA-AES256-SHA384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384},
Adam Langley95c29f32014-06-20 12:00:00 -0700798 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700799 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400800 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
Adam Langley95c29f32014-06-20 12:00:00 -0700801}
802
David Benjaminf7768e42014-08-31 02:06:47 -0400803func isTLS12Only(suiteName string) bool {
804 return strings.HasSuffix(suiteName, "-GCM") ||
805 strings.HasSuffix(suiteName, "-SHA256") ||
806 strings.HasSuffix(suiteName, "-SHA384")
807}
808
Adam Langley95c29f32014-06-20 12:00:00 -0700809func addCipherSuiteTests() {
810 for _, suite := range testCipherSuites {
811 var cert Certificate
David Benjamin025b3d32014-07-01 19:53:04 -0400812 var certFile string
813 var keyFile string
Adam Langley95c29f32014-06-20 12:00:00 -0700814 if strings.Contains(suite.name, "ECDSA") {
815 cert = getECDSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400816 certFile = ecdsaCertificateFile
817 keyFile = ecdsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700818 } else {
819 cert = getRSACertificate()
David Benjamin025b3d32014-07-01 19:53:04 -0400820 certFile = rsaCertificateFile
821 keyFile = rsaKeyFile
Adam Langley95c29f32014-06-20 12:00:00 -0700822 }
823
824 for _, ver := range tlsVersions {
David Benjaminf7768e42014-08-31 02:06:47 -0400825 if ver.version < VersionTLS12 && isTLS12Only(suite.name) {
Adam Langley95c29f32014-06-20 12:00:00 -0700826 continue
827 }
828
David Benjamin1d5c83e2014-07-22 19:20:02 -0400829 // Go's TLS implementation only implements session
830 // resumption with tickets, so SSLv3 cannot resume
831 // sessions.
832 resumeSession := ver.version != VersionSSL30
833
David Benjamin025b3d32014-07-01 19:53:04 -0400834 testCases = append(testCases, testCase{
835 testType: clientTest,
836 name: ver.name + "-" + suite.name + "-client",
Adam Langley95c29f32014-06-20 12:00:00 -0700837 config: Config{
838 MinVersion: ver.version,
839 MaxVersion: ver.version,
840 CipherSuites: []uint16{suite.id},
841 Certificates: []Certificate{cert},
842 },
David Benjamin1d5c83e2014-07-22 19:20:02 -0400843 resumeSession: resumeSession,
Adam Langley95c29f32014-06-20 12:00:00 -0700844 })
David Benjamin025b3d32014-07-01 19:53:04 -0400845
David Benjamin76d8abe2014-08-14 16:25:34 -0400846 testCases = append(testCases, testCase{
847 testType: serverTest,
848 name: ver.name + "-" + suite.name + "-server",
849 config: Config{
850 MinVersion: ver.version,
851 MaxVersion: ver.version,
852 CipherSuites: []uint16{suite.id},
853 Certificates: []Certificate{cert},
854 },
855 certFile: certFile,
856 keyFile: keyFile,
857 resumeSession: resumeSession,
858 })
David Benjamin6fd297b2014-08-11 18:43:38 -0400859
860 // TODO(davidben): Fix DTLS 1.2 support and test that.
861 if ver.version == VersionTLS10 && strings.Index(suite.name, "RC4") == -1 {
862 testCases = append(testCases, testCase{
863 testType: clientTest,
864 protocol: dtls,
865 name: "D" + ver.name + "-" + suite.name + "-client",
866 config: Config{
867 MinVersion: ver.version,
868 MaxVersion: ver.version,
869 CipherSuites: []uint16{suite.id},
870 Certificates: []Certificate{cert},
871 },
872 resumeSession: resumeSession,
873 })
874 testCases = append(testCases, testCase{
875 testType: serverTest,
876 protocol: dtls,
877 name: "D" + ver.name + "-" + suite.name + "-server",
878 config: Config{
879 MinVersion: ver.version,
880 MaxVersion: ver.version,
881 CipherSuites: []uint16{suite.id},
882 Certificates: []Certificate{cert},
883 },
884 certFile: certFile,
885 keyFile: keyFile,
886 resumeSession: resumeSession,
887 })
888 }
Adam Langley95c29f32014-06-20 12:00:00 -0700889 }
890 }
891}
892
893func addBadECDSASignatureTests() {
894 for badR := BadValue(1); badR < NumBadValues; badR++ {
895 for badS := BadValue(1); badS < NumBadValues; badS++ {
David Benjamin025b3d32014-07-01 19:53:04 -0400896 testCases = append(testCases, testCase{
Adam Langley95c29f32014-06-20 12:00:00 -0700897 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
898 config: Config{
899 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
900 Certificates: []Certificate{getECDSACertificate()},
901 Bugs: ProtocolBugs{
902 BadECDSAR: badR,
903 BadECDSAS: badS,
904 },
905 },
906 shouldFail: true,
907 expectedError: "SIGNATURE",
908 })
909 }
910 }
911}
912
Adam Langley80842bd2014-06-20 12:00:00 -0700913func addCBCPaddingTests() {
David Benjamin025b3d32014-07-01 19:53:04 -0400914 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700915 name: "MaxCBCPadding",
916 config: Config{
917 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
918 Bugs: ProtocolBugs{
919 MaxPadding: true,
920 },
921 },
922 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
923 })
David Benjamin025b3d32014-07-01 19:53:04 -0400924 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700925 name: "BadCBCPadding",
926 config: Config{
927 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
928 Bugs: ProtocolBugs{
929 PaddingFirstByteBad: true,
930 },
931 },
932 shouldFail: true,
933 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
934 })
935 // OpenSSL previously had an issue where the first byte of padding in
936 // 255 bytes of padding wasn't checked.
David Benjamin025b3d32014-07-01 19:53:04 -0400937 testCases = append(testCases, testCase{
Adam Langley80842bd2014-06-20 12:00:00 -0700938 name: "BadCBCPadding255",
939 config: Config{
940 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
941 Bugs: ProtocolBugs{
942 MaxPadding: true,
943 PaddingFirstByteBadIf255: true,
944 },
945 },
946 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
947 shouldFail: true,
948 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
949 })
950}
951
Kenny Root7fdeaf12014-08-05 15:23:37 -0700952func addCBCSplittingTests() {
953 testCases = append(testCases, testCase{
954 name: "CBCRecordSplitting",
955 config: Config{
956 MaxVersion: VersionTLS10,
957 MinVersion: VersionTLS10,
958 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
959 },
960 messageLen: -1, // read until EOF
961 flags: []string{
962 "-async",
963 "-write-different-record-sizes",
964 "-cbc-record-splitting",
965 },
David Benjamina8e3e0e2014-08-06 22:11:10 -0400966 })
967 testCases = append(testCases, testCase{
Kenny Root7fdeaf12014-08-05 15:23:37 -0700968 name: "CBCRecordSplittingPartialWrite",
969 config: Config{
970 MaxVersion: VersionTLS10,
971 MinVersion: VersionTLS10,
972 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
973 },
974 messageLen: -1, // read until EOF
975 flags: []string{
976 "-async",
977 "-write-different-record-sizes",
978 "-cbc-record-splitting",
979 "-partial-write",
980 },
981 })
982}
983
David Benjamin636293b2014-07-08 17:59:18 -0400984func addClientAuthTests() {
David Benjamin407a10c2014-07-16 12:58:59 -0400985 // Add a dummy cert pool to stress certificate authority parsing.
986 // TODO(davidben): Add tests that those values parse out correctly.
987 certPool := x509.NewCertPool()
988 cert, err := x509.ParseCertificate(rsaCertificate.Certificate[0])
989 if err != nil {
990 panic(err)
991 }
992 certPool.AddCert(cert)
993
David Benjamin636293b2014-07-08 17:59:18 -0400994 for _, ver := range tlsVersions {
David Benjamin636293b2014-07-08 17:59:18 -0400995 testCases = append(testCases, testCase{
996 testType: clientTest,
David Benjamin67666e72014-07-12 15:47:52 -0400997 name: ver.name + "-Client-ClientAuth-RSA",
David Benjamin636293b2014-07-08 17:59:18 -0400998 config: Config{
David Benjamine098ec22014-08-27 23:13:20 -0400999 MinVersion: ver.version,
1000 MaxVersion: ver.version,
1001 ClientAuth: RequireAnyClientCert,
1002 ClientCAs: certPool,
David Benjamin636293b2014-07-08 17:59:18 -04001003 },
1004 flags: []string{
1005 "-cert-file", rsaCertificateFile,
1006 "-key-file", rsaKeyFile,
1007 },
1008 })
1009 testCases = append(testCases, testCase{
David Benjamin67666e72014-07-12 15:47:52 -04001010 testType: serverTest,
1011 name: ver.name + "-Server-ClientAuth-RSA",
1012 config: Config{
David Benjamine098ec22014-08-27 23:13:20 -04001013 MinVersion: ver.version,
1014 MaxVersion: ver.version,
David Benjamin67666e72014-07-12 15:47:52 -04001015 Certificates: []Certificate{rsaCertificate},
1016 },
1017 flags: []string{"-require-any-client-certificate"},
1018 })
David Benjamine098ec22014-08-27 23:13:20 -04001019 if ver.version != VersionSSL30 {
1020 testCases = append(testCases, testCase{
1021 testType: serverTest,
1022 name: ver.name + "-Server-ClientAuth-ECDSA",
1023 config: Config{
1024 MinVersion: ver.version,
1025 MaxVersion: ver.version,
1026 Certificates: []Certificate{ecdsaCertificate},
1027 },
1028 flags: []string{"-require-any-client-certificate"},
1029 })
1030 testCases = append(testCases, testCase{
1031 testType: clientTest,
1032 name: ver.name + "-Client-ClientAuth-ECDSA",
1033 config: Config{
1034 MinVersion: ver.version,
1035 MaxVersion: ver.version,
1036 ClientAuth: RequireAnyClientCert,
1037 ClientCAs: certPool,
1038 },
1039 flags: []string{
1040 "-cert-file", ecdsaCertificateFile,
1041 "-key-file", ecdsaKeyFile,
1042 },
1043 })
1044 }
David Benjamin636293b2014-07-08 17:59:18 -04001045 }
1046}
1047
David Benjamin43ec06f2014-08-05 02:28:57 -04001048// Adds tests that try to cover the range of the handshake state machine, under
1049// various conditions. Some of these are redundant with other tests, but they
1050// only cover the synchronous case.
David Benjamin6fd297b2014-08-11 18:43:38 -04001051func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) {
David Benjamin43ec06f2014-08-05 02:28:57 -04001052 var suffix string
1053 var flags []string
1054 var maxHandshakeRecordLength int
David Benjamin6fd297b2014-08-11 18:43:38 -04001055 if protocol == dtls {
1056 suffix = "-DTLS"
1057 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001058 if async {
David Benjamin6fd297b2014-08-11 18:43:38 -04001059 suffix += "-Async"
David Benjamin43ec06f2014-08-05 02:28:57 -04001060 flags = append(flags, "-async")
1061 } else {
David Benjamin6fd297b2014-08-11 18:43:38 -04001062 suffix += "-Sync"
David Benjamin43ec06f2014-08-05 02:28:57 -04001063 }
1064 if splitHandshake {
1065 suffix += "-SplitHandshakeRecords"
David Benjamin98214542014-08-07 18:02:39 -04001066 maxHandshakeRecordLength = 1
David Benjamin43ec06f2014-08-05 02:28:57 -04001067 }
1068
1069 // Basic handshake, with resumption. Client and server.
1070 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001071 protocol: protocol,
1072 name: "Basic-Client" + suffix,
David Benjamin43ec06f2014-08-05 02:28:57 -04001073 config: Config{
1074 Bugs: ProtocolBugs{
1075 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1076 },
1077 },
David Benjaminbed9aae2014-08-07 19:13:38 -04001078 flags: flags,
1079 resumeSession: true,
1080 })
1081 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001082 protocol: protocol,
1083 name: "Basic-Client-RenewTicket" + suffix,
David Benjaminbed9aae2014-08-07 19:13:38 -04001084 config: Config{
1085 Bugs: ProtocolBugs{
1086 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1087 RenewTicketOnResume: true,
1088 },
1089 },
1090 flags: flags,
1091 resumeSession: true,
David Benjamin43ec06f2014-08-05 02:28:57 -04001092 })
1093 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001094 protocol: protocol,
David Benjamin43ec06f2014-08-05 02:28:57 -04001095 testType: serverTest,
1096 name: "Basic-Server" + suffix,
1097 config: Config{
1098 Bugs: ProtocolBugs{
1099 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1100 },
1101 },
David Benjaminbed9aae2014-08-07 19:13:38 -04001102 flags: flags,
1103 resumeSession: true,
David Benjamin43ec06f2014-08-05 02:28:57 -04001104 })
1105
David Benjamin6fd297b2014-08-11 18:43:38 -04001106 // TLS client auth.
1107 testCases = append(testCases, testCase{
1108 protocol: protocol,
1109 testType: clientTest,
1110 name: "ClientAuth-Client" + suffix,
1111 config: Config{
David Benjamine098ec22014-08-27 23:13:20 -04001112 ClientAuth: RequireAnyClientCert,
David Benjamin6fd297b2014-08-11 18:43:38 -04001113 Bugs: ProtocolBugs{
1114 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1115 },
1116 },
1117 flags: append(flags,
1118 "-cert-file", rsaCertificateFile,
1119 "-key-file", rsaKeyFile),
1120 })
1121 testCases = append(testCases, testCase{
1122 protocol: protocol,
1123 testType: serverTest,
1124 name: "ClientAuth-Server" + suffix,
1125 config: Config{
1126 Certificates: []Certificate{rsaCertificate},
1127 },
1128 flags: append(flags, "-require-any-client-certificate"),
1129 })
1130
David Benjamin43ec06f2014-08-05 02:28:57 -04001131 // No session ticket support; server doesn't send NewSessionTicket.
1132 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001133 protocol: protocol,
1134 name: "SessionTicketsDisabled-Client" + suffix,
David Benjamin43ec06f2014-08-05 02:28:57 -04001135 config: Config{
1136 SessionTicketsDisabled: true,
1137 Bugs: ProtocolBugs{
1138 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1139 },
1140 },
1141 flags: flags,
1142 })
1143 testCases = append(testCases, testCase{
David Benjamin6fd297b2014-08-11 18:43:38 -04001144 protocol: protocol,
David Benjamin43ec06f2014-08-05 02:28:57 -04001145 testType: serverTest,
1146 name: "SessionTicketsDisabled-Server" + suffix,
1147 config: Config{
1148 SessionTicketsDisabled: true,
1149 Bugs: ProtocolBugs{
1150 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1151 },
1152 },
1153 flags: flags,
1154 })
1155
David Benjamin6fd297b2014-08-11 18:43:38 -04001156 if protocol == tls {
1157 // NPN on client and server; results in post-handshake message.
1158 testCases = append(testCases, testCase{
1159 protocol: protocol,
1160 name: "NPN-Client" + suffix,
1161 config: Config{
1162 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1163 NextProtos: []string{"foo"},
1164 Bugs: ProtocolBugs{
1165 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1166 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001167 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001168 flags: append(flags, "-select-next-proto", "foo"),
1169 })
1170 testCases = append(testCases, testCase{
1171 protocol: protocol,
1172 testType: serverTest,
1173 name: "NPN-Server" + suffix,
1174 config: Config{
1175 NextProtos: []string{"bar"},
1176 Bugs: ProtocolBugs{
1177 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1178 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001179 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001180 flags: append(flags,
1181 "-advertise-npn", "\x03foo\x03bar\x03baz",
1182 "-expect-next-proto", "bar"),
1183 })
David Benjamin43ec06f2014-08-05 02:28:57 -04001184
David Benjamin6fd297b2014-08-11 18:43:38 -04001185 // Client does False Start and negotiates NPN.
1186 testCases = append(testCases, testCase{
1187 protocol: protocol,
1188 name: "FalseStart" + suffix,
1189 config: Config{
1190 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1191 NextProtos: []string{"foo"},
1192 Bugs: ProtocolBugs{
David Benjamine58c4f52014-08-24 03:47:07 -04001193 ExpectFalseStart: true,
David Benjamin6fd297b2014-08-11 18:43:38 -04001194 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1195 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001196 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001197 flags: append(flags,
1198 "-false-start",
1199 "-select-next-proto", "foo"),
David Benjamine58c4f52014-08-24 03:47:07 -04001200 shimWritesFirst: true,
1201 resumeSession: true,
David Benjamin6fd297b2014-08-11 18:43:38 -04001202 })
David Benjamin43ec06f2014-08-05 02:28:57 -04001203
David Benjamin6fd297b2014-08-11 18:43:38 -04001204 // False Start without session tickets.
1205 testCases = append(testCases, testCase{
1206 name: "FalseStart-SessionTicketsDisabled",
1207 config: Config{
1208 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
1209 NextProtos: []string{"foo"},
1210 SessionTicketsDisabled: true,
David Benjamin4e99c522014-08-24 01:45:30 -04001211 Bugs: ProtocolBugs{
David Benjamine58c4f52014-08-24 03:47:07 -04001212 ExpectFalseStart: true,
David Benjamin4e99c522014-08-24 01:45:30 -04001213 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1214 },
David Benjamin43ec06f2014-08-05 02:28:57 -04001215 },
David Benjamin4e99c522014-08-24 01:45:30 -04001216 flags: append(flags,
David Benjamin6fd297b2014-08-11 18:43:38 -04001217 "-false-start",
1218 "-select-next-proto", "foo",
David Benjamin4e99c522014-08-24 01:45:30 -04001219 ),
David Benjamine58c4f52014-08-24 03:47:07 -04001220 shimWritesFirst: true,
David Benjamin6fd297b2014-08-11 18:43:38 -04001221 })
David Benjamin1e7f8d72014-08-08 12:27:04 -04001222
David Benjamina08e49d2014-08-24 01:46:07 -04001223 // Server parses a V2ClientHello.
David Benjamin6fd297b2014-08-11 18:43:38 -04001224 testCases = append(testCases, testCase{
1225 protocol: protocol,
1226 testType: serverTest,
1227 name: "SendV2ClientHello" + suffix,
1228 config: Config{
1229 // Choose a cipher suite that does not involve
1230 // elliptic curves, so no extensions are
1231 // involved.
1232 CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
1233 Bugs: ProtocolBugs{
1234 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1235 SendV2ClientHello: true,
1236 },
David Benjamin1e7f8d72014-08-08 12:27:04 -04001237 },
David Benjamin6fd297b2014-08-11 18:43:38 -04001238 flags: flags,
1239 })
David Benjamina08e49d2014-08-24 01:46:07 -04001240
1241 // Client sends a Channel ID.
1242 testCases = append(testCases, testCase{
1243 protocol: protocol,
1244 name: "ChannelID-Client" + suffix,
1245 config: Config{
1246 RequestChannelID: true,
1247 Bugs: ProtocolBugs{
1248 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1249 },
1250 },
1251 flags: append(flags,
1252 "-send-channel-id", channelIDKeyFile,
1253 ),
1254 resumeSession: true,
1255 expectChannelID: true,
1256 })
1257
1258 // Server accepts a Channel ID.
1259 testCases = append(testCases, testCase{
1260 protocol: protocol,
1261 testType: serverTest,
1262 name: "ChannelID-Server" + suffix,
1263 config: Config{
1264 ChannelID: channelIDKey,
1265 Bugs: ProtocolBugs{
1266 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1267 },
1268 },
1269 flags: append(flags,
1270 "-expect-channel-id",
1271 base64.StdEncoding.EncodeToString(channelIDBytes),
1272 ),
1273 resumeSession: true,
1274 expectChannelID: true,
1275 })
David Benjamin6fd297b2014-08-11 18:43:38 -04001276 } else {
1277 testCases = append(testCases, testCase{
1278 protocol: protocol,
1279 name: "SkipHelloVerifyRequest" + suffix,
1280 config: Config{
1281 Bugs: ProtocolBugs{
1282 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1283 SkipHelloVerifyRequest: true,
1284 },
1285 },
1286 flags: flags,
1287 })
1288
1289 testCases = append(testCases, testCase{
1290 testType: serverTest,
1291 protocol: protocol,
1292 name: "CookieExchange" + suffix,
1293 config: Config{
1294 Bugs: ProtocolBugs{
1295 MaxHandshakeRecordLength: maxHandshakeRecordLength,
1296 },
1297 },
1298 flags: append(flags, "-cookie-exchange"),
1299 })
1300 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001301}
1302
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001303func addVersionNegotiationTests() {
1304 for i, shimVers := range tlsVersions {
1305 // Assemble flags to disable all newer versions on the shim.
1306 var flags []string
1307 for _, vers := range tlsVersions[i+1:] {
1308 flags = append(flags, vers.flag)
1309 }
1310
1311 for _, runnerVers := range tlsVersions {
1312 expectedVersion := shimVers.version
1313 if runnerVers.version < shimVers.version {
1314 expectedVersion = runnerVers.version
1315 }
1316 suffix := shimVers.name + "-" + runnerVers.name
1317
1318 testCases = append(testCases, testCase{
1319 testType: clientTest,
1320 name: "VersionNegotiation-Client-" + suffix,
1321 config: Config{
1322 MaxVersion: runnerVers.version,
1323 },
1324 flags: flags,
1325 expectedVersion: expectedVersion,
1326 })
1327
David Benjamin76d8abe2014-08-14 16:25:34 -04001328 testCases = append(testCases, testCase{
1329 testType: serverTest,
1330 name: "VersionNegotiation-Server-" + suffix,
1331 config: Config{
1332 MaxVersion: runnerVers.version,
1333 },
1334 flags: flags,
1335 expectedVersion: expectedVersion,
1336 })
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001337 }
1338 }
1339}
1340
David Benjamin5c24a1d2014-08-31 00:59:27 -04001341func addD5BugTests() {
1342 testCases = append(testCases, testCase{
1343 testType: serverTest,
1344 name: "D5Bug-NoQuirk-Reject",
1345 config: Config{
1346 CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
1347 Bugs: ProtocolBugs{
1348 SSL3RSAKeyExchange: true,
1349 },
1350 },
1351 shouldFail: true,
1352 expectedError: ":TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG:",
1353 })
1354 testCases = append(testCases, testCase{
1355 testType: serverTest,
1356 name: "D5Bug-Quirk-Normal",
1357 config: Config{
1358 CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
1359 },
1360 flags: []string{"-tls-d5-bug"},
1361 })
1362 testCases = append(testCases, testCase{
1363 testType: serverTest,
1364 name: "D5Bug-Quirk-Bug",
1365 config: Config{
1366 CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
1367 Bugs: ProtocolBugs{
1368 SSL3RSAKeyExchange: true,
1369 },
1370 },
1371 flags: []string{"-tls-d5-bug"},
1372 })
1373}
1374
David Benjamin884fdf12014-08-02 15:28:23 -04001375func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) {
Adam Langley95c29f32014-06-20 12:00:00 -07001376 defer wg.Done()
1377
1378 for test := range c {
1379 statusChan <- statusMsg{test: test, started: true}
David Benjamin884fdf12014-08-02 15:28:23 -04001380 err := runTest(test, buildDir)
Adam Langley95c29f32014-06-20 12:00:00 -07001381 statusChan <- statusMsg{test: test, err: err}
1382 }
1383}
1384
1385type statusMsg struct {
1386 test *testCase
1387 started bool
1388 err error
1389}
1390
1391func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
1392 var started, done, failed, lineLen int
1393 defer close(doneChan)
1394
1395 for msg := range statusChan {
1396 if msg.started {
1397 started++
1398 } else {
1399 done++
1400 }
1401
1402 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
1403
1404 if msg.err != nil {
1405 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
1406 failed++
1407 }
1408 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
1409 lineLen = len(line)
1410 os.Stdout.WriteString(line)
1411 }
1412}
1413
1414func main() {
1415 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 -04001416 var flagNumWorkers *int = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
David Benjamin884fdf12014-08-02 15:28:23 -04001417 var flagBuildDir *string = flag.String("build-dir", "../../../build", "The build directory to run the shim from.")
Adam Langley95c29f32014-06-20 12:00:00 -07001418
1419 flag.Parse()
1420
1421 addCipherSuiteTests()
1422 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -07001423 addCBCPaddingTests()
Kenny Root7fdeaf12014-08-05 15:23:37 -07001424 addCBCSplittingTests()
David Benjamin636293b2014-07-08 17:59:18 -04001425 addClientAuthTests()
David Benjamin7e2e6cf2014-08-07 17:44:24 -04001426 addVersionNegotiationTests()
David Benjamin5c24a1d2014-08-31 00:59:27 -04001427 addD5BugTests()
David Benjamin43ec06f2014-08-05 02:28:57 -04001428 for _, async := range []bool{false, true} {
1429 for _, splitHandshake := range []bool{false, true} {
David Benjamin6fd297b2014-08-11 18:43:38 -04001430 for _, protocol := range []protocol{tls, dtls} {
1431 addStateMachineCoverageTests(async, splitHandshake, protocol)
1432 }
David Benjamin43ec06f2014-08-05 02:28:57 -04001433 }
1434 }
Adam Langley95c29f32014-06-20 12:00:00 -07001435
1436 var wg sync.WaitGroup
1437
David Benjamin2bc8e6f2014-08-02 15:22:37 -04001438 numWorkers := *flagNumWorkers
Adam Langley95c29f32014-06-20 12:00:00 -07001439
1440 statusChan := make(chan statusMsg, numWorkers)
1441 testChan := make(chan *testCase, numWorkers)
1442 doneChan := make(chan struct{})
1443
David Benjamin025b3d32014-07-01 19:53:04 -04001444 go statusPrinter(doneChan, statusChan, len(testCases))
Adam Langley95c29f32014-06-20 12:00:00 -07001445
1446 for i := 0; i < numWorkers; i++ {
1447 wg.Add(1)
David Benjamin884fdf12014-08-02 15:28:23 -04001448 go worker(statusChan, testChan, *flagBuildDir, &wg)
Adam Langley95c29f32014-06-20 12:00:00 -07001449 }
1450
David Benjamin025b3d32014-07-01 19:53:04 -04001451 for i := range testCases {
1452 if len(*flagTest) == 0 || *flagTest == testCases[i].name {
1453 testChan <- &testCases[i]
Adam Langley95c29f32014-06-20 12:00:00 -07001454 }
1455 }
1456
1457 close(testChan)
1458 wg.Wait()
1459 close(statusChan)
1460 <-doneChan
1461
1462 fmt.Printf("\n")
1463}