bundler_test.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  1. package bundler
  2. // This test file contains mostly tests on checking Bundle.Status when bundling under different circumstances.
  3. import (
  4. "bytes"
  5. "crypto/x509"
  6. "encoding/json"
  7. "os"
  8. "strings"
  9. "testing"
  10. "github.com/cloudflare/cfssl/errors"
  11. "github.com/cloudflare/cfssl/helpers"
  12. "github.com/cloudflare/cfssl/ubiquity"
  13. )
  14. const (
  15. // from https://github.com/cloudflare/cfssl_trust/blob/master/ca-bundle.crt
  16. testCaBundle = "testdata/ca-bundle.pem"
  17. // from https://github.com/cloudflare/cfssl_trust/blob/master/int-bundle.crt
  18. testIntCaBundle = "testdata/int-bundle.pem"
  19. testNSSRootBundle = "testdata/nss.pem"
  20. testMetadata = "testdata/ca-bundle.crt.metadata"
  21. testCFSSLRootBundle = "testdata/ca.pem"
  22. testCAFile = "testdata/ca.pem"
  23. testCAKeyFile = "testdata/ca.key"
  24. testCFSSLIntBundle = "testdata/intermediates.crt"
  25. emptyPEM = "testdata/empty.pem"
  26. interL1SHA1 = "testdata/inter-L1-sha1.pem"
  27. interL1Key = "testdata/inter-L1.key"
  28. interL2SHA2 = "testdata/inter-L2.pem"
  29. interL2Key = "testdata/inter-L2.key"
  30. )
  31. // Simply create a bundler
  32. func TestNewBundler(t *testing.T) {
  33. newBundler(t)
  34. }
  35. func TestNewBundlerMissingCA(t *testing.T) {
  36. badFile := "testdata/no_such_file.pem"
  37. _, err := NewBundler(badFile, testIntCaBundle)
  38. if err == nil {
  39. t.Fatal("Should fail with error code 4001")
  40. }
  41. // generate a function checking error content
  42. errorCheck := ExpectErrorMessage(`"code":4001`)
  43. errorCheck(t, err)
  44. }
  45. func TestNewBundlerMissingIntermediate(t *testing.T) {
  46. badFile := "testdata/no_such_file.pem"
  47. _, err := NewBundler(testCaBundle, badFile)
  48. if err == nil {
  49. t.Fatal("Should fail with error code 3001")
  50. }
  51. // generate a function checking error content
  52. errorCheck := ExpectErrorMessage(`"code":3001`)
  53. errorCheck(t, err)
  54. }
  55. // JSON object of a bundle
  56. type bundleObject struct {
  57. Bundle string `json:"bundle"`
  58. Root string `json:"root"`
  59. Cert string `json:"crt"`
  60. Key string `json:"key"`
  61. KeyType string `json:"key_type"`
  62. KeySize int `json:"key_size"`
  63. Issuer string `json:"issuer"`
  64. Subject string `json:"subject"`
  65. Expires string `json:"expires"`
  66. Hostnames []string `json:"hostnames"`
  67. OCSPSupport bool `json:"ocsp_support"`
  68. CRLSupport bool `json:"crl_support"`
  69. OCSP []string `json:"ocsp"`
  70. Signature string `json:"signature"`
  71. Status BundleStatus
  72. }
  73. var godaddyIssuerString = `/Country=US/Organization=The Go Daddy Group, Inc./OrganizationalUnit=Go Daddy Class 2 Certification Authority`
  74. 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`
  75. // Test marshal to JSON
  76. // Also serves as a JSON format regression test.
  77. func TestBundleMarshalJSON(t *testing.T) {
  78. b := newBundler(t)
  79. bundle, _ := b.BundleFromPEMorDER(GoDaddyIntermediateCert, nil, Optimal, "")
  80. bytes, err := json.Marshal(bundle)
  81. if err != nil {
  82. t.Fatal(err)
  83. }
  84. var obj bundleObject
  85. err = json.Unmarshal(bytes, &obj)
  86. if err != nil {
  87. t.Fatal(err)
  88. }
  89. if obj.Bundle == "" {
  90. t.Fatal("bundle is empty.")
  91. }
  92. if obj.Bundle != string(GoDaddyIntermediateCert) {
  93. t.Fatal("bundle is incorrect:", obj.Bundle)
  94. }
  95. if obj.Key != "" {
  96. t.Fatal("key is not empty:", obj.Key)
  97. }
  98. if obj.Root != string(GoDaddyRootCert) {
  99. t.Fatal("Root is not recovered")
  100. }
  101. if obj.Cert != string(GoDaddyIntermediateCert) {
  102. t.Fatal("Cert is not recovered")
  103. }
  104. if obj.KeyType != "2048-bit RSA" {
  105. t.Fatal("Incorrect key type:", obj.KeyType)
  106. }
  107. if obj.KeySize != 2048 {
  108. t.Fatal("Incorrect key size:", obj.KeySize)
  109. }
  110. if obj.Issuer != godaddyIssuerString {
  111. t.Fatal("Incorrect issuer:", obj.Issuer)
  112. }
  113. if obj.Subject != godaddySubjectString {
  114. t.Fatal("Incorrect subject:", obj.Subject)
  115. }
  116. if obj.Expires != "2026-11-16T01:54:37Z" {
  117. t.Fatal("Incorrect expiration time:", obj.Expires)
  118. }
  119. if len(obj.Hostnames) != 1 || obj.Hostnames[0] != "Go Daddy Secure Certification Authority" {
  120. t.Fatal("Incorrect hostnames:", obj.Hostnames)
  121. }
  122. if obj.OCSPSupport != true {
  123. t.Fatal("Incorrect OCSP support flag:", obj.OCSPSupport)
  124. }
  125. if obj.CRLSupport != true {
  126. t.Fatal("Incorrect CRL support flag:", obj.CRLSupport)
  127. }
  128. if len(obj.OCSP) != 1 || obj.OCSP[0] != `http://ocsp.godaddy.com` {
  129. t.Fatal("Incorrect ocsp server list:", obj.OCSP)
  130. }
  131. if obj.Signature != "SHA1WithRSA" {
  132. t.Fatal("Incorrect cert signature method:", obj.Signature)
  133. }
  134. }
  135. func TestBundleWithECDSAKeyMarshalJSON(t *testing.T) {
  136. b := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, testCFSSLIntBundle, "")
  137. bundle, _ := b.BundleFromFile(leafECDSA256, leafKeyECDSA256, Optimal, "")
  138. jsonBytes, err := json.Marshal(bundle)
  139. if err != nil {
  140. t.Fatal(err)
  141. }
  142. var obj map[string]interface{}
  143. err = json.Unmarshal(jsonBytes, &obj)
  144. if err != nil {
  145. t.Fatal(err)
  146. }
  147. key := obj["key"].(string)
  148. keyBytes, _ := os.ReadFile(leafKeyECDSA256)
  149. keyBytes = bytes.Trim(keyBytes, " \n")
  150. if key != string(keyBytes) {
  151. t.Fatal("key is not recovered.")
  152. }
  153. cert := obj["crt"].(string)
  154. certBytes, _ := os.ReadFile(leafECDSA256)
  155. certBytes = bytes.Trim(certBytes, " \n")
  156. if cert != string(certBytes) {
  157. t.Fatal("cert is not recovered.")
  158. }
  159. keyType := obj["key_type"]
  160. if keyType != "256-bit ECDSA" {
  161. t.Fatal("Incorrect key type:", keyType)
  162. }
  163. }
  164. func TestBundleWithRSAKeyMarshalJSON(t *testing.T) {
  165. b := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, testCFSSLIntBundle, "")
  166. bundle, _ := b.BundleFromFile(leafRSA2048, leafKeyRSA2048, Optimal, "")
  167. jsonBytes, err := json.Marshal(bundle)
  168. if err != nil {
  169. t.Fatal(err)
  170. }
  171. var obj map[string]interface{}
  172. err = json.Unmarshal(jsonBytes, &obj)
  173. if err != nil {
  174. t.Fatal(err)
  175. }
  176. key := obj["key"].(string)
  177. keyBytes, _ := os.ReadFile(leafKeyRSA2048)
  178. keyBytes = bytes.Trim(keyBytes, " \n")
  179. if key != string(keyBytes) {
  180. t.Error("key is", key)
  181. t.Error("keyBytes is", string(keyBytes))
  182. t.Fatal("key is not recovered.")
  183. }
  184. cert := obj["crt"].(string)
  185. certBytes, _ := os.ReadFile(leafRSA2048)
  186. certBytes = bytes.Trim(certBytes, " \n")
  187. if cert != string(certBytes) {
  188. t.Fatal("cert is not recovered.")
  189. }
  190. keyType := obj["key_type"]
  191. if keyType != "2048-bit RSA" {
  192. t.Fatal("Incorrect key type:", keyType)
  193. }
  194. }
  195. // Test marshal to JSON on hostnames
  196. func TestBundleHostnamesMarshalJSON(t *testing.T) {
  197. b := newBundler(t)
  198. bundle, _ := b.BundleFromPEMorDER(GoDaddyIntermediateCert, nil, Optimal, "")
  199. expected := []byte(`["Go Daddy Secure Certification Authority"]`)
  200. hostnames, _ := json.Marshal(bundle.Hostnames)
  201. if !bytes.Equal(hostnames, expected) {
  202. t.Fatal("Hostnames construction failed for godaddy root cert.", string(hostnames))
  203. }
  204. }
  205. // Tests on verifying the rebundle flag and error code in Bundle.Status when rebundling.
  206. func TestRebundleFromPEM(t *testing.T) {
  207. t.Skip("expired cert https://github.com/cloudflare/cfssl/issues/1237")
  208. newBundler := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, interL1, "")
  209. newBundle, err := newBundler.BundleFromPEMorDER(expiredBundlePEM, nil, Optimal, "")
  210. if err != nil {
  211. t.Fatalf("Re-bundle failed. %s", err.Error())
  212. }
  213. newChain := newBundle.Chain
  214. if len(newChain) != 2 {
  215. t.Fatalf("Expected bundle chain length is 2. Got %d.", len(newChain))
  216. }
  217. expiredChain, _ := helpers.ParseCertificatesPEM(expiredBundlePEM)
  218. for i, cert := range newChain {
  219. old := expiredChain[i]
  220. if i == 0 {
  221. if !bytes.Equal(old.Signature, cert.Signature) {
  222. t.Fatal("Leaf cert should be the same.")
  223. }
  224. } else {
  225. if bytes.Equal(old.Signature, cert.Signature) {
  226. t.Fatal("Intermediate cert should be different.")
  227. }
  228. }
  229. }
  230. // The status must be {Code: ExpiringBit is not set, IsRebundled:true, ExpiringSKIs:{}}
  231. if len(newBundle.Status.ExpiringSKIs) != 0 || !newBundle.Status.IsRebundled || newBundle.Status.Code&errors.BundleExpiringBit != 0 {
  232. t.Fatal("Rebundle Status is incorrect.")
  233. }
  234. }
  235. // Test on verifying ubiquitous messaging in Bundle.Status.
  236. func TestUbiquitousBundle(t *testing.T) {
  237. L1Cert := readCert(interL1)
  238. // Simulate the case that L1Cert is added to trust store by one platform but not yet in another.
  239. b := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, testCFSSLIntBundle, "")
  240. b.RootPool.AddCert(L1Cert)
  241. // Prepare Platforms.
  242. platformA := ubiquity.Platform{Name: "MacroSoft", Weight: 100, HashAlgo: "SHA2", KeyAlgo: "ECDSA256", KeyStoreFile: testCFSSLRootBundle}
  243. platformA.ParseAndLoad()
  244. platformB := ubiquity.Platform{Name: "Godzilla", Weight: 100, HashAlgo: "SHA2", KeyAlgo: "ECDSA256", KeyStoreFile: testCFSSLRootBundle}
  245. platformB.ParseAndLoad()
  246. platformA.KeyStore.Add(L1Cert)
  247. ubiquity.Platforms = []ubiquity.Platform{platformA, platformB}
  248. // Optimal bundle algorithm will picks up the new root and shorten the chain.
  249. optimalBundle, err := b.BundleFromFile(leafECDSA256, "", Optimal, "")
  250. if err != nil {
  251. t.Fatal("Optimal bundle failed:", err)
  252. }
  253. if len(optimalBundle.Chain) != 2 {
  254. t.Fatal("Optimal bundle failed the chain length test. Chain length:", len(optimalBundle.Chain))
  255. }
  256. // The only trust platform is "Macrosoft".
  257. if len(optimalBundle.Status.Untrusted) != 1 {
  258. t.Fatal("Optimal bundle status has incorrect untrusted platforms", optimalBundle.Status.Untrusted)
  259. }
  260. checkUbiquityWarningAndCode(t, optimalBundle, true)
  261. // Ubiquitous bundle will remain the same.
  262. ubiquitousBundle, err := b.BundleFromFile(leafECDSA256, "", Ubiquitous, "")
  263. if err != nil {
  264. t.Fatal("Ubiquitous bundle failed")
  265. }
  266. if len(ubiquitousBundle.Chain) != 3 {
  267. t.Fatal("Ubiquitous bundle failed")
  268. }
  269. // Should be trusted by both platforms.
  270. if len(ubiquitousBundle.Status.Untrusted) != 0 {
  271. t.Fatal("Ubiquitous bundle status has incorrect untrusted platforms", len(ubiquitousBundle.Status.Untrusted))
  272. }
  273. checkUbiquityWarningAndCode(t, ubiquitousBundle, false)
  274. }
  275. func TestUbiquityBundleWithoutMetadata(t *testing.T) {
  276. b := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, testCFSSLIntBundle, "")
  277. L1Cert := readCert(interL1)
  278. b.RootPool.AddCert(L1Cert)
  279. // Without platform info, ubiquitous bundling falls back to optimal bundling.
  280. ubiquity.Platforms = nil
  281. nuBundle, err := b.BundleFromFile(leafECDSA256, "", Ubiquitous, "")
  282. if err != nil {
  283. t.Fatal("Ubiquitous-fall-back-to-optimal bundle failed: ", err)
  284. }
  285. if len(nuBundle.Chain) != 2 {
  286. t.Fatal("Ubiquitous-fall-back-to-optimal bundle failed")
  287. }
  288. // Should be trusted by all (i.e. zero) platforms.
  289. if len(nuBundle.Status.Untrusted) != 0 {
  290. t.Fatal("Ubiquitous-fall-back-to-optimal bundle status has incorrect untrusted platforms", len(nuBundle.Status.Untrusted))
  291. }
  292. checkUbiquityWarningAndCode(t, nuBundle, true)
  293. }
  294. func checkUbiquityWarningAndCode(t *testing.T, bundle *Bundle, expected bool) {
  295. found := false
  296. for _, msg := range bundle.Status.Messages {
  297. if strings.Contains(msg, untrustedWarningStub) || strings.Contains(msg, ubiquityWarning) {
  298. found = true
  299. }
  300. }
  301. if found != expected {
  302. t.Fatal("Expected ubiquity warning: ", expected, " Found ubiquity warning:", found)
  303. }
  304. // check status code
  305. if expected && bundle.Status.Code&errors.BundleNotUbiquitousBit == 0 {
  306. t.Fatal("Bundle status doesn't set BundleNotUbiquitousBit :", bundle.Status.Code)
  307. }
  308. }
  309. // Regression test on bundle with all flavors:
  310. // Ubiquitous bundle optimizes bundle length given the platform ubiquity is the same; Force bundle
  311. // with return the same bundle; Optimal bundle always chooses shortest bundle length.
  312. func TestForceBundle(t *testing.T) {
  313. // create a CA signer and signs a new intermediate with SHA-2
  314. caSigner := makeCASignerFromFile(testCAFile, testCAKeyFile, x509.SHA256WithRSA, t)
  315. interL1Bytes := signCSRFile(caSigner, interL1CSR, t)
  316. // create a inter L1 signer
  317. interL1KeyBytes, err := os.ReadFile(interL1Key)
  318. if err != nil {
  319. t.Fatal(err)
  320. }
  321. interL1Signer := makeCASigner(interL1Bytes, interL1KeyBytes, x509.SHA256WithRSA, t)
  322. // sign a level 2 intermediate
  323. interL2Bytes := signCSRFile(interL1Signer, interL2CSR, t)
  324. // create a inter L2 signer
  325. interL2KeyBytes, err := os.ReadFile(interL2Key)
  326. if err != nil {
  327. t.Fatal(err)
  328. }
  329. interL2Signer := makeCASigner(interL2Bytes, interL2KeyBytes, x509.ECDSAWithSHA256, t)
  330. // interL2 sign a leaf cert
  331. leafBytes := signCSRFile(interL2Signer, leafCSR, t)
  332. // create two platforms
  333. // both trust the CA cert and L1 intermediate
  334. caBytes, err := os.ReadFile(testCAFile)
  335. if err != nil {
  336. t.Fatal(err)
  337. }
  338. ca, _ := helpers.ParseCertificatePEM(caBytes)
  339. interL1, _ := helpers.ParseCertificatePEM(interL1Bytes)
  340. platformA := ubiquity.Platform{
  341. Name: "A",
  342. Weight: 100,
  343. KeyStore: make(ubiquity.CertSet),
  344. HashUbiquity: ubiquity.SHA2Ubiquity,
  345. KeyAlgoUbiquity: ubiquity.ECDSA521Ubiquity,
  346. }
  347. platformB := ubiquity.Platform{
  348. Name: "B",
  349. Weight: 100,
  350. KeyStore: make(ubiquity.CertSet),
  351. HashUbiquity: ubiquity.SHA2Ubiquity,
  352. KeyAlgoUbiquity: ubiquity.ECDSA521Ubiquity,
  353. }
  354. platformA.KeyStore.Add(ca)
  355. platformA.KeyStore.Add(interL1)
  356. platformB.KeyStore.Add(ca)
  357. platformB.KeyStore.Add(interL1)
  358. ubiquity.Platforms = []ubiquity.Platform{platformA, platformB}
  359. caBundle := string(caBytes) + string(interL1Bytes)
  360. interBundle := string(interL2Bytes) + string(interL1Bytes)
  361. fullChain := string(leafBytes) + string(interL2Bytes) + string(interL1Bytes)
  362. // create bundler
  363. b, err := NewBundlerFromPEM([]byte(caBundle), []byte(interBundle))
  364. if err != nil {
  365. t.Fatal(err)
  366. }
  367. // The input PEM bundle is 3-cert chain.
  368. bundle, err := b.BundleFromPEMorDER([]byte(fullChain), nil, Force, "")
  369. if err != nil {
  370. t.Fatal("Force bundle failed:", err)
  371. }
  372. if len(bundle.Chain) != 3 {
  373. t.Fatal("Force bundle failed:")
  374. }
  375. if len(bundle.Status.Untrusted) != 0 {
  376. t.Fatal("Force bundle failed:")
  377. }
  378. // With ubiquity flavor, we should have a shorter chain, given L1 is ubiquitous trusted.
  379. bundle, err = b.BundleFromPEMorDER([]byte(fullChain), nil, Ubiquitous, "")
  380. if err != nil {
  381. t.Fatal("Ubiquitous bundle failed:", err)
  382. }
  383. if len(bundle.Chain) != 2 {
  384. t.Fatal("Ubiquitous bundle failed:")
  385. }
  386. if len(bundle.Status.Untrusted) != 0 {
  387. t.Fatal("Ubiquitous bundle failed:")
  388. }
  389. // With optimal flavor, we should have a shorter chain as well.
  390. bundle, err = b.BundleFromPEMorDER([]byte(fullChain), nil, Optimal, "")
  391. if err != nil {
  392. t.Fatal("Optimal bundle failed:", err)
  393. }
  394. if len(bundle.Chain) != 2 {
  395. t.Fatal("Optimal bundle failed:")
  396. }
  397. if len(bundle.Status.Untrusted) != 0 {
  398. t.Fatal("Optimal bundle failed:")
  399. }
  400. }
  401. func TestUpdateIntermediate(t *testing.T) {
  402. // create a CA signer and signs a new intermediate with SHA-2
  403. caSigner := makeCASignerFromFile(testCAFile, testCAKeyFile, x509.SHA256WithRSA, t)
  404. sha2InterBytes := signCSRFile(caSigner, interL1CSR, t)
  405. interKeyBytes, err := os.ReadFile(interL1Key)
  406. if err != nil {
  407. t.Fatal(err)
  408. }
  409. // create a intermediate signer from intermediate cert/key
  410. sha2InterSigner := makeCASigner(sha2InterBytes, interKeyBytes, x509.SHA256WithRSA, t)
  411. // sign a leaf cert
  412. leafBytes := signCSRFile(sha2InterSigner, leafCSR, t)
  413. // read CA cert bytes
  414. caCertBytes, err := os.ReadFile(testCAFile)
  415. if err != nil {
  416. t.Fatal(err)
  417. }
  418. // create a bundler with the test root CA and no intermediates
  419. b, err := NewBundlerFromPEM(caCertBytes, nil)
  420. if err != nil {
  421. t.Fatal(err)
  422. }
  423. // create a cert bundle: leaf + inter
  424. chainBytes := string(leafBytes) + string(sha2InterBytes)
  425. bundle, err := b.BundleFromPEMorDER([]byte(chainBytes), nil, Ubiquitous, "")
  426. if err != nil {
  427. t.Fatal("Valid bundle should be accepted. error:", err)
  428. }
  429. if bundle.Status.IsRebundled {
  430. t.Fatal("rebundle should never happen here", bundle.Status)
  431. }
  432. // Now bundle with the leaf cert
  433. bundle2, err := b.BundleFromPEMorDER(leafBytes, nil, Ubiquitous, "")
  434. if err != nil {
  435. t.Fatal("Valid bundle should be accepted. error:", err)
  436. }
  437. if !bundle2.Status.IsRebundled {
  438. t.Fatal("rebundle should happen here")
  439. }
  440. }
  441. func TestForceBundleNoFallback(t *testing.T) {
  442. // create a CA signer and signs a new intermediate with SHA-2
  443. caSigner := makeCASignerFromFile(testCAFile, testCAKeyFile, x509.SHA256WithRSA, t)
  444. sha2InterBytes := signCSRFile(caSigner, interL1CSR, t)
  445. interKeyBytes, err := os.ReadFile(interL1Key)
  446. if err != nil {
  447. t.Fatal(err)
  448. }
  449. // create a intermediate signer from intermediate cert/key
  450. sha2InterSigner := makeCASigner(sha2InterBytes, interKeyBytes, x509.SHA256WithRSA, t)
  451. // sign a leaf cert
  452. leafBytes := signCSRFile(sha2InterSigner, leafCSR, t)
  453. // read CA cert bytes
  454. caCertBytes, err := os.ReadFile(testCAFile)
  455. if err != nil {
  456. t.Fatal(err)
  457. }
  458. // create a bundler with the test root CA and the new intermediate
  459. b, err := NewBundlerFromPEM(caCertBytes, sha2InterBytes)
  460. if err != nil {
  461. t.Fatal(err)
  462. }
  463. // Now bundle with the leaf cert with Force
  464. bundle, err := b.BundleFromPEMorDER(leafBytes, nil, Force, "")
  465. if err != nil {
  466. t.Fatal("Valid bundle should be generated, error:", err)
  467. }
  468. // Force bundle fallback to creating a valid bundle
  469. if len(bundle.Chain) != 1 {
  470. t.Fatal("incorrect bundling")
  471. }
  472. if bundle.Status.IsRebundled {
  473. t.Fatal("rebundle should happen here")
  474. }
  475. }
  476. // Regression test: ubiquity bundle test with SHA2-homogeneous preference should not override root ubiquity.
  477. func TestSHA2HomogeneityAgainstUbiquity(t *testing.T) {
  478. // create a CA signer and signs a new intermediate with SHA-1
  479. caSigner := makeCASignerFromFile(testCAFile, testCAKeyFile, x509.SHA1WithRSA, t)
  480. interL1Bytes := signCSRFile(caSigner, interL1CSR, t)
  481. // create a inter L1 signer
  482. interL1KeyBytes, err := os.ReadFile(interL1Key)
  483. if err != nil {
  484. t.Fatal(err)
  485. }
  486. interL1Signer := makeCASigner(interL1Bytes, interL1KeyBytes, x509.SHA256WithRSA, t)
  487. // sign a level 2 intermediate
  488. interL2Bytes := signCSRFile(interL1Signer, interL2CSR, t)
  489. // create a inter L2 signer
  490. interL2KeyBytes, err := os.ReadFile(interL2Key)
  491. if err != nil {
  492. t.Fatal(err)
  493. }
  494. interL2Signer := makeCASigner(interL2Bytes, interL2KeyBytes, x509.ECDSAWithSHA256, t)
  495. // interL2 sign a leaf cert
  496. leafBytes := signCSRFile(interL2Signer, leafCSR, t)
  497. // create two platforms
  498. // platform A trusts the CA cert and L1 intermediate
  499. // platform B trusts the CA cert
  500. caBytes, err := os.ReadFile(testCAFile)
  501. if err != nil {
  502. t.Fatal(err)
  503. }
  504. ca, _ := helpers.ParseCertificatePEM(caBytes)
  505. interL1, _ := helpers.ParseCertificatePEM(interL1Bytes)
  506. platformA := ubiquity.Platform{
  507. Name: "A",
  508. Weight: 100,
  509. KeyStore: make(ubiquity.CertSet),
  510. HashUbiquity: ubiquity.SHA2Ubiquity,
  511. KeyAlgoUbiquity: ubiquity.ECDSA521Ubiquity,
  512. }
  513. platformB := ubiquity.Platform{
  514. Name: "B",
  515. Weight: 100,
  516. KeyStore: make(ubiquity.CertSet),
  517. HashUbiquity: ubiquity.SHA2Ubiquity,
  518. KeyAlgoUbiquity: ubiquity.ECDSA521Ubiquity,
  519. }
  520. platformA.KeyStore.Add(ca)
  521. platformA.KeyStore.Add(interL1)
  522. platformB.KeyStore.Add(ca)
  523. ubiquity.Platforms = []ubiquity.Platform{platformA, platformB}
  524. caBundle := string(caBytes) + string(interL1Bytes)
  525. interBundle := string(interL2Bytes) + string(interL1Bytes)
  526. fullChain := string(leafBytes) + string(interL2Bytes) + string(interL1Bytes)
  527. // create bundler
  528. b, err := NewBundlerFromPEM([]byte(caBundle), []byte(interBundle))
  529. if err != nil {
  530. t.Fatal(err)
  531. }
  532. // The input PEM bundle is 3-cert chain.
  533. bundle, err := b.BundleFromPEMorDER([]byte(fullChain), nil, Force, "")
  534. if err != nil {
  535. t.Fatal("Force bundle failed:", err)
  536. }
  537. if len(bundle.Chain) != 3 {
  538. t.Fatal("Force bundle failed:")
  539. }
  540. if len(bundle.Status.Untrusted) != 0 {
  541. t.Fatal("Force bundle failed:")
  542. }
  543. // With ubiquity flavor, we should not sacrifice trust store ubiquity and rebundle with a shorter chain
  544. // with SHA2 homogenity.
  545. bundle, err = b.BundleFromPEMorDER([]byte(fullChain), nil, Ubiquitous, "")
  546. if err != nil {
  547. t.Fatal("Ubiquitous bundle failed:", err)
  548. }
  549. if len(bundle.Chain) != 3 {
  550. t.Fatal("Ubiquitous bundle failed:")
  551. }
  552. if len(bundle.Status.Untrusted) != 0 {
  553. t.Fatal("Ubiquitous bundle failed:")
  554. }
  555. // With optimal flavor, we should have a shorter chain.
  556. bundle, err = b.BundleFromPEMorDER([]byte(fullChain), nil, Optimal, "")
  557. if err != nil {
  558. t.Fatal("Optimal bundle failed:", err)
  559. }
  560. if len(bundle.Chain) != 2 {
  561. t.Fatal("Optimal bundle failed:")
  562. }
  563. if len(bundle.Status.Untrusted) == 0 {
  564. t.Fatal("Optimal bundle failed:")
  565. }
  566. }
  567. func checkSHA2WarningAndCode(t *testing.T, bundle *Bundle, expected bool) {
  568. found := false
  569. for _, msg := range bundle.Status.Messages {
  570. if strings.Contains(msg, sha2Warning) {
  571. found = true
  572. }
  573. }
  574. if found != expected {
  575. t.Fatal("Expected ubiquity warning: ", expected, " Found ubiquity warning:", found)
  576. }
  577. // check status code
  578. if bundle.Status.Code&errors.BundleNotUbiquitousBit == 0 {
  579. t.Fatal("Bundle status code is incorrect:", bundle.Status.Code)
  580. }
  581. }
  582. func checkECDSAWarningAndCode(t *testing.T, bundle *Bundle, expected bool) {
  583. found := false
  584. for _, msg := range bundle.Status.Messages {
  585. if strings.Contains(msg, ecdsaWarning) {
  586. found = true
  587. }
  588. }
  589. if found != expected {
  590. t.Fatal("Expected ubiquity warning: ", expected, " Found ubiquity warning:", found)
  591. }
  592. // check status code
  593. if bundle.Status.Code&errors.BundleNotUbiquitousBit == 0 {
  594. t.Fatal("Bundle status code is incorrect:", bundle.Status.Code)
  595. }
  596. }
  597. // Regression test on SHA-2 Warning
  598. // Riot Games once bundle a cert issued by DigiCert SHA2 High Assurance Server CA. The resulting
  599. // bundle uses SHA-256 which is not supported in Windows XP SP2. We should present a warning
  600. // on this.
  601. func TestSHA2Warning(t *testing.T) {
  602. // create a CA signer and signs a new intermediate with SHA-2
  603. caSigner := makeCASignerFromFile(testCAFile, testCAKeyFile, x509.SHA256WithRSA, t)
  604. sha2InterBytes := signCSRFile(caSigner, interL1CSR, t)
  605. // read CA cert bytes
  606. caCertBytes, err := os.ReadFile(testCAFile)
  607. if err != nil {
  608. t.Fatal(err)
  609. }
  610. // create a bundler with the test root CA and no intermediates
  611. b, err := NewBundlerFromPEM(caCertBytes, nil)
  612. if err != nil {
  613. t.Fatal(err)
  614. }
  615. optimalBundle, err := b.BundleFromPEMorDER(sha2InterBytes, nil, Optimal, "")
  616. if err != nil {
  617. t.Fatal("Optimal bundle failed:", err)
  618. }
  619. checkSHA2WarningAndCode(t, optimalBundle, true)
  620. // Ubiquitous bundle will include a 2nd intermediate CA.
  621. ubiquitousBundle, err := b.BundleFromPEMorDER(sha2InterBytes, nil, Ubiquitous, "")
  622. if err != nil {
  623. t.Fatal("Ubiquitous bundle failed")
  624. }
  625. checkSHA2WarningAndCode(t, ubiquitousBundle, true)
  626. }
  627. // Regression test on ECDSA Warning
  628. // A test bundle that contains ECDSA384 and SHA-2. Expect ECDSA warning and SHA-2 warning.
  629. func TestECDSAWarning(t *testing.T) {
  630. b := newCustomizedBundlerFromFile(t, testCAFile, interL1SHA1, "")
  631. optimalBundle, err := b.BundleFromFile(interL2SHA2, "", Optimal, "")
  632. if err != nil {
  633. t.Fatal("Optimal bundle failed:", err)
  634. }
  635. checkSHA2WarningAndCode(t, optimalBundle, true)
  636. checkECDSAWarningAndCode(t, optimalBundle, true)
  637. }
  638. // === Helper function block ===
  639. // readCert read a PEM file and returns a cert.
  640. func readCert(filename string) *x509.Certificate {
  641. bytes, _ := os.ReadFile(filename)
  642. cert, _ := helpers.ParseCertificatePEM(bytes)
  643. return cert
  644. }
  645. // newBundler is a helper function that returns a new Bundler. If it fails to do so,
  646. // it fails the test suite immediately.
  647. func newBundler(t *testing.T) (b *Bundler) {
  648. b, err := NewBundler(testCaBundle, testIntCaBundle)
  649. if err != nil {
  650. t.Fatal(err)
  651. }
  652. return
  653. }
  654. // newBundler creates bundler from byte slices of CA certs and intermediate certs in PEM format
  655. func newBundlerFromPEM(t *testing.T, caBundlePEM, intBundlePEM []byte) (b *Bundler) {
  656. b, err := NewBundlerFromPEM(caBundlePEM, intBundlePEM)
  657. if err != nil {
  658. t.Fatal(err)
  659. }
  660. return
  661. }
  662. // newCustomizedBundleCreator is a helper function that returns a new Bundler
  663. // takes specified CA bundle, intermediate bundle, and any additional intermediate certs to generate a bundler.
  664. func newCustomizedBundlerFromFile(t *testing.T, caBundle, intBundle, adhocInters string) (b *Bundler) {
  665. b, err := NewBundler(caBundle, intBundle)
  666. if err != nil {
  667. t.Fatal(err)
  668. }
  669. if adhocInters != "" {
  670. moreIntersPEM, err := os.ReadFile(adhocInters)
  671. if err != nil {
  672. t.Fatalf("Read additional intermediates failed. %v",
  673. err)
  674. }
  675. intermediates, err := helpers.ParseCertificatesPEM(moreIntersPEM)
  676. if err != nil {
  677. t.Fatalf("Parsing additional intermediates failed. %s", err.Error())
  678. }
  679. for _, c := range intermediates {
  680. b.IntermediatePool.AddCert(c)
  681. }
  682. }
  683. return
  684. }
  685. // newBundlerWithoutInters is a helper function that returns a bundler with an empty
  686. // intermediate cert pool. Such bundlers can help testing error handling in cert
  687. // bundling.
  688. func newBundlerWithoutInters(t *testing.T) (b *Bundler) {
  689. b = newBundler(t)
  690. // Re-assign an empty intermediate cert pool
  691. b.IntermediatePool = x509.NewCertPool()
  692. return
  693. }
  694. // newBundlerWithoutRoots is a helper function that returns a bundler with an empty
  695. // root cert pool. Such bundlers can help testing error handling in cert
  696. // bundling.
  697. func newBundlerWithoutRoots(t *testing.T) (b *Bundler) {
  698. b = newBundler(t)
  699. // Re-assign an empty root cert pool
  700. b.RootPool = x509.NewCertPool()
  701. return
  702. }
  703. func newBundlerWithoutRootsAndInters(t *testing.T) *Bundler {
  704. b, err := NewBundler("", "")
  705. if err != nil {
  706. t.Fatal(err)
  707. }
  708. return b
  709. }
  710. // A helper function that returns a errorCallback function which expects certain error content in
  711. // an error message.
  712. func ExpectErrorMessage(expectedErrorContent string) func(*testing.T, error) {
  713. return func(t *testing.T, err error) {
  714. if err == nil {
  715. t.Fatalf("Expected error has %s. Got nothing.", expectedErrorContent)
  716. } else if !strings.Contains(err.Error(), expectedErrorContent) {
  717. t.Fatalf("Expected error has %s. Got %s", expectedErrorContent, err.Error())
  718. }
  719. }
  720. }
  721. // A helper function that returns a errorCallback function which inspect error message for
  722. // all expected messages.
  723. func ExpectErrorMessages(expectedContents []string) func(*testing.T, error) {
  724. return func(t *testing.T, err error) {
  725. if err == nil {
  726. t.Fatalf("Expected error has %s. Got nothing.", expectedContents)
  727. } else {
  728. for _, expected := range expectedContents {
  729. if !strings.Contains(err.Error(), expected) {
  730. t.Fatalf("Expected error has %s. Got %s", expected, err.Error())
  731. }
  732. }
  733. }
  734. }
  735. }
  736. // A helper function that returns a bundle chain length checking function
  737. func ExpectBundleLength(expectedLen int) func(*testing.T, *Bundle) {
  738. return func(t *testing.T, bundle *Bundle) {
  739. if bundle == nil {
  740. t.Fatalf("Cert bundle should have a chain of length %d. Got nil.",
  741. expectedLen)
  742. } else if len(bundle.Chain) != expectedLen {
  743. t.Fatalf("Cert bundle should have a chain of length %d. Got chain length %d.",
  744. expectedLen, len(bundle.Chain))
  745. }
  746. }
  747. }
  748. func TestBundlerWithEmptyRootInfo(t *testing.T) {
  749. t.Skip("broken relating to https://github.com/cloudflare/cfssl/issues/1230")
  750. b := newBundlerWithoutRootsAndInters(t)
  751. // "force" bundle should be ok
  752. bundle, err := b.BundleFromPEMorDER(GoDaddyIntermediateCert, nil, Force, "")
  753. if err != nil {
  754. t.Fatal(err)
  755. }
  756. checkBundleFunc := ExpectBundleLength(1)
  757. checkBundleFunc(t, bundle)
  758. // force non-verifying bundle should fail.
  759. _, err = b.BundleFromFile(badBundle, "", Force, "")
  760. if err == nil {
  761. t.Fatal("expected error. but no error occurred")
  762. }
  763. checkErrorFunc := ExpectErrorMessage("\"code\":1200")
  764. checkErrorFunc(t, err)
  765. // "optimal" and "ubiquitous" bundle should be ok
  766. bundle, err = b.BundleFromPEMorDER(GoDaddyIntermediateCert, nil, Ubiquitous, "")
  767. if err != nil {
  768. t.Fatal(err)
  769. }
  770. checkBundleFunc = ExpectBundleLength(1)
  771. checkBundleFunc(t, bundle)
  772. bundle, err = b.BundleFromPEMorDER(GoDaddyIntermediateCert, nil, Optimal, "")
  773. if err != nil {
  774. t.Fatal(err)
  775. }
  776. checkBundleFunc = ExpectBundleLength(1)
  777. checkBundleFunc(t, bundle)
  778. // bundle remote should be ok
  779. bundle, err = b.BundleFromRemote("www.google.com", "", Ubiquitous)
  780. if err != nil {
  781. t.Fatal(err)
  782. }
  783. checkBundleFunc = ExpectBundleLength(2)
  784. checkBundleFunc(t, bundle)
  785. }
  786. func TestBundlerClientAuth(t *testing.T) {
  787. t.Skip("expired cert https://github.com/cloudflare/cfssl/issues/1237")
  788. b, err := NewBundler("testdata/client-auth/root.pem", "testdata/client-auth/int.pem")
  789. if err != nil {
  790. t.Fatal(err)
  791. }
  792. for _, leafFile := range []string{"testdata/client-auth/leaf-server.pem", "testdata/client-auth/leaf-client.pem"} {
  793. if _, err := b.BundleFromFile(leafFile, "", Optimal, ""); err != nil {
  794. t.Fatal(err)
  795. }
  796. }
  797. }