sign_test.go 12 KB

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