mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-09 21:17:09 +08:00
vendor: initial vendor
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
163
vendor/github.com/theupdateframework/notary/trustpinning/trustpin.go
generated
vendored
Normal file
163
vendor/github.com/theupdateframework/notary/trustpinning/trustpin.go
generated
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
package trustpinning
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
// TrustPinConfig represents the configuration under the trust_pinning section of the config file
|
||||
// This struct represents the preferred way to bootstrap trust for this repository
|
||||
// This is fully optional. If left at the default, uninitialized value Notary will use TOFU over
|
||||
// HTTPS.
|
||||
// You can use this to provide certificates or a CA to pin to as a root of trust for a GUN.
|
||||
// These are used with the following precedence:
|
||||
//
|
||||
// 1. Certs
|
||||
// 2. CA
|
||||
// 3. TOFUS (TOFU over HTTPS)
|
||||
//
|
||||
// Only one trust pinning option will be used to validate a particular GUN.
|
||||
type TrustPinConfig struct {
|
||||
// CA maps a GUN prefix to file paths containing the root CA.
|
||||
// This file can contain multiple root certificates, bundled in separate PEM blocks.
|
||||
CA map[string]string
|
||||
// Certs maps a GUN to a list of certificate IDs
|
||||
Certs map[string][]string
|
||||
// DisableTOFU, when true, disables "Trust On First Use" of new key data
|
||||
// This is false by default, which means new key data will always be trusted the first time it is seen.
|
||||
DisableTOFU bool
|
||||
}
|
||||
|
||||
type trustPinChecker struct {
|
||||
gun data.GUN
|
||||
config TrustPinConfig
|
||||
pinnedCAPool *x509.CertPool
|
||||
pinnedCertIDs []string
|
||||
}
|
||||
|
||||
// CertChecker is a function type that will be used to check leaf certs against pinned trust
|
||||
type CertChecker func(leafCert *x509.Certificate, intCerts []*x509.Certificate) bool
|
||||
|
||||
// NewTrustPinChecker returns a new certChecker function from a TrustPinConfig for a GUN
|
||||
func NewTrustPinChecker(trustPinConfig TrustPinConfig, gun data.GUN, firstBootstrap bool) (CertChecker, error) {
|
||||
t := trustPinChecker{gun: gun, config: trustPinConfig}
|
||||
// Determine the mode, and if it's even valid
|
||||
if pinnedCerts, ok := trustPinConfig.Certs[gun.String()]; ok {
|
||||
logrus.Debugf("trust-pinning using Cert IDs")
|
||||
t.pinnedCertIDs = pinnedCerts
|
||||
return t.certsCheck, nil
|
||||
}
|
||||
var ok bool
|
||||
t.pinnedCertIDs, ok = wildcardMatch(gun, trustPinConfig.Certs)
|
||||
if ok {
|
||||
return t.certsCheck, nil
|
||||
}
|
||||
|
||||
if caFilepath, err := getPinnedCAFilepathByPrefix(gun, trustPinConfig); err == nil {
|
||||
logrus.Debugf("trust-pinning using root CA bundle at: %s", caFilepath)
|
||||
|
||||
// Try to add the CA certs from its bundle file to our certificate store,
|
||||
// and use it to validate certs in the root.json later
|
||||
caCerts, err := utils.LoadCertBundleFromFile(caFilepath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load root cert from CA path")
|
||||
}
|
||||
// Now only consider certificates that are direct children from this CA cert chain
|
||||
caRootPool := x509.NewCertPool()
|
||||
for _, caCert := range caCerts {
|
||||
if err = utils.ValidateCertificate(caCert, true); err != nil {
|
||||
logrus.Debugf("ignoring root CA certificate with CN %s in bundle: %s", caCert.Subject.CommonName, err)
|
||||
continue
|
||||
}
|
||||
caRootPool.AddCert(caCert)
|
||||
}
|
||||
// If we didn't have any valid CA certs, error out
|
||||
if len(caRootPool.Subjects()) == 0 {
|
||||
return nil, fmt.Errorf("invalid CA certs provided")
|
||||
}
|
||||
t.pinnedCAPool = caRootPool
|
||||
return t.caCheck, nil
|
||||
}
|
||||
|
||||
// If TOFUs is disabled and we don't have any previous trusted root data for this GUN, we error out
|
||||
if trustPinConfig.DisableTOFU && firstBootstrap {
|
||||
return nil, fmt.Errorf("invalid trust pinning specified")
|
||||
|
||||
}
|
||||
return t.tofusCheck, nil
|
||||
}
|
||||
|
||||
func (t trustPinChecker) certsCheck(leafCert *x509.Certificate, intCerts []*x509.Certificate) bool {
|
||||
// reconstruct the leaf + intermediate cert chain, which is bundled as {leaf, intermediates...},
|
||||
// in order to get the matching id in the root file
|
||||
key, err := utils.CertBundleToKey(leafCert, intCerts)
|
||||
if err != nil {
|
||||
logrus.Debug("error creating cert bundle: ", err.Error())
|
||||
return false
|
||||
}
|
||||
return utils.StrSliceContains(t.pinnedCertIDs, key.ID())
|
||||
}
|
||||
|
||||
func (t trustPinChecker) caCheck(leafCert *x509.Certificate, intCerts []*x509.Certificate) bool {
|
||||
// Use intermediate certificates included in the root TUF metadata for our validation
|
||||
caIntPool := x509.NewCertPool()
|
||||
for _, intCert := range intCerts {
|
||||
caIntPool.AddCert(intCert)
|
||||
}
|
||||
// Attempt to find a valid certificate chain from the leaf cert to CA root
|
||||
// Use this certificate if such a valid chain exists (possibly using intermediates)
|
||||
var err error
|
||||
if _, err = leafCert.Verify(x509.VerifyOptions{Roots: t.pinnedCAPool, Intermediates: caIntPool}); err == nil {
|
||||
return true
|
||||
}
|
||||
logrus.Debugf("unable to find a valid certificate chain from leaf cert to CA root: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
func (t trustPinChecker) tofusCheck(leafCert *x509.Certificate, intCerts []*x509.Certificate) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Will return the CA filepath corresponding to the most specific (longest) entry in the map that is still a prefix
|
||||
// of the provided gun. Returns an error if no entry matches this GUN as a prefix.
|
||||
func getPinnedCAFilepathByPrefix(gun data.GUN, t TrustPinConfig) (string, error) {
|
||||
specificGUN := ""
|
||||
specificCAFilepath := ""
|
||||
foundCA := false
|
||||
for gunPrefix, caFilepath := range t.CA {
|
||||
if strings.HasPrefix(gun.String(), gunPrefix) && len(gunPrefix) >= len(specificGUN) {
|
||||
specificGUN = gunPrefix
|
||||
specificCAFilepath = caFilepath
|
||||
foundCA = true
|
||||
}
|
||||
}
|
||||
if !foundCA {
|
||||
return "", fmt.Errorf("could not find pinned CA for GUN: %s", gun)
|
||||
}
|
||||
return specificCAFilepath, nil
|
||||
}
|
||||
|
||||
// wildcardMatch will attempt to match the most specific (longest prefix) wildcarded
|
||||
// trustpinning option for key IDs. Given the simple globbing and the use of maps,
|
||||
// it is impossible to have two different prefixes of equal length.
|
||||
// This logic also solves the issue of Go's randomization of map iteration.
|
||||
func wildcardMatch(gun data.GUN, certs map[string][]string) ([]string, bool) {
|
||||
var (
|
||||
longest = ""
|
||||
ids []string
|
||||
)
|
||||
for gunPrefix, keyIDs := range certs {
|
||||
if strings.HasSuffix(gunPrefix, "*") {
|
||||
if strings.HasPrefix(gun.String(), gunPrefix[:len(gunPrefix)-1]) && len(gunPrefix) > len(longest) {
|
||||
longest = gunPrefix
|
||||
ids = keyIDs
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids, ids != nil
|
||||
}
|
Reference in New Issue
Block a user