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  }