123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930 |
- package bundler
- // This test file contains mostly tests on checking Bundle.Status when bundling under different circumstances.
- import (
- "bytes"
- "crypto/x509"
- "encoding/json"
- "os"
- "strings"
- "testing"
- "github.com/cloudflare/cfssl/errors"
- "github.com/cloudflare/cfssl/helpers"
- "github.com/cloudflare/cfssl/ubiquity"
- )
- const (
- // from https://github.com/cloudflare/cfssl_trust/blob/master/ca-bundle.crt
- testCaBundle = "testdata/ca-bundle.pem"
- // from https://github.com/cloudflare/cfssl_trust/blob/master/int-bundle.crt
- 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, _ := os.ReadFile(leafKeyECDSA256)
- keyBytes = bytes.Trim(keyBytes, " \n")
- if key != string(keyBytes) {
- t.Fatal("key is not recovered.")
- }
- cert := obj["crt"].(string)
- certBytes, _ := os.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, _ := os.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, _ := os.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) {
- t.Skip("expired cert https://github.com/cloudflare/cfssl/issues/1237")
- 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 := os.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 := os.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 := os.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 := os.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 := os.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 := os.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 := os.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 := os.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 := os.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 := os.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 := os.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, _ := os.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 intermediate 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 := os.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) {
- t.Skip("broken relating to https://github.com/cloudflare/cfssl/issues/1230")
- 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) {
- t.Skip("expired cert https://github.com/cloudflare/cfssl/issues/1237")
- 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)
- }
- }
- }
|