github.com/zmap/zcrypto@v0.0.0-20240512203510-0fef58d9a9db/verifier/graph_test.go (about) 1 /* 2 * ZCrypto Copyright 2017 Regents of the University of Michigan 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * of the License at http://www.apache.org/licenses/LICENSE-2.0 7 * 8 * Unless required by applicable law or agreed to in writing, software 9 * distributed under the License is distributed on an "AS IS" BASIS, 10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 11 * implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package verifier 16 17 import ( 18 "encoding/hex" 19 "strings" 20 "testing" 21 22 "github.com/zmap/zcrypto/x509" 23 24 data "github.com/zmap/zcrypto/data/test/certificates" 25 ) 26 27 type edgeIdx struct { 28 issuer, child, cert int 29 } 30 31 type graphTest struct { 32 name string 33 certificates []string 34 expectedNodes []string 35 expectedEdges []edgeIdx 36 } 37 38 var graphTests = []graphTest{ 39 { 40 name: "one-certificate", 41 certificates: []string{data.PEMDoDRootCA3SignedByDoDInteropCA2Serial655}, 42 expectedNodes: []string{data.HexSPKISubjectFingerprintDoDRootCA3}, 43 expectedEdges: []edgeIdx{{-1, 0, 0}}, 44 }, 45 { 46 name: "child-parent", 47 certificates: []string{data.PEMDoDRootCA3SignedByDoDInteropCA2Serial655, data.PEMDoDInteropCA2SignedByFederalBridgeCA2016}, 48 expectedNodes: []string{data.HexSPKISubjectFingerprintDoDRootCA3, data.HexSPKISubjectFingerprintDoDInteropCA2}, 49 expectedEdges: []edgeIdx{{1, 0, 0}, {-1, 1, 1}}, 50 }, 51 { 52 name: "parent-child", 53 certificates: []string{data.PEMDoDInteropCA2SignedByFederalBridgeCA2016, data.PEMDoDRootCA3SignedByDoDInteropCA2Serial655}, 54 expectedNodes: []string{data.HexSPKISubjectFingerprintDoDRootCA3, data.HexSPKISubjectFingerprintDoDInteropCA2}, 55 expectedEdges: []edgeIdx{{1, 0, 1}, {-1, 1, 0}}, 56 }, 57 { 58 name: "two-unrelated", 59 certificates: []string{data.PEMDoDInteropCA2SignedByFederalBridgeCA2016, data.PEMDAdrianIOSignedByLEX3}, 60 expectedNodes: []string{data.HexSPKISubjectFingerprintDoDInteropCA2, data.HexSPKISubjectFingerprintDAdrianIO}, 61 expectedEdges: []edgeIdx{{-1, 0, 0}, {-1, 1, 1}}, 62 }, 63 { 64 name: "self-signed", 65 certificates: []string{data.PEMDoDRootCA3SignedBySelf}, 66 expectedNodes: []string{data.HexSPKISubjectFingerprintDoDRootCA3}, 67 expectedEdges: []edgeIdx{{0, 0, 0}}, 68 }, 69 { 70 name: "dod-root-ca-3-no-issuers", 71 certificates: []string{data.PEMDoDRootCA3SignedBySelf, data.PEMDoDRootCA3SignedByCCEBInteropRootCA2, data.PEMDoDRootCA3SignedByDoDInteropCA2Serial655, data.PEMDoDRootCA3SignedByDoDInteropCA2Serial748}, 72 expectedNodes: []string{data.HexSPKISubjectFingerprintDoDRootCA3}, 73 expectedEdges: []edgeIdx{{0, 0, 0}, {-1, 0, 1}, {-1, 0, 2}, {-1, 0, 3}}, 74 }, 75 { 76 name: "dod-root-ca-3-interop-issued-by-bridge-16", 77 certificates: []string{ 78 data.PEMDoDRootCA3SignedBySelf, 79 data.PEMDoDRootCA3SignedByCCEBInteropRootCA2, 80 data.PEMDoDRootCA3SignedByDoDInteropCA2Serial655, 81 data.PEMDoDRootCA3SignedByDoDInteropCA2Serial748, 82 data.PEMDoDInteropCA2SignedByFederalBridgeCA2016, // issuer (idx=4) 83 }, 84 expectedNodes: []string{ 85 data.HexSPKISubjectFingerprintDoDRootCA3, 86 data.HexSPKISubjectFingerprintDoDInteropCA2, 87 }, 88 expectedEdges: []edgeIdx{{0, 0, 0}, {-1, 0, 1}, {1, 0, 2}, {1, 0, 3}, {-1, 1, 4}}, 89 }, 90 { 91 name: "dod-root-ca-3-interop-issued-by-bridge-16-reversed", 92 certificates: []string{ 93 data.PEMDoDInteropCA2SignedByFederalBridgeCA2016, // issuer (idx=0) 94 data.PEMDoDRootCA3SignedBySelf, 95 data.PEMDoDRootCA3SignedByCCEBInteropRootCA2, 96 data.PEMDoDRootCA3SignedByDoDInteropCA2Serial655, 97 data.PEMDoDRootCA3SignedByDoDInteropCA2Serial748, 98 }, 99 expectedNodes: []string{ 100 data.HexSPKISubjectFingerprintDoDRootCA3, 101 data.HexSPKISubjectFingerprintDoDInteropCA2, 102 }, 103 expectedEdges: []edgeIdx{{0, 0, 1}, {-1, 0, 2}, {1, 0, 3}, {1, 0, 4}, {-1, 1, 0}}, 104 }, 105 { 106 name: "dod-root-ca-3-interop-ca-2", 107 certificates: []string{ 108 data.PEMDoDRootCA3SignedBySelf, 109 data.PEMDoDRootCA3SignedByCCEBInteropRootCA2, 110 data.PEMDoDRootCA3SignedByDoDInteropCA2Serial655, 111 data.PEMDoDRootCA3SignedByDoDInteropCA2Serial748, 112 data.PEMDoDInteropCA2SignedByFederalBridgeCA2016, // issuer (idx=4), 113 data.PEMDoDInteropCA2SignedByFederalBridgeCA, 114 data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial906, 115 data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial8225, 116 data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial8844, 117 data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial9644, // (idx=9) 118 }, 119 expectedNodes: []string{ 120 data.HexSPKISubjectFingerprintDoDRootCA3, 121 data.HexSPKISubjectFingerprintDoDInteropCA2, 122 }, 123 expectedEdges: []edgeIdx{ 124 {0, 0, 0}, 125 {-1, 0, 1}, 126 {1, 0, 2}, 127 {1, 0, 3}, 128 {-1, 1, 4}, 129 {-1, 1, 5}, 130 {-1, 1, 6}, 131 {-1, 1, 7}, 132 {-1, 1, 8}, 133 {-1, 1, 9}, 134 }, 135 }, 136 { 137 name: "bridge-ca-13", 138 certificates: []string{ 139 data.PEMFederalBridgeCA2013SignedByIdenTrust, 140 data.PEMFederalBridgeCA2013SignedByCommonPolicyCASerial5524, 141 data.PEMFederalBridgeCA2013SignedByCommonPolicyCASerial11424, 142 }, 143 expectedNodes: []string{ 144 data.HexSPKISubjectFingerprintFederalBridgeCA2013, 145 }, 146 expectedEdges: []edgeIdx{ 147 {-1, 0, 0}, 148 {-1, 0, 1}, 149 {-1, 0, 2}, 150 }, 151 }, 152 { 153 name: "bridge-ca-13-dod-root-ca-3-dod-interop-join", 154 certificates: []string{ 155 data.PEMFederalBridgeCA2013SignedByIdenTrust, 156 data.PEMFederalBridgeCA2013SignedByCommonPolicyCASerial5524, 157 data.PEMFederalBridgeCA2013SignedByCommonPolicyCASerial11424, 158 data.PEMDoDRootCA3SignedBySelf, // idx=3 159 data.PEMDoDRootCA3SignedByCCEBInteropRootCA2, 160 data.PEMDoDRootCA3SignedByDoDInteropCA2Serial655, 161 data.PEMDoDRootCA3SignedByDoDInteropCA2Serial748, 162 data.PEMDoDInteropCA2SignedByFederalBridgeCA2016, // idx=7 163 data.PEMDoDInteropCA2SignedByFederalBridgeCA, 164 data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial906, 165 data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial8225, 166 data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial8844, 167 data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial9644, // idx=12 168 }, 169 expectedNodes: []string{ 170 data.HexSPKISubjectFingerprintDoDRootCA3, 171 data.HexSPKISubjectFingerprintDoDInteropCA2, 172 data.HexSPKISubjectFingerprintFederalBridgeCA2013, 173 }, 174 expectedEdges: []edgeIdx{ 175 {-1, 2, 0}, 176 {-1, 2, 1}, 177 {-1, 2, 2}, 178 {0, 0, 3}, 179 {-1, 0, 4}, 180 {1, 0, 5}, 181 {1, 0, 6}, 182 {-1, 1, 7}, 183 {-1, 1, 8}, 184 {2, 1, 9}, 185 {2, 1, 10}, 186 {2, 1, 11}, 187 {2, 1, 12}, 188 }, 189 }, 190 { 191 name: "bridge-ca-2016-loop-with-interop", 192 certificates: []string{ 193 data.PEMFederalBridgeCA2016SignedByDodInteropCA2, 194 data.PEMDoDInteropCA2SignedByFederalBridgeCA2016, 195 }, 196 expectedNodes: []string{ 197 data.HexSPKISubjectFingerprintFederalBridgeCA2016, 198 data.HexSPKISubjectFingerprintDoDInteropCA2, 199 }, 200 expectedEdges: []edgeIdx{ 201 {1, 0, 0}, 202 {0, 1, 1}, 203 }, 204 }, 205 { 206 name: "all-bridge-ca-joined-by-common-policy-self-signed", 207 certificates: []string{ 208 data.PEMFederalBridgeCA2016SignedByFederalCommonPolicyCA, 209 data.PEMFederalBridgeCASignedByFederalCommonPolicyCA, 210 data.PEMFederalBridgeCA2013SignedByCommonPolicyCASerial5524, 211 data.PEMFederalBridgeCA2013SignedByCommonPolicyCASerial11424, 212 data.PEMFederalCommonPolicyCASignedBySelf, // idx=4 213 }, 214 expectedNodes: []string{ 215 data.HexSPKISubjectFingerprintFederalBridgeCA, 216 data.HexSPKISubjectFingerprintFederalBridgeCA2013, 217 data.HexSPKISubjectFingerprintFederalBridgeCA2016, 218 data.HexSPKISubjectFingerprintFederalCommonPolicyCA, // idx=3 219 }, 220 expectedEdges: []edgeIdx{ 221 {3, 2, 0}, 222 {3, 0, 1}, 223 {3, 1, 2}, 224 {3, 1, 3}, 225 {3, 3, 4}, 226 }, 227 }, 228 { 229 name: "fpki", 230 certificates: []string{ 231 data.PEMDoDRootCA3SignedByCCEBInteropRootCA2, 232 data.PEMDoDRootCA3SignedBySelf, 233 data.PEMDoDRootCA3SignedByDoDInteropCA2Serial655, 234 data.PEMDoDRootCA3SignedByDoDInteropCA2Serial748, 235 data.PEMFederalCommonPolicyCASignedBySelf, // idx=4 236 data.PEMFederalCommonPolicyCASignedByFederalBridgeCA, 237 data.PEMFederalCommonPolicyCASignedByFederalBridgeCA2013, 238 data.PEMFederalCommonPolicyCASignedByFederalBridgeCA2016, 239 data.PEMDoDInteropCA2SignedByFederalBridgeCA, // idx=8 240 data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial906, 241 data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial8225, 242 data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial8844, 243 data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial9644, 244 data.PEMDoDInteropCA2SignedByFederalBridgeCA2016, 245 data.PEMFederalBridgeCASignedByDoDInteropCA2, // idx=14 246 data.PEMFederalBridgeCASignedByFederalBridgeCA2013, 247 data.PEMFederalBridgeCASignedByFederalCommonPolicyCA, 248 data.PEMFederalBridgeCA2013SignedByCommonPolicyCASerial5524, // idx=17 249 data.PEMFederalBridgeCA2013SignedByCommonPolicyCASerial11424, 250 data.PEMFederalBridgeCA2013SignedByDoDInteropCA2, 251 data.PEMFederalBridgeCA2013SignedByIdenTrust, 252 data.PEMFederalBridgeCA2016SignedByDodInteropCA2, // idx=21 253 data.PEMFederalBridgeCA2016SignedByFederalCommonPolicyCA, 254 }, 255 expectedNodes: []string{ 256 data.HexSPKISubjectFingerprintDoDRootCA3, 257 data.HexSPKISubjectFingerprintDoDInteropCA2, 258 data.HexSPKISubjectFingerprintFederalBridgeCA, //idx=2 259 data.HexSPKISubjectFingerprintFederalBridgeCA2013, 260 data.HexSPKISubjectFingerprintFederalBridgeCA2016, 261 data.HexSPKISubjectFingerprintFederalCommonPolicyCA, //idx=5 262 }, 263 expectedEdges: []edgeIdx{ 264 {-1, 0, 0}, 265 {0, 0, 1}, 266 {1, 0, 2}, 267 {1, 0, 3}, 268 {5, 5, 4}, 269 {2, 5, 5}, 270 {3, 5, 6}, 271 {4, 5, 7}, 272 {2, 1, 8}, 273 {3, 1, 9}, 274 {3, 1, 10}, 275 {3, 1, 11}, 276 {3, 1, 12}, 277 {4, 1, 13}, 278 {1, 2, 14}, 279 {3, 2, 15}, 280 {5, 2, 16}, 281 {5, 3, 17}, 282 {5, 3, 18}, 283 {1, 3, 19}, 284 {-1, 3, 20}, 285 {1, 4, 21}, 286 {5, 4, 22}, 287 }, 288 }, 289 { 290 name: "wuerzburg", 291 certificates: []string{ 292 data.PEMSBHome6WuerzburgSignedByUNIWUCAG01, 293 data.PEMUNIWUCAG01SignedByDFNVerin, 294 }, 295 expectedNodes: []string{ 296 data.HexSPKISubjectFingerprintUNIWUCAG01, 297 data.HexSPKISubjectFingerprintSBHome6Wuerzburg, 298 }, 299 expectedEdges: []edgeIdx{ 300 {0, 1, 0}, 301 {-1, 0, 1}, 302 }, 303 }, 304 { 305 name: "empty", // this shouldn't panic 306 }, 307 } 308 309 func TestGraphAddOneCert(t *testing.T) { 310 c := loadPEM(data.PEMDoDRootCA3SignedByDoDInteropCA2Serial655) 311 g := NewGraph() 312 g.AddCert(c) 313 nodes := g.Nodes() 314 if len(nodes) != 1 { 315 t.Errorf("expected len(nodes) = 1, got %d", len(nodes)) 316 } 317 edge := g.FindEdge(c.FingerprintSHA256) 318 if edge == nil { 319 t.Error("did not find edge") 320 t.FailNow() 321 } 322 if edge.child == nil { 323 t.Error("child should never be nil") 324 } 325 if edge.issuer != nil { 326 t.Error("issuer should not be set, only one certificate was added") 327 } 328 } 329 330 func TestGraph(t *testing.T) { 331 for _, test := range graphTests { 332 g := NewGraph() 333 var certificates []*x509.Certificate 334 // Add all the certificates to the graph 335 for _, pem := range test.certificates { 336 c := loadPEM(pem) 337 certificates = append(certificates, c) 338 g.AddCert(c) 339 } 340 341 var expectedNodeFingerprints []x509.CertificateFingerprint 342 for _, hexfp := range test.expectedNodes { 343 fp, err := hex.DecodeString(hexfp) 344 if err != nil { 345 t.Errorf("%s: unabled to decode hex spki_subject_fingerprint %s", test.name, hexfp) 346 t.FailNow() 347 } 348 expectedNodeFingerprints = append(expectedNodeFingerprints, fp) 349 } 350 351 // Ensure each node exists 352 nodes := g.Nodes() 353 if len(nodes) != len(test.expectedNodes) { 354 t.Errorf("%s: expected %d nodes, got %d", test.name, len(test.expectedNodes), len(nodes)) 355 } 356 for _, fp := range expectedNodeFingerprints { 357 node := g.FindNode(fp) 358 if node == nil { 359 t.Errorf("%s: missing expected node %s", test.name, fp.Hex()) 360 } 361 } 362 363 // Ensure each certificate has an edge 364 edges := g.Edges() 365 if len(test.expectedEdges) != len(edges) { 366 t.Errorf("%s: expected %d edges, got %d", test.name, len(test.expectedEdges), len(edges)) 367 } 368 for certIdx, c := range certificates { 369 edge := g.FindEdge(c.FingerprintSHA256) 370 if edge == nil { 371 t.Errorf("%s: certificate #%d had no edge (%s)", test.name, certIdx, c.FingerprintSHA256.Hex()) 372 } 373 } 374 for _, indicies := range test.expectedEdges { 375 c := certificates[indicies.cert] 376 edge := g.FindEdge(c.FingerprintSHA256) 377 expectedChildFP := expectedNodeFingerprints[indicies.child] 378 actualChildFP := edge.child.SubjectAndKey.Fingerprint 379 if !expectedChildFP.Equal(actualChildFP) { 380 t.Errorf("%s: expected edge for certificate %s to have subject node %s, got %s", test.name, c.FingerprintSHA256.Hex(), expectedChildFP.Hex(), actualChildFP.Hex()) 381 } 382 if indicies.issuer < 0 { 383 if edge.issuer != nil { 384 t.Errorf("%s: expected edge for certificate %s to have nil issuer, got %s", test.name, c.FingerprintSHA256.Hex(), edge.issuer.SubjectAndKey.Fingerprint.Hex()) 385 } 386 continue 387 } 388 expectedIssuerFP := expectedNodeFingerprints[indicies.issuer] 389 if edge.issuer == nil { 390 t.Errorf("%s: expected edge for certificate %s to have issuer %s, got nil", test.name, c.FingerprintSHA256.Hex(), expectedIssuerFP.Hex()) 391 continue 392 } 393 actualIssuerFP := edge.issuer.SubjectAndKey.Fingerprint 394 if !expectedIssuerFP.Equal(actualIssuerFP) { 395 t.Errorf("%s: expected edge for certificate %s to have issuer %s, got %s", test.name, c.FingerprintSHA256.Hex(), expectedIssuerFP.Hex(), actualIssuerFP.Hex()) 396 } 397 } 398 399 } 400 } 401 402 func TestAppendFromPEM(t *testing.T) { 403 for _, test := range graphTests { 404 g := NewGraph() 405 joined := strings.Join(test.certificates, "\n") 406 r := strings.NewReader(joined) 407 n := g.AppendFromPEM(r, false) 408 if len(test.certificates) != n { 409 t.Errorf("%s: expected size %d, got %d", test.name, len(test.certificates), n) 410 } 411 } 412 }