Moved to google.golang.org/genproto/googleapis/api/annotations
Fixes #52
This commit is contained in:
		
							
								
								
									
										793
									
								
								vendor/golang.org/x/crypto/acme/autocert/autocert.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										793
									
								
								vendor/golang.org/x/crypto/acme/autocert/autocert.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,793 @@
 | 
			
		||||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Package autocert provides automatic access to certificates from Let's Encrypt
 | 
			
		||||
// and any other ACME-based CA.
 | 
			
		||||
//
 | 
			
		||||
// This package is a work in progress and makes no API stability promises.
 | 
			
		||||
package autocert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto"
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"crypto/x509/pkix"
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	mathrand "math/rand"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/acme"
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// pseudoRand is safe for concurrent use.
 | 
			
		||||
var pseudoRand *lockedMathRand
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	src := mathrand.NewSource(timeNow().UnixNano())
 | 
			
		||||
	pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AcceptTOS always returns true to indicate the acceptance of a CA Terms of Service
 | 
			
		||||
// during account registration.
 | 
			
		||||
func AcceptTOS(tosURL string) bool { return true }
 | 
			
		||||
 | 
			
		||||
// HostPolicy specifies which host names the Manager is allowed to respond to.
 | 
			
		||||
// It returns a non-nil error if the host should be rejected.
 | 
			
		||||
// The returned error is accessible via tls.Conn.Handshake and its callers.
 | 
			
		||||
// See Manager's HostPolicy field and GetCertificate method docs for more details.
 | 
			
		||||
type HostPolicy func(ctx context.Context, host string) error
 | 
			
		||||
 | 
			
		||||
// HostWhitelist returns a policy where only the specified host names are allowed.
 | 
			
		||||
// Only exact matches are currently supported. Subdomains, regexp or wildcard
 | 
			
		||||
// will not match.
 | 
			
		||||
