github.com/zmap/zcrypto@v0.0.0-20240512203510-0fef58d9a9db/verifier/verifier_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  	"bytes"
    19  	"crypto/sha256"
    20  	"encoding/hex"
    21  	"encoding/pem"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"os"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/zmap/zcrypto/x509"
    30  	"github.com/zmap/zcrypto/x509/revocation/google"
    31  	"github.com/zmap/zcrypto/x509/revocation/mozilla"
    32  
    33  	data "github.com/zmap/zcrypto/data/test/certificates"
    34  )
    35  
    36  func loadPEMs(pems []string) (out []*x509.Certificate) {
    37  	for _, s := range pems {
    38  		c := loadPEM(s)
    39  		out = append(out, c)
    40  	}
    41  	return
    42  }
    43  
    44  func loadPEM(pemBytes string) *x509.Certificate {
    45  	block, _ := pem.Decode([]byte(pemBytes))
    46  	if block == nil {
    47  		return nil
    48  	}
    49  	c, _ := x509.ParseCertificate(block.Bytes)
    50  	return c
    51  }
    52  
    53  func getChainID(chain x509.CertificateChain) string {
    54  	parts := make([]string, 0, len(chain))
    55  	for _, c := range chain {
    56  		hexHash := hex.EncodeToString(c.FingerprintSHA256)
    57  		parts = append(parts, hexHash)
    58  	}
    59  	return strings.Join(parts, "|")
    60  }
    61  
    62  type chainError struct {
    63  	Extra, Missing []string
    64  }
    65  
    66  func (e *chainError) Error() string {
    67  	out := fmt.Sprintf("missing chains: %v, extra chains: %v", e.Missing, e.Extra)
    68  	return out
    69  }
    70  
    71  type parentError struct {
    72  	Extra, Missing []string
    73  }
    74  
    75  func (e *parentError) Error() string {
    76  	out := fmt.Sprintf("missing parents: %v, extra parents: %v", e.Missing, e.Extra)
    77  	return out
    78  }
    79  
    80  type crlSetLoader func() (*google.CRLSet, error)
    81  type oneCRLLoader func() (*mozilla.OneCRL, error)
    82  
    83  type verifyTest struct {
    84  	Name string
    85  
    86  	Leaf          string
    87  	Presented     []string
    88  	Intermediates []string
    89  	Roots         []string
    90  
    91  	CurrentTime int64
    92  	DNSName     string
    93  
    94  	ExpectedChains  [][]int
    95  	ExpiredChains   [][]int
    96  	NeverChains     [][]int
    97  	ExpectedParents []int
    98  
    99  	ExpectHostnameError bool
   100  
   101  	InRevocationSet bool
   102  	CRLSetFn        crlSetLoader
   103  	OneCRLFn        oneCRLLoader
   104  
   105  	certificates                    []*x509.Certificate
   106  	leaf                            *x509.Certificate
   107  	presented, intermediates, roots []*x509.Certificate
   108  }
   109  
   110  func (vt *verifyTest) parseSelf() {
   111  	vt.leaf = loadPEM(vt.Leaf)
   112  	vt.presented = loadPEMs(vt.Presented)
   113  	vt.intermediates = loadPEMs(vt.Intermediates)
   114  	vt.roots = loadPEMs(vt.Roots)
   115  
   116  	vt.certificates = append(vt.certificates, vt.leaf)
   117  	vt.certificates = append(vt.certificates, vt.presented...)
   118  	vt.certificates = append(vt.certificates, vt.intermediates...)
   119  	vt.certificates = append(vt.certificates, vt.roots...)
   120  }
   121  
   122  func (vt *verifyTest) parsedLeaf() *x509.Certificate {
   123  	return vt.leaf
   124  }
   125  
   126  func (vt *verifyTest) parsedIntermediates() []*x509.Certificate {
   127  	out := make([]*x509.Certificate, len(vt.intermediates))
   128  	copy(out, vt.intermediates)
   129  	return out
   130  }
   131  
   132  func (vt *verifyTest) parsedRoots() []*x509.Certificate {
   133  	out := make([]*x509.Certificate, len(vt.roots))
   134  	copy(out, vt.roots)
   135  	return out
   136  }
   137  
   138  func (vt *verifyTest) unionAllExpected() [][]int {
   139  	out := make([][]int, 0, 3)
   140  	current := make([][]int, len(vt.ExpectedChains))
   141  	copy(current, vt.ExpectedChains)
   142  	expired := make([][]int, len(vt.ExpiredChains))
   143  	copy(expired, vt.ExpiredChains)
   144  	never := make([][]int, len(vt.NeverChains))
   145  	copy(never, vt.NeverChains)
   146  	out = append(out, current...)
   147  	out = append(out, expired...)
   148  	out = append(out, never...)
   149  	return out
   150  }
   151  
   152  func (vt *verifyTest) compareChains(expected [][]int, actual []x509.CertificateChain) *chainError {
   153  	type empty struct{}
   154  
   155  	expectedChainMap := make(map[string]empty)
   156  	for _, expectedChainIndices := range expected {
   157  		expectedCerts := make([]*x509.Certificate, 0, len(expectedChainIndices))
   158  		for _, certIdx := range expectedChainIndices {
   159  			expectedCerts = append(expectedCerts, vt.certificates[certIdx])
   160  		}
   161  		chainID := getChainID(expectedCerts)
   162  		expectedChainMap[chainID] = empty{}
   163  	}
   164  	actualChainMap := make(map[string]empty)
   165  	for _, chain := range actual {
   166  		chainID := getChainID(chain)
   167  		actualChainMap[chainID] = empty{}
   168  	}
   169  
   170  	var missing, extra []string
   171  	for expectedID := range expectedChainMap {
   172  		_, ok := actualChainMap[expectedID]
   173  		if !ok {
   174  			missing = append(missing, expectedID)
   175  		}
   176  	}
   177  	for actualID := range actualChainMap {
   178  		_, ok := expectedChainMap[actualID]
   179  		if !ok {
   180  			extra = append(extra, actualID)
   181  		}
   182  	}
   183  
   184  	if len(missing) > 0 || len(extra) > 0 {
   185  		err := chainError{
   186  			Missing: missing,
   187  			Extra:   extra,
   188  		}
   189  		return &err
   190  	}
   191  	return nil
   192  }
   193  
   194  func (vt *verifyTest) compareParents(expected []int, actual []*x509.Certificate) *parentError {
   195  	type empty struct{}
   196  	expectedHashMap := make(map[string]empty)
   197  	for _, certIdx := range expected {
   198  		c := vt.certificates[certIdx]
   199  		hexHash := hex.EncodeToString(c.FingerprintSHA256)
   200  		expectedHashMap[hexHash] = empty{}
   201  	}
   202  	actualHashMap := make(map[string]empty)
   203  	for _, c := range actual {
   204  		hexHash := hex.EncodeToString(c.FingerprintSHA256)
   205  		actualHashMap[hexHash] = empty{}
   206  	}
   207  
   208  	var missing, extra []string
   209  	for expectedHash := range expectedHashMap {
   210  		_, ok := actualHashMap[expectedHash]
   211  		if !ok {
   212  			missing = append(missing, expectedHash)
   213  		}
   214  	}
   215  	for actualHash := range actualHashMap {
   216  		_, ok := expectedHashMap[actualHash]
   217  		if !ok {
   218  			extra = append(extra, actualHash)
   219  		}
   220  	}
   221  
   222  	if len(missing) > 0 || len(extra) > 0 {
   223  		err := parentError{
   224  			Missing: missing,
   225  			Extra:   extra,
   226  		}
   227  		return &err
   228  	}
   229  	return nil
   230  }
   231  
   232  func (vt *verifyTest) compareParentSPKISubjectToParents(verifyResult *VerificationResult) error {
   233  	if len(verifyResult.ParentSPKISubjectFingerprint) == 0 && len(verifyResult.Parents) > 0 {
   234  		return fmt.Errorf("got empty ParentSPKISubjectFingerprint, but have %d parents", len(verifyResult.Parents))
   235  	}
   236  	if len(verifyResult.Parents) == 0 && len(verifyResult.ParentSPKISubjectFingerprint) != 0 {
   237  		return fmt.Errorf("got ParentSPKISubjectFingeprint %s, but no parents", verifyResult.ParentSPKISubjectFingerprint.Hex())
   238  	}
   239  	expected := verifyResult.ParentSPKISubjectFingerprint
   240  	for i, parent := range verifyResult.Parents {
   241  		actualParentFp := parent.SPKISubjectFingerprint
   242  		if !bytes.Equal(expected, actualParentFp) {
   243  			return fmt.Errorf("got ParentSPKISubjectFingerprint %s, but parent index %d and hash %s had SPKISubjectFingerprint %s", expected.Hex(), i, parent.FingerprintSHA256.Hex(), actualParentFp.Hex())
   244  		}
   245  	}
   246  	return nil
   247  }
   248  
   249  func (vt *verifyTest) makeVerifier() *Verifier {
   250  	pki := NewGraph()
   251  
   252  	joinedIntermediates := strings.Join(vt.Intermediates, "\n")
   253  	intermediateReader := strings.NewReader(joinedIntermediates)
   254  	pki.AppendFromPEM(intermediateReader, false)
   255  
   256  	joinedRoots := strings.Join(vt.Roots, "\n")
   257  	rootReader := strings.NewReader(joinedRoots)
   258  	pki.AppendFromPEM(rootReader, true)
   259  	v := NewNSS(pki)
   260  	return v
   261  }
   262  
   263  func (vt *verifyTest) makeVerifyOptions() (opts *VerificationOptions) {
   264  	opts = new(VerificationOptions)
   265  	opts.Name = vt.DNSName
   266  	opts.VerifyTime = time.Unix(vt.CurrentTime, 0)
   267  
   268  	if vt.CRLSetFn != nil {
   269  		crl, err := vt.CRLSetFn()
   270  		if err != nil {
   271  			panic(err)
   272  		}
   273  		opts.CRLSet = crl
   274  	}
   275  	if vt.OneCRLFn != nil {
   276  		crl, err := vt.OneCRLFn()
   277  		if err != nil {
   278  			panic(err)
   279  		}
   280  
   281  		opts.OneCRL = crl
   282  	}
   283  
   284  	return opts
   285  }
   286  
   287  func (vt *verifyTest) checkVerifyResult(res *VerificationResult) error {
   288  	if err := vt.compareChains(vt.ExpectedChains, res.CurrentChains); err != nil {
   289  		return fmt.Errorf("bad expected chains: %s", err)
   290  	}
   291  	if err := vt.compareChains(vt.ExpiredChains, res.ExpiredChains); err != nil {
   292  		return fmt.Errorf("bad expired chains: %s", err)
   293  	}
   294  	if err := vt.compareChains(vt.NeverChains, res.NeverValidChains); err != nil {
   295  		return fmt.Errorf("bad never chains: %s", err)
   296  	}
   297  	if err := vt.compareParents(vt.ExpectedParents, res.Parents); err != nil {
   298  		return fmt.Errorf("bad parents: %s", err)
   299  	}
   300  	if vt.ExpectHostnameError && res.NameError == nil {
   301  		return fmt.Errorf("expected hostname error, got nil")
   302  	}
   303  	if res.NameError != nil && !vt.ExpectHostnameError {
   304  		return fmt.Errorf("unexpected name error: %s", res.NameError)
   305  	}
   306  	if err := vt.compareParentSPKISubjectToParents(res); err != nil {
   307  		return err
   308  	}
   309  
   310  	if vt.InRevocationSet != res.InRevocationSet {
   311  		return fmt.Errorf("unexpected InRevocationSet: %t", res.InRevocationSet)
   312  	}
   313  	return nil
   314  }
   315  
   316  var verifyTests = []verifyTest{
   317  	{
   318  		Name:      "PEMLEX3SignedByISRGRootX1-in-CRLSet",
   319  		Leaf:      data.PEMDAdrianIOSignedByLEX3, // idx=0
   320  		Presented: nil,
   321  		Intermediates: []string{
   322  			data.PEMLEX3SignedByDSTRootCAX3, // idx=1
   323  			data.PEMLEX3SignedByISRGRootX1,
   324  		},
   325  		Roots: []string{
   326  			data.PEMDSTRootCAX3SignedBySelf, // idx=3
   327  		},
   328  		CurrentTime: 1501804800, // 2017-08-04T00:00:00
   329  		ExpectedChains: [][]int{
   330  			{0, 1, 3},
   331  		},
   332  		ExpectedParents: []int{1},
   333  		CRLSetFn: func() (*google.CRLSet, error) {
   334  			return crlSetIntermediate(data.PEMDAdrianIOSignedByLEX3, []string{
   335  				data.PEMLEX3SignedByDSTRootCAX3, // idx=1
   336  				data.PEMLEX3SignedByISRGRootX1,
   337  			})
   338  		},
   339  		InRevocationSet: true,
   340  	},
   341  	{
   342  		Name:      "PEMLEX3SignedByISRGRootX1-in-CRLSet-clocked",
   343  		Leaf:      data.PEMDAdrianIOSignedByLEX3, // idx=0
   344  		Presented: nil,
   345  		Intermediates: []string{
   346  			data.PEMLEX3SignedByDSTRootCAX3, // idx=1
   347  			data.PEMLEX3SignedByISRGRootX1,
   348  		},
   349  		Roots: []string{
   350  			data.PEMDSTRootCAX3SignedBySelf, // idx=3
   351  		},
   352  		CurrentTime: 1501804800, // 2017-08-04T00:00:00
   353  		ExpectedChains: [][]int{
   354  			{0, 1, 3},
   355  		},
   356  		ExpectedParents: []int{1},
   357  		CRLSetFn: func() (*google.CRLSet, error) {
   358  			return crlSetBlocked([]string{data.PEMLEX3SignedByDSTRootCAX3})
   359  		},
   360  		InRevocationSet: true,
   361  	},
   362  	{
   363  		Name:      "PEMLEX3SignedByISRGRootX1-in-OneCRL",
   364  		Leaf:      data.PEMDAdrianIOSignedByLEX3, // idx=0
   365  		Presented: nil,
   366  		Intermediates: []string{
   367  			data.PEMLEX3SignedByDSTRootCAX3, // idx=1
   368  			data.PEMLEX3SignedByISRGRootX1,
   369  		},
   370  		Roots: []string{
   371  			data.PEMDSTRootCAX3SignedBySelf, // idx=3
   372  		},
   373  		CurrentTime: 1501804800, // 2017-08-04T00:00:00
   374  		ExpectedChains: [][]int{
   375  			{0, 1, 3},
   376  		},
   377  		ExpectedParents: []int{1},
   378  		OneCRLFn: func() (*mozilla.OneCRL, error) {
   379  			return oneCrlIntermediate(data.PEMDAdrianIOSignedByLEX3, []string{
   380  				data.PEMLEX3SignedByDSTRootCAX3, // idx=1
   381  				data.PEMLEX3SignedByISRGRootX1,
   382  			})
   383  		},
   384  		InRevocationSet: true,
   385  	},
   386  	{
   387  		Name:      "PEMLEX3SignedByISRGRootX1-in-OneCRL-blocked",
   388  		Leaf:      data.PEMDAdrianIOSignedByLEX3, // idx=0
   389  		Presented: nil,
   390  		Intermediates: []string{
   391  			data.PEMLEX3SignedByDSTRootCAX3, // idx=1
   392  			data.PEMLEX3SignedByISRGRootX1,
   393  		},
   394  		Roots: []string{
   395  			data.PEMDSTRootCAX3SignedBySelf, // idx=3
   396  		},
   397  		CurrentTime: 1501804800, // 2017-08-04T00:00:00
   398  		ExpectedChains: [][]int{
   399  			{0, 1, 3},
   400  		},
   401  		ExpectedParents: []int{1},
   402  		OneCRLFn: func() (*mozilla.OneCRL, error) {
   403  			return oneCrlBlocked(data.PEMDAdrianIOSignedByLEX3)
   404  		},
   405  		InRevocationSet: true,
   406  	},
   407  	{
   408  		Name:      "le-two-intermediate-dst-root",
   409  		Leaf:      data.PEMDAdrianIOSignedByLEX3, // idx=0
   410  		Presented: nil,
   411  		Intermediates: []string{
   412  			data.PEMLEX3SignedByDSTRootCAX3, // idx=1
   413  			data.PEMLEX3SignedByISRGRootX1,
   414  		},
   415  		Roots: []string{
   416  			data.PEMDSTRootCAX3SignedBySelf, // idx=3
   417  		},
   418  		CurrentTime: 1501804800, // 2017-08-04T00:00:00
   419  		ExpectedChains: [][]int{
   420  			{0, 1, 3},
   421  		},
   422  		ExpectedParents: []int{1},
   423  	},
   424  	{
   425  		Name:          "dadrian-missing-intermediate",
   426  		Leaf:          data.PEMDAdrianIOSignedByLEX3,
   427  		Intermediates: nil,
   428  		Roots: []string{
   429  			data.PEMDSTRootCAX3SignedBySelf,
   430  		},
   431  		CurrentTime:     1501804800, // 2017-08-04T00:00:00
   432  		ExpectedChains:  nil,
   433  		ExpectedParents: nil,
   434  	},
   435  	{
   436  		Name:      "root-only",
   437  		Leaf:      data.PEMDSTRootCAX3SignedBySelf,
   438  		Presented: nil,
   439  		Intermediates: []string{
   440  			data.PEMLEX3SignedByDSTRootCAX3,
   441  		},
   442  		Roots: []string{
   443  			data.PEMDSTRootCAX3SignedBySelf,
   444  		},
   445  		CurrentTime: 1501804800, // 2017-08-04T00:00:00
   446  		ExpectedChains: [][]int{
   447  			{0},
   448  		},
   449  		ExpiredChains:   nil,
   450  		NeverChains:     nil,
   451  		ExpectedParents: nil,
   452  	},
   453  	{
   454  		Name:      "two-dadrian-le-in-intermediates",
   455  		Leaf:      data.PEMDAdrianIOSignedByLEX3, // idx=0
   456  		Presented: nil,
   457  		Intermediates: []string{
   458  			data.PEMDAdrianIOSignedByLEX3, // idx=1
   459  			data.PEMLEX3SignedByDSTRootCAX3,
   460  			data.PEMLEX3SignedByISRGRootX1,
   461  			data.PEMISRGRootX1SignedBySelf,
   462  		},
   463  		Roots: []string{
   464  			data.PEMISRGRootX1SignedBySelf, // idx=5
   465  			data.PEMDSTRootCAX3SignedBySelf,
   466  		},
   467  		CurrentTime: 1501804800, // 2017-08-04T00:00:00
   468  		ExpectedChains: [][]int{
   469  			{0, 2, 6}, {0, 3, 5},
   470  		},
   471  		ExpiredChains:   nil,
   472  		NeverChains:     nil,
   473  		ExpectedParents: []int{2, 3},
   474  	},
   475  	{
   476  		Name:      "two-dadrian-le-no-presented",
   477  		Leaf:      data.PEMDAdrianIOSignedByLEX3, // idx=0
   478  		Presented: nil,
   479  		Intermediates: []string{
   480  			data.PEMLEX3SignedByDSTRootCAX3, // idx=1
   481  			data.PEMLEX3SignedByISRGRootX1,
   482  			data.PEMISRGRootX1SignedBySelf,
   483  		},
   484  		Roots: []string{
   485  			data.PEMISRGRootX1SignedBySelf, // idx=4
   486  			data.PEMDSTRootCAX3SignedBySelf,
   487  		},
   488  		CurrentTime: 1501804800, // 2017-08-04T00:00:00
   489  		ExpectedChains: [][]int{
   490  			{0, 1, 5}, {0, 2, 4},
   491  		},
   492  		ExpiredChains:   nil,
   493  		NeverChains:     nil,
   494  		ExpectedParents: []int{1, 2},
   495  	},
   496  	{
   497  		Name:      "dod-root-ca-3-in-intermediates",
   498  		Leaf:      data.PEMDoDRootCA3SignedByDoDInteropCA2Serial748,
   499  		Presented: nil,
   500  		Intermediates: []string{
   501  			data.PEMDoDRootCA3SignedByCCEBInteropRootCA2, // idx=1
   502  			data.PEMDoDRootCA3SignedBySelf,
   503  			data.PEMDoDRootCA3SignedByDoDInteropCA2Serial655,
   504  			data.PEMDoDRootCA3SignedByDoDInteropCA2Serial748,
   505  			data.PEMFederalCommonPolicyCASignedByFederalBridgeCA, // idx=5
   506  			data.PEMFederalCommonPolicyCASignedByFederalBridgeCA2013,
   507  			data.PEMFederalCommonPolicyCASignedByFederalBridgeCA2016,
   508  			data.PEMDoDInteropCA2SignedByFederalBridgeCA, // idx=8
   509  			data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial906,
   510  			data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial8225,
   511  			data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial8844,
   512  			data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial9644,
   513  			data.PEMDoDInteropCA2SignedByFederalBridgeCA2016,
   514  			data.PEMFederalBridgeCASignedByDoDInteropCA2, // idx=14
   515  			data.PEMFederalBridgeCASignedByFederalBridgeCA2013,
   516  			data.PEMFederalBridgeCASignedByFederalCommonPolicyCA,
   517  			data.PEMFederalBridgeCA2013SignedByCommonPolicyCASerial5524, // idx=17
   518  			data.PEMFederalBridgeCA2013SignedByCommonPolicyCASerial11424,
   519  			data.PEMFederalBridgeCA2013SignedByDoDInteropCA2,
   520  			data.PEMFederalBridgeCA2013SignedByIdenTrust,
   521  			data.PEMFederalBridgeCA2016SignedByDodInteropCA2, // idx=21
   522  			data.PEMFederalBridgeCA2016SignedByFederalCommonPolicyCA,
   523  		},
   524  		Roots: []string{
   525  			data.PEMFederalCommonPolicyCASignedBySelf, // idx=23
   526  		},
   527  		CurrentTime: 1501545600, // 2017-08-01T00:00:00
   528  		ExpectedChains: [][]int{
   529  			{0, 12, 18, 23},
   530  			{0, 13, 22, 23},
   531  		},
   532  		ExpiredChains: [][]int{
   533  			{0, 8, 15, 17, 23},
   534  			{0, 8, 15, 18, 23},
   535  			{0, 9, 17, 23},
   536  			{0, 9, 18, 23},
   537  			{0, 10, 17, 23},
   538  			{0, 10, 18, 23},
   539  			{0, 11, 17, 23},
   540  			{0, 11, 18, 23},
   541  			{0, 12, 17, 23},
   542  		},
   543  		NeverChains: [][]int{
   544  			{0, 8, 16, 23},
   545  		},
   546  		ExpectedParents: []int{12, 13},
   547  	},
   548  	{
   549  		Name:      "dod-root-ca-3-leaf-no-presented",
   550  		Leaf:      data.PEMDoDRootCA3SignedByDoDInteropCA2Serial748,
   551  		Presented: nil,
   552  		Intermediates: []string{
   553  			data.PEMDoDRootCA3SignedByCCEBInteropRootCA2, // idx=1
   554  			data.PEMDoDRootCA3SignedBySelf,
   555  			data.PEMDoDRootCA3SignedByDoDInteropCA2Serial655,
   556  			data.PEMDAdrianIOSignedByLEX3,
   557  			data.PEMFederalCommonPolicyCASignedByFederalBridgeCA, // idx=5
   558  			data.PEMFederalCommonPolicyCASignedByFederalBridgeCA2013,
   559  			data.PEMFederalCommonPolicyCASignedByFederalBridgeCA2016,
   560  			data.PEMDoDInteropCA2SignedByFederalBridgeCA, // idx=8
   561  			data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial906,
   562  			data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial8225,
   563  			data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial8844,
   564  			data.PEMDoDInteropCA2SignedByFederalBridgeCA2013Serial9644,
   565  			data.PEMDoDInteropCA2SignedByFederalBridgeCA2016,
   566  			data.PEMFederalBridgeCASignedByDoDInteropCA2, // idx=14
   567  			data.PEMFederalBridgeCASignedByFederalBridgeCA2013,
   568  			data.PEMFederalBridgeCASignedByFederalCommonPolicyCA,
   569  			data.PEMFederalBridgeCA2013SignedByCommonPolicyCASerial5524, // idx=17
   570  			data.PEMFederalBridgeCA2013SignedByCommonPolicyCASerial11424,
   571  			data.PEMFederalBridgeCA2013SignedByDoDInteropCA2,
   572  			data.PEMFederalBridgeCA2013SignedByIdenTrust,
   573  			data.PEMFederalBridgeCA2016SignedByDodInteropCA2, // idx=21
   574  			data.PEMFederalBridgeCA2016SignedByFederalCommonPolicyCA,
   575  		},
   576  		Roots: []string{
   577  			data.PEMFederalCommonPolicyCASignedBySelf, // idx=23
   578  		},
   579  		CurrentTime: 1501545600, // 2017-08-01T00:00:00
   580  		ExpectedChains: [][]int{
   581  			{0, 12, 18, 23},
   582  			{0, 13, 22, 23},
   583  		},
   584  		ExpiredChains: [][]int{
   585  			{0, 8, 15, 17, 23},
   586  			{0, 8, 15, 18, 23},
   587  			{0, 9, 17, 23},
   588  			{0, 9, 18, 23},
   589  			{0, 10, 17, 23},
   590  			{0, 10, 18, 23},
   591  			{0, 11, 17, 23},
   592  			{0, 11, 18, 23},
   593  			{0, 12, 17, 23},
   594  		},
   595  		NeverChains: [][]int{
   596  			{0, 8, 16, 23},
   597  		},
   598  		ExpectedParents: []int{12, 13},
   599  	},
   600  	{
   601  		Name: "google-no-presented-chain",
   602  		Leaf: data.PEMGoogleSignedByGIAG2,
   603  		Intermediates: []string{
   604  			data.PEMGIAG2SignedByGeoTrust,
   605  		},
   606  		Roots: []string{
   607  			data.PEMGeoTrustSignedBySelf,
   608  		},
   609  		CurrentTime: 1395785200,
   610  		DNSName:     "www.google.com",
   611  		ExpectedChains: [][]int{
   612  			{0, 1, 2},
   613  		},
   614  		ExpectedParents: []int{1},
   615  		CRLSetFn:        loadCrlSet6375,
   616  		OneCRLFn:        loadTestOneCRL,
   617  	},
   618  	{
   619  		Name: "google-mixed-case",
   620  		Leaf: data.PEMGoogleSignedByGIAG2,
   621  		Intermediates: []string{
   622  			data.PEMGIAG2SignedByGeoTrust,
   623  		},
   624  		Roots: []string{
   625  			data.PEMGeoTrustSignedBySelf,
   626  		},
   627  		CurrentTime: 1395785200,
   628  		DNSName:     "www.google.com",
   629  		ExpectedChains: [][]int{
   630  			{0, 1, 2},
   631  		},
   632  		ExpectedParents: []int{1},
   633  		CRLSetFn:        loadCrlSet6375,
   634  		OneCRLFn:        loadTestOneCRL,
   635  	},
   636  	{
   637  		Name: "google-not-yet-valid",
   638  		Leaf: data.PEMGoogleSignedByGIAG2,
   639  		Intermediates: []string{
   640  			data.PEMGIAG2SignedByGeoTrust,
   641  		},
   642  		Roots: []string{
   643  			data.PEMGeoTrustSignedBySelf,
   644  		},
   645  		CurrentTime:    1,
   646  		DNSName:        "www.google.com",
   647  		ExpectedChains: nil,
   648  		ExpiredChains: [][]int{
   649  			{0, 1, 2},
   650  		},
   651  		ExpectedParents: []int{1},
   652  	},
   653  	{
   654  		Name: "google-expired",
   655  		Leaf: data.PEMGoogleSignedByGIAG2,
   656  		Intermediates: []string{
   657  			data.PEMGIAG2SignedByGeoTrust,
   658  		},
   659  		Roots: []string{
   660  			data.PEMGeoTrustSignedBySelf,
   661  		},
   662  		CurrentTime:    2000000000,
   663  		DNSName:        "www.google.com",
   664  		ExpectedChains: nil,
   665  		ExpiredChains: [][]int{
   666  			{0, 1, 2},
   667  		},
   668  		ExpectedParents: []int{1},
   669  		CRLSetFn:        loadCrlSet6375,
   670  		OneCRLFn:        loadTestOneCRL,
   671  	},
   672  	{
   673  		Name: "google-name-mismatch",
   674  		Leaf: data.PEMGoogleSignedByGIAG2,
   675  		Intermediates: []string{
   676  			data.PEMGIAG2SignedByGeoTrust,
   677  		},
   678  		Roots: []string{
   679  			data.PEMGeoTrustSignedBySelf,
   680  		},
   681  		CurrentTime: 1395785200,
   682  		DNSName:     "www.example.com",
   683  		ExpectedChains: [][]int{
   684  			{0, 1, 2},
   685  		},
   686  		ExpectedParents:     []int{1},
   687  		ExpectHostnameError: true,
   688  	},
   689  	{
   690  		Name:          "google-missing-intermediate",
   691  		Leaf:          data.PEMGoogleSignedByGIAG2,
   692  		Intermediates: nil,
   693  		Roots: []string{
   694  			data.PEMGeoTrustSignedBySelf,
   695  		},
   696  		CurrentTime:     1395785200,
   697  		ExpectedChains:  nil,
   698  		ExpectedParents: nil,
   699  	},
   700  	{
   701  		Name: "google-with-unrelated-intermediate",
   702  		Leaf: data.PEMGoogleSignedByGIAG2,
   703  		Intermediates: []string{
   704  			data.PEMGIAG2SignedByGeoTrust,
   705  			data.PEMDAdrianIOSignedByLEX3,
   706  		},
   707  		Roots: []string{
   708  			data.PEMGeoTrustSignedBySelf,
   709  		},
   710  		CurrentTime: 1395785200,
   711  		DNSName:     "www.google.com",
   712  		ExpectedChains: [][]int{
   713  			{0, 1, 3},
   714  		},
   715  		ExpectedParents: []int{1},
   716  	},
   717  }
   718  
   719  func TestVerify(t *testing.T) {
   720  	for _, test := range verifyTests {
   721  		test.parseSelf()
   722  		v := test.makeVerifier()
   723  		opts := test.makeVerifyOptions()
   724  		verifyResult := v.Verify(test.parsedLeaf(), *opts)
   725  		if err := test.checkVerifyResult(verifyResult); err != nil {
   726  			t.Errorf("%s: %s", test.Name, err)
   727  		}
   728  	}
   729  }
   730  
   731  func loadCRLSet(data string) (*google.CRLSet, error) {
   732  	crlSetFile, err := os.Open(data)
   733  	if err != nil {
   734  		return nil, err
   735  	}
   736  	defer crlSetFile.Close()
   737  
   738  	crlSetBytes, err := ioutil.ReadAll(crlSetFile)
   739  	if err != nil {
   740  		return nil, err
   741  	}
   742  
   743  	crlset, err := google.Parse(crlSetBytes, "6375")
   744  	if err != nil {
   745  		return nil, err
   746  	}
   747  	return crlset, nil
   748  }
   749  
   750  func loadOneCRL(data string) (*mozilla.OneCRL, error) {
   751  	oneCRLFile, err := os.Open(data)
   752  	if err != nil {
   753  		return nil, err
   754  	}
   755  	defer oneCRLFile.Close()
   756  	oneCRLBytes, err := ioutil.ReadAll(oneCRLFile)
   757  	if err != nil {
   758  		return nil, err
   759  	}
   760  
   761  	onecrl, err := mozilla.Parse(oneCRLBytes)
   762  	if err != nil {
   763  		return nil, err
   764  	}
   765  	return onecrl, nil
   766  }
   767  
   768  func loadCrlSet6375() (*google.CRLSet, error) {
   769  	return loadCRLSet("testdata/crl-set-6375")
   770  }
   771  
   772  func loadTestOneCRL() (*mozilla.OneCRL, error) {
   773  	return loadOneCRL("testdata/test_onecrl.json")
   774  }
   775  
   776  func crlSetIntermediate(leafPEM string, intermediatesPEM []string) (*google.CRLSet, error) {
   777  	crl := &google.CRLSet{
   778  		IssuerLists: make(map[string]*google.IssuerList),
   779  	}
   780  
   781  	leaf := loadPEM(leafPEM)
   782  	for _, ca := range loadPEMs(intermediatesPEM) {
   783  		if ca.Subject.CommonName == leaf.Issuer.CommonName {
   784  			spki := hex.EncodeToString(ca.SPKIFingerprint)
   785  
   786  			entries := &google.IssuerList{
   787  				SPKIHash: spki,
   788  				Entries: []*google.Entry{
   789  					{SerialNumber: leaf.SerialNumber},
   790  				},
   791  			}
   792  
   793  			crl.IssuerLists[spki] = entries
   794  		}
   795  	}
   796  	return crl, nil
   797  }
   798  
   799  func crlSetBlocked(intermediatesPEM []string) (*google.CRLSet, error) {
   800  	crl := &google.CRLSet{
   801  		IssuerLists:  make(map[string]*google.IssuerList),
   802  		BlockedSPKIs: make([]string, 0),
   803  	}
   804  
   805  	for _, ca := range loadPEMs(intermediatesPEM) {
   806  		spki := hex.EncodeToString(ca.SPKIFingerprint)
   807  		crl.BlockedSPKIs = append(crl.BlockedSPKIs, spki)
   808  
   809  	}
   810  	return crl, nil
   811  }
   812  
   813  func oneCrlIntermediate(leafPEM string, intermediatesPEM []string) (*mozilla.OneCRL, error) {
   814  	crl := &mozilla.OneCRL{
   815  		IssuerLists: make(map[string]*mozilla.IssuerList),
   816  	}
   817  
   818  	leaf := loadPEM(leafPEM)
   819  	for _, ca := range loadPEMs(intermediatesPEM) {
   820  		if ca.Subject.CommonName == leaf.Issuer.CommonName {
   821  			entries := &mozilla.IssuerList{
   822  				Issuer: &ca.Subject,
   823  				Entries: []*mozilla.Entry{
   824  					{
   825  						Issuer:       &ca.Subject,
   826  						SerialNumber: leaf.SerialNumber,
   827  					},
   828  				},
   829  			}
   830  
   831  			crl.IssuerLists[leaf.Issuer.String()] = entries
   832  		}
   833  	}
   834  	return crl, nil
   835  }
   836  
   837  func oneCrlBlocked(leafPEM string) (*mozilla.OneCRL, error) {
   838  	crl := &mozilla.OneCRL{
   839  		IssuerLists: make(map[string]*mozilla.IssuerList),
   840  		Blocked:     make([]*mozilla.SubjectAndPublicKey, 0),
   841  	}
   842  
   843  	leaf := loadPEM(leafPEM)
   844  
   845  	pubKeyData, _ := x509.MarshalPKIXPublicKey(leaf.PublicKey)
   846  	hash := sha256.Sum256(pubKeyData)
   847  
   848  	spk := &mozilla.SubjectAndPublicKey{
   849  		RawSubject: leaf.RawSubject,
   850  		Subject:    &leaf.Subject,
   851  		PubKeyHash: hash[:],
   852  	}
   853  
   854  	crl.Blocked = append(crl.Blocked, spk)
   855  	return crl, nil
   856  }