bundle_from_file_test.go 12 KB

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