blob: 1ec3795f8e063fe1fb7a103d7155e3d8fb552a68 [file] [log] [blame]
Adam Langley95c29f32014-06-20 12:00:00 -07001package main
2
3import (
4 "bytes"
5 "flag"
6 "fmt"
7 "io"
8 "net"
9 "os"
10 "os/exec"
11 "strings"
12 "sync"
13 "syscall"
14)
15
16var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
17
18var rsaCertificate, ecdsaCertificate Certificate
19
20func initCertificates() {
21 var err error
22 rsaCertificate, err = LoadX509KeyPair("cert.pem", "key.pem")
23 if err != nil {
24 panic(err)
25 }
26
27 ecdsaCertificate, err = LoadX509KeyPair("ecdsa_cert.pem", "ecdsa_key.pem")
28 if err != nil {
29 panic(err)
30 }
31}
32
33var certificateOnce sync.Once
34
35func getRSACertificate() Certificate {
36 certificateOnce.Do(initCertificates)
37 return rsaCertificate
38}
39
40func getECDSACertificate() Certificate {
41 certificateOnce.Do(initCertificates)
42 return ecdsaCertificate
43}
44
45type testCase struct {
46 name string
47 config Config
48 shouldFail bool
49 expectedError string
Adam Langleyac61fa32014-06-23 12:03:11 -070050 // expectedLocalError, if not empty, contains a substring that must be
51 // found in the local error.
52 expectedLocalError string
Adam Langley80842bd2014-06-20 12:00:00 -070053 // messageLen is the length, in bytes, of the test message that will be
54 // sent.
55 messageLen int
Adam Langleyac61fa32014-06-23 12:03:11 -070056 // flag, if not nil, contains a command line flag that will be passed
57 // to the shim program.
58 flag string
Adam Langley95c29f32014-06-20 12:00:00 -070059}
60
61var clientTests = []testCase{
62 {
63 name: "BadRSASignature",
64 config: Config{
65 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
66 Bugs: ProtocolBugs{
67 InvalidSKXSignature: true,
68 },
69 },
70 shouldFail: true,
71 expectedError: ":BAD_SIGNATURE:",
72 },
73 {
74 name: "BadECDSASignature",
75 config: Config{
76 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
77 Bugs: ProtocolBugs{
78 InvalidSKXSignature: true,
79 },
80 Certificates: []Certificate{getECDSACertificate()},
81 },
82 shouldFail: true,
83 expectedError: ":BAD_SIGNATURE:",
84 },
85 {
86 name: "BadECDSACurve",
87 config: Config{
88 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
89 Bugs: ProtocolBugs{
90 InvalidSKXCurve: true,
91 },
92 Certificates: []Certificate{getECDSACertificate()},
93 },
94 shouldFail: true,
95 expectedError: ":WRONG_CURVE:",
96 },
Adam Langleyac61fa32014-06-23 12:03:11 -070097 {
98 name: "FallbackSCSV",
99 config: Config{
100 Bugs: ProtocolBugs{
101 FailIfNotFallbackSCSV: true,
102 },
103 },
104 shouldFail: true,
105 expectedLocalError: "no fallback SCSV found",
106 },
Adam Langley95c29f32014-06-20 12:00:00 -0700107}
108
Adam Langley80842bd2014-06-20 12:00:00 -0700109func doExchange(tlsConn *Conn, messageLen int) error {
Adam Langley95c29f32014-06-20 12:00:00 -0700110 if err := tlsConn.Handshake(); err != nil {
111 return err
112 }
Adam Langley80842bd2014-06-20 12:00:00 -0700113 if messageLen == 0 {
114 messageLen = 32
115 }
116 testMessage := make([]byte, messageLen)
117 for i := range testMessage {
118 testMessage[i] = 0x42
119 }
Adam Langley95c29f32014-06-20 12:00:00 -0700120 tlsConn.Write(testMessage)
121
122 buf := make([]byte, len(testMessage))
123 _, err := io.ReadFull(tlsConn, buf)
124 if err != nil {
125 return err
126 }
127
128 for i, v := range buf {
129 if v != testMessage[i]^0xff {
130 return fmt.Errorf("bad reply contents at byte %d", i)
131 }
132 }
133
134 return nil
135}
136
137func valgrindOf(dbAttach bool, baseArgs ...string) *exec.Cmd {
138 args := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
139 if dbAttach {
140 args = append(args, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
141 }
142 args = append(args, baseArgs...)
143
144 return exec.Command("valgrind", args...)
145}
146
147func gdbOf(baseArgs ...string) *exec.Cmd {
148 args := []string{"-e", "gdb", "--args"}
149 args = append(args, baseArgs...)
150
151 return exec.Command("xterm", args...)
152}
153
154func runTest(test *testCase) error {
155 socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
156 if err != nil {
157 panic(err)
158 }
159
160 syscall.CloseOnExec(socks[0])
161 syscall.CloseOnExec(socks[1])
162 clientEnd := os.NewFile(uintptr(socks[0]), "client end")
163 connFile := os.NewFile(uintptr(socks[1]), "our end")
164 conn, err := net.FileConn(connFile)
165 connFile.Close()
166 if err != nil {
167 panic(err)
168 }
169
170 const shim_path = "../../../build/ssl/test/client_shim"
171 var client *exec.Cmd
172 if *useValgrind {
173 client = valgrindOf(false, shim_path)
174 } else {
175 client = exec.Command(shim_path)
176 }
177 //client := gdbOf(shim_path)
178 client.ExtraFiles = []*os.File{clientEnd}
179 client.Stdin = os.Stdin
180 var stdoutBuf, stderrBuf bytes.Buffer
181 client.Stdout = &stdoutBuf
182 client.Stderr = &stderrBuf
183
184 if err := client.Start(); err != nil {
185 panic(err)
186 }
187 clientEnd.Close()
188
189 config := test.config
190 if len(config.Certificates) == 0 {
191 config.Certificates = []Certificate{getRSACertificate()}
192 }
193
194 tlsConn := Server(conn, &config)
Adam Langley80842bd2014-06-20 12:00:00 -0700195 err = doExchange(tlsConn, test.messageLen)
Adam Langley95c29f32014-06-20 12:00:00 -0700196
197 conn.Close()
198 childErr := client.Wait()
199
200 stdout := string(stdoutBuf.Bytes())
201 stderr := string(stderrBuf.Bytes())
202 failed := err != nil || childErr != nil
203 correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
Adam Langleyac61fa32014-06-23 12:03:11 -0700204 localError := "none"
205 if err != nil {
206 localError = err.Error()
207 }
208 if len(test.expectedLocalError) != 0 {
209 correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError)
210 }
Adam Langley95c29f32014-06-20 12:00:00 -0700211
212 if failed != test.shouldFail || failed && !correctFailure {
Adam Langley95c29f32014-06-20 12:00:00 -0700213 childError := "none"
Adam Langley95c29f32014-06-20 12:00:00 -0700214 if childErr != nil {
215 childError = childErr.Error()
216 }
217
218 var msg string
219 switch {
220 case failed && !test.shouldFail:
221 msg = "unexpected failure"
222 case !failed && test.shouldFail:
223 msg = "unexpected success"
224 case failed && !correctFailure:
Adam Langleyac61fa32014-06-23 12:03:11 -0700225 msg = "bad error (wanted '" + test.expectedError + "' / '" + test.expectedLocalError + "')"
Adam Langley95c29f32014-06-20 12:00:00 -0700226 default:
227 panic("internal error")
228 }
229
230 return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
231 }
232
233 if !*useValgrind && len(stderr) > 0 {
234 println(stderr)
235 }
236
237 return nil
238}
239
240var tlsVersions = []struct {
241 name string
242 version uint16
243}{
244 {"SSL3", VersionSSL30},
245 {"TLS1", VersionTLS10},
246 {"TLS11", VersionTLS11},
247 {"TLS12", VersionTLS12},
248}
249
250var testCipherSuites = []struct {
251 name string
252 id uint16
253}{
254 {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
255 {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
256 {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
257 {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
258 {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
259 {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
260 {"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
261 {"ECDHE-RSA-3DES-SHA", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
262 {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
263 {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
264 {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
265 {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
266 {"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
267 {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
268 {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
269}
270
271func addCipherSuiteTests() {
272 for _, suite := range testCipherSuites {
273 var cert Certificate
274 if strings.Contains(suite.name, "ECDSA") {
275 cert = getECDSACertificate()
276 } else {
277 cert = getRSACertificate()
278 }
279
280 for _, ver := range tlsVersions {
281 if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
282 continue
283 }
284
285 clientTests = append(clientTests, testCase{
286 name: ver.name + "-" + suite.name,
287 config: Config{
288 MinVersion: ver.version,
289 MaxVersion: ver.version,
290 CipherSuites: []uint16{suite.id},
291 Certificates: []Certificate{cert},
292 },
293 })
294 }
295 }
296}
297
298func addBadECDSASignatureTests() {
299 for badR := BadValue(1); badR < NumBadValues; badR++ {
300 for badS := BadValue(1); badS < NumBadValues; badS++ {
301 clientTests = append(clientTests, testCase{
302 name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
303 config: Config{
304 CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
305 Certificates: []Certificate{getECDSACertificate()},
306 Bugs: ProtocolBugs{
307 BadECDSAR: badR,
308 BadECDSAS: badS,
309 },
310 },
311 shouldFail: true,
312 expectedError: "SIGNATURE",
313 })
314 }
315 }
316}
317
Adam Langley80842bd2014-06-20 12:00:00 -0700318func addCBCPaddingTests() {
319 clientTests = append(clientTests, testCase{
320 name: "MaxCBCPadding",
321 config: Config{
322 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
323 Bugs: ProtocolBugs{
324 MaxPadding: true,
325 },
326 },
327 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
328 })
329 clientTests = append(clientTests, testCase{
330 name: "BadCBCPadding",
331 config: Config{
332 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
333 Bugs: ProtocolBugs{
334 PaddingFirstByteBad: true,
335 },
336 },
337 shouldFail: true,
338 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
339 })
340 // OpenSSL previously had an issue where the first byte of padding in
341 // 255 bytes of padding wasn't checked.
342 clientTests = append(clientTests, testCase{
343 name: "BadCBCPadding255",
344 config: Config{
345 CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
346 Bugs: ProtocolBugs{
347 MaxPadding: true,
348 PaddingFirstByteBadIf255: true,
349 },
350 },
351 messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size
352 shouldFail: true,
353 expectedError: "DECRYPTION_FAILED_OR_BAD_RECORD_MAC",
354 })
355}
356
Adam Langley95c29f32014-06-20 12:00:00 -0700357func worker(statusChan chan statusMsg, c chan *testCase, wg *sync.WaitGroup) {
358 defer wg.Done()
359
360 for test := range c {
361 statusChan <- statusMsg{test: test, started: true}
362 err := runTest(test)
363 statusChan <- statusMsg{test: test, err: err}
364 }
365}
366
367type statusMsg struct {
368 test *testCase
369 started bool
370 err error
371}
372
373func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
374 var started, done, failed, lineLen int
375 defer close(doneChan)
376
377 for msg := range statusChan {
378 if msg.started {
379 started++
380 } else {
381 done++
382 }
383
384 fmt.Printf("\x1b[%dD\x1b[K", lineLen)
385
386 if msg.err != nil {
387 fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
388 failed++
389 }
390 line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
391 lineLen = len(line)
392 os.Stdout.WriteString(line)
393 }
394}
395
396func main() {
397 var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests")
398
399 flag.Parse()
400
401 addCipherSuiteTests()
402 addBadECDSASignatureTests()
Adam Langley80842bd2014-06-20 12:00:00 -0700403 addCBCPaddingTests()
Adam Langley95c29f32014-06-20 12:00:00 -0700404
405 var wg sync.WaitGroup
406
407 const numWorkers = 64
408
409 statusChan := make(chan statusMsg, numWorkers)
410 testChan := make(chan *testCase, numWorkers)
411 doneChan := make(chan struct{})
412
413 go statusPrinter(doneChan, statusChan, len(clientTests))
414
415 for i := 0; i < numWorkers; i++ {
416 wg.Add(1)
417 go worker(statusChan, testChan, &wg)
418 }
419
420 for i := range clientTests {
421 if len(*flagTest) == 0 || *flagTest == clientTests[i].name {
422 testChan <- &clientTests[i]
423 }
424 }
425
426 close(testChan)
427 wg.Wait()
428 close(statusChan)
429 <-doneChan
430
431 fmt.Printf("\n")
432}