forked from TrueCloudLab/distribution
vendor: update golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d (CVE-2020-7919)
Includes69ecbb4d6d
(forward-port of8b5121be2f
), which fixes CVE-2020-7919: - Panic in crypto/x509 certificate parsing and golang.org/x/crypto/cryptobyte On 32-bit architectures, a malformed input to crypto/x509 or the ASN.1 parsing functions of golang.org/x/crypto/cryptobyte can lead to a panic. The malformed certificate can be delivered via a crypto/tls connection to a client, or to a server that accepts client certificates. net/http clients can be made to crash by an HTTPS server, while net/http servers that accept client certificates will recover the panic and are unaffected. Thanks to Project Wycheproof for providing the test cases that led to the discovery of this issue. The issue is CVE-2020-7919 and Go issue golang.org/issue/36837. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
016549532f
commit
7e290869e7
11 changed files with 1253 additions and 276 deletions
2
go.mod
2
go.mod
|
@ -45,7 +45,7 @@ require (
|
||||||
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 // indirect
|
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 // indirect
|
||||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50
|
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50
|
||||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f // indirect
|
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f // indirect
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d
|
||||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b // indirect
|
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||||
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed // indirect
|
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed // indirect
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -111,10 +111,13 @@ github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1
|
||||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U=
|
||||||
|
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b h1:lkjdUzSyJ5P1+eal9fxXX9Xg2BTfswsonKUse48C0uE=
|
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b h1:lkjdUzSyJ5P1+eal9fxXX9Xg2BTfswsonKUse48C0uE=
|
||||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||||
|
@ -122,6 +125,7 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed h1:uPxWBzB3+mlnjy9W58qY1j/cjyFjutgw/Vhan2zLy/A=
|
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed h1:uPxWBzB3+mlnjy9W58qY1j/cjyFjutgw/Vhan2zLy/A=
|
||||||
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
|
282
vendor/golang.org/x/crypto/acme/acme.go
generated
vendored
282
vendor/golang.org/x/crypto/acme/acme.go
generated
vendored
|
@ -4,7 +4,10 @@
|
||||||
|
|
||||||
// Package acme provides an implementation of the
|
// Package acme provides an implementation of the
|
||||||
// Automatic Certificate Management Environment (ACME) spec.
|
// Automatic Certificate Management Environment (ACME) spec.
|
||||||
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02 for details.
|
// The intial implementation was based on ACME draft-02 and
|
||||||
|
// is now being extended to comply with RFC 8555.
|
||||||
|
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02
|
||||||
|
// and https://tools.ietf.org/html/rfc8555 for details.
|
||||||
//
|
//
|
||||||
// Most common scenarios will want to use autocert subdirectory instead,
|
// Most common scenarios will want to use autocert subdirectory instead,
|
||||||
// which provides automatic access to certificates from Let's Encrypt
|
// which provides automatic access to certificates from Let's Encrypt
|
||||||
|
@ -41,7 +44,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
|
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
|
||||||
LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"
|
LetsEncryptURL = "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
|
|
||||||
// ALPNProto is the ALPN protocol name used by a CA server when validating
|
// ALPNProto is the ALPN protocol name used by a CA server when validating
|
||||||
// tls-alpn-01 challenges.
|
// tls-alpn-01 challenges.
|
||||||
|
@ -57,7 +60,10 @@ var idPeACMEIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxChainLen = 5 // max depth and breadth of a certificate chain
|
maxChainLen = 5 // max depth and breadth of a certificate chain
|
||||||
maxCertSize = 1 << 20 // max size of a certificate, in bytes
|
maxCertSize = 1 << 20 // max size of a certificate, in DER bytes
|
||||||
|
// Used for decoding certs from application/pem-certificate-chain response,
|
||||||
|
// the default when in RFC mode.
|
||||||
|
maxCertChainSize = maxCertSize * maxChainLen
|
||||||
|
|
||||||
// Max number of collected nonces kept in memory.
|
// Max number of collected nonces kept in memory.
|
||||||
// Expect usual peak of 1 or 2.
|
// Expect usual peak of 1 or 2.
|
||||||
|
@ -109,21 +115,55 @@ type Client struct {
|
||||||
// The jitter is a random value up to 1 second.
|
// The jitter is a random value up to 1 second.
|
||||||
RetryBackoff func(n int, r *http.Request, resp *http.Response) time.Duration
|
RetryBackoff func(n int, r *http.Request, resp *http.Response) time.Duration
|
||||||
|
|
||||||
dirMu sync.Mutex // guards writes to dir
|
// UserAgent is prepended to the User-Agent header sent to the ACME server,
|
||||||
|
// which by default is this package's name and version.
|
||||||
|
//
|
||||||
|
// Reusable libraries and tools in particular should set this value to be
|
||||||
|
// identifiable by the server, in case they are causing issues.
|
||||||
|
UserAgent string
|
||||||
|
|
||||||
|
cacheMu sync.Mutex
|
||||||
dir *Directory // cached result of Client's Discover method
|
dir *Directory // cached result of Client's Discover method
|
||||||
|
kid keyID // cached Account.URI obtained from registerRFC or getAccountRFC
|
||||||
|
|
||||||
noncesMu sync.Mutex
|
noncesMu sync.Mutex
|
||||||
nonces map[string]struct{} // nonces collected from previous responses
|
nonces map[string]struct{} // nonces collected from previous responses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// accountKID returns a key ID associated with c.Key, the account identity
|
||||||
|
// provided by the CA during RFC based registration.
|
||||||
|
// It assumes c.Discover has already been called.
|
||||||
|
//
|
||||||
|
// accountKID requires at most one network roundtrip.
|
||||||
|
// It caches only successful result.
|
||||||
|
//
|
||||||
|
// When in pre-RFC mode or when c.getRegRFC responds with an error, accountKID
|
||||||
|
// returns noKeyID.
|
||||||
|
func (c *Client) accountKID(ctx context.Context) keyID {
|
||||||
|
c.cacheMu.Lock()
|
||||||
|
defer c.cacheMu.Unlock()
|
||||||
|
if !c.dir.rfcCompliant() {
|
||||||
|
return noKeyID
|
||||||
|
}
|
||||||
|
if c.kid != noKeyID {
|
||||||
|
return c.kid
|
||||||
|
}
|
||||||
|
a, err := c.getRegRFC(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return noKeyID
|
||||||
|
}
|
||||||
|
c.kid = keyID(a.URI)
|
||||||
|
return c.kid
|
||||||
|
}
|
||||||
|
|
||||||
// Discover performs ACME server discovery using c.DirectoryURL.
|
// Discover performs ACME server discovery using c.DirectoryURL.
|
||||||
//
|
//
|
||||||
// It caches successful result. So, subsequent calls will not result in
|
// It caches successful result. So, subsequent calls will not result in
|
||||||
// a network round-trip. This also means mutating c.DirectoryURL after successful call
|
// a network round-trip. This also means mutating c.DirectoryURL after successful call
|
||||||
// of this method will have no effect.
|
// of this method will have no effect.
|
||||||
func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
||||||
c.dirMu.Lock()
|
c.cacheMu.Lock()
|
||||||
defer c.dirMu.Unlock()
|
defer c.cacheMu.Unlock()
|
||||||
if c.dir != nil {
|
if c.dir != nil {
|
||||||
return *c.dir, nil
|
return *c.dir, nil
|
||||||
}
|
}
|
||||||
|
@ -137,29 +177,55 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
||||||
|
|
||||||
var v struct {
|
var v struct {
|
||||||
Reg string `json:"new-reg"`
|
Reg string `json:"new-reg"`
|
||||||
|
RegRFC string `json:"newAccount"`
|
||||||
Authz string `json:"new-authz"`
|
Authz string `json:"new-authz"`
|
||||||
|
AuthzRFC string `json:"newAuthz"`
|
||||||
|
OrderRFC string `json:"newOrder"`
|
||||||
Cert string `json:"new-cert"`
|
Cert string `json:"new-cert"`
|
||||||
Revoke string `json:"revoke-cert"`
|
Revoke string `json:"revoke-cert"`
|
||||||
|
RevokeRFC string `json:"revokeCert"`
|
||||||
|
NonceRFC string `json:"newNonce"`
|
||||||
|
KeyChangeRFC string `json:"keyChange"`
|
||||||
Meta struct {
|
Meta struct {
|
||||||
Terms string `json:"terms-of-service"`
|
Terms string `json:"terms-of-service"`
|
||||||
Website string `json:"website"`
|
TermsRFC string `json:"termsOfService"`
|
||||||
|
WebsiteRFC string `json:"website"`
|
||||||
CAA []string `json:"caa-identities"`
|
CAA []string `json:"caa-identities"`
|
||||||
|
CAARFC []string `json:"caaIdentities"`
|
||||||
|
ExternalAcctRFC bool `json:"externalAccountRequired"`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
return Directory{}, err
|
return Directory{}, err
|
||||||
}
|
}
|
||||||
|
if v.OrderRFC == "" {
|
||||||
|
// Non-RFC compliant ACME CA.
|
||||||
c.dir = &Directory{
|
c.dir = &Directory{
|
||||||
RegURL: v.Reg,
|
RegURL: v.Reg,
|
||||||
AuthzURL: v.Authz,
|
AuthzURL: v.Authz,
|
||||||
CertURL: v.Cert,
|
CertURL: v.Cert,
|
||||||
RevokeURL: v.Revoke,
|
RevokeURL: v.Revoke,
|
||||||
Terms: v.Meta.Terms,
|
Terms: v.Meta.Terms,
|
||||||
Website: v.Meta.Website,
|
Website: v.Meta.WebsiteRFC,
|
||||||
CAA: v.Meta.CAA,
|
CAA: v.Meta.CAA,
|
||||||
}
|
}
|
||||||
return *c.dir, nil
|
return *c.dir, nil
|
||||||
}
|
}
|
||||||
|
// RFC compliant ACME CA.
|
||||||
|
c.dir = &Directory{
|
||||||
|
RegURL: v.RegRFC,
|
||||||
|
AuthzURL: v.AuthzRFC,
|
||||||
|
OrderURL: v.OrderRFC,
|
||||||
|
RevokeURL: v.RevokeRFC,
|
||||||
|
NonceURL: v.NonceRFC,
|
||||||
|
KeyChangeURL: v.KeyChangeRFC,
|
||||||
|
Terms: v.Meta.TermsRFC,
|
||||||
|
Website: v.Meta.WebsiteRFC,
|
||||||
|
CAA: v.Meta.CAARFC,
|
||||||
|
ExternalAccountRequired: v.Meta.ExternalAcctRFC,
|
||||||
|
}
|
||||||
|
return *c.dir, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) directoryURL() string {
|
func (c *Client) directoryURL() string {
|
||||||
if c.DirectoryURL != "" {
|
if c.DirectoryURL != "" {
|
||||||
|
@ -169,6 +235,9 @@ func (c *Client) directoryURL() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
|
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
|
||||||
|
// It is incompatible with RFC 8555. Callers should use CreateOrderCert when interfacing
|
||||||
|
// with an RFC-compliant CA.
|
||||||
|
//
|
||||||
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
|
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
|
||||||
// with a different duration.
|
// with a different duration.
|
||||||
// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain.
|
// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain.
|
||||||
|
@ -199,7 +268,7 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
|
||||||
req.NotAfter = now.Add(exp).Format(time.RFC3339)
|
req.NotAfter = now.Add(exp).Format(time.RFC3339)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.post(ctx, c.Key, c.dir.CertURL, req, wantStatus(http.StatusCreated))
|
res, err := c.post(ctx, nil, c.dir.CertURL, req, wantStatus(http.StatusCreated))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
@ -220,12 +289,22 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
|
||||||
// It retries the request until the certificate is successfully retrieved,
|
// It retries the request until the certificate is successfully retrieved,
|
||||||
// context is cancelled by the caller or an error response is received.
|
// context is cancelled by the caller or an error response is received.
|
||||||
//
|
//
|
||||||
// The returned value will also contain the CA (issuer) certificate if the bundle argument is true.
|
// If the bundle argument is true, the returned value also contains the CA (issuer)
|
||||||
|
// certificate chain.
|
||||||
//
|
//
|
||||||
// FetchCert returns an error if the CA's response or chain was unreasonably large.
|
// FetchCert returns an error if the CA's response or chain was unreasonably large.
|
||||||
// Callers are encouraged to parse the returned value to ensure the certificate is valid
|
// Callers are encouraged to parse the returned value to ensure the certificate is valid
|
||||||
// and has expected features.
|
// and has expected features.
|
||||||
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
|
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dir.rfcCompliant() {
|
||||||
|
return c.fetchCertRFC(ctx, url, bundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy non-authenticated GET request.
|
||||||
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -240,10 +319,15 @@ func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]by
|
||||||
// For instance, the key pair of the certificate may be authorized.
|
// For instance, the key pair of the certificate may be authorized.
|
||||||
// If the key is nil, c.Key is used instead.
|
// If the key is nil, c.Key is used instead.
|
||||||
func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
|
func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
|
||||||
if _, err := c.Discover(ctx); err != nil {
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if dir.rfcCompliant() {
|
||||||
|
return c.revokeCertRFC(ctx, key, cert, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy CA.
|
||||||
body := &struct {
|
body := &struct {
|
||||||
Resource string `json:"resource"`
|
Resource string `json:"resource"`
|
||||||
Cert string `json:"certificate"`
|
Cert string `json:"certificate"`
|
||||||
|
@ -253,10 +337,7 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
|
||||||
Cert: base64.RawURLEncoding.EncodeToString(cert),
|
Cert: base64.RawURLEncoding.EncodeToString(cert),
|
||||||
Reason: int(reason),
|
Reason: int(reason),
|
||||||
}
|
}
|
||||||
if key == nil {
|
res, err := c.post(ctx, key, dir.RevokeURL, body, wantStatus(http.StatusOK))
|
||||||
key = c.Key
|
|
||||||
}
|
|
||||||
res, err := c.post(ctx, key, c.dir.RevokeURL, body, wantStatus(http.StatusOK))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -268,20 +349,30 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
|
||||||
// during account registration. See Register method of Client for more details.
|
// during account registration. See Register method of Client for more details.
|
||||||
func AcceptTOS(tosURL string) bool { return true }
|
func AcceptTOS(tosURL string) bool { return true }
|
||||||
|
|
||||||
// Register creates a new account registration by following the "new-reg" flow.
|
// Register creates a new account with the CA using c.Key.
|
||||||
// It returns the registered account. The account is not modified.
|
// It returns the registered account. The account acct is not modified.
|
||||||
//
|
//
|
||||||
// The registration may require the caller to agree to the CA's Terms of Service (TOS).
|
// The registration may require the caller to agree to the CA's Terms of Service (TOS).
|
||||||
// If so, and the account has not indicated the acceptance of the terms (see Account for details),
|
// If so, and the account has not indicated the acceptance of the terms (see Account for details),
|
||||||
// Register calls prompt with a TOS URL provided by the CA. Prompt should report
|
// Register 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 caller can use AcceptTOS.
|
// whether the caller agrees to the terms. To always accept the terms, the caller can use AcceptTOS.
|
||||||
func (c *Client) Register(ctx context.Context, a *Account, prompt func(tosURL string) bool) (*Account, error) {
|
//
|
||||||
if _, err := c.Discover(ctx); err != nil {
|
// When interfacing with an RFC-compliant CA, non-RFC 8555 fields of acct are ignored
|
||||||
|
// and prompt is called if Directory's Terms field is non-zero.
|
||||||
|
// Also see Error's Instance field for when a CA requires already registered accounts to agree
|
||||||
|
// to an updated Terms of Service.
|
||||||
|
func (c *Client) Register(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if dir.rfcCompliant() {
|
||||||
|
return c.registerRFC(ctx, acct, prompt)
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
// Legacy ACME draft registration flow.
|
||||||
if a, err = c.doReg(ctx, c.dir.RegURL, "new-reg", a); err != nil {
|
a, err := c.doReg(ctx, dir.RegURL, "new-reg", acct)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var accept bool
|
var accept bool
|
||||||
|
@ -295,9 +386,20 @@ func (c *Client) Register(ctx context.Context, a *Account, prompt func(tosURL st
|
||||||
return a, err
|
return a, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetReg retrieves an existing registration.
|
// GetReg retrieves an existing account associated with c.Key.
|
||||||
// The url argument is an Account URI.
|
//
|
||||||
|
// The url argument is an Account URI used with pre-RFC 8555 CAs.
|
||||||
|
// It is ignored when interfacing with an RFC-compliant CA.
|
||||||
func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
|
func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dir.rfcCompliant() {
|
||||||
|
return c.getRegRFC(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy CA.
|
||||||
a, err := c.doReg(ctx, url, "reg", nil)
|
a, err := c.doReg(ctx, url, "reg", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -308,9 +410,21 @@ func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
|
||||||
|
|
||||||
// UpdateReg updates an existing registration.
|
// UpdateReg updates an existing registration.
|
||||||
// It returns an updated account copy. The provided account is not modified.
|
// It returns an updated account copy. The provided account is not modified.
|
||||||
func (c *Client) UpdateReg(ctx context.Context, a *Account) (*Account, error) {
|
//
|
||||||
uri := a.URI
|
// When interfacing with RFC-compliant CAs, a.URI is ignored and the account URL
|
||||||
a, err := c.doReg(ctx, uri, "reg", a)
|
// associated with c.Key is used instead.
|
||||||
|
func (c *Client) UpdateReg(ctx context.Context, acct *Account) (*Account, error) {
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dir.rfcCompliant() {
|
||||||
|
return c.updateRegRFC(ctx, acct)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy CA.
|
||||||
|
uri := acct.URI
|
||||||
|
a, err := c.doReg(ctx, uri, "reg", acct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -318,13 +432,21 @@ func (c *Client) UpdateReg(ctx context.Context, a *Account) (*Account, error) {
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authorize performs the initial step in an authorization flow.
|
// Authorize performs the initial step in the pre-authorization flow,
|
||||||
|
// as opposed to order-based flow.
|
||||||
// The caller will then need to choose from and perform a set of returned
|
// The caller will then need to choose from and perform a set of returned
|
||||||
// challenges using c.Accept in order to successfully complete authorization.
|
// challenges using c.Accept in order to successfully complete authorization.
|
||||||
//
|
//
|
||||||
|
// Once complete, the caller can use AuthorizeOrder which the CA
|
||||||
|
// should provision with the already satisfied authorization.
|
||||||
|
// For pre-RFC CAs, the caller can proceed directly to requesting a certificate
|
||||||
|
// using CreateCert method.
|
||||||
|
//
|
||||||
// If an authorization has been previously granted, the CA may return
|
// If an authorization has been previously granted, the CA may return
|
||||||
// a valid authorization (Authorization.Status is StatusValid). If so, the caller
|
// a valid authorization which has its Status field set to StatusValid.
|
||||||
// need not fulfill any challenge and can proceed to requesting a certificate.
|
//
|
||||||
|
// More about pre-authorization can be found at
|
||||||
|
// https://tools.ietf.org/html/rfc8555#section-7.4.1.
|
||||||
func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) {
|
func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) {
|
||||||
return c.authorize(ctx, "dns", domain)
|
return c.authorize(ctx, "dns", domain)
|
||||||
}
|
}
|
||||||
|
@ -355,7 +477,7 @@ func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization
|
||||||
Resource: "new-authz",
|
Resource: "new-authz",
|
||||||
Identifier: authzID{Type: typ, Value: val},
|
Identifier: authzID{Type: typ, Value: val},
|
||||||
}
|
}
|
||||||
res, err := c.post(ctx, c.Key, c.dir.AuthzURL, req, wantStatus(http.StatusCreated))
|
res, err := c.post(ctx, nil, c.dir.AuthzURL, req, wantStatus(http.StatusCreated))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -376,7 +498,17 @@ func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization
|
||||||
// If a caller needs to poll an authorization until its status is final,
|
// If a caller needs to poll an authorization until its status is final,
|
||||||
// see the WaitAuthorization method.
|
// see the WaitAuthorization method.
|
||||||
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
||||||
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res *http.Response
|
||||||
|
if dir.rfcCompliant() {
|
||||||
|
res, err = c.postAsGet(ctx, url, wantStatus(http.StatusOK))
|
||||||
|
} else {
|
||||||
|
res, err = c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -393,11 +525,16 @@ func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorizati
|
||||||
// The url argument is an Authorization.URI value.
|
// The url argument is an Authorization.URI value.
|
||||||
//
|
//
|
||||||
// If successful, the caller will be required to obtain a new authorization
|
// If successful, the caller will be required to obtain a new authorization
|
||||||
// using the Authorize method before being able to request a new certificate
|
// using the Authorize or AuthorizeOrder methods before being able to request
|
||||||
// for the domain associated with the authorization.
|
// a new certificate for the domain associated with the authorization.
|
||||||
//
|
//
|
||||||
// It does not revoke existing certificates.
|
// It does not revoke existing certificates.
|
||||||
func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||||
|
// Required for c.accountKID() when in RFC mode.
|
||||||
|
if _, err := c.Discover(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
req := struct {
|
req := struct {
|
||||||
Resource string `json:"resource"`
|
Resource string `json:"resource"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
|
@ -407,7 +544,7 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||||
Status: "deactivated",
|
Status: "deactivated",
|
||||||
Delete: true,
|
Delete: true,
|
||||||
}
|
}
|
||||||
res, err := c.post(ctx, c.Key, url, req, wantStatus(http.StatusOK))
|
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -423,8 +560,18 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||||
// In all other cases WaitAuthorization returns an error.
|
// In all other cases WaitAuthorization returns an error.
|
||||||
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
|
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
|
||||||
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
||||||
|
// Required for c.accountKID() when in RFC mode.
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
getfn := c.postAsGet
|
||||||
|
if !dir.rfcCompliant() {
|
||||||
|
getfn = c.get
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
res, err := getfn(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -467,10 +614,21 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
|
||||||
//
|
//
|
||||||
// A client typically polls a challenge status using this method.
|
// A client typically polls a challenge status using this method.
|
||||||
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
|
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
|
||||||
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
// Required for c.accountKID() when in RFC mode.
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getfn := c.postAsGet
|
||||||
|
if !dir.rfcCompliant() {
|
||||||
|
getfn = c.get
|
||||||
|
}
|
||||||
|
res, err := getfn(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
v := wireChallenge{URI: url}
|
v := wireChallenge{URI: url}
|
||||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
@ -484,12 +642,19 @@ func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, erro
|
||||||
//
|
//
|
||||||
// The server will then perform the validation asynchronously.
|
// The server will then perform the validation asynchronously.
|
||||||
func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) {
|
func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) {
|
||||||
auth, err := keyAuth(c.Key.Public(), chal.Token)
|
// Required for c.accountKID() when in RFC mode.
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req := struct {
|
var req interface{} = json.RawMessage("{}") // RFC-compliant CA
|
||||||
|
if !dir.rfcCompliant() {
|
||||||
|
auth, err := keyAuth(c.Key.Public(), chal.Token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req = struct {
|
||||||
Resource string `json:"resource"`
|
Resource string `json:"resource"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Auth string `json:"keyAuthorization"`
|
Auth string `json:"keyAuthorization"`
|
||||||
|
@ -498,7 +663,8 @@ func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error
|
||||||
Type: chal.Type,
|
Type: chal.Type,
|
||||||
Auth: auth,
|
Auth: auth,
|
||||||
}
|
}
|
||||||
res, err := c.post(ctx, c.Key, chal.URI, req, wantStatus(
|
}
|
||||||
|
res, err := c.post(ctx, nil, chal.URI, req, wantStatus(
|
||||||
http.StatusOK, // according to the spec
|
http.StatusOK, // according to the spec
|
||||||
http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
|
http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
|
||||||
))
|
))
|
||||||
|
@ -548,21 +714,8 @@ func (c *Client) HTTP01ChallengePath(token string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
|
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
|
||||||
// Servers can present the certificate to validate the challenge and prove control
|
|
||||||
// over a domain name.
|
|
||||||
//
|
//
|
||||||
// The implementation is incomplete in that the returned value is a single certificate,
|
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of ACME spec.
|
||||||
// computed only for Z0 of the key authorization. ACME CAs are expected to update
|
|
||||||
// their implementations to use the newer version, TLS-SNI-02.
|
|
||||||
// For more details on TLS-SNI-01 see https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.3.
|
|
||||||
//
|
|
||||||
// The token argument is a Challenge.Token value.
|
|
||||||
// If a WithKey option is provided, its private part signs the returned cert,
|
|
||||||
// and the public part is used to specify the signee.
|
|
||||||
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
|
||||||
//
|
|
||||||
// The returned certificate is valid for the next 24 hours and must be presented only when
|
|
||||||
// the server name of the TLS ClientHello matches exactly the returned name value.
|
|
||||||
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||||
ka, err := keyAuth(c.Key.Public(), token)
|
ka, err := keyAuth(c.Key.Public(), token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -579,17 +732,8 @@ func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tl
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
|
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
|
||||||
// Servers can present the certificate to validate the challenge and prove control
|
|
||||||
// over a domain name. For more details on TLS-SNI-02 see
|
|
||||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.3.
|
|
||||||
//
|
//
|
||||||
// The token argument is a Challenge.Token value.
|
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of ACME spec.
|
||||||
// If a WithKey option is provided, its private part signs the returned cert,
|
|
||||||
// and the public part is used to specify the signee.
|
|
||||||
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
|
||||||
//
|
|
||||||
// The returned certificate is valid for the next 24 hours and must be presented only when
|
|
||||||
// the server name in the TLS ClientHello matches exactly the returned name value.
|
|
||||||
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||||
b := sha256.Sum256([]byte(token))
|
b := sha256.Sum256([]byte(token))
|
||||||
h := hex.EncodeToString(b[:])
|
h := hex.EncodeToString(b[:])
|
||||||
|
@ -656,7 +800,7 @@ func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption)
|
||||||
return tlsChallengeCert([]string{domain}, newOpt)
|
return tlsChallengeCert([]string{domain}, newOpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// doReg sends all types of registration requests.
|
// doReg sends all types of registration requests the old way (pre-RFC world).
|
||||||
// The type of request is identified by typ argument, which is a "resource"
|
// The type of request is identified by typ argument, which is a "resource"
|
||||||
// in the ACME spec terms.
|
// in the ACME spec terms.
|
||||||
//
|
//
|
||||||
|
@ -675,7 +819,7 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
|
||||||
req.Contact = acct.Contact
|
req.Contact = acct.Contact
|
||||||
req.Agreement = acct.AgreedTerms
|
req.Agreement = acct.AgreedTerms
|
||||||
}
|
}
|
||||||
res, err := c.post(ctx, c.Key, url, req, wantStatus(
|
res, err := c.post(ctx, nil, url, req, wantStatus(
|
||||||
http.StatusOK, // updates and deletes
|
http.StatusOK, // updates and deletes
|
||||||
http.StatusCreated, // new account creation
|
http.StatusCreated, // new account creation
|
||||||
http.StatusAccepted, // Let's Encrypt divergent implementation
|
http.StatusAccepted, // Let's Encrypt divergent implementation
|
||||||
|
@ -714,12 +858,16 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
|
||||||
}
|
}
|
||||||
|
|
||||||
// popNonce returns a nonce value previously stored with c.addNonce
|
// popNonce returns a nonce value previously stored with c.addNonce
|
||||||
// or fetches a fresh one from a URL by issuing a HEAD request.
|
// or fetches a fresh one from c.dir.NonceURL.
|
||||||
// It first tries c.directoryURL() and then the provided url if the former fails.
|
// If NonceURL is empty, it first tries c.directoryURL() and, failing that,
|
||||||
|
// the provided url.
|
||||||
func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
|
func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
|
||||||
c.noncesMu.Lock()
|
c.noncesMu.Lock()
|
||||||
defer c.noncesMu.Unlock()
|
defer c.noncesMu.Unlock()
|
||||||
if len(c.nonces) == 0 {
|
if len(c.nonces) == 0 {
|
||||||
|
if c.dir != nil && c.dir.NonceURL != "" {
|
||||||
|
return c.fetchNonce(ctx, c.dir.NonceURL)
|
||||||
|
}
|
||||||
dirURL := c.directoryURL()
|
dirURL := c.directoryURL()
|
||||||
v, err := c.fetchNonce(ctx, dirURL)
|
v, err := c.fetchNonce(ctx, dirURL)
|
||||||
if err != nil && url != dirURL {
|
if err != nil && url != dirURL {
|
||||||
|
|
338
vendor/golang.org/x/crypto/acme/autocert/autocert.go
generated
vendored
338
vendor/golang.org/x/crypto/acme/autocert/autocert.go
generated
vendored
|
@ -32,8 +32,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/acme"
|
"golang.org/x/crypto/acme"
|
||||||
|
"golang.org/x/net/idna"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultACMEDirectory is the default ACME Directory URL used when the Manager's Client is nil.
|
||||||
|
const DefaultACMEDirectory = "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
|
|
||||||
// createCertRetryAfter is how much time to wait before removing a failed state
|
// createCertRetryAfter is how much time to wait before removing a failed state
|
||||||
// entry due to an unsuccessful createCert call.
|
// entry due to an unsuccessful createCert call.
|
||||||
// This is a variable instead of a const for testing.
|
// This is a variable instead of a const for testing.
|
||||||
|
@ -62,11 +66,17 @@ type HostPolicy func(ctx context.Context, host string) error
|
||||||
// HostWhitelist returns a policy where only the specified host names are allowed.
|
// HostWhitelist returns a policy where only the specified host names are allowed.
|
||||||
// Only exact matches are currently supported. Subdomains, regexp or wildcard
|
// Only exact matches are currently supported. Subdomains, regexp or wildcard
|
||||||
// will not match.
|
// will not match.
|
||||||
|
//
|
||||||
|
// Note that all hosts will be converted to Punycode via idna.Lookup.ToASCII so that
|
||||||
|
// Manager.GetCertificate can handle the Unicode IDN and mixedcase hosts correctly.
|
||||||
|
// Invalid hosts will be silently ignored.
|
||||||
func HostWhitelist(hosts ...string) HostPolicy {
|
func HostWhitelist(hosts ...string) HostPolicy {
|
||||||
whitelist := make(map[string]bool, len(hosts))
|
whitelist := make(map[string]bool, len(hosts))
|
||||||
for _, h := range hosts {
|
for _, h := range hosts {
|
||||||
|
if h, err := idna.Lookup.ToASCII(h); err == nil {
|
||||||
whitelist[h] = true
|
whitelist[h] = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return func(_ context.Context, host string) error {
|
return func(_ context.Context, host string) error {
|
||||||
if !whitelist[host] {
|
if !whitelist[host] {
|
||||||
return fmt.Errorf("acme/autocert: host %q not configured in HostWhitelist", host)
|
return fmt.Errorf("acme/autocert: host %q not configured in HostWhitelist", host)
|
||||||
|
@ -81,9 +91,9 @@ func defaultHostPolicy(context.Context, string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager is a stateful certificate manager built on top of acme.Client.
|
// Manager is a stateful certificate manager built on top of acme.Client.
|
||||||
// It obtains and refreshes certificates automatically using "tls-alpn-01",
|
// It obtains and refreshes certificates automatically using "tls-alpn-01"
|
||||||
// "tls-sni-01", "tls-sni-02" and "http-01" challenge types,
|
// or "http-01" challenge types, as well as providing them to a TLS server
|
||||||
// as well as providing them to a TLS server via tls.Config.
|
// via tls.Config.
|
||||||
//
|
//
|
||||||
// You must specify a cache implementation, such as DirCache,
|
// You must specify a cache implementation, such as DirCache,
|
||||||
// to reuse obtained certificates across program restarts.
|
// to reuse obtained certificates across program restarts.
|
||||||
|
@ -128,9 +138,10 @@ type Manager struct {
|
||||||
// Client is used to perform low-level operations, such as account registration
|
// Client is used to perform low-level operations, such as account registration
|
||||||
// and requesting new certificates.
|
// and requesting new certificates.
|
||||||
//
|
//
|
||||||
// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
|
// If Client is nil, a zero-value acme.Client is used with DefaultACMEDirectory
|
||||||
// as directory endpoint. If the Client.Key is nil, a new ECDSA P-256 key is
|
// as the directory endpoint.
|
||||||
// generated and, if Cache is not nil, stored in cache.
|
// If the Client.Key is nil, a new ECDSA P-256 key is generated and,
|
||||||
|
// if Cache is not nil, stored in cache.
|
||||||
//
|
//
|
||||||
// Mutating the field after the first call of GetCertificate method will have no effect.
|
// Mutating the field after the first call of GetCertificate method will have no effect.
|
||||||
Client *acme.Client
|
Client *acme.Client
|
||||||
|
@ -167,8 +178,8 @@ type Manager struct {
|
||||||
renewalMu sync.Mutex
|
renewalMu sync.Mutex
|
||||||
renewal map[certKey]*domainRenewal
|
renewal map[certKey]*domainRenewal
|
||||||
|
|
||||||
// tokensMu guards the rest of the fields: tryHTTP01, certTokens and httpTokens.
|
// challengeMu guards tryHTTP01, certTokens and httpTokens.
|
||||||
tokensMu sync.RWMutex
|
challengeMu sync.RWMutex
|
||||||
// tryHTTP01 indicates whether the Manager should try "http-01" challenge type
|
// tryHTTP01 indicates whether the Manager should try "http-01" challenge type
|
||||||
// during the authorization flow.
|
// during the authorization flow.
|
||||||
tryHTTP01 bool
|
tryHTTP01 bool
|
||||||
|
@ -177,12 +188,11 @@ type Manager struct {
|
||||||
// to be provisioned.
|
// to be provisioned.
|
||||||
// The entries are stored for the duration of the authorization flow.
|
// The entries are stored for the duration of the authorization flow.
|
||||||
httpTokens map[string][]byte
|
httpTokens map[string][]byte
|
||||||
// certTokens contains temporary certificates for tls-sni and tls-alpn challenges
|
// certTokens contains temporary certificates for tls-alpn-01 challenges
|
||||||
// and is keyed by token domain name, which matches server name of ClientHello.
|
// and is keyed by the domain name which matches the ClientHello server name.
|
||||||
// Keys always have ".acme.invalid" suffix for tls-sni. Otherwise, they are domain names
|
|
||||||
// for tls-alpn.
|
|
||||||
// The entries are stored for the duration of the authorization flow.
|
// The entries are stored for the duration of the authorization flow.
|
||||||
certTokens map[string]*tls.Certificate
|
certTokens map[string]*tls.Certificate
|
||||||
|
|
||||||
// nowFunc, if not nil, returns the current time. This may be set for
|
// nowFunc, if not nil, returns the current time. This may be set for
|
||||||
// testing purposes.
|
// testing purposes.
|
||||||
nowFunc func() time.Time
|
nowFunc func() time.Time
|
||||||
|
@ -219,7 +229,7 @@ func (m *Manager) TLSConfig() *tls.Config {
|
||||||
|
|
||||||
// GetCertificate implements the tls.Config.GetCertificate hook.
|
// GetCertificate implements the tls.Config.GetCertificate hook.
|
||||||
// It provides a TLS certificate for hello.ServerName host, including answering
|
// It provides a TLS certificate for hello.ServerName host, including answering
|
||||||
// tls-alpn-01 and *.acme.invalid (tls-sni-01 and tls-sni-02) challenges.
|
// tls-alpn-01 challenges.
|
||||||
// All other fields of hello are ignored.
|
// All other fields of hello are ignored.
|
||||||
//
|
//
|
||||||
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
|
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
|
||||||
|
@ -228,9 +238,7 @@ func (m *Manager) TLSConfig() *tls.Config {
|
||||||
// This does not affect cached certs. See HostPolicy field description for more details.
|
// This does not affect cached certs. See HostPolicy field description for more details.
|
||||||
//
|
//
|
||||||
// If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will
|
// If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will
|
||||||
// also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler
|
// also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler for http-01.
|
||||||
// for http-01. (The tls-sni-* challenges have been deprecated by popular ACME providers
|
|
||||||
// due to security issues in the ecosystem.)
|
|
||||||
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
if m.Prompt == nil {
|
if m.Prompt == nil {
|
||||||
return nil, errors.New("acme/autocert: Manager.Prompt not set")
|
return nil, errors.New("acme/autocert: Manager.Prompt not set")
|
||||||
|
@ -243,7 +251,17 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||||
if !strings.Contains(strings.Trim(name, "."), ".") {
|
if !strings.Contains(strings.Trim(name, "."), ".") {
|
||||||
return nil, errors.New("acme/autocert: server name component count invalid")
|
return nil, errors.New("acme/autocert: server name component count invalid")
|
||||||
}
|
}
|
||||||
if strings.ContainsAny(name, `+/\`) {
|
|
||||||
|
// Note that this conversion is necessary because some server names in the handshakes
|
||||||
|
// started by some clients (such as cURL) are not converted to Punycode, which will
|
||||||
|
// prevent us from obtaining certificates for them. In addition, we should also treat
|
||||||
|
// example.com and EXAMPLE.COM as equivalent and return the same certificate for them.
|
||||||
|
// Fortunately, this conversion also helped us deal with this kind of mixedcase problems.
|
||||||
|
//
|
||||||
|
// Due to the "σςΣ" problem (see https://unicode.org/faq/idn.html#22), we can't use
|
||||||
|
// idna.Punycode.ToASCII (or just idna.ToASCII) here.
|
||||||
|
name, err := idna.Lookup.ToASCII(name)
|
||||||
|
if err != nil {
|
||||||
return nil, errors.New("acme/autocert: server name contains invalid character")
|
return nil, errors.New("acme/autocert: server name contains invalid character")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,13 +270,10 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// Check whether this is a token cert requested for TLS-SNI or TLS-ALPN challenge.
|
// Check whether this is a token cert requested for TLS-ALPN challenge.
|
||||||
if wantsTokenCert(hello) {
|
if wantsTokenCert(hello) {
|
||||||
m.tokensMu.RLock()
|
m.challengeMu.RLock()
|
||||||
defer m.tokensMu.RUnlock()
|
defer m.challengeMu.RUnlock()
|
||||||
// It's ok to use the same token cert key for both tls-sni and tls-alpn
|
|
||||||
// because there's always at most 1 token cert per on-going domain authorization.
|
|
||||||
// See m.verify for details.
|
|
||||||
if cert := m.certTokens[name]; cert != nil {
|
if cert := m.certTokens[name]; cert != nil {
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
@ -301,8 +316,7 @@ func wantsTokenCert(hello *tls.ClientHelloInfo) bool {
|
||||||
if len(hello.SupportedProtos) == 1 && hello.SupportedProtos[0] == acme.ALPNProto {
|
if len(hello.SupportedProtos) == 1 && hello.SupportedProtos[0] == acme.ALPNProto {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// tls-sni-xx
|
return false
|
||||||
return strings.HasSuffix(hello.ServerName, ".acme.invalid")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func supportsECDSA(hello *tls.ClientHelloInfo) bool {
|
func supportsECDSA(hello *tls.ClientHelloInfo) bool {
|
||||||
|
@ -367,8 +381,8 @@ func supportsECDSA(hello *tls.ClientHelloInfo) bool {
|
||||||
// If HTTPHandler is never called, the Manager will only use the "tls-alpn-01"
|
// If HTTPHandler is never called, the Manager will only use the "tls-alpn-01"
|
||||||
// challenge for domain verification.
|
// challenge for domain verification.
|
||||||
func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler {
|
func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler {
|
||||||
m.tokensMu.Lock()
|
m.challengeMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.challengeMu.Unlock()
|
||||||
m.tryHTTP01 = true
|
m.tryHTTP01 = true
|
||||||
|
|
||||||
if fallback == nil {
|
if fallback == nil {
|
||||||
|
@ -631,71 +645,64 @@ func (m *Manager) certState(ck certKey) (*certState, error) {
|
||||||
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
|
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
|
||||||
// The key argument is the certificate private key.
|
// The key argument is the certificate private key.
|
||||||
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck certKey) (der [][]byte, leaf *x509.Certificate, err error) {
|
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck certKey) (der [][]byte, leaf *x509.Certificate, err error) {
|
||||||
client, err := m.acmeClient(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := m.verify(ctx, client, ck.domain); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
csr, err := certRequest(key, ck.domain, m.ExtraExtensions)
|
csr, err := certRequest(key, ck.domain, m.ExtraExtensions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
der, _, err = client.CreateCert(ctx, csr, 0, true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
leaf, err = validCert(ck, der, key, m.now())
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return der, leaf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// revokePendingAuthz revokes all authorizations idenfied by the elements of uri slice.
|
|
||||||
// It ignores revocation errors.
|
|
||||||
func (m *Manager) revokePendingAuthz(ctx context.Context, uri []string) {
|
|
||||||
client, err := m.acmeClient(ctx)
|
client, err := m.acmeClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, nil, err
|
||||||
}
|
|
||||||
for _, u := range uri {
|
|
||||||
client.RevokeAuthorization(ctx, u)
|
|
||||||
}
|
}
|
||||||
|
dir, err := client.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify runs the identifier (domain) authorization flow
|
var chain [][]byte
|
||||||
|
switch {
|
||||||
|
// Pre-RFC legacy CA.
|
||||||
|
case dir.OrderURL == "":
|
||||||
|
if err := m.verify(ctx, client, ck.domain); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
der, _, err := client.CreateCert(ctx, csr, 0, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
chain = der
|
||||||
|
// RFC 8555 compliant CA.
|
||||||
|
default:
|
||||||
|
o, err := m.verifyRFC(ctx, client, ck.domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
der, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
chain = der
|
||||||
|
}
|
||||||
|
leaf, err = validCert(ck, chain, key, m.now())
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return chain, leaf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify runs the identifier (domain) pre-authorization flow for legacy CAs
|
||||||
// using each applicable ACME challenge type.
|
// using each applicable ACME challenge type.
|
||||||
func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
|
func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
|
||||||
// The list of challenge types we'll try to fulfill
|
// Remove all hanging authorizations to reduce rate limit quotas
|
||||||
// in this specific order.
|
// after we're done.
|
||||||
challengeTypes := []string{"tls-alpn-01", "tls-sni-02", "tls-sni-01"}
|
var authzURLs []string
|
||||||
m.tokensMu.RLock()
|
|
||||||
if m.tryHTTP01 {
|
|
||||||
challengeTypes = append(challengeTypes, "http-01")
|
|
||||||
}
|
|
||||||
m.tokensMu.RUnlock()
|
|
||||||
|
|
||||||
// Keep track of pending authzs and revoke the ones that did not validate.
|
|
||||||
pendingAuthzs := make(map[string]bool)
|
|
||||||
defer func() {
|
defer func() {
|
||||||
var uri []string
|
go m.deactivatePendingAuthz(authzURLs)
|
||||||
for k, pending := range pendingAuthzs {
|
|
||||||
if pending {
|
|
||||||
uri = append(uri, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(uri) > 0 {
|
|
||||||
// Use "detached" background context.
|
|
||||||
// The revocations need not happen in the current verification flow.
|
|
||||||
go m.revokePendingAuthz(context.Background(), uri)
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// errs accumulates challenge failure errors, printed if all fail
|
// errs accumulates challenge failure errors, printed if all fail
|
||||||
errs := make(map[*acme.Challenge]error)
|
errs := make(map[*acme.Challenge]error)
|
||||||
|
challengeTypes := m.supportedChallengeTypes()
|
||||||
var nextTyp int // challengeType index of the next challenge type to try
|
var nextTyp int // challengeType index of the next challenge type to try
|
||||||
for {
|
for {
|
||||||
// Start domain authorization and get the challenge.
|
// Start domain authorization and get the challenge.
|
||||||
|
@ -703,6 +710,7 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
authzURLs = append(authzURLs, authz.URI)
|
||||||
// No point in accepting challenges if the authorization status
|
// No point in accepting challenges if the authorization status
|
||||||
// is in a final state.
|
// is in a final state.
|
||||||
switch authz.Status {
|
switch authz.Status {
|
||||||
|
@ -712,8 +720,6 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
|
||||||
return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
|
return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingAuthzs[authz.URI] = true
|
|
||||||
|
|
||||||
// Pick the next preferred challenge.
|
// Pick the next preferred challenge.
|
||||||
var chal *acme.Challenge
|
var chal *acme.Challenge
|
||||||
for chal == nil && nextTyp < len(challengeTypes) {
|
for chal == nil && nextTyp < len(challengeTypes) {
|
||||||
|
@ -743,11 +749,126 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
|
||||||
errs[chal] = err
|
errs[chal] = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
delete(pendingAuthzs, authz.URI)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verifyRFC runs the identifier (domain) order-based authorization flow for RFC compliant CAs
|
||||||
|
// using each applicable ACME challenge type.
|
||||||
|
func (m *Manager) verifyRFC(ctx context.Context, client *acme.Client, domain string) (*acme.Order, error) {
|
||||||
|
// Try each supported challenge type starting with a new order each time.
|
||||||
|
// The nextTyp index of the next challenge type to try is shared across
|
||||||
|
// all order authorizations: if we've tried a challenge type once and it didn't work,
|
||||||
|
// it will most likely not work on another order's authorization either.
|
||||||
|
challengeTypes := m.supportedChallengeTypes()
|
||||||
|
nextTyp := 0 // challengeTypes index
|
||||||
|
AuthorizeOrderLoop:
|
||||||
|
for {
|
||||||
|
o, err := client.AuthorizeOrder(ctx, acme.DomainIDs(domain))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Remove all hanging authorizations to reduce rate limit quotas
|
||||||
|
// after we're done.
|
||||||
|
defer func(urls []string) {
|
||||||
|
go m.deactivatePendingAuthz(urls)
|
||||||
|
}(o.AuthzURLs)
|
||||||
|
|
||||||
|
// Check if there's actually anything we need to do.
|
||||||
|
switch o.Status {
|
||||||
|
case acme.StatusReady:
|
||||||
|
// Already authorized.
|
||||||
|
return o, nil
|
||||||
|
case acme.StatusPending:
|
||||||
|
// Continue normal Order-based flow.
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("acme/autocert: invalid new order status %q; order URL: %q", o.Status, o.URI)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Satisfy all pending authorizations.
|
||||||
|
for _, zurl := range o.AuthzURLs {
|
||||||
|
z, err := client.GetAuthorization(ctx, zurl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if z.Status != acme.StatusPending {
|
||||||
|
// We are interested only in pending authorizations.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Pick the next preferred challenge.
|
||||||
|
var chal *acme.Challenge
|
||||||
|
for chal == nil && nextTyp < len(challengeTypes) {
|
||||||
|
chal = pickChallenge(challengeTypes[nextTyp], z.Challenges)
|
||||||
|
nextTyp++
|
||||||
|
}
|
||||||
|
if chal == nil {
|
||||||
|
return nil, fmt.Errorf("acme/autocert: unable to satisfy %q for domain %q: no viable challenge type found", z.URI, domain)
|
||||||
|
}
|
||||||
|
// Respond to the challenge and wait for validation result.
|
||||||
|
cleanup, err := m.fulfill(ctx, client, chal, domain)
|
||||||
|
if err != nil {
|
||||||
|
continue AuthorizeOrderLoop
|
||||||
|
}
|
||||||
|
defer cleanup()
|
||||||
|
if _, err := client.Accept(ctx, chal); err != nil {
|
||||||
|
continue AuthorizeOrderLoop
|
||||||
|
}
|
||||||
|
if _, err := client.WaitAuthorization(ctx, z.URI); err != nil {
|
||||||
|
continue AuthorizeOrderLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All authorizations are satisfied.
|
||||||
|
// Wait for the CA to update the order status.
|
||||||
|
o, err = client.WaitOrder(ctx, o.URI)
|
||||||
|
if err != nil {
|
||||||
|
continue AuthorizeOrderLoop
|
||||||
|
}
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge {
|
||||||
|
for _, c := range chal {
|
||||||
|
if c.Type == typ {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) supportedChallengeTypes() []string {
|
||||||
|
m.challengeMu.RLock()
|
||||||
|
defer m.challengeMu.RUnlock()
|
||||||
|
typ := []string{"tls-alpn-01"}
|
||||||
|
if m.tryHTTP01 {
|
||||||
|
typ = append(typ, "http-01")
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// deactivatePendingAuthz relinquishes all authorizations identified by the elements
|
||||||
|
// of the provided uri slice which are in "pending" state.
|
||||||
|
// It ignores revocation errors.
|
||||||
|
//
|
||||||
|
// deactivatePendingAuthz takes no context argument and instead runs with its own
|
||||||
|
// "detached" context because deactivations are done in a goroutine separate from
|
||||||
|
// that of the main issuance or renewal flow.
|
||||||
|
func (m *Manager) deactivatePendingAuthz(uri []string) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
client, err := m.acmeClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, u := range uri {
|
||||||
|
z, err := client.GetAuthorization(ctx, u)
|
||||||
|
if err == nil && z.Status == acme.StatusPending {
|
||||||
|
client.RevokeAuthorization(ctx, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fulfill provisions a response to the challenge chal.
|
// fulfill provisions a response to the challenge chal.
|
||||||
// The cleanup is non-nil only if provisioning succeeded.
|
// The cleanup is non-nil only if provisioning succeeded.
|
||||||
func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge, domain string) (cleanup func(), err error) {
|
func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge, domain string) (cleanup func(), err error) {
|
||||||
|
@ -759,20 +880,6 @@ func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.C
|
||||||
}
|
}
|
||||||
m.putCertToken(ctx, domain, &cert)
|
m.putCertToken(ctx, domain, &cert)
|
||||||
return func() { go m.deleteCertToken(domain) }, nil
|
return func() { go m.deleteCertToken(domain) }, nil
|
||||||
case "tls-sni-01":
|
|
||||||
cert, name, err := client.TLSSNI01ChallengeCert(chal.Token)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m.putCertToken(ctx, name, &cert)
|
|
||||||
return func() { go m.deleteCertToken(name) }, nil
|
|
||||||
case "tls-sni-02":
|
|
||||||
cert, name, err := client.TLSSNI02ChallengeCert(chal.Token)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m.putCertToken(ctx, name, &cert)
|
|
||||||
return func() { go m.deleteCertToken(name) }, nil
|
|
||||||
case "http-01":
|
case "http-01":
|
||||||
resp, err := client.HTTP01ChallengeResponse(chal.Token)
|
resp, err := client.HTTP01ChallengeResponse(chal.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -785,20 +892,11 @@ func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.C
|
||||||
return nil, fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
|
return nil, fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge {
|
|
||||||
for _, c := range chal {
|
|
||||||
if c.Type == typ {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// putCertToken stores the token certificate with the specified name
|
// putCertToken stores the token certificate with the specified name
|
||||||
// in both m.certTokens map and m.Cache.
|
// in both m.certTokens map and m.Cache.
|
||||||
func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) {
|
func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) {
|
||||||
m.tokensMu.Lock()
|
m.challengeMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.challengeMu.Unlock()
|
||||||
if m.certTokens == nil {
|
if m.certTokens == nil {
|
||||||
m.certTokens = make(map[string]*tls.Certificate)
|
m.certTokens = make(map[string]*tls.Certificate)
|
||||||
}
|
}
|
||||||
|
@ -809,8 +907,8 @@ func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certi
|
||||||
// deleteCertToken removes the token certificate with the specified name
|
// deleteCertToken removes the token certificate with the specified name
|
||||||
// from both m.certTokens map and m.Cache.
|
// from both m.certTokens map and m.Cache.
|
||||||
func (m *Manager) deleteCertToken(name string) {
|
func (m *Manager) deleteCertToken(name string) {
|
||||||
m.tokensMu.Lock()
|
m.challengeMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.challengeMu.Unlock()
|
||||||
delete(m.certTokens, name)
|
delete(m.certTokens, name)
|
||||||
if m.Cache != nil {
|
if m.Cache != nil {
|
||||||
ck := certKey{domain: name, isToken: true}
|
ck := certKey{domain: name, isToken: true}
|
||||||
|
@ -821,8 +919,8 @@ func (m *Manager) deleteCertToken(name string) {
|
||||||
// httpToken retrieves an existing http-01 token value from an in-memory map
|
// httpToken retrieves an existing http-01 token value from an in-memory map
|
||||||
// or the optional cache.
|
// or the optional cache.
|
||||||
func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, error) {
|
func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, error) {
|
||||||
m.tokensMu.RLock()
|
m.challengeMu.RLock()
|
||||||
defer m.tokensMu.RUnlock()
|
defer m.challengeMu.RUnlock()
|
||||||
if v, ok := m.httpTokens[tokenPath]; ok {
|
if v, ok := m.httpTokens[tokenPath]; ok {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
@ -837,8 +935,8 @@ func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, erro
|
||||||
//
|
//
|
||||||
// It ignores any error returned from Cache.Put.
|
// It ignores any error returned from Cache.Put.
|
||||||
func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) {
|
func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) {
|
||||||
m.tokensMu.Lock()
|
m.challengeMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.challengeMu.Unlock()
|
||||||
if m.httpTokens == nil {
|
if m.httpTokens == nil {
|
||||||
m.httpTokens = make(map[string][]byte)
|
m.httpTokens = make(map[string][]byte)
|
||||||
}
|
}
|
||||||
|
@ -854,8 +952,8 @@ func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) {
|
||||||
//
|
//
|
||||||
// If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout.
|
// If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout.
|
||||||
func (m *Manager) deleteHTTPToken(tokenPath string) {
|
func (m *Manager) deleteHTTPToken(tokenPath string) {
|
||||||
m.tokensMu.Lock()
|
m.challengeMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.challengeMu.Unlock()
|
||||||
delete(m.httpTokens, tokenPath)
|
delete(m.httpTokens, tokenPath)
|
||||||
if m.Cache != nil {
|
if m.Cache != nil {
|
||||||
m.Cache.Delete(context.Background(), httpTokenCacheKey(tokenPath))
|
m.Cache.Delete(context.Background(), httpTokenCacheKey(tokenPath))
|
||||||
|
@ -954,7 +1052,7 @@ func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
|
||||||
|
|
||||||
client := m.Client
|
client := m.Client
|
||||||
if client == nil {
|
if client == nil {
|
||||||
client = &acme.Client{DirectoryURL: acme.LetsEncryptURL}
|
client = &acme.Client{DirectoryURL: DefaultACMEDirectory}
|
||||||
}
|
}
|
||||||
if client.Key == nil {
|
if client.Key == nil {
|
||||||
var err error
|
var err error
|
||||||
|
@ -963,20 +1061,32 @@ func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if client.UserAgent == "" {
|
||||||
|
client.UserAgent = "autocert"
|
||||||
|
}
|
||||||
var contact []string
|
var contact []string
|
||||||
if m.Email != "" {
|
if m.Email != "" {
|
||||||
contact = []string{"mailto:" + m.Email}
|
contact = []string{"mailto:" + m.Email}
|
||||||
}
|
}
|
||||||
a := &acme.Account{Contact: contact}
|
a := &acme.Account{Contact: contact}
|
||||||
_, err := client.Register(ctx, a, m.Prompt)
|
_, err := client.Register(ctx, a, m.Prompt)
|
||||||
if ae, ok := err.(*acme.Error); err == nil || ok && ae.StatusCode == http.StatusConflict {
|
if err == nil || isAccountAlreadyExist(err) {
|
||||||
// conflict indicates the key is already registered
|
|
||||||
m.client = client
|
m.client = client
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
return m.client, err
|
return m.client, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isAccountAlreadyExist reports whether the err, as returned from acme.Client.Register,
|
||||||
|
// indicates the account has already been registered.
|
||||||
|
func isAccountAlreadyExist(err error) bool {
|
||||||
|
if err == acme.ErrAccountAlreadyExists {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ae, ok := err.(*acme.Error)
|
||||||
|
return ok && ae.StatusCode == http.StatusConflict
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) hostPolicy() HostPolicy {
|
func (m *Manager) hostPolicy() HostPolicy {
|
||||||
if m.HostPolicy != nil {
|
if m.HostPolicy != nil {
|
||||||
return m.HostPolicy
|
return m.HostPolicy
|
||||||
|
|
8
vendor/golang.org/x/crypto/acme/autocert/cache.go
generated
vendored
8
vendor/golang.org/x/crypto/acme/autocert/cache.go
generated
vendored
|
@ -77,6 +77,7 @@ func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
|
||||||
if tmp, err = d.writeTempFile(name, data); err != nil {
|
if tmp, err = d.writeTempFile(name, data); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer os.Remove(tmp)
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
// Don't overwrite the file if the context was canceled.
|
// Don't overwrite the file if the context was canceled.
|
||||||
|
@ -116,12 +117,17 @@ func (d DirCache) Delete(ctx context.Context, name string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeTempFile writes b to a temporary file, closes the file and returns its path.
|
// writeTempFile writes b to a temporary file, closes the file and returns its path.
|
||||||
func (d DirCache) writeTempFile(prefix string, b []byte) (string, error) {
|
func (d DirCache) writeTempFile(prefix string, b []byte) (name string, reterr error) {
|
||||||
// TempFile uses 0600 permissions
|
// TempFile uses 0600 permissions
|
||||||
f, err := ioutil.TempFile(string(d), prefix)
|
f, err := ioutil.TempFile(string(d), prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if reterr != nil {
|
||||||
|
os.Remove(f.Name())
|
||||||
|
}
|
||||||
|
}()
|
||||||
if _, err := f.Write(b); err != nil {
|
if _, err := f.Write(b); err != nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
return "", err
|
return "", err
|
||||||
|
|
46
vendor/golang.org/x/crypto/acme/http.go
generated
vendored
46
vendor/golang.org/x/crypto/acme/http.go
generated
vendored
|
@ -155,8 +155,16 @@ func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Respons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// postAsGet is POST-as-GET, a replacement for GET in RFC8555
|
||||||
|
// as described in https://tools.ietf.org/html/rfc8555#section-6.3.
|
||||||
|
// It makes a POST request in KID form with zero JWS payload.
|
||||||
|
// See nopayload doc comments in jws.go.
|
||||||
|
func (c *Client) postAsGet(ctx context.Context, url string, ok resOkay) (*http.Response, error) {
|
||||||
|
return c.post(ctx, nil, url, noPayload, ok)
|
||||||
|
}
|
||||||
|
|
||||||
// post issues a signed POST request in JWS format using the provided key
|
// post issues a signed POST request in JWS format using the provided key
|
||||||
// to the specified URL.
|
// to the specified URL. If key is nil, c.Key is used instead.
|
||||||
// It returns a non-error value only when ok reports true.
|
// It returns a non-error value only when ok reports true.
|
||||||
//
|
//
|
||||||
// post retries unsuccessful attempts according to c.RetryBackoff
|
// post retries unsuccessful attempts according to c.RetryBackoff
|
||||||
|
@ -193,14 +201,28 @@ func (c *Client) post(ctx context.Context, key crypto.Signer, url string, body i
|
||||||
}
|
}
|
||||||
|
|
||||||
// postNoRetry signs the body with the given key and POSTs it to the provided url.
|
// postNoRetry signs the body with the given key and POSTs it to the provided url.
|
||||||
// The body argument must be JSON-serializable.
|
|
||||||
// It is used by c.post to retry unsuccessful attempts.
|
// It is used by c.post to retry unsuccessful attempts.
|
||||||
|
// The body argument must be JSON-serializable.
|
||||||
|
//
|
||||||
|
// If key argument is nil, c.Key is used to sign the request.
|
||||||
|
// If key argument is nil and c.accountKID returns a non-zero keyID,
|
||||||
|
// the request is sent in KID form. Otherwise, JWK form is used.
|
||||||
|
//
|
||||||
|
// In practice, when interfacing with RFC-compliant CAs most requests are sent in KID form
|
||||||
|
// and JWK is used only when KID is unavailable: new account endpoint and certificate
|
||||||
|
// revocation requests authenticated by a cert key.
|
||||||
|
// See jwsEncodeJSON for other details.
|
||||||
func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, *http.Request, error) {
|
func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, *http.Request, error) {
|
||||||
|
kid := noKeyID
|
||||||
|
if key == nil {
|
||||||
|
key = c.Key
|
||||||
|
kid = c.accountKID(ctx)
|
||||||
|
}
|
||||||
nonce, err := c.popNonce(ctx, url)
|
nonce, err := c.popNonce(ctx, url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
b, err := jwsEncodeJSON(body, key, nonce)
|
b, err := jwsEncodeJSON(body, key, kid, nonce, url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -219,6 +241,7 @@ func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string,
|
||||||
|
|
||||||
// doNoRetry issues a request req, replacing its context (if any) with ctx.
|
// doNoRetry issues a request req, replacing its context (if any) with ctx.
|
||||||
func (c *Client) doNoRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
|
func (c *Client) doNoRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
|
||||||
|
req.Header.Set("User-Agent", c.userAgent())
|
||||||
res, err := c.httpClient().Do(req.WithContext(ctx))
|
res, err := c.httpClient().Do(req.WithContext(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
select {
|
select {
|
||||||
|
@ -243,6 +266,23 @@ func (c *Client) httpClient() *http.Client {
|
||||||
return http.DefaultClient
|
return http.DefaultClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// packageVersion is the version of the module that contains this package, for
|
||||||
|
// sending as part of the User-Agent header. It's set in version_go112.go.
|
||||||
|
var packageVersion string
|
||||||
|
|
||||||
|
// userAgent returns the User-Agent header value. It includes the package name,
|
||||||
|
// the module version (if available), and the c.UserAgent value (if set).
|
||||||
|
func (c *Client) userAgent() string {
|
||||||
|
ua := "golang.org/x/crypto/acme"
|
||||||
|
if packageVersion != "" {
|
||||||
|
ua += "@" + packageVersion
|
||||||
|
}
|
||||||
|
if c.UserAgent != "" {
|
||||||
|
ua = c.UserAgent + " " + ua
|
||||||
|
}
|
||||||
|
return ua
|
||||||
|
}
|
||||||
|
|
||||||
// isBadNonce reports whether err is an ACME "badnonce" error.
|
// isBadNonce reports whether err is an ACME "badnonce" error.
|
||||||
func isBadNonce(err error) bool {
|
func isBadNonce(err error) bool {
|
||||||
// According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
|
// According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
|
||||||
|
|
71
vendor/golang.org/x/crypto/acme/jws.go
generated
vendored
71
vendor/golang.org/x/crypto/acme/jws.go
generated
vendored
|
@ -11,31 +11,60 @@ import (
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
_ "crypto/sha512" // need for EC keys
|
_ "crypto/sha512" // need for EC keys
|
||||||
|
"encoding/asn1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// keyID is the account identity provided by a CA during registration.
|
||||||
|
type keyID string
|
||||||
|
|
||||||
|
// noKeyID indicates that jwsEncodeJSON should compute and use JWK instead of a KID.
|
||||||
|
// See jwsEncodeJSON for details.
|
||||||
|
const noKeyID = keyID("")
|
||||||
|
|
||||||
|
// noPayload indicates jwsEncodeJSON will encode zero-length octet string
|
||||||
|
// in a JWS request. This is called POST-as-GET in RFC 8555 and is used to make
|
||||||
|
// authenticated GET requests via POSTing with an empty payload.
|
||||||
|
// See https://tools.ietf.org/html/rfc8555#section-6.3 for more details.
|
||||||
|
const noPayload = ""
|
||||||
|
|
||||||
// jwsEncodeJSON signs claimset using provided key and a nonce.
|
// jwsEncodeJSON signs claimset using provided key and a nonce.
|
||||||
// The result is serialized in JSON format.
|
// The result is serialized in JSON format containing either kid or jwk
|
||||||
|
// fields based on the provided keyID value.
|
||||||
|
//
|
||||||
|
// If kid is non-empty, its quoted value is inserted in the protected head
|
||||||
|
// as "kid" field value. Otherwise, JWK is computed using jwkEncode and inserted
|
||||||
|
// as "jwk" field value. The "jwk" and "kid" fields are mutually exclusive.
|
||||||
|
//
|
||||||
// See https://tools.ietf.org/html/rfc7515#section-7.
|
// See https://tools.ietf.org/html/rfc7515#section-7.
|
||||||
func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byte, error) {
|
func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid keyID, nonce, url string) ([]byte, error) {
|
||||||
jwk, err := jwkEncode(key.Public())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
alg, sha := jwsHasher(key.Public())
|
alg, sha := jwsHasher(key.Public())
|
||||||
if alg == "" || !sha.Available() {
|
if alg == "" || !sha.Available() {
|
||||||
return nil, ErrUnsupportedKey
|
return nil, ErrUnsupportedKey
|
||||||
}
|
}
|
||||||
phead := fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q}`, alg, jwk, nonce)
|
var phead string
|
||||||
|
switch kid {
|
||||||
|
case noKeyID:
|
||||||
|
jwk, err := jwkEncode(key.Public())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
phead = fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q,"url":%q}`, alg, jwk, nonce, url)
|
||||||
|
default:
|
||||||
|
phead = fmt.Sprintf(`{"alg":%q,"kid":%q,"nonce":%q,"url":%q}`, alg, kid, nonce, url)
|
||||||
|
}
|
||||||
phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
|
phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
|
||||||
|
var payload string
|
||||||
|
if claimset != noPayload {
|
||||||
cs, err := json.Marshal(claimset)
|
cs, err := json.Marshal(claimset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
payload := base64.RawURLEncoding.EncodeToString(cs)
|
payload = base64.RawURLEncoding.EncodeToString(cs)
|
||||||
|
}
|
||||||
hash := sha.New()
|
hash := sha.New()
|
||||||
hash.Write([]byte(phead + "." + payload))
|
hash.Write([]byte(phead + "." + payload))
|
||||||
sig, err := jwsSign(key, sha, hash.Sum(nil))
|
sig, err := jwsSign(key, sha, hash.Sum(nil))
|
||||||
|
@ -98,21 +127,23 @@ func jwkEncode(pub crypto.PublicKey) (string, error) {
|
||||||
|
|
||||||
// jwsSign signs the digest using the given key.
|
// jwsSign signs the digest using the given key.
|
||||||
// The hash is unused for ECDSA keys.
|
// The hash is unused for ECDSA keys.
|
||||||
//
|
|
||||||
// Note: non-stdlib crypto.Signer implementations are expected to return
|
|
||||||
// the signature in the format as specified in RFC7518.
|
|
||||||
// See https://tools.ietf.org/html/rfc7518 for more details.
|
|
||||||
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
|
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
|
||||||
if key, ok := key.(*ecdsa.PrivateKey); ok {
|
switch pub := key.Public().(type) {
|
||||||
// The key.Sign method of ecdsa returns ASN1-encoded signature.
|
case *rsa.PublicKey:
|
||||||
// So, we use the package Sign function instead
|
return key.Sign(rand.Reader, digest, hash)
|
||||||
// to get R and S values directly and format the result accordingly.
|
case *ecdsa.PublicKey:
|
||||||
r, s, err := ecdsa.Sign(rand.Reader, key, digest)
|
sigASN1, err := key.Sign(rand.Reader, digest, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rb, sb := r.Bytes(), s.Bytes()
|
|
||||||
size := key.Params().BitSize / 8
|
var rs struct{ R, S *big.Int }
|
||||||
|
if _, err := asn1.Unmarshal(sigASN1, &rs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rb, sb := rs.R.Bytes(), rs.S.Bytes()
|
||||||
|
size := pub.Params().BitSize / 8
|
||||||
if size%8 > 0 {
|
if size%8 > 0 {
|
||||||
size++
|
size++
|
||||||
}
|
}
|
||||||
|
@ -121,7 +152,7 @@ func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error)
|
||||||
copy(sig[size*2-len(sb):], sb)
|
copy(sig[size*2-len(sb):], sb)
|
||||||
return sig, nil
|
return sig, nil
|
||||||
}
|
}
|
||||||
return key.Sign(rand.Reader, digest, hash)
|
return nil, ErrUnsupportedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// jwsHasher indicates suitable JWS algorithm name and a hash function
|
// jwsHasher indicates suitable JWS algorithm name and a hash function
|
||||||
|
|
392
vendor/golang.org/x/crypto/acme/rfc8555.go
generated
vendored
Normal file
392
vendor/golang.org/x/crypto/acme/rfc8555.go
generated
vendored
Normal file
|
@ -0,0 +1,392 @@
|
||||||
|
// Copyright 2019 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 acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeactivateReg permanently disables an existing account associated with c.Key.
|
||||||
|
// A deactivated account can no longer request certificate issuance or access
|
||||||
|
// resources related to the account, such as orders or authorizations.
|
||||||
|
//
|
||||||
|
// It only works with CAs implementing RFC 8555.
|
||||||
|
func (c *Client) DeactivateReg(ctx context.Context) error {
|
||||||
|
url := string(c.accountKID(ctx))
|
||||||
|
if url == "" {
|
||||||
|
return ErrNoAccount
|
||||||
|
}
|
||||||
|
req := json.RawMessage(`{"status": "deactivated"}`)
|
||||||
|
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerRFC is quivalent to c.Register but for CAs implementing RFC 8555.
|
||||||
|
// It expects c.Discover to have already been called.
|
||||||
|
// TODO: Implement externalAccountBinding.
|
||||||
|
func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
|
||||||
|
c.cacheMu.Lock() // guard c.kid access
|
||||||
|
defer c.cacheMu.Unlock()
|
||||||
|
|
||||||
|
req := struct {
|
||||||
|
TermsAgreed bool `json:"termsOfServiceAgreed,omitempty"`
|
||||||
|
Contact []string `json:"contact,omitempty"`
|
||||||
|
}{
|
||||||
|
Contact: acct.Contact,
|
||||||
|
}
|
||||||
|
if c.dir.Terms != "" {
|
||||||
|
req.TermsAgreed = prompt(c.dir.Terms)
|
||||||
|
}
|
||||||
|
res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(
|
||||||
|
http.StatusOK, // account with this key already registered
|
||||||
|
http.StatusCreated, // new account created
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer res.Body.Close()
|
||||||
|
a, err := responseAccount(res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Cache Account URL even if we return an error to the caller.
|
||||||
|
// It is by all means a valid and usable "kid" value for future requests.
|
||||||
|
c.kid = keyID(a.URI)
|
||||||
|
if res.StatusCode == http.StatusOK {
|
||||||
|
return nil, ErrAccountAlreadyExists
|
||||||
|
}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateGegRFC is equivalent to c.UpdateReg but for CAs implementing RFC 8555.
|
||||||
|
// It expects c.Discover to have already been called.
|
||||||
|
func (c *Client) updateRegRFC(ctx context.Context, a *Account) (*Account, error) {
|
||||||
|
url := string(c.accountKID(ctx))
|
||||||
|
if url == "" {
|
||||||
|
return nil, ErrNoAccount
|
||||||
|
}
|
||||||
|
req := struct {
|
||||||
|
Contact []string `json:"contact,omitempty"`
|
||||||
|
}{
|
||||||
|
Contact: a.Contact,
|
||||||
|
}
|
||||||
|
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
return responseAccount(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGegRFC is equivalent to c.GetReg but for CAs implementing RFC 8555.
|
||||||
|
// It expects c.Discover to have already been called.
|
||||||
|
func (c *Client) getRegRFC(ctx context.Context) (*Account, error) {
|
||||||
|
req := json.RawMessage(`{"onlyReturnExisting": true}`)
|
||||||
|
res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(http.StatusOK))
|
||||||
|
if e, ok := err.(*Error); ok && e.ProblemType == "urn:ietf:params:acme:error:accountDoesNotExist" {
|
||||||
|
return nil, ErrNoAccount
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer res.Body.Close()
|
||||||
|
return responseAccount(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseAccount(res *http.Response) (*Account, error) {
|
||||||
|
var v struct {
|
||||||
|
Status string
|
||||||
|
Contact []string
|
||||||
|
Orders string
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: invalid account response: %v", err)
|
||||||
|
}
|
||||||
|
return &Account{
|
||||||
|
URI: res.Header.Get("Location"),
|
||||||
|
Status: v.Status,
|
||||||
|
Contact: v.Contact,
|
||||||
|
OrdersURL: v.Orders,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthorizeOrder initiates the order-based application for certificate issuance,
|
||||||
|
// as opposed to pre-authorization in Authorize.
|
||||||
|
// It is only supported by CAs implementing RFC 8555.
|
||||||
|
//
|
||||||
|
// The caller then needs to fetch each authorization with GetAuthorization,
|
||||||
|
// identify those with StatusPending status and fulfill a challenge using Accept.
|
||||||
|
// Once all authorizations are satisfied, the caller will typically want to poll
|
||||||
|
// order status using WaitOrder until it's in StatusReady state.
|
||||||
|
// To finalize the order and obtain a certificate, the caller submits a CSR with CreateOrderCert.
|
||||||
|
func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderOption) (*Order, error) {
|
||||||
|
dir, err := c.Discover(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := struct {
|
||||||
|
Identifiers []wireAuthzID `json:"identifiers"`
|
||||||
|
NotBefore string `json:"notBefore,omitempty"`
|
||||||
|
NotAfter string `json:"notAfter,omitempty"`
|
||||||
|
}{}
|
||||||
|
for _, v := range id {
|
||||||
|
req.Identifiers = append(req.Identifiers, wireAuthzID{
|
||||||
|
Type: v.Type,
|
||||||
|
Value: v.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, o := range opt {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case orderNotBeforeOpt:
|
||||||
|
req.NotBefore = time.Time(o).Format(time.RFC3339)
|
||||||
|
case orderNotAfterOpt:
|
||||||
|
req.NotAfter = time.Time(o).Format(time.RFC3339)
|
||||||
|
default:
|
||||||
|
// Package's fault if we let this happen.
|
||||||
|
panic(fmt.Sprintf("unsupported order option type %T", o))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.post(ctx, nil, dir.OrderURL, req, wantStatus(http.StatusCreated))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
return responseOrder(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrder retrives an order identified by the given URL.
|
||||||
|
// For orders created with AuthorizeOrder, the url value is Order.URI.
|
||||||
|
//
|
||||||
|
// If a caller needs to poll an order until its status is final,
|
||||||
|
// see the WaitOrder method.
|
||||||
|
func (c *Client) GetOrder(ctx context.Context, url string) (*Order, error) {
|
||||||
|
if _, err := c.Discover(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
return responseOrder(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitOrder polls an order from the given URL until it is in one of the final states,
|
||||||
|
// StatusReady, StatusValid or StatusInvalid, the CA responded with a non-retryable error
|
||||||
|
// or the context is done.
|
||||||
|
//
|
||||||
|
// It returns a non-nil Order only if its Status is StatusReady or StatusValid.
|
||||||
|
// In all other cases WaitOrder returns an error.
|
||||||
|
// If the Status is StatusInvalid, the returned error is of type *OrderError.
|
||||||
|
func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) {
|
||||||
|
if _, err := c.Discover(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
o, err := responseOrder(res)
|
||||||
|
res.Body.Close()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
// Skip and retry.
|
||||||
|
case o.Status == StatusInvalid:
|
||||||
|
return nil, &OrderError{OrderURL: o.URI, Status: o.Status}
|
||||||
|
case o.Status == StatusReady || o.Status == StatusValid:
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
d := retryAfter(res.Header.Get("Retry-After"))
|
||||||
|
if d == 0 {
|
||||||
|
// Default retry-after.
|
||||||
|
// Same reasoning as in WaitAuthorization.
|
||||||
|
d = time.Second
|
||||||
|
}
|
||||||
|
t := time.NewTimer(d)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Stop()
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-t.C:
|
||||||
|
// Retry.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseOrder(res *http.Response) (*Order, error) {
|
||||||
|
var v struct {
|
||||||
|
Status string
|
||||||
|
Expires time.Time
|
||||||
|
Identifiers []wireAuthzID
|
||||||
|
NotBefore time.Time
|
||||||
|
NotAfter time.Time
|
||||||
|
Error *wireError
|
||||||
|
Authorizations []string
|
||||||
|
Finalize string
|
||||||
|
Certificate string
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: error reading order: %v", err)
|
||||||
|
}
|
||||||
|
o := &Order{
|
||||||
|
URI: res.Header.Get("Location"),
|
||||||
|
Status: v.Status,
|
||||||
|
Expires: v.Expires,
|
||||||
|
NotBefore: v.NotBefore,
|
||||||
|
NotAfter: v.NotAfter,
|
||||||
|
AuthzURLs: v.Authorizations,
|
||||||
|
FinalizeURL: v.Finalize,
|
||||||
|
CertURL: v.Certificate,
|
||||||
|
}
|
||||||
|
for _, id := range v.Identifiers {
|
||||||
|
o.Identifiers = append(o.Identifiers, AuthzID{Type: id.Type, Value: id.Value})
|
||||||
|
}
|
||||||
|
if v.Error != nil {
|
||||||
|
o.Error = v.Error.error(nil /* headers */)
|
||||||
|
}
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrderCert submits the CSR (Certificate Signing Request) to a CA at the specified URL.
|
||||||
|
// The URL is the FinalizeURL field of an Order created with AuthorizeOrder.
|
||||||
|
//
|
||||||
|
// If the bundle argument is true, the returned value also contain the CA (issuer)
|
||||||
|
// certificate chain. Otherwise, only a leaf certificate is returned.
|
||||||
|
// The returned URL can be used to re-fetch the certificate using FetchCert.
|
||||||
|
//
|
||||||
|
// This method is only supported by CAs implementing RFC 8555. See CreateCert for pre-RFC CAs.
|
||||||
|
//
|
||||||
|
// CreateOrderCert returns an error if the CA's response is unreasonably large.
|
||||||
|
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
|
||||||
|
func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bundle bool) (der [][]byte, certURL string, err error) {
|
||||||
|
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC describes this as "finalize order" request.
|
||||||
|
req := struct {
|
||||||
|
CSR string `json:"csr"`
|
||||||
|
}{
|
||||||
|
CSR: base64.RawURLEncoding.EncodeToString(csr),
|
||||||
|
}
|
||||||
|
res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
o, err := responseOrder(res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for CA to issue the cert if they haven't.
|
||||||
|
if o.Status != StatusValid {
|
||||||
|
o, err = c.WaitOrder(ctx, o.URI)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
// The only acceptable status post finalize and WaitOrder is "valid".
|
||||||
|
if o.Status != StatusValid {
|
||||||
|
return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status}
|
||||||
|
}
|
||||||
|
crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle)
|
||||||
|
return crt, o.CertURL, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchCertRFC downloads issued certificate from the given URL.
|
||||||
|
// It expects the CA to respond with PEM-encoded certificate chain.
|
||||||
|
//
|
||||||
|
// The URL argument is the CertURL field of Order.
|
||||||
|
func (c *Client) fetchCertRFC(ctx context.Context, url string, bundle bool) ([][]byte, error) {
|
||||||
|
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
// Get all the bytes up to a sane maximum.
|
||||||
|
// Account very roughly for base64 overhead.
|
||||||
|
const max = maxCertChainSize + maxCertChainSize/33
|
||||||
|
b, err := ioutil.ReadAll(io.LimitReader(res.Body, max+1))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: fetch cert response stream: %v", err)
|
||||||
|
}
|
||||||
|
if len(b) > max {
|
||||||
|
return nil, errors.New("acme: certificate chain is too big")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode PEM chain.
|
||||||
|
var chain [][]byte
|
||||||
|
for {
|
||||||
|
var p *pem.Block
|
||||||
|
p, b = pem.Decode(b)
|
||||||
|
if p == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if p.Type != "CERTIFICATE" {
|
||||||
|
return nil, fmt.Errorf("acme: invalid PEM cert type %q", p.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
chain = append(chain, p.Bytes)
|
||||||
|
if !bundle {
|
||||||
|
return chain, nil
|
||||||
|
}
|
||||||
|
if len(chain) > maxChainLen {
|
||||||
|
return nil, errors.New("acme: certificate chain is too long")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(chain) == 0 {
|
||||||
|
return nil, errors.New("acme: certificate chain is empty")
|
||||||
|
}
|
||||||
|
return chain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sends a cert revocation request in either JWK form when key is non-nil or KID form otherwise.
|
||||||
|
func (c *Client) revokeCertRFC(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
|
||||||
|
req := &struct {
|
||||||
|
Cert string `json:"certificate"`
|
||||||
|
Reason int `json:"reason"`
|
||||||
|
}{
|
||||||
|
Cert: base64.RawURLEncoding.EncodeToString(cert),
|
||||||
|
Reason: int(reason),
|
||||||
|
}
|
||||||
|
res, err := c.post(ctx, key, c.dir.RevokeURL, req, wantStatus(http.StatusOK))
|
||||||
|
if err != nil {
|
||||||
|
if isAlreadyRevoked(err) {
|
||||||
|
// Assume it is not an error to revoke an already revoked cert.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAlreadyRevoked(err error) bool {
|
||||||
|
e, ok := err.(*Error)
|
||||||
|
return ok && e.ProblemType == "urn:ietf:params:acme:error:alreadyRevoked"
|
||||||
|
}
|
285
vendor/golang.org/x/crypto/acme/types.go
generated
vendored
285
vendor/golang.org/x/crypto/acme/types.go
generated
vendored
|
@ -14,14 +14,18 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ACME server response statuses used to describe Authorization and Challenge states.
|
// ACME status values of Account, Order, Authorization and Challenge objects.
|
||||||
|
// See https://tools.ietf.org/html/rfc8555#section-7.1.6 for details.
|
||||||
const (
|
const (
|
||||||
StatusUnknown = "unknown"
|
StatusDeactivated = "deactivated"
|
||||||
|
StatusExpired = "expired"
|
||||||
|
StatusInvalid = "invalid"
|
||||||
StatusPending = "pending"
|
StatusPending = "pending"
|
||||||
StatusProcessing = "processing"
|
StatusProcessing = "processing"
|
||||||
StatusValid = "valid"
|
StatusReady = "ready"
|
||||||
StatusInvalid = "invalid"
|
|
||||||
StatusRevoked = "revoked"
|
StatusRevoked = "revoked"
|
||||||
|
StatusUnknown = "unknown"
|
||||||
|
StatusValid = "valid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CRLReasonCode identifies the reason for a certificate revocation.
|
// CRLReasonCode identifies the reason for a certificate revocation.
|
||||||
|
@ -41,8 +45,17 @@ const (
|
||||||
CRLReasonAACompromise CRLReasonCode = 10
|
CRLReasonAACompromise CRLReasonCode = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
// ErrUnsupportedKey is returned when an unsupported key type is encountered.
|
// ErrUnsupportedKey is returned when an unsupported key type is encountered.
|
||||||
var ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported")
|
ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported")
|
||||||
|
|
||||||
|
// ErrAccountAlreadyExists indicates that the Client's key has already been registered
|
||||||
|
// with the CA. It is returned by Register method.
|
||||||
|
ErrAccountAlreadyExists = errors.New("acme: account already exists")
|
||||||
|
|
||||||
|
// ErrNoAccount indicates that the Client's key has not been registered with the CA.
|
||||||
|
ErrNoAccount = errors.New("acme: account does not exist")
|
||||||
|
)
|
||||||
|
|
||||||
// Error is an ACME error, defined in Problem Details for HTTP APIs doc
|
// Error is an ACME error, defined in Problem Details for HTTP APIs doc
|
||||||
// http://tools.ietf.org/html/draft-ietf-appsawg-http-problem.
|
// http://tools.ietf.org/html/draft-ietf-appsawg-http-problem.
|
||||||
|
@ -54,6 +67,12 @@ type Error struct {
|
||||||
ProblemType string
|
ProblemType string
|
||||||
// Detail is a human-readable explanation specific to this occurrence of the problem.
|
// Detail is a human-readable explanation specific to this occurrence of the problem.
|
||||||
Detail string
|
Detail string
|
||||||
|
// Instance indicates a URL that the client should direct a human user to visit
|
||||||
|
// in order for instructions on how to agree to the updated Terms of Service.
|
||||||
|
// In such an event CA sets StatusCode to 403, ProblemType to
|
||||||
|
// "urn:ietf:params:acme:error:userActionRequired" and a Link header with relation
|
||||||
|
// "terms-of-service" containing the latest TOS URL.
|
||||||
|
Instance string
|
||||||
// Header is the original server error response headers.
|
// Header is the original server error response headers.
|
||||||
// It may be nil.
|
// It may be nil.
|
||||||
Header http.Header
|
Header http.Header
|
||||||
|
@ -86,6 +105,21 @@ func (a *AuthorizationError) Error() string {
|
||||||
return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; "))
|
return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OrderError is returned from Client's order related methods.
|
||||||
|
// It indicates the order is unusable and the clients should start over with
|
||||||
|
// AuthorizeOrder.
|
||||||
|
//
|
||||||
|
// The clients can still fetch the order object from CA using GetOrder
|
||||||
|
// to inspect its state.
|
||||||
|
type OrderError struct {
|
||||||
|
OrderURL string
|
||||||
|
Status string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oe *OrderError) Error() string {
|
||||||
|
return fmt.Sprintf("acme: order %s status: %s", oe.OrderURL, oe.Status)
|
||||||
|
}
|
||||||
|
|
||||||
// RateLimit reports whether err represents a rate limit error and
|
// RateLimit reports whether err represents a rate limit error and
|
||||||
// any Retry-After duration returned by the server.
|
// any Retry-After duration returned by the server.
|
||||||
//
|
//
|
||||||
|
@ -108,49 +142,88 @@ func RateLimit(err error) (time.Duration, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account is a user account. It is associated with a private key.
|
// Account is a user account. It is associated with a private key.
|
||||||
|
// Non-RFC 8555 fields are empty when interfacing with a compliant CA.
|
||||||
type Account struct {
|
type Account struct {
|
||||||
// URI is the account unique ID, which is also a URL used to retrieve
|
// URI is the account unique ID, which is also a URL used to retrieve
|
||||||
// account data from the CA.
|
// account data from the CA.
|
||||||
|
// When interfacing with RFC 8555-compliant CAs, URI is the "kid" field
|
||||||
|
// value in JWS signed requests.
|
||||||
URI string
|
URI string
|
||||||
|
|
||||||
// Contact is a slice of contact info used during registration.
|
// Contact is a slice of contact info used during registration.
|
||||||
|
// See https://tools.ietf.org/html/rfc8555#section-7.3 for supported
|
||||||
|
// formats.
|
||||||
Contact []string
|
Contact []string
|
||||||
|
|
||||||
|
// Status indicates current account status as returned by the CA.
|
||||||
|
// Possible values are StatusValid, StatusDeactivated, and StatusRevoked.
|
||||||
|
Status string
|
||||||
|
|
||||||
|
// OrdersURL is a URL from which a list of orders submitted by this account
|
||||||
|
// can be fetched.
|
||||||
|
OrdersURL string
|
||||||
|
|
||||||
// The terms user has agreed to.
|
// The terms user has agreed to.
|
||||||
// A value not matching CurrentTerms indicates that the user hasn't agreed
|
// A value not matching CurrentTerms indicates that the user hasn't agreed
|
||||||
// to the actual Terms of Service of the CA.
|
// to the actual Terms of Service of the CA.
|
||||||
|
//
|
||||||
|
// It is non-RFC 8555 compliant. Package users can store the ToS they agree to
|
||||||
|
// during Client's Register call in the prompt callback function.
|
||||||
AgreedTerms string
|
AgreedTerms string
|
||||||
|
|
||||||
// Actual terms of a CA.
|
// Actual terms of a CA.
|
||||||
|
//
|
||||||
|
// It is non-RFC 8555 compliant. Use Directory's Terms field.
|
||||||
|
// When a CA updates their terms and requires an account agreement,
|
||||||
|
// a URL at which instructions to do so is available in Error's Instance field.
|
||||||
CurrentTerms string
|
CurrentTerms string
|
||||||
|
|
||||||
// Authz is the authorization URL used to initiate a new authz flow.
|
// Authz is the authorization URL used to initiate a new authz flow.
|
||||||
|
//
|
||||||
|
// It is non-RFC 8555 compliant. Use Directory's AuthzURL or OrderURL.
|
||||||
Authz string
|
Authz string
|
||||||
|
|
||||||
// Authorizations is a URI from which a list of authorizations
|
// Authorizations is a URI from which a list of authorizations
|
||||||
// granted to this account can be fetched via a GET request.
|
// granted to this account can be fetched via a GET request.
|
||||||
|
//
|
||||||
|
// It is non-RFC 8555 compliant and is obsoleted by OrdersURL.
|
||||||
Authorizations string
|
Authorizations string
|
||||||
|
|
||||||
// Certificates is a URI from which a list of certificates
|
// Certificates is a URI from which a list of certificates
|
||||||
// issued for this account can be fetched via a GET request.
|
// issued for this account can be fetched via a GET request.
|
||||||
|
//
|
||||||
|
// It is non-RFC 8555 compliant and is obsoleted by OrdersURL.
|
||||||
Certificates string
|
Certificates string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Directory is ACME server discovery data.
|
// Directory is ACME server discovery data.
|
||||||
|
// See https://tools.ietf.org/html/rfc8555#section-7.1.1 for more details.
|
||||||
type Directory struct {
|
type Directory struct {
|
||||||
// RegURL is an account endpoint URL, allowing for creating new
|
// NonceURL indicates an endpoint where to fetch fresh nonce values from.
|
||||||
// and modifying existing accounts.
|
NonceURL string
|
||||||
|
|
||||||
|
// RegURL is an account endpoint URL, allowing for creating new accounts.
|
||||||
|
// Pre-RFC 8555 CAs also allow modifying existing accounts at this URL.
|
||||||
RegURL string
|
RegURL string
|
||||||
|
|
||||||
// AuthzURL is used to initiate Identifier Authorization flow.
|
// OrderURL is used to initiate the certificate issuance flow
|
||||||
|
// as described in RFC 8555.
|
||||||
|
OrderURL string
|
||||||
|
|
||||||
|
// AuthzURL is used to initiate identifier pre-authorization flow.
|
||||||
|
// Empty string indicates the flow is unsupported by the CA.
|
||||||
AuthzURL string
|
AuthzURL string
|
||||||
|
|
||||||
// CertURL is a new certificate issuance endpoint URL.
|
// CertURL is a new certificate issuance endpoint URL.
|
||||||
|
// It is non-RFC 8555 compliant and is obsoleted by OrderURL.
|
||||||
CertURL string
|
CertURL string
|
||||||
|
|
||||||
// RevokeURL is used to initiate a certificate revocation flow.
|
// RevokeURL is used to initiate a certificate revocation flow.
|
||||||
RevokeURL string
|
RevokeURL string
|
||||||
|
|
||||||
|
// KeyChangeURL allows to perform account key rollover flow.
|
||||||
|
KeyChangeURL string
|
||||||
|
|
||||||
// Term is a URI identifying the current terms of service.
|
// Term is a URI identifying the current terms of service.
|
||||||
Terms string
|
Terms string
|
||||||
|
|
||||||
|
@ -162,44 +235,126 @@ type Directory struct {
|
||||||
// recognises as referring to itself for the purposes of CAA record validation
|
// recognises as referring to itself for the purposes of CAA record validation
|
||||||
// as defined in RFC6844.
|
// as defined in RFC6844.
|
||||||
CAA []string
|
CAA []string
|
||||||
|
|
||||||
|
// ExternalAccountRequired indicates that the CA requires for all account-related
|
||||||
|
// requests to include external account binding information.
|
||||||
|
ExternalAccountRequired bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Challenge encodes a returned CA challenge.
|
// rfcCompliant reports whether the ACME server implements RFC 8555.
|
||||||
// Its Error field may be non-nil if the challenge is part of an Authorization
|
// Note that some servers may have incomplete RFC implementation
|
||||||
// with StatusInvalid.
|
// even if the returned value is true.
|
||||||
type Challenge struct {
|
// If rfcCompliant reports false, the server most likely implements draft-02.
|
||||||
// Type is the challenge type, e.g. "http-01", "tls-sni-02", "dns-01".
|
func (d *Directory) rfcCompliant() bool {
|
||||||
Type string
|
return d.OrderURL != ""
|
||||||
|
}
|
||||||
|
|
||||||
// URI is where a challenge response can be posted to.
|
// Order represents a client's request for a certificate.
|
||||||
|
// It tracks the request flow progress through to issuance.
|
||||||
|
type Order struct {
|
||||||
|
// URI uniquely identifies an order.
|
||||||
URI string
|
URI string
|
||||||
|
|
||||||
// Token is a random value that uniquely identifies the challenge.
|
// Status represents the current status of the order.
|
||||||
Token string
|
// It indicates which action the client should take.
|
||||||
|
//
|
||||||
// Status identifies the status of this challenge.
|
// Possible values are StatusPending, StatusReady, StatusProcessing, StatusValid and StatusInvalid.
|
||||||
|
// Pending means the CA does not believe that the client has fulfilled the requirements.
|
||||||
|
// Ready indicates that the client has fulfilled all the requirements and can submit a CSR
|
||||||
|
// to obtain a certificate. This is done with Client's CreateOrderCert.
|
||||||
|
// Processing means the certificate is being issued.
|
||||||
|
// Valid indicates the CA has issued the certificate. It can be downloaded
|
||||||
|
// from the Order's CertURL. This is done with Client's FetchCert.
|
||||||
|
// Invalid means the certificate will not be issued. Users should consider this order
|
||||||
|
// abandoned.
|
||||||
Status string
|
Status string
|
||||||
|
|
||||||
// Error indicates the reason for an authorization failure
|
// Expires is the timestamp after which CA considers this order invalid.
|
||||||
// when this challenge was used.
|
Expires time.Time
|
||||||
// The type of a non-nil value is *Error.
|
|
||||||
Error error
|
// Identifiers contains all identifier objects which the order pertains to.
|
||||||
|
Identifiers []AuthzID
|
||||||
|
|
||||||
|
// NotBefore is the requested value of the notBefore field in the certificate.
|
||||||
|
NotBefore time.Time
|
||||||
|
|
||||||
|
// NotAfter is the requested value of the notAfter field in the certificate.
|
||||||
|
NotAfter time.Time
|
||||||
|
|
||||||
|
// AuthzURLs represents authorizations to complete before a certificate
|
||||||
|
// for identifiers specified in the order can be issued.
|
||||||
|
// It also contains unexpired authorizations that the client has completed
|
||||||
|
// in the past.
|
||||||
|
//
|
||||||
|
// Authorization objects can be fetched using Client's GetAuthorization method.
|
||||||
|
//
|
||||||
|
// The required authorizations are dictated by CA policies.
|
||||||
|
// There may not be a 1:1 relationship between the identifiers and required authorizations.
|
||||||
|
// Required authorizations can be identified by their StatusPending status.
|
||||||
|
//
|
||||||
|
// For orders in the StatusValid or StatusInvalid state these are the authorizations
|
||||||
|
// which were completed.
|
||||||
|
AuthzURLs []string
|
||||||
|
|
||||||
|
// FinalizeURL is the endpoint at which a CSR is submitted to obtain a certificate
|
||||||
|
// once all the authorizations are satisfied.
|
||||||
|
FinalizeURL string
|
||||||
|
|
||||||
|
// CertURL points to the certificate that has been issued in response to this order.
|
||||||
|
CertURL string
|
||||||
|
|
||||||
|
// The error that occurred while processing the order as received from a CA, if any.
|
||||||
|
Error *Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OrderOption allows customizing Client.AuthorizeOrder call.
|
||||||
|
type OrderOption interface {
|
||||||
|
privateOrderOpt()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithOrderNotBefore sets order's NotBefore field.
|
||||||
|
func WithOrderNotBefore(t time.Time) OrderOption {
|
||||||
|
return orderNotBeforeOpt(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithOrderNotAfter sets order's NotAfter field.
|
||||||
|
func WithOrderNotAfter(t time.Time) OrderOption {
|
||||||
|
return orderNotAfterOpt(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
type orderNotBeforeOpt time.Time
|
||||||
|
|
||||||
|
func (orderNotBeforeOpt) privateOrderOpt() {}
|
||||||
|
|
||||||
|
type orderNotAfterOpt time.Time
|
||||||
|
|
||||||
|
func (orderNotAfterOpt) privateOrderOpt() {}
|
||||||
|
|
||||||
// Authorization encodes an authorization response.
|
// Authorization encodes an authorization response.
|
||||||
type Authorization struct {
|
type Authorization struct {
|
||||||
// URI uniquely identifies a authorization.
|
// URI uniquely identifies a authorization.
|
||||||
URI string
|
URI string
|
||||||
|
|
||||||
// Status identifies the status of an authorization.
|
// Status is the current status of an authorization.
|
||||||
|
// Possible values are StatusPending, StatusValid, StatusInvalid, StatusDeactivated,
|
||||||
|
// StatusExpired and StatusRevoked.
|
||||||
Status string
|
Status string
|
||||||
|
|
||||||
// Identifier is what the account is authorized to represent.
|
// Identifier is what the account is authorized to represent.
|
||||||
Identifier AuthzID
|
Identifier AuthzID
|
||||||
|
|
||||||
|
// The timestamp after which the CA considers the authorization invalid.
|
||||||
|
Expires time.Time
|
||||||
|
|
||||||
|
// Wildcard is true for authorizations of a wildcard domain name.
|
||||||
|
Wildcard bool
|
||||||
|
|
||||||
// Challenges that the client needs to fulfill in order to prove possession
|
// Challenges that the client needs to fulfill in order to prove possession
|
||||||
// of the identifier (for pending authorizations).
|
// of the identifier (for pending authorizations).
|
||||||
// For final authorizations, the challenges that were used.
|
// For valid authorizations, the challenge that was validated.
|
||||||
|
// For invalid authorizations, the challenge that was attempted and failed.
|
||||||
|
//
|
||||||
|
// RFC 8555 compatible CAs require users to fuflfill only one of the challenges.
|
||||||
Challenges []*Challenge
|
Challenges []*Challenge
|
||||||
|
|
||||||
// A collection of sets of challenges, each of which would be sufficient
|
// A collection of sets of challenges, each of which would be sufficient
|
||||||
|
@ -207,24 +362,51 @@ type Authorization struct {
|
||||||
// Clients must complete a set of challenges that covers at least one set.
|
// Clients must complete a set of challenges that covers at least one set.
|
||||||
// Challenges are identified by their indices in the challenges array.
|
// Challenges are identified by their indices in the challenges array.
|
||||||
// If this field is empty, the client needs to complete all challenges.
|
// If this field is empty, the client needs to complete all challenges.
|
||||||
|
//
|
||||||
|
// This field is unused in RFC 8555.
|
||||||
Combinations [][]int
|
Combinations [][]int
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthzID is an identifier that an account is authorized to represent.
|
// AuthzID is an identifier that an account is authorized to represent.
|
||||||
type AuthzID struct {
|
type AuthzID struct {
|
||||||
Type string // The type of identifier, e.g. "dns".
|
Type string // The type of identifier, "dns" or "ip".
|
||||||
Value string // The identifier itself, e.g. "example.org".
|
Value string // The identifier itself, e.g. "example.org".
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DomainIDs creates a slice of AuthzID with "dns" identifier type.
|
||||||
|
func DomainIDs(names ...string) []AuthzID {
|
||||||
|
a := make([]AuthzID, len(names))
|
||||||
|
for i, v := range names {
|
||||||
|
a[i] = AuthzID{Type: "dns", Value: v}
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPIDs creates a slice of AuthzID with "ip" identifier type.
|
||||||
|
// Each element of addr is textual form of an address as defined
|
||||||
|
// in RFC1123 Section 2.1 for IPv4 and in RFC5952 Section 4 for IPv6.
|
||||||
|
func IPIDs(addr ...string) []AuthzID {
|
||||||
|
a := make([]AuthzID, len(addr))
|
||||||
|
for i, v := range addr {
|
||||||
|
a[i] = AuthzID{Type: "ip", Value: v}
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// wireAuthzID is ACME JSON representation of authorization identifier objects.
|
||||||
|
type wireAuthzID struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
// wireAuthz is ACME JSON representation of Authorization objects.
|
// wireAuthz is ACME JSON representation of Authorization objects.
|
||||||
type wireAuthz struct {
|
type wireAuthz struct {
|
||||||
|
Identifier wireAuthzID
|
||||||
Status string
|
Status string
|
||||||
|
Expires time.Time
|
||||||
|
Wildcard bool
|
||||||
Challenges []wireChallenge
|
Challenges []wireChallenge
|
||||||
Combinations [][]int
|
Combinations [][]int
|
||||||
Identifier struct {
|
|
||||||
Type string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (z *wireAuthz) authorization(uri string) *Authorization {
|
func (z *wireAuthz) authorization(uri string) *Authorization {
|
||||||
|
@ -232,8 +414,10 @@ func (z *wireAuthz) authorization(uri string) *Authorization {
|
||||||
URI: uri,
|
URI: uri,
|
||||||
Status: z.Status,
|
Status: z.Status,
|
||||||
Identifier: AuthzID{Type: z.Identifier.Type, Value: z.Identifier.Value},
|
Identifier: AuthzID{Type: z.Identifier.Type, Value: z.Identifier.Value},
|
||||||
Combinations: z.Combinations, // shallow copy
|
Expires: z.Expires,
|
||||||
|
Wildcard: z.Wildcard,
|
||||||
Challenges: make([]*Challenge, len(z.Challenges)),
|
Challenges: make([]*Challenge, len(z.Challenges)),
|
||||||
|
Combinations: z.Combinations, // shallow copy
|
||||||
}
|
}
|
||||||
for i, v := range z.Challenges {
|
for i, v := range z.Challenges {
|
||||||
a.Challenges[i] = v.challenge()
|
a.Challenges[i] = v.challenge()
|
||||||
|
@ -254,22 +438,55 @@ func (z *wireAuthz) error(uri string) *AuthorizationError {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Challenge encodes a returned CA challenge.
|
||||||
|
// Its Error field may be non-nil if the challenge is part of an Authorization
|
||||||
|
// with StatusInvalid.
|
||||||
|
type Challenge struct {
|
||||||
|
// Type is the challenge type, e.g. "http-01", "tls-alpn-01", "dns-01".
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// URI is where a challenge response can be posted to.
|
||||||
|
URI string
|
||||||
|
|
||||||
|
// Token is a random value that uniquely identifies the challenge.
|
||||||
|
Token string
|
||||||
|
|
||||||
|
// Status identifies the status of this challenge.
|
||||||
|
// In RFC 8555, possible values are StatusPending, StatusProcessing, StatusValid,
|
||||||
|
// and StatusInvalid.
|
||||||
|
Status string
|
||||||
|
|
||||||
|
// Validated is the time at which the CA validated this challenge.
|
||||||
|
// Always zero value in pre-RFC 8555.
|
||||||
|
Validated time.Time
|
||||||
|
|
||||||
|
// Error indicates the reason for an authorization failure
|
||||||
|
// when this challenge was used.
|
||||||
|
// The type of a non-nil value is *Error.
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
// wireChallenge is ACME JSON challenge representation.
|
// wireChallenge is ACME JSON challenge representation.
|
||||||
type wireChallenge struct {
|
type wireChallenge struct {
|
||||||
URI string `json:"uri"`
|
URL string `json:"url"` // RFC
|
||||||
|
URI string `json:"uri"` // pre-RFC
|
||||||
Type string
|
Type string
|
||||||
Token string
|
Token string
|
||||||
Status string
|
Status string
|
||||||
|
Validated time.Time
|
||||||
Error *wireError
|
Error *wireError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *wireChallenge) challenge() *Challenge {
|
func (c *wireChallenge) challenge() *Challenge {
|
||||||
v := &Challenge{
|
v := &Challenge{
|
||||||
URI: c.URI,
|
URI: c.URL,
|
||||||
Type: c.Type,
|
Type: c.Type,
|
||||||
Token: c.Token,
|
Token: c.Token,
|
||||||
Status: c.Status,
|
Status: c.Status,
|
||||||
}
|
}
|
||||||
|
if v.URI == "" {
|
||||||
|
v.URI = c.URI // c.URL was empty; use legacy
|
||||||
|
}
|
||||||
if v.Status == "" {
|
if v.Status == "" {
|
||||||
v.Status = StatusPending
|
v.Status = StatusPending
|
||||||
}
|
}
|
||||||
|
@ -285,6 +502,7 @@ type wireError struct {
|
||||||
Status int
|
Status int
|
||||||
Type string
|
Type string
|
||||||
Detail string
|
Detail string
|
||||||
|
Instance string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *wireError) error(h http.Header) *Error {
|
func (e *wireError) error(h http.Header) *Error {
|
||||||
|
@ -292,6 +510,7 @@ func (e *wireError) error(h http.Header) *Error {
|
||||||
StatusCode: e.Status,
|
StatusCode: e.Status,
|
||||||
ProblemType: e.Type,
|
ProblemType: e.Type,
|
||||||
Detail: e.Detail,
|
Detail: e.Detail,
|
||||||
|
Instance: e.Instance,
|
||||||
Header: h,
|
Header: h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
27
vendor/golang.org/x/crypto/acme/version_go112.go
generated
vendored
Normal file
27
vendor/golang.org/x/crypto/acme/version_go112.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright 2019 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.
|
||||||
|
|
||||||
|
// +build go1.12
|
||||||
|
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import "runtime/debug"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Set packageVersion if the binary was built in modules mode and x/crypto
|
||||||
|
// was not replaced with a different module.
|
||||||
|
info, ok := debug.ReadBuildInfo()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, m := range info.Deps {
|
||||||
|
if m.Path != "golang.org/x/crypto" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if m.Replace == nil {
|
||||||
|
packageVersion = m.Version
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
|
@ -124,7 +124,7 @@ github.com/yvasiyarov/go-metrics
|
||||||
github.com/yvasiyarov/gorelic
|
github.com/yvasiyarov/gorelic
|
||||||
# github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f
|
# github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f
|
||||||
github.com/yvasiyarov/newrelic_platform_go
|
github.com/yvasiyarov/newrelic_platform_go
|
||||||
# golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
# golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d
|
||||||
golang.org/x/crypto/acme
|
golang.org/x/crypto/acme
|
||||||
golang.org/x/crypto/acme/autocert
|
golang.org/x/crypto/acme/autocert
|
||||||
golang.org/x/crypto/bcrypt
|
golang.org/x/crypto/bcrypt
|
||||||
|
|
Loading…
Reference in a new issue