func rateLimit(limit int) func(next http.Handler) http.Handler {
	limiter := rate.NewLimiter(rate.Every(time.Second/time.Duration(limit)), limit*2)
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if !limiter.Allow() {
				errRateLimitExceeded.Write(w)
				return
			}
			next.ServeHTTP(w, r)
		})
	}
}
rateLimit is middleware which applies a general rate limit of limit requests
per second, with a burst size of limit * 2 requests.
package est
import (
	"bytes"
	"context"
	"crypto/x509"
	"crypto/x509/pkix"
	"errors"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"runtime/debug"
	"strconv"
	"strings"
	"time"
	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"go.mozilla.org/pkcs7"
	"golang.org/x/time/rate"
)
type ServerConfig struct {
	CA CA
	Logger Logger
	Timeout time.Duration
	AllowedHosts []string
	RateLimit int
	CheckBasicAuth func(ctx context.Context, r *http.Request, aps, username, password string) error
}
type ctxKey int
const (
	ctxKeyCA ctxKey = iota
	ctxKeyCertCache
	ctxKeyLogger
	ctxKeyReenroll
)
const (
	apsParamName   = "additionalPathSegment"
	defaultTimeout = time.Second * 60
)
const (
	logFieldError                 = "Error"
	logMsgCACertsFailed           = "failed to retrieve CA certificates"
	logMsgContentTypeInvalid      = "invalid content-type"
	logMsgCSRAttrsFailed          = "failed to retrieve CSR attributes"
	logMsgEnrollFailed            = "failed to enroll"
	logMsgMultipartDecodeFailed   = "failed to decode multipart request"
	logMsgMultipartEncodeFailed   = "failed to encode multipart response"
	logMsgPanicRecovery           = "recovered from panic"
	logMsgPublicKeyInvalid        = "invalid public key"
	logMsgReadBodyFailed          = "failed to read request body"
	logMsgTransferEncodingInvalid = "invalid content-transfer-encoding"
	logMsgVerifyFailed            = "failed to verify client certificate"
)
func LoggerFromContext(ctx context.Context) Logger {
	logger, _ := ctx.Value(ctxKeyLogger).(Logger)
	return logger
}
func caFromContext(ctx context.Context) CA {
	ca, _ := ctx.Value(ctxKeyCA).(CA)
	return ca
}
func certCacheFromContext(ctx context.Context) *cacertCache {
	cache, _ := ctx.Value(ctxKeyCertCache).(*cacertCache)
	return cache
}
func isReenroll(ctx context.Context) bool {
	_, ok := ctx.Value(ctxKeyReenroll).(bool)
	return ok
}
func NewRouter(cfg *ServerConfig) (http.Handler, error) {
	r := chi.NewRouter()
	timeout := defaultTimeout
	if cfg.Timeout != 0 {
		timeout = cfg.Timeout
	}
	var logger Logger
	if cfg.Logger != nil {
		logger = cfg.Logger
	} else {
		logger = newNOPLogger()
	}
	r.Use(middleware.Timeout(timeout))
	r.Use(middleware.RequestID)
	r.Use(middleware.RealIP)
	r.Use(withLogger(logger))
	r.Use(recoverer(logger))
	r.Use(addServerHeader)
	r.Use(addSecureHeaders)
	if len(cfg.AllowedHosts) > 0 {
		r.Use(verifyAllowedHosts(cfg.AllowedHosts))
	}
	r.Use(maxBodySize(65536))
	if cfg.RateLimit != 0 {
		r.Use(rateLimit(cfg.RateLimit))
	}
	r.Use(middleware.WithValue(ctxKeyCA, cfg.CA))
	r.Use(middleware.WithValue(ctxKeyCertCache, newCACertCache(cfg.CA)))
	r.With(
		requireBasicAuth(cfg.CheckBasicAuth, false),
	).Get("/healthcheck", healthcheck)
	r.Route(estPathPrefix, func(r chi.Router) {
		r.Get(cacertsEndpoint, cacerts)
		r.Get(csrattrsEndpoint, csrattrs)
		r.With(
			requireContentType(mimeTypePKCS10),
		).With(
			requireTransferEncoding(encodingTypeBase64),
		).With(
			requireBasicAuth(cfg.CheckBasicAuth, true),
		).Post(enrollEndpoint, enroll)
		r.With(
			requireContentType(mimeTypePKCS10),
		).With(
			requireTransferEncoding(encodingTypeBase64),
		).With(
			requireBasicAuth(cfg.CheckBasicAuth, true),
		).With(
			middleware.WithValue(ctxKeyReenroll, true),
		).Post(reenrollEndpoint, enroll)
		r.With(
			requireContentType(mimeTypePKCS10),
		).With(
			requireTransferEncoding(encodingTypeBase64),
		).With(
			requireBasicAuth(cfg.CheckBasicAuth, true),
		).Post(serverkeygenEndpoint, serverkeygen)
		r.With(
			requireContentType(mimeTypeMultipart),
		).With(
			requireBasicAuth(cfg.CheckBasicAuth, true),
		).Post(tpmenrollEndpoint, tpmenroll)
		r.Route(fmt.Sprintf("/{%s}", apsParamName), func(r chi.Router) {
			r.Get(cacertsEndpoint, cacerts)
			r.Get(csrattrsEndpoint, csrattrs)
			r.With(
				requireContentType(mimeTypePKCS10),
			).With(
				requireTransferEncoding(encodingTypeBase64),
			).With(
				requireBasicAuth(cfg.CheckBasicAuth, true),
			).Post(enrollEndpoint, enroll)
			r.With(
				requireContentType(mimeTypePKCS10),
			).With(
				requireTransferEncoding(encodingTypeBase64),
			).With(
				requireBasicAuth(cfg.CheckBasicAuth, true),
			).With(
				middleware.WithValue(ctxKeyReenroll, true),
			).Post(reenrollEndpoint, enroll)
			r.With(
				requireContentType(mimeTypePKCS10),
			).With(
				requireTransferEncoding(encodingTypeBase64),
			).With(
				requireBasicAuth(cfg.CheckBasicAuth, true),
			).Post(serverkeygenEndpoint, serverkeygen)
			r.With(
				requireContentType(mimeTypeMultipart),
			).With(
				requireBasicAuth(cfg.CheckBasicAuth, true),
			).Post(tpmenrollEndpoint, tpmenroll)
		})
	})
	return r, nil
}
func healthcheck(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}
func cacerts(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	aps := chi.URLParam(r, apsParamName)
	certs, err := caFromContext(ctx).CACerts(ctx, aps, r)
	if writeOnError(ctx, w, logMsgCACertsFailed, err) {
		return
	}
	certCacheFromContext(ctx).Add(aps, certs)
	writeResponse(w, mimeTypePKCS7, true, certs)
}
func csrattrs(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	aps := chi.URLParam(r, apsParamName)
	attrs, err := caFromContext(ctx).CSRAttrs(ctx, aps, r)
	if writeOnError(ctx, w, logMsgCSRAttrsFailed, err) {
		return
	}
	writeResponse(w, mimeTypeCSRAttrs, true, attrs)
}
func enroll(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	aps := chi.URLParam(r, apsParamName)
	csr, err := readCSRRequest(r.Body, true)
	if writeOnError(ctx, w, logMsgReadBodyFailed, err) {
		return
	}
	renew := isReenroll(ctx)
	if renew {
		if len(r.TLS.PeerCertificates) == 0 {
			errNoClientCertificate.Write(w)
			return
		}
		cert := r.TLS.PeerCertificates[0]
		if !bytes.Equal(csr.RawSubject, cert.RawSubject) {
			errSubjectChanged.Write(w)
			return
		}
		var csrSAN pkix.Extension
		var certSAN pkix.Extension
		for _, ext := range csr.Extensions {
			if ext.Id.Equal(oidSubjectAltName) {
				csrSAN = ext
				break
			}
		}
		for _, ext := range cert.Extensions {
			if ext.Id.Equal(oidSubjectAltName) {
				certSAN = ext
				break
			}
		}
		if !bytes.Equal(csrSAN.Value, certSAN.Value) {
			errSubjectChanged.Write(w)
			return
		}
		err := certCacheFromContext(ctx).Verify(ctx, aps, cert, r)
		if writeOnError(ctx, w, logMsgVerifyFailed, err) {
			return
		}
	}
	var cert *x509.Certificate
	if renew {
		cert, err = caFromContext(ctx).Reenroll(ctx, r.TLS.PeerCertificates[0], csr, aps, r)
	} else {
		cert, err = caFromContext(ctx).Enroll(ctx, csr, aps, r)
	}
	if writeOnError(r.Context(), w, logMsgEnrollFailed, err) {
		return
	}
	writeResponse(w, mimeTypePKCS7CertsOnly, true, cert)
}
func serverkeygen(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	aps := chi.URLParam(r, apsParamName)
	csr, err := readCSRRequest(r.Body, true)
	if writeOnError(ctx, w, logMsgReadBodyFailed, err) {
		return
	}
	cert, key, err := caFromContext(ctx).ServerKeyGen(ctx, csr, aps, r)
	if writeOnError(ctx, w, logMsgEnrollFailed, err) {
		return
	}
	var keyContentType string
	if _, p8err := x509.ParsePKCS8PrivateKey(key); p8err == nil {
		keyContentType = mimeTypePKCS8
	} else if _, p7err := pkcs7.Parse(key); p7err == nil {
		keyContentType = mimeTypePKCS7GenKey
	} else {
		LoggerFromContext(ctx).Errorf("failed to parse private key: %v, %v", p8err, p7err)
		errInternal.Write(w)
		return
	}
	buf, contentType, err := encodeMultiPart(
		serverKeyGenBoundary,
		[]multipartPart{
			{contentType: keyContentType, data: key},
			{contentType: mimeTypePKCS7CertsOnly, data: cert},
		},
	)
	if writeOnError(ctx, w, logMsgMultipartEncodeFailed, err) {
		return
	}
	writeResponse(w, contentType, false, buf.Bytes())
}
func tpmenroll(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	aps := chi.URLParam(r, apsParamName)
	var csr *x509.CertificateRequest
	var ekCerts []*x509.Certificate
	var ekPub []byte
	var akPub []byte
	_, err := decodeMultipartRequest(
		r,
		[]multipartPart{
			{contentType: mimeTypePKCS10, data: &csr},
			{contentType: mimeTypePKCS7, data: &ekCerts},
			{contentType: mimeTypeOctetStream, data: &ekPub},
			{contentType: mimeTypeOctetStream, data: &akPub},
		},
	)
	if writeOnError(ctx, w, logMsgMultipartDecodeFailed, err) {
		return
	}
	if err := validatePublicAreaPublicKey(ekPub, ekCerts[0].PublicKey); err != nil {
		writeOnError(ctx, w, logMsgPublicKeyInvalid, err)
		return
	}
	credBlob, encSeed, cred, err := caFromContext(ctx).TPMEnroll(ctx, csr, ekCerts, ekPub, akPub, aps, r)
	if writeOnError(ctx, w, logMsgEnrollFailed, err) {
		return
	}
	buf, contentType, err := encodeMultiPart(
		tpmEnrollBoundary,
		[]multipartPart{
			{contentType: mimeTypeOctetStream, data: credBlob},
			{contentType: mimeTypeOctetStream, data: encSeed},
			{contentType: mimeTypePKCS7Enveloped, data: cred},
		},
	)
	if writeOnError(ctx, w, logMsgMultipartEncodeFailed, err) {
		return
	}
	writeResponse(w, contentType, false, buf.Bytes())
}
func writeOnError(ctx context.Context, w http.ResponseWriter, msg string, err error) bool {
	if err == nil {
		return false
	}
	var estErr Error
	if errors.As(err, &estErr) {
		if estErr.StatusCode() == http.StatusInternalServerError {
			LoggerFromContext(ctx).Errorw(msg, logFieldError, err.Error())
		}
		w.Header().Set(contentTypeHeader, mimeTypeTextPlainUTF8)
		if secs := estErr.RetryAfter(); secs != 0 {
			w.Header().Set(retryAfterHeader, strconv.Itoa(secs))
		}
		w.WriteHeader(estErr.StatusCode())
		w.Write([]byte(fmt.Sprintf("%d %s\n", estErr.StatusCode(), estErr.Error())))
	} else {
		LoggerFromContext(ctx).Errorw(msg, logFieldError, err.Error())
		errInternal.Write(w)
	}
	return true
}
func withLogger(logger Logger) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		fn := func(w http.ResponseWriter, r *http.Request) {
			logger := logger
			if reqID := middleware.GetReqID(r.Context()); reqID != "" {
				logger = logger.With("HTTP Request ID", reqID)
			}
			ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
			t1 := time.Now()
			defer func() {
				scheme := "http"
				if r.TLS != nil {
					scheme = "https"
				}
				logger.Infow("HTTP request",
					"Method", r.Method,
					"URI", fmt.Sprintf("%s://%s%s", scheme, r.Host, r.RequestURI),
					"Protocol", r.Proto,
					"Remote Address", r.RemoteAddr,
					"Status", ww.Status(),
					"Bytes Written", ww.BytesWritten(),
					"Time Taken", time.Since(t1),
				)
			}()
			ctx := context.WithValue(r.Context(), ctxKeyLogger, logger)
			next.ServeHTTP(ww, r.WithContext(ctx))
		}
		return http.HandlerFunc(fn)
	}
}
func recoverer(logger Logger) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			logger := logger
			defer func() {
				if rcv := recover(); rcv != nil {
					if reqID := middleware.GetReqID(r.Context()); reqID != "" {
						logger = logger.With("HTTP Request ID", reqID)
					}
					scheme := "http"
					if r.TLS != nil {
						scheme = "https"
					}
					logger.Errorw(
						logMsgPanicRecovery,
						"Method", r.Method,
						"URI", fmt.Sprintf("%s://%s%s", scheme, r.Host, r.RequestURI),
						"Protocol", r.Proto,
						"Remote Address", r.RemoteAddr,
						"Panic Value", rcv,
						"Stack Trace", string(debug.Stack()),
					)
				}
			}()
			next.ServeHTTP(w, r)
		})
	}
}
func requireContentType(t string) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if err := verifyRequestType(r.Header.Get(contentTypeHeader), t); err != nil {
				writeOnError(r.Context(), w, logMsgContentTypeInvalid, err)
				return
			}
			next.ServeHTTP(w, r)
		})
	}
}
func requireTransferEncoding(e string) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if err := verifyRequestEncoding(r.Header.Get(transferEncodingHeader), e); err != nil {
				writeOnError(r.Context(), w, logMsgTransferEncodingInvalid, err)
				return
			}
			next.ServeHTTP(w, r)
		})
	}
}
func addServerHeader(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set(serverHeader, "GlobalSign EST Server "+estVersion)
		next.ServeHTTP(w, r)
	})
}
func addSecureHeaders(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set(strictTransportHeader, "max-age=31536000")
		w.Header().Set(contentTypeOptionsHeader, "nosniff")
		next.ServeHTTP(w, r)
	})
}
func verifyAllowedHosts(allowed []string) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			reqHost := r.Host
			if host, _, err := net.SplitHostPort(reqHost); err == nil {
				reqHost = host
			}
			goodHost := false
			for _, host := range allowed {
				if strings.EqualFold(host, reqHost) {
					goodHost = true
					break
				}
			}
			if !goodHost {
				errHostNotAllowed.Write(w)
				return
			}
			next.ServeHTTP(w, r)
		})
	}
}