Handle cert bundles as key IDs

Signed-off-by: Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
This commit is contained in:
Riyaz Faizullabhoy 2016-04-19 00:41:16 -07:00
parent 4776700215
commit 26a95ef5a3
7 changed files with 54 additions and 20 deletions

View File

@ -556,12 +556,34 @@ func CertToKey(cert *x509.Certificate) data.PublicKey {
}
}
// CertsToKeys transforms each of the input certificates into it's corresponding
// CertsToKeys transforms each of the input certificate chains into its corresponding
// PublicKey
func CertsToKeys(certs []*x509.Certificate) map[string]data.PublicKey {
func CertsToKeys(leafCerts []*x509.Certificate, intCerts map[string][]*x509.Certificate) map[string]data.PublicKey {
keys := make(map[string]data.PublicKey)
for _, cert := range certs {
newKey := CertToKey(cert)
for _, leafCert := range leafCerts {
certBundle := []*x509.Certificate{leafCert}
certID, err := FingerprintCert(leafCert)
if err != nil {
continue
}
if intCertsForLeafs, ok := intCerts[certID]; ok {
certBundle = append(certBundle, intCertsForLeafs...)
}
certChainPEM, err := CertChainToPEM(certBundle)
if err != nil {
continue
}
var newKey data.PublicKey
// Use the leaf cert's public key algorithm for typing
switch leafCert.PublicKeyAlgorithm {
case x509.RSA:
newKey = data.NewRSAx509PublicKey(certChainPEM)
case x509.ECDSA:
newKey = data.NewECDSAx509PublicKey(certChainPEM)
default:
logrus.Debugf("Unknown key type parsed from certificate: %v", leafCert.PublicKeyAlgorithm)
continue
}
keys[newKey.ID()] = newKey
}
return keys
@ -593,6 +615,7 @@ func NewCertificate(gun string, startTime, endTime time.Time) (*x509.Certificate
// X509PublicKeyID returns a public key ID as a string, given a
// data.PublicKey that contains an X509 Certificate
func X509PublicKeyID(certPubKey data.PublicKey) (string, error) {
// Note that this only loads the first certificate from the public key
cert, err := LoadCertFromPEM(certPubKey.Public())
if err != nil {
return "", err

View File

@ -30,8 +30,8 @@ func TestCertsToKeys(t *testing.T) {
// Get our certList with Leaf Cert and Intermediate
certList := []*x509.Certificate{leafCert, intermediateCA, rootCA}
// Call CertsToKEys
keys := CertsToKeys(certList)
// Call CertsToKeys
keys := CertsToKeys(certList, make(map[string][]*x509.Certificate))
require.NotNil(t, keys)
require.Len(t, keys, 3)
@ -44,6 +44,13 @@ func TestCertsToKeys(t *testing.T) {
newKeys = GetIntermediateCerts(certList)
require.NotNil(t, newKeys)
require.Len(t, newKeys, 2)
// Try calling CertToKeys on a junk leaf cert that won't fingerprint
emptyCert := x509.Certificate{}
// Also try changing the pre-existing leaf cert into an invalid algorithm
leafCert.PublicKeyAlgorithm = x509.DSA
keys = CertsToKeys([]*x509.Certificate{&emptyCert, leafCert}, make(map[string][]*x509.Certificate))
require.Empty(t, keys)
}
func TestNewCertificate(t *testing.T) {

View File

@ -117,7 +117,7 @@ func ValidateRoot(certStore trustmanager.X509Store, root *data.Signed, gun strin
if len(trustedCerts) != 0 {
logrus.Debugf("found %d valid root certificates for %s: %s", len(trustedCerts), gun,
prettyFormatCertIDs(trustedCerts))
err = signed.VerifyRoot(root, 0, trustmanager.CertsToKeys(trustedCerts))
err = signed.VerifyRoot(root, 0, trustmanager.CertsToKeys(trustedCerts, allIntCerts))
if err != nil {
logrus.Debugf("failed to verify TUF data for: %s, %v", gun, err)
return &ErrValidationFail{Reason: "failed to validate data with current trusted certificates"}
@ -149,7 +149,7 @@ func ValidateRoot(certStore trustmanager.X509Store, root *data.Signed, gun strin
// 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.VerifyRoot(root, 0, trustmanager.CertsToKeys(certsFromRoot))
err = signed.VerifyRoot(root, 0, trustmanager.CertsToKeys(certsFromRoot, allIntCerts))
if err != nil {
logrus.Debugf("failed to verify TUF data for: %s, %v", gun, err)
return &ErrValidationFail{Reason: "failed to validate integrity of roots"}

View File

@ -409,19 +409,19 @@ func TestValidateRootWithPinnerCertAndIntermediates(t *testing.T) {
otherKey.ID(): otherKey,
},
Roles: map[string]*data.RootRole{
"root": &data.RootRole{
"root": {
KeyIDs: []string{ecdsax509Key.ID()},
Threshold: 1,
},
"targets": &data.RootRole{
"targets": {
KeyIDs: []string{otherKey.ID()},
Threshold: 1,
},
"snapshot": &data.RootRole{
"snapshot": {
KeyIDs: []string{otherKey.ID()},
Threshold: 1,
},
"timestamp": &data.RootRole{
"timestamp": {
KeyIDs: []string{otherKey.ID()},
Threshold: 1,
},
@ -557,8 +557,6 @@ func TestValidateRootWithPinnedCA(t *testing.T) {
// Now construct a new root with a valid cert chain, such that signatures are correct over the 'notary-signer' GUN. Pin the root-ca and validate
leafCert, err := trustmanager.LoadCertFromFile("../fixtures/notary-signer.crt")
require.NoError(t, err)
pemLeafBytes := trustmanager.CertToPEM(leafCert)
newRootLeafKey := data.NewPublicKey(data.RSAx509Key, pemLeafBytes)
intermediateCert, err := trustmanager.LoadCertFromFile("../fixtures/intermediate-ca.crt")
require.NoError(t, err)
@ -599,7 +597,7 @@ func TestValidateRootWithPinnedCA(t *testing.T) {
newTestSignedRoot, err := testRoot.ToSigned()
require.NoError(t, err)
err = signed.Sign(cs, newTestSignedRoot, []data.PublicKey{newRootLeafKey}, 1, nil)
err = signed.Sign(cs, newTestSignedRoot, []data.PublicKey{newRootKey}, 1, nil)
require.NoError(t, err)
// Check that we validate correctly against a pinned CA and provided bundle

View File

@ -65,11 +65,19 @@ func NewTrustPinChecker(trustPinConfig TrustPinConfig, gun string) (CertChecker,
}
func (t trustPinChecker) certsCheck(leafCert *x509.Certificate, intCerts []*x509.Certificate) bool {
certID, err := trustmanager.FingerprintCert(leafCert)
// reconstruct the leaf + intermediate cert chain, which is bundled as {leaf, intermediates...},
// in order to get the matching id in the root file
leafCertID, err := trustmanager.FingerprintCert(leafCert)
if err != nil {
return false
}
return utils.StrSliceContains(t.pinnedCertIDs, certID)
rootKeys := trustmanager.CertsToKeys([]*x509.Certificate{leafCert}, map[string][]*x509.Certificate{leafCertID: intCerts})
for keyID := range rootKeys {
if utils.StrSliceContains(t.pinnedCertIDs, keyID) {
return true
}
}
return false
}
func (t trustPinChecker) caCheck(leafCert *x509.Certificate, intCerts []*x509.Certificate) bool {

View File

@ -36,7 +36,6 @@ func VerifyRoot(s *data.Signed, minVersion int, keys map[string]data.PublicKey)
if err != nil {
return err
}
for _, sig := range s.Signatures {
// method lookup is consistent due to Unmarshal JSON doing lower case for us.
method := sig.Method
@ -45,7 +44,6 @@ func VerifyRoot(s *data.Signed, minVersion int, keys map[string]data.PublicKey)
logrus.Debugf("continuing b/c signing method is not supported for verify root: %s\n", sig.Method)
continue
}
key, ok := keys[sig.KeyID]
if !ok {
logrus.Debugf("continuing b/c signing key isn't present in keys: %s\n", sig.KeyID)

View File

@ -98,7 +98,7 @@ func HashedPaths(path string, hashes data.Hashes) []string {
// CanonicalKeyID returns the ID of the public bytes version of a TUF key.
// On regular RSA/ECDSA TUF keys, this is just the key ID. On X509 RSA/ECDSA
// TUF keys, this is the key ID of the public key part of the key.
// TUF keys, this is the key ID of the public key part of the key in the leaf cert
func CanonicalKeyID(k data.PublicKey) (string, error) {
switch k.Algorithm() {
case data.ECDSAx509Key, data.RSAx509Key: