Source file
src/crypto/x509/bettertls_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package x509
16
17 import (
18 "crypto/internal/cryptotest"
19 "encoding/base64"
20 "encoding/json"
21 "internal/testenv"
22 "os"
23 "path/filepath"
24 "testing"
25 )
26
27
28
29
30
31
32
33
34
35
36
37
38
39 func TestBetterTLS(t *testing.T) {
40 testenv.SkipIfShortAndSlow(t)
41
42 data, roots := betterTLSTestData(t)
43
44 for _, suite := range []string{"pathbuilding", "nameconstraints"} {
45 t.Run(suite, func(t *testing.T) {
46 runTestSuite(t, suite, &data, roots)
47 })
48 }
49 }
50
51 func runTestSuite(t *testing.T, suiteName string, data *betterTLS, roots *CertPool) {
52 suite, exists := data.Suites[suiteName]
53 if !exists {
54 t.Fatalf("missing %s suite", suiteName)
55 }
56
57 t.Logf(
58 "running %s test suite with %d test cases",
59 suiteName, len(suite.TestCases))
60
61 for _, tc := range suite.TestCases {
62 t.Logf("testing %s test case %d", suiteName, tc.ID)
63
64 certsDER, err := tc.Certs()
65 if err != nil {
66 t.Fatalf(
67 "failed to decode certificates for test case %d: %v",
68 tc.ID, err)
69 }
70
71 if len(certsDER) == 0 {
72 t.Fatalf("test case %d has no certificates", tc.ID)
73 }
74
75 eeCert, err := ParseCertificate(certsDER[0])
76 if err != nil {
77
78
79
80
81
82
83
84 if suiteName == "nameconstraints" && tc.Expected == expectedReject {
85 t.Logf(
86 "skipping expected reject test case %d "+
87 "- end entity certificate parse error: %v",
88 tc.ID, err)
89 continue
90 }
91 t.Fatalf(
92 "failed to parse end entity certificate for test case %d: %v",
93 tc.ID, err)
94 }
95
96 intermediates := NewCertPool()
97 for i, certDER := range certsDER[1:] {
98 cert, err := ParseCertificate(certDER)
99 if err != nil {
100 t.Fatalf(
101 "failed to parse intermediate certificate %d for test case %d: %v",
102 i+1, tc.ID, err)
103 }
104 intermediates.AddCert(cert)
105 }
106
107 _, err = eeCert.Verify(VerifyOptions{
108 Roots: roots,
109 Intermediates: intermediates,
110 DNSName: tc.Hostname,
111 KeyUsages: []ExtKeyUsage{ExtKeyUsageServerAuth},
112 })
113
114 switch tc.Expected {
115 case expectedAccept:
116 if err != nil {
117 t.Errorf(
118 "test case %d failed: expected success, got error: %v",
119 tc.ID, err)
120 }
121 case expectedReject:
122 if err == nil {
123 t.Errorf(
124 "test case %d failed: expected failure, but verification succeeded",
125 tc.ID)
126 }
127 default:
128 t.Fatalf(
129 "test case %d failed: unknown expected result: %s",
130 tc.ID, tc.Expected)
131 }
132 }
133 }
134
135 func betterTLSTestData(t *testing.T) (betterTLS, *CertPool) {
136 const (
137 bettertlsModule = "github.com/Netflix/bettertls"
138 bettertlsVersion = "v0.0.0-20250909192348-e1e99e353074"
139 )
140
141 bettertlsDir := cryptotest.FetchModule(t, bettertlsModule, bettertlsVersion)
142
143 tempDir := t.TempDir()
144 testsJSONPath := filepath.Join(tempDir, "tests.json")
145
146 cmd := testenv.Command(t, testenv.GoToolPath(t),
147 "run", "./test-suites/cmd/bettertls",
148 "export-tests",
149 "--out", testsJSONPath)
150 cmd.Dir = bettertlsDir
151 t.Log("running bettertls export-tests command")
152 if out, err := cmd.CombinedOutput(); err != nil {
153 t.Fatalf("failed to run bettertls export-tests: %v\n%s", err, out)
154 }
155
156 jsonData, err := os.ReadFile(testsJSONPath)
157 if err != nil {
158 t.Fatalf("failed to read exported tests.json: %v", err)
159 }
160
161 t.Logf("successfully loaded tests.json at %s", testsJSONPath)
162
163 var data betterTLS
164 if err := json.Unmarshal(jsonData, &data); err != nil {
165 t.Fatalf("failed to unmarshal JSON data: %v", err)
166 }
167
168 t.Logf("testing betterTLS revision: %s", data.Revision)
169 t.Logf("number of test suites: %d", len(data.Suites))
170
171 rootDER, err := data.RootCert()
172 if err != nil {
173 t.Fatalf("failed to decode trust root: %v", err)
174 }
175
176 rootCert, err := ParseCertificate(rootDER)
177 if err != nil {
178 t.Fatalf("failed to parse trust root certificate: %v", err)
179 }
180
181 roots := NewCertPool()
182 roots.AddCert(rootCert)
183
184 return data, roots
185 }
186
187 type betterTLS struct {
188 Revision string `json:"betterTlsRevision"`
189 Root string `json:"trustRoot"`
190 Suites map[string]betterTLSSuite `json:"suites"`
191 }
192
193 func (b *betterTLS) RootCert() ([]byte, error) {
194 return base64.StdEncoding.DecodeString(b.Root)
195 }
196
197 type betterTLSSuite struct {
198 TestCases []betterTLSTest `json:"testCases"`
199 }
200
201 type betterTLSTest struct {
202 ID uint32 `json:"id"`
203 Certificates []string `json:"certificates"`
204 Hostname string `json:"hostname"`
205 Expected expectedResult `json:"expected"`
206 }
207
208 func (test *betterTLSTest) Certs() ([][]byte, error) {
209 certs := make([][]byte, len(test.Certificates))
210 for i, cert := range test.Certificates {
211 decoded, err := base64.StdEncoding.DecodeString(cert)
212 if err != nil {
213 return nil, err
214 }
215 certs[i] = decoded
216 }
217 return certs, nil
218 }
219
220 type expectedResult string
221
222 const (
223 expectedAccept expectedResult = "ACCEPT"
224 expectedReject expectedResult = "REJECT"
225 )
226
View as plain text