sign_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. package sign
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "io/ioutil"
  6. "net/http"
  7. "net/http/httptest"
  8. "strings"
  9. "testing"
  10. "github.com/cloudflare/cfssl/api"
  11. "github.com/cloudflare/cfssl/auth"
  12. "github.com/cloudflare/cfssl/config"
  13. "github.com/cloudflare/cfssl/signer"
  14. )
  15. const (
  16. testCaFile = "../testdata/ca.pem"
  17. testCaKeyFile = "../testdata/ca_key.pem"
  18. testCSRFile = "../testdata/csr.pem"
  19. testBrokenCertFile = "../testdata/broken.pem"
  20. testBrokenCSRFile = "../testdata/broken_csr.pem"
  21. )
  22. var validLocalConfig = `
  23. {
  24. "signing": {
  25. "default": {
  26. "usages": ["digital signature", "email protection"],
  27. "expiry": "1m"
  28. }
  29. }
  30. }`
  31. var validAuthLocalConfig = `
  32. {
  33. "signing": {
  34. "default": {
  35. "usages": ["digital signature", "email protection"],
  36. "expiry": "1m",
  37. "auth_key": "sample"
  38. }
  39. },
  40. "auth_keys": {
  41. "sample": {
  42. "type":"standard",
  43. "key":"0123456789ABCDEF0123456789ABCDEF"
  44. }
  45. }
  46. }`
  47. var validMixedLocalConfig = `
  48. {
  49. "signing": {
  50. "default": {
  51. "usages": ["digital signature", "email protection"],
  52. "expiry": "1m"
  53. },
  54. "profiles": {
  55. "auth": {
  56. "usages": ["digital signature", "email protection"],
  57. "expiry": "1m",
  58. "auth_key": "sample"
  59. }
  60. }
  61. },
  62. "auth_keys": {
  63. "sample": {
  64. "type":"standard",
  65. "key":"0123456789ABCDEF0123456789ABCDEF"
  66. }
  67. }
  68. }`
  69. var alsoValidMixedLocalConfig = `
  70. {
  71. "signing": {
  72. "default": {
  73. "usages": ["digital signature", "email protection"],
  74. "expiry": "1m",
  75. "auth_key": "sample"
  76. },
  77. "profiles": {
  78. "no-auth": {
  79. "usages": ["digital signature", "email protection"],
  80. "expiry": "1m"
  81. }
  82. }
  83. },
  84. "auth_keys": {
  85. "sample": {
  86. "type":"standard",
  87. "key":"0123456789ABCDEF0123456789ABCDEF"
  88. }
  89. }
  90. }`
  91. func newTestHandler(t *testing.T) (h http.Handler) {
  92. h, err := NewHandler(testCaFile, testCaKeyFile, nil)
  93. if err != nil {
  94. t.Fatal(err)
  95. }
  96. return
  97. }
  98. func TestNewHandler(t *testing.T) {
  99. newTestHandler(t)
  100. }
  101. func TestNewHandlerWithProfile(t *testing.T) {
  102. conf, err := config.LoadConfig([]byte(validLocalConfig))
  103. if err != nil {
  104. t.Fatal(err)
  105. }
  106. _, err = NewHandler(testCaFile, testCaKeyFile, conf.Signing)
  107. if err != nil {
  108. t.Fatal(err)
  109. }
  110. }
  111. func TestNewHandlerWithAuthProfile(t *testing.T) {
  112. conf, err := config.LoadConfig([]byte(validAuthLocalConfig))
  113. if err != nil {
  114. t.Fatal(err)
  115. }
  116. _, err = NewHandler(testCaFile, testCaKeyFile, conf.Signing)
  117. if err == nil {
  118. t.Fatal("All profiles have auth keys. Should have failed to create non-auth sign handler.")
  119. }
  120. }
  121. func TestNewHandlerError(t *testing.T) {
  122. // using testBrokenCSRFile as badly formed key
  123. _, err := NewHandler(testCaFile, testBrokenCSRFile, nil)
  124. if err == nil {
  125. t.Fatal("Expect error when create a signer with broken file.")
  126. }
  127. }
  128. func TestNewAuthHandlerWithNonAuthProfile(t *testing.T) {
  129. conf, err := config.LoadConfig([]byte(validLocalConfig))
  130. if err != nil {
  131. t.Fatal(err)
  132. }
  133. _, err = NewAuthHandler(testCaFile, testCaKeyFile, conf.Signing)
  134. if err == nil {
  135. t.Fatal("No profile have auth keys. Should have failed to create auth sign handler.")
  136. }
  137. }
  138. func TestNewHandlersWithMixedProfile(t *testing.T) {
  139. conf, err := config.LoadConfig([]byte(validMixedLocalConfig))
  140. if err != nil {
  141. t.Fatal(err)
  142. }
  143. _, err = NewHandler(testCaFile, testCaKeyFile, conf.Signing)
  144. if err != nil {
  145. t.Fatal("Should be able to create non-auth sign handler.")
  146. }
  147. _, err = NewAuthHandler(testCaFile, testCaKeyFile, conf.Signing)
  148. if err != nil {
  149. t.Fatal("Should be able to create auth sign handler.")
  150. }
  151. }
  152. func TestNewHandlersWithAnotherMixedProfile(t *testing.T) {
  153. conf, err := config.LoadConfig([]byte(alsoValidMixedLocalConfig))
  154. if err != nil {
  155. t.Fatal(err)
  156. }
  157. _, err = NewHandler(testCaFile, testCaKeyFile, conf.Signing)
  158. if err != nil {
  159. t.Fatal("Should be able to create non-auth sign handler.")
  160. }
  161. _, err = NewAuthHandler(testCaFile, testCaKeyFile, conf.Signing)
  162. if err != nil {
  163. t.Fatal("Should be able to create auth sign handler.")
  164. }
  165. }
  166. func newSignServer(t *testing.T) *httptest.Server {
  167. ts := httptest.NewServer(newTestHandler(t))
  168. return ts
  169. }
  170. func testSignFileOldInterface(t *testing.T, hostname, csrFile string) (resp *http.Response, body []byte) {
  171. ts := newSignServer(t)
  172. defer ts.Close()
  173. var csrPEM []byte
  174. if csrFile != "" {
  175. var err error
  176. csrPEM, err = ioutil.ReadFile(csrFile)
  177. if err != nil {
  178. t.Fatal(err)
  179. }
  180. }
  181. obj := map[string]string{}
  182. if len(hostname) > 0 {
  183. obj["hostname"] = hostname
  184. }
  185. if len(csrPEM) > 0 {
  186. obj["certificate_request"] = string(csrPEM)
  187. }
  188. blob, err := json.Marshal(obj)
  189. if err != nil {
  190. t.Fatal(err)
  191. }
  192. resp, err = http.Post(ts.URL, "application/json", bytes.NewReader(blob))
  193. if err != nil {
  194. t.Fatal(err)
  195. }
  196. body, err = ioutil.ReadAll(resp.Body)
  197. if err != nil {
  198. t.Fatal(err)
  199. }
  200. return
  201. }
  202. func testSignFile(t *testing.T, hosts []string, subject *signer.Subject, csrFile string) (resp *http.Response, body []byte) {
  203. ts := newSignServer(t)
  204. defer ts.Close()
  205. var csrPEM []byte
  206. if csrFile != "" {
  207. var err error
  208. csrPEM, err = ioutil.ReadFile(csrFile)
  209. if err != nil {
  210. t.Fatal(err)
  211. }
  212. }
  213. obj := map[string]interface{}{}
  214. if hosts != nil {
  215. obj["hosts"] = hosts
  216. }
  217. if len(csrPEM) > 0 {
  218. obj["certificate_request"] = string(csrPEM)
  219. }
  220. if subject != nil {
  221. obj["subject"] = subject
  222. }
  223. blob, err := json.Marshal(obj)
  224. if err != nil {
  225. t.Fatal(err)
  226. }
  227. resp, err = http.Post(ts.URL, "application/json", bytes.NewReader(blob))
  228. if err != nil {
  229. t.Fatal(err)
  230. }
  231. body, err = ioutil.ReadAll(resp.Body)
  232. if err != nil {
  233. t.Fatal(err)
  234. }
  235. return
  236. }
  237. const (
  238. testHostName = "localhost"
  239. testDomainName = "cloudflare.com"
  240. )
  241. type signTest struct {
  242. Hosts []string
  243. Subject *signer.Subject
  244. CSRFile string
  245. ExpectedHTTPStatus int
  246. ExpectedSuccess bool
  247. ExpectedErrorCode int
  248. }
  249. var signTests = []signTest{
  250. {
  251. Hosts: []string{testHostName},
  252. CSRFile: testCSRFile,
  253. ExpectedHTTPStatus: http.StatusOK,
  254. ExpectedSuccess: true,
  255. ExpectedErrorCode: 0,
  256. },
  257. {
  258. Hosts: []string{testDomainName},
  259. CSRFile: testCSRFile,
  260. ExpectedHTTPStatus: http.StatusOK,
  261. ExpectedSuccess: true,
  262. ExpectedErrorCode: 0,
  263. },
  264. {
  265. Hosts: []string{testDomainName, testHostName},
  266. CSRFile: testCSRFile,
  267. ExpectedHTTPStatus: http.StatusOK,
  268. ExpectedSuccess: true,
  269. ExpectedErrorCode: 0,
  270. },
  271. {
  272. Hosts: []string{testDomainName},
  273. Subject: &signer.Subject{CN: "example.com"},
  274. CSRFile: testCSRFile,
  275. ExpectedHTTPStatus: http.StatusOK,
  276. ExpectedSuccess: true,
  277. ExpectedErrorCode: 0,
  278. },
  279. {
  280. Hosts: []string{},
  281. Subject: &signer.Subject{CN: "example.com"},
  282. CSRFile: testCSRFile,
  283. ExpectedHTTPStatus: http.StatusOK,
  284. ExpectedSuccess: true,
  285. ExpectedErrorCode: 0,
  286. },
  287. {
  288. Hosts: nil,
  289. CSRFile: testCSRFile,
  290. ExpectedHTTPStatus: http.StatusOK,
  291. ExpectedSuccess: true,
  292. ExpectedErrorCode: 0,
  293. },
  294. {
  295. Hosts: []string{testDomainName},
  296. CSRFile: "",
  297. ExpectedHTTPStatus: http.StatusBadRequest,
  298. ExpectedSuccess: false,
  299. ExpectedErrorCode: http.StatusBadRequest,
  300. },
  301. {
  302. Hosts: []string{testDomainName},
  303. CSRFile: testBrokenCSRFile,
  304. ExpectedHTTPStatus: http.StatusBadRequest,
  305. ExpectedSuccess: false,
  306. ExpectedErrorCode: 9002,
  307. },
  308. }
  309. func TestSign(t *testing.T) {
  310. for i, test := range signTests {
  311. resp, body := testSignFile(t, test.Hosts, test.Subject, test.CSRFile)
  312. if resp.StatusCode != test.ExpectedHTTPStatus {
  313. t.Logf("Test %d: expected: %d, have %d", i, test.ExpectedHTTPStatus, resp.StatusCode)
  314. t.Fatal(resp.Status, test.ExpectedHTTPStatus, string(body))
  315. }
  316. message := new(api.Response)
  317. err := json.Unmarshal(body, message)
  318. if err != nil {
  319. t.Logf("failed to read response body: %v", err)
  320. t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
  321. }
  322. if test.ExpectedSuccess != message.Success {
  323. t.Logf("Test %d: expected: %v, have %v", i, test.ExpectedSuccess, message.Success)
  324. t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
  325. }
  326. if test.ExpectedSuccess == true {
  327. continue
  328. }
  329. if test.ExpectedErrorCode != message.Errors[0].Code {
  330. t.Fatalf("Test %d: expected: %v, have %v", i, test.ExpectedErrorCode, message.Errors[0].Code)
  331. t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
  332. }
  333. }
  334. // Test for backward compatibility
  335. // TODO remove after API transition is complete.
  336. for i, test := range signTests {
  337. // an empty hostname is not accepted by the old interface but an empty hosts array should be accepted
  338. // so skip the case of empty hosts array for the old interface.
  339. if test.Hosts != nil && len(test.Hosts) == 0 {
  340. continue
  341. }
  342. hostname := strings.Join(test.Hosts, ",")
  343. resp, body := testSignFileOldInterface(t, hostname, test.CSRFile)
  344. if resp.StatusCode != test.ExpectedHTTPStatus {
  345. t.Logf("Test %d: expected: %d, have %d", i, test.ExpectedHTTPStatus, resp.StatusCode)
  346. t.Fatal(resp.Status, test.ExpectedHTTPStatus, string(body))
  347. }
  348. message := new(api.Response)
  349. err := json.Unmarshal(body, message)
  350. if err != nil {
  351. t.Logf("failed to read response body: %v", err)
  352. t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
  353. }
  354. if test.ExpectedSuccess != message.Success {
  355. t.Logf("Test %d: expected: %v, have %v", i, test.ExpectedSuccess, message.Success)
  356. t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
  357. }
  358. if test.ExpectedSuccess == true {
  359. continue
  360. }
  361. if test.ExpectedErrorCode != message.Errors[0].Code {
  362. t.Fatalf("Test %d: expected: %v, have %v", i, test.ExpectedErrorCode, message.Errors[0].Code)
  363. t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
  364. }
  365. }
  366. }
  367. func newTestAuthHandler(t *testing.T) http.Handler {
  368. conf, err := config.LoadConfig([]byte(validAuthLocalConfig))
  369. if err != nil {
  370. t.Fatal(err)
  371. }
  372. h, err := NewAuthHandler(testCaFile, testCaKeyFile, conf.Signing)
  373. if err != nil {
  374. t.Fatal(err)
  375. }
  376. return h
  377. }
  378. func TestNewAuthHandler(t *testing.T) {
  379. newTestAuthHandler(t)
  380. }
  381. func TestNewAuthHandlerWithNoAuthConfig(t *testing.T) {
  382. conf, err := config.LoadConfig([]byte(validLocalConfig))
  383. if err != nil {
  384. t.Fatal(err)
  385. }
  386. _, err = NewAuthHandler(testCaFile, testCaKeyFile, conf.Signing)
  387. if err == nil {
  388. t.Fatal("Config doesn't have auth keys. Should have failed.")
  389. }
  390. return
  391. }
  392. func testAuthSignFile(t *testing.T, hosts []string, subject *signer.Subject, csrFile string, profile *config.SigningProfile) (resp *http.Response, body []byte) {
  393. ts := newAuthSignServer(t)
  394. defer ts.Close()
  395. var csrPEM []byte
  396. if csrFile != "" {
  397. var err error
  398. csrPEM, err = ioutil.ReadFile(csrFile)
  399. if err != nil {
  400. t.Fatal(err)
  401. }
  402. }
  403. obj := map[string]interface{}{}
  404. if hosts != nil {
  405. obj["hosts"] = hosts
  406. }
  407. if subject != nil {
  408. obj["subject"] = subject
  409. }
  410. if len(csrPEM) > 0 {
  411. obj["certificate_request"] = string(csrPEM)
  412. }
  413. reqBlob, err := json.Marshal(obj)
  414. if err != nil {
  415. t.Fatal(err)
  416. }
  417. var aReq auth.AuthenticatedRequest
  418. aReq.Request = reqBlob
  419. aReq.Token, err = profile.Provider.Token(aReq.Request)
  420. if err != nil {
  421. t.Fatal(err)
  422. }
  423. blob, err := json.Marshal(aReq)
  424. if err != nil {
  425. t.Fatal(err)
  426. }
  427. resp, err = http.Post(ts.URL, "application/json", bytes.NewReader(blob))
  428. if err != nil {
  429. t.Fatal(err)
  430. }
  431. body, err = ioutil.ReadAll(resp.Body)
  432. if err != nil {
  433. t.Fatal(err)
  434. }
  435. return
  436. }
  437. func newAuthSignServer(t *testing.T) *httptest.Server {
  438. ts := httptest.NewServer(newTestAuthHandler(t))
  439. return ts
  440. }
  441. func TestAuthSign(t *testing.T) {
  442. conf, err := config.LoadConfig([]byte(validAuthLocalConfig))
  443. if err != nil {
  444. t.Fatal(err)
  445. }
  446. for i, test := range signTests {
  447. resp, body := testAuthSignFile(t, test.Hosts, test.Subject, test.CSRFile, conf.Signing.Default)
  448. if resp.StatusCode != test.ExpectedHTTPStatus {
  449. t.Logf("Test %d: expected: %d, have %d", i, test.ExpectedHTTPStatus, resp.StatusCode)
  450. t.Fatal(resp.Status, test.ExpectedHTTPStatus, string(body))
  451. }
  452. message := new(api.Response)
  453. err := json.Unmarshal(body, message)
  454. if err != nil {
  455. t.Logf("failed to read response body: %v", err)
  456. t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
  457. }
  458. if test.ExpectedSuccess != message.Success {
  459. t.Fatalf("Test %d: expected: %v, have %v", i, test.ExpectedSuccess, message.Success)
  460. t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
  461. }
  462. if test.ExpectedSuccess == true {
  463. continue
  464. }
  465. if test.ExpectedErrorCode != message.Errors[0].Code {
  466. t.Fatalf("Test %d: expected: %v, have %v", i, test.ExpectedErrorCode, message.Errors[0].Code)
  467. t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
  468. }
  469. }
  470. }