1// Copyright 2025 The BoringSSL Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package runner 16 17import "golang.org/x/crypto/cryptobyte" 18 19func trustAnchorListFlagValue(ids ...[]byte) string { 20 b := cryptobyte.NewBuilder(nil) 21 for _, id := range ids { 22 addUint8LengthPrefixedBytes(b, id) 23 } 24 return base64FlagValue(b.BytesOrPanic()) 25} 26 27func addTrustAnchorTests() { 28 id1 := []byte{1} 29 id2 := []byte{2, 2} 30 id3 := []byte{3, 3, 3} 31 id4 := []byte{4, 4, 4, 4} 32 33 // Unsolicited trust_anchors extensions should be rejected. 34 testCases = append(testCases, testCase{ 35 name: "TrustAnchors-Unsolicited-Certificate", 36 config: Config{ 37 MinVersion: VersionTLS13, 38 Bugs: ProtocolBugs{ 39 AlwaysMatchTrustAnchorID: true, 40 }, 41 }, 42 shouldFail: true, 43 expectedLocalError: "remote error: unsupported extension", 44 expectedError: ":UNEXPECTED_EXTENSION:", 45 }) 46 testCases = append(testCases, testCase{ 47 name: "TrustAnchors-Unsolicited-EncryptedExtensions", 48 config: Config{ 49 MinVersion: VersionTLS13, 50 AvailableTrustAnchors: [][]byte{id1, id2}, 51 Bugs: ProtocolBugs{ 52 AlwaysSendAvailableTrustAnchors: true, 53 }, 54 }, 55 shouldFail: true, 56 expectedLocalError: "remote error: unsupported extension", 57 expectedError: ":UNEXPECTED_EXTENSION:", 58 }) 59 60 // Test that the client sends trust anchors when configured, and correctly 61 // reports the server's response. 62 testCases = append(testCases, testCase{ 63 name: "TrustAnchors-ClientRequest-Match", 64 config: Config{ 65 MinVersion: VersionTLS13, 66 AvailableTrustAnchors: [][]byte{id1, id2}, 67 Credential: rsaChainCertificate.WithTrustAnchorID(id1), 68 Bugs: ProtocolBugs{ 69 ExpectPeerRequestedTrustAnchors: [][]byte{id1, id3}, 70 }, 71 }, 72 flags: []string{ 73 "-requested-trust-anchors", trustAnchorListFlagValue(id1, id3), 74 "-expect-peer-match-trust-anchor", 75 "-expect-peer-available-trust-anchors", trustAnchorListFlagValue(id1, id2), 76 }, 77 }) 78 // The client should not like it if the server indicates the match with a non-empty 79 // extension. 80 testCases = append(testCases, testCase{ 81 name: "TrustAnchors-ClientRequest-Match-Non-Empty-Extension", 82 config: Config{ 83 MinVersion: VersionTLS13, 84 AvailableTrustAnchors: [][]byte{id1, id2}, 85 Credential: rsaChainCertificate.WithTrustAnchorID(id1), 86 Bugs: ProtocolBugs{ 87 SendNonEmptyTrustAnchorMatch: true, 88 ExpectPeerRequestedTrustAnchors: [][]byte{id1, id3}, 89 }, 90 }, 91 flags: []string{ 92 "-requested-trust-anchors", trustAnchorListFlagValue(id1, id3), 93 }, 94 shouldFail: true, 95 expectedLocalError: "remote error: error decoding message", 96 expectedError: ":ERROR_PARSING_EXTENSION:", 97 }) 98 // The client should not like it if the server indicates the match on the incorrect 99 // certificate in the Certificate message. 100 testCases = append(testCases, testCase{ 101 name: "TrustAnchors-ClientRequest-Match-On-Incorrect-Certificate", 102 config: Config{ 103 MinVersion: VersionTLS13, 104 AvailableTrustAnchors: [][]byte{id1, id2}, 105 Credential: rsaChainCertificate.WithTrustAnchorID(id1), 106 Bugs: ProtocolBugs{ 107 SendTrustAnchorWrongCertificate: true, 108 ExpectPeerRequestedTrustAnchors: [][]byte{id1, id3}, 109 }, 110 }, 111 flags: []string{ 112 "-requested-trust-anchors", trustAnchorListFlagValue(id1, id3), 113 }, 114 shouldFail: true, 115 expectedLocalError: "remote error: unsupported extension", 116 expectedError: ":UNEXPECTED_EXTENSION:", 117 }) 118 testCases = append(testCases, testCase{ 119 name: "TrustAnchors-ClientRequest-NoMatch", 120 config: Config{ 121 MinVersion: VersionTLS13, 122 AvailableTrustAnchors: [][]byte{id1, id2}, 123 Bugs: ProtocolBugs{ 124 ExpectPeerRequestedTrustAnchors: [][]byte{id3}, 125 }, 126 }, 127 flags: []string{ 128 "-requested-trust-anchors", trustAnchorListFlagValue(id3), 129 "-expect-no-peer-match-trust-anchor", 130 "-expect-peer-available-trust-anchors", trustAnchorListFlagValue(id1, id2), 131 }, 132 }) 133 134 // An empty trust anchor ID is a syntax error, so most be rejected in both 135 // ClientHello and EncryptedExtensions. 136 testCases = append(testCases, testCase{ 137 testType: serverTest, 138 name: "TrustAnchors-EmptyID-ClientHello", 139 config: Config{ 140 MinVersion: VersionTLS13, 141 RequestTrustAnchors: [][]byte{{}}, 142 }, 143 shouldFail: true, 144 expectedError: ":DECODE_ERROR:", 145 }) 146 testCases = append(testCases, testCase{ 147 name: "TrustAnchors-EmptyID-EncryptedExtensions", 148 config: Config{ 149 MinVersion: VersionTLS13, 150 AvailableTrustAnchors: [][]byte{{}}, 151 }, 152 flags: []string{"-requested-trust-anchors", trustAnchorListFlagValue(id1)}, 153 shouldFail: true, 154 expectedError: ":DECODE_ERROR:", 155 }) 156 157 // Test the server selection logic, as well as whether it correctly reports 158 // available trust anchors and the match status. (The general selection flow 159 // is covered in addCertificateSelectionTests.) 160 testCases = append(testCases, testCase{ 161 testType: serverTest, 162 name: "TrustAnchors-ServerSelect-Match", 163 config: Config{ 164 MinVersion: VersionTLS13, 165 RequestTrustAnchors: [][]byte{id2}, 166 Bugs: ProtocolBugs{ 167 ExpectPeerAvailableTrustAnchors: [][]byte{id1, id2}, 168 ExpectPeerMatchTrustAnchor: ptrTo(true), 169 }, 170 }, 171 shimCredentials: []*Credential{ 172 rsaCertificate.WithTrustAnchorID(id1), 173 rsaCertificate.WithTrustAnchorID(id2), 174 }, 175 flags: []string{"-expect-selected-credential", "1"}, 176 }) 177 testCases = append(testCases, testCase{ 178 testType: serverTest, 179 name: "TrustAnchors-ServerSelect-None", 180 config: Config{ 181 MinVersion: VersionTLS13, 182 RequestTrustAnchors: [][]byte{id1}, 183 }, 184 shimCredentials: []*Credential{ 185 rsaCertificate.WithTrustAnchorID(id2), 186 rsaCertificate.WithTrustAnchorID(id3), 187 }, 188 shouldFail: true, 189 expectedError: ":NO_MATCHING_ISSUER:", 190 }) 191 testCases = append(testCases, testCase{ 192 testType: serverTest, 193 name: "TrustAnchors-ServerSelect-Fallback", 194 config: Config{ 195 MinVersion: VersionTLS13, 196 RequestTrustAnchors: [][]byte{id1}, 197 Bugs: ProtocolBugs{ 198 ExpectPeerAvailableTrustAnchors: [][]byte{id2, id3}, 199 ExpectPeerMatchTrustAnchor: ptrTo(false), 200 }, 201 }, 202 shimCredentials: []*Credential{ 203 rsaCertificate.WithTrustAnchorID(id2), 204 rsaCertificate.WithTrustAnchorID(id3), 205 &rsaCertificate, 206 }, 207 flags: []string{"-expect-selected-credential", "2"}, 208 }) 209 210 // When the server sends available trust anchors, it should filter the list 211 // by whether the credential would be usable with the connection at all. 212 p256DC := createDelegatedCredential(&rsaCertificate, delegatedCredentialConfig{ 213 dcAlgo: signatureECDSAWithP256AndSHA256, 214 algo: signatureRSAPSSWithSHA256, 215 }) 216 testCases = append(testCases, testCase{ 217 testType: serverTest, 218 name: "TrustAnchors-Server-FilterAvailable", 219 config: Config{ 220 MinVersion: VersionTLS13, 221 RequestTrustAnchors: [][]byte{id1}, 222 VerifySignatureAlgorithms: []signatureAlgorithm{signatureRSAPSSWithSHA256, signatureECDSAWithP256AndSHA256}, 223 Bugs: ProtocolBugs{ 224 ExpectPeerAvailableTrustAnchors: [][]byte{id2}, 225 ExpectPeerMatchTrustAnchor: ptrTo(false), 226 }, 227 }, 228 shimCredentials: []*Credential{ 229 rsaCertificate.WithTrustAnchorID(id2), 230 // Ineligible because of signature algorithms. 231 rsaCertificate.WithTrustAnchorID(id3).WithSignatureAlgorithms(signatureRSAPSSWithSHA384), 232 // Ineligible because of credential type. 233 p256DC.WithTrustAnchorID(id4), 234 &rsaCertificate, 235 }, 236 flags: []string{"-expect-selected-credential", "3"}, 237 }) 238 239 // After filtering, the list of available trust anchors may even be empty. 240 testCases = append(testCases, testCase{ 241 testType: serverTest, 242 name: "TrustAnchors-Server-FilterAvailable-Empty", 243 config: Config{ 244 MinVersion: VersionTLS13, 245 RequestTrustAnchors: [][]byte{id1}, 246 VerifySignatureAlgorithms: []signatureAlgorithm{signatureRSAPSSWithSHA256, signatureECDSAWithP256AndSHA256}, 247 Bugs: ProtocolBugs{ 248 ExpectPeerAvailableTrustAnchors: [][]byte{}, 249 ExpectPeerMatchTrustAnchor: ptrTo(false), 250 }, 251 }, 252 shimCredentials: []*Credential{ 253 // Ineligible because of signature algorithms. 254 rsaCertificate.WithTrustAnchorID(id2).WithSignatureAlgorithms(signatureRSAPSSWithSHA384), 255 // Ineligible because of credential type. 256 p256DC.WithTrustAnchorID(id4), 257 &rsaCertificate, 258 }, 259 flags: []string{"-expect-selected-credential", "2"}, 260 }) 261 262 // The ClientHello list may be empty. The client must be able to send it and 263 // receive available trust anchors. 264 testCases = append(testCases, testCase{ 265 name: "TrustAnchors-ClientRequestEmpty", 266 config: Config{ 267 MinVersion: VersionTLS13, 268 AvailableTrustAnchors: [][]byte{id1, id2}, 269 Bugs: ProtocolBugs{ 270 ExpectPeerRequestedTrustAnchors: [][]byte{}, 271 }, 272 }, 273 flags: []string{ 274 "-requested-trust-anchors", trustAnchorListFlagValue(), 275 "-expect-peer-available-trust-anchors", trustAnchorListFlagValue(id1, id2), 276 }, 277 }) 278 // The server must be able to process it, and send available trust anchors. 279 testCases = append(testCases, testCase{ 280 testType: serverTest, 281 name: "TrustAnchors-ServerReceiveEmptyRequest", 282 config: Config{ 283 MinVersion: VersionTLS13, 284 RequestTrustAnchors: [][]byte{}, 285 Bugs: ProtocolBugs{ 286 ExpectPeerAvailableTrustAnchors: [][]byte{id1, id2}, 287 ExpectPeerMatchTrustAnchor: ptrTo(false), 288 }, 289 }, 290 shimCredentials: []*Credential{ 291 rsaCertificate.WithTrustAnchorID(id1), 292 rsaCertificate.WithTrustAnchorID(id2), 293 &rsaCertificate, 294 }, 295 flags: []string{"-expect-selected-credential", "2"}, 296 }) 297 298 // This extension requires TLS 1.3. If a server receives this and negotiates 299 // TLS 1.2, it should ignore the extension and not accidentally send 300 // something in ServerHello (implicitly checked by runner). 301 testCases = append(testCases, testCase{ 302 testType: serverTest, 303 name: "TrustAnchors-TLS12-Server", 304 config: Config{ 305 MaxVersion: VersionTLS12, 306 RequestTrustAnchors: [][]byte{id1}, 307 }, 308 shimCredentials: []*Credential{ 309 rsaCertificate.WithTrustAnchorID(id1), 310 &rsaCertificate, 311 }, 312 // The first credential is skipped because the extension is ignored. 313 flags: []string{"-expect-selected-credential", "1"}, 314 }) 315 // The client should reject the extension in TLS 1.2 ServerHello. 316 testCases = append(testCases, testCase{ 317 name: "TrustAnchors-TLS12-Client", 318 config: Config{ 319 MaxVersion: VersionTLS12, 320 AvailableTrustAnchors: [][]byte{id1}, 321 Bugs: ProtocolBugs{ 322 AlwaysSendAvailableTrustAnchors: true, 323 }, 324 }, 325 flags: []string{"-requested-trust-anchors", trustAnchorListFlagValue(id1)}, 326 shouldFail: true, 327 expectedError: ":UNEXPECTED_EXTENSION:", 328 expectedLocalError: "remote error: unsupported extension", 329 }) 330} 331