responder_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. package ocsp
  2. import (
  3. "encoding/hex"
  4. "io/ioutil"
  5. "net/http"
  6. "net/http/httptest"
  7. "net/url"
  8. "reflect"
  9. "testing"
  10. "time"
  11. "github.com/cloudflare/cfssl/certdb"
  12. "github.com/cloudflare/cfssl/certdb/sql"
  13. "github.com/cloudflare/cfssl/certdb/testdb"
  14. "github.com/cloudflare/cfssl/helpers"
  15. "github.com/jmhodges/clock"
  16. goocsp "golang.org/x/crypto/ocsp"
  17. )
  18. const (
  19. responseFile = "testdata/resp64.pem"
  20. binResponseFile = "testdata/response.der"
  21. brokenResponseFile = "testdata/response_broken.pem"
  22. mixResponseFile = "testdata/response_mix.pem"
  23. )
  24. type testSource struct{}
  25. func (ts testSource) Response(r *goocsp.Request) ([]byte, http.Header, error) {
  26. return []byte("hi"), nil, nil
  27. }
  28. type testCase struct {
  29. method, path string
  30. expected int
  31. }
  32. func TestOCSP(t *testing.T) {
  33. cases := []testCase{
  34. {"OPTIONS", "/", http.StatusMethodNotAllowed},
  35. {"GET", "/", http.StatusBadRequest},
  36. // Bad URL encoding
  37. {"GET", "%ZZFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest},
  38. // Bad URL encoding
  39. {"GET", "%%FQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest},
  40. // Bad base64 encoding
  41. {"GET", "==MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest},
  42. // Bad OCSP DER encoding
  43. {"GET", "AAAMFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest},
  44. // Good encoding all around, including a double slash
  45. {"GET", "MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusOK},
  46. // Good request, leading slash
  47. {"GET", "/MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusOK},
  48. }
  49. responder := Responder{
  50. Source: testSource{},
  51. clk: clock.NewFake(),
  52. }
  53. for _, tc := range cases {
  54. rw := httptest.NewRecorder()
  55. responder.ServeHTTP(rw, &http.Request{
  56. Method: tc.method,
  57. URL: &url.URL{
  58. Path: tc.path,
  59. },
  60. })
  61. if rw.Code != tc.expected {
  62. t.Errorf("Incorrect response code: got %d, wanted %d", rw.Code, tc.expected)
  63. }
  64. }
  65. }
  66. var testResp = `308204f90a0100a08204f2308204ee06092b0601050507300101048204df308204db3081a7a003020100a121301f311d301b06035504030c146861707079206861636b65722066616b65204341180f32303135303932333231303630305a306c306a3042300906052b0e03021a0500041439e45eb0e3a861c7fa3a3973876be61f7b7d98860414fb784f12f96015832c9f177f3419b32e36ea41890209009cf1912ea8d509088000180f32303135303932333030303030305aa011180f32303330303832363030303030305a300d06092a864886f70d01010b05000382010100c17ed5f12c408d214092c86cb2d6ba9881637a9d5cafb8ddc05aed85806a554c37abdd83c2e00a4bb25b2d0dda1e1c0be65144377471bca53f14616f379ee0c0b436c697b400b7eba9513c5be6d92fbc817586d568156293cfa0099d64585146def907dee36eb650c424a00207b01813aa7ae90e65045339482eeef12b6fa8656315da8f8bb1375caa29ac3858f891adb85066c35b5176e154726ae746016e42e0d6016668ff10a8aa9637417d29be387a1bdba9268b13558034ab5f3e498a47fb096f2e1b39236b22956545884fbbed1884f1bc9686b834d8def4802bac8f79924a36867af87412f808977abaf6457f3cda9e7eccbd0731bcd04865b899ee41a08203193082031530820311308201f9a0030201020209009cf1912ea8d50908300d06092a864886f70d01010b0500301f311d301b06035504030c146861707079206861636b65722066616b65204341301e170d3135303430373233353033385a170d3235303430343233353033385a301f311d301b06035504030c146861707079206861636b65722066616b6520434130820122300d06092a864886f70d01010105000382010f003082010a0282010100c20a47799a05c512b27717633413d770f936bf99de62f130c8774d476deac0029aa6c9d1bb519605df32d34b336394d48e9adc9bbeb48652767dafdb5241c2fc54ce9650e33cb672298888c403642407270cc2f46667f07696d3dd62cfd1f41a8dc0ed60d7c18366b1d2cd462d34a35e148e8695a9a3ec62b656bd129a211a9a534847992d005b0412bcdffdde23085eeca2c32c2693029b5a79f1090fe0b1cb4a154b5c36bc04c7d5a08fa2a58700d3c88d5059205bc5560dc9480f1732b1ad29b030ed3235f7fb868f904fdc79f98ffb5c4e7d4b831ce195f171729ec3f81294df54e66bd3f83d81843b640aea5d7ec64d0905a9dbb03e6ff0e6ac523d36ab0203010001a350304e301d0603551d0e04160414fb784f12f96015832c9f177f3419b32e36ea4189301f0603551d23041830168014fb784f12f96015832c9f177f3419b32e36ea4189300c0603551d13040530030101ff300d06092a864886f70d01010b050003820101001df436be66ff938ccbfb353026962aa758763a777531119377845109e7c2105476c165565d5bbce1464b41bd1d392b079a7341c978af754ca9b3bd7976d485cbbe1d2070d2d4feec1e0f79e8fec9df741e0ea05a26a658d3866825cc1aa2a96a0a04942b2c203cc39501f917a899161dfc461717fe9301fce6ea1afffd7b7998f8941cf76f62def994c028bd1c4b49b17c4d243a6fb058c484968cf80501234da89347108b56b2640cb408e3c336fd72cd355c7f690a15405a7f4ba1e30a6be4a51d262b586f77f8472b207fdd194efab8d3a2683cc148abda7a11b9de1db9307b8ed5a9cd20226f668bd6ac5a3852fd449e42899b7bc915ee747891a110a971`
  67. type testHeaderSource struct {
  68. headers http.Header
  69. }
  70. func (ts testHeaderSource) Response(r *goocsp.Request) ([]byte, http.Header, error) {
  71. resp, _ := hex.DecodeString(testResp)
  72. return resp, ts.headers, nil
  73. }
  74. func TestOverrideHeaders(t *testing.T) {
  75. headers := http.Header(map[string][]string{
  76. "Content-Type": {"yup"},
  77. "Cache-Control": {"nope"},
  78. "New": {"header"},
  79. "Expires": {"0"},
  80. "Last-Modified": {"now"},
  81. "Etag": {"mhm"},
  82. })
  83. responder := Responder{
  84. Source: testHeaderSource{headers: headers},
  85. clk: clock.NewFake(),
  86. }
  87. rw := httptest.NewRecorder()
  88. responder.ServeHTTP(rw, &http.Request{
  89. Method: "GET",
  90. URL: &url.URL{Path: "MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D"},
  91. })
  92. if !reflect.DeepEqual(rw.Header(), headers) {
  93. t.Fatalf("Unexpected Headers returned: wanted %s, got %s", headers, rw.Header())
  94. }
  95. }
  96. func TestCacheHeaders(t *testing.T) {
  97. source, err := NewSourceFromFile(responseFile)
  98. if err != nil {
  99. t.Fatalf("Error constructing source: %s", err)
  100. }
  101. fc := clock.NewFake()
  102. fc.Set(time.Date(2015, 11, 12, 0, 0, 0, 0, time.UTC))
  103. responder := Responder{
  104. Source: source,
  105. clk: fc,
  106. }
  107. rw := httptest.NewRecorder()
  108. responder.ServeHTTP(rw, &http.Request{
  109. Method: "GET",
  110. URL: &url.URL{
  111. Path: "MEMwQTA/MD0wOzAJBgUrDgMCGgUABBSwLsMRhyg1dJUwnXWk++D57lvgagQU6aQ/7p6l5vLV13lgPJOmLiSOl6oCAhJN",
  112. },
  113. })
  114. if rw.Code != http.StatusOK {
  115. t.Errorf("Unexpected HTTP status code %d", rw.Code)
  116. }
  117. testCases := []struct {
  118. header string
  119. value string
  120. }{
  121. {"Last-Modified", "Tue, 20 Oct 2015 00:00:00 UTC"},
  122. {"Expires", "Sun, 20 Oct 2030 00:00:00 UTC"},
  123. {"Cache-Control", "max-age=471398400, public, no-transform, must-revalidate"},
  124. {"Etag", "\"8169FB0843B081A76E9F6F13FD70C8411597BEACF8B182136FFDD19FBD26140A\""},
  125. }
  126. for _, tc := range testCases {
  127. headers, ok := rw.HeaderMap[tc.header]
  128. if !ok {
  129. t.Errorf("Header %s missing from HTTP response", tc.header)
  130. continue
  131. }
  132. if len(headers) != 1 {
  133. t.Errorf("Wrong number of headers in HTTP response. Wanted 1, got %d", len(headers))
  134. continue
  135. }
  136. actual := headers[0]
  137. if actual != tc.value {
  138. t.Errorf("Got header %s: %s. Expected %s", tc.header, actual, tc.value)
  139. }
  140. }
  141. rw = httptest.NewRecorder()
  142. headers := http.Header{}
  143. headers.Add("If-None-Match", "\"8169FB0843B081A76E9F6F13FD70C8411597BEACF8B182136FFDD19FBD26140A\"")
  144. responder.ServeHTTP(rw, &http.Request{
  145. Method: "GET",
  146. URL: &url.URL{
  147. Path: "MEMwQTA/MD0wOzAJBgUrDgMCGgUABBSwLsMRhyg1dJUwnXWk++D57lvgagQU6aQ/7p6l5vLV13lgPJOmLiSOl6oCAhJN",
  148. },
  149. Header: headers,
  150. })
  151. if rw.Code != http.StatusNotModified {
  152. t.Fatalf("Got wrong status code: expected %d, got %d", http.StatusNotModified, rw.Code)
  153. }
  154. }
  155. func TestNewSourceFromFile(t *testing.T) {
  156. _, err := NewSourceFromFile("")
  157. if err == nil {
  158. t.Fatal("Didn't fail on non-file input")
  159. }
  160. // expected case
  161. _, err = NewSourceFromFile(responseFile)
  162. if err != nil {
  163. t.Fatal(err)
  164. }
  165. // binary-formatted file
  166. _, err = NewSourceFromFile(binResponseFile)
  167. if err != nil {
  168. t.Fatal(err)
  169. }
  170. // the response file from before, with stuff deleted
  171. _, err = NewSourceFromFile(brokenResponseFile)
  172. if err != nil {
  173. t.Fatal(err)
  174. }
  175. // mix of a correct and malformed responses
  176. _, err = NewSourceFromFile(mixResponseFile)
  177. if err != nil {
  178. t.Fatal(err)
  179. }
  180. }
  181. func TestSqliteTrivial(t *testing.T) {
  182. // First, read and parse certificate and issuer files needed to make
  183. // an OCSP request.
  184. certFile := "testdata/sqlite_ca.pem"
  185. issuerFile := "testdata/ca.pem"
  186. certContent, err := ioutil.ReadFile(certFile)
  187. if err != nil {
  188. t.Errorf("Error reading cert file: %s", err)
  189. }
  190. issuerContent, err := ioutil.ReadFile(issuerFile)
  191. if err != nil {
  192. t.Errorf("Error reading issuer file: %s", err)
  193. }
  194. cert, err := helpers.ParseCertificatePEM(certContent)
  195. if err != nil {
  196. t.Errorf("Error parsing cert file: %s", err)
  197. }
  198. issuer, err := helpers.ParseCertificatePEM(issuerContent)
  199. if err != nil {
  200. t.Errorf("Error parsing cert file: %s", err)
  201. }
  202. // Next, create the OCSP request.
  203. reqByte, err := goocsp.CreateRequest(cert, issuer, nil)
  204. if err != nil {
  205. t.Errorf("Error creating OCSP request: %s", err)
  206. }
  207. req, err := goocsp.ParseRequest(reqByte)
  208. if err != nil {
  209. t.Errorf("Error parsing OCSP request: %s", err)
  210. }
  211. sqliteDBfile := "testdata/sqlite_test.db"
  212. db := testdb.SQLiteDB(sqliteDBfile)
  213. accessor := sql.NewAccessor(db)
  214. // Populate the DB with the OCSPRecord, and check
  215. // that Response() handles the request appropiately.
  216. ocsp := certdb.OCSPRecord{
  217. AKI: hex.EncodeToString(req.IssuerKeyHash),
  218. Body: "Test OCSP",
  219. Expiry: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
  220. Serial: req.SerialNumber.String(),
  221. }
  222. err = accessor.InsertOCSP(ocsp)
  223. if err != nil {
  224. t.Errorf("Error inserting OCSP record into DB: %s", err)
  225. }
  226. // Use the created Accessor to create a new DBSource.
  227. src := NewDBSource(accessor)
  228. // Call Response() method on constructed request and check the output.
  229. response, _, err := src.Response(req)
  230. if err != nil {
  231. t.Error(err)
  232. }
  233. if string(response) != "Test OCSP" {
  234. t.Error("Incorrect response received from Sqlite DB")
  235. }
  236. }
  237. func TestSqliteRealResponse(t *testing.T) {
  238. sqliteDBfile := "testdata/sqlite_test.db"
  239. db := testdb.SQLiteDB(sqliteDBfile)
  240. accessor := sql.NewAccessor(db)
  241. certFile := "testdata/cert.pem"
  242. issuerFile := "testdata/ca.pem"
  243. certContent, err := ioutil.ReadFile(certFile)
  244. if err != nil {
  245. t.Errorf("Error reading cert file: %s", err)
  246. }
  247. issuerContent, err := ioutil.ReadFile(issuerFile)
  248. if err != nil {
  249. t.Errorf("Error reading issuer file: %s", err)
  250. }
  251. cert, err := helpers.ParseCertificatePEM(certContent)
  252. if err != nil {
  253. t.Errorf("Error parsing cert file: %s", err)
  254. }
  255. issuer, err := helpers.ParseCertificatePEM(issuerContent)
  256. if err != nil {
  257. t.Errorf("Error parsing cert file: %s", err)
  258. }
  259. // Create an OCSP request.
  260. reqByte, err := goocsp.CreateRequest(cert, issuer, nil)
  261. if err != nil {
  262. t.Errorf("Error creating OCSP request: %s", err)
  263. }
  264. req, err := goocsp.ParseRequest(reqByte)
  265. if err != nil {
  266. t.Errorf("Error parsing OCSP request: %s", err)
  267. }
  268. // Create the template to be used in making an OCSP response.
  269. template := goocsp.Response{
  270. Status: goocsp.Good,
  271. SerialNumber: req.SerialNumber,
  272. ThisUpdate: time.Now(),
  273. NextUpdate: time.Now().AddDate(0, 1, 0),
  274. }
  275. keyPEM, err := ioutil.ReadFile("testdata/ca-key.pem")
  276. if err != nil {
  277. t.Errorf("Error reading private key file: %s", err)
  278. }
  279. priv, err := helpers.ParsePrivateKeyPEM(keyPEM)
  280. if err != nil {
  281. t.Errorf("Error parsing private key: %s", err)
  282. }
  283. // Create an OCSP response to be inserted into the DB.
  284. response, err := goocsp.CreateResponse(issuer, cert, template, priv)
  285. if err != nil {
  286. t.Errorf("Error creating OCSP response: %s", err)
  287. }
  288. // Create record for the OCSP response and add the record to the DB.
  289. ocsp := certdb.OCSPRecord{
  290. AKI: hex.EncodeToString(req.IssuerKeyHash),
  291. Body: string(response),
  292. Expiry: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
  293. Serial: req.SerialNumber.String(),
  294. }
  295. err = accessor.InsertOCSP(ocsp)
  296. if err != nil {
  297. t.Errorf("Error inserting OCSP record into DB: %s", err)
  298. }
  299. // Use the created Accessor to create new DBSource.
  300. src := NewDBSource(accessor)
  301. // Call Response() method on constructed request and check the output.
  302. response, _, err = src.Response(req)
  303. if err != nil {
  304. t.Error(err)
  305. }
  306. // Attempt to parse the returned response and make sure it is well formed.
  307. _, err = goocsp.ParseResponse(response, issuer)
  308. if err != nil {
  309. t.Errorf("Error parsing response: %v", err)
  310. }
  311. }