func HostWhitelist(hosts ...string) HostPolicy {
 | 
			
		||||
	whitelist := make(map[string]bool, len(hosts))
 | 
			
		||||
	for _, h := range hosts {
 | 
			
		||||
		whitelist[h] = true
 | 
			
		||||
	}
 | 
			
		||||
	return func(_ context.Context, host string) error {
 | 
			
		||||
		if !whitelist[host] {
 | 
			
		||||
			return errors.New("acme/autocert: host not configured")
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// defaultHostPolicy is used when Manager.HostPolicy is not set.
 | 
			
		||||
func defaultHostPolicy(context.Context, string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Manager is a stateful certificate manager built on top of acme.Client.
 | 
			
		||||
// It obtains and refreshes certificates automatically,
 | 
			
		||||
// as well as providing them to a TLS server via tls.Config.
 | 
			
		||||
//
 | 
			
		||||
// A simple usage example:
 | 
			
		||||
//
 | 
			
		||||
//	m := autocert.Manager{
 | 
			
		||||
//		Prompt: autocert.AcceptTOS,
 | 
			
		||||
//		HostPolicy: autocert.HostWhitelist("example.org"),
 | 
			
		||||
//	}
 | 
			
		||||
//	s := &http.Server{
 | 
			
		||||
//		Addr: ":https",
 | 
			
		||||
//		TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
 | 
			
		||||
//	}
 | 
			
		||||
//	s.ListenAndServeTLS("", "")
 | 
			
		||||
//
 | 
			
		||||
// To preserve issued certificates and improve overall performance,
 | 
			
		||||
// use a cache implementation of Cache. For instance, DirCache.
 | 
			
		||||
type Manager struct {
 | 
			
		||||
	// Prompt specifies a callback function to conditionally accept a CA's Terms of Service (TOS).
 | 
			
		||||
	// The registration may require the caller to agree to the CA's TOS.
 | 
			
		||||
	// If so, Manager calls Prompt with a TOS URL provided by the CA. Prompt should report
 | 
			
		||||
	// whether the caller agrees to the terms.
 | 
			
		||||
	//
 | 
			
		||||
	// To always accept the terms, the callers can use AcceptTOS.
 | 
			
		||||
	Prompt func(tosURL string) bool
 | 
			
		||||
 | 
			
		||||
	// Cache optionally stores and retrieves previously-obtained certificates.
 | 
			
		||||
	// If nil, certs will only be cached for the lifetime of the Manager.
 | 
			
		||||
	//
 | 
			
		||||
	// Manager passes the Cache certificates data encoded in PEM, with private/public
 | 
			
		||||
	// parts combined in a single Cache.Put call, private key first.
 | 
			
		||||
	Cache Cache
 | 
			
		||||
 | 
			
		||||
	// HostPolicy controls which domains the Manager will attempt
 | 
			
		||||
	// to retrieve new certificates for. It does not affect cached certs.
 | 
			
		||||
	//
 | 
			
		||||
	// If non-nil, HostPolicy is called before requesting a new cert.
 | 
			
		||||
	// If nil, all hosts are currently allowed. This is not recommended,
 | 
			
		||||
	// as it opens a potential attack where clients connect to a server
 | 
			
		||||
	// by IP address and pretend to be asking for an incorrect host name.
 | 
			
		||||
	// Manager will attempt to obtain a certificate for that host, incorrectly,
 | 
			
		||||
	// eventually reaching the CA's rate limit for certificate requests
 | 
			
		||||
	// and making it impossible to obtain actual certificates.
 | 
			
		||||
	//
 | 
			
		||||
	// See GetCertificate for more details.
 | 
			
		||||
	HostPolicy HostPolicy
 | 
			
		||||
 | 
			
		||||
	// RenewBefore optionally specifies how early certificates should
 | 
			
		||||
	// be renewed before they expire.
 | 
			
		||||
	//
 | 
			
		||||
	// If zero, they're renewed 1 week before expiration.
 | 
			
		||||
	RenewBefore time.Duration
 | 
			
		||||
 | 
			
		||||
	// Client is used to perform low-level operations, such as account registration
 | 
			
		||||
	// and requesting new certificates.
 | 
			
		||||
	// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
 | 
			
		||||
	// directory endpoint and a newly-generated ECDSA P-256 key.
 | 
			
		||||
	//
 | 
			
		||||
	// Mutating the field after the first call of GetCertificate method will have no effect.
 | 
			
		||||
	Client *acme.Client
 | 
			
		||||
 | 
			
		||||
	// Email optionally specifies a contact email address.
 | 
			
		||||
	// This is used by CAs, such as Let's Encrypt, to notify about problems
 | 
			
		||||
	// with issued certificates.
 | 
			
		||||
	//
 | 
			
		||||
	// If the Client's account key is already registered, Email is not used.
 | 
			
		||||
	Email string
 | 
			
		||||
 | 
			
		||||
	// ForceRSA makes the Manager generate certificates with 2048-bit RSA keys.
 | 
			
		||||
	//
 | 
			
		||||
	// If false, a default is used. Currently the default
 | 
			
		||||
	// is EC-based keys using the P-256 curve.
 | 
			
		||||
	ForceRSA bool
 | 
			
		||||
 | 
			
		||||
	clientMu sync.Mutex
 | 
			
		||||
	client   *acme.Client // initialized by acmeClient method
 | 
			
		||||
 | 
			
		||||
	stateMu sync.Mutex
 | 
			
		||||
	state   map[string]*certState // keyed by domain name
 | 
			
		||||
 | 
			
		||||
	// tokenCert is keyed by token domain name, which matches server name
 | 
			
		||||
	// of ClientHello. Keys always have ".acme.invalid" suffix.
 | 
			
		||||
	tokenCertMu sync.RWMutex
 | 
			
		||||
	tokenCert   map[string]*tls.Certificate
 | 
			
		||||
 | 
			
		||||
	// renewal tracks the set of domains currently running renewal timers.
 | 
			
		||||
	// It is keyed by domain name.
 | 
			
		||||
	renewalMu sync.Mutex
 | 
			
		||||
	renewal   map[string]*domainRenewal
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetCertificate implements the tls.Config.GetCertificate hook.
 | 
			
		||||
// It provides a TLS certificate for hello.ServerName host, including answering
 | 
			
		||||
// *.acme.invalid (TLS-SNI) challenges. All other fields of hello are ignored.
 | 
			
		||||
//
 | 
			
		||||
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
 | 
			
		||||
// a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation.
 | 
			
		||||
// The error is propagated back to the caller of GetCertificate and is user-visible.
 | 
			
		||||
// This does not affect cached certs. See HostPolicy field description for more details.
 | 
			
		||||
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
 | 
			
		||||
	name := hello.ServerName
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return nil, errors.New("acme/autocert: missing server name")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// check whether this is a token cert requested for TLS-SNI challenge
 | 
			
		||||
	if strings.HasSuffix(name, ".acme.invalid") {
 | 
			
		||||
		m.tokenCertMu.RLock()
 | 
			
		||||
		defer m.tokenCertMu.RUnlock()
 | 
			
		||||
		if cert := m.tokenCert[name]; cert != nil {
 | 
			
		||||
			return cert, nil
 | 
			
		||||
		}
 | 
			
		||||
		if cert, err := m.cacheGet(name); err == nil {
 | 
			
		||||
			return cert, nil
 | 
			
		||||
		}
 | 
			
		||||
		// TODO: cache error results?
 | 
			
		||||
		return nil, fmt.Errorf("acme/autocert: no token cert for %q", name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// regular domain
 | 
			
		||||
	name = strings.TrimSuffix(name, ".") // golang.org/issue/18114
 | 
			
		||||
	cert, err := m.cert(name)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return cert, nil
 | 
			
		||||
	}
 | 
			
		||||
	if err != ErrCacheMiss {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// first-time
 | 
			
		||||
	ctx := context.Background() // TODO: use a deadline?
 | 
			
		||||
	if err := m.hostPolicy()(ctx, name); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	cert, err = m.createCert(ctx, name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	m.cachePut(name, cert)
 | 
			
		||||
	return cert, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// cert returns an existing certificate either from m.state or cache.
 | 
			
		||||
// If a certificate is found in cache but not in m.state, the latter will be filled
 | 
			
		||||
// with the cached value.
 | 
			
		||||
func (m *Manager) cert(name string) (*tls.Certificate, error) {
 | 
			
		||||
	m.stateMu.Lock()
 | 
			
		||||
	if s, ok := m.state[name]; ok {
 | 
			
		||||
		m.stateMu.Unlock()
 | 
			
		||||
		s.RLock()
 | 
			
		||||
		defer s.RUnlock()
 | 
			
		||||
		return s.tlscert()
 | 
			
		||||
	}
 | 
			
		||||
	defer m.stateMu.Unlock()
 | 
			
		||||
	cert, err := m.cacheGet(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	signer, ok := cert.PrivateKey.(crypto.Signer)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("acme/autocert: private key cannot sign")
 | 
			
		||||
	}
 | 
			
		||||
	if m.state == nil {
 | 
			
		||||
		m.state = make(map[string]*certState)
 | 
			
		||||
	}
 | 
			
		||||
	s := &certState{
 | 
			
		||||
		key:  signer,
 | 
			
		||||
		cert: cert.Certificate,
 | 
			
		||||
		leaf: cert.Leaf,
 | 
			
		||||
	}
 | 
			
		||||
	m.state[name] = s
 | 
			
		||||
	go m.renew(name, s.key, s.leaf.NotAfter)
 | 
			
		||||
	return cert, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// cacheGet always returns a valid certificate, or an error otherwise.
 | 
			
		||||
func (m *Manager) cacheGet(domain string) (*tls.Certificate, error) {
 | 
			
		||||
	if m.Cache == nil {
 | 
			
		||||
		return nil, ErrCacheMiss
 | 
			
		||||
	}
 | 
			
		||||
	// TODO: might want to define a cache timeout on m
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	data, err := m.Cache.Get(ctx, domain)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// private
 | 
			
		||||
	priv, pub := pem.Decode(data)
 | 
			
		||||
	if priv == nil || !strings.Contains(priv.Type, "PRIVATE") {
 | 
			
		||||
		return nil, errors.New("acme/autocert: no private key found in cache")
 | 
			
		||||
	}
 | 
			
		||||
	privKey, err := parsePrivateKey(priv.Bytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// public
 | 
			
		||||
	var pubDER [][]byte
 | 
			
		||||
	for len(pub) > 0 {
 | 
			
		||||
		var b *pem.Block
 | 
			
		||||
		b, pub = pem.Decode(pub)
 | 
			
		||||
		if b == nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		pubDER = append(pubDER, b.Bytes)
 | 
			
		||||
	}
 | 
			
		||||
	if len(pub) > 0 {
 | 
			
		||||
		return nil, errors.New("acme/autocert: invalid public key")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// verify and create TLS cert
 | 
			
		||||
	leaf, err := validCert(domain, pubDER, privKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	tlscert := &tls.Certificate{
 | 
			
		||||
		Certificate: pubDER,
 | 
			
		||||
		PrivateKey:  privKey,
 | 
			
		||||
		Leaf:        leaf,
 | 
			
		||||
	}
 | 
			
		||||
	return tlscert, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Manager) cachePut(domain string, tlscert *tls.Certificate) error {
 | 
			
		||||
	if m.Cache == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// contains PEM-encoded data
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
 | 
			
		||||
	// private
 | 
			
		||||
	switch key := tlscert.PrivateKey.(type) {
 | 
			
		||||
	case *ecdsa.PrivateKey:
 | 
			
		||||
		if err := encodeECDSAKey(&buf, key); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	case *rsa.PrivateKey:
 | 
			
		||||
		b := x509.MarshalPKCS1PrivateKey(key)
 | 
			
		||||
		pb := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: b}
 | 
			
		||||
		if err := pem.Encode(&buf, pb); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return errors.New("acme/autocert: unknown private key type")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// public
 | 
			
		||||
	for _, b := range tlscert.Certificate {
 | 
			
		||||
		pb := &pem.Block{Type: "CERTIFICATE", Bytes: b}
 | 
			
		||||
		if err := pem.Encode(&buf, pb); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: might want to define a cache timeout on m
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	return m.Cache.Put(ctx, domain, buf.Bytes())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
 | 
			
		||||
	b, err := x509.MarshalECPrivateKey(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	pb := &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
 | 
			
		||||
	return pem.Encode(w, pb)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// createCert starts the domain ownership verification and returns a certificate
 | 
			
		||||
// for that domain upon success.
 | 
			
		||||
//
 | 
			
		||||
// If the domain is already being verified, it waits for the existing verification to complete.
 | 
			
		||||
// Either way, createCert blocks for the duration of the whole process.
 | 
			
		||||
func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certificate, error) {
 | 
			
		||||
	// TODO: maybe rewrite this whole piece using sync.Once
 | 
			
		||||
	state, err := m.certState(domain)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	// state may exist if another goroutine is already working on it
 | 
			
		||||
	// in which case just wait for it to finish
 | 
			
		||||
	if !state.locked {
 | 
			
		||||
		state.RLock()
 | 
			
		||||
		defer state.RUnlock()
 | 
			
		||||
		return state.tlscert()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We are the first; state is locked.
 | 
			
		||||
	// Unblock the readers when domain ownership is verified
 | 
			
		||||
	// and the we got the cert or the process failed.
 | 
			
		||||
	defer state.Unlock()
 | 
			
		||||
	state.locked = false
 | 
			
		||||
 | 
			
		||||
	der, leaf, err := m.authorizedCert(ctx, state.key, domain)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	state.cert = der
 | 
			
		||||
	state.leaf = leaf
 | 
			
		||||
	go m.renew(domain, state.key, state.leaf.NotAfter)
 | 
			
		||||
	return state.tlscert()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// certState returns a new or existing certState.
 | 
			
		||||
// If a new certState is returned, state.exist is false and the state is locked.
 | 
			
		||||
// The returned error is non-nil only in the case where a new state could not be created.
 | 
			
		||||
func (m *Manager) certState(domain string) (*certState, error) {
 | 
			
		||||
	m.stateMu.Lock()
 | 
			
		||||
	defer m.stateMu.Unlock()
 | 
			
		||||
	if m.state == nil {
 | 
			
		||||
		m.state = make(map[string]*certState)
 | 
			
		||||
	}
 | 
			
		||||
	// existing state
 | 
			
		||||
	if state, ok := m.state[domain]; ok {
 | 
			
		||||
		return state, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// new locked state
 | 
			
		||||
	var (
 | 
			
		||||
		err error
 | 
			
		||||
		key crypto.Signer
 | 
			
		||||
	)
 | 
			
		||||
	if m.ForceRSA {
 | 
			
		||||
		key, err = rsa.GenerateKey(rand.Reader, 2048)
 | 
			
		||||
	} else {
 | 
			
		||||
		key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state := &certState{
 | 
			
		||||
		key:    key,
 | 
			
		||||
		locked: true,
 | 
			
		||||
	}
 | 
			
		||||
	state.Lock() // will be unlocked by m.certState caller
 | 
			
		||||
	m.state[domain] = state
 | 
			
		||||
	return state, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// authorizedCert starts domain ownership verification process and requests a new cert upon success.
 | 
			
		||||
// The key argument is the certificate private key.
 | 
			
		||||
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) {
 | 
			
		||||
	// TODO: make m.verify retry or retry m.verify calls here
 | 
			
		||||
	if err := m.verify(ctx, domain); err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	client, err := m.acmeClient(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	csr, err := certRequest(key, domain)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	der, _, err = client.CreateCert(ctx, csr, 0, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	leaf, err = validCert(domain, der, key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return der, leaf, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// verify starts a new identifier (domain) authorization flow.
 | 
			
		||||
// It prepares a challenge response and then blocks until the authorization
 | 
			
		||||
// is marked as "completed" by the CA (either succeeded or failed).
 | 
			
		||||
//
 | 
			
		||||
// verify returns nil iff the verification was successful.
 | 
			
		||||
func (m *Manager) verify(ctx context.Context, domain string) error {
 | 
			
		||||
	client, err := m.acmeClient(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// start domain authorization and get the challenge
 | 
			
		||||
	authz, err := client.Authorize(ctx, domain)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// maybe don't need to at all
 | 
			
		||||
	if authz.Status == acme.StatusValid {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// pick a challenge: prefer tls-sni-02 over tls-sni-01
 | 
			
		||||
	// TODO: consider authz.Combinations
 | 
			
		||||
	var chal *acme.Challenge
 | 
			
		||||
	for _, c := range authz.Challenges {
 | 
			
		||||
		if c.Type == "tls-sni-02" {
 | 
			
		||||
			chal = c
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if c.Type == "tls-sni-01" {
 | 
			
		||||
			chal = c
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if chal == nil {
 | 
			
		||||
		return errors.New("acme/autocert: no supported challenge type found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// create a token cert for the challenge response
 | 
			
		||||
	var (
 | 
			
		||||
		cert tls.Certificate
 | 
			
		||||
		name string
 | 
			
		||||
	)
 | 
			
		||||
	switch chal.Type {
 | 
			
		||||
	case "tls-sni-01":
 | 
			
		||||
		cert, name, err = client.TLSSNI01ChallengeCert(chal.Token)
 | 
			
		||||
	case "tls-sni-02":
 | 
			
		||||
		cert, name, err = client.TLSSNI02ChallengeCert(chal.Token)
 | 
			
		||||
	default:
 | 
			
		||||
		err = fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	m.putTokenCert(name, &cert)
 | 
			
		||||
	defer func() {
 | 
			
		||||
		// verification has ended at this point
 | 
			
		||||
		// don't need token cert anymore
 | 
			
		||||
		go m.deleteTokenCert(name)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// ready to fulfill the challenge
 | 
			
		||||
	if _, err := client.Accept(ctx, chal); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// wait for the CA to validate
 | 
			
		||||
	_, err = client.WaitAuthorization(ctx, authz.URI)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// putTokenCert stores the cert under the named key in both m.tokenCert map
 | 
			
		||||
// and m.Cache.
 | 
			
		||||
func (m *Manager) putTokenCert(name string, cert *tls.Certificate) {
 | 
			
		||||
	m.tokenCertMu.Lock()
 | 
			
		||||
	defer m.tokenCertMu.Unlock()
 | 
			
		||||
	if m.tokenCert == nil {
 | 
			
		||||
		m.tokenCert = make(map[string]*tls.Certificate)
 | 
			
		||||
	}
 | 
			
		||||
	m.tokenCert[name] = cert
 | 
			
		||||
	m.cachePut(name, cert)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// deleteTokenCert removes the token certificate for the specified domain name
 | 
			
		||||
// from both m.tokenCert map and m.Cache.
 | 
			
		||||
func (m *Manager) deleteTokenCert(name string) {
 | 
			
		||||
	m.tokenCertMu.Lock()
 | 
			
		||||
	defer m.tokenCertMu.Unlock()
 | 
			
		||||
	delete(m.tokenCert, name)
 | 
			
		||||
	if m.Cache != nil {
 | 
			
		||||
		m.Cache.Delete(context.Background(), name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// renew starts a cert renewal timer loop, one per domain.
 | 
			
		||||
//
 | 
			
		||||
// The loop is scheduled in two cases:
 | 
			
		||||
// - a cert was fetched from cache for the first time (wasn't in m.state)
 | 
			
		||||
// - a new cert was created by m.createCert
 | 
			
		||||
//
 | 
			
		||||
// The key argument is a certificate private key.
 | 
			
		||||
// The exp argument is the cert expiration time (NotAfter).
 | 
			
		||||
func (m *Manager) renew(domain string, key crypto.Signer, exp time.Time) {
 | 
			
		||||
	m.renewalMu.Lock()
 | 
			
		||||
	defer m.renewalMu.Unlock()
 | 
			
		||||
	if m.renewal[domain] != nil {
 | 
			
		||||
		// another goroutine is already on it
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if m.renewal == nil {
 | 
			
		||||
		m.renewal = make(map[string]*domainRenewal)
 | 
			
		||||
	}
 | 
			
		||||
	dr := &domainRenewal{m: m, domain: domain, key: key}
 | 
			
		||||
	m.renewal[domain] = dr
 | 
			
		||||
	dr.start(exp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// stopRenew stops all currently running cert renewal timers.
 | 
			
		||||
// The timers are not restarted during the lifetime of the Manager.
 | 
			
		||||
func (m *Manager) stopRenew() {
 | 
			
		||||
	m.renewalMu.Lock()
 | 
			
		||||
	defer m.renewalMu.Unlock()
 | 
			
		||||
	for name, dr := range m.renewal {
 | 
			
		||||
		delete(m.renewal, name)
 | 
			
		||||
		dr.stop()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
 | 
			
		||||
	const keyName = "acme_account.key"
 | 
			
		||||
 | 
			
		||||
	genKey := func() (*ecdsa.PrivateKey, error) {
 | 
			
		||||
		return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if m.Cache == nil {
 | 
			
		||||
		return genKey()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data, err := m.Cache.Get(ctx, keyName)
 | 
			
		||||
	if err == ErrCacheMiss {
 | 
			
		||||
		key, err := genKey()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		var buf bytes.Buffer
 | 
			
		||||
		if err := encodeECDSAKey(&buf, key); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if err := m.Cache.Put(ctx, keyName, buf.Bytes()); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		return key, nil
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	priv, _ := pem.Decode(data)
 | 
			
		||||
	if priv == nil || !strings.Contains(priv.Type, "PRIVATE") {
 | 
			
		||||
		return nil, errors.New("acme/autocert: invalid account key found in cache")
 | 
			
		||||
	}
 | 
			
		||||
	return parsePrivateKey(priv.Bytes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
 | 
			
		||||
	m.clientMu.Lock()
 | 
			
		||||
	defer m.clientMu.Unlock()
 | 
			
		||||
	if m.client != nil {
 | 
			
		||||
		return m.client, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client := m.Client
 | 
			
		||||
	if client == nil {
 | 
			
		||||
		client = &acme.Client{DirectoryURL: acme.LetsEncryptURL}
 | 
			
		||||
	}
 | 
			
		||||
	if client.Key == nil {
 | 
			
		||||
		var err error
 | 
			
		||||
		client.Key, err = m.accountKey(ctx)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	var contact []string
 | 
			
		||||
	if m.Email != "" {
 | 
			
		||||
		contact = []string{"mailto:" + m.Email}
 | 
			
		||||
	}
 | 
			
		||||
	a := &acme.Account{Contact: contact}
 | 
			
		||||
	_, err := client.Register(ctx, a, m.Prompt)
 | 
			
		||||
	if ae, ok := err.(*acme.Error); err == nil || ok && ae.StatusCode == http.StatusConflict {
 | 
			
		||||
		// conflict indicates the key is already registered
 | 
			
		||||
		m.client = client
 | 
			
		||||
		err = nil
 | 
			
		||||
	}
 | 
			
		||||
	return m.client, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Manager) hostPolicy() HostPolicy {
 | 
			
		||||
	if m.HostPolicy != nil {
 | 
			
		||||
		return m.HostPolicy
 | 
			
		||||
	}
 | 
			
		||||
	return defaultHostPolicy
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Manager) renewBefore() time.Duration {
 | 
			
		||||
	if m.RenewBefore > maxRandRenew {
 | 
			
		||||
		return m.RenewBefore
 | 
			
		||||
	}
 | 
			
		||||
	return 7 * 24 * time.Hour // 1 week
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// certState is ready when its mutex is unlocked for reading.
 | 
			
		||||
type certState struct {
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	locked bool              // locked for read/write
 | 
			
		||||
	key    crypto.Signer     // private key for cert
 | 
			
		||||
	cert   [][]byte          // DER encoding
 | 
			
		||||
	leaf   *x509.Certificate // parsed cert[0]; always non-nil if cert != nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// tlscert creates a tls.Certificate from s.key and s.cert.
 | 
			
		||||
// Callers should wrap it in s.RLock() and s.RUnlock().
 | 
			
		||||
func (s *certState) tlscert() (*tls.Certificate, error) {
 | 
			
		||||
	if s.key == nil {
 | 
			
		||||
		return nil, errors.New("acme/autocert: missing signer")
 | 
			
		||||
	}
 | 
			
		||||
	if len(s.cert) == 0 {
 | 
			
		||||
		return nil, errors.New("acme/autocert: missing certificate")
 | 
			
		||||
	}
 | 
			
		||||
	return &tls.Certificate{
 | 
			
		||||
		PrivateKey:  s.key,
 | 
			
		||||
		Certificate: s.cert,
 | 
			
		||||
		Leaf:        s.leaf,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// certRequest creates a certificate request for the given common name cn
 | 
			
		||||
// and optional SANs.
 | 
			
		||||
func certRequest(key crypto.Signer, cn string, san ...string) ([]byte, error) {
 | 
			
		||||
	req := &x509.CertificateRequest{
 | 
			
		||||
		Subject:  pkix.Name{CommonName: cn},
 | 
			
		||||
		DNSNames: san,
 | 
			
		||||
	}
 | 
			
		||||
	return x509.CreateCertificateRequest(rand.Reader, req, key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
 | 
			
		||||
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
 | 
			
		||||
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
 | 
			
		||||
//
 | 
			
		||||
// Inspired by parsePrivateKey in crypto/tls/tls.go.
 | 
			
		||||
func parsePrivateKey(der []byte) (crypto.Signer, error) {
 | 
			
		||||
	if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
 | 
			
		||||
		return key, nil
 | 
			
		||||
	}
 | 
			
		||||
	if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
 | 
			
		||||
		switch key := key.(type) {
 | 
			
		||||
		case *rsa.PrivateKey:
 | 
			
		||||
			return key, nil
 | 
			
		||||
		case *ecdsa.PrivateKey:
 | 
			
		||||
			return key, nil
 | 
			
		||||
		default:
 | 
			
		||||
			return nil, errors.New("acme/autocert: unknown private key type in PKCS#8 wrapping")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if key, err := x509.ParseECPrivateKey(der); err == nil {
 | 
			
		||||
		return key, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, errors.New("acme/autocert: failed to parse private key")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// validCert parses a cert chain provided as der argument and verifies the leaf, der[0],
 | 
			
		||||
// corresponds to the private key, as well as the domain match and expiration dates.
 | 
			
		||||
// It doesn't do any revocation checking.
 | 
			
		||||
//
 | 
			
		||||
// The returned value is the verified leaf cert.
 | 
			
		||||
func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certificate, err error) {
 | 
			
		||||
	// parse public part(s)
 | 
			
		||||
	var n int
 | 
			
		||||
	for _, b := range der {
 | 
			
		||||
		n += len(b)
 | 
			
		||||
	}
 | 
			
		||||
	pub := make([]byte, n)
 | 
			
		||||
	n = 0
 | 
			
		||||
	for _, b := range der {
 | 
			
		||||
		n += copy(pub[n:], b)
 | 
			
		||||
	}
 | 
			
		||||
	x509Cert, err := x509.ParseCertificates(pub)
 | 
			
		||||
	if len(x509Cert) == 0 {
 | 
			
		||||
		return nil, errors.New("acme/autocert: no public key found")
 | 
			
		||||
	}
 | 
			
		||||
	// verify the leaf is not expired and matches the domain name
 | 
			
		||||
	leaf = x509Cert[0]
 | 
			
		||||
	now := timeNow()
 | 
			
		||||
	if now.Before(leaf.NotBefore) {
 | 
			
		||||
		return nil, errors.New("acme/autocert: certificate is not valid yet")
 | 
			
		||||
	}
 | 
			
		||||
	if now.After(leaf.NotAfter) {
 | 
			
		||||
		return nil, errors.New("acme/autocert: expired certificate")
 | 
			
		||||
	}
 | 
			
		||||
	if err := leaf.VerifyHostname(domain); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	// ensure the leaf corresponds to the private key
 | 
			
		||||
	switch pub := leaf.PublicKey.(type) {
 | 
			
		||||
	case *rsa.PublicKey:
 | 
			
		||||
		prv, ok := key.(*rsa.PrivateKey)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return nil, errors.New("acme/autocert: private key type does not match public key type")
 | 
			
		||||
		}
 | 
			
		||||
		if pub.N.Cmp(prv.N) != 0 {
 | 
			
		||||
			return nil, errors.New("acme/autocert: private key does not match public key")
 | 
			
		||||
		}
 | 
			
		||||
	case *ecdsa.PublicKey:
 | 
			
		||||
		prv, ok := key.(*ecdsa.PrivateKey)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return nil, errors.New("acme/autocert: private key type does not match public key type")
 | 
			
		||||
		}
 | 
			
		||||
		if pub.X.Cmp(prv.X) != 0 || pub.Y.Cmp(prv.Y) != 0 {
 | 
			
		||||
			return nil, errors.New("acme/autocert: private key does not match public key")
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, errors.New("acme/autocert: unknown public key algorithm")
 | 
			
		||||
	}
 | 
			
		||||
	return leaf, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func retryAfter(v string) time.Duration {
 | 
			
		||||
	if i, err := strconv.Atoi(v); err == nil {
 | 
			
		||||
		return time.Duration(i) * time.Second
 | 
			
		||||
	}
 | 
			
		||||
	if t, err := http.ParseTime(v); err == nil {
 | 
			
		||||
		return t.Sub(timeNow())
 | 
			
		||||
	}
 | 
			
		||||
	return time.Second
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type lockedMathRand struct {
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
	rnd *mathrand.Rand
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *lockedMathRand) int63n(max int64) int64 {
 | 
			
		||||
	r.Lock()
 | 
			
		||||
	n := r.rnd.Int63n(max)
 | 
			
		||||
	r.Unlock()
 | 
			
		||||
	return n
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// for easier testing
 | 
			
		||||
var timeNow = time.Now
 | 
			
		||||
							
								
								
									
										440
									
								
								vendor/golang.org/x/crypto/acme/autocert/autocert_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										440
									
								
								vendor/golang.org/x/crypto/acme/autocert/autocert_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,440 @@
 | 
			
		||||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package autocert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto"
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"crypto/x509/pkix"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"html/template"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/acme"
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var discoTmpl = template.Must(template.New("disco").Parse(`{
 | 
			
		||||
	"new-reg": "{{.}}/new-reg",
 | 
			
		||||
	"new-authz": "{{.}}/new-authz",
 | 
			
		||||
	"new-cert": "{{.}}/new-cert"
 | 
			
		||||
}`))
 | 
			
		||||
 | 
			
		||||
var authzTmpl = template.Must(template.New("authz").Parse(`{
 | 
			
		||||
	"status": "pending",
 | 
			
		||||
	"challenges": [
 | 
			
		||||
		{
 | 
			
		||||
			"uri": "{{.}}/challenge/1",
 | 
			
		||||
			"type": "tls-sni-01",
 | 
			
		||||
			"token": "token-01"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"uri": "{{.}}/challenge/2",
 | 
			
		||||
			"type": "tls-sni-02",
 | 
			
		||||
			"token": "token-02"
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}`))
 | 
			
		||||
 | 
			
		||||
type memCache struct {
 | 
			
		||||
	mu      sync.Mutex
 | 
			
		||||
	keyData map[string][]byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *memCache) Get(ctx context.Context, key string) ([]byte, error) {
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	v, ok := m.keyData[key]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, ErrCacheMiss
 | 
			
		||||
	}
 | 
			
		||||
	return v, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *memCache) Put(ctx context.Context, key string, data []byte) error {
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	m.keyData[key] = data
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *memCache) Delete(ctx context.Context, key string) error {
 | 
			
		||||
	m.mu.Lock()
 | 
			
		||||
	defer m.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	delete(m.keyData, key)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newMemCache() *memCache {
 | 
			
		||||
	return &memCache{
 | 
			
		||||
		keyData: make(map[string][]byte),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func dummyCert(pub interface{}, san ...string) ([]byte, error) {
 | 
			
		||||
	return dateDummyCert(pub, time.Now(), time.Now().Add(90*24*time.Hour), san...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func dateDummyCert(pub interface{}, start, end time.Time, san ...string) ([]byte, error) {
 | 
			
		||||
	// use EC key to run faster on 386
 | 
			
		||||
	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	t := &x509.Certificate{
 | 
			
		||||
		SerialNumber:          big.NewInt(1),
 | 
			
		||||
		NotBefore:             start,
 | 
			
		||||
		NotAfter:              end,
 | 
			
		||||
		BasicConstraintsValid: true,
 | 
			
		||||
		KeyUsage:              x509.KeyUsageKeyEncipherment,
 | 
			
		||||
		DNSNames:              san,
 | 
			
		||||
	}
 | 
			
		||||
	if pub == nil {
 | 
			
		||||
		pub = &key.PublicKey
 | 
			
		||||
	}
 | 
			
		||||
	return x509.CreateCertificate(rand.Reader, t, t, pub, key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func decodePayload(v interface{}, r io.Reader) error {
 | 
			
		||||
	var req struct{ Payload string }
 | 
			
		||||
	if err := json.NewDecoder(r).Decode(&req); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	payload, err := base64.RawURLEncoding.DecodeString(req.Payload)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return json.Unmarshal(payload, v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetCertificate(t *testing.T) {
 | 
			
		||||
	man := &Manager{Prompt: AcceptTOS}
 | 
			
		||||
	defer man.stopRenew()
 | 
			
		||||
	hello := &tls.ClientHelloInfo{ServerName: "example.org"}
 | 
			
		||||
	testGetCertificate(t, man, "example.org", hello)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetCertificate_trailingDot(t *testing.T) {
 | 
			
		||||
	man := &Manager{Prompt: AcceptTOS}
 | 
			
		||||
	defer man.stopRenew()
 | 
			
		||||
	hello := &tls.ClientHelloInfo{ServerName: "example.org."}
 | 
			
		||||
	testGetCertificate(t, man, "example.org", hello)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetCertificate_ForceRSA(t *testing.T) {
 | 
			
		||||
	man := &Manager{
 | 
			
		||||
		Prompt:   AcceptTOS,
 | 
			
		||||
		Cache:    newMemCache(),
 | 
			
		||||
		ForceRSA: true,
 | 
			
		||||
	}
 | 
			
		||||
	defer man.stopRenew()
 | 
			
		||||
	hello := &tls.ClientHelloInfo{ServerName: "example.org"}
 | 
			
		||||
	testGetCertificate(t, man, "example.org", hello)
 | 
			
		||||
 | 
			
		||||
	cert, err := man.cacheGet("example.org")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("man.cacheGet: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := cert.PrivateKey.(*rsa.PrivateKey); !ok {
 | 
			
		||||
		t.Errorf("cert.PrivateKey is %T; want *rsa.PrivateKey", cert.PrivateKey)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// tests man.GetCertificate flow using the provided hello argument.
 | 
			
		||||
// The domain argument is the expected domain name of a certificate request.
 | 
			
		||||
func testGetCertificate(t *testing.T, man *Manager, domain string, hello *tls.ClientHelloInfo) {
 | 
			
		||||
	// echo token-02 | shasum -a 256
 | 
			
		||||
	// then divide result in 2 parts separated by dot
 | 
			
		||||
	tokenCertName := "4e8eb87631187e9ff2153b56b13a4dec.13a35d002e485d60ff37354b32f665d9.token.acme.invalid"
 | 
			
		||||
	verifyTokenCert := func() {
 | 
			
		||||
		hello := &tls.ClientHelloInfo{ServerName: tokenCertName}
 | 
			
		||||
		_, err := man.GetCertificate(hello)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("verifyTokenCert: GetCertificate(%q): %v", tokenCertName, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// ACME CA server stub
 | 
			
		||||
	var ca *httptest.Server
 | 
			
		||||
	ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		w.Header().Set("replay-nonce", "nonce")
 | 
			
		||||
		if r.Method == "HEAD" {
 | 
			
		||||
			// a nonce request
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch r.URL.Path {
 | 
			
		||||
		// discovery
 | 
			
		||||
		case "/":
 | 
			
		||||
			if err := discoTmpl.Execute(w, ca.URL); err != nil {
 | 
			
		||||
				t.Fatalf("discoTmpl: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		// client key registration
 | 
			
		||||
		case "/new-reg":
 | 
			
		||||
			w.Write([]byte("{}"))
 | 
			
		||||
		// domain authorization
 | 
			
		||||
		case "/new-authz":
 | 
			
		||||
			w.Header().Set("location", ca.URL+"/authz/1")
 | 
			
		||||
			w.WriteHeader(http.StatusCreated)
 | 
			
		||||
			if err := authzTmpl.Execute(w, ca.URL); err != nil {
 | 
			
		||||
				t.Fatalf("authzTmpl: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		// accept tls-sni-02 challenge
 | 
			
		||||
		case "/challenge/2":
 | 
			
		||||
			verifyTokenCert()
 | 
			
		||||
			w.Write([]byte("{}"))
 | 
			
		||||
		// authorization status
 | 
			
		||||
		case "/authz/1":
 | 
			
		||||
			w.Write([]byte(`{"status": "valid"}`))
 | 
			
		||||
		// cert request
 | 
			
		||||
		case "/new-cert":
 | 
			
		||||
			var req struct {
 | 
			
		||||
				CSR string `json:"csr"`
 | 
			
		||||
			}
 | 
			
		||||
			decodePayload(&req, r.Body)
 | 
			
		||||
			b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
 | 
			
		||||
			csr, err := x509.ParseCertificateRequest(b)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatalf("new-cert: CSR: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			if csr.Subject.CommonName != domain {
 | 
			
		||||
				t.Errorf("CommonName in CSR = %q; want %q", csr.Subject.CommonName, domain)
 | 
			
		||||
			}
 | 
			
		||||
			der, err := dummyCert(csr.PublicKey, domain)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatalf("new-cert: dummyCert: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL)
 | 
			
		||||
			w.Header().Set("link", chainUp)
 | 
			
		||||
			w.WriteHeader(http.StatusCreated)
 | 
			
		||||
			w.Write(der)
 | 
			
		||||
		// CA chain cert
 | 
			
		||||
		case "/ca-cert":
 | 
			
		||||
			der, err := dummyCert(nil, "ca")
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatalf("ca-cert: dummyCert: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			w.Write(der)
 | 
			
		||||
		default:
 | 
			
		||||
			t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
 | 
			
		||||
		}
 | 
			
		||||
	}))
 | 
			
		||||
	defer ca.Close()
 | 
			
		||||
 | 
			
		||||
	// use EC key to run faster on 386
 | 
			
		||||
	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	man.Client = &acme.Client{
 | 
			
		||||
		Key:          key,
 | 
			
		||||
		DirectoryURL: ca.URL,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// simulate tls.Config.GetCertificate
 | 
			
		||||
	var tlscert *tls.Certificate
 | 
			
		||||
	done := make(chan struct{})
 | 
			
		||||
	go func() {
 | 
			
		||||
		tlscert, err = man.GetCertificate(hello)
 | 
			
		||||
		close(done)
 | 
			
		||||
	}()
 | 
			
		||||
	select {
 | 
			
		||||
	case <-time.After(time.Minute):
 | 
			
		||||
		t.Fatal("man.GetCertificate took too long to return")
 | 
			
		||||
	case <-done:
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("man.GetCertificate: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// verify the tlscert is the same we responded with from the CA stub
 | 
			
		||||
	if len(tlscert.Certificate) == 0 {
 | 
			
		||||
		t.Fatal("len(tlscert.Certificate) is 0")
 | 
			
		||||
	}
 | 
			
		||||
	cert, err := x509.ParseCertificate(tlscert.Certificate[0])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("x509.ParseCertificate: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(cert.DNSNames) == 0 || cert.DNSNames[0] != domain {
 | 
			
		||||
		t.Errorf("cert.DNSNames = %v; want %q", cert.DNSNames, domain)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// make sure token cert was removed
 | 
			
		||||
	done = make(chan struct{})
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			hello := &tls.ClientHelloInfo{ServerName: tokenCertName}
 | 
			
		||||
			if _, err := man.GetCertificate(hello); err != nil {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			time.Sleep(100 * time.Millisecond)
 | 
			
		||||
		}
 | 
			
		||||
		close(done)
 | 
			
		||||
	}()
 | 
			
		||||
	select {
 | 
			
		||||
	case <-time.After(5 * time.Second):
 | 
			
		||||
		t.Error("token cert was not removed")
 | 
			
		||||
	case <-done:
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestAccountKeyCache(t *testing.T) {
 | 
			
		||||
	m := Manager{Cache: newMemCache()}
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	k1, err := m.accountKey(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	k2, err := m.accountKey(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(k1, k2) {
 | 
			
		||||
		t.Errorf("account keys don't match: k1 = %#v; k2 = %#v", k1, k2)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCache(t *testing.T) {
 | 
			
		||||
	privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	tmpl := &x509.Certificate{
 | 
			
		||||
		SerialNumber: big.NewInt(1),
 | 
			
		||||
		Subject:      pkix.Name{CommonName: "example.org"},
 | 
			
		||||
		NotAfter:     time.Now().Add(time.Hour),
 | 
			
		||||
	}
 | 
			
		||||
	pub, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &privKey.PublicKey, privKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	tlscert := &tls.Certificate{
 | 
			
		||||
		Certificate: [][]byte{pub},
 | 
			
		||||
		PrivateKey:  privKey,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	man := &Manager{Cache: newMemCache()}
 | 
			
		||||
	defer man.stopRenew()
 | 
			
		||||
	if err := man.cachePut("example.org", tlscert); err != nil {
 | 
			
		||||
		t.Fatalf("man.cachePut: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	res, err := man.cacheGet("example.org")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("man.cacheGet: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if res == nil {
 | 
			
		||||
		t.Fatal("res is nil")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestHostWhitelist(t *testing.T) {
 | 
			
		||||
	policy := HostWhitelist("example.com", "example.org", "*.example.net")
 | 
			
		||||
	tt := []struct {
 | 
			
		||||
		host  string
 | 
			
		||||
		allow bool
 | 
			
		||||
	}{
 | 
			
		||||
		{"example.com", true},
 | 
			
		||||
		{"example.org", true},
 | 
			
		||||
		{"one.example.com", false},
 | 
			
		||||
		{"two.example.org", false},
 | 
			
		||||
		{"three.example.net", false},
 | 
			
		||||
		{"dummy", false},
 | 
			
		||||
	}
 | 
			
		||||
	for i, test := range tt {
 | 
			
		||||
		err := policy(nil, test.host)
 | 
			
		||||
		if err != nil && test.allow {
 | 
			
		||||
			t.Errorf("%d: policy(%q): %v; want nil", i, test.host, err)
 | 
			
		||||
		}
 | 
			
		||||
		if err == nil && !test.allow {
 | 
			
		||||
			t.Errorf("%d: policy(%q): nil; want an error", i, test.host)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValidCert(t *testing.T) {
 | 
			
		||||
	key1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	key2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	key3, err := rsa.GenerateKey(rand.Reader, 512)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	cert1, err := dummyCert(key1.Public(), "example.org")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	cert2, err := dummyCert(key2.Public(), "example.org")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	cert3, err := dummyCert(key3.Public(), "example.org")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	early, err := dateDummyCert(key1.Public(), now.Add(time.Hour), now.Add(2*time.Hour), "example.org")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	expired, err := dateDummyCert(key1.Public(), now.Add(-2*time.Hour), now.Add(-time.Hour), "example.org")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tt := []struct {
 | 
			
		||||
		domain string
 | 
			
		||||
		key    crypto.Signer
 | 
			
		||||
		cert   [][]byte
 | 
			
		||||
		ok     bool
 | 
			
		||||
	}{
 | 
			
		||||
		{"example.org", key1, [][]byte{cert1}, true},
 | 
			
		||||
		{"example.org", key3, [][]byte{cert3}, true},
 | 
			
		||||
		{"example.org", key1, [][]byte{cert1, cert2, cert3}, true},
 | 
			
		||||
		{"example.org", key1, [][]byte{cert1, {1}}, false},
 | 
			
		||||
		{"example.org", key1, [][]byte{{1}}, false},
 | 
			
		||||
		{"example.org", key1, [][]byte{cert2}, false},
 | 
			
		||||
		{"example.org", key2, [][]byte{cert1}, false},
 | 
			
		||||
		{"example.org", key1, [][]byte{cert3}, false},
 | 
			
		||||
		{"example.org", key3, [][]byte{cert1}, false},
 | 
			
		||||
		{"example.net", key1, [][]byte{cert1}, false},
 | 
			
		||||
		{"example.org", key1, [][]byte{early}, false},
 | 
			
		||||
		{"example.org", key1, [][]byte{expired}, false},
 | 
			
		||||
	}
 | 
			
		||||
	for i, test := range tt {
 | 
			
		||||
		leaf, err := validCert(test.domain, test.cert, test.key)
 | 
			
		||||
		if err != nil && test.ok {
 | 
			
		||||
			t.Errorf("%d: err = %v", i, err)
 | 
			
		||||
		}
 | 
			
		||||
		if err == nil && !test.ok {
 | 
			
		||||
			t.Errorf("%d: err is nil", i)
 | 
			
		||||
		}
 | 
			
		||||
		if err == nil && test.ok && leaf == nil {
 | 
			
		||||
			t.Errorf("%d: leaf is nil", i)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										130
									
								
								vendor/golang.org/x/crypto/acme/autocert/cache.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								vendor/golang.org/x/crypto/acme/autocert/cache.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
			
		||||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package autocert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ErrCacheMiss is returned when a certificate is not found in cache.
 | 
			
		||||
var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
 | 
			
		||||
 | 
			
		||||
// Cache is used by Manager to store and retrieve previously obtained certificates
 | 
			
		||||
// as opaque data.
 | 
			
		||||
//
 | 
			
		||||
// The key argument of the methods refers to a domain name but need not be an FQDN.
 | 
			
		||||
// Cache implementations should not rely on the key naming pattern.
 | 
			
		||||
type Cache interface {
 | 
			
		||||
	// Get returns a certificate data for the specified key.
 | 
			
		||||
	// If there's no such key, Get returns ErrCacheMiss.
 | 
			
		||||
	Get(ctx context.Context, key string) ([]byte, error)
 | 
			
		||||
 | 
			
		||||
	// Put stores the data in the cache under the specified key.
 | 
			
		||||
	// Underlying implementations may use any data storage format,
 | 
			
		||||
	// as long as the reverse operation, Get, results in the original data.
 | 
			
		||||
	Put(ctx context.Context, key string, data []byte) error
 | 
			
		||||
 | 
			
		||||
	// Delete removes a certificate data from the cache under the specified key.
 | 
			
		||||
	// If there's no such key in the cache, Delete returns nil.
 | 
			
		||||
	Delete(ctx context.Context, key string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DirCache implements Cache using a directory on the local filesystem.
 | 
			
		||||
// If the directory does not exist, it will be created with 0700 permissions.
 | 
			
		||||
type DirCache string
 | 
			
		||||
 | 
			
		||||
// Get reads a certificate data from the specified file name.
 | 
			
		||||
func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) {
 | 
			
		||||
	name = filepath.Join(string(d), name)
 | 
			
		||||
	var (
 | 
			
		||||
		data []byte
 | 
			
		||||
		err  error
 | 
			
		||||
		done = make(chan struct{})
 | 
			
		||||
	)
 | 
			
		||||
	go func() {
 | 
			
		||||
		data, err = ioutil.ReadFile(name)
 | 
			
		||||
		close(done)
 | 
			
		||||
	}()
 | 
			
		||||
	select {
 | 
			
		||||
	case <-ctx.Done():
 | 
			
		||||
		return nil, ctx.Err()
 | 
			
		||||
	case <-done:
 | 
			
		||||
	}
 | 
			
		||||
	if os.IsNotExist(err) {
 | 
			
		||||
		return nil, ErrCacheMiss
 | 
			
		||||
	}
 | 
			
		||||
	return data, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Put writes the certificate data to the specified file name.
 | 
			
		||||
// The file will be created with 0600 permissions.
 | 
			
		||||
func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
 | 
			
		||||
	if err := os.MkdirAll(string(d), 0700); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	done := make(chan struct{})
 | 
			
		||||
	var err error
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer close(done)
 | 
			
		||||
		var tmp string
 | 
			
		||||
		if tmp, err = d.writeTempFile(name, data); err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		// prevent overwriting the file if the context was cancelled
 | 
			
		||||
		if ctx.Err() != nil {
 | 
			
		||||
			return // no need to set err
 | 
			
		||||
		}
 | 
			
		||||
		name = filepath.Join(string(d), name)
 | 
			
		||||
		err = os.Rename(tmp, name)
 | 
			
		||||
	}()
 | 
			
		||||
	select {
 | 
			
		||||
	case <-ctx.Done():
 | 
			
		||||
		return ctx.Err()
 | 
			
		||||
	case <-done:
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete removes the specified file name.
 | 
			
		||||
func (d DirCache) Delete(ctx context.Context, name string) error {
 | 
			
		||||
	name = filepath.Join(string(d), name)
 | 
			
		||||
	var (
 | 
			
		||||
		err  error
 | 
			
		||||
		done = make(chan struct{})
 | 
			
		||||
	)
 | 
			
		||||
	go func() {
 | 
			
		||||
		err = os.Remove(name)
 | 
			
		||||
		close(done)
 | 
			
		||||
	}()
 | 
			
		||||
	select {
 | 
			
		||||
	case <-ctx.Done():
 | 
			
		||||
		return ctx.Err()
 | 
			
		||||
	case <-done:
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil && !os.IsNotExist(err) {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeTempFile writes b to a temporary file, closes the file and returns its path.
 | 
			
		||||
func (d DirCache) writeTempFile(prefix string, b []byte) (string, error) {
 | 
			
		||||
	// TempFile uses 0600 permissions
 | 
			
		||||
	f, err := ioutil.TempFile(string(d), prefix)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := f.Write(b); err != nil {
 | 
			
		||||
		f.Close()
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return f.Name(), f.Close()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								vendor/golang.org/x/crypto/acme/autocert/cache_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								vendor/golang.org/x/crypto/acme/autocert/cache_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package autocert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// make sure DirCache satisfies Cache interface
 | 
			
		||||
var _ Cache = DirCache("/")
 | 
			
		||||
 | 
			
		||||
func TestDirCache(t *testing.T) {
 | 
			
		||||
	dir, err := ioutil.TempDir("", "autocert")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	dir = filepath.Join(dir, "certs") // a nonexistent dir
 | 
			
		||||
	cache := DirCache(dir)
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
 | 
			
		||||
	// test cache miss
 | 
			
		||||
	if _, err := cache.Get(ctx, "nonexistent"); err != ErrCacheMiss {
 | 
			
		||||
		t.Errorf("get: %v; want ErrCacheMiss", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// test put/get
 | 
			
		||||
	b1 := []byte{1}
 | 
			
		||||
	if err := cache.Put(ctx, "dummy", b1); err != nil {
 | 
			
		||||
		t.Fatalf("put: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	b2, err := cache.Get(ctx, "dummy")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("get: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(b1, b2) {
 | 
			
		||||
		t.Errorf("b1 = %v; want %v", b1, b2)
 | 
			
		||||
	}
 | 
			
		||||
	name := filepath.Join(dir, "dummy")
 | 
			
		||||
	if _, err := os.Stat(name); err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// test delete
 | 
			
		||||
	if err := cache.Delete(ctx, "dummy"); err != nil {
 | 
			
		||||
		t.Fatalf("delete: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := cache.Get(ctx, "dummy"); err != ErrCacheMiss {
 | 
			
		||||
		t.Errorf("get: %v; want ErrCacheMiss", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										125
									
								
								vendor/golang.org/x/crypto/acme/autocert/renewal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								vendor/golang.org/x/crypto/acme/autocert/renewal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,125 @@
 | 
			
		||||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package autocert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/context"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// maxRandRenew is a maximum deviation from Manager.RenewBefore.
 | 
			
		||||
const maxRandRenew = time.Hour
 | 
			
		||||
 | 
			
		||||
// domainRenewal tracks the state used by the periodic timers
 | 
			
		||||
// renewing a single domain's cert.
 | 
			
		||||
type domainRenewal struct {
 | 
			
		||||
	m      *Manager
 | 
			
		||||
	domain string
 | 
			
		||||
	key    crypto.Signer
 | 
			
		||||
 | 
			
		||||
	timerMu sync.Mutex
 | 
			
		||||
	timer   *time.Timer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// start starts a cert renewal timer at the time
 | 
			
		||||
// defined by the certificate expiration time exp.
 | 
			
		||||
//
 | 
			
		||||
// If the timer is already started, calling start is a noop.
 | 
			
		||||
func (dr *domainRenewal) start(exp time.Time) {
 | 
			
		||||
	dr.timerMu.Lock()
 | 
			
		||||
	defer dr.timerMu.Unlock()
 | 
			
		||||
	if dr.timer != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	dr.timer = time.AfterFunc(dr.next(exp), dr.renew)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// stop stops the cert renewal timer.
 | 
			
		||||
// If the timer is already stopped, calling stop is a noop.
 | 
			
		||||
func (dr *domainRenewal) stop() {
 | 
			
		||||
	dr.timerMu.Lock()
 | 
			
		||||
	defer dr.timerMu.Unlock()
 | 
			
		||||
	if dr.timer == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	dr.timer.Stop()
 | 
			
		||||
	dr.timer = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// renew is called periodically by a timer.
 | 
			
		||||
// The first renew call is kicked off by dr.start.
 | 
			
		||||
func (dr *domainRenewal) renew() {
 | 
			
		||||
	dr.timerMu.Lock()
 | 
			
		||||
	defer dr.timerMu.Unlock()
 | 
			
		||||
	if dr.timer == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
 | 
			
		||||
	defer cancel()
 | 
			
		||||
	// TODO: rotate dr.key at some point?
 | 
			
		||||
	next, err := dr.do(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		next = maxRandRenew / 2
 | 
			
		||||
		next += time.Duration(pseudoRand.int63n(int64(next)))
 | 
			
		||||
	}
 | 
			
		||||
	dr.timer = time.AfterFunc(next, dr.renew)
 | 
			
		||||
	testDidRenewLoop(next, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// do is similar to Manager.createCert but it doesn't lock a Manager.state item.
 | 
			
		||||
// Instead, it requests a new certificate independently and, upon success,
 | 
			
		||||
// replaces dr.m.state item with a new one and updates cache for the given domain.
 | 
			
		||||
//
 | 
			
		||||
// It may return immediately if the expiration date of the currently cached cert
 | 
			
		||||
// is far enough in the future.
 | 
			
		||||
//
 | 
			
		||||
// The returned value is a time interval after which the renewal should occur again.
 | 
			
		||||
func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
 | 
			
		||||
	// a race is likely unavoidable in a distributed environment
 | 
			
		||||
	// but we try nonetheless
 | 
			
		||||
	if tlscert, err := dr.m.cacheGet(dr.domain); err == nil {
 | 
			
		||||
		next := dr.next(tlscert.Leaf.NotAfter)
 | 
			
		||||
		if next > dr.m.renewBefore()+maxRandRenew {
 | 
			
		||||
			return next, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.domain)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	state := &certState{
 | 
			
		||||
		key:  dr.key,
 | 
			
		||||
		cert: der,
 | 
			
		||||
		leaf: leaf,
 | 
			
		||||
	}
 | 
			
		||||
	tlscert, err := state.tlscert()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	dr.m.cachePut(dr.domain, tlscert)
 | 
			
		||||
	dr.m.stateMu.Lock()
 | 
			
		||||
	defer dr.m.stateMu.Unlock()
 | 
			
		||||
	// m.state is guaranteed to be non-nil at this point
 | 
			
		||||
	dr.m.state[dr.domain] = state
 | 
			
		||||
	return dr.next(leaf.NotAfter), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dr *domainRenewal) next(expiry time.Time) time.Duration {
 | 
			
		||||
	d := expiry.Sub(timeNow()) - dr.m.renewBefore()
 | 
			
		||||
	// add a bit of randomness to renew deadline
 | 
			
		||||
	n := pseudoRand.int63n(int64(maxRandRenew))
 | 
			
		||||
	d -= time.Duration(n)
 | 
			
		||||
	if d < 0 {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	return d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var testDidRenewLoop = func(next time.Duration, err error) {}
 | 
			
		||||
							
								
								
									
										190
									
								
								vendor/golang.org/x/crypto/acme/autocert/renewal_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								vendor/golang.org/x/crypto/acme/autocert/renewal_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,190 @@
 | 
			
		||||
// Copyright 2016 The Go Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a BSD-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package autocert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/acme"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestRenewalNext(t *testing.T) {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	timeNow = func() time.Time { return now }
 | 
			
		||||
	defer func() { timeNow = time.Now }()
 | 
			
		||||
 | 
			
		||||
	man := &Manager{RenewBefore: 7 * 24 * time.Hour}
 | 
			
		||||
	defer man.stopRenew()
 | 
			
		||||
	tt := []struct {
 | 
			
		||||
		expiry   time.Time
 | 
			
		||||
		min, max time.Duration
 | 
			
		||||
	}{
 | 
			
		||||
		{now.Add(90 * 24 * time.Hour), 83*24*time.Hour - maxRandRenew, 83 * 24 * time.Hour},
 | 
			
		||||
		{now.Add(time.Hour), 0, 1},
 | 
			
		||||
		{now, 0, 1},
 | 
			
		||||
		{now.Add(-time.Hour), 0, 1},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dr := &domainRenewal{m: man}
 | 
			
		||||
	for i, test := range tt {
 | 
			
		||||
		next := dr.next(test.expiry)
 | 
			
		||||
		if next < test.min || test.max < next {
 | 
			
		||||
			t.Errorf("%d: next = %v; want between %v and %v", i, next, test.min, test.max)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRenewFromCache(t *testing.T) {
 | 
			
		||||
	const domain = "example.org"
 | 
			
		||||
 | 
			
		||||
	// ACME CA server stub
 | 
			
		||||
	var ca *httptest.Server
 | 
			
		||||
	ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		w.Header().Set("replay-nonce", "nonce")
 | 
			
		||||
		if r.Method == "HEAD" {
 | 
			
		||||
			// a nonce request
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch r.URL.Path {
 | 
			
		||||
		// discovery
 | 
			
		||||
		case "/":
 | 
			
		||||
			if err := discoTmpl.Execute(w, ca.URL); err != nil {
 | 
			
		||||
				t.Fatalf("discoTmpl: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		// client key registration
 | 
			
		||||
		case "/new-reg":
 | 
			
		||||
			w.Write([]byte("{}"))
 | 
			
		||||
		// domain authorization
 | 
			
		||||
		case "/new-authz":
 | 
			
		||||
			w.Header().Set("location", ca.URL+"/authz/1")
 | 
			
		||||
			w.WriteHeader(http.StatusCreated)
 | 
			
		||||
			w.Write([]byte(`{"status": "valid"}`))
 | 
			
		||||
		// cert request
 | 
			
		||||
		case "/new-cert":
 | 
			
		||||
			var req struct {
 | 
			
		||||
				CSR string `json:"csr"`
 | 
			
		||||
			}
 | 
			
		||||
			decodePayload(&req, r.Body)
 | 
			
		||||
			b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
 | 
			
		||||
			csr, err := x509.ParseCertificateRequest(b)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatalf("new-cert: CSR: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			der, err := dummyCert(csr.PublicKey, domain)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatalf("new-cert: dummyCert: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL)
 | 
			
		||||
			w.Header().Set("link", chainUp)
 | 
			
		||||
			w.WriteHeader(http.StatusCreated)
 | 
			
		||||
			w.Write(der)
 | 
			
		||||
		// CA chain cert
 | 
			
		||||
		case "/ca-cert":
 | 
			
		||||
			der, err := dummyCert(nil, "ca")
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatalf("ca-cert: dummyCert: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			w.Write(der)
 | 
			
		||||
		default:
 | 
			
		||||
			t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
 | 
			
		||||
		}
 | 
			
		||||
	}))
 | 
			
		||||
	defer ca.Close()
 | 
			
		||||
 | 
			
		||||
	// use EC key to run faster on 386
 | 
			
		||||
	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	man := &Manager{
 | 
			
		||||
		Prompt:      AcceptTOS,
 | 
			
		||||
		Cache:       newMemCache(),
 | 
			
		||||
		RenewBefore: 24 * time.Hour,
 | 
			
		||||
		Client: &acme.Client{
 | 
			
		||||
			Key:          key,
 | 
			
		||||
			DirectoryURL: ca.URL,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	defer man.stopRenew()
 | 
			
		||||
 | 
			
		||||
	// cache an almost expired cert
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	cert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), domain)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}}
 | 
			
		||||
	if err := man.cachePut(domain, tlscert); err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// veriy the renewal happened
 | 
			
		||||
	defer func() {
 | 
			
		||||
		testDidRenewLoop = func(next time.Duration, err error) {}
 | 
			
		||||
	}()
 | 
			
		||||
	done := make(chan struct{})
 | 
			
		||||
	testDidRenewLoop = func(next time.Duration, err error) {
 | 
			
		||||
		defer close(done)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("testDidRenewLoop: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		// Next should be about 90 days:
 | 
			
		||||
		// dummyCert creates 90days expiry + account for man.RenewBefore.
 | 
			
		||||
		// Previous expiration was within 1 min.
 | 
			
		||||
		future := 88 * 24 * time.Hour
 | 
			
		||||
		if next < future {
 | 
			
		||||
			t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// ensure the new cert is cached
 | 
			
		||||
		after := time.Now().Add(future)
 | 
			
		||||
		tlscert, err := man.cacheGet(domain)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("man.cacheGet: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if !tlscert.Leaf.NotAfter.After(after) {
 | 
			
		||||
			t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// verify the old cert is also replaced in memory
 | 
			
		||||
		man.stateMu.Lock()
 | 
			
		||||
		defer man.stateMu.Unlock()
 | 
			
		||||
		s := man.state[domain]
 | 
			
		||||
		if s == nil {
 | 
			
		||||
			t.Fatalf("m.state[%q] is nil", domain)
 | 
			
		||||
		}
 | 
			
		||||
		tlscert, err = s.tlscert()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("s.tlscert: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if !tlscert.Leaf.NotAfter.After(after) {
 | 
			
		||||
			t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// trigger renew
 | 
			
		||||
	hello := &tls.ClientHelloInfo{ServerName: domain}
 | 
			
		||||
	if _, err := man.GetCertificate(hello); err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// wait for renew loop
 | 
			
		||||
	select {
 | 
			
		||||
	case <-time.After(10 * time.Second):
 | 
			
		||||
		t.Fatal("renew took too long to occur")
 | 
			
		||||
	case <-done:
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user