Source file
src/crypto/tls/bogo_shim_test.go
1
2
3
4
5 package tls
6
7 import (
8 "bytes"
9 "crypto/internal/cryptotest"
10 "crypto/x509"
11 "encoding/base64"
12 "encoding/json"
13 "encoding/pem"
14 "flag"
15 "fmt"
16 "internal/byteorder"
17 "internal/testenv"
18 "io"
19 "log"
20 "net"
21 "os"
22 "path/filepath"
23 "runtime"
24 "slices"
25 "strconv"
26 "strings"
27 "testing"
28
29 "golang.org/x/crypto/cryptobyte"
30 )
31
32 var (
33 port = flag.String("port", "", "")
34 server = flag.Bool("server", false, "")
35
36 isHandshakerSupported = flag.Bool("is-handshaker-supported", false, "")
37
38 keyfile = flag.String("key-file", "", "")
39 certfile = flag.String("cert-file", "", "")
40 ocspResponse = flagBase64("ocsp-response", "")
41 signingPrefs = flagIntSlice("signing-prefs", "")
42
43 trustCert = flag.String("trust-cert", "", "")
44
45 minVersion = flag.Int("min-version", VersionSSL30, "")
46 maxVersion = flag.Int("max-version", VersionTLS13, "")
47 expectVersion = flag.Int("expect-version", 0, "")
48
49 noTLS1 = flag.Bool("no-tls1", false, "")
50 noTLS11 = flag.Bool("no-tls11", false, "")
51 noTLS12 = flag.Bool("no-tls12", false, "")
52 noTLS13 = flag.Bool("no-tls13", false, "")
53
54 requireAnyClientCertificate = flag.Bool("require-any-client-certificate", false, "")
55
56 shimWritesFirst = flag.Bool("shim-writes-first", false, "")
57
58 resumeCount = flag.Int("resume-count", 0, "")
59
60 curves = flagIntSlice("curves", "")
61 expectedCurve = flag.String("expect-curve-id", "", "")
62
63 verifyPrefs = flagIntSlice("verify-prefs", "")
64 expectedSigAlg = flag.String("expect-peer-signature-algorithm", "", "")
65 expectedPeerSigAlg = flagIntSlice("expect-peer-verify-pref", "")
66
67 shimID = flag.Uint64("shim-id", 0, "")
68 _ = flag.Bool("ipv6", false, "")
69
70 echConfigList = flagBase64("ech-config-list", "")
71 expectECHAccepted = flag.Bool("expect-ech-accept", false, "")
72 expectHRR = flag.Bool("expect-hrr", false, "")
73 expectNoHRR = flag.Bool("expect-no-hrr", false, "")
74 expectedECHRetryConfigs = flag.String("expect-ech-retry-configs", "", "")
75 expectNoECHRetryConfigs = flag.Bool("expect-no-ech-retry-configs", false, "")
76 onInitialExpectECHAccepted = flag.Bool("on-initial-expect-ech-accept", false, "")
77 _ = flag.Bool("expect-no-ech-name-override", false, "")
78 _ = flag.String("expect-ech-name-override", "", "")
79 _ = flag.Bool("reverify-on-resume", false, "")
80 onResumeECHConfigList = flagBase64("on-resume-ech-config-list", "")
81 _ = flag.Bool("on-resume-expect-reject-early-data", false, "")
82 onResumeExpectECHAccepted = flag.Bool("on-resume-expect-ech-accept", false, "")
83 _ = flag.Bool("on-resume-expect-no-ech-name-override", false, "")
84 expectedServerName = flag.String("expect-server-name", "", "")
85 echServerConfig = flagStringSlice("ech-server-config", "")
86 echServerKey = flagStringSlice("ech-server-key", "")
87 echServerRetryConfig = flagStringSlice("ech-is-retry-config", "")
88
89 expectSessionMiss = flag.Bool("expect-session-miss", false, "")
90
91 _ = flag.Bool("enable-early-data", false, "")
92 _ = flag.Bool("on-resume-expect-accept-early-data", false, "")
93 _ = flag.Bool("expect-ticket-supports-early-data", false, "")
94 _ = flag.Bool("on-resume-shim-writes-first", false, "")
95
96 advertiseALPN = flag.String("advertise-alpn", "", "")
97 expectALPN = flag.String("expect-alpn", "", "")
98 rejectALPN = flag.Bool("reject-alpn", false, "")
99 declineALPN = flag.Bool("decline-alpn", false, "")
100 expectAdvertisedALPN = flag.String("expect-advertised-alpn", "", "")
101 selectALPN = flag.String("select-alpn", "", "")
102
103 hostName = flag.String("host-name", "", "")
104
105 verifyPeer = flag.Bool("verify-peer", false, "")
106 _ = flag.Bool("use-custom-verify-callback", false, "")
107
108 waitForDebugger = flag.Bool("wait-for-debugger", false, "")
109 )
110
111 type stringSlice []string
112
113 func flagStringSlice(name, usage string) *stringSlice {
114 f := new(stringSlice)
115 flag.Var(f, name, usage)
116 return f
117 }
118
119 func (saf *stringSlice) String() string {
120 return strings.Join(*saf, ",")
121 }
122
123 func (saf *stringSlice) Set(s string) error {
124 *saf = append(*saf, s)
125 return nil
126 }
127
128 type intSlice []int64
129
130 func flagIntSlice(name, usage string) *intSlice {
131 f := new(intSlice)
132 flag.Var(f, name, usage)
133 return f
134 }
135
136 func (sf *intSlice) String() string {
137 return strings.Join(strings.Split(fmt.Sprint(*sf), " "), ",")
138 }
139
140 func (sf *intSlice) Set(s string) error {
141 i, err := strconv.ParseInt(s, 10, 64)
142 if err != nil {
143 return err
144 }
145 *sf = append(*sf, i)
146 return nil
147 }
148
149 type base64Flag []byte
150
151 func flagBase64(name, usage string) *base64Flag {
152 f := new(base64Flag)
153 flag.Var(f, name, usage)
154 return f
155 }
156
157 func (f *base64Flag) String() string {
158 return base64.StdEncoding.EncodeToString(*f)
159 }
160
161 func (f *base64Flag) Set(s string) error {
162 if *f != nil {
163 return fmt.Errorf("multiple base64 values not supported")
164 }
165 b, err := base64.StdEncoding.DecodeString(s)
166 if err != nil {
167 return err
168 }
169 *f = b
170 return nil
171 }
172
173 func bogoShim() {
174 if *isHandshakerSupported {
175 fmt.Println("No")
176 return
177 }
178
179 fmt.Printf("BoGo shim flags: %q", os.Args[1:])
180
181
182 var ciphersuites []uint16
183 for _, s := range append(CipherSuites(), InsecureCipherSuites()...) {
184 ciphersuites = append(ciphersuites, s.ID)
185 }
186
187 cfg := &Config{
188 ServerName: "test",
189
190 MinVersion: uint16(*minVersion),
191 MaxVersion: uint16(*maxVersion),
192
193 ClientSessionCache: NewLRUClientSessionCache(0),
194
195 CipherSuites: ciphersuites,
196
197 GetConfigForClient: func(chi *ClientHelloInfo) (*Config, error) {
198
199 if *expectAdvertisedALPN != "" {
200
201 s := cryptobyte.String(*expectAdvertisedALPN)
202
203 var expectedALPNs []string
204
205 for !s.Empty() {
206 var alpn cryptobyte.String
207 if !s.ReadUint8LengthPrefixed(&alpn) {
208 return nil, fmt.Errorf("unexpected error while parsing arguments for -expect-advertised-alpn")
209 }
210 expectedALPNs = append(expectedALPNs, string(alpn))
211 }
212
213 if !slices.Equal(chi.SupportedProtos, expectedALPNs) {
214 return nil, fmt.Errorf("unexpected ALPN: got %q, want %q", chi.SupportedProtos, expectedALPNs)
215 }
216 }
217 return nil, nil
218 },
219 }
220
221 if *noTLS1 {
222 cfg.MinVersion = VersionTLS11
223 if *noTLS11 {
224 cfg.MinVersion = VersionTLS12
225 if *noTLS12 {
226 cfg.MinVersion = VersionTLS13
227 if *noTLS13 {
228 log.Fatalf("no supported versions enabled")
229 }
230 }
231 }
232 } else if *noTLS13 {
233 cfg.MaxVersion = VersionTLS12
234 if *noTLS12 {
235 cfg.MaxVersion = VersionTLS11
236 if *noTLS11 {
237 cfg.MaxVersion = VersionTLS10
238 if *noTLS1 {
239 log.Fatalf("no supported versions enabled")
240 }
241 }
242 }
243 }
244
245 if *advertiseALPN != "" {
246 alpns := *advertiseALPN
247 for len(alpns) > 0 {
248 alpnLen := int(alpns[0])
249 cfg.NextProtos = append(cfg.NextProtos, alpns[1:1+alpnLen])
250 alpns = alpns[alpnLen+1:]
251 }
252 }
253
254 if *rejectALPN {
255 cfg.NextProtos = []string{"unnegotiableprotocol"}
256 }
257
258 if *declineALPN {
259 cfg.NextProtos = []string{}
260 }
261 if *selectALPN != "" {
262 cfg.NextProtos = []string{*selectALPN}
263 }
264
265 if *hostName != "" {
266 cfg.ServerName = *hostName
267 }
268
269 if *keyfile != "" || *certfile != "" {
270 pair, err := LoadX509KeyPair(*certfile, *keyfile)
271 if err != nil {
272 log.Fatalf("load key-file err: %s", err)
273 }
274 for _, id := range *signingPrefs {
275 pair.SupportedSignatureAlgorithms = append(pair.SupportedSignatureAlgorithms, SignatureScheme(id))
276 }
277 pair.OCSPStaple = *ocspResponse
278
279
280
281 cfg.GetCertificate = func(chi *ClientHelloInfo) (*Certificate, error) {
282 if *expectedPeerSigAlg != nil {
283 if len(chi.SignatureSchemes) != len(*expectedPeerSigAlg) {
284 return nil, fmt.Errorf("unexpected signature algorithms: got %s, want %v", chi.SignatureSchemes, *expectedPeerSigAlg)
285 }
286 for i := range *expectedPeerSigAlg {
287 if chi.SignatureSchemes[i] != SignatureScheme((*expectedPeerSigAlg)[i]) {
288 return nil, fmt.Errorf("unexpected signature algorithms: got %s, want %v", chi.SignatureSchemes, *expectedPeerSigAlg)
289 }
290 }
291 }
292 return &pair, nil
293 }
294 cfg.GetClientCertificate = func(cri *CertificateRequestInfo) (*Certificate, error) {
295 if *expectedPeerSigAlg != nil {
296 if len(cri.SignatureSchemes) != len(*expectedPeerSigAlg) {
297 return nil, fmt.Errorf("unexpected signature algorithms: got %s, want %v", cri.SignatureSchemes, *expectedPeerSigAlg)
298 }
299 for i := range *expectedPeerSigAlg {
300 if cri.SignatureSchemes[i] != SignatureScheme((*expectedPeerSigAlg)[i]) {
301 return nil, fmt.Errorf("unexpected signature algorithms: got %s, want %v", cri.SignatureSchemes, *expectedPeerSigAlg)
302 }
303 }
304 }
305 return &pair, nil
306 }
307 }
308 if *trustCert != "" {
309 pool := x509.NewCertPool()
310 certFile, err := os.ReadFile(*trustCert)
311 if err != nil {
312 log.Fatalf("load trust-cert err: %s", err)
313 }
314 block, _ := pem.Decode(certFile)
315 cert, err := x509.ParseCertificate(block.Bytes)
316 if err != nil {
317 log.Fatalf("parse trust-cert err: %s", err)
318 }
319 pool.AddCert(cert)
320 cfg.RootCAs = pool
321 }
322
323 if *requireAnyClientCertificate {
324 cfg.ClientAuth = RequireAnyClientCert
325 }
326 if *verifyPeer {
327 cfg.ClientAuth = VerifyClientCertIfGiven
328 }
329
330 if *echConfigList != nil {
331 cfg.EncryptedClientHelloConfigList = *echConfigList
332 cfg.MinVersion = VersionTLS13
333 }
334
335 if *curves != nil {
336 for _, id := range *curves {
337 cfg.CurvePreferences = append(cfg.CurvePreferences, CurveID(id))
338 }
339 }
340
341 if *verifyPrefs != nil {
342 for _, id := range *verifyPrefs {
343 testingOnlySupportedSignatureAlgorithms = append(testingOnlySupportedSignatureAlgorithms, SignatureScheme(id))
344 }
345 }
346
347 if *echServerConfig != nil {
348 if len(*echServerConfig) != len(*echServerKey) || len(*echServerConfig) != len(*echServerRetryConfig) {
349 log.Fatal("-ech-server-config, -ech-server-key, and -ech-is-retry-config mismatch")
350 }
351
352 for i, c := range *echServerConfig {
353 configBytes, err := base64.StdEncoding.DecodeString(c)
354 if err != nil {
355 log.Fatalf("parse ech-server-config err: %s", err)
356 }
357 privBytes, err := base64.StdEncoding.DecodeString((*echServerKey)[i])
358 if err != nil {
359 log.Fatalf("parse ech-server-key err: %s", err)
360 }
361
362 cfg.EncryptedClientHelloKeys = append(cfg.EncryptedClientHelloKeys, EncryptedClientHelloKey{
363 Config: configBytes,
364 PrivateKey: privBytes,
365 SendAsRetry: (*echServerRetryConfig)[i] == "1",
366 })
367 }
368 }
369
370 for i := 0; i < *resumeCount+1; i++ {
371 if i > 0 && *onResumeECHConfigList != nil {
372 cfg.EncryptedClientHelloConfigList = *onResumeECHConfigList
373 }
374
375 conn, err := net.Dial("tcp", net.JoinHostPort("localhost", *port))
376 if err != nil {
377 log.Fatalf("dial err: %s", err)
378 }
379 defer conn.Close()
380
381
382 shimIDBytes := make([]byte, 8)
383 byteorder.LEPutUint64(shimIDBytes, *shimID)
384 if _, err := conn.Write(shimIDBytes); err != nil {
385 log.Fatalf("failed to write shim id: %s", err)
386 }
387
388 var tlsConn *Conn
389 if *server {
390 tlsConn = Server(conn, cfg)
391 } else {
392 tlsConn = Client(conn, cfg)
393 }
394
395 if i == 0 && *shimWritesFirst {
396 if _, err := tlsConn.Write([]byte("hello")); err != nil {
397 log.Fatalf("write err: %s", err)
398 }
399 }
400
401
402
403 if *waitForDebugger {
404 pauseProcess()
405 }
406
407 for {
408 buf := make([]byte, 500)
409 var n int
410 n, err = tlsConn.Read(buf)
411 if err != nil {
412 break
413 }
414 buf = buf[:n]
415 for i := range buf {
416 buf[i] ^= 0xff
417 }
418 if _, err = tlsConn.Write(buf); err != nil {
419 break
420 }
421 }
422 if err != io.EOF {
423
424
425
426
427 orderlyShutdown(tlsConn)
428
429 retryErr, ok := err.(*ECHRejectionError)
430 if !ok {
431 log.Fatal(err)
432 }
433 if *expectNoECHRetryConfigs && len(retryErr.RetryConfigList) > 0 {
434 log.Fatalf("expected no ECH retry configs, got some")
435 }
436 if *expectedECHRetryConfigs != "" {
437 expectedRetryConfigs, err := base64.StdEncoding.DecodeString(*expectedECHRetryConfigs)
438 if err != nil {
439 log.Fatalf("failed to decode expected retry configs: %s", err)
440 }
441 if !bytes.Equal(retryErr.RetryConfigList, expectedRetryConfigs) {
442 log.Fatalf("unexpected retry list returned: got %x, want %x", retryErr.RetryConfigList, expectedRetryConfigs)
443 }
444 }
445 log.Fatalf("conn error: %s", err)
446 }
447
448 cs := tlsConn.ConnectionState()
449 if cs.HandshakeComplete {
450 if *expectALPN != "" && cs.NegotiatedProtocol != *expectALPN {
451 log.Fatalf("unexpected protocol negotiated: want %q, got %q", *expectALPN, cs.NegotiatedProtocol)
452 }
453
454 if *selectALPN != "" && cs.NegotiatedProtocol != *selectALPN {
455 log.Fatalf("unexpected protocol negotiated: want %q, got %q", *selectALPN, cs.NegotiatedProtocol)
456 }
457
458 if *expectVersion != 0 && cs.Version != uint16(*expectVersion) {
459 log.Fatalf("expected ssl version %q, got %q", uint16(*expectVersion), cs.Version)
460 }
461 if *declineALPN && cs.NegotiatedProtocol != "" {
462 log.Fatal("unexpected ALPN protocol")
463 }
464 if *expectECHAccepted && !cs.ECHAccepted {
465 log.Fatal("expected ECH to be accepted, but connection state shows it was not")
466 } else if i == 0 && *onInitialExpectECHAccepted && !cs.ECHAccepted {
467 log.Fatal("expected ECH to be accepted, but connection state shows it was not")
468 } else if i > 0 && *onResumeExpectECHAccepted && !cs.ECHAccepted {
469 log.Fatal("expected ECH to be accepted on resumption, but connection state shows it was not")
470 } else if i == 0 && !*expectECHAccepted && cs.ECHAccepted {
471 log.Fatal("did not expect ECH, but it was accepted")
472 }
473
474 if *expectHRR && !cs.testingOnlyDidHRR {
475 log.Fatal("expected HRR but did not do it")
476 }
477
478 if *expectNoHRR && cs.testingOnlyDidHRR {
479 log.Fatal("expected no HRR but did do it")
480 }
481
482 if *expectSessionMiss && cs.DidResume {
483 log.Fatal("unexpected session resumption")
484 }
485
486 if *expectedServerName != "" && cs.ServerName != *expectedServerName {
487 log.Fatalf("unexpected server name: got %q, want %q", cs.ServerName, *expectedServerName)
488 }
489 }
490
491 if *expectedCurve != "" {
492 expectedCurveID, err := strconv.Atoi(*expectedCurve)
493 if err != nil {
494 log.Fatalf("failed to parse -expect-curve-id: %s", err)
495 }
496 if cs.CurveID != CurveID(expectedCurveID) {
497 log.Fatalf("unexpected curve id: want %d, got %d", expectedCurveID, tlsConn.curveID)
498 }
499 }
500
501
502 if *expectedSigAlg != "" && !cs.DidResume {
503 expectedSigAlgID, err := strconv.Atoi(*expectedSigAlg)
504 if err != nil {
505 log.Fatalf("failed to parse -expect-peer-signature-algorithm: %s", err)
506 }
507 if cs.testingOnlyPeerSignatureAlgorithm != SignatureScheme(expectedSigAlgID) {
508 log.Fatalf("unexpected peer signature algorithm: want %s, got %s", SignatureScheme(expectedSigAlgID), cs.testingOnlyPeerSignatureAlgorithm)
509 }
510 }
511 }
512 }
513
514
515
516
517
518
519 func orderlyShutdown(tlsConn *Conn) {
520
521 tlsConn.flush()
522
523 netConn := tlsConn.NetConn()
524 tcpConn := netConn.(*net.TCPConn)
525 tcpConn.CloseWrite()
526
527
528 buf := make([]byte, maxPlaintext)
529 for {
530 n, err := tcpConn.Read(buf)
531 if n == 0 || err != nil {
532 break
533 }
534 }
535
536 tcpConn.CloseRead()
537 }
538
539 func TestBogoSuite(t *testing.T) {
540 if testing.Short() {
541 t.Skip("skipping in short mode")
542 }
543 if testenv.Builder() != "" && runtime.GOOS == "windows" {
544 t.Skip("#66913: windows network connections are flakey on builders")
545 }
546 skipFIPS(t)
547
548
549
550
551
552 if _, err := os.Stat("bogo_config.json"); err != nil {
553 t.Fatal(err)
554 }
555
556 var bogoDir string
557 if *bogoLocalDir != "" {
558 bogoDir = *bogoLocalDir
559 } else {
560 const boringsslModVer = "v0.0.0-20250620172916-f51d8b099832"
561 bogoDir = cryptotest.FetchModule(t, "boringssl.googlesource.com/boringssl.git", boringsslModVer)
562 }
563
564 cwd, err := os.Getwd()
565 if err != nil {
566 t.Fatal(err)
567 }
568
569 resultsFile := filepath.Join(t.TempDir(), "results.json")
570
571 args := []string{
572 "test",
573 ".",
574 fmt.Sprintf("-shim-config=%s", filepath.Join(cwd, "bogo_config.json")),
575 fmt.Sprintf("-shim-path=%s", os.Args[0]),
576 "-shim-extra-flags=-bogo-mode",
577 "-allow-unimplemented",
578 "-loose-errors",
579 fmt.Sprintf("-json-output=%s", resultsFile),
580 }
581 if *bogoFilter != "" {
582 args = append(args, fmt.Sprintf("-test=%s", *bogoFilter))
583 }
584
585 cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
586 out := &strings.Builder{}
587 cmd.Stderr = out
588 cmd.Dir = filepath.Join(bogoDir, "ssl/test/runner")
589 err = cmd.Run()
590
591
592
593
594
595
596 resultsJSON, jsonErr := os.ReadFile(resultsFile)
597 if jsonErr != nil {
598 if err != nil {
599 t.Fatalf("bogo failed: %s\n%s", err, out)
600 }
601 t.Fatalf("failed to read results JSON file: %s", jsonErr)
602 }
603
604 var results bogoResults
605 if err := json.Unmarshal(resultsJSON, &results); err != nil {
606 t.Fatalf("failed to parse results JSON: %s", err)
607 }
608
609
610
611
612 assertResults := map[string]string{
613 "CurveTest-Client-MLKEM-TLS13": "PASS",
614 "CurveTest-Server-MLKEM-TLS13": "PASS",
615
616
617
618 "ClientAuth-Enforced": "PASS",
619 "ServerAuth-Enforced": "PASS",
620 "ClientAuth-Enforced-TLS13": "PASS",
621 "ServerAuth-Enforced-TLS13": "PASS",
622 "VerifyPreferences-Advertised": "PASS",
623 "VerifyPreferences-Enforced": "PASS",
624 "Client-TLS12-NoSign-RSA_PKCS1_MD5_SHA1": "PASS",
625 "Server-TLS12-NoSign-RSA_PKCS1_MD5_SHA1": "PASS",
626 "Client-TLS13-NoSign-RSA_PKCS1_MD5_SHA1": "PASS",
627 "Server-TLS13-NoSign-RSA_PKCS1_MD5_SHA1": "PASS",
628 }
629
630 for name, result := range results.Tests {
631
632 t.Run(name, func(t *testing.T) {
633 if result.Actual == "FAIL" && result.IsUnexpected {
634 t.Fail()
635 }
636 if result.Error != "" {
637 t.Log(result.Error)
638 }
639 if exp, ok := assertResults[name]; ok && exp != result.Actual {
640 t.Errorf("unexpected result: got %s, want %s", result.Actual, exp)
641 }
642 delete(assertResults, name)
643 if result.Actual == "SKIP" {
644 t.SkipNow()
645 }
646 })
647 }
648 if *bogoFilter == "" {
649
650 for name, expectedResult := range assertResults {
651 t.Run(name, func(t *testing.T) {
652 t.Fatalf("expected test to run with result %s, but it was not present in the test results", expectedResult)
653 })
654 }
655 }
656 }
657
658
659 type bogoResults struct {
660 Version int `json:"version"`
661 Interrupted bool `json:"interrupted"`
662 PathDelimiter string `json:"path_delimiter"`
663 SecondsSinceEpoch float64 `json:"seconds_since_epoch"`
664 NumFailuresByType map[string]int `json:"num_failures_by_type"`
665 Tests map[string]struct {
666 Actual string `json:"actual"`
667 Expected string `json:"expected"`
668 IsUnexpected bool `json:"is_unexpected"`
669 Error string `json:"error,omitempty"`
670 } `json:"tests"`
671 }
672
View as plain text