bundle_from_file_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. package bundler
  2. // This test file contains tests on checking the correctness of BundleFromFile and Bundle.
  3. // We simulate various scenarios for Bundle and funnel the tests through BundleFromFile.
  4. import (
  5. "encoding/json"
  6. "testing"
  7. )
  8. // A helper structure that defines a BundleFromFile test case.
  9. type fileTest struct {
  10. // PEM cert file to be bundled
  11. cert string
  12. // PEM private key file to be bundled
  13. key string
  14. // Root CA bundle
  15. caBundleFile string
  16. // Trust intermediate bundle
  17. intBundleFile string
  18. // Additional PEM intermediate certificates to be added into the bundler
  19. extraIntermediates string
  20. // Bundler creation function
  21. bundlerConstructor func(*testing.T) (b *Bundler)
  22. // Error checking function
  23. errorCallback func(*testing.T, error)
  24. // Bundle checking function
  25. bundleChecking func(*testing.T, *Bundle)
  26. }
  27. /*
  28. ========== BundleFromFile Test Setup =============
  29. For each pair of crypto algorithm X and key size Y, a CA chain is constructed:
  30. Test_root_CA -> inter-L1 -> inter-L2--> cfssl-leaf-ecdsa256
  31. |-> cfssl-leaf-ecdsa384
  32. |-> cfssl-leaf-ecdsa521
  33. |-> cfssl-leaf-rsa2048
  34. |-> cfssl-leaf-rsa3072
  35. |-> cfssl-leaf-rsa4096
  36. Test_root_CA is a RSA cert, inter-L1 is RSA 4096 cert, inter-L2 is ecdsa-384 cert.
  37. The max path length is set to be 1 for non-root CAs.
  38. Two inter-* certs are assembled in intermediates.crt
  39. There is also an expired L1 cert, sharing the same CSR with inter-L1. Also the
  40. root CA processes the inter-L2 CSR directly to generate inter-L2-direct cert.
  41. Test_root_CA--> inter-L1-expired
  42. |-> inter-L2-direct
  43. Using inter-L2-direct as additional intermediate cert should shorten the
  44. bundle chain.
  45. */
  46. const (
  47. leafECDSA256 = "testdata/cfssl-leaf-ecdsa256.pem"
  48. leafECDSA384 = "testdata/cfssl-leaf-ecdsa384.pem"
  49. leafECDSA521 = "testdata/cfssl-leaf-ecdsa521.pem"
  50. leafRSA2048 = "testdata/cfssl-leaf-rsa2048.pem"
  51. leafRSA3072 = "testdata/cfssl-leaf-rsa3072.pem"
  52. leafRSA4096 = "testdata/cfssl-leaf-rsa4096.pem"
  53. leafKeyECDSA256 = "testdata/cfssl-leaf-ecdsa256.key"
  54. leafKeyECDSA384 = "testdata/cfssl-leaf-ecdsa384.key"
  55. leafKeyECDSA521 = "testdata/cfssl-leaf-ecdsa521.key"
  56. leafKeyRSA2048 = "testdata/cfssl-leaf-rsa2048.key"
  57. leafKeyRSA3072 = "testdata/cfssl-leaf-rsa3072.key"
  58. leafKeyRSA4096 = "testdata/cfssl-leaf-rsa4096.key"
  59. leafletRSA4096 = "testdata/cfssl-leaflet-rsa4096.pem"
  60. interL1 = "testdata/inter-L1.pem"
  61. interL1Expired = "testdata/inter-L1-expired.pem"
  62. interL1CSR = "testdata/inter-L1.csr"
  63. interL2 = "testdata/inter-L2.pem"
  64. interL2Direct = "testdata/inter-L2-direct.pem"
  65. partialBundle = "testdata/partial-bundle.pem" // partialBundle is a partial cert chain {leaf-ecds256, inter-L2}
  66. rpBundle = "testdata/reverse-partial-bundle.pem" // partialBundle is a partial cert chain in the reverse order {inter-L2, leaf-ecdsa256}
  67. badBundle = "testdata/bad-bundle.pem" // badBundle is a non-verifying partial bundle {leaf-ecdsa256, leaf-ecdsa384}
  68. interL2CSR = "testdata/inter-L2.csr"
  69. certDSA2048 = "testdata/dsa2048.pem"
  70. keyDSA2048 = "testdata/dsa2048.key"
  71. )
  72. // BundleFromFile test cases.
  73. var fileTests = []fileTest{
  74. // Input verification
  75. {
  76. cert: "not_such_cert.pem",
  77. caBundleFile: testCFSSLRootBundle,
  78. intBundleFile: testCFSSLIntBundle,
  79. errorCallback: ExpectErrorMessage(`"code":1001`),
  80. },
  81. {
  82. cert: emptyPEM,
  83. caBundleFile: testCFSSLRootBundle,
  84. intBundleFile: testCFSSLIntBundle,
  85. errorCallback: ExpectErrorMessage(`"code":1002`),
  86. },
  87. // Normal Keyless bundling for all supported public key types
  88. {
  89. cert: leafECDSA256,
  90. caBundleFile: testCFSSLRootBundle,
  91. intBundleFile: testCFSSLIntBundle,
  92. errorCallback: nil,
  93. bundleChecking: ExpectBundleLength(3),
  94. },
  95. {
  96. cert: leafECDSA384,
  97. caBundleFile: testCFSSLRootBundle,
  98. intBundleFile: testCFSSLIntBundle,
  99. errorCallback: nil,
  100. bundleChecking: ExpectBundleLength(3),
  101. },
  102. {
  103. cert: leafECDSA521,
  104. caBundleFile: testCFSSLRootBundle,
  105. intBundleFile: testCFSSLIntBundle,
  106. errorCallback: nil,
  107. bundleChecking: ExpectBundleLength(3),
  108. },
  109. {
  110. cert: leafRSA2048,
  111. caBundleFile: testCFSSLRootBundle,
  112. intBundleFile: testCFSSLIntBundle,
  113. errorCallback: nil,
  114. bundleChecking: ExpectBundleLength(3),
  115. },
  116. /*
  117. TODO: Re-enable once leafRSA3072 is regenerated with new expiry.
  118. {
  119. cert: leafRSA3072,
  120. caBundleFile: testCFSSLRootBundle,
  121. intBundleFile: testCFSSLIntBundle,
  122. errorCallback: nil,
  123. bundleChecking: ExpectBundleLength(3),
  124. },
  125. */
  126. {
  127. cert: leafRSA4096,
  128. caBundleFile: testCFSSLRootBundle,
  129. intBundleFile: testCFSSLIntBundle,
  130. errorCallback: nil,
  131. bundleChecking: ExpectBundleLength(3),
  132. },
  133. // Normal bundling with private key for all supported key types
  134. {
  135. cert: leafECDSA256,
  136. key: leafKeyECDSA256,
  137. caBundleFile: testCFSSLRootBundle,
  138. intBundleFile: testCFSSLIntBundle,
  139. errorCallback: nil,
  140. bundleChecking: ExpectBundleLength(3),
  141. },
  142. {
  143. cert: leafECDSA384,
  144. key: leafKeyECDSA384,
  145. caBundleFile: testCFSSLRootBundle,
  146. intBundleFile: testCFSSLIntBundle,
  147. errorCallback: nil,
  148. bundleChecking: ExpectBundleLength(3),
  149. },
  150. {
  151. cert: leafECDSA521,
  152. key: leafKeyECDSA521,
  153. caBundleFile: testCFSSLRootBundle,
  154. intBundleFile: testCFSSLIntBundle,
  155. errorCallback: nil,
  156. bundleChecking: ExpectBundleLength(3),
  157. },
  158. {
  159. cert: leafRSA2048,
  160. key: leafKeyRSA2048,
  161. caBundleFile: testCFSSLRootBundle,
  162. intBundleFile: testCFSSLIntBundle,
  163. errorCallback: nil,
  164. bundleChecking: ExpectBundleLength(3),
  165. },
  166. /*
  167. TODO: Re-enable once leafRSA3072 is regenerated with new expiry.
  168. {
  169. cert: leafRSA3072,
  170. key: leafKeyRSA3072,
  171. caBundleFile: testCFSSLRootBundle,
  172. intBundleFile: testCFSSLIntBundle,
  173. errorCallback: nil,
  174. bundleChecking: ExpectBundleLength(3),
  175. },
  176. */
  177. {
  178. cert: leafRSA4096,
  179. key: leafKeyRSA4096,
  180. caBundleFile: testCFSSLRootBundle,
  181. intBundleFile: testCFSSLIntBundle,
  182. errorCallback: nil,
  183. bundleChecking: ExpectBundleLength(3),
  184. },
  185. // Bundling with errors
  186. // leaflet cert is signed by a leaf cert which is not included the intermediate bundle.
  187. // So an UnknownAuthority error is expected.
  188. {
  189. cert: leafletRSA4096,
  190. caBundleFile: testCFSSLRootBundle,
  191. intBundleFile: testCFSSLIntBundle,
  192. errorCallback: ExpectErrorMessage(`"code":1220`),
  193. },
  194. // Expect TooManyIntermediates error because max path length is 1 for
  195. // inter-L1 but the leaflet cert is 2 CA away from inter-L1.
  196. {
  197. cert: leafletRSA4096,
  198. extraIntermediates: leafRSA4096,
  199. caBundleFile: testCFSSLRootBundle,
  200. intBundleFile: testCFSSLIntBundle,
  201. errorCallback: ExpectErrorMessage(`"code":1213`),
  202. },
  203. // Bundle with expired inter-L1 intermediate cert only, expect error 1211 VerifyFailed:Expired.
  204. {
  205. cert: interL2,
  206. extraIntermediates: interL1Expired,
  207. caBundleFile: testCFSSLRootBundle,
  208. intBundleFile: emptyPEM,
  209. errorCallback: ExpectErrorMessage(`"code":1211`),
  210. },
  211. // Bundle with private key mismatch
  212. // RSA cert, ECC private key
  213. {
  214. cert: leafRSA4096,
  215. key: leafKeyECDSA256,
  216. caBundleFile: testCFSSLRootBundle,
  217. intBundleFile: testCFSSLIntBundle,
  218. errorCallback: ExpectErrorMessages([]string{`"code":2300,`, `"message":"Private key does not match public key"`}),
  219. },
  220. // ECC cert, RSA private key
  221. {
  222. cert: leafECDSA256,
  223. key: leafKeyRSA4096,
  224. caBundleFile: testCFSSLRootBundle,
  225. intBundleFile: testCFSSLIntBundle,
  226. errorCallback: ExpectErrorMessages([]string{`"code":2300,`, `"message":"Private key does not match public key"`}),
  227. },
  228. // RSA 2048 cert, RSA 4096 private key
  229. {
  230. cert: leafRSA2048,
  231. key: leafKeyRSA4096,
  232. caBundleFile: testCFSSLRootBundle,
  233. intBundleFile: testCFSSLIntBundle,
  234. errorCallback: ExpectErrorMessages([]string{`"code":2300,`, `"message":"Private key does not match public key"`}),
  235. },
  236. // ECDSA 256 cert, ECDSA 384 private key
  237. {
  238. cert: leafECDSA256,
  239. key: leafKeyECDSA384,
  240. caBundleFile: testCFSSLRootBundle,
  241. intBundleFile: testCFSSLIntBundle,
  242. errorCallback: ExpectErrorMessages([]string{`"code":2300,`, `"message":"Private key does not match public key"`}),
  243. },
  244. // DSA is NOT supported.
  245. // Keyless bundling, expect private key error "NotRSAOrECCOrEd25519"
  246. {
  247. cert: certDSA2048,
  248. caBundleFile: testCFSSLRootBundle,
  249. intBundleFile: testCFSSLIntBundle,
  250. errorCallback: ExpectErrorMessages([]string{`"code":2200,`, `"message":"Private key algorithm is not RSA or ECC or Ed25519"`}),
  251. },
  252. // Bundling with DSA private key, expect error "Failed to parse private key"
  253. {
  254. cert: certDSA2048,
  255. key: keyDSA2048,
  256. caBundleFile: testCFSSLRootBundle,
  257. intBundleFile: testCFSSLIntBundle,
  258. errorCallback: ExpectErrorMessages([]string{`"code":2003,`, `"message":"Failed to parse private key"`}),
  259. },
  260. // Bundle with partial chain less some intermediates, expected error 1220: UnknownAuthority
  261. {
  262. cert: badBundle,
  263. caBundleFile: testCFSSLRootBundle,
  264. intBundleFile: interL1,
  265. errorCallback: ExpectErrorMessage(`"code":1220`),
  266. },
  267. // Bundle with misplaced key as cert
  268. {
  269. cert: leafKeyECDSA256,
  270. caBundleFile: testCFSSLRootBundle,
  271. intBundleFile: testCFSSLIntBundle,
  272. errorCallback: ExpectErrorMessages([]string{`"code":1003,`, `"message":"Failed to parse certificate"`}),
  273. },
  274. // Bundle with misplaced cert as key
  275. {
  276. cert: leafECDSA256,
  277. key: leafECDSA256,
  278. caBundleFile: testCFSSLRootBundle,
  279. intBundleFile: testCFSSLIntBundle,
  280. errorCallback: ExpectErrorMessages([]string{`"code":2003,`, `"message":"Failed to parse private key"`}),
  281. },
  282. // Smart Bundling
  283. // Bundling with a partial bundle should work the same as bundling the leaf.
  284. {
  285. cert: partialBundle,
  286. caBundleFile: testCFSSLRootBundle,
  287. intBundleFile: testCFSSLIntBundle,
  288. errorCallback: nil,
  289. bundleChecking: ExpectBundleLength(3),
  290. },
  291. // Bundle with a partial bundle such that the intermediate provided in the
  292. // partial bundle is verify by an intermediate. Yet itself is not in the intermediate
  293. // pool. In such cases, the bundling should be able to store the new intermediate
  294. // and return a correct bundle.
  295. {
  296. cert: partialBundle,
  297. caBundleFile: testCFSSLRootBundle,
  298. intBundleFile: interL1,
  299. errorCallback: nil,
  300. bundleChecking: ExpectBundleLength(3),
  301. },
  302. // Bundle with a reverse-ordered partial bundle.
  303. // Bundler should be able to detect it and return a correct bundle.
  304. {
  305. cert: rpBundle,
  306. caBundleFile: testCFSSLRootBundle,
  307. intBundleFile: interL1,
  308. errorCallback: nil,
  309. bundleChecking: ExpectBundleLength(3),
  310. },
  311. // Bundle with a L2 cert direct signed by root, expect a shorter chain of length 2.
  312. {
  313. cert: leafECDSA256,
  314. extraIntermediates: interL2Direct,
  315. caBundleFile: testCFSSLRootBundle,
  316. intBundleFile: testCFSSLIntBundle,
  317. errorCallback: nil,
  318. bundleChecking: ExpectBundleLength(2),
  319. },
  320. }
  321. // TestBundleFromFile goes through test cases defined in fileTests. See below for test cases definition and details.
  322. func TestBundleFromFile(t *testing.T) {
  323. for _, test := range fileTests {
  324. b := newCustomizedBundlerFromFile(t, test.caBundleFile, test.intBundleFile, test.extraIntermediates)
  325. bundle, err := b.BundleFromFile(test.cert, test.key, Optimal, "")
  326. if test.errorCallback != nil {
  327. test.errorCallback(t, err)
  328. } else {
  329. if err != nil {
  330. t.Fatalf("expected no error bundling %q. but an error occurred: %v", test.cert, err)
  331. }
  332. if test.bundleChecking != nil {
  333. test.bundleChecking(t, bundle)
  334. }
  335. }
  336. if bundle != nil {
  337. bundle.Cert = nil
  338. if _, err = json.Marshal(bundle); err == nil {
  339. t.Fatal("bundle should fail with no cert")
  340. }
  341. }
  342. }
  343. }