bundle_from_file_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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. cert: leafRSA3072,
  117. caBundleFile: testCFSSLRootBundle,
  118. intBundleFile: testCFSSLIntBundle,
  119. errorCallback: nil,
  120. bundleChecking: ExpectBundleLength(3),
  121. },
  122. {
  123. cert: leafRSA4096,
  124. caBundleFile: testCFSSLRootBundle,
  125. intBundleFile: testCFSSLIntBundle,
  126. errorCallback: nil,
  127. bundleChecking: ExpectBundleLength(3),
  128. },
  129. // Normal bundling with private key for all supported key types
  130. {
  131. cert: leafECDSA256,
  132. key: leafKeyECDSA256,
  133. caBundleFile: testCFSSLRootBundle,
  134. intBundleFile: testCFSSLIntBundle,
  135. errorCallback: nil,
  136. bundleChecking: ExpectBundleLength(3),
  137. },
  138. {
  139. cert: leafECDSA384,
  140. key: leafKeyECDSA384,
  141. caBundleFile: testCFSSLRootBundle,
  142. intBundleFile: testCFSSLIntBundle,
  143. errorCallback: nil,
  144. bundleChecking: ExpectBundleLength(3),
  145. },
  146. {
  147. cert: leafECDSA521,
  148. key: leafKeyECDSA521,
  149. caBundleFile: testCFSSLRootBundle,
  150. intBundleFile: testCFSSLIntBundle,
  151. errorCallback: nil,
  152. bundleChecking: ExpectBundleLength(3),
  153. },
  154. {
  155. cert: leafRSA2048,
  156. key: leafKeyRSA2048,
  157. caBundleFile: testCFSSLRootBundle,
  158. intBundleFile: testCFSSLIntBundle,
  159. errorCallback: nil,
  160. bundleChecking: ExpectBundleLength(3),
  161. },
  162. {
  163. cert: leafRSA3072,
  164. key: leafKeyRSA3072,
  165. caBundleFile: testCFSSLRootBundle,
  166. intBundleFile: testCFSSLIntBundle,
  167. errorCallback: nil,
  168. bundleChecking: ExpectBundleLength(3),
  169. },
  170. {
  171. cert: leafRSA4096,
  172. key: leafKeyRSA4096,
  173. caBundleFile: testCFSSLRootBundle,
  174. intBundleFile: testCFSSLIntBundle,
  175. errorCallback: nil,
  176. bundleChecking: ExpectBundleLength(3),
  177. },
  178. // Bundling with errors
  179. // leaflet cert is signed by a leaf cert which is not included the intermediate bundle.
  180. // So an UnknownAuthority error is expected.
  181. {
  182. cert: leafletRSA4096,
  183. caBundleFile: testCFSSLRootBundle,
  184. intBundleFile: testCFSSLIntBundle,
  185. errorCallback: ExpectErrorMessage(`"code":1220`),
  186. },
  187. // Expect TooManyIntermediates error because max path length is 1 for
  188. // inter-L1 but the leaflet cert is 2 CA away from inter-L1.
  189. {
  190. cert: leafletRSA4096,
  191. extraIntermediates: leafRSA4096,
  192. caBundleFile: testCFSSLRootBundle,
  193. intBundleFile: testCFSSLIntBundle,
  194. errorCallback: ExpectErrorMessage(`"code":1213`),
  195. },
  196. // Bundle with expired inter-L1 intermediate cert only, expect error 1211 VerifyFailed:Expired.
  197. {
  198. cert: interL2,
  199. extraIntermediates: interL1Expired,
  200. caBundleFile: testCFSSLRootBundle,
  201. intBundleFile: emptyPEM,
  202. errorCallback: ExpectErrorMessage(`"code":1211`),
  203. },
  204. // Bundle with private key mismatch
  205. // RSA cert, ECC private key
  206. {
  207. cert: leafRSA4096,
  208. key: leafKeyECDSA256,
  209. caBundleFile: testCFSSLRootBundle,
  210. intBundleFile: testCFSSLIntBundle,
  211. errorCallback: ExpectErrorMessages([]string{`"code":2300,`, `"message":"Private key does not match public key"`}),
  212. },
  213. // ECC cert, RSA private key
  214. {
  215. cert: leafECDSA256,
  216. key: leafKeyRSA4096,
  217. caBundleFile: testCFSSLRootBundle,
  218. intBundleFile: testCFSSLIntBundle,
  219. errorCallback: ExpectErrorMessages([]string{`"code":2300,`, `"message":"Private key does not match public key"`}),
  220. },
  221. // RSA 2048 cert, RSA 4096 private key
  222. {
  223. cert: leafRSA2048,
  224. key: leafKeyRSA4096,
  225. caBundleFile: testCFSSLRootBundle,
  226. intBundleFile: testCFSSLIntBundle,
  227. errorCallback: ExpectErrorMessages([]string{`"code":2300,`, `"message":"Private key does not match public key"`}),
  228. },
  229. // ECDSA 256 cert, ECDSA 384 private key
  230. {
  231. cert: leafECDSA256,
  232. key: leafKeyECDSA384,
  233. caBundleFile: testCFSSLRootBundle,
  234. intBundleFile: testCFSSLIntBundle,
  235. errorCallback: ExpectErrorMessages([]string{`"code":2300,`, `"message":"Private key does not match public key"`}),
  236. },
  237. // DSA is NOT supported.
  238. // Keyless bundling, expect private key error "NotRSAOrECC"
  239. {
  240. cert: certDSA2048,
  241. caBundleFile: testCFSSLRootBundle,
  242. intBundleFile: testCFSSLIntBundle,
  243. errorCallback: ExpectErrorMessages([]string{`"code":2200,`, `"message":"Private key algorithm is not RSA or ECC"`}),
  244. },
  245. // Bundling with DSA private key, expect error "Failed to parse private key"
  246. {
  247. cert: certDSA2048,
  248. key: keyDSA2048,
  249. caBundleFile: testCFSSLRootBundle,
  250. intBundleFile: testCFSSLIntBundle,
  251. errorCallback: ExpectErrorMessages([]string{`"code":2003,`, `"message":"Failed to parse private key"`}),
  252. },
  253. // Bundle with partial chain less some intermediates, expected error 1220: UnknownAuthority
  254. {
  255. cert: badBundle,
  256. caBundleFile: testCFSSLRootBundle,
  257. intBundleFile: interL1,
  258. errorCallback: ExpectErrorMessage(`"code":1220`),
  259. },
  260. // Bundle with misplaced key as cert
  261. {
  262. cert: leafKeyECDSA256,
  263. caBundleFile: testCFSSLRootBundle,
  264. intBundleFile: testCFSSLIntBundle,
  265. errorCallback: ExpectErrorMessages([]string{`"code":1003,`, `"message":"Failed to parse certificate"`}),
  266. },
  267. // Bundle with misplaced cert as key
  268. {
  269. cert: leafECDSA256,
  270. key: leafECDSA256,
  271. caBundleFile: testCFSSLRootBundle,
  272. intBundleFile: testCFSSLIntBundle,
  273. errorCallback: ExpectErrorMessages([]string{`"code":2003,`, `"message":"Failed to parse private key"`}),
  274. },
  275. // Smart Bundling
  276. // Bundling with a partial bundle should work the same as bundling the leaf.
  277. {
  278. cert: partialBundle,
  279. caBundleFile: testCFSSLRootBundle,
  280. intBundleFile: testCFSSLIntBundle,
  281. errorCallback: nil,
  282. bundleChecking: ExpectBundleLength(3),
  283. },
  284. // Bundle with a partial bundle such that the intermediate provided in the
  285. // partial bundle is verify by an intermediate. Yet itself is not in the intermediate
  286. // pool. In such cases, the bundling should be able to store the new intermediate
  287. // and return a correct bundle.
  288. {
  289. cert: partialBundle,
  290. caBundleFile: testCFSSLRootBundle,
  291. intBundleFile: interL1,
  292. errorCallback: nil,
  293. bundleChecking: ExpectBundleLength(3),
  294. },
  295. // Bundle with a reverse-ordered partial bundle.
  296. // Bundler should be able to detect it and return a correct bundle.
  297. {
  298. cert: rpBundle,
  299. caBundleFile: testCFSSLRootBundle,
  300. intBundleFile: interL1,
  301. errorCallback: nil,
  302. bundleChecking: ExpectBundleLength(3),
  303. },
  304. // Bundle with a L2 cert direct signed by root, expect a shorter chain of length 2.
  305. {
  306. cert: leafECDSA256,
  307. extraIntermediates: interL2Direct,
  308. caBundleFile: testCFSSLRootBundle,
  309. intBundleFile: testCFSSLIntBundle,
  310. errorCallback: nil,
  311. bundleChecking: ExpectBundleLength(2),
  312. },
  313. }
  314. // TestBundleFromFile goes through test cases defined in fileTests. See below for test cases definition and details.
  315. func TestBundleFromFile(t *testing.T) {
  316. for _, test := range fileTests {
  317. b := newCustomizedBundlerFromFile(t, test.caBundleFile, test.intBundleFile, test.extraIntermediates)
  318. bundle, err := b.BundleFromFile(test.cert, test.key, Optimal, "")
  319. if test.errorCallback != nil {
  320. test.errorCallback(t, err)
  321. } else {
  322. if err != nil {
  323. t.Fatalf("expected no error. but an error occurred: %v", err)
  324. }
  325. if test.bundleChecking != nil {
  326. test.bundleChecking(t, bundle)
  327. }
  328. }
  329. if bundle != nil {
  330. bundle.Cert = nil
  331. if _, err = json.Marshal(bundle); err == nil {
  332. t.Fatal("bundle should fail with no cert")
  333. }
  334. }
  335. }
  336. }