mirror of
https://github.com/golang/go
synced 2024-09-30 05:07:17 +00:00
[release-branch.go1.10] all: merge release-branch.go1.10-security into release-branch.go1.10
Change-Id: I7048387350b683030d9f3106979370b0d096ea38
This commit is contained in:
commit
f40c04941a
|
@ -72,6 +72,13 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.10.5">Go
|
||||||
1.10.5 milestone</a> on our issue tracker for details.
|
1.10.5 milestone</a> on our issue tracker for details.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
go1.10.6 (released 2018/12/12) includes three security fixes to "go get" and
|
||||||
|
the <code>crypto/x509</code> package.
|
||||||
|
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.10.6">Go
|
||||||
|
1.10.6 milestone</a> on our issue tracker for details.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2 id="go1.9">go1.9 (released 2017/08/24)</h2>
|
<h2 id="go1.9">go1.9 (released 2017/08/24)</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -376,6 +376,10 @@ func downloadPackage(p *load.Package) error {
|
||||||
security = web.Insecure
|
security = web.Insecure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := CheckImportPath(p.ImportPath); err != nil {
|
||||||
|
return fmt.Errorf("%s: invalid import path: %v", p.ImportPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
if p.Internal.Build.SrcRoot != "" {
|
if p.Internal.Build.SrcRoot != "" {
|
||||||
// Directory exists. Look for checkout along path to src.
|
// Directory exists. Look for checkout along path to src.
|
||||||
vcs, rootPath, err = vcsFromDir(p.Dir, p.Internal.Build.SrcRoot)
|
vcs, rootPath, err = vcsFromDir(p.Dir, p.Internal.Build.SrcRoot)
|
||||||
|
|
192
src/cmd/go/internal/get/path.go
Normal file
192
src/cmd/go/internal/get/path.go
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
// Copyright 2018 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 get
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The following functions are copied verbatim from cmd/go/internal/module/module.go,
|
||||||
|
// with a change to additionally reject Windows short-names,
|
||||||
|
// and one to accept arbitrary letters (golang.org/issue/29101).
|
||||||
|
//
|
||||||
|
// TODO(bcmills): After the call site for this function is backported,
|
||||||
|
// consolidate this back down to a single copy.
|
||||||
|
//
|
||||||
|
// NOTE: DO NOT MERGE THESE UNTIL WE DECIDE ABOUT ARBITRARY LETTERS IN MODULE MODE.
|
||||||
|
|
||||||
|
// CheckImportPath checks that an import path is valid.
|
||||||
|
func CheckImportPath(path string) error {
|
||||||
|
if err := checkPath(path, false); err != nil {
|
||||||
|
return fmt.Errorf("malformed import path %q: %v", path, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkPath checks that a general path is valid.
|
||||||
|
// It returns an error describing why but not mentioning path.
|
||||||
|
// Because these checks apply to both module paths and import paths,
|
||||||
|
// the caller is expected to add the "malformed ___ path %q: " prefix.
|
||||||
|
// fileName indicates whether the final element of the path is a file name
|
||||||
|
// (as opposed to a directory name).
|
||||||
|
func checkPath(path string, fileName bool) error {
|
||||||
|
if !utf8.ValidString(path) {
|
||||||
|
return fmt.Errorf("invalid UTF-8")
|
||||||
|
}
|
||||||
|
if path == "" {
|
||||||
|
return fmt.Errorf("empty string")
|
||||||
|
}
|
||||||
|
if strings.Contains(path, "..") {
|
||||||
|
return fmt.Errorf("double dot")
|
||||||
|
}
|
||||||
|
if strings.Contains(path, "//") {
|
||||||
|
return fmt.Errorf("double slash")
|
||||||
|
}
|
||||||
|
if path[len(path)-1] == '/' {
|
||||||
|
return fmt.Errorf("trailing slash")
|
||||||
|
}
|
||||||
|
elemStart := 0
|
||||||
|
for i, r := range path {
|
||||||
|
if r == '/' {
|
||||||
|
if err := checkElem(path[elemStart:i], fileName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
elemStart = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := checkElem(path[elemStart:], fileName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkElem checks whether an individual path element is valid.
|
||||||
|
// fileName indicates whether the element is a file name (not a directory name).
|
||||||
|
func checkElem(elem string, fileName bool) error {
|
||||||
|
if elem == "" {
|
||||||
|
return fmt.Errorf("empty path element")
|
||||||
|
}
|
||||||
|
if strings.Count(elem, ".") == len(elem) {
|
||||||
|
return fmt.Errorf("invalid path element %q", elem)
|
||||||
|
}
|
||||||
|
if elem[0] == '.' && !fileName {
|
||||||
|
return fmt.Errorf("leading dot in path element")
|
||||||
|
}
|
||||||
|
if elem[len(elem)-1] == '.' {
|
||||||
|
return fmt.Errorf("trailing dot in path element")
|
||||||
|
}
|
||||||
|
|
||||||
|
charOK := pathOK
|
||||||
|
if fileName {
|
||||||
|
charOK = fileNameOK
|
||||||
|
}
|
||||||
|
for _, r := range elem {
|
||||||
|
if !charOK(r) {
|
||||||
|
return fmt.Errorf("invalid char %q", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows disallows a bunch of path elements, sadly.
|
||||||
|
// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
|
||||||
|
short := elem
|
||||||
|
if i := strings.Index(short, "."); i >= 0 {
|
||||||
|
short = short[:i]
|
||||||
|
}
|
||||||
|
for _, bad := range badWindowsNames {
|
||||||
|
if strings.EqualFold(bad, short) {
|
||||||
|
return fmt.Errorf("disallowed path element %q", elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject path components that look like Windows short-names.
|
||||||
|
// Those usually end in a tilde followed by one or more ASCII digits.
|
||||||
|
if tilde := strings.LastIndexByte(short, '~'); tilde >= 0 && tilde < len(short)-1 {
|
||||||
|
suffix := short[tilde+1:]
|
||||||
|
suffixIsDigits := true
|
||||||
|
for _, r := range suffix {
|
||||||
|
if r < '0' || r > '9' {
|
||||||
|
suffixIsDigits = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if suffixIsDigits {
|
||||||
|
return fmt.Errorf("trailing tilde and digits in path element")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pathOK reports whether r can appear in an import path element.
|
||||||
|
//
|
||||||
|
// NOTE: This function DIVERGES from module mode pathOK by accepting Unicode letters.
|
||||||
|
func pathOK(r rune) bool {
|
||||||
|
if r < utf8.RuneSelf {
|
||||||
|
return r == '+' || r == '-' || r == '.' || r == '_' || r == '~' ||
|
||||||
|
'0' <= r && r <= '9' ||
|
||||||
|
'A' <= r && r <= 'Z' ||
|
||||||
|
'a' <= r && r <= 'z'
|
||||||
|
}
|
||||||
|
return unicode.IsLetter(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileNameOK reports whether r can appear in a file name.
|
||||||
|
// For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters.
|
||||||
|
// If we expand the set of allowed characters here, we have to
|
||||||
|
// work harder at detecting potential case-folding and normalization collisions.
|
||||||
|
// See note about "safe encoding" below.
|
||||||
|
func fileNameOK(r rune) bool {
|
||||||
|
if r < utf8.RuneSelf {
|
||||||
|
// Entire set of ASCII punctuation, from which we remove characters:
|
||||||
|
// ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
|
||||||
|
// We disallow some shell special characters: " ' * < > ? ` |
|
||||||
|
// (Note that some of those are disallowed by the Windows file system as well.)
|
||||||
|
// We also disallow path separators / : and \ (fileNameOK is only called on path element characters).
|
||||||
|
// We allow spaces (U+0020) in file names.
|
||||||
|
const allowed = "!#$%&()+,-.=@[]^_{}~ "
|
||||||
|
if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for i := 0; i < len(allowed); i++ {
|
||||||
|
if rune(allowed[i]) == r {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// It may be OK to add more ASCII punctuation here, but only carefully.
|
||||||
|
// For example Windows disallows < > \, and macOS disallows :, so we must not allow those.
|
||||||
|
return unicode.IsLetter(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// badWindowsNames are the reserved file path elements on Windows.
|
||||||
|
// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
|
||||||
|
var badWindowsNames = []string{
|
||||||
|
"CON",
|
||||||
|
"PRN",
|
||||||
|
"AUX",
|
||||||
|
"NUL",
|
||||||
|
"COM1",
|
||||||
|
"COM2",
|
||||||
|
"COM3",
|
||||||
|
"COM4",
|
||||||
|
"COM5",
|
||||||
|
"COM6",
|
||||||
|
"COM7",
|
||||||
|
"COM8",
|
||||||
|
"COM9",
|
||||||
|
"LPT1",
|
||||||
|
"LPT2",
|
||||||
|
"LPT3",
|
||||||
|
"LPT4",
|
||||||
|
"LPT5",
|
||||||
|
"LPT6",
|
||||||
|
"LPT7",
|
||||||
|
"LPT8",
|
||||||
|
"LPT9",
|
||||||
|
}
|
|
@ -970,10 +970,14 @@ func matchGoImport(imports []metaImport, importPath string) (metaImport, error)
|
||||||
|
|
||||||
// expand rewrites s to replace {k} with match[k] for each key k in match.
|
// expand rewrites s to replace {k} with match[k] for each key k in match.
|
||||||
func expand(match map[string]string, s string) string {
|
func expand(match map[string]string, s string) string {
|
||||||
|
// We want to replace each match exactly once, and the result of expansion
|
||||||
|
// must not depend on the iteration order through the map.
|
||||||
|
// A strings.Replacer has exactly the properties we're looking for.
|
||||||
|
oldNew := make([]string, 0, 2*len(match))
|
||||||
for k, v := range match {
|
for k, v := range match {
|
||||||
s = strings.Replace(s, "{"+k+"}", v, -1)
|
oldNew = append(oldNew, "{"+k+"}", v)
|
||||||
}
|
}
|
||||||
return s
|
return strings.NewReplacer(oldNew...).Replace(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// vcsPaths defines the meaning of import paths referring to
|
// vcsPaths defines the meaning of import paths referring to
|
||||||
|
|
|
@ -38,32 +38,16 @@ func SystemCertPool() (*CertPool, error) {
|
||||||
return loadSystemRoots()
|
return loadSystemRoots()
|
||||||
}
|
}
|
||||||
|
|
||||||
// findVerifiedParents attempts to find certificates in s which have signed the
|
// findPotentialParents returns the indexes of certificates in s which might
|
||||||
// given certificate. If any candidates were rejected then errCert will be set
|
// have signed cert. The caller must not modify the returned slice.
|
||||||
// to one of them, arbitrarily, and err will contain the reason that it was
|
func (s *CertPool) findPotentialParents(cert *Certificate) []int {
|
||||||
// rejected.
|
|
||||||
func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int, errCert *Certificate, err error) {
|
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
var candidates []int
|
|
||||||
|
|
||||||
if len(cert.AuthorityKeyId) > 0 {
|
if len(cert.AuthorityKeyId) > 0 {
|
||||||
candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)]
|
return s.bySubjectKeyId[string(cert.AuthorityKeyId)]
|
||||||
}
|
}
|
||||||
if len(candidates) == 0 {
|
return s.byName[string(cert.RawIssuer)]
|
||||||
candidates = s.byName[string(cert.RawIssuer)]
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range candidates {
|
|
||||||
if err = cert.CheckSignatureFrom(s.certs[c]); err == nil {
|
|
||||||
parents = append(parents, c)
|
|
||||||
} else {
|
|
||||||
errCert = s.certs[c]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CertPool) contains(cert *Certificate) bool {
|
func (s *CertPool) contains(cert *Certificate) bool {
|
||||||
|
|
|
@ -765,7 +765,7 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
|
||||||
if opts.Roots.contains(c) {
|
if opts.Roots.contains(c) {
|
||||||
candidateChains = append(candidateChains, []*Certificate{c})
|
candidateChains = append(candidateChains, []*Certificate{c})
|
||||||
} else {
|
} else {
|
||||||
if candidateChains, err = c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts); err != nil {
|
if candidateChains, err = c.buildChains(nil, []*Certificate{c}, nil, &opts); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -802,58 +802,74 @@ func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
// maxChainSignatureChecks is the maximum number of CheckSignatureFrom calls
|
||||||
possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c)
|
// that an invocation of buildChains will (tranistively) make. Most chains are
|
||||||
nextRoot:
|
// less than 15 certificates long, so this leaves space for multiple chains and
|
||||||
for _, rootNum := range possibleRoots {
|
// for failed checks due to different intermediates having the same Subject.
|
||||||
root := opts.Roots.certs[rootNum]
|
const maxChainSignatureChecks = 100
|
||||||
|
|
||||||
|
func (c *Certificate) buildChains(cache map[*Certificate][][]*Certificate, currentChain []*Certificate, sigChecks *int, opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||||
|
var (
|
||||||
|
hintErr error
|
||||||
|
hintCert *Certificate
|
||||||
|
)
|
||||||
|
|
||||||
|
considerCandidate := func(certType int, candidate *Certificate) {
|
||||||
for _, cert := range currentChain {
|
for _, cert := range currentChain {
|
||||||
if cert.Equal(root) {
|
if cert.Equal(candidate) {
|
||||||
continue nextRoot
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = root.isValid(rootCertificate, currentChain, opts)
|
if sigChecks == nil {
|
||||||
if err != nil {
|
sigChecks = new(int)
|
||||||
continue
|
}
|
||||||
|
*sigChecks++
|
||||||
|
if *sigChecks > maxChainSignatureChecks {
|
||||||
|
err = errors.New("x509: signature check attempts limit reached while verifying certificate chain")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.CheckSignatureFrom(candidate); err != nil {
|
||||||
|
if hintErr == nil {
|
||||||
|
hintErr = err
|
||||||
|
hintCert = candidate
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = candidate.isValid(certType, currentChain, opts)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch certType {
|
||||||
|
case rootCertificate:
|
||||||
|
chains = append(chains, appendToFreshChain(currentChain, candidate))
|
||||||
|
case intermediateCertificate:
|
||||||
|
if cache == nil {
|
||||||
|
cache = make(map[*Certificate][][]*Certificate)
|
||||||
|
}
|
||||||
|
childChains, ok := cache[candidate]
|
||||||
|
if !ok {
|
||||||
|
childChains, err = candidate.buildChains(cache, appendToFreshChain(currentChain, candidate), sigChecks, opts)
|
||||||
|
cache[candidate] = childChains
|
||||||
|
}
|
||||||
|
chains = append(chains, childChains...)
|
||||||
}
|
}
|
||||||
chains = append(chains, appendToFreshChain(currentChain, root))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
possibleIntermediates, failedIntermediate, intermediateErr := opts.Intermediates.findVerifiedParents(c)
|
for _, rootNum := range opts.Roots.findPotentialParents(c) {
|
||||||
nextIntermediate:
|
considerCandidate(rootCertificate, opts.Roots.certs[rootNum])
|
||||||
for _, intermediateNum := range possibleIntermediates {
|
}
|
||||||
intermediate := opts.Intermediates.certs[intermediateNum]
|
for _, intermediateNum := range opts.Intermediates.findPotentialParents(c) {
|
||||||
for _, cert := range currentChain {
|
considerCandidate(intermediateCertificate, opts.Intermediates.certs[intermediateNum])
|
||||||
if cert.Equal(intermediate) {
|
|
||||||
continue nextIntermediate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = intermediate.isValid(intermediateCertificate, currentChain, opts)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var childChains [][]*Certificate
|
|
||||||
childChains, ok := cache[intermediateNum]
|
|
||||||
if !ok {
|
|
||||||
childChains, err = intermediate.buildChains(cache, appendToFreshChain(currentChain, intermediate), opts)
|
|
||||||
cache[intermediateNum] = childChains
|
|
||||||
}
|
|
||||||
chains = append(chains, childChains...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(chains) > 0 {
|
if len(chains) > 0 {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(chains) == 0 && err == nil {
|
if len(chains) == 0 && err == nil {
|
||||||
hintErr := rootErr
|
|
||||||
hintCert := failedRoot
|
|
||||||
if hintErr == nil {
|
|
||||||
hintErr = intermediateErr
|
|
||||||
hintCert = failedIntermediate
|
|
||||||
}
|
|
||||||
err = UnknownAuthorityError{c, hintErr, hintCert}
|
err = UnknownAuthorityError{c, hintErr, hintCert}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,15 @@
|
||||||
package x509
|
package x509
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -1707,3 +1712,117 @@ UNhY4JhezH9gQYqvDMWrWDAbBgNVHSMEFDASgBArF29S5Bnqw7de8GzGA1nfMAoG
|
||||||
CCqGSM49BAMCA0gAMEUCIQClA3d4tdrDu9Eb5ZBpgyC+fU1xTZB0dKQHz6M5fPZA
|
CCqGSM49BAMCA0gAMEUCIQClA3d4tdrDu9Eb5ZBpgyC+fU1xTZB0dKQHz6M5fPZA
|
||||||
2AIgN96lM+CPGicwhN24uQI6flOsO3H0TJ5lNzBYLtnQtlc=
|
2AIgN96lM+CPGicwhN24uQI6flOsO3H0TJ5lNzBYLtnQtlc=
|
||||||
-----END CERTIFICATE-----`
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
func generateCert(cn string, isCA bool, issuer *Certificate, issuerKey crypto.PrivateKey) (*Certificate, crypto.PrivateKey, error) {
|
||||||
|
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
|
serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)
|
||||||
|
|
||||||
|
template := &Certificate{
|
||||||
|
SerialNumber: serialNumber,
|
||||||
|
Subject: pkix.Name{CommonName: cn},
|
||||||
|
NotBefore: time.Now().Add(-1 * time.Hour),
|
||||||
|
NotAfter: time.Now().Add(24 * time.Hour),
|
||||||
|
|
||||||
|
KeyUsage: KeyUsageKeyEncipherment | KeyUsageDigitalSignature | KeyUsageCertSign,
|
||||||
|
ExtKeyUsage: []ExtKeyUsage{ExtKeyUsageServerAuth},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
IsCA: isCA,
|
||||||
|
}
|
||||||
|
if issuer == nil {
|
||||||
|
issuer = template
|
||||||
|
issuerKey = priv
|
||||||
|
}
|
||||||
|
|
||||||
|
derBytes, err := CreateCertificate(rand.Reader, template, issuer, priv.Public(), issuerKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
cert, err := ParseCertificate(derBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cert, priv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPathologicalChain(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping generation of a long chain of certificates in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a chain where all intermediates share the same subject, to hit the
|
||||||
|
// path building worst behavior.
|
||||||
|
roots, intermediates := NewCertPool(), NewCertPool()
|
||||||
|
|
||||||
|
parent, parentKey, err := generateCert("Root CA", true, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
roots.AddCert(parent)
|
||||||
|
|
||||||
|
for i := 1; i < 100; i++ {
|
||||||
|
parent, parentKey, err = generateCert("Intermediate CA", true, parent, parentKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
intermediates.AddCert(parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf, _, err := generateCert("Leaf", false, parent, parentKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
_, err = leaf.Verify(VerifyOptions{
|
||||||
|
Roots: roots,
|
||||||
|
Intermediates: intermediates,
|
||||||
|
})
|
||||||
|
t.Logf("verification took %v", time.Since(start))
|
||||||
|
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "signature check attempts limit") {
|
||||||
|
t.Errorf("expected verification to fail with a signature checks limit error; got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLongChain(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping generation of a long chain of certificates in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
roots, intermediates := NewCertPool(), NewCertPool()
|
||||||
|
|
||||||
|
parent, parentKey, err := generateCert("Root CA", true, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
roots.AddCert(parent)
|
||||||
|
|
||||||
|
for i := 1; i < 15; i++ {
|
||||||
|
name := fmt.Sprintf("Intermediate CA #%d", i)
|
||||||
|
parent, parentKey, err = generateCert(name, true, parent, parentKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
intermediates.AddCert(parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf, _, err := generateCert("Leaf", false, parent, parentKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
if _, err := leaf.Verify(VerifyOptions{
|
||||||
|
Roots: roots,
|
||||||
|
Intermediates: intermediates,
|
||||||
|
}); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
t.Logf("verification took %v", time.Since(start))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue