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
152 t.Log("running bettertls export-tests command")
153 output, err := cmd.CombinedOutput()
154 if err != nil {
155 t.Fatalf(
156 "failed to run bettertls export-tests: %v\nOutput: %s",
157 err, output)
158 }
159
160 jsonData, err := os.ReadFile(testsJSONPath)
161 if err != nil {
162 t.Fatalf("failed to read exported tests.json: %v", err)
163 }
164
165 t.Logf("successfully loaded tests.json at %s", testsJSONPath)
166
167 var data betterTLS
168 if err := json.Unmarshal(jsonData, &data); err != nil {
169 t.Fatalf("failed to unmarshal JSON data: %v", err)
170 }
171
172 t.Logf("testing betterTLS revision: %s", data.Revision)
173 t.Logf("number of test suites: %d", len(data.Suites))
174
175 rootDER, err := data.RootCert()
176 if err != nil {
177 t.Fatalf("failed to decode trust root: %v", err)
178 }
179
180 rootCert, err := ParseCertificate(rootDER)
181 if err != nil {
182 t.Fatalf("failed to parse trust root certificate: %v", err)
183 }
184
185 roots := NewCertPool()
186 roots.AddCert(rootCert)
187
188 return data, roots
189 }
190
191 type betterTLS struct {
192 Revision string `json:"betterTlsRevision"`
193 Root string `json:"trustRoot"`
194 Suites map[string]betterTLSSuite `json:"suites"`
195 }
196
197 func (b *betterTLS) RootCert() ([]byte, error) {
198 return base64.StdEncoding.DecodeString(b.Root)
199 }
200
201 type betterTLSSuite struct {
202 TestCases []betterTLSTest `json:"testCases"`
203 }
204
205 type betterTLSTest struct {
206 ID uint32 `json:"id"`
207 Certificates []string `json:"certificates"`
208 Hostname string `json:"hostname"`
209 Expected expectedResult `json:"expected"`
210 }
211
212 func (test *betterTLSTest) Certs() ([][]byte, error) {
213 certs := make([][]byte, len(test.Certificates))
214 for i, cert := range test.Certificates {
215 decoded, err := base64.StdEncoding.DecodeString(cert)
216 if err != nil {
217 return nil, err
218 }
219 certs[i] = decoded
220 }
221 return certs, nil
222 }
223
224 type expectedResult string
225
226 const (
227 expectedAccept expectedResult = "ACCEPT"
228 expectedReject expectedResult = "REJECT"
229 )
230
View as plain text