universal_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. package universal
  2. import (
  3. "bytes"
  4. "math/big"
  5. "net/http"
  6. "net/http/httptest"
  7. "os"
  8. "strings"
  9. "testing"
  10. "time"
  11. apiinfo "github.com/cloudflare/cfssl/api/info"
  12. apisign "github.com/cloudflare/cfssl/api/signhandler"
  13. "github.com/cloudflare/cfssl/config"
  14. "github.com/cloudflare/cfssl/helpers"
  15. "github.com/cloudflare/cfssl/helpers/testsuite"
  16. "github.com/cloudflare/cfssl/info"
  17. "github.com/cloudflare/cfssl/signer"
  18. )
  19. const (
  20. testCaFile = "../local/testdata/ca.pem"
  21. testCaKeyFile = "../local/testdata/ca_key.pem"
  22. )
  23. var expiry = 1 * time.Minute
  24. var validLocalConfig = &config.Config{
  25. Signing: &config.Signing{
  26. Profiles: map[string]*config.SigningProfile{
  27. "valid": {
  28. Usage: []string{"digital signature"},
  29. Expiry: expiry,
  30. },
  31. },
  32. Default: &config.SigningProfile{
  33. Usage: []string{"digital signature"},
  34. Expiry: expiry,
  35. },
  36. },
  37. }
  38. var validMinimalRemoteConfig = `
  39. {
  40. "signing": {
  41. "profiles": {
  42. "CA": {
  43. "usages": [
  44. "cert sign",
  45. "crl sign"
  46. ],
  47. "expiry": "720h",
  48. "auth_key": "ca-auth"
  49. }
  50. },
  51. "default": {
  52. "usages": [
  53. "digital signature",
  54. "email protection"
  55. ],
  56. "expiry": "8000h"
  57. }
  58. },
  59. "auth_keys": {
  60. "ca-auth": {
  61. "type": "standard",
  62. "key": "0123456789ABCDEF0123456789ABCDEF"
  63. }
  64. }
  65. }`
  66. var validMinimalUniversalConfig = `
  67. {
  68. "signing": {
  69. "profiles": {
  70. "CA": {
  71. "usages": [
  72. "cert sign",
  73. "crl sign"
  74. ],
  75. "expiry": "720h",
  76. "auth_key": "local-auth",
  77. "auth_remote": {
  78. "remote": "localhost",
  79. "auth_key": "ca-auth"
  80. }
  81. },
  82. "email": {
  83. "usages": [
  84. "s/mime"
  85. ],
  86. "expiry": "720h"
  87. }
  88. },
  89. "default": {
  90. "usages": [
  91. "digital signature",
  92. "email protection"
  93. ],
  94. "expiry": "8000h"
  95. }
  96. },
  97. "auth_keys": {
  98. "local-auth": {
  99. "type": "standard",
  100. "key": "123456789ABCDEF0123456789ABCDEF0"
  101. },
  102. "ca-auth": {
  103. "type": "standard",
  104. "key": "0123456789ABCDEF0123456789ABCDEF"
  105. }
  106. },
  107. "remotes": {
  108. "localhost": "http://127.0.0.1:1234"
  109. }
  110. }`
  111. var validRemoteConfig = `
  112. {
  113. "signing": {
  114. "profiles": {
  115. "CA": {
  116. "usages": [
  117. "cert sign",
  118. "crl sign"
  119. ],
  120. "expiry": "720h",
  121. "auth_key": "ca-auth"
  122. },
  123. "ipsec": {
  124. "usages": [
  125. "ipsec tunnel"
  126. ],
  127. "expiry": "720h"
  128. }
  129. },
  130. "default": {
  131. "usages": [
  132. "digital signature",
  133. "email protection"
  134. ],
  135. "expiry": "8000h"
  136. }
  137. },
  138. "auth_keys": {
  139. "ca-auth": {
  140. "type": "standard",
  141. "key": "0123456789ABCDEF0123456789ABCDEF"
  142. }
  143. }
  144. }`
  145. var validUniversalConfig = `
  146. {
  147. "signing": {
  148. "profiles": {
  149. "CA": {
  150. "usages": [
  151. "cert sign",
  152. "crl sign"
  153. ],
  154. "expiry": "720h",
  155. "auth_key": "local-auth",
  156. "auth_remote": {
  157. "remote": "localhost",
  158. "auth_key": "ca-auth"
  159. }
  160. },
  161. "ipsec": {
  162. "usages": [
  163. "ipsec tunnel"
  164. ],
  165. "expiry": "720h",
  166. "remote": "localhost"
  167. },
  168. "email": {
  169. "usages": [
  170. "s/mime"
  171. ],
  172. "expiry": "720h"
  173. }
  174. },
  175. "default": {
  176. "usages": [
  177. "digital signature",
  178. "email protection"
  179. ],
  180. "expiry": "8000h"
  181. }
  182. },
  183. "auth_keys": {
  184. "local-auth": {
  185. "type": "standard",
  186. "key": "123456789ABCDEF0123456789ABCDEF0"
  187. },
  188. "ca-auth": {
  189. "type": "standard",
  190. "key": "0123456789ABCDEF0123456789ABCDEF"
  191. }
  192. },
  193. "remotes": {
  194. "localhost": "http://127.0.0.1:1234"
  195. }
  196. }`
  197. var validNoAuthRemoteConfig = `
  198. {
  199. "signing": {
  200. "profiles": {
  201. "CA": {
  202. "usages": [
  203. "cert sign",
  204. "crl sign"
  205. ],
  206. "expiry": "720h"
  207. },
  208. "ipsec": {
  209. "usages": [
  210. "ipsec tunnel"
  211. ],
  212. "expiry": "720h"
  213. }
  214. },
  215. "default": {
  216. "usages": [
  217. "digital signature",
  218. "email protection"
  219. ],
  220. "expiry": "8000h"
  221. }
  222. }
  223. }`
  224. var validNoAuthUniversalConfig = `
  225. {
  226. "signing": {
  227. "profiles": {
  228. "CA": {
  229. "usages": [
  230. "cert sign",
  231. "crl sign"
  232. ],
  233. "expiry": "720h",
  234. "remote": "localhost"
  235. },
  236. "ipsec": {
  237. "usages": [
  238. "ipsec tunnel"
  239. ],
  240. "expiry": "720h",
  241. "remote": "localhost"
  242. },
  243. "email": {
  244. "usages": [
  245. "s/mime"
  246. ],
  247. "expiry": "720h"
  248. }
  249. },
  250. "default": {
  251. "usages": [
  252. "digital signature",
  253. "email protection"
  254. ],
  255. "expiry": "8000h"
  256. }
  257. },
  258. "remotes": {
  259. "localhost": "127.0.0.1:1234"
  260. }
  261. }`
  262. func TestNewSigner(t *testing.T) {
  263. h := map[string]string{
  264. "key-file": testCaKeyFile,
  265. "cert-file": testCaFile,
  266. }
  267. r := &Root{
  268. Config: h,
  269. ForceRemote: false,
  270. }
  271. _, err := NewSigner(*r, validLocalConfig.Signing)
  272. if err != nil {
  273. t.Fatal(err)
  274. }
  275. }
  276. func checkInfo(t *testing.T, s signer.Signer, name string, profile *config.SigningProfile) {
  277. req := info.Req{
  278. Profile: name,
  279. }
  280. resp, err := s.Info(req)
  281. if err != nil {
  282. t.Fatal("remote info failed:", err)
  283. }
  284. if strings.Join(profile.Usage, ",") != strings.Join(resp.Usage, ",") {
  285. t.Fatalf("Expected usage for profile %s to be %+v, got %+v", name, profile.Usage, resp.Usage)
  286. }
  287. caBytes, err := os.ReadFile(testCaFile)
  288. caBytes = bytes.TrimSpace(caBytes)
  289. if err != nil {
  290. t.Fatal("fail to read test CA cert:", err)
  291. }
  292. if bytes.Compare(caBytes, []byte(resp.Certificate)) != 0 {
  293. t.Fatal("Get a different CA cert through info api.", len(resp.Certificate), len(caBytes))
  294. }
  295. }
  296. func TestUniversalRemoteAndLocalInfo(t *testing.T) {
  297. // set up remote server
  298. remoteConfig := testsuite.NewConfig(t, []byte(validMinimalRemoteConfig))
  299. remoteServer := newTestInfoServer(t, newTestUniversalSigner(t, remoteConfig.Signing))
  300. defer closeTestServer(t, remoteServer)
  301. universalConfig := testsuite.NewConfig(t, []byte(validMinimalUniversalConfig))
  302. // override with test server address
  303. for name, profile := range universalConfig.Signing.Profiles {
  304. if profile.RemoteServer != "" {
  305. universalConfig.Signing.Profiles[name].RemoteServer = remoteServer.URL
  306. }
  307. }
  308. s := newTestUniversalSigner(t, universalConfig.Signing)
  309. for name, profile := range universalConfig.Signing.Profiles {
  310. checkInfo(t, s, name, profile)
  311. }
  312. // add check for default profile
  313. checkInfo(t, s, "", universalConfig.Signing.Default)
  314. }
  315. func TestUniversalMultipleRemoteAndLocalInfo(t *testing.T) {
  316. // set up remote server
  317. remoteConfig := testsuite.NewConfig(t, []byte(validRemoteConfig))
  318. remoteServer := newTestInfoServer(t, newTestUniversalSigner(t, remoteConfig.Signing))
  319. defer closeTestServer(t, remoteServer)
  320. universalConfig := testsuite.NewConfig(t, []byte(validUniversalConfig))
  321. // override with test server address
  322. for name, profile := range universalConfig.Signing.Profiles {
  323. if profile.RemoteServer != "" {
  324. universalConfig.Signing.Profiles[name].RemoteServer = remoteServer.URL
  325. }
  326. }
  327. s := newTestUniversalSigner(t, universalConfig.Signing)
  328. for name, profile := range universalConfig.Signing.Profiles {
  329. checkInfo(t, s, name, profile)
  330. }
  331. // add check for default profile
  332. checkInfo(t, s, "", universalConfig.Signing.Default)
  333. }
  334. func TestUniversalRemoteAndLocalSign(t *testing.T) {
  335. // set up remote server
  336. remoteConfig := testsuite.NewConfig(t, []byte(validNoAuthRemoteConfig))
  337. remoteServer := newTestSignServer(t, newTestUniversalSigner(t, remoteConfig.Signing))
  338. defer closeTestServer(t, remoteServer)
  339. universalConfig := testsuite.NewConfig(t, []byte(validNoAuthUniversalConfig))
  340. // override with test server address
  341. for name, profile := range universalConfig.Signing.Profiles {
  342. if profile.RemoteServer != "" {
  343. universalConfig.Signing.Profiles[name].RemoteServer = remoteServer.URL
  344. }
  345. }
  346. s := newTestUniversalSigner(t, universalConfig.Signing)
  347. checkSign := func(name string, profile *config.SigningProfile) {
  348. hosts := []string{"cloudflare.com"}
  349. for _, test := range testsuite.CSRTests {
  350. csr, err := os.ReadFile(test.File)
  351. if err != nil {
  352. t.Fatalf("CSR loading error (%s): %v", name, err)
  353. }
  354. testSerial := big.NewInt(0x7007F)
  355. certBytes, err := s.Sign(signer.SignRequest{
  356. Hosts: hosts,
  357. Request: string(csr),
  358. Serial: testSerial,
  359. Profile: name,
  360. })
  361. if test.ErrorCallback != nil {
  362. test.ErrorCallback(t, err)
  363. } else {
  364. if err != nil {
  365. t.Fatalf("Expected no error. Got %s. Param %s %d", err.Error(), test.KeyAlgo, test.KeyLen)
  366. }
  367. cert, err := helpers.ParseCertificatePEM(certBytes)
  368. if err != nil {
  369. t.Fatal("Fail to parse returned certificate:", err)
  370. }
  371. ku, _, _ := profile.Usages()
  372. if cert.KeyUsage != ku {
  373. t.Fatalf("Key usage was incorrect expected %+v, got %+v", ku, cert.KeyUsage)
  374. }
  375. }
  376. }
  377. }
  378. for name, profile := range universalConfig.Signing.Profiles {
  379. checkSign(name, profile)
  380. }
  381. // add check for default profile
  382. checkSign("", universalConfig.Signing.Default)
  383. }
  384. func newTestUniversalSigner(t *testing.T, policy *config.Signing) signer.Signer {
  385. h := map[string]string{
  386. "key-file": testCaKeyFile,
  387. "cert-file": testCaFile,
  388. }
  389. r := &Root{
  390. Config: h,
  391. ForceRemote: false,
  392. }
  393. s, err := NewSigner(*r, policy)
  394. if err != nil {
  395. t.Fatal("fail to init universal signer:", err)
  396. }
  397. return s
  398. }
  399. func newTestSignHandler(t *testing.T, s signer.Signer) (h http.Handler) {
  400. h, err := apisign.NewHandlerFromSigner(s)
  401. if err != nil {
  402. t.Fatal(err)
  403. }
  404. return
  405. }
  406. func newTestInfoHandler(t *testing.T, s signer.Signer) (h http.Handler) {
  407. h, err := apiinfo.NewHandler(s)
  408. if err != nil {
  409. t.Fatal(err)
  410. }
  411. return
  412. }
  413. func newTestSignServer(t *testing.T, s signer.Signer) *httptest.Server {
  414. mux := http.NewServeMux()
  415. mux.Handle("/api/v1/cfssl/sign", newTestSignHandler(t, s))
  416. ts := httptest.NewUnstartedServer(mux)
  417. ts.Start()
  418. t.Log(ts.URL)
  419. return ts
  420. }
  421. func newTestInfoServer(t *testing.T, s signer.Signer) *httptest.Server {
  422. mux := http.NewServeMux()
  423. mux.Handle("/api/v1/cfssl/info", newTestInfoHandler(t, s))
  424. ts := httptest.NewUnstartedServer(mux)
  425. ts.Start()
  426. t.Log(ts.URL)
  427. return ts
  428. }
  429. func closeTestServer(t *testing.T, ts *httptest.Server) {
  430. t.Log("Finalizing test server.")
  431. ts.Close()
  432. }