header.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package hpkp
  2. import (
  3. "net/http"
  4. "strconv"
  5. "strings"
  6. "time"
  7. )
  8. // Header holds a domain's hpkp information
  9. type Header struct {
  10. Created int64
  11. MaxAge int64
  12. IncludeSubDomains bool
  13. Permanent bool
  14. Sha256Pins []string
  15. ReportURI string
  16. }
  17. // Matches checks whether the provided pin is in the header list
  18. func (h *Header) Matches(pin string) bool {
  19. for i := range h.Sha256Pins {
  20. if h.Sha256Pins[i] == pin {
  21. return true
  22. }
  23. }
  24. return false
  25. }
  26. // ParseHeader parses the hpkp information from an http.Response.
  27. func ParseHeader(resp *http.Response) *Header {
  28. if resp == nil {
  29. return nil
  30. }
  31. // only make a header when using TLS
  32. if resp.TLS == nil {
  33. return nil
  34. }
  35. v, ok := resp.Header["Public-Key-Pins"]
  36. if !ok {
  37. return nil
  38. }
  39. // use the first header per RFC
  40. return populate(&Header{}, v[0])
  41. }
  42. // ParseReportOnlyHeader parses the hpkp information from an http.Response.
  43. // The resulting header information should not be cached as max_age is
  44. // ignored on HPKP-RO headers per the RFC.
  45. func ParseReportOnlyHeader(resp *http.Response) *Header {
  46. if resp == nil {
  47. return nil
  48. }
  49. // only make a header when using TLS
  50. if resp.TLS == nil {
  51. return nil
  52. }
  53. v, ok := resp.Header["Public-Key-Pins-Report-Only"]
  54. if !ok {
  55. return nil
  56. }
  57. // use the first header per RFC
  58. return populate(&Header{}, v[0])
  59. }
  60. func populate(h *Header, v string) *Header {
  61. h.Sha256Pins = []string{}
  62. for _, field := range strings.Split(v, ";") {
  63. field = strings.TrimSpace(field)
  64. i := strings.Index(field, "pin-sha256")
  65. if i >= 0 {
  66. h.Sha256Pins = append(h.Sha256Pins, field[i+12:len(field)-1])
  67. continue
  68. }
  69. i = strings.Index(field, "report-uri")
  70. if i >= 0 {
  71. h.ReportURI = field[i+12 : len(field)-1]
  72. continue
  73. }
  74. i = strings.Index(field, "max-age=")
  75. if i >= 0 {
  76. ma, err := strconv.Atoi(field[i+8:])
  77. if err == nil {
  78. h.MaxAge = int64(ma)
  79. }
  80. continue
  81. }
  82. if strings.Contains(field, "includeSubDomains") {
  83. h.IncludeSubDomains = true
  84. continue
  85. }
  86. }
  87. h.Created = time.Now().Unix()
  88. return h
  89. }