You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

330 lines
8.9 KiB

  1. // Copyright 2016 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package autocert
  5. import (
  6. "context"
  7. "crypto/ecdsa"
  8. "crypto/elliptic"
  9. "crypto/rand"
  10. "crypto/tls"
  11. "crypto/x509"
  12. "encoding/base64"
  13. "fmt"
  14. "net/http"
  15. "net/http/httptest"
  16. "testing"
  17. "time"
  18. "golang.org/x/crypto/acme"
  19. )
  20. func TestRenewalNext(t *testing.T) {
  21. now := time.Now()
  22. timeNow = func() time.Time { return now }
  23. defer func() { timeNow = time.Now }()
  24. man := &Manager{RenewBefore: 7 * 24 * time.Hour}
  25. defer man.stopRenew()
  26. tt := []struct {
  27. expiry time.Time
  28. min, max time.Duration
  29. }{
  30. {now.Add(90 * 24 * time.Hour), 83*24*time.Hour - renewJitter, 83 * 24 * time.Hour},
  31. {now.Add(time.Hour), 0, 1},
  32. {now, 0, 1},
  33. {now.Add(-time.Hour), 0, 1},
  34. }
  35. dr := &domainRenewal{m: man}
  36. for i, test := range tt {
  37. next := dr.next(test.expiry)
  38. if next < test.min || test.max < next {
  39. t.Errorf("%d: next = %v; want between %v and %v", i, next, test.min, test.max)
  40. }
  41. }
  42. }
  43. func TestRenewFromCache(t *testing.T) {
  44. // ACME CA server stub
  45. var ca *httptest.Server
  46. ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  47. w.Header().Set("Replay-Nonce", "nonce")
  48. if r.Method == "HEAD" {
  49. // a nonce request
  50. return
  51. }
  52. switch r.URL.Path {
  53. // discovery
  54. case "/":
  55. if err := discoTmpl.Execute(w, ca.URL); err != nil {
  56. t.Fatalf("discoTmpl: %v", err)
  57. }
  58. // client key registration
  59. case "/new-reg":
  60. w.Write([]byte("{}"))
  61. // domain authorization
  62. case "/new-authz":
  63. w.Header().Set("Location", ca.URL+"/authz/1")
  64. w.WriteHeader(http.StatusCreated)
  65. w.Write([]byte(`{"status": "valid"}`))
  66. // cert request
  67. case "/new-cert":
  68. var req struct {
  69. CSR string `json:"csr"`
  70. }
  71. decodePayload(&req, r.Body)
  72. b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
  73. csr, err := x509.ParseCertificateRequest(b)
  74. if err != nil {
  75. t.Fatalf("new-cert: CSR: %v", err)
  76. }
  77. der, err := dummyCert(csr.PublicKey, exampleDomain)
  78. if err != nil {
  79. t.Fatalf("new-cert: dummyCert: %v", err)
  80. }
  81. chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL)
  82. w.Header().Set("Link", chainUp)
  83. w.WriteHeader(http.StatusCreated)
  84. w.Write(der)
  85. // CA chain cert
  86. case "/ca-cert":
  87. der, err := dummyCert(nil, "ca")
  88. if err != nil {
  89. t.Fatalf("ca-cert: dummyCert: %v", err)
  90. }
  91. w.Write(der)
  92. default:
  93. t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
  94. }
  95. }))
  96. defer ca.Close()
  97. man := &Manager{
  98. Prompt: AcceptTOS,
  99. Cache: newMemCache(t),
  100. RenewBefore: 24 * time.Hour,
  101. Client: &acme.Client{
  102. DirectoryURL: ca.URL,
  103. },
  104. }
  105. defer man.stopRenew()
  106. // cache an almost expired cert
  107. key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  108. if err != nil {
  109. t.Fatal(err)
  110. }
  111. now := time.Now()
  112. cert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), exampleDomain)
  113. if err != nil {
  114. t.Fatal(err)
  115. }
  116. tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}}
  117. if err := man.cachePut(context.Background(), exampleCertKey, tlscert); err != nil {
  118. t.Fatal(err)
  119. }
  120. // veriy the renewal happened
  121. defer func() {
  122. testDidRenewLoop = func(next time.Duration, err error) {}
  123. }()
  124. done := make(chan struct{})
  125. testDidRenewLoop = func(next time.Duration, err error) {
  126. defer close(done)
  127. if err != nil {
  128. t.Errorf("testDidRenewLoop: %v", err)
  129. }
  130. // Next should be about 90 days:
  131. // dummyCert creates 90days expiry + account for man.RenewBefore.
  132. // Previous expiration was within 1 min.
  133. future := 88 * 24 * time.Hour
  134. if next < future {
  135. t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
  136. }
  137. // ensure the new cert is cached
  138. after := time.Now().Add(future)
  139. tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
  140. if err != nil {
  141. t.Fatalf("man.cacheGet: %v", err)
  142. }
  143. if !tlscert.Leaf.NotAfter.After(after) {
  144. t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
  145. }
  146. // verify the old cert is also replaced in memory
  147. man.stateMu.Lock()
  148. defer man.stateMu.Unlock()
  149. s := man.state[exampleCertKey]
  150. if s == nil {
  151. t.Fatalf("m.state[%q] is nil", exampleCertKey)
  152. }
  153. tlscert, err = s.tlscert()
  154. if err != nil {
  155. t.Fatalf("s.tlscert: %v", err)
  156. }
  157. if !tlscert.Leaf.NotAfter.After(after) {
  158. t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
  159. }
  160. }
  161. // trigger renew
  162. hello := clientHelloInfo(exampleDomain, true)
  163. if _, err := man.GetCertificate(hello); err != nil {
  164. t.Fatal(err)
  165. }
  166. // wait for renew loop
  167. select {
  168. case <-time.After(10 * time.Second):
  169. t.Fatal("renew took too long to occur")
  170. case <-done:
  171. }
  172. }
  173. func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
  174. man := &Manager{
  175. Prompt: AcceptTOS,
  176. Cache: newMemCache(t),
  177. RenewBefore: 24 * time.Hour,
  178. Client: &acme.Client{
  179. DirectoryURL: "invalid",
  180. },
  181. }
  182. defer man.stopRenew()
  183. // cache a recently renewed cert with a different private key
  184. newKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  185. if err != nil {
  186. t.Fatal(err)
  187. }
  188. now := time.Now()
  189. newCert, err := dateDummyCert(newKey.Public(), now.Add(-2*time.Hour), now.Add(time.Hour*24*90), exampleDomain)
  190. if err != nil {
  191. t.Fatal(err)
  192. }
  193. newLeaf, err := validCert(exampleCertKey, [][]byte{newCert}, newKey)
  194. if err != nil {
  195. t.Fatal(err)
  196. }
  197. newTLSCert := &tls.Certificate{PrivateKey: newKey, Certificate: [][]byte{newCert}, Leaf: newLeaf}
  198. if err := man.cachePut(context.Background(), exampleCertKey, newTLSCert); err != nil {
  199. t.Fatal(err)
  200. }
  201. // set internal state to an almost expired cert
  202. key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  203. if err != nil {
  204. t.Fatal(err)
  205. }
  206. oldCert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), exampleDomain)
  207. if err != nil {
  208. t.Fatal(err)
  209. }
  210. oldLeaf, err := validCert(exampleCertKey, [][]byte{oldCert}, key)
  211. if err != nil {
  212. t.Fatal(err)
  213. }
  214. man.stateMu.Lock()
  215. if man.state == nil {
  216. man.state = make(map[certKey]*certState)
  217. }
  218. s := &certState{
  219. key: key,
  220. cert: [][]byte{oldCert},
  221. leaf: oldLeaf,
  222. }
  223. man.state[exampleCertKey] = s
  224. man.stateMu.Unlock()
  225. // veriy the renewal accepted the newer cached cert
  226. defer func() {
  227. testDidRenewLoop = func(next time.Duration, err error) {}
  228. }()
  229. done := make(chan struct{})
  230. testDidRenewLoop = func(next time.Duration, err error) {
  231. defer close(done)
  232. if err != nil {
  233. t.Errorf("testDidRenewLoop: %v", err)
  234. }
  235. // Next should be about 90 days
  236. // Previous expiration was within 1 min.
  237. future := 88 * 24 * time.Hour
  238. if next < future {
  239. t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
  240. }
  241. // ensure the cached cert was not modified
  242. tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
  243. if err != nil {
  244. t.Fatalf("man.cacheGet: %v", err)
  245. }
  246. if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
  247. t.Errorf("cache leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
  248. }
  249. // verify the old cert is also replaced in memory
  250. man.stateMu.Lock()
  251. defer man.stateMu.Unlock()
  252. s := man.state[exampleCertKey]
  253. if s == nil {
  254. t.Fatalf("m.state[%q] is nil", exampleCertKey)
  255. }
  256. stateKey := s.key.Public().(*ecdsa.PublicKey)
  257. if stateKey.X.Cmp(newKey.X) != 0 || stateKey.Y.Cmp(newKey.Y) != 0 {
  258. t.Fatalf("state key was not updated from cache x: %v y: %v; want x: %v y: %v", stateKey.X, stateKey.Y, newKey.X, newKey.Y)
  259. }
  260. tlscert, err = s.tlscert()
  261. if err != nil {
  262. t.Fatalf("s.tlscert: %v", err)
  263. }
  264. if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
  265. t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
  266. }
  267. // verify the private key is replaced in the renewal state
  268. r := man.renewal[exampleCertKey]
  269. if r == nil {
  270. t.Fatalf("m.renewal[%q] is nil", exampleCertKey)
  271. }
  272. renewalKey := r.key.Public().(*ecdsa.PublicKey)
  273. if renewalKey.X.Cmp(newKey.X) != 0 || renewalKey.Y.Cmp(newKey.Y) != 0 {
  274. t.Fatalf("renewal private key was not updated from cache x: %v y: %v; want x: %v y: %v", renewalKey.X, renewalKey.Y, newKey.X, newKey.Y)
  275. }
  276. }
  277. // assert the expiring cert is returned from state
  278. hello := clientHelloInfo(exampleDomain, true)
  279. tlscert, err := man.GetCertificate(hello)
  280. if err != nil {
  281. t.Fatal(err)
  282. }
  283. if !oldLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
  284. t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, oldLeaf.NotAfter)
  285. }
  286. // trigger renew
  287. go man.renew(exampleCertKey, s.key, s.leaf.NotAfter)
  288. // wait for renew loop
  289. select {
  290. case <-time.After(10 * time.Second):
  291. t.Fatal("renew took too long to occur")
  292. case <-done:
  293. // assert the new cert is returned from state after renew
  294. hello := clientHelloInfo(exampleDomain, true)
  295. tlscert, err := man.GetCertificate(hello)
  296. if err != nil {
  297. t.Fatal(err)
  298. }
  299. if !newTLSCert.Leaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
  300. t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newTLSCert.Leaf.NotAfter)
  301. }
  302. }
  303. }