diff options
Diffstat (limited to 'vendor/golang.org/x/crypto')
19 files changed, 598 insertions, 162 deletions
diff --git a/vendor/golang.org/x/crypto/acme/acme.go b/vendor/golang.org/x/crypto/acme/acme.go index d650604d3..a7b6ce4e9 100644 --- a/vendor/golang.org/x/crypto/acme/acme.go +++ b/vendor/golang.org/x/crypto/acme/acme.go @@ -152,7 +152,7 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) { CAA []string `json:"caa-identities"` } } - if json.NewDecoder(res.Body).Decode(&v); err != nil { + if err := json.NewDecoder(res.Body).Decode(&v); err != nil { return Directory{}, err } c.dir = &Directory{ @@ -436,7 +436,7 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error { // // It returns a non-nil Authorization only if its Status is StatusValid. // In all other cases WaitAuthorization returns an error. -// If the Status is StatusInvalid, the returned error is ErrAuthorizationFailed. +// If the Status is StatusInvalid, the returned error is of type *AuthorizationError. func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) { sleep := sleeper(ctx) for { @@ -465,7 +465,7 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat return raw.authorization(url), nil } if raw.Status == StatusInvalid { - return nil, ErrAuthorizationFailed + return nil, raw.error(url) } if err := sleep(retry, 0); err != nil { return nil, err @@ -882,14 +882,8 @@ func responseError(resp *http.Response) error { // don't care if ReadAll returns an error: // json.Unmarshal will fail in that case anyway b, _ := ioutil.ReadAll(resp.Body) - e := struct { - Status int - Type string - Detail string - }{ - Status: resp.StatusCode, - } - if err := json.Unmarshal(b, &e); err != nil { + e := &wireError{Status: resp.StatusCode} + if err := json.Unmarshal(b, e); err != nil { // this is not a regular error response: // populate detail with anything we received, // e.Status will already contain HTTP response code value @@ -898,12 +892,7 @@ func responseError(resp *http.Response) error { e.Detail = resp.Status } } - return &Error{ - StatusCode: e.Status, - ProblemType: e.Type, - Detail: e.Detail, - Header: resp.Header, - } + return e.error(resp.Header) } // chainCert fetches CA certificate chain recursively by following "up" links. diff --git a/vendor/golang.org/x/crypto/acme/acme_test.go b/vendor/golang.org/x/crypto/acme/acme_test.go index 0210ce3df..a4d276db8 100644 --- a/vendor/golang.org/x/crypto/acme/acme_test.go +++ b/vendor/golang.org/x/crypto/acme/acme_test.go @@ -543,6 +543,9 @@ func TestWaitAuthorizationInvalid(t *testing.T) { if err == nil { t.Error("err is nil") } + if _, ok := err.(*AuthorizationError); !ok { + t.Errorf("err is %T; want *AuthorizationError", err) + } } } diff --git a/vendor/golang.org/x/crypto/acme/autocert/autocert.go b/vendor/golang.org/x/crypto/acme/autocert/autocert.go index 98842b457..a478eff54 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/autocert.go +++ b/vendor/golang.org/x/crypto/acme/autocert/autocert.go @@ -33,6 +33,12 @@ import ( "golang.org/x/crypto/acme" ) +// createCertRetryAfter is how much time to wait before removing a failed state +// entry due to an unsuccessful createCert call. +// This is a variable instead of a const for testing. +// TODO: Consider making it configurable or an exp backoff? +var createCertRetryAfter = time.Minute + // pseudoRand is safe for concurrent use. var pseudoRand *lockedMathRand @@ -170,6 +176,12 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, if name == "" { return nil, errors.New("acme/autocert: missing server name") } + if !strings.Contains(strings.Trim(name, "."), ".") { + return nil, errors.New("acme/autocert: server name component count invalid") + } + if strings.ContainsAny(name, `/\`) { + return nil, errors.New("acme/autocert: server name contains invalid character") + } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() @@ -244,6 +256,7 @@ func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, erro } // cacheGet always returns a valid certificate, or an error otherwise. +// If a cached certficate exists but is not valid, ErrCacheMiss is returned. func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate, error) { if m.Cache == nil { return nil, ErrCacheMiss @@ -256,7 +269,7 @@ func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate // private priv, pub := pem.Decode(data) if priv == nil || !strings.Contains(priv.Type, "PRIVATE") { - return nil, errors.New("acme/autocert: no private key found in cache") + return nil, ErrCacheMiss } privKey, err := parsePrivateKey(priv.Bytes) if err != nil { @@ -274,13 +287,14 @@ func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate pubDER = append(pubDER, b.Bytes) } if len(pub) > 0 { - return nil, errors.New("acme/autocert: invalid public key") + // Leftover content not consumed by pem.Decode. Corrupt. Ignore. + return nil, ErrCacheMiss } // verify and create TLS cert leaf, err := validCert(domain, pubDER, privKey) if err != nil { - return nil, err + return nil, ErrCacheMiss } tlscert := &tls.Certificate{ Certificate: pubDER, @@ -361,6 +375,23 @@ func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certifica der, leaf, err := m.authorizedCert(ctx, state.key, domain) if err != nil { + // Remove the failed state after some time, + // making the manager call createCert again on the following TLS hello. + time.AfterFunc(createCertRetryAfter, func() { + defer testDidRemoveState(domain) + m.stateMu.Lock() + defer m.stateMu.Unlock() + // Verify the state hasn't changed and it's still invalid + // before deleting. + s, ok := m.state[domain] + if !ok { + return + } + if _, err := validCert(domain, s.cert, s.key); err == nil { + return + } + delete(m.state, domain) + }) return nil, err } state.cert = der @@ -409,7 +440,6 @@ func (m *Manager) certState(domain string) (*certState, error) { // authorizedCert starts domain ownership verification process and requests a new cert upon success. // The key argument is the certificate private key. func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) { - // TODO: make m.verify retry or retry m.verify calls here if err := m.verify(ctx, domain); err != nil { return nil, nil, err } @@ -780,5 +810,10 @@ func (r *lockedMathRand) int63n(max int64) int64 { return n } -// for easier testing -var timeNow = time.Now +// For easier testing. +var ( + timeNow = time.Now + + // Called when a state is removed. + testDidRemoveState = func(domain string) {} +) diff --git a/vendor/golang.org/x/crypto/acme/autocert/autocert_test.go b/vendor/golang.org/x/crypto/acme/autocert/autocert_test.go index 6df4cf3bf..0352e340d 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/autocert_test.go +++ b/vendor/golang.org/x/crypto/acme/autocert/autocert_test.go @@ -178,6 +178,88 @@ func TestGetCertificate_nilPrompt(t *testing.T) { } } +func TestGetCertificate_expiredCache(t *testing.T) { + // Make an expired cert and cache it. + pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + tmpl := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{CommonName: "example.org"}, + NotAfter: time.Now(), + } + pub, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &pk.PublicKey, pk) + if err != nil { + t.Fatal(err) + } + tlscert := &tls.Certificate{ + Certificate: [][]byte{pub}, + PrivateKey: pk, + } + + man := &Manager{Prompt: AcceptTOS, Cache: newMemCache()} + defer man.stopRenew() + if err := man.cachePut(context.Background(), "example.org", tlscert); err != nil { + t.Fatalf("man.cachePut: %v", err) + } + + // The expired cached cert should trigger a new cert issuance + // and return without an error. + hello := &tls.ClientHelloInfo{ServerName: "example.org"} + testGetCertificate(t, man, "example.org", hello) +} + +func TestGetCertificate_failedAttempt(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusBadRequest) + })) + defer ts.Close() + + const example = "example.org" + d := createCertRetryAfter + f := testDidRemoveState + defer func() { + createCertRetryAfter = d + testDidRemoveState = f + }() + createCertRetryAfter = 0 + done := make(chan struct{}) + testDidRemoveState = func(domain string) { + if domain != example { + t.Errorf("testDidRemoveState: domain = %q; want %q", domain, example) + } + close(done) + } + + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + man := &Manager{ + Prompt: AcceptTOS, + Client: &acme.Client{ + Key: key, + DirectoryURL: ts.URL, + }, + } + defer man.stopRenew() + hello := &tls.ClientHelloInfo{ServerName: example} + if _, err := man.GetCertificate(hello); err == nil { + t.Error("GetCertificate: err is nil") + } + select { + case <-time.After(5 * time.Second): + t.Errorf("took too long to remove the %q state", example) + case <-done: + man.stateMu.Lock() + defer man.stateMu.Unlock() + if v, exist := man.state[example]; exist { + t.Errorf("state exists for %q: %+v", example, v) + } + } +} + // startACMEServerStub runs an ACME server // The domain argument is the expected domain name of a certificate request. func startACMEServerStub(t *testing.T, man *Manager, domain string) (url string, finish func()) { @@ -478,3 +560,47 @@ func TestValidCert(t *testing.T) { } } } + +type cacheGetFunc func(ctx context.Context, key string) ([]byte, error) + +func (f cacheGetFunc) Get(ctx context.Context, key string) ([]byte, error) { + return f(ctx, key) +} + +func (f cacheGetFunc) Put(ctx context.Context, key string, data []byte) error { + return fmt.Errorf("unsupported Put of %q = %q", key, data) +} + +func (f cacheGetFunc) Delete(ctx context.Context, key string) error { + return fmt.Errorf("unsupported Delete of %q", key) +} + +func TestManagerGetCertificateBogusSNI(t *testing.T) { + m := Manager{ + Prompt: AcceptTOS, + Cache: cacheGetFunc(func(ctx context.Context, key string) ([]byte, error) { + return nil, fmt.Errorf("cache.Get of %s", key) + }), + } + tests := []struct { + name string + wantErr string + }{ + {"foo.com", "cache.Get of foo.com"}, + {"foo.com.", "cache.Get of foo.com"}, + {`a\b.com`, "acme/autocert: server name contains invalid character"}, + {`a/b.com`, "acme/autocert: server name contains invalid character"}, + {"", "acme/autocert: missing server name"}, + {"foo", "acme/autocert: server name component count invalid"}, + {".foo", "acme/autocert: server name component count invalid"}, + {"foo.", "acme/autocert: server name component count invalid"}, + {"fo.o", "cache.Get of fo.o"}, + } + for _, tt := range tests { + _, err := m.GetCertificate(&tls.ClientHelloInfo{ServerName: tt.name}) + got := fmt.Sprint(err) + if got != tt.wantErr { + t.Errorf("GetCertificate(SNI = %q) = %q; want %q", tt.name, got, tt.wantErr) + } + } +} diff --git a/vendor/golang.org/x/crypto/acme/jws.go b/vendor/golang.org/x/crypto/acme/jws.go index 49ba313ca..6cbca25de 100644 --- a/vendor/golang.org/x/crypto/acme/jws.go +++ b/vendor/golang.org/x/crypto/acme/jws.go @@ -134,7 +134,7 @@ func jwsHasher(key crypto.Signer) (string, crypto.Hash) { return "ES256", crypto.SHA256 case "P-384": return "ES384", crypto.SHA384 - case "P-512": + case "P-521": return "ES512", crypto.SHA512 } } diff --git a/vendor/golang.org/x/crypto/acme/jws_test.go b/vendor/golang.org/x/crypto/acme/jws_test.go index 1def87397..0ff0fb5a3 100644 --- a/vendor/golang.org/x/crypto/acme/jws_test.go +++ b/vendor/golang.org/x/crypto/acme/jws_test.go @@ -12,11 +12,13 @@ import ( "encoding/base64" "encoding/json" "encoding/pem" + "fmt" "math/big" "testing" ) -const testKeyPEM = ` +const ( + testKeyPEM = ` -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA4xgZ3eRPkwoRvy7qeRUbmMDe0V+xH9eWLdu0iheeLlrmD2mq WXfP9IeSKApbn34g8TuAS9g5zhq8ELQ3kmjr+KV86GAMgI6VAcGlq3QrzpTCf/30 @@ -46,10 +48,9 @@ EQeIP6dZtv8IMgtGIb91QX9pXvP0aznzQKwYIA8nZgoENCPfiMTPiEDT9e/0lObO -----END RSA PRIVATE KEY----- ` -// This thumbprint is for the testKey defined above. -const testKeyThumbprint = "6nicxzh6WETQlrvdchkz-U3e3DOQZ4heJKU63rfqMqQ" + // This thumbprint is for the testKey defined above. + testKeyThumbprint = "6nicxzh6WETQlrvdchkz-U3e3DOQZ4heJKU63rfqMqQ" -const ( // openssl ecparam -name secp256k1 -genkey -noout testKeyECPEM = ` -----BEGIN EC PRIVATE KEY----- @@ -58,39 +59,78 @@ AwEHoUQDQgAE5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HThqIrvawF5 QAaS/RNouybCiRhRjI3EaxLkQwgrCw0gqQ== -----END EC PRIVATE KEY----- ` - // 1. opnessl ec -in key.pem -noout -text + // openssl ecparam -name secp384r1 -genkey -noout + testKeyEC384PEM = ` +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDAQ4lNtXRORWr1bgKR1CGysr9AJ9SyEk4jiVnlUWWUChmSNL+i9SLSD +Oe/naPqXJ6CgBwYFK4EEACKhZANiAAQzKtj+Ms0vHoTX5dzv3/L5YMXOWuI5UKRj +JigpahYCqXD2BA1j0E/2xt5vlPf+gm0PL+UHSQsCokGnIGuaHCsJAp3ry0gHQEke +WYXapUUFdvaK1R2/2hn5O+eiQM8YzCg= +-----END EC PRIVATE KEY----- +` + // openssl ecparam -name secp521r1 -genkey -noout + testKeyEC512PEM = ` +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIBSNZKFcWzXzB/aJClAb305ibalKgtDA7+70eEkdPt28/3LZMM935Z +KqYHh/COcxuu3Kt8azRAUz3gyr4zZKhlKUSgBwYFK4EEACOhgYkDgYYABAHUNKbx +7JwC7H6pa2sV0tERWhHhB3JmW+OP6SUgMWryvIKajlx73eS24dy4QPGrWO9/ABsD +FqcRSkNVTXnIv6+0mAF25knqIBIg5Q8M9BnOu9GGAchcwt3O7RDHmqewnJJDrbjd +GGnm6rb+NnWR9DIopM0nKNkToWoF/hzopxu4Ae/GsQ== +-----END EC PRIVATE KEY----- +` + // 1. openssl ec -in key.pem -noout -text // 2. remove first byte, 04 (the header); the rest is X and Y - // 3. covert each with: echo <val> | xxd -r -p | base64 | tr -d '=' | tr '/+' '_-' - testKeyECPubX = "5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HQ" - testKeyECPubY = "4aiK72sBeUAGkv0TaLsmwokYUYyNxGsS5EMIKwsNIKk" + // 3. convert each with: echo <val> | xxd -r -p | base64 -w 100 | tr -d '=' | tr '/+' '_-' + testKeyECPubX = "5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HQ" + testKeyECPubY = "4aiK72sBeUAGkv0TaLsmwokYUYyNxGsS5EMIKwsNIKk" + testKeyEC384PubX = "MyrY_jLNLx6E1-Xc79_y-WDFzlriOVCkYyYoKWoWAqlw9gQNY9BP9sbeb5T3_oJt" + testKeyEC384PubY = "Dy_lB0kLAqJBpyBrmhwrCQKd68tIB0BJHlmF2qVFBXb2itUdv9oZ-TvnokDPGMwo" + testKeyEC512PubX = "AdQ0pvHsnALsfqlraxXS0RFaEeEHcmZb44_pJSAxavK8gpqOXHvd5Lbh3LhA8atY738AGwMWpxFKQ1VNeci_r7SY" + testKeyEC512PubY = "AXbmSeogEiDlDwz0Gc670YYByFzC3c7tEMeap7CckkOtuN0Yaebqtv42dZH0MiikzSco2ROhagX-HOinG7gB78ax" + // echo -n '{"crv":"P-256","kty":"EC","x":"<testKeyECPubX>","y":"<testKeyECPubY>"}' | \ // openssl dgst -binary -sha256 | base64 | tr -d '=' | tr '/+' '_-' testKeyECThumbprint = "zedj-Bd1Zshp8KLePv2MB-lJ_Hagp7wAwdkA0NUTniU" ) var ( - testKey *rsa.PrivateKey - testKeyEC *ecdsa.PrivateKey + testKey *rsa.PrivateKey + testKeyEC *ecdsa.PrivateKey + testKeyEC384 *ecdsa.PrivateKey + testKeyEC512 *ecdsa.PrivateKey ) func init() { - d, _ := pem.Decode([]byte(testKeyPEM)) + testKey = parseRSA(testKeyPEM, "testKeyPEM") + testKeyEC = parseEC(testKeyECPEM, "testKeyECPEM") + testKeyEC384 = parseEC(testKeyEC384PEM, "testKeyEC384PEM") + testKeyEC512 = parseEC(testKeyEC512PEM, "testKeyEC512PEM") +} + +func decodePEM(s, name string) []byte { + d, _ := pem.Decode([]byte(s)) if d == nil { - panic("no block found in testKeyPEM") + panic("no block found in " + name) } - var err error - testKey, err = x509.ParsePKCS1PrivateKey(d.Bytes) + return d.Bytes +} + +func parseRSA(s, name string) *rsa.PrivateKey { + b := decodePEM(s, name) + k, err := x509.ParsePKCS1PrivateKey(b) if err != nil { - panic(err.Error()) + panic(fmt.Sprintf("%s: %v", name, err)) } + return k +} - if d, _ = pem.Decode([]byte(testKeyECPEM)); d == nil { - panic("no block found in testKeyECPEM") - } - testKeyEC, err = x509.ParseECPrivateKey(d.Bytes) +func parseEC(s, name string) *ecdsa.PrivateKey { + b := decodePEM(s, name) + k, err := x509.ParseECPrivateKey(b) if err != nil { - panic(err.Error()) + panic(fmt.Sprintf("%s: %v", name, err)) } + return k } func TestJWSEncodeJSON(t *testing.T) { @@ -141,50 +181,63 @@ func TestJWSEncodeJSON(t *testing.T) { } func TestJWSEncodeJSONEC(t *testing.T) { - claims := struct{ Msg string }{"Hello JWS"} - - b, err := jwsEncodeJSON(claims, testKeyEC, "nonce") - if err != nil { - t.Fatal(err) - } - var jws struct{ Protected, Payload, Signature string } - if err := json.Unmarshal(b, &jws); err != nil { - t.Fatal(err) + tt := []struct { + key *ecdsa.PrivateKey + x, y string + alg, crv string + }{ + {testKeyEC, testKeyECPubX, testKeyECPubY, "ES256", "P-256"}, + {testKeyEC384, testKeyEC384PubX, testKeyEC384PubY, "ES384", "P-384"}, + {testKeyEC512, testKeyEC512PubX, testKeyEC512PubY, "ES512", "P-521"}, } + for i, test := range tt { + claims := struct{ Msg string }{"Hello JWS"} + b, err := jwsEncodeJSON(claims, test.key, "nonce") + if err != nil { + t.Errorf("%d: %v", i, err) + continue + } + var jws struct{ Protected, Payload, Signature string } + if err := json.Unmarshal(b, &jws); err != nil { + t.Errorf("%d: %v", i, err) + continue + } - if b, err = base64.RawURLEncoding.DecodeString(jws.Protected); err != nil { - t.Fatalf("jws.Protected: %v", err) - } - var head struct { - Alg string - Nonce string - JWK struct { - Crv string - Kty string - X string - Y string - } `json:"jwk"` - } - if err := json.Unmarshal(b, &head); err != nil { - t.Fatalf("jws.Protected: %v", err) - } - if head.Alg != "ES256" { - t.Errorf("head.Alg = %q; want ES256", head.Alg) - } - if head.Nonce != "nonce" { - t.Errorf("head.Nonce = %q; want nonce", head.Nonce) - } - if head.JWK.Crv != "P-256" { - t.Errorf("head.JWK.Crv = %q; want P-256", head.JWK.Crv) - } - if head.JWK.Kty != "EC" { - t.Errorf("head.JWK.Kty = %q; want EC", head.JWK.Kty) - } - if head.JWK.X != testKeyECPubX { - t.Errorf("head.JWK.X = %q; want %q", head.JWK.X, testKeyECPubX) - } - if head.JWK.Y != testKeyECPubY { - t.Errorf("head.JWK.Y = %q; want %q", head.JWK.Y, testKeyECPubY) + b, err = base64.RawURLEncoding.DecodeString(jws.Protected) + if err != nil { + t.Errorf("%d: jws.Protected: %v", i, err) + } + var head struct { + Alg string + Nonce string + JWK struct { + Crv string + Kty string + X string + Y string + } `json:"jwk"` + } + if err := json.Unmarshal(b, &head); err != nil { + t.Errorf("%d: jws.Protected: %v", i, err) + } + if head.Alg != test.alg { + t.Errorf("%d: head.Alg = %q; want %q", i, head.Alg, test.alg) + } + if head.Nonce != "nonce" { + t.Errorf("%d: head.Nonce = %q; want nonce", i, head.Nonce) + } + if head.JWK.Crv != test.crv { + t.Errorf("%d: head.JWK.Crv = %q; want %q", i, head.JWK.Crv, test.crv) + } + if head.JWK.Kty != "EC" { + t.Errorf("%d: head.JWK.Kty = %q; want EC", i, head.JWK.Kty) + } + if head.JWK.X != test.x { + t.Errorf("%d: head.JWK.X = %q; want %q", i, head.JWK.X, test.x) + } + if head.JWK.Y != test.y { + t.Errorf("%d: head.JWK.Y = %q; want %q", i, head.JWK.Y, test.y) + } } } diff --git a/vendor/golang.org/x/crypto/acme/types.go b/vendor/golang.org/x/crypto/acme/types.go index 0513b2e55..ab4de0b88 100644 --- a/vendor/golang.org/x/crypto/acme/types.go +++ b/vendor/golang.org/x/crypto/acme/types.go @@ -1,9 +1,15 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package acme import ( "errors" "fmt" "net/http" + "strings" + "time" ) // ACME server response statuses used to describe Authorization and Challenge states. @@ -33,14 +39,8 @@ const ( CRLReasonAACompromise CRLReasonCode = 10 ) -var ( - // ErrAuthorizationFailed indicates that an authorization for an identifier - // did not succeed. - ErrAuthorizationFailed = errors.New("acme: identifier authorization failed") - - // ErrUnsupportedKey is returned when an unsupported key type is encountered. - ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported") -) +// ErrUnsupportedKey is returned when an unsupported key type is encountered. +var ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported") // Error is an ACME error, defined in Problem Details for HTTP APIs doc // http://tools.ietf.org/html/draft-ietf-appsawg-http-problem. @@ -53,6 +53,7 @@ type Error struct { // Detail is a human-readable explanation specific to this occurrence of the problem. Detail string // Header is the original server error response headers. + // It may be nil. Header http.Header } @@ -60,6 +61,50 @@ func (e *Error) Error() string { return fmt.Sprintf("%d %s: %s", e.StatusCode, e.ProblemType, e.Detail) } +// AuthorizationError indicates that an authorization for an identifier +// did not succeed. +// It contains all errors from Challenge items of the failed Authorization. +type AuthorizationError struct { + // URI uniquely identifies the failed Authorization. + URI string + + // Identifier is an AuthzID.Value of the failed Authorization. + Identifier string + + // Errors is a collection of non-nil error values of Challenge items + // of the failed Authorization. + Errors []error +} + +func (a *AuthorizationError) Error() string { + e := make([]string, len(a.Errors)) + for i, err := range a.Errors { + e[i] = err.Error() + } + return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; ")) +} + +// RateLimit reports whether err represents a rate limit error and +// any Retry-After duration returned by the server. +// +// See the following for more details on rate limiting: +// https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-5.6 +func RateLimit(err error) (time.Duration, bool) { + e, ok := err.(*Error) + if !ok { + return 0, false + } + // Some CA implementations may return incorrect values. + // Use case-insensitive comparison. + if !strings.HasSuffix(strings.ToLower(e.ProblemType), ":ratelimited") { + return 0, false + } + if e.Header == nil { + return 0, true + } + return retryAfter(e.Header.Get("Retry-After"), 0), true +} + // Account is a user account. It is associated with a private key. type Account struct { // URI is the account unique ID, which is also a URL used to retrieve @@ -118,6 +163,8 @@ type Directory struct { } // 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-sni-02", "dns-01". Type string @@ -130,6 +177,11 @@ type Challenge struct { // Status identifies the status of this challenge. Status string + + // Error indicates the reason for an authorization failure + // when this challenge was used. + // The type of a non-nil value is *Error. + Error error } // Authorization encodes an authorization response. @@ -187,12 +239,26 @@ func (z *wireAuthz) authorization(uri string) *Authorization { return a } +func (z *wireAuthz) error(uri string) *AuthorizationError { + err := &AuthorizationError{ + URI: uri, + Identifier: z.Identifier.Value, + } + for _, raw := range z.Challenges { + if raw.Error != nil { + err.Errors = append(err.Errors, raw.Error.error(nil)) + } + } + return err +} + // wireChallenge is ACME JSON challenge representation. type wireChallenge struct { URI string `json:"uri"` Type string Token string Status string + Error *wireError } func (c *wireChallenge) challenge() *Challenge { @@ -205,5 +271,25 @@ func (c *wireChallenge) challenge() *Challenge { if v.Status == "" { v.Status = StatusPending } + if c.Error != nil { + v.Error = c.Error.error(nil) + } return v } + +// wireError is a subset of fields of the Problem Details object +// as described in https://tools.ietf.org/html/rfc7807#section-3.1. +type wireError struct { + Status int + Type string + Detail string +} + +func (e *wireError) error(h http.Header) *Error { + return &Error{ + StatusCode: e.Status, + ProblemType: e.Type, + Detail: e.Detail, + Header: h, + } +} diff --git a/vendor/golang.org/x/crypto/acme/types_test.go b/vendor/golang.org/x/crypto/acme/types_test.go new file mode 100644 index 000000000..a7553e6b7 --- /dev/null +++ b/vendor/golang.org/x/crypto/acme/types_test.go @@ -0,0 +1,63 @@ +// Copyright 2017 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 ( + "errors" + "net/http" + "testing" + "time" +) + +func TestRateLimit(t *testing.T) { + now := time.Date(2017, 04, 27, 10, 0, 0, 0, time.UTC) + f := timeNow + defer func() { timeNow = f }() + timeNow = func() time.Time { return now } + + h120, hTime := http.Header{}, http.Header{} + h120.Set("Retry-After", "120") + hTime.Set("Retry-After", "Tue Apr 27 11:00:00 2017") + + err1 := &Error{ + ProblemType: "urn:ietf:params:acme:error:nolimit", + Header: h120, + } + err2 := &Error{ + ProblemType: "urn:ietf:params:acme:error:rateLimited", + Header: h120, + } + err3 := &Error{ + ProblemType: "urn:ietf:params:acme:error:rateLimited", + Header: nil, + } + err4 := &Error{ + ProblemType: "urn:ietf:params:acme:error:rateLimited", + Header: hTime, + } + + tt := []struct { + err error + res time.Duration + ok bool + }{ + {nil, 0, false}, + {errors.New("dummy"), 0, false}, + {err1, 0, false}, + {err2, 2 * time.Minute, true}, + {err3, 0, true}, + {err4, time.Hour, true}, + } + for i, test := range tt { + res, ok := RateLimit(test.err) + if ok != test.ok { + t.Errorf("%d: RateLimit(%+v): ok = %v; want %v", i, test.err, ok, test.ok) + continue + } + if res != test.res { + t.Errorf("%d: RateLimit(%+v) = %v; want %v", i, test.err, res, test.res) + } + } +} diff --git a/vendor/golang.org/x/crypto/blake2b/blake2b_test.go b/vendor/golang.org/x/crypto/blake2b/blake2b_test.go index a38fceb20..7954346f4 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2b_test.go +++ b/vendor/golang.org/x/crypto/blake2b/blake2b_test.go @@ -22,7 +22,7 @@ func fromHex(s string) []byte { func TestHashes(t *testing.T) { defer func(sse4, avx, avx2 bool) { - useSSE4, useAVX, useAVX2 = sse4, useAVX, avx2 + useSSE4, useAVX, useAVX2 = sse4, avx, avx2 }(useSSE4, useAVX, useAVX2) if useAVX2 { diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go index 475503321..7cd7ad834 100644 --- a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go +++ b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go @@ -14,13 +14,60 @@ func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool //go:noescape func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte) -//go:noescape -func haveSSSE3() bool +// cpuid is implemented in chacha20poly1305_amd64.s. +func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) + +// xgetbv with ecx = 0 is implemented in chacha20poly1305_amd64.s. +func xgetbv() (eax, edx uint32) -var canUseASM bool +var ( + useASM bool + useAVX2 bool +) func init() { - canUseASM = haveSSSE3() + detectCPUFeatures() +} + +// detectCPUFeatures is used to detect if cpu instructions +// used by the functions implemented in assembler in +// chacha20poly1305_amd64.s are supported. +func detectCPUFeatures() { + maxID, _, _, _ := cpuid(0, 0) + if maxID < 1 { + return + } + + _, _, ecx1, _ := cpuid(1, 0) + + haveSSSE3 := isSet(9, ecx1) + useASM = haveSSSE3 + + haveOSXSAVE := isSet(27, ecx1) + + osSupportsAVX := false + // For XGETBV, OSXSAVE bit is required and sufficient. + if haveOSXSAVE { + eax, _ := xgetbv() + // Check if XMM and YMM registers have OS support. + osSupportsAVX = isSet(1, eax) && isSet(2, eax) + } + haveAVX := isSet(28, ecx1) && osSupportsAVX + + if maxID < 7 { + return + } + + _, ebx7, _, _ := cpuid(7, 0) + haveAVX2 := isSet(5, ebx7) && haveAVX + haveBMI2 := isSet(8, ebx7) + + useAVX2 = haveAVX2 && haveBMI2 +} + +// isSet checks if bit at bitpos is set in value. +func isSet(bitpos uint, value uint32) bool { + return value&(1<<bitpos) != 0 } // setupState writes a ChaCha20 input matrix to state. See @@ -47,7 +94,7 @@ func setupState(state *[16]uint32, key *[32]byte, nonce []byte) { } func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte { - if !canUseASM { + if !useASM { return c.sealGeneric(dst, nonce, plaintext, additionalData) } @@ -60,7 +107,7 @@ func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) [] } func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { - if !canUseASM { + if !useASM { return c.openGeneric(dst, nonce, ciphertext, additionalData) } diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s index 39c58b44a..1c57e3894 100644 --- a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s +++ b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s @@ -278,15 +278,8 @@ TEXT ·chacha20Poly1305Open(SB), 0, $288-97 MOVQ ad+72(FP), adp // Check for AVX2 support - CMPB runtime·support_avx2(SB), $0 - JE noavx2bmi2Open - - // Check BMI2 bit for MULXQ. - // runtime·cpuid_ebx7 is always available here - // because it passed avx2 check - TESTL $(1<<8), runtime·cpuid_ebx7(SB) - JNE chacha20Poly1305Open_AVX2 -noavx2bmi2Open: + CMPB ·useAVX2(SB), $1 + JE chacha20Poly1305Open_AVX2 // Special optimization, for very short buffers CMPQ inl, $128 @@ -1491,16 +1484,8 @@ TEXT ·chacha20Poly1305Seal(SB), 0, $288-96 MOVQ src_len+56(FP), inl MOVQ ad+72(FP), adp - // Check for AVX2 support - CMPB runtime·support_avx2(SB), $0 - JE noavx2bmi2Seal - - // Check BMI2 bit for MULXQ. - // runtime·cpuid_ebx7 is always available here - // because it passed avx2 check - TESTL $(1<<8), runtime·cpuid_ebx7(SB) - JNE chacha20Poly1305Seal_AVX2 -noavx2bmi2Seal: + CMPB ·useAVX2(SB), $1 + JE chacha20Poly1305Seal_AVX2 // Special optimization, for very short buffers CMPQ inl, $128 @@ -2709,13 +2694,21 @@ sealAVX2Tail512LoopB: JMP sealAVX2SealHash -// func haveSSSE3() bool -TEXT ·haveSSSE3(SB), NOSPLIT, $0 - XORQ AX, AX - INCL AX +// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) +TEXT ·cpuid(SB), NOSPLIT, $0-24 + MOVL eaxArg+0(FP), AX + MOVL ecxArg+4(FP), CX CPUID - SHRQ $9, CX - ANDQ $1, CX - MOVB CX, ret+0(FP) + MOVL AX, eax+8(FP) + MOVL BX, ebx+12(FP) + MOVL CX, ecx+16(FP) + MOVL DX, edx+20(FP) RET +// func xgetbv() (eax, edx uint32) +TEXT ·xgetbv(SB),NOSPLIT,$0-8 + MOVL $0, CX + XGETBV + MOVL AX, eax+0(FP) + MOVL DX, edx+4(FP) + RET diff --git a/vendor/golang.org/x/crypto/ssh/agent/client_test.go b/vendor/golang.org/x/crypto/ssh/agent/client_test.go index 93d3a9cd2..5fc47e577 100644 --- a/vendor/golang.org/x/crypto/ssh/agent/client_test.go +++ b/vendor/golang.org/x/crypto/ssh/agent/client_test.go @@ -180,9 +180,12 @@ func TestCert(t *testing.T) { // therefore is buffered (net.Pipe deadlocks if both sides start with // a write.) func netPipe() (net.Conn, net.Conn, error) { - listener, err := net.Listen("tcp", ":0") + listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { - return nil, nil, err + listener, err = net.Listen("tcp", "[::1]:0") + if err != nil { + return nil, nil, err + } } defer listener.Close() c1, err := net.Dial("tcp", listener.Addr().String()) @@ -200,6 +203,9 @@ func netPipe() (net.Conn, net.Conn, error) { } func TestAuth(t *testing.T) { + agent, _, cleanup := startAgent(t) + defer cleanup() + a, b, err := netPipe() if err != nil { t.Fatalf("netPipe: %v", err) @@ -208,9 +214,6 @@ func TestAuth(t *testing.T) { defer a.Close() defer b.Close() - agent, _, cleanup := startAgent(t) - defer cleanup() - if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment"}); err != nil { t.Errorf("Add: %v", err) } diff --git a/vendor/golang.org/x/crypto/ssh/certs.go b/vendor/golang.org/x/crypto/ssh/certs.go index 67600e240..2fc8af1b1 100644 --- a/vendor/golang.org/x/crypto/ssh/certs.go +++ b/vendor/golang.org/x/crypto/ssh/certs.go @@ -251,10 +251,18 @@ type CertChecker struct { // for user certificates. SupportedCriticalOptions []string - // IsAuthority should return true if the key is recognized as - // an authority. This allows for certificates to be signed by other - // certificates. - IsAuthority func(auth PublicKey) bool + // IsUserAuthority should return true if the key is recognized as an + // authority for the given user certificate. This allows for + // certificates to be signed by other certificates. This must be set + // if this CertChecker will be checking user certificates. + IsUserAuthority func(auth PublicKey) bool + + // IsHostAuthority should report whether the key is recognized as + // an authority for this host. This allows for certificates to be + // signed by other keys, and for those other keys to only be valid + // signers for particular hostnames. This must be set if this + // CertChecker will be checking host certificates. + IsHostAuthority func(auth PublicKey, address string) bool // Clock is used for verifying time stamps. If nil, time.Now // is used. @@ -356,7 +364,13 @@ func (c *CertChecker) CheckCert(principal string, cert *Certificate) error { } } - if !c.IsAuthority(cert.SignatureKey) { + // if this is a host cert, principal is the remote hostname as passed + // to CheckHostCert. + if cert.CertType == HostCert && !c.IsHostAuthority(cert.SignatureKey, principal) { + return fmt.Errorf("ssh: no authorities for hostname: %v", principal) + } + + if cert.CertType == UserCert && !c.IsUserAuthority(cert.SignatureKey) { return fmt.Errorf("ssh: certificate signed by unrecognized authority") } diff --git a/vendor/golang.org/x/crypto/ssh/certs_test.go b/vendor/golang.org/x/crypto/ssh/certs_test.go index c5f2e5330..fba6310c5 100644 --- a/vendor/golang.org/x/crypto/ssh/certs_test.go +++ b/vendor/golang.org/x/crypto/ssh/certs_test.go @@ -104,7 +104,7 @@ func TestValidateCert(t *testing.T) { t.Fatalf("got %v (%T), want *Certificate", key, key) } checker := CertChecker{} - checker.IsAuthority = func(k PublicKey) bool { + checker.IsUserAuthority = func(k PublicKey) bool { return bytes.Equal(k.Marshal(), validCert.SignatureKey.Marshal()) } @@ -142,7 +142,7 @@ func TestValidateCertTime(t *testing.T) { checker := CertChecker{ Clock: func() time.Time { return time.Unix(ts, 0) }, } - checker.IsAuthority = func(k PublicKey) bool { + checker.IsUserAuthority = func(k PublicKey) bool { return bytes.Equal(k.Marshal(), testPublicKeys["ecdsa"].Marshal()) } @@ -160,7 +160,7 @@ func TestValidateCertTime(t *testing.T) { func TestHostKeyCert(t *testing.T) { cert := &Certificate{ - ValidPrincipals: []string{"hostname", "hostname.domain"}, + ValidPrincipals: []string{"hostname", "hostname.domain", "otherhost"}, Key: testPublicKeys["rsa"], ValidBefore: CertTimeInfinity, CertType: HostCert, @@ -168,8 +168,8 @@ func TestHostKeyCert(t *testing.T) { cert.SignCert(rand.Reader, testSigners["ecdsa"]) checker := &CertChecker{ - IsAuthority: func(p PublicKey) bool { - return bytes.Equal(testPublicKeys["ecdsa"].Marshal(), p.Marshal()) + IsHostAuthority: func(p PublicKey, h string) bool { + return h == "hostname" && bytes.Equal(testPublicKeys["ecdsa"].Marshal(), p.Marshal()) }, } @@ -178,7 +178,7 @@ func TestHostKeyCert(t *testing.T) { t.Errorf("NewCertSigner: %v", err) } - for _, name := range []string{"hostname", "otherhost"} { + for _, name := range []string{"hostname", "otherhost", "lasthost"} { c1, c2, err := netPipe() if err != nil { t.Fatalf("netPipe: %v", err) diff --git a/vendor/golang.org/x/crypto/ssh/client_auth_test.go b/vendor/golang.org/x/crypto/ssh/client_auth_test.go index dd83a3c84..bd9f8a169 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth_test.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth_test.go @@ -38,7 +38,7 @@ func tryAuth(t *testing.T, config *ClientConfig) error { defer c2.Close() certChecker := CertChecker{ - IsAuthority: func(k PublicKey) bool { + IsUserAuthority: func(k PublicKey) bool { return bytes.Equal(k.Marshal(), testPublicKeys["ecdsa"].Marshal()) }, UserKeyFallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) { diff --git a/vendor/golang.org/x/crypto/ssh/handshake_test.go b/vendor/golang.org/x/crypto/ssh/handshake_test.go index 51a4c5ade..91d493568 100644 --- a/vendor/golang.org/x/crypto/ssh/handshake_test.go +++ b/vendor/golang.org/x/crypto/ssh/handshake_test.go @@ -40,9 +40,12 @@ func (t *testChecker) Check(dialAddr string, addr net.Addr, key PublicKey) error // therefore is buffered (net.Pipe deadlocks if both sides start with // a write.) func netPipe() (net.Conn, net.Conn, error) { - listener, err := net.Listen("tcp", ":0") + listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { - return nil, nil, err + listener, err = net.Listen("tcp", "[::1]:0") + if err != nil { + return nil, nil, err + } } defer listener.Close() c1, err := net.Dial("tcp", listener.Addr().String()) diff --git a/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go b/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go index d1f371868..ea92b2983 100644 --- a/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go +++ b/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go @@ -144,11 +144,16 @@ func keyEq(a, b ssh.PublicKey) bool { return bytes.Equal(a.Marshal(), b.Marshal()) } -// IsAuthority can be used as a callback in ssh.CertChecker -func (db *hostKeyDB) IsAuthority(remote ssh.PublicKey) bool { +// IsAuthorityForHost can be used as a callback in ssh.CertChecker +func (db *hostKeyDB) IsHostAuthority(remote ssh.PublicKey, address string) bool { + h, p, err := net.SplitHostPort(address) + if err != nil { + return false + } + a := addr{host: h, port: p} + for _, l := range db.lines { - // TODO(hanwen): should we check the hostname against host pattern? - if l.cert && keyEq(l.knownKey.Key, remote) { + if l.cert && keyEq(l.knownKey.Key, remote) && l.match([]addr{a}) { return true } } @@ -409,9 +414,7 @@ func (db *hostKeyDB) Read(r io.Reader, filename string) error { // New creates a host key callback from the given OpenSSH host key // files. The returned callback is for use in -// ssh.ClientConfig.HostKeyCallback. Hostnames are ignored for -// certificates, ie. any certificate authority is assumed to be valid -// for all remote hosts. Hashed hostnames are not supported. +// ssh.ClientConfig.HostKeyCallback. Hashed hostnames are not supported. func New(files ...string) (ssh.HostKeyCallback, error) { db := newHostKeyDB() for _, fn := range files { @@ -425,12 +428,8 @@ func New(files ...string) (ssh.HostKeyCallback, error) { } } - // TODO(hanwen): properly supporting certificates requires an - // API change in the SSH library: IsAuthority should provide - // the address too? - var certChecker ssh.CertChecker - certChecker.IsAuthority = db.IsAuthority + certChecker.IsHostAuthority = db.IsHostAuthority certChecker.IsRevoked = db.IsRevoked certChecker.HostKeyFallback = db.check diff --git a/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts_test.go b/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts_test.go index 63aff9927..be7cc0e80 100644 --- a/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts_test.go +++ b/vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts_test.go @@ -76,6 +76,28 @@ func TestRevoked(t *testing.T) { } } +func TestHostAuthority(t *testing.T) { + for _, m := range []struct { + authorityFor string + address string + + good bool + }{ + {authorityFor: "localhost", address: "localhost:22", good: true}, + {authorityFor: "localhost", address: "localhost", good: false}, + {authorityFor: "localhost", address: "localhost:1234", good: false}, + {authorityFor: "[localhost]:1234", address: "localhost:1234", good: true}, + {authorityFor: "[localhost]:1234", address: "localhost:22", good: false}, + {authorityFor: "[localhost]:1234", address: "localhost", good: false}, + } { + db := testDB(t, `@cert-authority `+m.authorityFor+` `+edKeyStr) + if ok := db.IsHostAuthority(db.lines[0].knownKey.Key, m.address); ok != m.good { + t.Errorf("IsHostAuthority: authority %s, address %s, wanted good = %v, got good = %v", + m.authorityFor, m.address, m.good, ok) + } + } +} + func TestBracket(t *testing.T) { db := testDB(t, `[git.eclipse.org]:29418,[198.41.30.196]:29418 `+edKeyStr) diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index 8e95acc6a..23b41d943 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -147,12 +147,12 @@ type ServerConn struct { // Request and NewChannel channels must be serviced, or the connection // will hang. func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) { - if config.MaxAuthTries == 0 { - config.MaxAuthTries = 6 - } - fullConf := *config fullConf.SetDefaults() + if fullConf.MaxAuthTries == 0 { + fullConf.MaxAuthTries = 6 + } + s := &connection{ sshConn: sshConn{conn: c}, } |