package bundler // This test file contains mostly tests on checking Bundle.Status when bundling under different circumstances. import ( "bytes" "crypto/x509" "encoding/json" "io/ioutil" "strings" "testing" "github.com/cloudflare/cfssl/errors" "github.com/cloudflare/cfssl/helpers" "github.com/cloudflare/cfssl/ubiquity" ) const ( testCaBundle = "testdata/ca-bundle.pem" testIntCaBundle = "testdata/int-bundle.pem" testNSSRootBundle = "testdata/nss.pem" testMetadata = "testdata/ca-bundle.crt.metadata" testCFSSLRootBundle = "testdata/ca.pem" testCAFile = "testdata/ca.pem" testCAKeyFile = "testdata/ca.key" testCFSSLIntBundle = "testdata/intermediates.crt" emptyPEM = "testdata/empty.pem" interL1SHA1 = "testdata/inter-L1-sha1.pem" interL1Key = "testdata/inter-L1.key" interL2SHA2 = "testdata/inter-L2.pem" interL2Key = "testdata/inter-L2.key" ) // Simply create a bundler func TestNewBundler(t *testing.T) { newBundler(t) } func TestNewBundlerMissingCA(t *testing.T) { badFile := "testdata/no_such_file.pem" _, err := NewBundler(badFile, testIntCaBundle) if err == nil { t.Fatal("Should fail with error code 4001") } // generate a function checking error content errorCheck := ExpectErrorMessage(`"code":4001`) errorCheck(t, err) } func TestNewBundlerMissingIntermediate(t *testing.T) { badFile := "testdata/no_such_file.pem" _, err := NewBundler(testCaBundle, badFile) if err == nil { t.Fatal("Should fail with error code 3001") } // generate a function checking error content errorCheck := ExpectErrorMessage(`"code":3001`) errorCheck(t, err) } // JSON object of a bundle type bundleObject struct { Bundle string `json:"bundle"` Root string `json:"root"` Cert string `json:"crt"` Key string `json:"key"` KeyType string `json:"key_type"` KeySize int `json:"key_size"` Issuer string `json:"issuer"` Subject string `json:"subject"` Expires string `json:"expires"` Hostnames []string `json:"hostnames"` OCSPSupport bool `json:"ocsp_support"` CRLSupport bool `json:"crl_support"` OCSP []string `json:"ocsp"` Signature string `json:"signature"` Status BundleStatus } var godaddyIssuerString = `/Country=US/Organization=The Go Daddy Group, Inc./OrganizationalUnit=Go Daddy Class 2 Certification Authority` var godaddySubjectString = `/Country=US/Province=Arizona/Locality=Scottsdale/Organization=GoDaddy.com, Inc./OrganizationalUnit=http://certificates.godaddy.com/repository/CommonName=Go Daddy Secure Certification Authority/SerialNumber=07969287` // Test marshal to JSON // Also serves as a JSON format regression test. func TestBundleMarshalJSON(t *testing.T) { b := newBundler(t) bundle, _ := b.BundleFromPEMorDER(GoDaddyIntermediateCert, nil, Optimal, "") bytes, err := json.Marshal(bundle) if err != nil { t.Fatal(err) } var obj bundleObject err = json.Unmarshal(bytes, &obj) if err != nil { t.Fatal(err) } if obj.Bundle == "" { t.Fatal("bundle is empty.") } if obj.Bundle != string(GoDaddyIntermediateCert) { t.Fatal("bundle is incorrect:", obj.Bundle) } if obj.Key != "" { t.Fatal("key is not empty:", obj.Key) } if obj.Root != string(GoDaddyRootCert) { t.Fatal("Root is not recovered") } if obj.Cert != string(GoDaddyIntermediateCert) { t.Fatal("Cert is not recovered") } if obj.KeyType != "2048-bit RSA" { t.Fatal("Incorrect key type:", obj.KeyType) } if obj.KeySize != 2048 { t.Fatal("Incorrect key size:", obj.KeySize) } if obj.Issuer != godaddyIssuerString { t.Fatal("Incorrect issuer:", obj.Issuer) } if obj.Subject != godaddySubjectString { t.Fatal("Incorrect subject:", obj.Subject) } if obj.Expires != "2026-11-16T01:54:37Z" { t.Fatal("Incorrect expiration time:", obj.Expires) } if len(obj.Hostnames) != 1 || obj.Hostnames[0] != "Go Daddy Secure Certification Authority" { t.Fatal("Incorrect hostnames:", obj.Hostnames) } if obj.OCSPSupport != true { t.Fatal("Incorrect OCSP support flag:", obj.OCSPSupport) } if obj.CRLSupport != true { t.Fatal("Incorrect CRL support flag:", obj.CRLSupport) } if len(obj.OCSP) != 1 || obj.OCSP[0] != `http://ocsp.godaddy.com` { t.Fatal("Incorrect ocsp server list:", obj.OCSP) } if obj.Signature != "SHA1WithRSA" { t.Fatal("Incorrect cert signature method:", obj.Signature) } } func TestBundleWithECDSAKeyMarshalJSON(t *testing.T) { b := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, testCFSSLIntBundle, "") bundle, _ := b.BundleFromFile(leafECDSA256, leafKeyECDSA256, Optimal, "") jsonBytes, err := json.Marshal(bundle) if err != nil { t.Fatal(err) } var obj map[string]interface{} err = json.Unmarshal(jsonBytes, &obj) if err != nil { t.Fatal(err) } key := obj["key"].(string) keyBytes, _ := ioutil.ReadFile(leafKeyECDSA256) keyBytes = bytes.Trim(keyBytes, " \n") if key != string(keyBytes) { t.Fatal("key is not recovered.") } cert := obj["crt"].(string) certBytes, _ := ioutil.ReadFile(leafECDSA256) certBytes = bytes.Trim(certBytes, " \n") if cert != string(certBytes) { t.Fatal("cert is not recovered.") } keyType := obj["key_type"] if keyType != "256-bit ECDSA" { t.Fatal("Incorrect key type:", keyType) } } func TestBundleWithRSAKeyMarshalJSON(t *testing.T) { b := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, testCFSSLIntBundle, "") bundle, _ := b.BundleFromFile(leafRSA2048, leafKeyRSA2048, Optimal, "") jsonBytes, err := json.Marshal(bundle) if err != nil { t.Fatal(err) } var obj map[string]interface{} err = json.Unmarshal(jsonBytes, &obj) if err != nil { t.Fatal(err) } key := obj["key"].(string) keyBytes, _ := ioutil.ReadFile(leafKeyRSA2048) keyBytes = bytes.Trim(keyBytes, " \n") if key != string(keyBytes) { t.Error("key is", key) t.Error("keyBytes is", string(keyBytes)) t.Fatal("key is not recovered.") } cert := obj["crt"].(string) certBytes, _ := ioutil.ReadFile(leafRSA2048) certBytes = bytes.Trim(certBytes, " \n") if cert != string(certBytes) { t.Fatal("cert is not recovered.") } keyType := obj["key_type"] if keyType != "2048-bit RSA" { t.Fatal("Incorrect key type:", keyType) } } // Test marshal to JSON on hostnames func TestBundleHostnamesMarshalJSON(t *testing.T) { b := newBundler(t) bundle, _ := b.BundleFromPEMorDER(GoDaddyIntermediateCert, nil, Optimal, "") expected := []byte(`["Go Daddy Secure Certification Authority"]`) hostnames, _ := json.Marshal(bundle.Hostnames) if !bytes.Equal(hostnames, expected) { t.Fatal("Hostnames construction failed for godaddy root cert.", string(hostnames)) } } // Tests on verifying the rebundle flag and error code in Bundle.Status when rebundling. func TestRebundleFromPEM(t *testing.T) { newBundler := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, interL1, "") newBundle, err := newBundler.BundleFromPEMorDER(expiredBundlePEM, nil, Optimal, "") if err != nil { t.Fatalf("Re-bundle failed. %s", err.Error()) } newChain := newBundle.Chain if len(newChain) != 2 { t.Fatalf("Expected bundle chain length is 2. Got %d.", len(newChain)) } expiredChain, _ := helpers.ParseCertificatesPEM(expiredBundlePEM) for i, cert := range newChain { old := expiredChain[i] if i == 0 { if !bytes.Equal(old.Signature, cert.Signature) { t.Fatal("Leaf cert should be the same.") } } else { if bytes.Equal(old.Signature, cert.Signature) { t.Fatal("Intermediate cert should be different.") } } } // The status must be {Code: ExpiringBit is not set, IsRebundled:true, ExpiringSKIs:{}} if len(newBundle.Status.ExpiringSKIs) != 0 || !newBundle.Status.IsRebundled || newBundle.Status.Code&errors.BundleExpiringBit != 0 { t.Fatal("Rebundle Status is incorrect.") } } // Test on verifying ubiquitous messaging in Bundle.Status. func TestUbiquitousBundle(t *testing.T) { L1Cert := readCert(interL1) // Simulate the case that L1Cert is added to trust store by one platform but not yet in another. b := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, testCFSSLIntBundle, "") b.RootPool.AddCert(L1Cert) // Prepare Platforms. platformA := ubiquity.Platform{Name: "MacroSoft", Weight: 100, HashAlgo: "SHA2", KeyAlgo: "ECDSA256", KeyStoreFile: testCFSSLRootBundle} platformA.ParseAndLoad() platformB := ubiquity.Platform{Name: "Godzilla", Weight: 100, HashAlgo: "SHA2", KeyAlgo: "ECDSA256", KeyStoreFile: testCFSSLRootBundle} platformB.ParseAndLoad() platformA.KeyStore.Add(L1Cert) ubiquity.Platforms = []ubiquity.Platform{platformA, platformB} // Optimal bundle algorithm will picks up the new root and shorten the chain. optimalBundle, err := b.BundleFromFile(leafECDSA256, "", Optimal, "") if err != nil { t.Fatal("Optimal bundle failed:", err) } if len(optimalBundle.Chain) != 2 { t.Fatal("Optimal bundle failed the chain length test. Chain length:", len(optimalBundle.Chain)) } // The only trust platform is "Macrosoft". if len(optimalBundle.Status.Untrusted) != 1 { t.Fatal("Optimal bundle status has incorrect untrusted platforms", optimalBundle.Status.Untrusted) } checkUbiquityWarningAndCode(t, optimalBundle, true) // Ubiquitous bundle will remain the same. ubiquitousBundle, err := b.BundleFromFile(leafECDSA256, "", Ubiquitous, "") if err != nil { t.Fatal("Ubiquitous bundle failed") } if len(ubiquitousBundle.Chain) != 3 { t.Fatal("Ubiquitous bundle failed") } // Should be trusted by both platforms. if len(ubiquitousBundle.Status.Untrusted) != 0 { t.Fatal("Ubiquitous bundle status has incorrect untrusted platforms", len(ubiquitousBundle.Status.Untrusted)) } checkUbiquityWarningAndCode(t, ubiquitousBundle, false) } func TestUbiquityBundleWithoutMetadata(t *testing.T) { b := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, testCFSSLIntBundle, "") L1Cert := readCert(interL1) b.RootPool.AddCert(L1Cert) // Without platform info, ubiquitous bundling falls back to optimal bundling. ubiquity.Platforms = nil nuBundle, err := b.BundleFromFile(leafECDSA256, "", Ubiquitous, "") if err != nil { t.Fatal("Ubiquitous-fall-back-to-optimal bundle failed: ", err) } if len(nuBundle.Chain) != 2 { t.Fatal("Ubiquitous-fall-back-to-optimal bundle failed") } // Should be trusted by all (i.e. zero) platforms. if len(nuBundle.Status.Untrusted) != 0 { t.Fatal("Ubiquitous-fall-back-to-optimal bundle status has incorrect untrusted platforms", len(nuBundle.Status.Untrusted)) } checkUbiquityWarningAndCode(t, nuBundle, true) } func checkUbiquityWarningAndCode(t *testing.T, bundle *Bundle, expected bool) { found := false for _, msg := range bundle.Status.Messages { if strings.Contains(msg, untrustedWarningStub) || strings.Contains(msg, ubiquityWarning) { found = true } } if found != expected { t.Fatal("Expected ubiquity warning: ", expected, " Found ubiquity warning:", found) } // check status code if expected && bundle.Status.Code&errors.BundleNotUbiquitousBit == 0 { t.Fatal("Bundle status doesn't set BundleNotUbiquitousBit :", bundle.Status.Code) } } // Regression test on bundle with all flavors: // Ubiquitous bundle optimizes bundle length given the platform ubiquity is the same; Force bundle // with return the same bundle; Optimal bundle always chooses shortest bundle length. func TestForceBundle(t *testing.T) { // create a CA signer and signs a new intermediate with SHA-2 caSigner := makeCASignerFromFile(testCAFile, testCAKeyFile, x509.SHA256WithRSA, t) interL1Bytes := signCSRFile(caSigner, interL1CSR, t) // create a inter L1 signer interL1KeyBytes, err := ioutil.ReadFile(interL1Key) if err != nil { t.Fatal(err) } interL1Signer := makeCASigner(interL1Bytes, interL1KeyBytes, x509.SHA256WithRSA, t) // sign a level 2 intermediate interL2Bytes := signCSRFile(interL1Signer, interL2CSR, t) // create a inter L2 signer interL2KeyBytes, err := ioutil.ReadFile(interL2Key) if err != nil { t.Fatal(err) } interL2Signer := makeCASigner(interL2Bytes, interL2KeyBytes, x509.ECDSAWithSHA256, t) // interL2 sign a leaf cert leafBytes := signCSRFile(interL2Signer, leafCSR, t) // create two platforms // both trust the CA cert and L1 intermediate caBytes, err := ioutil.ReadFile(testCAFile) if err != nil { t.Fatal(err) } ca, _ := helpers.ParseCertificatePEM(caBytes) interL1, _ := helpers.ParseCertificatePEM(interL1Bytes) platformA := ubiquity.Platform{ Name: "A", Weight: 100, KeyStore: make(ubiquity.CertSet), HashUbiquity: ubiquity.SHA2Ubiquity, KeyAlgoUbiquity: ubiquity.ECDSA521Ubiquity, } platformB := ubiquity.Platform{ Name: "B", Weight: 100, KeyStore: make(ubiquity.CertSet), HashUbiquity: ubiquity.SHA2Ubiquity, KeyAlgoUbiquity: ubiquity.ECDSA521Ubiquity, } platformA.KeyStore.Add(ca) platformA.KeyStore.Add(interL1) platformB.KeyStore.Add(ca) platformB.KeyStore.Add(interL1) ubiquity.Platforms = []ubiquity.Platform{platformA, platformB} caBundle := string(caBytes) + string(interL1Bytes) interBundle := string(interL2Bytes) + string(interL1Bytes) fullChain := string(leafBytes) + string(interL2Bytes) + string(interL1Bytes) // create bundler b, err := NewBundlerFromPEM([]byte(caBundle), []byte(interBundle)) if err != nil { t.Fatal(err) } // The input PEM bundle is 3-cert chain. bundle, err := b.BundleFromPEMorDER([]byte(fullChain), nil, Force, "") if err != nil { t.Fatal("Force bundle failed:", err) } if len(bundle.Chain) != 3 { t.Fatal("Force bundle failed:") } if len(bundle.Status.Untrusted) != 0 { t.Fatal("Force bundle failed:") } // With ubiquity flavor, we should have a shorter chain, given L1 is ubiquitous trusted. bundle, err = b.BundleFromPEMorDER([]byte(fullChain), nil, Ubiquitous, "") if err != nil { t.Fatal("Ubiquitous bundle failed:", err) } if len(bundle.Chain) != 2 { t.Fatal("Ubiquitous bundle failed:") } if len(bundle.Status.Untrusted) != 0 { t.Fatal("Ubiquitous bundle failed:") } // With optimal flavor, we should have a shorter chain as well. bundle, err = b.BundleFromPEMorDER([]byte(fullChain), nil, Optimal, "") if err != nil { t.Fatal("Optimal bundle failed:", err) } if len(bundle.Chain) != 2 { t.Fatal("Optimal bundle failed:") } if len(bundle.Status.Untrusted) != 0 { t.Fatal("Optimal bundle failed:") } } func TestUpdateIntermediate(t *testing.T) { // create a CA signer and signs a new intermediate with SHA-2 caSigner := makeCASignerFromFile(testCAFile, testCAKeyFile, x509.SHA256WithRSA, t) sha2InterBytes := signCSRFile(caSigner, interL1CSR, t) interKeyBytes, err := ioutil.ReadFile(interL1Key) if err != nil { t.Fatal(err) } // create a intermediate signer from intermediate cert/key sha2InterSigner := makeCASigner(sha2InterBytes, interKeyBytes, x509.SHA256WithRSA, t) // sign a leaf cert leafBytes := signCSRFile(sha2InterSigner, leafCSR, t) // read CA cert bytes caCertBytes, err := ioutil.ReadFile(testCAFile) if err != nil { t.Fatal(err) } // create a bundler with the test root CA and no intermediates b, err := NewBundlerFromPEM(caCertBytes, nil) if err != nil { t.Fatal(err) } // create a cert bundle: leaf + inter chainBytes := string(leafBytes) + string(sha2InterBytes) bundle, err := b.BundleFromPEMorDER([]byte(chainBytes), nil, Ubiquitous, "") if err != nil { t.Fatal("Valid bundle should be accepted. error:", err) } if bundle.Status.IsRebundled { t.Fatal("rebundle should never happen here", bundle.Status) } // Now bundle with the leaf cert bundle2, err := b.BundleFromPEMorDER(leafBytes, nil, Ubiquitous, "") if err != nil { t.Fatal("Valid bundle should be accepted. error:", err) } if !bundle2.Status.IsRebundled { t.Fatal("rebundle should happen here") } } func TestForceBundleNoFallback(t *testing.T) { // create a CA signer and signs a new intermediate with SHA-2 caSigner := makeCASignerFromFile(testCAFile, testCAKeyFile, x509.SHA256WithRSA, t) sha2InterBytes := signCSRFile(caSigner, interL1CSR, t) interKeyBytes, err := ioutil.ReadFile(interL1Key) if err != nil { t.Fatal(err) } // create a intermediate signer from intermediate cert/key sha2InterSigner := makeCASigner(sha2InterBytes, interKeyBytes, x509.SHA256WithRSA, t) // sign a leaf cert leafBytes := signCSRFile(sha2InterSigner, leafCSR, t) // read CA cert bytes caCertBytes, err := ioutil.ReadFile(testCAFile) if err != nil { t.Fatal(err) } // create a bundler with the test root CA and the new intermediate b, err := NewBundlerFromPEM(caCertBytes, sha2InterBytes) if err != nil { t.Fatal(err) } // Now bundle with the leaf cert with Force bundle, err := b.BundleFromPEMorDER(leafBytes, nil, Force, "") if err != nil { t.Fatal("Valid bundle should be generated, error:", err) } // Force bundle fallback to creating a valid bundle if len(bundle.Chain) != 1 { t.Fatal("incorrect bundling") } if bundle.Status.IsRebundled { t.Fatal("rebundle should happen here") } } // Regression test: ubiquity bundle test with SHA2-homogeneous preference should not override root ubiquity. func TestSHA2HomogeneityAgainstUbiquity(t *testing.T) { // create a CA signer and signs a new intermediate with SHA-1 caSigner := makeCASignerFromFile(testCAFile, testCAKeyFile, x509.SHA1WithRSA, t) interL1Bytes := signCSRFile(caSigner, interL1CSR, t) // create a inter L1 signer interL1KeyBytes, err := ioutil.ReadFile(interL1Key) if err != nil { t.Fatal(err) } interL1Signer := makeCASigner(interL1Bytes, interL1KeyBytes, x509.SHA256WithRSA, t) // sign a level 2 intermediate interL2Bytes := signCSRFile(interL1Signer, interL2CSR, t) // create a inter L2 signer interL2KeyBytes, err := ioutil.ReadFile(interL2Key) if err != nil { t.Fatal(err) } interL2Signer := makeCASigner(interL2Bytes, interL2KeyBytes, x509.ECDSAWithSHA256, t) // interL2 sign a leaf cert leafBytes := signCSRFile(interL2Signer, leafCSR, t) // create two platforms // platform A trusts the CA cert and L1 intermediate // platform B trusts the CA cert caBytes, err := ioutil.ReadFile(testCAFile) if err != nil { t.Fatal(err) } ca, _ := helpers.ParseCertificatePEM(caBytes) interL1, _ := helpers.ParseCertificatePEM(interL1Bytes) platformA := ubiquity.Platform{ Name: "A", Weight: 100, KeyStore: make(ubiquity.CertSet), HashUbiquity: ubiquity.SHA2Ubiquity, KeyAlgoUbiquity: ubiquity.ECDSA521Ubiquity, } platformB := ubiquity.Platform{ Name: "B", Weight: 100, KeyStore: make(ubiquity.CertSet), HashUbiquity: ubiquity.SHA2Ubiquity, KeyAlgoUbiquity: ubiquity.ECDSA521Ubiquity, } platformA.KeyStore.Add(ca) platformA.KeyStore.Add(interL1) platformB.KeyStore.Add(ca) ubiquity.Platforms = []ubiquity.Platform{platformA, platformB} caBundle := string(caBytes) + string(interL1Bytes) interBundle := string(interL2Bytes) + string(interL1Bytes) fullChain := string(leafBytes) + string(interL2Bytes) + string(interL1Bytes) // create bundler b, err := NewBundlerFromPEM([]byte(caBundle), []byte(interBundle)) if err != nil { t.Fatal(err) } // The input PEM bundle is 3-cert chain. bundle, err := b.BundleFromPEMorDER([]byte(fullChain), nil, Force, "") if err != nil { t.Fatal("Force bundle failed:", err) } if len(bundle.Chain) != 3 { t.Fatal("Force bundle failed:") } if len(bundle.Status.Untrusted) != 0 { t.Fatal("Force bundle failed:") } // With ubiquity flavor, we should not sacrifice trust store ubiquity and rebundle with a shorter chain // with SHA2 homogenity. bundle, err = b.BundleFromPEMorDER([]byte(fullChain), nil, Ubiquitous, "") if err != nil { t.Fatal("Ubiquitous bundle failed:", err) } if len(bundle.Chain) != 3 { t.Fatal("Ubiquitous bundle failed:") } if len(bundle.Status.Untrusted) != 0 { t.Fatal("Ubiquitous bundle failed:") } // With optimal flavor, we should have a shorter chain. bundle, err = b.BundleFromPEMorDER([]byte(fullChain), nil, Optimal, "") if err != nil { t.Fatal("Optimal bundle failed:", err) } if len(bundle.Chain) != 2 { t.Fatal("Optimal bundle failed:") } if len(bundle.Status.Untrusted) == 0 { t.Fatal("Optimal bundle failed:") } } func checkSHA2WarningAndCode(t *testing.T, bundle *Bundle, expected bool) { found := false for _, msg := range bundle.Status.Messages { if strings.Contains(msg, sha2Warning) { found = true } } if found != expected { t.Fatal("Expected ubiquity warning: ", expected, " Found ubiquity warning:", found) } // check status code if bundle.Status.Code&errors.BundleNotUbiquitousBit == 0 { t.Fatal("Bundle status code is incorrect:", bundle.Status.Code) } } func checkECDSAWarningAndCode(t *testing.T, bundle *Bundle, expected bool) { found := false for _, msg := range bundle.Status.Messages { if strings.Contains(msg, ecdsaWarning) { found = true } } if found != expected { t.Fatal("Expected ubiquity warning: ", expected, " Found ubiquity warning:", found) } // check status code if bundle.Status.Code&errors.BundleNotUbiquitousBit == 0 { t.Fatal("Bundle status code is incorrect:", bundle.Status.Code) } } // Regression test on SHA-2 Warning // Riot Games once bundle a cert issued by DigiCert SHA2 High Assurance Server CA. The resulting // bundle uses SHA-256 which is not supported in Windows XP SP2. We should present a warning // on this. func TestSHA2Warning(t *testing.T) { // create a CA signer and signs a new intermediate with SHA-2 caSigner := makeCASignerFromFile(testCAFile, testCAKeyFile, x509.SHA256WithRSA, t) sha2InterBytes := signCSRFile(caSigner, interL1CSR, t) // read CA cert bytes caCertBytes, err := ioutil.ReadFile(testCAFile) if err != nil { t.Fatal(err) } // create a bundler with the test root CA and no intermediates b, err := NewBundlerFromPEM(caCertBytes, nil) if err != nil { t.Fatal(err) } optimalBundle, err := b.BundleFromPEMorDER(sha2InterBytes, nil, Optimal, "") if err != nil { t.Fatal("Optimal bundle failed:", err) } checkSHA2WarningAndCode(t, optimalBundle, true) // Ubiquitous bundle will include a 2nd intermediate CA. ubiquitousBundle, err := b.BundleFromPEMorDER(sha2InterBytes, nil, Ubiquitous, "") if err != nil { t.Fatal("Ubiquitous bundle failed") } checkSHA2WarningAndCode(t, ubiquitousBundle, true) } // Regression test on ECDSA Warning // A test bundle that contains ECDSA384 and SHA-2. Expect ECDSA warning and SHA-2 warning. func TestECDSAWarning(t *testing.T) { b := newCustomizedBundlerFromFile(t, testCAFile, interL1SHA1, "") optimalBundle, err := b.BundleFromFile(interL2SHA2, "", Optimal, "") if err != nil { t.Fatal("Optimal bundle failed:", err) } checkSHA2WarningAndCode(t, optimalBundle, true) checkECDSAWarningAndCode(t, optimalBundle, true) } // === Helper function block === // readCert read a PEM file and returns a cert. func readCert(filename string) *x509.Certificate { bytes, _ := ioutil.ReadFile(filename) cert, _ := helpers.ParseCertificatePEM(bytes) return cert } // newBundler is a helper function that returns a new Bundler. If it fails to do so, // it fails the test suite immediately. func newBundler(t *testing.T) (b *Bundler) { b, err := NewBundler(testCaBundle, testIntCaBundle) if err != nil { t.Fatal(err) } return } // newBundler creates bundler from byte slices of CA certs and intermediate certs in PEM format func newBundlerFromPEM(t *testing.T, caBundlePEM, intBundlePEM []byte) (b *Bundler) { b, err := NewBundlerFromPEM(caBundlePEM, intBundlePEM) if err != nil { t.Fatal(err) } return } // newCustomizedBundleCreator is a helper function that returns a new Bundler // takes specified CA bundle, intermediate bundle, and any additional intermdiate certs to generate a bundler. func newCustomizedBundlerFromFile(t *testing.T, caBundle, intBundle, adhocInters string) (b *Bundler) { b, err := NewBundler(caBundle, intBundle) if err != nil { t.Fatal(err) } if adhocInters != "" { moreIntersPEM, err := ioutil.ReadFile(adhocInters) if err != nil { t.Fatalf("Read additional intermediates failed. %v", err) } intermediates, err := helpers.ParseCertificatesPEM(moreIntersPEM) if err != nil { t.Fatalf("Parsing additional intermediates failed. %s", err.Error()) } for _, c := range intermediates { b.IntermediatePool.AddCert(c) } } return } // newBundlerWithoutInters is a helper function that returns a bundler with an empty // intermediate cert pool. Such bundlers can help testing error handling in cert // bundling. func newBundlerWithoutInters(t *testing.T) (b *Bundler) { b = newBundler(t) // Re-assign an empty intermediate cert pool b.IntermediatePool = x509.NewCertPool() return } // newBundlerWithoutRoots is a helper function that returns a bundler with an empty // root cert pool. Such bundlers can help testing error handling in cert // bundling. func newBundlerWithoutRoots(t *testing.T) (b *Bundler) { b = newBundler(t) // Re-assign an empty root cert pool b.RootPool = x509.NewCertPool() return } func newBundlerWithoutRootsAndInters(t *testing.T) *Bundler { b, err := NewBundler("", "") if err != nil { t.Fatal(err) } return b } // A helper function that returns a errorCallback function which expects certain error content in // an error message. func ExpectErrorMessage(expectedErrorContent string) func(*testing.T, error) { return func(t *testing.T, err error) { if err == nil { t.Fatalf("Expected error has %s. Got nothing.", expectedErrorContent) } else if !strings.Contains(err.Error(), expectedErrorContent) { t.Fatalf("Expected error has %s. Got %s", expectedErrorContent, err.Error()) } } } // A helper function that returns a errorCallback function which inspect error message for // all expected messages. func ExpectErrorMessages(expectedContents []string) func(*testing.T, error) { return func(t *testing.T, err error) { if err == nil { t.Fatalf("Expected error has %s. Got nothing.", expectedContents) } else { for _, expected := range expectedContents { if !strings.Contains(err.Error(), expected) { t.Fatalf("Expected error has %s. Got %s", expected, err.Error()) } } } } } // A helper function that returns a bundle chain length checking function func ExpectBundleLength(expectedLen int) func(*testing.T, *Bundle) { return func(t *testing.T, bundle *Bundle) { if bundle == nil { t.Fatalf("Cert bundle should have a chain of length %d. Got nil.", expectedLen) } else if len(bundle.Chain) != expectedLen { t.Fatalf("Cert bundle should have a chain of length %d. Got chain length %d.", expectedLen, len(bundle.Chain)) } } } func TestBundlerWithEmptyRootInfo(t *testing.T) { b := newBundlerWithoutRootsAndInters(t) // "force" bundle should be ok bundle, err := b.BundleFromPEMorDER(GoDaddyIntermediateCert, nil, Force, "") if err != nil { t.Fatal(err) } checkBundleFunc := ExpectBundleLength(1) checkBundleFunc(t, bundle) // force non-verifying bundle should fail. _, err = b.BundleFromFile(badBundle, "", Force, "") if err == nil { t.Fatal("expected error. but no error occurred") } checkErrorFunc := ExpectErrorMessage("\"code\":1200") checkErrorFunc(t, err) // "optimal" and "ubiquitous" bundle should be ok bundle, err = b.BundleFromPEMorDER(GoDaddyIntermediateCert, nil, Ubiquitous, "") if err != nil { t.Fatal(err) } checkBundleFunc = ExpectBundleLength(1) checkBundleFunc(t, bundle) bundle, err = b.BundleFromPEMorDER(GoDaddyIntermediateCert, nil, Optimal, "") if err != nil { t.Fatal(err) } checkBundleFunc = ExpectBundleLength(1) checkBundleFunc(t, bundle) // bundle remote should be ok bundle, err = b.BundleFromRemote("www.google.com", "", Ubiquitous) if err != nil { t.Fatal(err) } checkBundleFunc = ExpectBundleLength(2) checkBundleFunc(t, bundle) } func TestBundlerClientAuth(t *testing.T) { b, err := NewBundler("testdata/client-auth/root.pem", "testdata/client-auth/int.pem") if err != nil { t.Fatal(err) } for _, leafFile := range []string{"testdata/client-auth/leaf-server.pem", "testdata/client-auth/leaf-client.pem"} { if _, err := b.BundleFromFile(leafFile, "", Optimal, ""); err != nil { t.Fatal(err) } } }