This commit is contained in:
37
vendor/github.com/theupdateframework/notary/trustpinning/ca.crt
generated
vendored
Normal file
37
vendor/github.com/theupdateframework/notary/trustpinning/ca.crt
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
lKmXlK4dTg==
|
||||
-----END CERTIFICATE-----
|
||||
|
304
vendor/github.com/theupdateframework/notary/trustpinning/certs.go
generated
vendored
Normal file
304
vendor/github.com/theupdateframework/notary/trustpinning/certs.go
generated
vendored
Normal file
@ -0,0 +1,304 @@
|
||||
package trustpinning
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/signed"
|
||||
"github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
const wildcard = "*"
|
||||
|
||||
// ErrValidationFail is returned when there is no valid trusted certificates
|
||||
// being served inside of the roots.json
|
||||
type ErrValidationFail struct {
|
||||
Reason string
|
||||
}
|
||||
|
||||
// ErrValidationFail is returned when there is no valid trusted certificates
|
||||
// being served inside of the roots.json
|
||||
func (err ErrValidationFail) Error() string {
|
||||
return fmt.Sprintf("could not validate the path to a trusted root: %s", err.Reason)
|
||||
}
|
||||
|
||||
// ErrRootRotationFail is returned when we fail to do a full root key rotation
|
||||
// by either failing to add the new root certificate, or delete the old ones
|
||||
type ErrRootRotationFail struct {
|
||||
Reason string
|
||||
}
|
||||
|
||||
// ErrRootRotationFail is returned when we fail to do a full root key rotation
|
||||
// by either failing to add the new root certificate, or delete the old ones
|
||||
func (err ErrRootRotationFail) Error() string {
|
||||
return fmt.Sprintf("could not rotate trust to a new trusted root: %s", err.Reason)
|
||||
}
|
||||
|
||||
func prettyFormatCertIDs(certs map[string]*x509.Certificate) string {
|
||||
ids := make([]string, 0, len(certs))
|
||||
for id := range certs {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return strings.Join(ids, ", ")
|
||||
}
|
||||
|
||||
/*
|
||||
ValidateRoot receives a new root, validates its correctness and attempts to
|
||||
do root key rotation if needed.
|
||||
|
||||
First we check if we have any trusted certificates for a particular GUN in
|
||||
a previous root, if we have one. If the previous root is not nil and we find
|
||||
certificates for this GUN, we've already seen this repository before, and
|
||||
have a list of trusted certificates for it. In this case, we use this list of
|
||||
certificates to attempt to validate this root file.
|
||||
|
||||
If the previous validation succeeds, we check the integrity of the root by
|
||||
making sure that it is validated by itself. This means that we will attempt to
|
||||
validate the root data with the certificates that are included in the root keys
|
||||
themselves.
|
||||
|
||||
However, if we do not have any current trusted certificates for this GUN, we
|
||||
check if there are any pinned certificates specified in the trust_pinning section
|
||||
of the notary client config. If this section specifies a Certs section with this
|
||||
GUN, we attempt to validate that the certificates present in the downloaded root
|
||||
file match the pinned ID.
|
||||
|
||||
If the Certs section is empty for this GUN, we check if the trust_pinning
|
||||
section specifies a CA section specified in the config for this GUN. If so, we check
|
||||
that the specified CA is valid and has signed a certificate included in the downloaded
|
||||
root file. The specified CA can be a prefix for this GUN.
|
||||
|
||||
If both the Certs and CA configs do not match this GUN, we fall back to the TOFU
|
||||
section in the config: if true, we trust certificates specified in the root for
|
||||
this GUN. If later we see a different certificate for that certificate, we return
|
||||
an ErrValidationFailed error.
|
||||
|
||||
Note that since we only allow trust data to be downloaded over an HTTPS channel
|
||||
we are using the current public PKI to validate the first download of the certificate
|
||||
adding an extra layer of security over the normal (SSH style) trust model.
|
||||
We shall call this: TOFUS.
|
||||
|
||||
Validation failure at any step will result in an ErrValidationFailed error.
|
||||
*/
|
||||
func ValidateRoot(prevRoot *data.SignedRoot, root *data.Signed, gun data.GUN, trustPinning TrustPinConfig) (*data.SignedRoot, error) {
|
||||
logrus.Debugf("entered ValidateRoot with dns: %s", gun)
|
||||
signedRoot, err := data.RootFromSigned(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rootRole, err := signedRoot.BuildBaseRole(data.CanonicalRootRole)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Retrieve all the leaf and intermediate certificates in root for which the CN matches the GUN
|
||||
allLeafCerts, allIntCerts := parseAllCerts(signedRoot)
|
||||
certsFromRoot, err := validRootLeafCerts(allLeafCerts, gun, true)
|
||||
validIntCerts := validRootIntCerts(allIntCerts)
|
||||
|
||||
if err != nil {
|
||||
logrus.Debugf("error retrieving valid leaf certificates for: %s, %v", gun, err)
|
||||
return nil, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}
|
||||
}
|
||||
|
||||
logrus.Debugf("found %d leaf certs, of which %d are valid leaf certs for %s", len(allLeafCerts), len(certsFromRoot), gun)
|
||||
|
||||
// If we have a previous root, let's try to use it to validate that this new root is valid.
|
||||
havePrevRoot := prevRoot != nil
|
||||
if havePrevRoot {
|
||||
// Retrieve all the trusted certificates from our previous root
|
||||
// Note that we do not validate expiries here since our originally trusted root might have expired certs
|
||||
allTrustedLeafCerts, allTrustedIntCerts := parseAllCerts(prevRoot)
|
||||
trustedLeafCerts, err := validRootLeafCerts(allTrustedLeafCerts, gun, false)
|
||||
if err != nil {
|
||||
return nil, &ErrValidationFail{Reason: "could not retrieve trusted certs from previous root role data"}
|
||||
}
|
||||
|
||||
// Use the certificates we found in the previous root for the GUN to verify its signatures
|
||||
// This could potentially be an empty set, in which case we will fail to verify
|
||||
logrus.Debugf("found %d valid root leaf certificates for %s: %s", len(trustedLeafCerts), gun,
|
||||
prettyFormatCertIDs(trustedLeafCerts))
|
||||
|
||||
// Extract the previous root's threshold for signature verification
|
||||
prevRootRoleData, ok := prevRoot.Signed.Roles[data.CanonicalRootRole]
|
||||
if !ok {
|
||||
return nil, &ErrValidationFail{Reason: "could not retrieve previous root role data"}
|
||||
}
|
||||
err = signed.VerifySignatures(
|
||||
root, data.BaseRole{Keys: utils.CertsToKeys(trustedLeafCerts, allTrustedIntCerts), Threshold: prevRootRoleData.Threshold})
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to verify TUF data for: %s, %v", gun, err)
|
||||
return nil, &ErrRootRotationFail{Reason: "failed to validate data with current trusted certificates"}
|
||||
}
|
||||
// Clear the IsValid marks we could have received from VerifySignatures
|
||||
for i := range root.Signatures {
|
||||
root.Signatures[i].IsValid = false
|
||||
}
|
||||
}
|
||||
|
||||
// Regardless of having a previous root or not, confirm that the new root validates against the trust pinning
|
||||
logrus.Debugf("checking root against trust_pinning config for %s", gun)
|
||||
trustPinCheckFunc, err := NewTrustPinChecker(trustPinning, gun, !havePrevRoot)
|
||||
if err != nil {
|
||||
return nil, &ErrValidationFail{Reason: err.Error()}
|
||||
}
|
||||
|
||||
validPinnedCerts := map[string]*x509.Certificate{}
|
||||
for id, cert := range certsFromRoot {
|
||||
logrus.Debugf("checking trust-pinning for cert: %s", id)
|
||||
if ok := trustPinCheckFunc(cert, validIntCerts[id]); !ok {
|
||||
logrus.Debugf("trust-pinning check failed for cert: %s", id)
|
||||
continue
|
||||
}
|
||||
validPinnedCerts[id] = cert
|
||||
}
|
||||
if len(validPinnedCerts) == 0 {
|
||||
return nil, &ErrValidationFail{Reason: "unable to match any certificates to trust_pinning config"}
|
||||
}
|
||||
certsFromRoot = validPinnedCerts
|
||||
|
||||
// Validate the integrity of the new root (does it have valid signatures)
|
||||
// Note that certsFromRoot is guaranteed to be unchanged only if we had prior cert data for this GUN or enabled TOFUS
|
||||
// If we attempted to pin a certain certificate or CA, certsFromRoot could have been pruned accordingly
|
||||
err = signed.VerifySignatures(root, data.BaseRole{
|
||||
Keys: utils.CertsToKeys(certsFromRoot, validIntCerts), Threshold: rootRole.Threshold})
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to verify TUF data for: %s, %v", gun, err)
|
||||
return nil, &ErrValidationFail{Reason: "failed to validate integrity of roots"}
|
||||
}
|
||||
|
||||
logrus.Debugf("root validation succeeded for %s", gun)
|
||||
// Call RootFromSigned to make sure we pick up on the IsValid markings from VerifySignatures
|
||||
return data.RootFromSigned(root)
|
||||
}
|
||||
|
||||
// MatchCNToGun checks that the common name in a cert is valid for the given gun.
|
||||
// This allows wildcards as suffixes, e.g. `namespace/*`
|
||||
func MatchCNToGun(commonName string, gun data.GUN) bool {
|
||||
if strings.HasSuffix(commonName, wildcard) {
|
||||
prefix := strings.TrimRight(commonName, wildcard)
|
||||
logrus.Debugf("checking gun %s against wildcard prefix %s", gun, prefix)
|
||||
return strings.HasPrefix(gun.String(), prefix)
|
||||
}
|
||||
return commonName == gun.String()
|
||||
}
|
||||
|
||||
// validRootLeafCerts returns a list of possibly (if checkExpiry is true) non-expired, non-sha1 certificates
|
||||
// found in root whose Common-Names match the provided GUN. Note that this
|
||||
// "validity" alone does not imply any measure of trust.
|
||||
func validRootLeafCerts(allLeafCerts map[string]*x509.Certificate, gun data.GUN, checkExpiry bool) (map[string]*x509.Certificate, error) {
|
||||
validLeafCerts := make(map[string]*x509.Certificate)
|
||||
|
||||
// Go through every leaf certificate and check that the CN matches the gun
|
||||
for id, cert := range allLeafCerts {
|
||||
// Validate that this leaf certificate has a CN that matches the gun
|
||||
if !MatchCNToGun(cert.Subject.CommonName, gun) {
|
||||
logrus.Debugf("error leaf certificate CN: %s doesn't match the given GUN: %s",
|
||||
cert.Subject.CommonName, gun)
|
||||
continue
|
||||
}
|
||||
// Make sure the certificate is not expired if checkExpiry is true
|
||||
// and warn if it hasn't expired yet but is within 6 months of expiry
|
||||
if err := utils.ValidateCertificate(cert, checkExpiry); err != nil {
|
||||
logrus.Debugf("%s is invalid: %s", id, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
validLeafCerts[id] = cert
|
||||
}
|
||||
|
||||
if len(validLeafCerts) < 1 {
|
||||
logrus.Debugf("didn't find any valid leaf certificates for %s", gun)
|
||||
return nil, errors.New("no valid leaf certificates found in any of the root keys")
|
||||
}
|
||||
|
||||
logrus.Debugf("found %d valid leaf certificates for %s: %s", len(validLeafCerts), gun,
|
||||
prettyFormatCertIDs(validLeafCerts))
|
||||
return validLeafCerts, nil
|
||||
}
|
||||
|
||||
// validRootIntCerts filters the passed in structure of intermediate certificates to only include non-expired, non-sha1 certificates
|
||||
// Note that this "validity" alone does not imply any measure of trust.
|
||||
func validRootIntCerts(allIntCerts map[string][]*x509.Certificate) map[string][]*x509.Certificate {
|
||||
validIntCerts := make(map[string][]*x509.Certificate)
|
||||
|
||||
// Go through every leaf cert ID, and build its valid intermediate certificate list
|
||||
for leafID, intCertList := range allIntCerts {
|
||||
for _, intCert := range intCertList {
|
||||
if err := utils.ValidateCertificate(intCert, true); err != nil {
|
||||
continue
|
||||
}
|
||||
validIntCerts[leafID] = append(validIntCerts[leafID], intCert)
|
||||
}
|
||||
|
||||
}
|
||||
return validIntCerts
|
||||
}
|
||||
|
||||
// parseAllCerts returns two maps, one with all of the leafCertificates and one
|
||||
// with all the intermediate certificates found in signedRoot
|
||||
func parseAllCerts(signedRoot *data.SignedRoot) (map[string]*x509.Certificate, map[string][]*x509.Certificate) {
|
||||
if signedRoot == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
leafCerts := make(map[string]*x509.Certificate)
|
||||
intCerts := make(map[string][]*x509.Certificate)
|
||||
|
||||
// Before we loop through all root keys available, make sure any exist
|
||||
rootRoles, ok := signedRoot.Signed.Roles[data.CanonicalRootRole]
|
||||
if !ok {
|
||||
logrus.Debugf("tried to parse certificates from invalid root signed data")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
logrus.Debugf("found the following root keys: %v", rootRoles.KeyIDs)
|
||||
// Iterate over every keyID for the root role inside of roots.json
|
||||
for _, keyID := range rootRoles.KeyIDs {
|
||||
// check that the key exists in the signed root keys map
|
||||
key, ok := signedRoot.Signed.Keys[keyID]
|
||||
if !ok {
|
||||
logrus.Debugf("error while getting data for keyID: %s", keyID)
|
||||
continue
|
||||
}
|
||||
|
||||
// Decode all the x509 certificates that were bundled with this
|
||||
// Specific root key
|
||||
decodedCerts, err := utils.LoadCertBundleFromPEM(key.Public())
|
||||
if err != nil {
|
||||
logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Get all non-CA certificates in the decoded certificates
|
||||
leafCertList := utils.GetLeafCerts(decodedCerts)
|
||||
|
||||
// If we got no leaf certificates or we got more than one, fail
|
||||
if len(leafCertList) != 1 {
|
||||
logrus.Debugf("invalid chain due to leaf certificate missing or too many leaf certificates for keyID: %s", keyID)
|
||||
continue
|
||||
}
|
||||
// If we found a leaf certificate, assert that the cert bundle started with a leaf
|
||||
if decodedCerts[0].IsCA {
|
||||
logrus.Debugf("invalid chain due to leaf certificate not being first certificate for keyID: %s", keyID)
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the ID of the leaf certificate
|
||||
leafCert := leafCertList[0]
|
||||
|
||||
// Store the leaf cert in the map
|
||||
leafCerts[key.ID()] = leafCert
|
||||
|
||||
// Get all the remainder certificates marked as a CA to be used as intermediates
|
||||
intermediateCerts := utils.GetIntermediateCerts(decodedCerts)
|
||||
intCerts[key.ID()] = intermediateCerts
|
||||
}
|
||||
|
||||
return leafCerts, intCerts
|
||||
}
|
31
vendor/github.com/theupdateframework/notary/trustpinning/test.crt
generated
vendored
Normal file
31
vendor/github.com/theupdateframework/notary/trustpinning/test.crt
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFKzCCAxWgAwIBAgIQRyp9QqcJfd3ayqdjiz8xIDALBgkqhkiG9w0BAQswODEa
|
||||
MBgGA1UEChMRZG9ja2VyLmNvbS9ub3RhcnkxGjAYBgNVBAMTEWRvY2tlci5jb20v
|
||||
bm90YXJ5MB4XDTE1MDcxNzA2MzQyM1oXDTE3MDcxNjA2MzQyM1owODEaMBgGA1UE
|
||||
ChMRZG9ja2VyLmNvbS9ub3RhcnkxGjAYBgNVBAMTEWRvY2tlci5jb20vbm90YXJ5
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoQffrzsYnsH8vGf4Jh55
|
||||
Cj5wrjUGzD/sHkaFHptjJ6ToJGJv5yMAPxzyInu5sIoGLJapnYVBoAU0YgI9qlAc
|
||||
YA6SxaSwgm6rpvmnl8Qn0qc6ger3inpGaUJylWHuPwWkvcimQAqHZx2dQtL7g6kp
|
||||
rmKeTWpWoWLw3JoAUZUVhZMd6a22ZL/DvAw+Hrogbz4XeyahFb9IH402zPxN6vga
|
||||
JEFTF0Ji1jtNg0Mo4pb9SHsMsiw+LZK7SffHVKPxvd21m/biNmwsgExA3U8OOG8p
|
||||
uygfacys5c8+ZrX+ZFG/cvwKz0k6/QfJU40s6MhXw5C2WttdVmsG9/7rGFYjHoIJ
|
||||
weDyxgWk7vxKzRJI/un7cagDIaQsKrJQcCHIGFRlpIR5TwX7vl3R7cRncrDRMVvc
|
||||
VSEG2esxbw7jtzIp/ypnVRxcOny7IypyjKqVeqZ6HgxZtTBVrF1O/aHo2kvlwyRS
|
||||
Aus4kvh6z3+jzTm9EzfXiPQzY9BEk5gOLxhW9rc6UhlS+pe5lkaN/Hyqy/lPuq89
|
||||
fMr2rr7lf5WFdFnze6WNYMAaW7dNA4NE0dyD53428ZLXxNVPL4WU66Gac6lynQ8l
|
||||
r5tPsYIFXzh6FVaRKGQUtW1hz9ecO6Y27Rh2JsyiIxgUqk2ooxE69uN42t+dtqKC
|
||||
1s8G/7VtY8GDALFLYTnzLvsCAwEAAaM1MDMwDgYDVR0PAQH/BAQDAgCgMBMGA1Ud
|
||||
JQQMMAoGCCsGAQUFBwMDMAwGA1UdEwEB/wQCMAAwCwYJKoZIhvcNAQELA4ICAQBM
|
||||
Oll3G/XBz8idiNdNJDWUh+5w3ojmwanrTBdCdqEk1WenaR6DtcflJx6Z3f/mwV4o
|
||||
b1skOAX1yX5RCahJHUMxMicz/Q38pOVelGPrWnc3TJB+VKjGyHXlQDVkZFb+4+ef
|
||||
wtj7HngXhHFFDSgjm3EdMndvgDQ7SQb4skOnCNS9iyX7eXxhFBCZmZL+HALKBj2B
|
||||
yhV4IcBDqmp504t14rx9/Jvty0dG7fY7I51gEQpm4S02JML5xvTm1xfboWIhZODI
|
||||
swEAO+ekBoFHbS1Q9KMPjIAw3TrCHH8x8XZq5zsYtAC1yZHdCKa26aWdy56A9eHj
|
||||
O1VxzwmbNyXRenVuBYP+0wr3HVKFG4JJ4ZZpNZzQW/pqEPghCTJIvIueK652ByUc
|
||||
//sv+nXd5f19LeES9pf0l253NDaFZPb6aegKfquWh8qlQBmUQ2GzaTLbtmNd28M6
|
||||
W7iL7tkKZe1ZnBz9RKgtPrDjjWGZInjjcOU8EtT4SLq7kCVDmPs5MD8vaAm96JsE
|
||||
jmLC3Uu/4k7HiDYX0i0mOWkFjZQMdVatcIF5FPSppwsSbW8QidnXt54UtwtFDEPz
|
||||
lpjs7ybeQE71JXcMZnVIK4bjRXsEFPI98RpIlEdedbSUdYAncLNJRT7HZBMPGSwZ
|
||||
0PNJuglnlr3srVzdW1dz2xQjdvLwxy6mNUF6rbQBWA==
|
||||
-----END CERTIFICATE-----
|
||||
|
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