aboutsummaryrefslogtreecommitdiff
path: root/crypto
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2023-08-01 18:26:05 -0700
committerMike Crute <mike@crute.us>2023-08-01 18:26:09 -0700
commitb3b8bd0d937b6143cda877127dae4cb1385b866e (patch)
treef6d09b191a74fcdd5b6ffc98bc3bef18eb98e2b9 /crypto
parentb29262b0a5384246b04764488869d1fbc81e2d1a (diff)
downloadgolib-b3b8bd0d937b6143cda877127dae4cb1385b866e.tar.bz2
golib-b3b8bd0d937b6143cda877127dae4cb1385b866e.tar.xz
golib-b3b8bd0d937b6143cda877127dae4cb1385b866e.zip
crypto: fork x/crypto autocertv0.6.0
Diffstat (limited to 'crypto')
-rw-r--r--crypto/acme/autocert/ABOUT_FORK.txt6
-rw-r--r--crypto/acme/autocert/fork/autocert.go1286
-rw-r--r--crypto/acme/autocert/fork/autocert_test.go1058
-rw-r--r--crypto/acme/autocert/fork/cache.go135
-rw-r--r--crypto/acme/autocert/fork/cache_test.go66
-rw-r--r--crypto/acme/autocert/fork/internal/acmetest/ca.go817
-rw-r--r--crypto/acme/autocert/fork/listener.go155
-rw-r--r--crypto/acme/autocert/fork/renewal.go156
-rw-r--r--crypto/acme/autocert/fork/renewal_test.go269
-rw-r--r--crypto/ocsp/client.go125
10 files changed, 4073 insertions, 0 deletions
diff --git a/crypto/acme/autocert/ABOUT_FORK.txt b/crypto/acme/autocert/ABOUT_FORK.txt
new file mode 100644
index 0000000..e8e41f0
--- /dev/null
+++ b/crypto/acme/autocert/ABOUT_FORK.txt
@@ -0,0 +1,6 @@
1This folder is a fork of x/crypto/acme/autocert that contains the following
2changes. This is being carried here because stdlib is very slow to picking up
3the changes.
4
5- DNS-01 challenges
6- OCSP stapling
diff --git a/crypto/acme/autocert/fork/autocert.go b/crypto/acme/autocert/fork/autocert.go
new file mode 100644
index 0000000..d57c49a
--- /dev/null
+++ b/crypto/acme/autocert/fork/autocert.go
@@ -0,0 +1,1286 @@
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
5// Package autocert provides automatic access to certificates from Let's Encrypt
6// and any other ACME-based CA.
7//
8// This package is a work in progress and makes no API stability promises.
9package fork
10
11import (
12 "bytes"
13 "context"
14 "crypto"
15 "crypto/ecdsa"
16 "crypto/elliptic"
17 "crypto/rand"
18 "crypto/rsa"
19 "crypto/tls"
20 "crypto/x509"
21 "crypto/x509/pkix"
22 "encoding/pem"
23 "errors"
24 "fmt"
25 "io"
26 mathrand "math/rand"
27 "net"
28 "net/http"
29 "path"
30 "strings"
31 "sync"
32 "time"
33
34 "code.crute.us/mcrute/golib/crypto/ocsp"
35
36 "golang.org/x/crypto/acme"
37 "golang.org/x/net/idna"
38)
39
40// DefaultACMEDirectory is the default ACME Directory URL used when the Manager's Client is nil.
41const DefaultACMEDirectory = "https://acme-v02.api.letsencrypt.org/directory"
42
43// createCertRetryAfter is how much time to wait before removing a failed state
44// entry due to an unsuccessful createCert call.
45// This is a variable instead of a const for testing.
46// TODO: Consider making it configurable or an exp backoff?
47var createCertRetryAfter = time.Minute
48
49// pseudoRand is safe for concurrent use.
50var pseudoRand *lockedMathRand
51
52var errPreRFC = errors.New("autocert: ACME server doesn't support RFC 8555")
53
54func init() {
55 src := mathrand.NewSource(time.Now().UnixNano())
56 pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
57}
58
59// AcceptTOS is a Manager.Prompt function that always returns true to
60// indicate acceptance of the CA's Terms of Service during account
61// registration.
62func AcceptTOS(tosURL string) bool { return true }
63
64// HostPolicy specifies which host names the Manager is allowed to respond to.
65// It returns a non-nil error if the host should be rejected.
66// The returned error is accessible via tls.Conn.Handshake and its callers.
67// See Manager's HostPolicy field and GetCertificate method docs for more details.
68type HostPolicy func(ctx context.Context, host string) error
69
70// HostWhitelist returns a policy where only the specified host names are allowed.
71// Only exact matches are currently supported. Subdomains, regexp or wildcard
72// will not match.
73//
74// Note that all hosts will be converted to Punycode via idna.Lookup.ToASCII so that
75// Manager.GetCertificate can handle the Unicode IDN and mixedcase hosts correctly.
76// Invalid hosts will be silently ignored.
77func HostWhitelist(hosts ...string) HostPolicy {
78 whitelist := make(map[string]bool, len(hosts))
79 for _, h := range hosts {
80 if h, err := idna.Lookup.ToASCII(h); err == nil {
81 whitelist[h] = true
82 }
83 }
84 return func(_ context.Context, host string) error {
85 if !whitelist[host] {
86 return fmt.Errorf("acme/autocert: host %q not configured in HostWhitelist", host)
87 }
88 return nil
89 }
90}
91
92// defaultHostPolicy is used when Manager.HostPolicy is not set.
93func defaultHostPolicy(context.Context, string) error {
94 return nil
95}
96
97// DNSManager is used by the Manager for handling dns-01 challenges with
98// external DNS services.
99type DNSManager interface {
100 // Fulfill receives a record in the format returned by
101 // acme.DNS01ChallengeRecord and places it into an external DNS service.
102 //
103 // The ACME challenge will be validated by the CA soon after this function
104 // returns. Therefore, this should ensure that DNS has propagated such that
105 // the ACME server can validate the record and block until it can verify
106 // the propagation.
107 Fulfill(ctx context.Context, domain string, record string) error
108
109 // Cleanup receives a record in the format returned by
110 // acme.DNS01ChallengeRecord and should remove it from the external DNS
111 // service.
112 Cleanup(ctx context.Context, domain string, record string)
113}
114
115// Manager is a stateful certificate manager built on top of acme.Client.
116// It obtains and refreshes certificates automatically using "tls-alpn-01",
117// "http-01", or "dns-01" challenge types, as well as providing them to a
118// TLS server via tls.Config.
119//
120// You must specify a cache implementation, such as DirCache,
121// to reuse obtained certificates across program restarts.
122// Otherwise your server is very likely to exceed the certificate
123// issuer's request rate limits.
124type Manager struct {
125 // Prompt specifies a callback function to conditionally accept a CA's Terms of Service (TOS).
126 // The registration may require the caller to agree to the CA's TOS.
127 // If so, Manager calls Prompt with a TOS URL provided by the CA. Prompt should report
128 // whether the caller agrees to the terms.
129 //
130 // To always accept the terms, the callers can use AcceptTOS.
131 Prompt func(tosURL string) bool
132
133 // Cache optionally stores and retrieves previously-obtained certificates
134 // and other state. If nil, certs will only be cached for the lifetime of
135 // the Manager. Multiple Managers can share the same Cache.
136 //
137 // Using a persistent Cache, such as DirCache, is strongly recommended.
138 Cache Cache
139
140 // HostPolicy controls which domains the Manager will attempt
141 // to retrieve new certificates for. It does not affect cached certs.
142 //
143 // If non-nil, HostPolicy is called before requesting a new cert.
144 // If nil, all hosts are currently allowed. This is not recommended,
145 // as it opens a potential attack where clients connect to a server
146 // by IP address and pretend to be asking for an incorrect host name.
147 // Manager will attempt to obtain a certificate for that host, incorrectly,
148 // eventually reaching the CA's rate limit for certificate requests
149 // and making it impossible to obtain actual certificates.
150 //
151 // See GetCertificate for more details.
152 HostPolicy HostPolicy
153
154 // RenewBefore optionally specifies how early certificates should
155 // be renewed before they expire.
156 //
157 // If zero, they're renewed 30 days before expiration.
158 RenewBefore time.Duration
159
160 // Client is used to perform low-level operations, such as account registration
161 // and requesting new certificates.
162 //
163 // If Client is nil, a zero-value acme.Client is used with DefaultACMEDirectory
164 // as the directory endpoint.
165 // If the Client.Key is nil, a new ECDSA P-256 key is generated and,
166 // if Cache is not nil, stored in cache.
167 //
168 // Mutating the field after the first call of GetCertificate method will have no effect.
169 Client *acme.Client
170
171 // Email optionally specifies a contact email address.
172 // This is used by CAs, such as Let's Encrypt, to notify about problems
173 // with issued certificates.
174 //
175 // If the Client's account key is already registered, Email is not used.
176 Email string
177
178 // ForceRSA used to make the Manager generate RSA certificates. It is now ignored.
179 //
180 // Deprecated: the Manager will request the correct type of certificate based
181 // on what each client supports.
182 ForceRSA bool
183
184 // StapleOCSP makes the Manager request an OCSP response from the CA
185 // and will staple it to the certificate returned by GetCertificate.
186 // This is currently best-effort and if the CA fails to return a valid
187 // OCSP response the certificate will be returned without the staple.
188 StapleOCSP bool
189
190 // ExtraExtensions are used when generating a new CSR (Certificate Request),
191 // thus allowing customization of the resulting certificate.
192 // For instance, TLS Feature Extension (RFC 7633) can be used
193 // to prevent an OCSP downgrade attack.
194 //
195 // The field value is passed to crypto/x509.CreateCertificateRequest
196 // in the template's ExtraExtensions field as is.
197 ExtraExtensions []pkix.Extension
198
199 // ExternalAccountBinding optionally represents an arbitrary binding to an
200 // account of the CA to which the ACME server is tied.
201 // See RFC 8555, Section 7.3.4 for more details.
202 ExternalAccountBinding *acme.ExternalAccountBinding
203
204 // DNSManager is used to respond to dns-01 challenges returned from the CA.
205 // If this field is nil then DNS challenges will not be requested from the
206 // CA.
207 DNSManager DNSManager
208
209 clientMu sync.Mutex
210 client *acme.Client // initialized by acmeClient method
211 ocspClient *ocsp.Client // initialized by getOcspClient method
212
213 stateMu sync.Mutex
214 state map[certKey]*certState
215
216 // renewal tracks the set of domains currently running renewal timers.
217 renewalMu sync.Mutex
218 renewal map[certKey]*domainRenewal
219
220 // challengeMu guards tryHTTP01, certTokens and httpTokens.
221 challengeMu sync.RWMutex
222 // tryHTTP01 indicates whether the Manager should try "http-01" challenge type
223 // during the authorization flow.
224 tryHTTP01 bool
225 // httpTokens contains response body values for http-01 challenges
226 // and is keyed by the URL path at which a challenge response is expected
227 // to be provisioned.
228 // The entries are stored for the duration of the authorization flow.
229 httpTokens map[string][]byte
230 // certTokens contains temporary certificates for tls-alpn-01 challenges
231 // and is keyed by the domain name which matches the ClientHello server name.
232 // The entries are stored for the duration of the authorization flow.
233 certTokens map[string]*tls.Certificate
234
235 // nowFunc, if not nil, returns the current time. This may be set for
236 // testing purposes.
237 nowFunc func() time.Time
238}
239
240// certKey is the key by which certificates are tracked in state, renewal and cache.
241type certKey struct {
242 domain string // without trailing dot
243 isRSA bool // RSA cert for legacy clients (as opposed to default ECDSA)
244 isToken bool // tls-based challenge token cert; key type is undefined regardless of isRSA
245}
246
247func (c certKey) String() string {
248 if c.isToken {
249 return c.domain + "+token"
250 }
251 if c.isRSA {
252 return c.domain + "+rsa"
253 }
254 return c.domain
255}
256
257// TLSConfig creates a new TLS config suitable for net/http.Server servers,
258// supporting HTTP/2 and the tls-alpn-01 ACME challenge type.
259func (m *Manager) TLSConfig() *tls.Config {
260 return &tls.Config{
261 GetCertificate: m.GetCertificate,
262 NextProtos: []string{
263 "h2", "http/1.1", // enable HTTP/2
264 acme.ALPNProto, // enable tls-alpn ACME challenges
265 },
266 }
267}
268
269// GetCertificate implements the tls.Config.GetCertificate hook.
270// It provides a TLS certificate for hello.ServerName host, including answering
271// tls-alpn-01 challenges.
272// All other fields of hello are ignored.
273//
274// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
275// a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation.
276// The error is propagated back to the caller of GetCertificate and is user-visible.
277// This does not affect cached certs. See HostPolicy field description for more details.
278//
279// If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will
280// also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler for http-01.
281// If DNSManager is specified, no additional configuration is required and GetCertificate
282// can be used directly for dns-01 challenges.
283func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
284 if m.Prompt == nil {
285 return nil, errors.New("acme/autocert: Manager.Prompt not set")
286 }
287
288 name := hello.ServerName
289 if name == "" {
290 return nil, errors.New("acme/autocert: missing server name")
291 }
292 if !strings.Contains(strings.Trim(name, "."), ".") {
293 return nil, errors.New("acme/autocert: server name component count invalid")
294 }
295
296 // Note that this conversion is necessary because some server names in the handshakes
297 // started by some clients (such as cURL) are not converted to Punycode, which will
298 // prevent us from obtaining certificates for them. In addition, we should also treat
299 // example.com and EXAMPLE.COM as equivalent and return the same certificate for them.
300 // Fortunately, this conversion also helped us deal with this kind of mixedcase problems.
301 //
302 // Due to the "σςΣ" problem (see https://unicode.org/faq/idn.html#22), we can't use
303 // idna.Punycode.ToASCII (or just idna.ToASCII) here.
304 name, err := idna.Lookup.ToASCII(name)
305 if err != nil {
306 return nil, errors.New("acme/autocert: server name contains invalid character")
307 }
308
309 // In the worst-case scenario, the timeout needs to account for caching, host policy,
310 // domain ownership verification and certificate issuance.
311 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
312 defer cancel()
313
314 // Check whether this is a token cert requested for TLS-ALPN challenge.
315 if wantsTokenCert(hello) {
316 m.challengeMu.RLock()
317 defer m.challengeMu.RUnlock()
318 if cert := m.certTokens[name]; cert != nil {
319 return cert, nil
320 }
321 if cert, err := m.cacheGet(ctx, certKey{domain: name, isToken: true}); err == nil {
322 return cert, nil
323 }
324 // TODO: cache error results?
325 return nil, fmt.Errorf("acme/autocert: no token cert for %q", name)
326 }
327
328 // regular domain
329 ck := certKey{
330 domain: strings.TrimSuffix(name, "."), // golang.org/issue/18114
331 isRSA: !supportsECDSA(hello),
332 }
333 cert, err := m.cert(ctx, ck)
334 if err == nil {
335 return cert, nil
336 }
337 if err != ErrCacheMiss {
338 return nil, err
339 }
340
341 // first-time
342 if err := m.hostPolicy()(ctx, name); err != nil {
343 return nil, err
344 }
345 cert, err = m.createCert(ctx, ck)
346 if err != nil {
347 return nil, err
348 }
349 m.cachePut(ctx, ck, cert)
350 return cert, nil
351}
352
353// wantsTokenCert reports whether a TLS request with SNI is made by a CA server
354// for a challenge verification.
355func wantsTokenCert(hello *tls.ClientHelloInfo) bool {
356 // tls-alpn-01
357 if len(hello.SupportedProtos) == 1 && hello.SupportedProtos[0] == acme.ALPNProto {
358 return true
359 }
360 return false
361}
362
363func supportsECDSA(hello *tls.ClientHelloInfo) bool {
364 // The "signature_algorithms" extension, if present, limits the key exchange
365 // algorithms allowed by the cipher suites. See RFC 5246, section 7.4.1.4.1.
366 if hello.SignatureSchemes != nil {
367 ecdsaOK := false
368 schemeLoop:
369 for _, scheme := range hello.SignatureSchemes {
370 const tlsECDSAWithSHA1 tls.SignatureScheme = 0x0203 // constant added in Go 1.10
371 switch scheme {
372 case tlsECDSAWithSHA1, tls.ECDSAWithP256AndSHA256,
373 tls.ECDSAWithP384AndSHA384, tls.ECDSAWithP521AndSHA512:
374 ecdsaOK = true
375 break schemeLoop
376 }
377 }
378 if !ecdsaOK {
379 return false
380 }
381 }
382 if hello.SupportedCurves != nil {
383 ecdsaOK := false
384 for _, curve := range hello.SupportedCurves {
385 if curve == tls.CurveP256 {
386 ecdsaOK = true
387 break
388 }
389 }
390 if !ecdsaOK {
391 return false
392 }
393 }
394 for _, suite := range hello.CipherSuites {
395 switch suite {
396 case tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
397 tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
398 tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
399 tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
400 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
401 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
402 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
403 return true
404 }
405 }
406 return false
407}
408
409// HTTPHandler configures the Manager to provision ACME "http-01" challenge responses.
410// It returns an http.Handler that responds to the challenges and must be
411// running on port 80. If it receives a request that is not an ACME challenge,
412// it delegates the request to the optional fallback handler.
413//
414// If fallback is nil, the returned handler redirects all GET and HEAD requests
415// to the default TLS port 443 with 302 Found status code, preserving the original
416// request path and query. It responds with 400 Bad Request to all other HTTP methods.
417// The fallback is not protected by the optional HostPolicy.
418//
419// Because the fallback handler is run with unencrypted port 80 requests,
420// the fallback should not serve TLS-only requests.
421//
422// If HTTPHandler is never called, the Manager will use the "tls-alpn-01"
423// challenge for domain verification and/or "dns-01" if DNSManager is specified.
424func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler {
425 m.challengeMu.Lock()
426 defer m.challengeMu.Unlock()
427 m.tryHTTP01 = true
428
429 if fallback == nil {
430 fallback = http.HandlerFunc(handleHTTPRedirect)
431 }
432 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
433 if !strings.HasPrefix(r.URL.Path, "/.well-known/acme-challenge/") {
434 fallback.ServeHTTP(w, r)
435 return
436 }
437 // A reasonable context timeout for cache and host policy only,
438 // because we don't wait for a new certificate issuance here.
439 ctx, cancel := context.WithTimeout(r.Context(), time.Minute)
440 defer cancel()
441 if err := m.hostPolicy()(ctx, r.Host); err != nil {
442 http.Error(w, err.Error(), http.StatusForbidden)
443 return
444 }
445 data, err := m.httpToken(ctx, r.URL.Path)
446 if err != nil {
447 http.Error(w, err.Error(), http.StatusNotFound)
448 return
449 }
450 w.Write(data)
451 })
452}
453
454func handleHTTPRedirect(w http.ResponseWriter, r *http.Request) {
455 if r.Method != "GET" && r.Method != "HEAD" {
456 http.Error(w, "Use HTTPS", http.StatusBadRequest)
457 return
458 }
459 target := "https://" + stripPort(r.Host) + r.URL.RequestURI()
460 http.Redirect(w, r, target, http.StatusFound)
461}
462
463func stripPort(hostport string) string {
464 host, _, err := net.SplitHostPort(hostport)
465 if err != nil {
466 return hostport
467 }
468 return net.JoinHostPort(host, "443")
469}
470
471// cert returns an existing certificate either from m.state or cache.
472// If a certificate is found in cache but not in m.state, the latter will be filled
473// with the cached value.
474func (m *Manager) cert(ctx context.Context, ck certKey) (*tls.Certificate, error) {
475 m.stateMu.Lock()
476 if s, ok := m.state[ck]; ok {
477 m.stateMu.Unlock()
478 s.RLock()
479 defer s.RUnlock()
480 m.stapleOcsp(ctx, s)
481 return s.tlscert()
482 }
483 defer m.stateMu.Unlock()
484 cert, err := m.cacheGet(ctx, ck)
485 if err != nil {
486 return nil, err
487 }
488 signer, ok := cert.PrivateKey.(crypto.Signer)
489 if !ok {
490 return nil, errors.New("acme/autocert: private key cannot sign")
491 }
492 if m.state == nil {
493 m.state = make(map[certKey]*certState)
494 }
495 s := &certState{
496 key: signer,
497 cert: cert.Certificate,
498 leaf: cert.Leaf,
499 }
500 m.state[ck] = s
501 m.stapleOcsp(ctx, s) // Should happen before startRenew
502 m.startRenew(ck, s)
503 return cert, nil
504}
505
506// cacheGet always returns a valid certificate, or an error otherwise.
507// If a cached certificate exists but is not valid, ErrCacheMiss is returned.
508func (m *Manager) cacheGet(ctx context.Context, ck certKey) (*tls.Certificate, error) {
509 if m.Cache == nil {
510 return nil, ErrCacheMiss
511 }
512 data, err := m.Cache.Get(ctx, ck.String())
513 if err != nil {
514 return nil, err
515 }
516
517 // private
518 priv, pub := pem.Decode(data)
519 if priv == nil || !strings.Contains(priv.Type, "PRIVATE") {
520 return nil, ErrCacheMiss
521 }
522 privKey, err := parsePrivateKey(priv.Bytes)
523 if err != nil {
524 return nil, err
525 }
526
527 // public
528 var pubDER [][]byte
529 for len(pub) > 0 {
530 var b *pem.Block
531 b, pub = pem.Decode(pub)
532 if b == nil {
533 break
534 }
535 pubDER = append(pubDER, b.Bytes)
536 }
537 if len(pub) > 0 {
538 // Leftover content not consumed by pem.Decode. Corrupt. Ignore.
539 return nil, ErrCacheMiss
540 }
541
542 // verify and create TLS cert
543 leaf, err := validCert(ck, pubDER, privKey, m.now())
544 if err != nil {
545 return nil, ErrCacheMiss
546 }
547 tlscert := &tls.Certificate{
548 Certificate: pubDER,
549 PrivateKey: privKey,
550 Leaf: leaf,
551 }
552 return tlscert, nil
553}
554
555func (m *Manager) cachePut(ctx context.Context, ck certKey, tlscert *tls.Certificate) error {
556 if m.Cache == nil {
557 return nil
558 }
559
560 // contains PEM-encoded data
561 var buf bytes.Buffer
562
563 // private
564 switch key := tlscert.PrivateKey.(type) {
565 case *ecdsa.PrivateKey:
566 if err := encodeECDSAKey(&buf, key); err != nil {
567 return err
568 }
569 case *rsa.PrivateKey:
570 b := x509.MarshalPKCS1PrivateKey(key)
571 pb := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: b}
572 if err := pem.Encode(&buf, pb); err != nil {
573 return err
574 }
575 default:
576 return errors.New("acme/autocert: unknown private key type")
577 }
578
579 // public
580 for _, b := range tlscert.Certificate {
581 pb := &pem.Block{Type: "CERTIFICATE", Bytes: b}
582 if err := pem.Encode(&buf, pb); err != nil {
583 return err
584 }
585 }
586
587 return m.Cache.Put(ctx, ck.String(), buf.Bytes())
588}
589
590func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
591 b, err := x509.MarshalECPrivateKey(key)
592 if err != nil {
593 return err
594 }
595 pb := &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
596 return pem.Encode(w, pb)
597}
598
599// createCert starts the domain ownership verification and returns a certificate
600// for that domain upon success.
601//
602// If the domain is already being verified, it waits for the existing verification to complete.
603// Either way, createCert blocks for the duration of the whole process.
604func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate, error) {
605 // TODO: maybe rewrite this whole piece using sync.Once
606 state, err := m.certState(ck)
607 if err != nil {
608 return nil, err
609 }
610 // state may exist if another goroutine is already working on it
611 // in which case just wait for it to finish
612 if !state.locked {
613 state.RLock()
614 defer state.RUnlock()
615 m.stapleOcsp(ctx, state)
616 return state.tlscert()
617 }
618
619 // We are the first; state is locked.
620 // Unblock the readers when domain ownership is verified
621 // and we got the cert or the process failed.
622 defer state.Unlock()
623 state.locked = false
624
625 der, leaf, err := m.authorizedCert(ctx, state.key, ck)
626 if err != nil {
627 // Remove the failed state after some time,
628 // making the manager call createCert again on the following TLS hello.
629 didRemove := testDidRemoveState // The lifetime of this timer is untracked, so copy mutable local state to avoid races.
630 time.AfterFunc(createCertRetryAfter, func() {
631 defer didRemove(ck)
632 m.stateMu.Lock()
633 defer m.stateMu.Unlock()
634 // Verify the state hasn't changed and it's still invalid
635 // before deleting.
636 s, ok := m.state[ck]
637 if !ok {
638 return
639 }
640 if _, err := validCert(ck, s.cert, s.key, m.now()); err == nil {
641 return
642 }
643 delete(m.state, ck)
644 })
645 return nil, err
646 }
647 state.cert = der
648 state.leaf = leaf
649 m.stapleOcsp(ctx, state) // Should happen before startRenew
650 m.startRenew(ck, state)
651 return state.tlscert()
652}
653
654// certState returns a new or existing certState.
655// If a new certState is returned, state.exist is false and the state is locked.
656// The returned error is non-nil only in the case where a new state could not be created.
657func (m *Manager) certState(ck certKey) (*certState, error) {
658 m.stateMu.Lock()
659 defer m.stateMu.Unlock()
660 if m.state == nil {
661 m.state = make(map[certKey]*certState)
662 }
663 // existing state
664 if state, ok := m.state[ck]; ok {
665 return state, nil
666 }
667
668 // new locked state
669 var (
670 err error
671 key crypto.Signer
672 )
673 if ck.isRSA {
674 key, err = rsa.GenerateKey(rand.Reader, 2048)
675 } else {
676 key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
677 }
678 if err != nil {
679 return nil, err
680 }
681
682 state := &certState{
683 key: key,
684 locked: true,
685 }
686 state.Lock() // will be unlocked by m.certState caller
687 m.state[ck] = state
688 return state, nil
689}
690
691// stapleOcsp fetches an OCSP response and stores it on the certState for later stapling
692func (m *Manager) stapleOcsp(ctx context.Context, s *certState) error {
693 // Only staple if that's the requested behavior
694 if !m.StapleOCSP {
695 return nil
696 }
697
698 // Don't staple if there's an existing fresh staple
699 if s.ocsp != nil && s.nextOcsp.Add(-time.Hour).Before(time.Now()) {
700 return nil
701 }
702
703 tc, err := s.tlscert()
704 if err != nil {
705 return err
706 }
707
708 raw, res, err := m.getOcspClient().Fetch(ctx, tc)
709 if err != nil {
710 return err
711 }
712
713 s.ocsp = raw
714 s.nextOcsp = res.NextUpdate
715
716 return nil
717}
718
719func (m *Manager) getOcspClient() *ocsp.Client {
720 m.clientMu.Lock()
721 defer m.clientMu.Unlock()
722 if m.ocspClient == nil {
723 m.ocspClient = &ocsp.Client{}
724 }
725 return m.ocspClient
726}
727
728// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
729// The key argument is the certificate private key.
730func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck certKey) (der [][]byte, leaf *x509.Certificate, err error) {
731 csr, err := certRequest(key, ck.domain, m.ExtraExtensions)
732 if err != nil {
733 return nil, nil, err
734 }
735
736 client, err := m.acmeClient(ctx)
737 if err != nil {
738 return nil, nil, err
739 }
740 dir, err := client.Discover(ctx)
741 if err != nil {
742 return nil, nil, err
743 }
744 if dir.OrderURL == "" {
745 return nil, nil, errPreRFC
746 }
747
748 o, err := m.verifyRFC(ctx, client, ck.domain)
749 if err != nil {
750 return nil, nil, err
751 }
752 chain, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
753 if err != nil {
754 return nil, nil, err
755 }
756
757 leaf, err = validCert(ck, chain, key, m.now())
758 if err != nil {
759 return nil, nil, err
760 }
761 return chain, leaf, nil
762}
763
764// verifyRFC runs the identifier (domain) order-based authorization flow for RFC compliant CAs
765// using each applicable ACME challenge type.
766func (m *Manager) verifyRFC(ctx context.Context, client *acme.Client, domain string) (*acme.Order, error) {
767 // Try each supported challenge type starting with a new order each time.
768 // The nextTyp index of the next challenge type to try is shared across
769 // all order authorizations: if we've tried a challenge type once and it didn't work,
770 // it will most likely not work on another order's authorization either.
771 challengeTypes := m.supportedChallengeTypes()
772 nextTyp := 0 // challengeTypes index
773AuthorizeOrderLoop:
774 for {
775 o, err := client.AuthorizeOrder(ctx, acme.DomainIDs(domain))
776 if err != nil {
777 return nil, err
778 }
779 // Remove all hanging authorizations to reduce rate limit quotas
780 // after we're done.
781 defer func(urls []string) {
782 go m.deactivatePendingAuthz(urls)
783 }(o.AuthzURLs)
784
785 // Check if there's actually anything we need to do.
786 switch o.Status {
787 case acme.StatusReady:
788 // Already authorized.
789 return o, nil
790 case acme.StatusPending:
791 // Continue normal Order-based flow.
792 default:
793 return nil, fmt.Errorf("acme/autocert: invalid new order status %q; order URL: %q", o.Status, o.URI)
794 }
795
796 // Satisfy all pending authorizations.
797 for _, zurl := range o.AuthzURLs {
798 z, err := client.GetAuthorization(ctx, zurl)
799 if err != nil {
800 return nil, err
801 }
802 if z.Status != acme.StatusPending {
803 // We are interested only in pending authorizations.
804 continue
805 }
806 // Pick the next preferred challenge.
807 var chal *acme.Challenge
808 for chal == nil && nextTyp < len(challengeTypes) {
809 chal = pickChallenge(challengeTypes[nextTyp], z.Challenges)
810 nextTyp++
811 }
812 if chal == nil {
813 return nil, fmt.Errorf("acme/autocert: unable to satisfy %q for domain %q: no viable challenge type found", z.URI, domain)
814 }
815 // Respond to the challenge and wait for validation result.
816 cleanup, err := m.fulfill(ctx, client, chal, domain)
817 if err != nil {
818 continue AuthorizeOrderLoop
819 }
820 defer cleanup()
821 if _, err := client.Accept(ctx, chal); err != nil {
822 continue AuthorizeOrderLoop
823 }
824 if _, err := client.WaitAuthorization(ctx, z.URI); err != nil {
825 continue AuthorizeOrderLoop
826 }
827 }
828
829 // All authorizations are satisfied.
830 // Wait for the CA to update the order status.
831 o, err = client.WaitOrder(ctx, o.URI)
832 if err != nil {
833 continue AuthorizeOrderLoop
834 }
835 return o, nil
836 }
837}
838
839func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge {
840 for _, c := range chal {
841 if c.Type == typ {
842 return c
843 }
844 }
845 return nil
846}
847
848func (m *Manager) supportedChallengeTypes() []string {
849 m.challengeMu.RLock()
850 defer m.challengeMu.RUnlock()
851 typ := []string{"tls-alpn-01"}
852 if m.tryHTTP01 {
853 typ = append(typ, "http-01")
854 }
855 if m.DNSManager != nil {
856 typ = append(typ, "dns-01")
857 }
858 return typ
859}
860
861// deactivatePendingAuthz relinquishes all authorizations identified by the elements
862// of the provided uri slice which are in "pending" state.
863// It ignores revocation errors.
864//
865// deactivatePendingAuthz takes no context argument and instead runs with its own
866// "detached" context because deactivations are done in a goroutine separate from
867// that of the main issuance or renewal flow.
868func (m *Manager) deactivatePendingAuthz(uri []string) {
869 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
870 defer cancel()
871 client, err := m.acmeClient(ctx)
872 if err != nil {
873 return
874 }
875 for _, u := range uri {
876 z, err := client.GetAuthorization(ctx, u)
877 if err == nil && z.Status == acme.StatusPending {
878 client.RevokeAuthorization(ctx, u)
879 }
880 }
881}
882
883// fulfill provisions a response to the challenge chal.
884// The cleanup is non-nil only if provisioning succeeded.
885func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge, domain string) (cleanup func(), err error) {
886 switch chal.Type {
887 case "tls-alpn-01":
888 cert, err := client.TLSALPN01ChallengeCert(chal.Token, domain)
889 if err != nil {
890 return nil, err
891 }
892 m.putCertToken(ctx, domain, &cert)
893 return func() { go m.deleteCertToken(domain) }, nil
894 case "http-01":
895 resp, err := client.HTTP01ChallengeResponse(chal.Token)
896 if err != nil {
897 return nil, err
898 }
899 p := client.HTTP01ChallengePath(chal.Token)
900 m.putHTTPToken(ctx, p, resp)
901 return func() { go m.deleteHTTPToken(p) }, nil
902 case "dns-01":
903 rec, err := client.DNS01ChallengeRecord(chal.Token)
904 if err != nil {
905 return nil, err
906 }
907 err = m.DNSManager.Fulfill(ctx, domain, rec)
908 if err != nil {
909 return nil, err
910 }
911 return func() { m.DNSManager.Cleanup(ctx, domain, rec) }, err
912 }
913 return nil, fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
914}
915
916// putCertToken stores the token certificate with the specified name
917// in both m.certTokens map and m.Cache.
918func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) {
919 m.challengeMu.Lock()
920 defer m.challengeMu.Unlock()
921 if m.certTokens == nil {
922 m.certTokens = make(map[string]*tls.Certificate)
923 }
924 m.certTokens[name] = cert
925 m.cachePut(ctx, certKey{domain: name, isToken: true}, cert)
926}
927
928// deleteCertToken removes the token certificate with the specified name
929// from both m.certTokens map and m.Cache.
930func (m *Manager) deleteCertToken(name string) {
931 m.challengeMu.Lock()
932 defer m.challengeMu.Unlock()
933 delete(m.certTokens, name)
934 if m.Cache != nil {
935 ck := certKey{domain: name, isToken: true}
936 m.Cache.Delete(context.Background(), ck.String())
937 }
938}
939
940// httpToken retrieves an existing http-01 token value from an in-memory map
941// or the optional cache.
942func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, error) {
943 m.challengeMu.RLock()
944 defer m.challengeMu.RUnlock()
945 if v, ok := m.httpTokens[tokenPath]; ok {
946 return v, nil
947 }
948 if m.Cache == nil {
949 return nil, fmt.Errorf("acme/autocert: no token at %q", tokenPath)
950 }
951 return m.Cache.Get(ctx, httpTokenCacheKey(tokenPath))
952}
953
954// putHTTPToken stores an http-01 token value using tokenPath as key
955// in both in-memory map and the optional Cache.
956//
957// It ignores any error returned from Cache.Put.
958func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) {
959 m.challengeMu.Lock()
960 defer m.challengeMu.Unlock()
961 if m.httpTokens == nil {
962 m.httpTokens = make(map[string][]byte)
963 }
964 b := []byte(val)
965 m.httpTokens[tokenPath] = b
966 if m.Cache != nil {
967 m.Cache.Put(ctx, httpTokenCacheKey(tokenPath), b)
968 }
969}
970
971// deleteHTTPToken removes an http-01 token value from both in-memory map
972// and the optional Cache, ignoring any error returned from the latter.
973//
974// If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout.
975func (m *Manager) deleteHTTPToken(tokenPath string) {
976 m.challengeMu.Lock()
977 defer m.challengeMu.Unlock()
978 delete(m.httpTokens, tokenPath)
979 if m.Cache != nil {
980 m.Cache.Delete(context.Background(), httpTokenCacheKey(tokenPath))
981 }
982}
983
984// httpTokenCacheKey returns a key at which an http-01 token value may be stored
985// in the Manager's optional Cache.
986func httpTokenCacheKey(tokenPath string) string {
987 return path.Base(tokenPath) + "+http-01"
988}
989
990// startRenew starts a cert renewal and OCSP staple timer loop, one per domain.
991//
992// The loop is scheduled in two cases:
993// - a cert was fetched from cache for the first time (wasn't in m.state)
994// - a new cert was created by m.createCert
995func (m *Manager) startRenew(ck certKey, s *certState) {
996 m.renewalMu.Lock()
997 defer m.renewalMu.Unlock()
998 if m.renewal[ck] != nil {
999 // another goroutine is already on it
1000 return
1001 }
1002 if m.renewal == nil {
1003 m.renewal = make(map[certKey]*domainRenewal)
1004 }
1005 dr := &domainRenewal{m: m, ck: ck, key: s.key}
1006 m.renewal[ck] = dr
1007 dr.start(s.leaf.NotAfter)
1008}
1009
1010// stopRenew stops all currently running cert renewal timers.
1011// The timers are not restarted during the lifetime of the Manager.
1012func (m *Manager) stopRenew() {
1013 m.renewalMu.Lock()
1014 defer m.renewalMu.Unlock()
1015 for name, dr := range m.renewal {
1016 delete(m.renewal, name)
1017 dr.stop()
1018 }
1019}
1020
1021func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
1022 const keyName = "acme_account+key"
1023
1024 // Previous versions of autocert stored the value under a different key.
1025 const legacyKeyName = "acme_account.key"
1026
1027 genKey := func() (*ecdsa.PrivateKey, error) {
1028 return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
1029 }
1030
1031 if m.Cache == nil {
1032 return genKey()
1033 }
1034
1035 data, err := m.Cache.Get(ctx, keyName)
1036 if err == ErrCacheMiss {
1037 data, err = m.Cache.Get(ctx, legacyKeyName)
1038 }
1039 if err == ErrCacheMiss {
1040 key, err := genKey()
1041 if err != nil {
1042 return nil, err
1043 }
1044 var buf bytes.Buffer
1045 if err := encodeECDSAKey(&buf, key); err != nil {
1046 return nil, err
1047 }
1048 if err := m.Cache.Put(ctx, keyName, buf.Bytes()); err != nil {
1049 return nil, err
1050 }
1051 return key, nil
1052 }
1053 if err != nil {
1054 return nil, err
1055 }
1056
1057 priv, _ := pem.Decode(data)
1058 if priv == nil || !strings.Contains(priv.Type, "PRIVATE") {
1059 return nil, errors.New("acme/autocert: invalid account key found in cache")
1060 }
1061 return parsePrivateKey(priv.Bytes)
1062}
1063
1064func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
1065 m.clientMu.Lock()
1066 defer m.clientMu.Unlock()
1067 if m.client != nil {
1068 return m.client, nil
1069 }
1070
1071 client := m.Client
1072 if client == nil {
1073 client = &acme.Client{DirectoryURL: DefaultACMEDirectory}
1074 }
1075 if client.Key == nil {
1076 var err error
1077 client.Key, err = m.accountKey(ctx)
1078 if err != nil {
1079 return nil, err
1080 }
1081 }
1082 if client.UserAgent == "" {
1083 client.UserAgent = "autocert"
1084 }
1085 var contact []string
1086 if m.Email != "" {
1087 contact = []string{"mailto:" + m.Email}
1088 }
1089 a := &acme.Account{Contact: contact, ExternalAccountBinding: m.ExternalAccountBinding}
1090 _, err := client.Register(ctx, a, m.Prompt)
1091 if err == nil || isAccountAlreadyExist(err) {
1092 m.client = client
1093 err = nil
1094 }
1095 return m.client, err
1096}
1097
1098// isAccountAlreadyExist reports whether the err, as returned from acme.Client.Register,
1099// indicates the account has already been registered.
1100func isAccountAlreadyExist(err error) bool {
1101 if err == acme.ErrAccountAlreadyExists {
1102 return true
1103 }
1104 ae, ok := err.(*acme.Error)
1105 return ok && ae.StatusCode == http.StatusConflict
1106}
1107
1108func (m *Manager) hostPolicy() HostPolicy {
1109 if m.HostPolicy != nil {
1110 return m.HostPolicy
1111 }
1112 return defaultHostPolicy
1113}
1114
1115func (m *Manager) renewBefore() time.Duration {
1116 if m.RenewBefore > renewJitter {
1117 return m.RenewBefore
1118 }
1119 return 720 * time.Hour // 30 days
1120}
1121
1122func (m *Manager) now() time.Time {
1123 if m.nowFunc != nil {
1124 return m.nowFunc()
1125 }
1126 return time.Now()
1127}
1128
1129// certState is ready when its mutex is unlocked for reading.
1130type certState struct {
1131 sync.RWMutex
1132 locked bool // locked for read/write
1133 key crypto.Signer // private key for cert
1134 cert [][]byte // DER encoding
1135 leaf *x509.Certificate // parsed cert[0]; always non-nil if cert != nil
1136 nextOcsp time.Time // time ocsp response expires, must renew before this
1137 ocsp []byte // raw ocsp response from CA for stapling to cert
1138}
1139
1140// tlscert creates a tls.Certificate from s.key and s.cert.
1141// Callers should wrap it in s.RLock() and s.RUnlock().
1142func (s *certState) tlscert() (*tls.Certificate, error) {
1143 if s.key == nil {
1144 return nil, errors.New("acme/autocert: missing signer")
1145 }
1146 if len(s.cert) == 0 {
1147 return nil, errors.New("acme/autocert: missing certificate")
1148 }
1149 return &tls.Certificate{
1150 PrivateKey: s.key,
1151 Certificate: s.cert,
1152 Leaf: s.leaf,
1153 OCSPStaple: s.ocsp,
1154 }, nil
1155}
1156
1157// certRequest generates a CSR for the given common name.
1158func certRequest(key crypto.Signer, name string, ext []pkix.Extension) ([]byte, error) {
1159 req := &x509.CertificateRequest{
1160 Subject: pkix.Name{CommonName: name},
1161 DNSNames: []string{name},
1162 ExtraExtensions: ext,
1163 }
1164 return x509.CreateCertificateRequest(rand.Reader, req, key)
1165}
1166
1167// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
1168// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
1169// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
1170//
1171// Inspired by parsePrivateKey in crypto/tls/tls.go.
1172func parsePrivateKey(der []byte) (crypto.Signer, error) {
1173 if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
1174 return key, nil
1175 }
1176 if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
1177 switch key := key.(type) {
1178 case *rsa.PrivateKey:
1179 return key, nil
1180 case *ecdsa.PrivateKey:
1181 return key, nil
1182 default:
1183 return nil, errors.New("acme/autocert: unknown private key type in PKCS#8 wrapping")
1184 }
1185 }
1186 if key, err := x509.ParseECPrivateKey(der); err == nil {
1187 return key, nil
1188 }
1189
1190 return nil, errors.New("acme/autocert: failed to parse private key")
1191}
1192
1193// validCert parses a cert chain provided as der argument and verifies the leaf and der[0]
1194// correspond to the private key, the domain and key type match, and expiration dates
1195// are valid. It doesn't do any revocation checking.
1196//
1197// The returned value is the verified leaf cert.
1198func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf *x509.Certificate, err error) {
1199 // parse public part(s)
1200 var n int
1201 for _, b := range der {
1202 n += len(b)
1203 }
1204 pub := make([]byte, n)
1205 n = 0
1206 for _, b := range der {
1207 n += copy(pub[n:], b)
1208 }
1209 x509Cert, err := x509.ParseCertificates(pub)
1210 if err != nil || len(x509Cert) == 0 {
1211 return nil, errors.New("acme/autocert: no public key found")
1212 }
1213 // verify the leaf is not expired and matches the domain name
1214 leaf = x509Cert[0]
1215 if now.Before(leaf.NotBefore) {
1216 return nil, errors.New("acme/autocert: certificate is not valid yet")
1217 }
1218 if now.After(leaf.NotAfter) {
1219 return nil, errors.New("acme/autocert: expired certificate")
1220 }
1221 if err := leaf.VerifyHostname(ck.domain); err != nil {
1222 return nil, err
1223 }
1224 // renew certificates revoked by Let's Encrypt in January 2022
1225 if isRevokedLetsEncrypt(leaf) {
1226 return nil, errors.New("acme/autocert: certificate was probably revoked by Let's Encrypt")
1227 }
1228 // ensure the leaf corresponds to the private key and matches the certKey type
1229 switch pub := leaf.PublicKey.(type) {
1230 case *rsa.PublicKey:
1231 prv, ok := key.(*rsa.PrivateKey)
1232 if !ok {
1233 return nil, errors.New("acme/autocert: private key type does not match public key type")
1234 }
1235 if pub.N.Cmp(prv.N) != 0 {
1236 return nil, errors.New("acme/autocert: private key does not match public key")
1237 }
1238 if !ck.isRSA && !ck.isToken {
1239 return nil, errors.New("acme/autocert: key type does not match expected value")
1240 }
1241 case *ecdsa.PublicKey:
1242 prv, ok := key.(*ecdsa.PrivateKey)
1243 if !ok {
1244 return nil, errors.New("acme/autocert: private key type does not match public key type")
1245 }
1246 if pub.X.Cmp(prv.X) != 0 || pub.Y.Cmp(prv.Y) != 0 {
1247 return nil, errors.New("acme/autocert: private key does not match public key")
1248 }
1249 if ck.isRSA && !ck.isToken {
1250 return nil, errors.New("acme/autocert: key type does not match expected value")
1251 }
1252 default:
1253 return nil, errors.New("acme/autocert: unknown public key algorithm")
1254 }
1255 return leaf, nil
1256}
1257
1258// https://community.letsencrypt.org/t/2022-01-25-issue-with-tls-alpn-01-validation-method/170450
1259var letsEncryptFixDeployTime = time.Date(2022, time.January, 26, 00, 48, 0, 0, time.UTC)
1260
1261// isRevokedLetsEncrypt returns whether the certificate is likely to be part of
1262// a batch of certificates revoked by Let's Encrypt in January 2022. This check
1263// can be safely removed from May 2022.
1264func isRevokedLetsEncrypt(cert *x509.Certificate) bool {
1265 O := cert.Issuer.Organization
1266 return len(O) == 1 && O[0] == "Let's Encrypt" &&
1267 cert.NotBefore.Before(letsEncryptFixDeployTime)
1268}
1269
1270type lockedMathRand struct {
1271 sync.Mutex
1272 rnd *mathrand.Rand
1273}
1274
1275func (r *lockedMathRand) int63n(max int64) int64 {
1276 r.Lock()
1277 n := r.rnd.Int63n(max)
1278 r.Unlock()
1279 return n
1280}
1281
1282// For easier testing.
1283var (
1284 // Called when a state is removed.
1285 testDidRemoveState = func(certKey) {}
1286)
diff --git a/crypto/acme/autocert/fork/autocert_test.go b/crypto/acme/autocert/fork/autocert_test.go
new file mode 100644
index 0000000..ef466b5
--- /dev/null
+++ b/crypto/acme/autocert/fork/autocert_test.go
@@ -0,0 +1,1058 @@
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
5package fork
6
7import (
8 "bytes"
9 "context"
10 "crypto"
11 "crypto/ecdsa"
12 "crypto/elliptic"
13 "crypto/rand"
14 "crypto/rsa"
15 "crypto/tls"
16 "crypto/x509"
17 "crypto/x509/pkix"
18 "encoding/asn1"
19 "fmt"
20 "io"
21 "math/big"
22 "net/http"
23 "net/http/httptest"
24 "reflect"
25 "strings"
26 "sync"
27 "testing"
28 "time"
29
30 "code.crute.us/mcrute/golib/crypto/acme/autocert/fork/internal/acmetest"
31 "golang.org/x/crypto/acme"
32)
33
34var (
35 exampleDomain = "example.org"
36 exampleCertKey = certKey{domain: exampleDomain}
37 exampleCertKeyRSA = certKey{domain: exampleDomain, isRSA: true}
38)
39
40type memCache struct {
41 t *testing.T
42 mu sync.Mutex
43 keyData map[string][]byte
44}
45
46func (m *memCache) Get(ctx context.Context, key string) ([]byte, error) {
47 m.mu.Lock()
48 defer m.mu.Unlock()
49
50 v, ok := m.keyData[key]
51 if !ok {
52 return nil, ErrCacheMiss
53 }
54 return v, nil
55}
56
57// filenameSafe returns whether all characters in s are printable ASCII
58// and safe to use in a filename on most filesystems.
59func filenameSafe(s string) bool {
60 for _, c := range s {
61 if c < 0x20 || c > 0x7E {
62 return false
63 }
64 switch c {
65 case '\\', '/', ':', '*', '?', '"', '<', '>', '|':
66 return false
67 }
68 }
69 return true
70}
71
72func (m *memCache) Put(ctx context.Context, key string, data []byte) error {
73 if !filenameSafe(key) {
74 m.t.Errorf("invalid characters in cache key %q", key)
75 }
76
77 m.mu.Lock()
78 defer m.mu.Unlock()
79
80 m.keyData[key] = data
81 return nil
82}
83
84func (m *memCache) Delete(ctx context.Context, key string) error {
85 m.mu.Lock()
86 defer m.mu.Unlock()
87
88 delete(m.keyData, key)
89 return nil
90}
91
92func newMemCache(t *testing.T) *memCache {
93 return &memCache{
94 t: t,
95 keyData: make(map[string][]byte),
96 }
97}
98
99func (m *memCache) numCerts() int {
100 m.mu.Lock()
101 defer m.mu.Unlock()
102
103 res := 0
104 for key := range m.keyData {
105 if strings.HasSuffix(key, "+token") ||
106 strings.HasSuffix(key, "+key") ||
107 strings.HasSuffix(key, "+http-01") {
108 continue
109 }
110 res++
111 }
112 return res
113}
114
115func dummyCert(pub interface{}, san ...string) ([]byte, error) {
116 return dateDummyCert(pub, time.Now(), time.Now().Add(90*24*time.Hour), san...)
117}
118
119func dateDummyCert(pub interface{}, start, end time.Time, san ...string) ([]byte, error) {
120 // use EC key to run faster on 386
121 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
122 if err != nil {
123 return nil, err
124 }
125 t := &x509.Certificate{
126 SerialNumber: randomSerial(),
127 NotBefore: start,
128 NotAfter: end,
129 BasicConstraintsValid: true,
130 KeyUsage: x509.KeyUsageKeyEncipherment,
131 DNSNames: san,
132 }
133 if pub == nil {
134 pub = &key.PublicKey
135 }
136 return x509.CreateCertificate(rand.Reader, t, t, pub, key)
137}
138
139func randomSerial() *big.Int {
140 serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 32))
141 if err != nil {
142 panic(err)
143 }
144 return serial
145}
146
147type algorithmSupport int
148
149const (
150 algRSA algorithmSupport = iota
151 algECDSA
152)
153
154func clientHelloInfo(sni string, alg algorithmSupport) *tls.ClientHelloInfo {
155 hello := &tls.ClientHelloInfo{
156 ServerName: sni,
157 CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},
158 }
159 if alg == algECDSA {
160 hello.CipherSuites = append(hello.CipherSuites, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305)
161 }
162 return hello
163}
164
165func testManager(t *testing.T) *Manager {
166 man := &Manager{
167 Prompt: AcceptTOS,
168 Cache: newMemCache(t),
169 }
170 t.Cleanup(man.stopRenew)
171 return man
172}
173
174func TestGetCertificate(t *testing.T) {
175 tests := []struct {
176 name string
177 hello *tls.ClientHelloInfo
178 domain string
179 expectError string
180 prepare func(t *testing.T, man *Manager, s *acmetest.CAServer)
181 verify func(t *testing.T, man *Manager, leaf *x509.Certificate)
182 disableALPN bool
183 disableHTTP bool
184 }{
185 {
186 name: "ALPN",
187 hello: clientHelloInfo("example.org", algECDSA),
188 domain: "example.org",
189 disableHTTP: true,
190 },
191 {
192 name: "HTTP",
193 hello: clientHelloInfo("example.org", algECDSA),
194 domain: "example.org",
195 disableALPN: true,
196 },
197 {
198 name: "nilPrompt",
199 hello: clientHelloInfo("example.org", algECDSA),
200 domain: "example.org",
201 prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
202 man.Prompt = nil
203 },
204 expectError: "Manager.Prompt not set",
205 },
206 {
207 name: "trailingDot",
208 hello: clientHelloInfo("example.org.", algECDSA),
209 domain: "example.org",
210 },
211 {
212 name: "unicodeIDN",
213 hello: clientHelloInfo("éé.com", algECDSA),
214 domain: "xn--9caa.com",
215 },
216 {
217 name: "unicodeIDN/mixedCase",
218 hello: clientHelloInfo("éÉ.com", algECDSA),
219 domain: "xn--9caa.com",
220 },
221 {
222 name: "upperCase",
223 hello: clientHelloInfo("EXAMPLE.ORG", algECDSA),
224 domain: "example.org",
225 },
226 {
227 name: "goodCache",
228 hello: clientHelloInfo("example.org", algECDSA),
229 domain: "example.org",
230 prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
231 // Make a valid cert and cache it.
232 c := s.Start().LeafCert(exampleDomain, "ECDSA",
233 // Use a time before the Let's Encrypt revocation cutoff to also test
234 // that non-Let's Encrypt certificates are not renewed.
235 time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC),
236 time.Date(2122, time.January, 1, 0, 0, 0, 0, time.UTC),
237 )
238 if err := man.cachePut(context.Background(), exampleCertKey, c); err != nil {
239 t.Fatalf("man.cachePut: %v", err)
240 }
241 },
242 // Break the server to check that the cache is used.
243 disableALPN: true, disableHTTP: true,
244 },
245 {
246 name: "expiredCache",
247 hello: clientHelloInfo("example.org", algECDSA),
248 domain: "example.org",
249 prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
250 // Make an expired cert and cache it.
251 c := s.Start().LeafCert(exampleDomain, "ECDSA", time.Now().Add(-10*time.Minute), time.Now().Add(-5*time.Minute))
252 if err := man.cachePut(context.Background(), exampleCertKey, c); err != nil {
253 t.Fatalf("man.cachePut: %v", err)
254 }
255 },
256 },
257 {
258 name: "forceRSA",
259 hello: clientHelloInfo("example.org", algECDSA),
260 domain: "example.org",
261 prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
262 man.ForceRSA = true
263 },
264 verify: func(t *testing.T, man *Manager, leaf *x509.Certificate) {
265 if _, ok := leaf.PublicKey.(*ecdsa.PublicKey); !ok {
266 t.Errorf("leaf.PublicKey is %T; want *ecdsa.PublicKey", leaf.PublicKey)
267 }
268 },
269 },
270 {
271 name: "goodLetsEncrypt",
272 hello: clientHelloInfo("example.org", algECDSA),
273 domain: "example.org",
274 prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
275 // Make a valid certificate issued after the TLS-ALPN-01
276 // revocation window and cache it.
277 s.IssuerName(pkix.Name{Country: []string{"US"},
278 Organization: []string{"Let's Encrypt"}, CommonName: "R3"})
279 c := s.Start().LeafCert(exampleDomain, "ECDSA",
280 time.Date(2022, time.January, 26, 12, 0, 0, 0, time.UTC),
281 time.Date(2122, time.January, 1, 0, 0, 0, 0, time.UTC),
282 )
283 if err := man.cachePut(context.Background(), exampleCertKey, c); err != nil {
284 t.Fatalf("man.cachePut: %v", err)
285 }
286 },
287 // Break the server to check that the cache is used.
288 disableALPN: true, disableHTTP: true,
289 },
290 {
291 name: "revokedLetsEncrypt",
292 hello: clientHelloInfo("example.org", algECDSA),
293 domain: "example.org",
294 prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
295 // Make a certificate issued during the TLS-ALPN-01
296 // revocation window and cache it.
297 s.IssuerName(pkix.Name{Country: []string{"US"},
298 Organization: []string{"Let's Encrypt"}, CommonName: "R3"})
299 c := s.Start().LeafCert(exampleDomain, "ECDSA",
300 time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC),
301 time.Date(2122, time.January, 1, 0, 0, 0, 0, time.UTC),
302 )
303 if err := man.cachePut(context.Background(), exampleCertKey, c); err != nil {
304 t.Fatalf("man.cachePut: %v", err)
305 }
306 },
307 verify: func(t *testing.T, man *Manager, leaf *x509.Certificate) {
308 if leaf.NotBefore.Before(time.Now().Add(-10 * time.Minute)) {
309 t.Error("certificate was not reissued")
310 }
311 },
312 },
313 {
314 // TestGetCertificate/tokenCache tests the fallback of token
315 // certificate fetches to cache when Manager.certTokens misses.
316 name: "tokenCacheALPN",
317 hello: clientHelloInfo("example.org", algECDSA),
318 domain: "example.org",
319 prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
320 // Make a separate manager with a shared cache, simulating
321 // separate nodes that serve requests for the same domain.
322 man2 := testManager(t)
323 man2.Cache = man.Cache
324 // Redirect the verification request to man2, although the
325 // client request will hit man, testing that they can complete a
326 // verification by communicating through the cache.
327 s.ResolveGetCertificate("example.org", man2.GetCertificate)
328 },
329 // Drop the default verification paths.
330 disableALPN: true,
331 },
332 {
333 name: "tokenCacheHTTP",
334 hello: clientHelloInfo("example.org", algECDSA),
335 domain: "example.org",
336 prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
337 man2 := testManager(t)
338 man2.Cache = man.Cache
339 s.ResolveHandler("example.org", man2.HTTPHandler(nil))
340 },
341 disableHTTP: true,
342 },
343 {
344 name: "ecdsa",
345 hello: clientHelloInfo("example.org", algECDSA),
346 domain: "example.org",
347 verify: func(t *testing.T, man *Manager, leaf *x509.Certificate) {
348 if _, ok := leaf.PublicKey.(*ecdsa.PublicKey); !ok {
349 t.Error("an ECDSA client was served a non-ECDSA certificate")
350 }
351 },
352 },
353 {
354 name: "rsa",
355 hello: clientHelloInfo("example.org", algRSA),
356 domain: "example.org",
357 verify: func(t *testing.T, man *Manager, leaf *x509.Certificate) {
358 if _, ok := leaf.PublicKey.(*rsa.PublicKey); !ok {
359 t.Error("an RSA client was served a non-RSA certificate")
360 }
361 },
362 },
363 {
364 name: "wrongCacheKeyType",
365 hello: clientHelloInfo("example.org", algECDSA),
366 domain: "example.org",
367 prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
368 // Make an RSA cert and cache it without suffix.
369 c := s.Start().LeafCert(exampleDomain, "RSA", time.Now(), time.Now().Add(90*24*time.Hour))
370 if err := man.cachePut(context.Background(), exampleCertKey, c); err != nil {
371 t.Fatalf("man.cachePut: %v", err)
372 }
373 },
374 verify: func(t *testing.T, man *Manager, leaf *x509.Certificate) {
375 // The RSA cached cert should be silently ignored and replaced.
376 if _, ok := leaf.PublicKey.(*ecdsa.PublicKey); !ok {
377 t.Error("an ECDSA client was served a non-ECDSA certificate")
378 }
379 if numCerts := man.Cache.(*memCache).numCerts(); numCerts != 1 {
380 t.Errorf("found %d certificates in cache; want %d", numCerts, 1)
381 }
382 },
383 },
384 {
385 name: "almostExpiredCache",
386 hello: clientHelloInfo("example.org", algECDSA),
387 domain: "example.org",
388 prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
389 man.RenewBefore = 24 * time.Hour
390 // Cache an almost expired cert.
391 c := s.Start().LeafCert(exampleDomain, "ECDSA", time.Now(), time.Now().Add(10*time.Minute))
392 if err := man.cachePut(context.Background(), exampleCertKey, c); err != nil {
393 t.Fatalf("man.cachePut: %v", err)
394 }
395 },
396 },
397 {
398 name: "provideExternalAuth",
399 hello: clientHelloInfo("example.org", algECDSA),
400 domain: "example.org",
401 prepare: func(t *testing.T, man *Manager, s *acmetest.CAServer) {
402 s.ExternalAccountRequired()
403
404 man.ExternalAccountBinding = &acme.ExternalAccountBinding{
405 KID: "test-key",
406 Key: make([]byte, 32),
407 }
408 },
409 },
410 }
411 for _, tt := range tests {
412 t.Run(tt.name, func(t *testing.T) {
413 man := testManager(t)
414 s := acmetest.NewCAServer(t)
415 if !tt.disableALPN {
416 s.ResolveGetCertificate(tt.domain, man.GetCertificate)
417 }
418 if !tt.disableHTTP {
419 s.ResolveHandler(tt.domain, man.HTTPHandler(nil))
420 }
421
422 if tt.prepare != nil {
423 tt.prepare(t, man, s)
424 }
425
426 s.Start()
427
428 man.Client = &acme.Client{DirectoryURL: s.URL()}
429
430 tlscert, err := man.GetCertificate(tt.hello)
431 if tt.expectError != "" {
432 if err == nil {
433 t.Fatal("expected error, got certificate")
434 }
435 if !strings.Contains(err.Error(), tt.expectError) {
436 t.Errorf("got %q, expected %q", err, tt.expectError)
437 }
438 return
439 }
440 if err != nil {
441 t.Fatalf("man.GetCertificate: %v", err)
442 }
443
444 leaf, err := x509.ParseCertificate(tlscert.Certificate[0])
445 if err != nil {
446 t.Fatal(err)
447 }
448 opts := x509.VerifyOptions{
449 DNSName: tt.domain,
450 Intermediates: x509.NewCertPool(),
451 Roots: s.Roots(),
452 }
453 for _, cert := range tlscert.Certificate[1:] {
454 c, err := x509.ParseCertificate(cert)
455 if err != nil {
456 t.Fatal(err)
457 }
458 opts.Intermediates.AddCert(c)
459 }
460 if _, err := leaf.Verify(opts); err != nil {
461 t.Error(err)
462 }
463
464 if san := leaf.DNSNames[0]; san != tt.domain {
465 t.Errorf("got SAN %q, expected %q", san, tt.domain)
466 }
467
468 if tt.verify != nil {
469 tt.verify(t, man, leaf)
470 }
471 })
472 }
473}
474
475func TestGetCertificate_failedAttempt(t *testing.T) {
476 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
477 w.WriteHeader(http.StatusBadRequest)
478 }))
479 defer ts.Close()
480
481 d := createCertRetryAfter
482 f := testDidRemoveState
483 defer func() {
484 createCertRetryAfter = d
485 testDidRemoveState = f
486 }()
487 createCertRetryAfter = 0
488 done := make(chan struct{})
489 testDidRemoveState = func(ck certKey) {
490 if ck != exampleCertKey {
491 t.Errorf("testDidRemoveState: domain = %v; want %v", ck, exampleCertKey)
492 }
493 close(done)
494 }
495
496 man := &Manager{
497 Prompt: AcceptTOS,
498 Client: &acme.Client{
499 DirectoryURL: ts.URL,
500 },
501 }
502 defer man.stopRenew()
503 hello := clientHelloInfo(exampleDomain, algECDSA)
504 if _, err := man.GetCertificate(hello); err == nil {
505 t.Error("GetCertificate: err is nil")
506 }
507
508 <-done
509 man.stateMu.Lock()
510 defer man.stateMu.Unlock()
511 if v, exist := man.state[exampleCertKey]; exist {
512 t.Errorf("state exists for %v: %+v", exampleCertKey, v)
513 }
514}
515
516func TestRevokeFailedAuthz(t *testing.T) {
517 ca := acmetest.NewCAServer(t)
518 // Make the authz unfulfillable on the client side, so it will be left
519 // pending at the end of the verification attempt.
520 ca.ChallengeTypes("fake-01", "fake-02")
521 ca.Start()
522
523 m := testManager(t)
524 m.Client = &acme.Client{DirectoryURL: ca.URL()}
525
526 _, err := m.GetCertificate(clientHelloInfo("example.org", algECDSA))
527 if err == nil {
528 t.Fatal("expected GetCertificate to fail")
529 }
530
531 logTicker := time.NewTicker(3 * time.Second)
532 defer logTicker.Stop()
533 for {
534 authz, err := m.Client.GetAuthorization(context.Background(), ca.URL()+"/authz/0")
535 if err != nil {
536 t.Fatal(err)
537 }
538 if authz.Status == acme.StatusDeactivated {
539 return
540 }
541
542 select {
543 case <-logTicker.C:
544 t.Logf("still waiting on revocations")
545 default:
546 }
547 time.Sleep(50 * time.Millisecond)
548 }
549}
550
551func TestHTTPHandlerDefaultFallback(t *testing.T) {
552 tt := []struct {
553 method, url string
554 wantCode int
555 wantLocation string
556 }{
557 {"GET", "http://example.org", 302, "https://example.org/"},
558 {"GET", "http://example.org/foo", 302, "https://example.org/foo"},
559 {"GET", "http://example.org/foo/bar/", 302, "https://example.org/foo/bar/"},
560 {"GET", "http://example.org/?a=b", 302, "https://example.org/?a=b"},
561 {"GET", "http://example.org/foo?a=b", 302, "https://example.org/foo?a=b"},
562 {"GET", "http://example.org:80/foo?a=b", 302, "https://example.org:443/foo?a=b"},
563 {"GET", "http://example.org:80/foo%20bar", 302, "https://example.org:443/foo%20bar"},
564 {"GET", "http://[2602:d1:xxxx::c60a]:1234", 302, "https://[2602:d1:xxxx::c60a]:443/"},
565 {"GET", "http://[2602:d1:xxxx::c60a]", 302, "https://[2602:d1:xxxx::c60a]/"},
566 {"GET", "http://[2602:d1:xxxx::c60a]/foo?a=b", 302, "https://[2602:d1:xxxx::c60a]/foo?a=b"},
567 {"HEAD", "http://example.org", 302, "https://example.org/"},
568 {"HEAD", "http://example.org/foo", 302, "https://example.org/foo"},
569 {"HEAD", "http://example.org/foo/bar/", 302, "https://example.org/foo/bar/"},
570 {"HEAD", "http://example.org/?a=b", 302, "https://example.org/?a=b"},
571 {"HEAD", "http://example.org/foo?a=b", 302, "https://example.org/foo?a=b"},
572 {"POST", "http://example.org", 400, ""},
573 {"PUT", "http://example.org", 400, ""},
574 {"GET", "http://example.org/.well-known/acme-challenge/x", 404, ""},
575 }
576 var m Manager
577 h := m.HTTPHandler(nil)
578 for i, test := range tt {
579 r := httptest.NewRequest(test.method, test.url, nil)
580 w := httptest.NewRecorder()
581 h.ServeHTTP(w, r)
582 if w.Code != test.wantCode {
583 t.Errorf("%d: w.Code = %d; want %d", i, w.Code, test.wantCode)
584 t.Errorf("%d: body: %s", i, w.Body.Bytes())
585 }
586 if v := w.Header().Get("Location"); v != test.wantLocation {
587 t.Errorf("%d: Location = %q; want %q", i, v, test.wantLocation)
588 }
589 }
590}
591
592func TestAccountKeyCache(t *testing.T) {
593 m := Manager{Cache: newMemCache(t)}
594 ctx := context.Background()
595 k1, err := m.accountKey(ctx)
596 if err != nil {
597 t.Fatal(err)
598 }
599 k2, err := m.accountKey(ctx)
600 if err != nil {
601 t.Fatal(err)
602 }
603 if !reflect.DeepEqual(k1, k2) {
604 t.Errorf("account keys don't match: k1 = %#v; k2 = %#v", k1, k2)
605 }
606}
607
608func TestCache(t *testing.T) {
609 ecdsaKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
610 if err != nil {
611 t.Fatal(err)
612 }
613 cert, err := dummyCert(ecdsaKey.Public(), exampleDomain)
614 if err != nil {
615 t.Fatal(err)
616 }
617 ecdsaCert := &tls.Certificate{
618 Certificate: [][]byte{cert},
619 PrivateKey: ecdsaKey,
620 }
621
622 rsaKey, err := rsa.GenerateKey(rand.Reader, 512)
623 if err != nil {
624 t.Fatal(err)
625 }
626 cert, err = dummyCert(rsaKey.Public(), exampleDomain)
627 if err != nil {
628 t.Fatal(err)
629 }
630 rsaCert := &tls.Certificate{
631 Certificate: [][]byte{cert},
632 PrivateKey: rsaKey,
633 }
634
635 man := &Manager{Cache: newMemCache(t)}
636 defer man.stopRenew()
637 ctx := context.Background()
638
639 if err := man.cachePut(ctx, exampleCertKey, ecdsaCert); err != nil {
640 t.Fatalf("man.cachePut: %v", err)
641 }
642 if err := man.cachePut(ctx, exampleCertKeyRSA, rsaCert); err != nil {
643 t.Fatalf("man.cachePut: %v", err)
644 }
645
646 res, err := man.cacheGet(ctx, exampleCertKey)
647 if err != nil {
648 t.Fatalf("man.cacheGet: %v", err)
649 }
650 if res == nil || !bytes.Equal(res.Certificate[0], ecdsaCert.Certificate[0]) {
651 t.Errorf("man.cacheGet = %+v; want %+v", res, ecdsaCert)
652 }
653
654 res, err = man.cacheGet(ctx, exampleCertKeyRSA)
655 if err != nil {
656 t.Fatalf("man.cacheGet: %v", err)
657 }
658 if res == nil || !bytes.Equal(res.Certificate[0], rsaCert.Certificate[0]) {
659 t.Errorf("man.cacheGet = %+v; want %+v", res, rsaCert)
660 }
661}
662
663func TestHostWhitelist(t *testing.T) {
664 policy := HostWhitelist("example.com", "EXAMPLE.ORG", "*.example.net", "éÉ.com")
665 tt := []struct {
666 host string
667 allow bool
668 }{
669 {"example.com", true},
670 {"example.org", true},
671 {"xn--9caa.com", true}, // éé.com
672 {"one.example.com", false},
673 {"two.example.org", false},
674 {"three.example.net", false},
675 {"dummy", false},
676 }
677 for i, test := range tt {
678 err := policy(nil, test.host)
679 if err != nil && test.allow {
680 t.Errorf("%d: policy(%q): %v; want nil", i, test.host, err)
681 }
682 if err == nil && !test.allow {
683 t.Errorf("%d: policy(%q): nil; want an error", i, test.host)
684 }
685 }
686}
687
688func TestValidCert(t *testing.T) {
689 key1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
690 if err != nil {
691 t.Fatal(err)
692 }
693 key2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
694 if err != nil {
695 t.Fatal(err)
696 }
697 key3, err := rsa.GenerateKey(rand.Reader, 512)
698 if err != nil {
699 t.Fatal(err)
700 }
701 cert1, err := dummyCert(key1.Public(), "example.org")
702 if err != nil {
703 t.Fatal(err)
704 }
705 cert2, err := dummyCert(key2.Public(), "example.org")
706 if err != nil {
707 t.Fatal(err)
708 }
709 cert3, err := dummyCert(key3.Public(), "example.org")
710 if err != nil {
711 t.Fatal(err)
712 }
713 now := time.Now()
714 early, err := dateDummyCert(key1.Public(), now.Add(time.Hour), now.Add(2*time.Hour), "example.org")
715 if err != nil {
716 t.Fatal(err)
717 }
718 expired, err := dateDummyCert(key1.Public(), now.Add(-2*time.Hour), now.Add(-time.Hour), "example.org")
719 if err != nil {
720 t.Fatal(err)
721 }
722
723 tt := []struct {
724 ck certKey
725 key crypto.Signer
726 cert [][]byte
727 ok bool
728 }{
729 {certKey{domain: "example.org"}, key1, [][]byte{cert1}, true},
730 {certKey{domain: "example.org", isRSA: true}, key3, [][]byte{cert3}, true},
731 {certKey{domain: "example.org"}, key1, [][]byte{cert1, cert2, cert3}, true},
732 {certKey{domain: "example.org"}, key1, [][]byte{cert1, {1}}, false},
733 {certKey{domain: "example.org"}, key1, [][]byte{{1}}, false},
734 {certKey{domain: "example.org"}, key1, [][]byte{cert2}, false},
735 {certKey{domain: "example.org"}, key2, [][]byte{cert1}, false},
736 {certKey{domain: "example.org"}, key1, [][]byte{cert3}, false},
737 {certKey{domain: "example.org"}, key3, [][]byte{cert1}, false},
738 {certKey{domain: "example.net"}, key1, [][]byte{cert1}, false},
739 {certKey{domain: "example.org"}, key1, [][]byte{early}, false},
740 {certKey{domain: "example.org"}, key1, [][]byte{expired}, false},
741 {certKey{domain: "example.org", isRSA: true}, key1, [][]byte{cert1}, false},
742 {certKey{domain: "example.org"}, key3, [][]byte{cert3}, false},
743 }
744 for i, test := range tt {
745 leaf, err := validCert(test.ck, test.cert, test.key, now)
746 if err != nil && test.ok {
747 t.Errorf("%d: err = %v", i, err)
748 }
749 if err == nil && !test.ok {
750 t.Errorf("%d: err is nil", i)
751 }
752 if err == nil && test.ok && leaf == nil {
753 t.Errorf("%d: leaf is nil", i)
754 }
755 }
756}
757
758type cacheGetFunc func(ctx context.Context, key string) ([]byte, error)
759
760func (f cacheGetFunc) Get(ctx context.Context, key string) ([]byte, error) {
761 return f(ctx, key)
762}
763
764func (f cacheGetFunc) Put(ctx context.Context, key string, data []byte) error {
765 return fmt.Errorf("unsupported Put of %q = %q", key, data)
766}
767
768func (f cacheGetFunc) Delete(ctx context.Context, key string) error {
769 return fmt.Errorf("unsupported Delete of %q", key)
770}
771
772func TestManagerGetCertificateBogusSNI(t *testing.T) {
773 m := Manager{
774 Prompt: AcceptTOS,
775 Cache: cacheGetFunc(func(ctx context.Context, key string) ([]byte, error) {
776 return nil, fmt.Errorf("cache.Get of %s", key)
777 }),
778 }
779 tests := []struct {
780 name string
781 wantErr string
782 }{
783 {"foo.com", "cache.Get of foo.com"},
784 {"foo.com.", "cache.Get of foo.com"},
785 {`a\b.com`, "acme/autocert: server name contains invalid character"},
786 {`a/b.com`, "acme/autocert: server name contains invalid character"},
787 {"", "acme/autocert: missing server name"},
788 {"foo", "acme/autocert: server name component count invalid"},
789 {".foo", "acme/autocert: server name component count invalid"},
790 {"foo.", "acme/autocert: server name component count invalid"},
791 {"fo.o", "cache.Get of fo.o"},
792 }
793 for _, tt := range tests {
794 _, err := m.GetCertificate(clientHelloInfo(tt.name, algECDSA))
795 got := fmt.Sprint(err)
796 if got != tt.wantErr {
797 t.Errorf("GetCertificate(SNI = %q) = %q; want %q", tt.name, got, tt.wantErr)
798 }
799 }
800}
801
802func TestCertRequest(t *testing.T) {
803 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
804 if err != nil {
805 t.Fatal(err)
806 }
807 // An extension from RFC7633. Any will do.
808 ext := pkix.Extension{
809 Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1},
810 Value: []byte("dummy"),
811 }
812 b, err := certRequest(key, "example.org", []pkix.Extension{ext})
813 if err != nil {
814 t.Fatalf("certRequest: %v", err)
815 }
816 r, err := x509.ParseCertificateRequest(b)
817 if err != nil {
818 t.Fatalf("ParseCertificateRequest: %v", err)
819 }
820 var found bool
821 for _, v := range r.Extensions {
822 if v.Id.Equal(ext.Id) {
823 found = true
824 break
825 }
826 }
827 if !found {
828 t.Errorf("want %v in Extensions: %v", ext, r.Extensions)
829 }
830}
831
832func TestSupportsECDSA(t *testing.T) {
833 tests := []struct {
834 CipherSuites []uint16
835 SignatureSchemes []tls.SignatureScheme
836 SupportedCurves []tls.CurveID
837 ecdsaOk bool
838 }{
839 {[]uint16{
840 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
841 }, nil, nil, false},
842 {[]uint16{
843 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
844 }, nil, nil, true},
845
846 // SignatureSchemes limits, not extends, CipherSuites
847 {[]uint16{
848 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
849 }, []tls.SignatureScheme{
850 tls.PKCS1WithSHA256, tls.ECDSAWithP256AndSHA256,
851 }, nil, false},
852 {[]uint16{
853 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
854 }, []tls.SignatureScheme{
855 tls.PKCS1WithSHA256,
856 }, nil, false},
857 {[]uint16{
858 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
859 }, []tls.SignatureScheme{
860 tls.PKCS1WithSHA256, tls.ECDSAWithP256AndSHA256,
861 }, nil, true},
862
863 {[]uint16{
864 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
865 }, []tls.SignatureScheme{
866 tls.PKCS1WithSHA256, tls.ECDSAWithP256AndSHA256,
867 }, []tls.CurveID{
868 tls.CurveP521,
869 }, false},
870 {[]uint16{
871 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
872 }, []tls.SignatureScheme{
873 tls.PKCS1WithSHA256, tls.ECDSAWithP256AndSHA256,
874 }, []tls.CurveID{
875 tls.CurveP256,
876 tls.CurveP521,
877 }, true},
878 }
879 for i, tt := range tests {
880 result := supportsECDSA(&tls.ClientHelloInfo{
881 CipherSuites: tt.CipherSuites,
882 SignatureSchemes: tt.SignatureSchemes,
883 SupportedCurves: tt.SupportedCurves,
884 })
885 if result != tt.ecdsaOk {
886 t.Errorf("%d: supportsECDSA = %v; want %v", i, result, tt.ecdsaOk)
887 }
888 }
889}
890
891func TestEndToEndALPN(t *testing.T) {
892 const domain = "example.org"
893
894 // ACME CA server
895 ca := acmetest.NewCAServer(t).Start()
896
897 // User HTTPS server.
898 m := &Manager{
899 Prompt: AcceptTOS,
900 Client: &acme.Client{DirectoryURL: ca.URL()},
901 }
902 us := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
903 w.Write([]byte("OK"))
904 }))
905 us.TLS = &tls.Config{
906 NextProtos: []string{"http/1.1", acme.ALPNProto},
907 GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
908 cert, err := m.GetCertificate(hello)
909 if err != nil {
910 t.Errorf("m.GetCertificate: %v", err)
911 }
912 return cert, err
913 },
914 }
915 us.StartTLS()
916 defer us.Close()
917 // In TLS-ALPN challenge verification, CA connects to the domain:443 in question.
918 // Because the domain won't resolve in tests, we need to tell the CA
919 // where to dial to instead.
920 ca.Resolve(domain, strings.TrimPrefix(us.URL, "https://"))
921
922 // A client visiting user's HTTPS server.
923 tr := &http.Transport{
924 TLSClientConfig: &tls.Config{
925 RootCAs: ca.Roots(),
926 ServerName: domain,
927 },
928 }
929 client := &http.Client{Transport: tr}
930 res, err := client.Get(us.URL)
931 if err != nil {
932 t.Fatal(err)
933 }
934 defer res.Body.Close()
935 b, err := io.ReadAll(res.Body)
936 if err != nil {
937 t.Fatal(err)
938 }
939 if v := string(b); v != "OK" {
940 t.Errorf("user server response: %q; want 'OK'", v)
941 }
942}
943
944func TestEndToEndHTTP(t *testing.T) {
945 const domain = "example.org"
946
947 // ACME CA server.
948 ca := acmetest.NewCAServer(t).ChallengeTypes("http-01").Start()
949
950 // User HTTP server for the ACME challenge.
951 m := testManager(t)
952 m.Client = &acme.Client{DirectoryURL: ca.URL()}
953 s := httptest.NewServer(m.HTTPHandler(nil))
954 defer s.Close()
955
956 // User HTTPS server.
957 ss := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
958 w.Write([]byte("OK"))
959 }))
960 ss.TLS = &tls.Config{
961 NextProtos: []string{"http/1.1", acme.ALPNProto},
962 GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
963 cert, err := m.GetCertificate(hello)
964 if err != nil {
965 t.Errorf("m.GetCertificate: %v", err)
966 }
967 return cert, err
968 },
969 }
970 ss.StartTLS()
971 defer ss.Close()
972
973 // Redirect the CA requests to the HTTP server.
974 ca.Resolve(domain, strings.TrimPrefix(s.URL, "http://"))
975
976 // A client visiting user's HTTPS server.
977 tr := &http.Transport{
978 TLSClientConfig: &tls.Config{
979 RootCAs: ca.Roots(),
980 ServerName: domain,
981 },
982 }
983 client := &http.Client{Transport: tr}
984 res, err := client.Get(ss.URL)
985 if err != nil {
986 t.Fatal(err)
987 }
988 defer res.Body.Close()
989 b, err := io.ReadAll(res.Body)
990 if err != nil {
991 t.Fatal(err)
992 }
993 if v := string(b); v != "OK" {
994 t.Errorf("user server response: %q; want 'OK'", v)
995 }
996}
997
998type dnsManager struct {
999 CA *acmetest.CAServer
1000}
1001
1002func (m *dnsManager) Fulfill(ctx context.Context, domain string, record string) error {
1003 m.CA.PutDNSResponse(domain, record)
1004 return nil
1005}
1006
1007func (m *dnsManager) Cleanup(ctx context.Context, domain string, record string) {
1008}
1009
1010func TestEndToEndDNS(t *testing.T) {
1011 const domain = "example.org"
1012
1013 // ACME CA server
1014 ca := acmetest.NewCAServer(t).ChallengeTypes("dns-01")
1015 ca.Start()
1016
1017 // User HTTPS server.
1018 m := &Manager{
1019 Prompt: AcceptTOS,
1020 Client: &acme.Client{DirectoryURL: ca.URL()},
1021 DNSManager: &dnsManager{ca},
1022 }
1023 us := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1024 w.Write([]byte("OK"))
1025 }))
1026 us.TLS = &tls.Config{
1027 GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
1028 cert, err := m.GetCertificate(hello)
1029 if err != nil {
1030 t.Errorf("m.GetCertificate: %v", err)
1031 }
1032 return cert, err
1033 },
1034 }
1035 us.StartTLS()
1036 defer us.Close()
1037
1038 // A client visiting user's HTTPS server.
1039 tr := &http.Transport{
1040 TLSClientConfig: &tls.Config{
1041 RootCAs: ca.Roots(),
1042 ServerName: domain,
1043 },
1044 }
1045 client := &http.Client{Transport: tr}
1046 res, err := client.Get(us.URL)
1047 if err != nil {
1048 t.Fatal(err)
1049 }
1050 defer res.Body.Close()
1051 b, err := io.ReadAll(res.Body)
1052 if err != nil {
1053 t.Fatal(err)
1054 }
1055 if v := string(b); v != "OK" {
1056 t.Errorf("user server response: %q; want 'OK'", v)
1057 }
1058}
diff --git a/crypto/acme/autocert/fork/cache.go b/crypto/acme/autocert/fork/cache.go
new file mode 100644
index 0000000..934c389
--- /dev/null
+++ b/crypto/acme/autocert/fork/cache.go
@@ -0,0 +1,135 @@
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
5package fork
6
7import (
8 "context"
9 "errors"
10 "os"
11 "path/filepath"
12)
13
14// ErrCacheMiss is returned when a certificate is not found in cache.
15var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
16
17// Cache is used by Manager to store and retrieve previously obtained certificates
18// and other account data as opaque blobs.
19//
20// Cache implementations should not rely on the key naming pattern. Keys can
21// include any printable ASCII characters, except the following: \/:*?"<>|
22type Cache interface {
23 // Get returns a certificate data for the specified key.
24 // If there's no such key, Get returns ErrCacheMiss.
25 Get(ctx context.Context, key string) ([]byte, error)
26
27 // Put stores the data in the cache under the specified key.
28 // Underlying implementations may use any data storage format,
29 // as long as the reverse operation, Get, results in the original data.
30 Put(ctx context.Context, key string, data []byte) error
31
32 // Delete removes a certificate data from the cache under the specified key.
33 // If there's no such key in the cache, Delete returns nil.
34 Delete(ctx context.Context, key string) error
35}
36
37// DirCache implements Cache using a directory on the local filesystem.
38// If the directory does not exist, it will be created with 0700 permissions.
39type DirCache string
40
41// Get reads a certificate data from the specified file name.
42func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) {
43 name = filepath.Join(string(d), filepath.Clean("/"+name))
44 var (
45 data []byte
46 err error
47 done = make(chan struct{})
48 )
49 go func() {
50 data, err = os.ReadFile(name)
51 close(done)
52 }()
53 select {
54 case <-ctx.Done():
55 return nil, ctx.Err()
56 case <-done:
57 }
58 if os.IsNotExist(err) {
59 return nil, ErrCacheMiss
60 }
61 return data, err
62}
63
64// Put writes the certificate data to the specified file name.
65// The file will be created with 0600 permissions.
66func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
67 if err := os.MkdirAll(string(d), 0700); err != nil {
68 return err
69 }
70
71 done := make(chan struct{})
72 var err error
73 go func() {
74 defer close(done)
75 var tmp string
76 if tmp, err = d.writeTempFile(name, data); err != nil {
77 return
78 }
79 defer os.Remove(tmp)
80 select {
81 case <-ctx.Done():
82 // Don't overwrite the file if the context was canceled.
83 default:
84 newName := filepath.Join(string(d), filepath.Clean("/"+name))
85 err = os.Rename(tmp, newName)
86 }
87 }()
88 select {
89 case <-ctx.Done():
90 return ctx.Err()
91 case <-done:
92 }
93 return err
94}
95
96// Delete removes the specified file name.
97func (d DirCache) Delete(ctx context.Context, name string) error {
98 name = filepath.Join(string(d), filepath.Clean("/"+name))
99 var (
100 err error
101 done = make(chan struct{})
102 )
103 go func() {
104 err = os.Remove(name)
105 close(done)
106 }()
107 select {
108 case <-ctx.Done():
109 return ctx.Err()
110 case <-done:
111 }
112 if err != nil && !os.IsNotExist(err) {
113 return err
114 }
115 return nil
116}
117
118// writeTempFile writes b to a temporary file, closes the file and returns its path.
119func (d DirCache) writeTempFile(prefix string, b []byte) (name string, reterr error) {
120 // TempFile uses 0600 permissions
121 f, err := os.CreateTemp(string(d), prefix)
122 if err != nil {
123 return "", err
124 }
125 defer func() {
126 if reterr != nil {
127 os.Remove(f.Name())
128 }
129 }()
130 if _, err := f.Write(b); err != nil {
131 f.Close()
132 return "", err
133 }
134 return f.Name(), f.Close()
135}
diff --git a/crypto/acme/autocert/fork/cache_test.go b/crypto/acme/autocert/fork/cache_test.go
new file mode 100644
index 0000000..0d690a6
--- /dev/null
+++ b/crypto/acme/autocert/fork/cache_test.go
@@ -0,0 +1,66 @@
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
5package fork
6
7import (
8 "context"
9 "os"
10 "path/filepath"
11 "reflect"
12 "testing"
13)
14
15// make sure DirCache satisfies Cache interface
16var _ Cache = DirCache("/")
17
18func TestDirCache(t *testing.T) {
19 dir, err := os.MkdirTemp("", "autocert")
20 if err != nil {
21 t.Fatal(err)
22 }
23 defer os.RemoveAll(dir)
24 dir = filepath.Join(dir, "certs") // a nonexistent dir
25 cache := DirCache(dir)
26 ctx := context.Background()
27
28 // test cache miss
29 if _, err := cache.Get(ctx, "nonexistent"); err != ErrCacheMiss {
30 t.Errorf("get: %v; want ErrCacheMiss", err)
31 }
32
33 // test put/get
34 b1 := []byte{1}
35 if err := cache.Put(ctx, "dummy", b1); err != nil {
36 t.Fatalf("put: %v", err)
37 }
38 b2, err := cache.Get(ctx, "dummy")
39 if err != nil {
40 t.Fatalf("get: %v", err)
41 }
42 if !reflect.DeepEqual(b1, b2) {
43 t.Errorf("b1 = %v; want %v", b1, b2)
44 }
45 name := filepath.Join(dir, "dummy")
46 if _, err := os.Stat(name); err != nil {
47 t.Error(err)
48 }
49
50 // test put deletes temp file
51 tmp, err := filepath.Glob(name + "?*")
52 if err != nil {
53 t.Error(err)
54 }
55 if tmp != nil {
56 t.Errorf("temp file exists: %s", tmp)
57 }
58
59 // test delete
60 if err := cache.Delete(ctx, "dummy"); err != nil {
61 t.Fatalf("delete: %v", err)
62 }
63 if _, err := cache.Get(ctx, "dummy"); err != ErrCacheMiss {
64 t.Errorf("get: %v; want ErrCacheMiss", err)
65 }
66}
diff --git a/crypto/acme/autocert/fork/internal/acmetest/ca.go b/crypto/acme/autocert/fork/internal/acmetest/ca.go
new file mode 100644
index 0000000..8fc4273
--- /dev/null
+++ b/crypto/acme/autocert/fork/internal/acmetest/ca.go
@@ -0,0 +1,817 @@
1// Copyright 2018 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
5// Package acmetest provides types for testing acme and autocert packages.
6//
7// TODO: Consider moving this to x/crypto/acme/internal/acmetest for acme tests as well.
8package acmetest
9
10import (
11 "context"
12 "crypto"
13 "crypto/ecdsa"
14 "crypto/elliptic"
15 "crypto/rand"
16 "crypto/rsa"
17 "crypto/tls"
18 "crypto/x509"
19 "crypto/x509/pkix"
20 "encoding/asn1"
21 "encoding/base64"
22 "encoding/json"
23 "encoding/pem"
24 "fmt"
25 "io"
26 "math/big"
27 "net"
28 "net/http"
29 "net/http/httptest"
30 "path"
31 "strconv"
32 "strings"
33 "sync"
34 "testing"
35 "time"
36
37 "golang.org/x/crypto/acme"
38)
39
40// CAServer is a simple test server which implements ACME spec bits needed for testing.
41type CAServer struct {
42 rootKey crypto.Signer
43 rootCert []byte // DER encoding
44 rootTemplate *x509.Certificate
45
46 t *testing.T
47 server *httptest.Server
48 issuer pkix.Name
49 challengeTypes []string
50 url string
51 roots *x509.CertPool
52 eabRequired bool
53
54 mu sync.Mutex
55 certCount int // number of issued certs
56 acctRegistered bool // set once an account has been registered
57 domainAddr map[string]string // domain name to addr:port resolution
58 dnsResponses map[string]string // responses to dns challenges
59 domainGetCert map[string]getCertificateFunc // domain name to GetCertificate function
60 domainHandler map[string]http.Handler // domain name to Handle function
61 validAuthz map[string]*authorization // valid authz, keyed by domain name
62 authorizations []*authorization // all authz, index is used as ID
63 orders []*order // index is used as order ID
64 errors []error // encountered client errors
65}
66
67type getCertificateFunc func(hello *tls.ClientHelloInfo) (*tls.Certificate, error)
68
69// NewCAServer creates a new ACME test server. The returned CAServer issues
70// certs signed with the CA roots available in the Roots field.
71func NewCAServer(t *testing.T) *CAServer {
72 ca := &CAServer{t: t,
73 challengeTypes: []string{"fake-01", "tls-alpn-01", "http-01", "dns-01"},
74 domainAddr: make(map[string]string),
75 domainGetCert: make(map[string]getCertificateFunc),
76 domainHandler: make(map[string]http.Handler),
77 validAuthz: make(map[string]*authorization),
78 dnsResponses: make(map[string]string),
79 }
80
81 ca.server = httptest.NewUnstartedServer(http.HandlerFunc(ca.handle))
82
83 r, err := rand.Int(rand.Reader, big.NewInt(1000000))
84 if err != nil {
85 panic(fmt.Sprintf("rand.Int: %v", err))
86 }
87 ca.issuer = pkix.Name{
88 Organization: []string{"Test Acme Co"},
89 CommonName: "Root CA " + r.String(),
90 }
91
92 return ca
93}
94
95func (ca *CAServer) generateRoot() {
96 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
97 if err != nil {
98 panic(fmt.Sprintf("ecdsa.GenerateKey: %v", err))
99 }
100 tmpl := &x509.Certificate{
101 SerialNumber: big.NewInt(1),
102 Subject: ca.issuer,
103 NotBefore: time.Now(),
104 NotAfter: time.Now().Add(365 * 24 * time.Hour),
105 KeyUsage: x509.KeyUsageCertSign,
106 BasicConstraintsValid: true,
107 IsCA: true,
108 }
109 der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
110 if err != nil {
111 panic(fmt.Sprintf("x509.CreateCertificate: %v", err))
112 }
113 cert, err := x509.ParseCertificate(der)
114 if err != nil {
115 panic(fmt.Sprintf("x509.ParseCertificate: %v", err))
116 }
117 ca.roots = x509.NewCertPool()
118 ca.roots.AddCert(cert)
119 ca.rootKey = key
120 ca.rootCert = der
121 ca.rootTemplate = tmpl
122}
123
124func (ca *CAServer) PutDNSResponse(domain, record string) {
125 ca.mu.Lock()
126 defer ca.mu.Unlock()
127 ca.dnsResponses[domain] = record
128}
129
130// IssuerName sets the name of the issuing CA.
131func (ca *CAServer) IssuerName(name pkix.Name) *CAServer {
132 if ca.url != "" {
133 panic("IssuerName must be called before Start")
134 }
135 ca.issuer = name
136 return ca
137}
138
139// ChallengeTypes sets the supported challenge types.
140func (ca *CAServer) ChallengeTypes(types ...string) *CAServer {
141 if ca.url != "" {
142 panic("ChallengeTypes must be called before Start")
143 }
144 ca.challengeTypes = types
145 return ca
146}
147
148// URL returns the server address, after Start has been called.
149func (ca *CAServer) URL() string {
150 if ca.url == "" {
151 panic("URL called before Start")
152 }
153 return ca.url
154}
155
156// Roots returns a pool cointaining the CA root.
157func (ca *CAServer) Roots() *x509.CertPool {
158 if ca.url == "" {
159 panic("Roots called before Start")
160 }
161 return ca.roots
162}
163
164// ExternalAccountRequired makes an EAB JWS required for account registration.
165func (ca *CAServer) ExternalAccountRequired() *CAServer {
166 if ca.url != "" {
167 panic("ExternalAccountRequired must be called before Start")
168 }
169 ca.eabRequired = true
170 return ca
171}
172
173// Start starts serving requests. The server address becomes available in the
174// URL field.
175func (ca *CAServer) Start() *CAServer {
176 if ca.url == "" {
177 ca.generateRoot()
178 ca.server.Start()
179 ca.t.Cleanup(ca.server.Close)
180 ca.url = ca.server.URL
181 }
182 return ca
183}
184
185func (ca *CAServer) serverURL(format string, arg ...interface{}) string {
186 return ca.server.URL + fmt.Sprintf(format, arg...)
187}
188
189func (ca *CAServer) addr(domain string) (string, bool) {
190 ca.mu.Lock()
191 defer ca.mu.Unlock()
192 addr, ok := ca.domainAddr[domain]
193 return addr, ok
194}
195
196func (ca *CAServer) getCert(domain string) (getCertificateFunc, bool) {
197 ca.mu.Lock()
198 defer ca.mu.Unlock()
199 f, ok := ca.domainGetCert[domain]
200 return f, ok
201}
202
203func (ca *CAServer) getHandler(domain string) (http.Handler, bool) {
204 ca.mu.Lock()
205 defer ca.mu.Unlock()
206 h, ok := ca.domainHandler[domain]
207 return h, ok
208}
209
210func (ca *CAServer) httpErrorf(w http.ResponseWriter, code int, format string, a ...interface{}) {
211 s := fmt.Sprintf(format, a...)
212 ca.t.Errorf(format, a...)
213 http.Error(w, s, code)
214}
215
216// Resolve adds a domain to address resolution for the ca to dial to
217// when validating challenges for the domain authorization.
218func (ca *CAServer) Resolve(domain, addr string) {
219 ca.mu.Lock()
220 defer ca.mu.Unlock()
221 ca.domainAddr[domain] = addr
222}
223
224// ResolveGetCertificate redirects TLS connections for domain to f when
225// validating challenges for the domain authorization.
226func (ca *CAServer) ResolveGetCertificate(domain string, f getCertificateFunc) {
227 ca.mu.Lock()
228 defer ca.mu.Unlock()
229 ca.domainGetCert[domain] = f
230}
231
232// ResolveHandler redirects HTTP requests for domain to f when
233// validating challenges for the domain authorization.
234func (ca *CAServer) ResolveHandler(domain string, h http.Handler) {
235 ca.mu.Lock()
236 defer ca.mu.Unlock()
237 ca.domainHandler[domain] = h
238}
239
240type discovery struct {
241 NewNonce string `json:"newNonce"`
242 NewAccount string `json:"newAccount"`
243 NewOrder string `json:"newOrder"`
244 NewAuthz string `json:"newAuthz"`
245
246 Meta discoveryMeta `json:"meta,omitempty"`
247}
248
249type discoveryMeta struct {
250 ExternalAccountRequired bool `json:"externalAccountRequired,omitempty"`
251}
252
253type challenge struct {
254 URI string `json:"uri"`
255 Type string `json:"type"`
256 Token string `json:"token"`
257}
258
259type authorization struct {
260 Status string `json:"status"`
261 Challenges []challenge `json:"challenges"`
262
263 domain string
264 id int
265}
266
267type order struct {
268 Status string `json:"status"`
269 AuthzURLs []string `json:"authorizations"`
270 FinalizeURL string `json:"finalize"` // CSR submit URL
271 CertURL string `json:"certificate"` // already issued cert
272
273 leaf []byte // issued cert in DER format
274}
275
276func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
277 ca.t.Logf("%s %s", r.Method, r.URL)
278 w.Header().Set("Replay-Nonce", "nonce")
279 // TODO: Verify nonce header for all POST requests.
280
281 switch {
282 default:
283 ca.httpErrorf(w, http.StatusBadRequest, "unrecognized r.URL.Path: %s", r.URL.Path)
284
285 // Discovery request.
286 case r.URL.Path == "/":
287 resp := &discovery{
288 NewNonce: ca.serverURL("/new-nonce"),
289 NewAccount: ca.serverURL("/new-account"),
290 NewOrder: ca.serverURL("/new-order"),
291 Meta: discoveryMeta{
292 ExternalAccountRequired: ca.eabRequired,
293 },
294 }
295 if err := json.NewEncoder(w).Encode(resp); err != nil {
296 panic(fmt.Sprintf("discovery response: %v", err))
297 }
298
299 // Nonce requests.
300 case r.URL.Path == "/new-nonce":
301 // Nonce values are always set. Nothing else to do.
302 return
303
304 // Client key registration request.
305 case r.URL.Path == "/new-account":
306 ca.mu.Lock()
307 defer ca.mu.Unlock()
308 if ca.acctRegistered {
309 ca.httpErrorf(w, http.StatusServiceUnavailable, "multiple accounts are not implemented")
310 return
311 }
312 ca.acctRegistered = true
313
314 var req struct {
315 ExternalAccountBinding json.RawMessage
316 }
317
318 if err := decodePayload(&req, r.Body); err != nil {
319 ca.httpErrorf(w, http.StatusBadRequest, err.Error())
320 return
321 }
322
323 if ca.eabRequired && len(req.ExternalAccountBinding) == 0 {
324 ca.httpErrorf(w, http.StatusBadRequest, "registration failed: no JWS for EAB")
325 return
326 }
327
328 // TODO: Check the user account key against a ca.accountKeys?
329 w.Header().Set("Location", ca.serverURL("/accounts/1"))
330 w.WriteHeader(http.StatusCreated)
331 w.Write([]byte("{}"))
332
333 // New order request.
334 case r.URL.Path == "/new-order":
335 var req struct {
336 Identifiers []struct{ Value string }
337 }
338 if err := decodePayload(&req, r.Body); err != nil {
339 ca.httpErrorf(w, http.StatusBadRequest, err.Error())
340 return
341 }
342 ca.mu.Lock()
343 defer ca.mu.Unlock()
344 o := &order{Status: acme.StatusPending}
345 for _, id := range req.Identifiers {
346 z := ca.authz(id.Value)
347 o.AuthzURLs = append(o.AuthzURLs, ca.serverURL("/authz/%d", z.id))
348 }
349 orderID := len(ca.orders)
350 ca.orders = append(ca.orders, o)
351 w.Header().Set("Location", ca.serverURL("/orders/%d", orderID))
352 w.WriteHeader(http.StatusCreated)
353 if err := json.NewEncoder(w).Encode(o); err != nil {
354 panic(err)
355 }
356
357 // Existing order status requests.
358 case strings.HasPrefix(r.URL.Path, "/orders/"):
359 ca.mu.Lock()
360 defer ca.mu.Unlock()
361 o, err := ca.storedOrder(strings.TrimPrefix(r.URL.Path, "/orders/"))
362 if err != nil {
363 ca.httpErrorf(w, http.StatusBadRequest, err.Error())
364 return
365 }
366 if err := json.NewEncoder(w).Encode(o); err != nil {
367 panic(err)
368 }
369
370 // Accept challenge requests.
371 case strings.HasPrefix(r.URL.Path, "/challenge/"):
372 parts := strings.Split(r.URL.Path, "/")
373 typ, id := parts[len(parts)-2], parts[len(parts)-1]
374 ca.mu.Lock()
375 supported := false
376 for _, suppTyp := range ca.challengeTypes {
377 if suppTyp == typ {
378 supported = true
379 }
380 }
381 a, err := ca.storedAuthz(id)
382 ca.mu.Unlock()
383 if !supported {
384 ca.httpErrorf(w, http.StatusBadRequest, "unsupported challenge: %v", typ)
385 return
386 }
387 if err != nil {
388 ca.httpErrorf(w, http.StatusBadRequest, "challenge accept: %v", err)
389 return
390 }
391 ca.validateChallenge(a, typ)
392 w.Write([]byte("{}"))
393
394 // Get authorization status requests.
395 case strings.HasPrefix(r.URL.Path, "/authz/"):
396 var req struct{ Status string }
397 decodePayload(&req, r.Body)
398 deactivate := req.Status == "deactivated"
399 ca.mu.Lock()
400 defer ca.mu.Unlock()
401 authz, err := ca.storedAuthz(strings.TrimPrefix(r.URL.Path, "/authz/"))
402 if err != nil {
403 ca.httpErrorf(w, http.StatusNotFound, "%v", err)
404 return
405 }
406 if deactivate {
407 // Note we don't invalidate authorized orders as we should.
408 authz.Status = "deactivated"
409 ca.t.Logf("authz %d is now %s", authz.id, authz.Status)
410 ca.updatePendingOrders()
411 }
412 if err := json.NewEncoder(w).Encode(authz); err != nil {
413 panic(fmt.Sprintf("encoding authz %d: %v", authz.id, err))
414 }
415
416 // Certificate issuance request.
417 case strings.HasPrefix(r.URL.Path, "/new-cert/"):
418 ca.mu.Lock()
419 defer ca.mu.Unlock()
420 orderID := strings.TrimPrefix(r.URL.Path, "/new-cert/")
421 o, err := ca.storedOrder(orderID)
422 if err != nil {
423 ca.httpErrorf(w, http.StatusBadRequest, err.Error())
424 return
425 }
426 if o.Status != acme.StatusReady {
427 ca.httpErrorf(w, http.StatusForbidden, "order status: %s", o.Status)
428 return
429 }
430 // Validate CSR request.
431 var req struct {
432 CSR string `json:"csr"`
433 }
434 decodePayload(&req, r.Body)
435 b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
436 csr, err := x509.ParseCertificateRequest(b)
437 if err != nil {
438 ca.httpErrorf(w, http.StatusBadRequest, err.Error())
439 return
440 }
441 // Issue the certificate.
442 der, err := ca.leafCert(csr)
443 if err != nil {
444 ca.httpErrorf(w, http.StatusBadRequest, "new-cert response: ca.leafCert: %v", err)
445 return
446 }
447 o.leaf = der
448 o.CertURL = ca.serverURL("/issued-cert/%s", orderID)
449 o.Status = acme.StatusValid
450 if err := json.NewEncoder(w).Encode(o); err != nil {
451 panic(err)
452 }
453
454 // Already issued cert download requests.
455 case strings.HasPrefix(r.URL.Path, "/issued-cert/"):
456 ca.mu.Lock()
457 defer ca.mu.Unlock()
458 o, err := ca.storedOrder(strings.TrimPrefix(r.URL.Path, "/issued-cert/"))
459 if err != nil {
460 ca.httpErrorf(w, http.StatusBadRequest, err.Error())
461 return
462 }
463 if o.Status != acme.StatusValid {
464 ca.httpErrorf(w, http.StatusForbidden, "order status: %s", o.Status)
465 return
466 }
467 w.Header().Set("Content-Type", "application/pem-certificate-chain")
468 pem.Encode(w, &pem.Block{Type: "CERTIFICATE", Bytes: o.leaf})
469 pem.Encode(w, &pem.Block{Type: "CERTIFICATE", Bytes: ca.rootCert})
470 }
471}
472
473// storedOrder retrieves a previously created order at index i.
474// It requires ca.mu to be locked.
475func (ca *CAServer) storedOrder(i string) (*order, error) {
476 idx, err := strconv.Atoi(i)
477 if err != nil {
478 return nil, fmt.Errorf("storedOrder: %v", err)
479 }
480 if idx < 0 {
481 return nil, fmt.Errorf("storedOrder: invalid order index %d", idx)
482 }
483 if idx > len(ca.orders)-1 {
484 return nil, fmt.Errorf("storedOrder: no such order %d", idx)
485 }
486
487 ca.updatePendingOrders()
488 return ca.orders[idx], nil
489}
490
491// storedAuthz retrieves a previously created authz at index i.
492// It requires ca.mu to be locked.
493func (ca *CAServer) storedAuthz(i string) (*authorization, error) {
494 idx, err := strconv.Atoi(i)
495 if err != nil {
496 return nil, fmt.Errorf("storedAuthz: %v", err)
497 }
498 if idx < 0 {
499 return nil, fmt.Errorf("storedAuthz: invalid authz index %d", idx)
500 }
501 if idx > len(ca.authorizations)-1 {
502 return nil, fmt.Errorf("storedAuthz: no such authz %d", idx)
503 }
504 return ca.authorizations[idx], nil
505}
506
507// authz returns an existing valid authorization for the identifier or creates a
508// new one. It requires ca.mu to be locked.
509func (ca *CAServer) authz(identifier string) *authorization {
510 authz, ok := ca.validAuthz[identifier]
511 if !ok {
512 authzId := len(ca.authorizations)
513 authz = &authorization{
514 id: authzId,
515 domain: identifier,
516 Status: acme.StatusPending,
517 }
518 for _, typ := range ca.challengeTypes {
519 authz.Challenges = append(authz.Challenges, challenge{
520 Type: typ,
521 URI: ca.serverURL("/challenge/%s/%d", typ, authzId),
522 Token: challengeToken(authz.domain, typ, authzId),
523 })
524 }
525 ca.authorizations = append(ca.authorizations, authz)
526 }
527 return authz
528}
529
530// leafCert issues a new certificate.
531// It requires ca.mu to be locked.
532func (ca *CAServer) leafCert(csr *x509.CertificateRequest) (der []byte, err error) {
533 ca.certCount++ // next leaf cert serial number
534 leaf := &x509.Certificate{
535 SerialNumber: big.NewInt(int64(ca.certCount)),
536 Subject: pkix.Name{Organization: []string{"Test Acme Co"}},
537 NotBefore: time.Now(),
538 NotAfter: time.Now().Add(90 * 24 * time.Hour),
539 KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
540 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
541 DNSNames: csr.DNSNames,
542 BasicConstraintsValid: true,
543 }
544 if len(csr.DNSNames) == 0 {
545 leaf.DNSNames = []string{csr.Subject.CommonName}
546 }
547 return x509.CreateCertificate(rand.Reader, leaf, ca.rootTemplate, csr.PublicKey, ca.rootKey)
548}
549
550// LeafCert issues a leaf certificate.
551func (ca *CAServer) LeafCert(name, keyType string, notBefore, notAfter time.Time) *tls.Certificate {
552 if ca.url == "" {
553 panic("LeafCert called before Start")
554 }
555
556 ca.mu.Lock()
557 defer ca.mu.Unlock()
558 var pk crypto.Signer
559 switch keyType {
560 case "RSA":
561 var err error
562 pk, err = rsa.GenerateKey(rand.Reader, 1024)
563 if err != nil {
564 ca.t.Fatal(err)
565 }
566 case "ECDSA":
567 var err error
568 pk, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
569 if err != nil {
570 ca.t.Fatal(err)
571 }
572 default:
573 panic("LeafCert: unknown key type")
574 }
575 ca.certCount++ // next leaf cert serial number
576 leaf := &x509.Certificate{
577 SerialNumber: big.NewInt(int64(ca.certCount)),
578 Subject: pkix.Name{Organization: []string{"Test Acme Co"}},
579 NotBefore: notBefore,
580 NotAfter: notAfter,
581 KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
582 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
583 DNSNames: []string{name},
584 BasicConstraintsValid: true,
585 }
586 der, err := x509.CreateCertificate(rand.Reader, leaf, ca.rootTemplate, pk.Public(), ca.rootKey)
587 if err != nil {
588 ca.t.Fatal(err)
589 }
590 return &tls.Certificate{
591 Certificate: [][]byte{der},
592 PrivateKey: pk,
593 }
594}
595
596func (ca *CAServer) validateChallenge(authz *authorization, typ string) {
597 var err error
598 switch typ {
599 case "tls-alpn-01":
600 err = ca.verifyALPNChallenge(authz)
601 case "http-01":
602 err = ca.verifyHTTPChallenge(authz)
603 case "dns-01":
604 err = ca.verifyDNSChallenge(authz)
605 default:
606 panic(fmt.Sprintf("validation of %q is not implemented", typ))
607 }
608 ca.mu.Lock()
609 defer ca.mu.Unlock()
610 if err != nil {
611 authz.Status = "invalid"
612 } else {
613 authz.Status = "valid"
614 ca.validAuthz[authz.domain] = authz
615 }
616 ca.t.Logf("validated %q for %q, err: %v", typ, authz.domain, err)
617 ca.t.Logf("authz %d is now %s", authz.id, authz.Status)
618
619 ca.updatePendingOrders()
620}
621
622func (ca *CAServer) updatePendingOrders() {
623 // Update all pending orders.
624 // An order becomes "ready" if all authorizations are "valid".
625 // An order becomes "invalid" if any authorization is "invalid".
626 // Status changes: https://tools.ietf.org/html/rfc8555#section-7.1.6
627 for i, o := range ca.orders {
628 if o.Status != acme.StatusPending {
629 continue
630 }
631
632 countValid, countInvalid := ca.validateAuthzURLs(o.AuthzURLs, i)
633 if countInvalid > 0 {
634 o.Status = acme.StatusInvalid
635 ca.t.Logf("order %d is now invalid", i)
636 continue
637 }
638 if countValid == len(o.AuthzURLs) {
639 o.Status = acme.StatusReady
640 o.FinalizeURL = ca.serverURL("/new-cert/%d", i)
641 ca.t.Logf("order %d is now ready", i)
642 }
643 }
644}
645
646func (ca *CAServer) validateAuthzURLs(urls []string, orderNum int) (countValid, countInvalid int) {
647 for _, zurl := range urls {
648 z, err := ca.storedAuthz(path.Base(zurl))
649 if err != nil {
650 ca.t.Logf("no authz %q for order %d", zurl, orderNum)
651 continue
652 }
653 if z.Status == acme.StatusInvalid {
654 countInvalid++
655 }
656 if z.Status == acme.StatusValid {
657 countValid++
658 }
659 }
660 return countValid, countInvalid
661}
662
663func (ca *CAServer) verifyALPNChallenge(a *authorization) error {
664 const acmeALPNProto = "acme-tls/1"
665
666 addr, haveAddr := ca.addr(a.domain)
667 getCert, haveGetCert := ca.getCert(a.domain)
668 if !haveAddr && !haveGetCert {
669 return fmt.Errorf("no resolution information for %q", a.domain)
670 }
671 if haveAddr && haveGetCert {
672 return fmt.Errorf("overlapping resolution information for %q", a.domain)
673 }
674
675 var crt *x509.Certificate
676 switch {
677 case haveAddr:
678 conn, err := tls.Dial("tcp", addr, &tls.Config{
679 ServerName: a.domain,
680 InsecureSkipVerify: true,
681 NextProtos: []string{acmeALPNProto},
682 MinVersion: tls.VersionTLS12,
683 })
684 if err != nil {
685 return err
686 }
687 if v := conn.ConnectionState().NegotiatedProtocol; v != acmeALPNProto {
688 return fmt.Errorf("CAServer: verifyALPNChallenge: negotiated proto is %q; want %q", v, acmeALPNProto)
689 }
690 if n := len(conn.ConnectionState().PeerCertificates); n != 1 {
691 return fmt.Errorf("len(PeerCertificates) = %d; want 1", n)
692 }
693 crt = conn.ConnectionState().PeerCertificates[0]
694 case haveGetCert:
695 hello := &tls.ClientHelloInfo{
696 ServerName: a.domain,
697 // TODO: support selecting ECDSA.
698 CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},
699 SupportedProtos: []string{acme.ALPNProto},
700 SupportedVersions: []uint16{tls.VersionTLS12},
701 }
702 c, err := getCert(hello)
703 if err != nil {
704 return err
705 }
706 crt, err = x509.ParseCertificate(c.Certificate[0])
707 if err != nil {
708 return err
709 }
710 }
711
712 if err := crt.VerifyHostname(a.domain); err != nil {
713 return fmt.Errorf("verifyALPNChallenge: VerifyHostname: %v", err)
714 }
715 // See RFC 8737, Section 6.1.
716 oid := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31}
717 for _, x := range crt.Extensions {
718 if x.Id.Equal(oid) {
719 // TODO: check the token.
720 return nil
721 }
722 }
723 return fmt.Errorf("verifyTokenCert: no id-pe-acmeIdentifier extension found")
724}
725
726func (ca *CAServer) verifyDNSChallenge(a *authorization) error {
727 ca.mu.Lock()
728 defer ca.mu.Unlock()
729
730 if _, ok := ca.dnsResponses[a.domain]; !ok {
731 return fmt.Errorf("verifyDNSChallenge: no DNS response registered for domain")
732 }
733
734 return nil
735}
736
737func (ca *CAServer) verifyHTTPChallenge(a *authorization) error {
738 addr, haveAddr := ca.addr(a.domain)
739 handler, haveHandler := ca.getHandler(a.domain)
740 if !haveAddr && !haveHandler {
741 return fmt.Errorf("no resolution information for %q", a.domain)
742 }
743 if haveAddr && haveHandler {
744 return fmt.Errorf("overlapping resolution information for %q", a.domain)
745 }
746
747 token := challengeToken(a.domain, "http-01", a.id)
748 path := "/.well-known/acme-challenge/" + token
749
750 var body string
751 switch {
752 case haveAddr:
753 t := &http.Transport{
754 DialContext: func(ctx context.Context, network, _ string) (net.Conn, error) {
755 return (&net.Dialer{}).DialContext(ctx, network, addr)
756 },
757 }
758 req, err := http.NewRequest("GET", "http://"+a.domain+path, nil)
759 if err != nil {
760 return err
761 }
762 res, err := t.RoundTrip(req)
763 if err != nil {
764 return err
765 }
766 if res.StatusCode != http.StatusOK {
767 return fmt.Errorf("http token: w.Code = %d; want %d", res.StatusCode, http.StatusOK)
768 }
769 b, err := io.ReadAll(res.Body)
770 if err != nil {
771 return err
772 }
773 body = string(b)
774 case haveHandler:
775 r := httptest.NewRequest("GET", path, nil)
776 r.Host = a.domain
777 w := httptest.NewRecorder()
778 handler.ServeHTTP(w, r)
779 if w.Code != http.StatusOK {
780 return fmt.Errorf("http token: w.Code = %d; want %d", w.Code, http.StatusOK)
781 }
782 body = w.Body.String()
783 }
784
785 if !strings.HasPrefix(body, token) {
786 return fmt.Errorf("http token value = %q; want 'token-http-01.' prefix", body)
787 }
788 return nil
789}
790
791func decodePayload(v interface{}, r io.Reader) error {
792 var req struct{ Payload string }
793 if err := json.NewDecoder(r).Decode(&req); err != nil {
794 return err
795 }
796 payload, err := base64.RawURLEncoding.DecodeString(req.Payload)
797 if err != nil {
798 return err
799 }
800 return json.Unmarshal(payload, v)
801}
802
803func challengeToken(domain, challType string, authzID int) string {
804 return fmt.Sprintf("token-%s-%s-%d", domain, challType, authzID)
805}
806
807func unique(a []string) []string {
808 seen := make(map[string]bool)
809 var res []string
810 for _, s := range a {
811 if s != "" && !seen[s] {
812 seen[s] = true
813 res = append(res, s)
814 }
815 }
816 return res
817}
diff --git a/crypto/acme/autocert/fork/listener.go b/crypto/acme/autocert/fork/listener.go
new file mode 100644
index 0000000..21fd6dd
--- /dev/null
+++ b/crypto/acme/autocert/fork/listener.go
@@ -0,0 +1,155 @@
1// Copyright 2017 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
5package fork
6
7import (
8 "crypto/tls"
9 "log"
10 "net"
11 "os"
12 "path/filepath"
13 "runtime"
14 "time"
15)
16
17// NewListener returns a net.Listener that listens on the standard TLS
18// port (443) on all interfaces and returns *tls.Conn connections with
19// LetsEncrypt certificates for the provided domain or domains.
20//
21// It enables one-line HTTPS servers:
22//
23// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
24//
25// NewListener is a convenience function for a common configuration.
26// More complex or custom configurations can use the autocert.Manager
27// type instead.
28//
29// Use of this function implies acceptance of the LetsEncrypt Terms of
30// Service. If domains is not empty, the provided domains are passed
31// to HostWhitelist. If domains is empty, the listener will do
32// LetsEncrypt challenges for any requested domain, which is not
33// recommended.
34//
35// Certificates are cached in a "golang-autocert" directory under an
36// operating system-specific cache or temp directory. This may not
37// be suitable for servers spanning multiple machines.
38//
39// The returned listener uses a *tls.Config that enables HTTP/2, and
40// should only be used with servers that support HTTP/2.
41//
42// The returned Listener also enables TCP keep-alives on the accepted
43// connections. The returned *tls.Conn are returned before their TLS
44// handshake has completed.
45func NewListener(domains ...string) net.Listener {
46 m := &Manager{
47 Prompt: AcceptTOS,
48 }
49 if len(domains) > 0 {
50 m.HostPolicy = HostWhitelist(domains...)
51 }
52 dir := cacheDir()
53 if err := os.MkdirAll(dir, 0700); err != nil {
54 log.Printf("warning: autocert.NewListener not using a cache: %v", err)
55 } else {
56 m.Cache = DirCache(dir)
57 }
58 return m.Listener()
59}
60
61// Listener listens on the standard TLS port (443) on all interfaces
62// and returns a net.Listener returning *tls.Conn connections.
63//
64// The returned listener uses a *tls.Config that enables HTTP/2, and
65// should only be used with servers that support HTTP/2.
66//
67// The returned Listener also enables TCP keep-alives on the accepted
68// connections. The returned *tls.Conn are returned before their TLS
69// handshake has completed.
70//
71// Unlike NewListener, it is the caller's responsibility to initialize
72// the Manager m's Prompt, Cache, HostPolicy, and other desired options.
73func (m *Manager) Listener() net.Listener {
74 ln := &listener{
75 conf: m.TLSConfig(),
76 }
77 ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
78 return ln
79}
80
81type listener struct {
82 conf *tls.Config
83
84 tcpListener net.Listener
85 tcpListenErr error
86}
87
88func (ln *listener) Accept() (net.Conn, error) {
89 if ln.tcpListenErr != nil {
90 return nil, ln.tcpListenErr
91 }
92 conn, err := ln.tcpListener.Accept()
93 if err != nil {
94 return nil, err
95 }
96 tcpConn := conn.(*net.TCPConn)
97
98 // Because Listener is a convenience function, help out with
99 // this too. This is not possible for the caller to set once
100 // we return a *tcp.Conn wrapping an inaccessible net.Conn.
101 // If callers don't want this, they can do things the manual
102 // way and tweak as needed. But this is what net/http does
103 // itself, so copy that. If net/http changes, we can change
104 // here too.
105 tcpConn.SetKeepAlive(true)
106 tcpConn.SetKeepAlivePeriod(3 * time.Minute)
107
108 return tls.Server(tcpConn, ln.conf), nil
109}
110
111func (ln *listener) Addr() net.Addr {
112 if ln.tcpListener != nil {
113 return ln.tcpListener.Addr()
114 }
115 // net.Listen failed. Return something non-nil in case callers
116 // call Addr before Accept:
117 return &net.TCPAddr{IP: net.IP{0, 0, 0, 0}, Port: 443}
118}
119
120func (ln *listener) Close() error {
121 if ln.tcpListenErr != nil {
122 return ln.tcpListenErr
123 }
124 return ln.tcpListener.Close()
125}
126
127func homeDir() string {
128 if runtime.GOOS == "windows" {
129 return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
130 }
131 if h := os.Getenv("HOME"); h != "" {
132 return h
133 }
134 return "/"
135}
136
137func cacheDir() string {
138 const base = "golang-autocert"
139 switch runtime.GOOS {
140 case "darwin":
141 return filepath.Join(homeDir(), "Library", "Caches", base)
142 case "windows":
143 for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
144 if v := os.Getenv(ev); v != "" {
145 return filepath.Join(v, base)
146 }
147 }
148 // Worst case:
149 return filepath.Join(homeDir(), base)
150 }
151 if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
152 return filepath.Join(xdg, base)
153 }
154 return filepath.Join(homeDir(), ".cache", base)
155}
diff --git a/crypto/acme/autocert/fork/renewal.go b/crypto/acme/autocert/fork/renewal.go
new file mode 100644
index 0000000..789e20a
--- /dev/null
+++ b/crypto/acme/autocert/fork/renewal.go
@@ -0,0 +1,156 @@
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
5package fork
6
7import (
8 "context"
9 "crypto"
10 "sync"
11 "time"
12)
13
14// renewJitter is the maximum deviation from Manager.RenewBefore.
15const renewJitter = time.Hour
16
17// domainRenewal tracks the state used by the periodic timers
18// renewing a single domain's cert.
19type domainRenewal struct {
20 m *Manager
21 ck certKey
22 key crypto.Signer
23
24 timerMu sync.Mutex
25 timer *time.Timer
26 timerClose chan struct{} // if non-nil, renew closes this channel (and nils out the timer fields) instead of running
27}
28
29// start starts a cert renewal timer at the time
30// defined by the certificate expiration time exp.
31//
32// If the timer is already started, calling start is a noop.
33func (dr *domainRenewal) start(exp time.Time) {
34 dr.timerMu.Lock()
35 defer dr.timerMu.Unlock()
36 if dr.timer != nil {
37 return
38 }
39 dr.timer = time.AfterFunc(dr.next(exp), dr.renew)
40}
41
42// stop stops the cert renewal timer and waits for any in-flight calls to renew
43// to complete. If the timer is already stopped, calling stop is a noop.
44func (dr *domainRenewal) stop() {
45 dr.timerMu.Lock()
46 defer dr.timerMu.Unlock()
47 for {
48 if dr.timer == nil {
49 return
50 }
51 if dr.timer.Stop() {
52 dr.timer = nil
53 return
54 } else {
55 // dr.timer fired, and we acquired dr.timerMu before the renew callback did.
56 // (We know this because otherwise the renew callback would have reset dr.timer!)
57 timerClose := make(chan struct{})
58 dr.timerClose = timerClose
59 dr.timerMu.Unlock()
60 <-timerClose
61 dr.timerMu.Lock()
62 }
63 }
64}
65
66// renew is called periodically by a timer.
67// The first renew call is kicked off by dr.start.
68func (dr *domainRenewal) renew() {
69 dr.timerMu.Lock()
70 defer dr.timerMu.Unlock()
71 if dr.timerClose != nil {
72 close(dr.timerClose)
73 dr.timer, dr.timerClose = nil, nil
74 return
75 }
76
77 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
78 defer cancel()
79 // TODO: rotate dr.key at some point?
80 next, err := dr.do(ctx)
81 if err != nil {
82 next = renewJitter / 2
83 next += time.Duration(pseudoRand.int63n(int64(next)))
84 }
85 testDidRenewLoop(next, err)
86 dr.timer = time.AfterFunc(next, dr.renew)
87}
88
89// updateState locks and replaces the relevant Manager.state item with the given
90// state. It additionally updates dr.key with the given state's key.
91func (dr *domainRenewal) updateState(state *certState) {
92 dr.m.stateMu.Lock()
93 defer dr.m.stateMu.Unlock()
94 dr.key = state.key
95 dr.m.state[dr.ck] = state
96}
97
98// do is similar to Manager.createCert but it doesn't lock a Manager.state item.
99// Instead, it requests a new certificate independently and, upon success,
100// replaces dr.m.state item with a new one and updates cache for the given domain.
101//
102// It may lock and update the Manager.state if the expiration date of the currently
103// cached cert is far enough in the future.
104//
105// The returned value is a time interval after which the renewal should occur again.
106func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
107 // a race is likely unavoidable in a distributed environment
108 // but we try nonetheless
109 if tlscert, err := dr.m.cacheGet(ctx, dr.ck); err == nil {
110 next := dr.next(tlscert.Leaf.NotAfter)
111 if next > dr.m.renewBefore()+renewJitter {
112 signer, ok := tlscert.PrivateKey.(crypto.Signer)
113 if ok {
114 state := &certState{
115 key: signer,
116 cert: tlscert.Certificate,
117 leaf: tlscert.Leaf,
118 }
119 dr.updateState(state)
120 return next, nil
121 }
122 }
123 }
124
125 der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.ck)
126 if err != nil {
127 return 0, err
128 }
129 state := &certState{
130 key: dr.key,
131 cert: der,
132 leaf: leaf,
133 }
134 tlscert, err := state.tlscert()
135 if err != nil {
136 return 0, err
137 }
138 if err := dr.m.cachePut(ctx, dr.ck, tlscert); err != nil {
139 return 0, err
140 }
141 dr.updateState(state)
142 return dr.next(leaf.NotAfter), nil
143}
144
145func (dr *domainRenewal) next(expiry time.Time) time.Duration {
146 d := expiry.Sub(dr.m.now()) - dr.m.renewBefore()
147 // add a bit of randomness to renew deadline
148 n := pseudoRand.int63n(int64(renewJitter))
149 d -= time.Duration(n)
150 if d < 0 {
151 return 0
152 }
153 return d
154}
155
156var testDidRenewLoop = func(next time.Duration, err error) {}
diff --git a/crypto/acme/autocert/fork/renewal_test.go b/crypto/acme/autocert/fork/renewal_test.go
new file mode 100644
index 0000000..25689ba
--- /dev/null
+++ b/crypto/acme/autocert/fork/renewal_test.go
@@ -0,0 +1,269 @@
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
5package fork
6
7import (
8 "context"
9 "crypto"
10 "crypto/ecdsa"
11 "testing"
12 "time"
13
14 "code.crute.us/mcrute/golib/crypto/acme/autocert/fork/internal/acmetest"
15 "golang.org/x/crypto/acme"
16)
17
18func TestRenewalNext(t *testing.T) {
19 now := time.Now()
20 man := &Manager{
21 RenewBefore: 7 * 24 * time.Hour,
22 nowFunc: func() time.Time { return now },
23 }
24 defer man.stopRenew()
25 tt := []struct {
26 expiry time.Time
27 min, max time.Duration
28 }{
29 {now.Add(90 * 24 * time.Hour), 83*24*time.Hour - renewJitter, 83 * 24 * time.Hour},
30 {now.Add(time.Hour), 0, 1},
31 {now, 0, 1},
32 {now.Add(-time.Hour), 0, 1},
33 }
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
44func TestRenewFromCache(t *testing.T) {
45 man := testManager(t)
46 man.RenewBefore = 24 * time.Hour
47
48 ca := acmetest.NewCAServer(t).Start()
49 ca.ResolveGetCertificate(exampleDomain, man.GetCertificate)
50
51 man.Client = &acme.Client{
52 DirectoryURL: ca.URL(),
53 }
54
55 // cache an almost expired cert
56 now := time.Now()
57 c := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
58 if err := man.cachePut(context.Background(), exampleCertKey, c); err != nil {
59 t.Fatal(err)
60 }
61
62 // verify the renewal happened
63 defer func() {
64 // Stop the timers that read and execute testDidRenewLoop before restoring it.
65 // Otherwise the timer callback may race with the deferred write.
66 man.stopRenew()
67 testDidRenewLoop = func(next time.Duration, err error) {}
68 }()
69 renewed := make(chan bool, 1)
70 testDidRenewLoop = func(next time.Duration, err error) {
71 defer func() {
72 select {
73 case renewed <- true:
74 default:
75 // The renewal timer uses a random backoff. If the first renewal fails for
76 // some reason, we could end up with multiple calls here before the test
77 // stops the timer.
78 }
79 }()
80
81 if err != nil {
82 t.Errorf("testDidRenewLoop: %v", err)
83 }
84 // Next should be about 90 days:
85 // CaServer creates 90days expiry + account for man.RenewBefore.
86 // Previous expiration was within 1 min.
87 future := 88 * 24 * time.Hour
88 if next < future {
89 t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
90 }
91
92 // ensure the new cert is cached
93 after := time.Now().Add(future)
94 tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
95 if err != nil {
96 t.Errorf("man.cacheGet: %v", err)
97 return
98 }
99 if !tlscert.Leaf.NotAfter.After(after) {
100 t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
101 }
102
103 // verify the old cert is also replaced in memory
104 man.stateMu.Lock()
105 defer man.stateMu.Unlock()
106 s := man.state[exampleCertKey]
107 if s == nil {
108 t.Errorf("m.state[%q] is nil", exampleCertKey)
109 return
110 }
111 tlscert, err = s.tlscert()
112 if err != nil {
113 t.Errorf("s.tlscert: %v", err)
114 return
115 }
116 if !tlscert.Leaf.NotAfter.After(after) {
117 t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
118 }
119 }
120
121 // trigger renew
122 hello := clientHelloInfo(exampleDomain, algECDSA)
123 if _, err := man.GetCertificate(hello); err != nil {
124 t.Fatal(err)
125 }
126 <-renewed
127}
128
129func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
130 ca := acmetest.NewCAServer(t).Start()
131 man := testManager(t)
132 man.RenewBefore = 24 * time.Hour
133 man.Client = &acme.Client{
134 DirectoryURL: "invalid",
135 }
136
137 // cache a recently renewed cert with a different private key
138 now := time.Now()
139 newCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Hour*24*90))
140 if err := man.cachePut(context.Background(), exampleCertKey, newCert); err != nil {
141 t.Fatal(err)
142 }
143 newLeaf, err := validCert(exampleCertKey, newCert.Certificate, newCert.PrivateKey.(crypto.Signer), now)
144 if err != nil {
145 t.Fatal(err)
146 }
147
148 // set internal state to an almost expired cert
149 oldCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
150 if err != nil {
151 t.Fatal(err)
152 }
153 oldLeaf, err := validCert(exampleCertKey, oldCert.Certificate, oldCert.PrivateKey.(crypto.Signer), now)
154 if err != nil {
155 t.Fatal(err)
156 }
157 man.stateMu.Lock()
158 if man.state == nil {
159 man.state = make(map[certKey]*certState)
160 }
161 s := &certState{
162 key: oldCert.PrivateKey.(crypto.Signer),
163 cert: oldCert.Certificate,
164 leaf: oldLeaf,
165 }
166 man.state[exampleCertKey] = s
167 man.stateMu.Unlock()
168
169 // verify the renewal accepted the newer cached cert
170 defer func() {
171 // Stop the timers that read and execute testDidRenewLoop before restoring it.
172 // Otherwise the timer callback may race with the deferred write.
173 man.stopRenew()
174 testDidRenewLoop = func(next time.Duration, err error) {}
175 }()
176 renewed := make(chan bool, 1)
177 testDidRenewLoop = func(next time.Duration, err error) {
178 defer func() {
179 select {
180 case renewed <- true:
181 default:
182 // The renewal timer uses a random backoff. If the first renewal fails for
183 // some reason, we could end up with multiple calls here before the test
184 // stops the timer.
185 }
186 }()
187
188 if err != nil {
189 t.Errorf("testDidRenewLoop: %v", err)
190 }
191 // Next should be about 90 days
192 // Previous expiration was within 1 min.
193 future := 88 * 24 * time.Hour
194 if next < future {
195 t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
196 }
197
198 // ensure the cached cert was not modified
199 tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
200 if err != nil {
201 t.Errorf("man.cacheGet: %v", err)
202 return
203 }
204 if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
205 t.Errorf("cache leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
206 }
207
208 // verify the old cert is also replaced in memory
209 man.stateMu.Lock()
210 defer man.stateMu.Unlock()
211 s := man.state[exampleCertKey]
212 if s == nil {
213 t.Errorf("m.state[%q] is nil", exampleCertKey)
214 return
215 }
216 stateKey := s.key.Public().(*ecdsa.PublicKey)
217 if !stateKey.Equal(newLeaf.PublicKey) {
218 t.Error("state key was not updated from cache")
219 return
220 }
221 tlscert, err = s.tlscert()
222 if err != nil {
223 t.Errorf("s.tlscert: %v", err)
224 return
225 }
226 if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
227 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
228 }
229 }
230
231 // assert the expiring cert is returned from state
232 hello := clientHelloInfo(exampleDomain, algECDSA)
233 tlscert, err := man.GetCertificate(hello)
234 if err != nil {
235 t.Fatal(err)
236 }
237 if !oldLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
238 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, oldLeaf.NotAfter)
239 }
240
241 // trigger renew
242 man.startRenew(exampleCertKey, s)
243 <-renewed
244 func() {
245 man.renewalMu.Lock()
246 defer man.renewalMu.Unlock()
247
248 // verify the private key is replaced in the renewal state
249 r := man.renewal[exampleCertKey]
250 if r == nil {
251 t.Errorf("m.renewal[%q] is nil", exampleCertKey)
252 return
253 }
254 renewalKey := r.key.Public().(*ecdsa.PublicKey)
255 if !renewalKey.Equal(newLeaf.PublicKey) {
256 t.Error("renewal private key was not updated from cache")
257 }
258 }()
259
260 // assert the new cert is returned from state after renew
261 hello = clientHelloInfo(exampleDomain, algECDSA)
262 tlscert, err = man.GetCertificate(hello)
263 if err != nil {
264 t.Fatal(err)
265 }
266 if !newLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
267 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
268 }
269}
diff --git a/crypto/ocsp/client.go b/crypto/ocsp/client.go
new file mode 100644
index 0000000..4f5ff97
--- /dev/null
+++ b/crypto/ocsp/client.go
@@ -0,0 +1,125 @@
1package ocsp
2
3import (
4 "bytes"
5 "context"
6 "crypto/tls"
7 "crypto/x509"
8 "fmt"
9 "io"
10 "net/http"
11
12 "golang.org/x/crypto/ocsp"
13)
14
15const (
16 ocspBodySizeLimit = 1024 * 1024
17 ocspMimeType = "application/ocsp-request"
18)
19
20type Client struct {
21 // HTTPClient optionally specifies an HTTP client to use
22 // instead of http.DefaultClient.
23 HTTPClient *http.Client
24}
25
26func (c *Client) httpClient() *http.Client {
27 if c.HTTPClient != nil {
28 return c.HTTPClient
29 }
30 return http.DefaultClient
31}
32
33func (c *Client) Fetch(ctx context.Context, chain *tls.Certificate) ([]byte, *ocsp.Response, error) {
34 var certs []*x509.Certificate
35 for _, c := range chain.Certificate {
36 cert, err := x509.ParseCertificate(c)
37 if err != nil {
38 return nil, nil, fmt.Errorf("ocsp/client: error parsing certificate chain: %w", err)
39 }
40 certs = append(certs, cert)
41 }
42
43 if len(certs) == 0 {
44 return nil, nil, fmt.Errorf("ocsp/client: no certificates found in bundle")
45 }
46
47 // We expect the certificate slice to be ordered downwards the chain.
48 // SRV CRT -> CA. We need to pull the leaf and issuer certs out of it,
49 // which should always be the first two certificates. If there's no
50 // OCSP server listed in the leaf cert, there's nothing to do. And if
51 // we have only one certificate so far, we need to get the issuer cert.
52 leaf := certs[0]
53 if len(leaf.OCSPServer) == 0 {
54 return nil, nil, fmt.Errorf("ocsp/client: no ocsp server specified in certificate")
55 }
56
57 if len(certs) == 1 {
58 if len(leaf.IssuingCertificateURL) == 0 {
59 return nil, nil, fmt.Errorf("ocsp/client: no URL to issuing certificate")
60 }
61
62 req, err := http.NewRequestWithContext(ctx, http.MethodGet, leaf.IssuingCertificateURL[0], nil)
63 if err != nil {
64 return nil, nil, fmt.Errorf("ocsp/client: building certificate request: %w", err)
65 }
66
67 resp, err := c.httpClient().Do(req)
68 if err != nil {
69 return nil, nil, fmt.Errorf("ocsp/client: getting issuer certificate: %w", err)
70 }
71 defer resp.Body.Close()
72
73 issuerBytes, err := io.ReadAll(io.LimitReader(resp.Body, ocspBodySizeLimit))
74 if err != nil {
75 return nil, nil, fmt.Errorf("ocsp/client: reading issuer certificate: %w", err)
76 }
77
78 issuer, err := x509.ParseCertificate(issuerBytes)
79 if err != nil {
80 return nil, nil, fmt.Errorf("ocsp/client: parsing issuer certificate: %w", err)
81 }
82
83 certs = append(certs, issuer)
84 }
85
86 issuer := certs[1]
87
88 req, err := ocsp.CreateRequest(leaf, issuer, nil)
89 if err != nil {
90 return nil, nil, fmt.Errorf("ocsp/client: creating ocsp request: %w", err)
91 }
92
93 // httpRes, err := http.Post(, ocspMimeType, )
94 httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, leaf.OCSPServer[0], bytes.NewReader(req))
95 if err != nil {
96 return nil, nil, fmt.Errorf("ocsp/client: building ocsp http request: %w", err)
97 }
98
99 httpRes, err := c.httpClient().Do(httpReq)
100 if err != nil {
101 return nil, nil, fmt.Errorf("ocsp/client: making ocsp request: %w", err)
102 }
103 defer httpRes.Body.Close()
104
105 rawRes, err := io.ReadAll(io.LimitReader(httpRes.Body, ocspBodySizeLimit))
106 if err != nil {
107 return nil, nil, fmt.Errorf("ocsp/client: reading ocsp response: %w", err)
108 }
109
110 res, err := ocsp.ParseResponse(rawRes, issuer)
111 if err != nil {
112 return nil, nil, fmt.Errorf("ocsp/client: parsing ocsp response: %w", err)
113 }
114
115 if res.Status != ocsp.Good {
116 return nil, nil, fmt.Errorf("ocsp/client: invalid: ocsp response was not of Good status")
117 }
118
119 // This is invalid, the response expires after the certificate
120 if res.NextUpdate.After(leaf.NotAfter) {
121 return nil, nil, fmt.Errorf("ocsp/client: invalid: ocsp response valid after certificate expiration")
122 }
123
124 return rawRes, res, nil
125}