add test for using a pinned cert ID when the root Public field has a bundle with intermediates

Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)
This commit is contained in:
David Lawrence 2016-04-18 18:20:56 -07:00 committed by Riyaz Faizullabhoy
parent 94a2e3a741
commit 4776700215
1 changed files with 183 additions and 1 deletions

View File

@ -2,21 +2,28 @@ package trustpinning
import ( import (
"bytes" "bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix"
"encoding/json" "encoding/json"
"encoding/pem"
"io/ioutil" "io/ioutil"
"math/big"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
"text/template" "text/template"
"time"
"github.com/docker/notary" "github.com/docker/notary"
"github.com/docker/notary/cryptoservice" "github.com/docker/notary/cryptoservice"
"github.com/docker/notary/trustmanager" "github.com/docker/notary/trustmanager"
"github.com/docker/notary/tuf/data" "github.com/docker/notary/tuf/data"
"github.com/docker/notary/tuf/signed" "github.com/docker/notary/tuf/signed"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"time"
) )
type SignedRSARootTemplate struct { type SignedRSARootTemplate struct {
@ -278,6 +285,181 @@ func TestValidateRootWithPinnedCert(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
} }
func TestValidateRootWithPinnerCertAndIntermediates(t *testing.T) {
now := time.Now()
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
pass := func(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error) {
return "password", false, nil
}
memStore := trustmanager.NewKeyMemoryStore(pass)
cs := cryptoservice.NewCryptoService(memStore)
// generate CA cert
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
require.NoError(t, err)
caTmpl := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "notary testing CA",
},
NotBefore: now.Add(-time.Hour),
NotAfter: now.Add(time.Hour),
KeyUsage: x509.KeyUsageCertSign,
BasicConstraintsValid: true,
IsCA: true,
MaxPathLen: 3,
}
caPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
_, err = x509.CreateCertificate(
rand.Reader,
&caTmpl,
&caTmpl,
caPrivKey.Public(),
caPrivKey,
)
// generate intermediate
intTmpl := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "notary testing intermediate",
},
NotBefore: now.Add(-time.Hour),
NotAfter: now.Add(time.Hour),
KeyUsage: x509.KeyUsageCertSign,
BasicConstraintsValid: true,
IsCA: true,
MaxPathLen: 2,
}
intPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
intCert, err := x509.CreateCertificate(
rand.Reader,
&intTmpl,
&caTmpl,
intPrivKey.Public(),
caPrivKey,
)
require.NoError(t, err)
// generate leaf
serialNumber, err = rand.Int(rand.Reader, serialNumberLimit)
require.NoError(t, err)
leafTmpl := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "docker.io/notary/test",
},
NotBefore: now.Add(-time.Hour),
NotAfter: now.Add(time.Hour),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning},
BasicConstraintsValid: true,
}
leafPubKey, err := cs.Create("root", "docker.io/notary/test", data.ECDSAKey)
require.NoError(t, err)
leafPrivKey, _, err := cs.GetPrivateKey(leafPubKey.ID())
require.NoError(t, err)
signer := leafPrivKey.CryptoSigner()
leafCert, err := x509.CreateCertificate(
rand.Reader,
&leafTmpl,
&intTmpl,
signer.Public(),
intPrivKey,
)
rootBundleWriter := bytes.NewBuffer(nil)
pem.Encode(
rootBundleWriter,
&pem.Block{
Type: "CERTIFICATE",
Bytes: leafCert,
},
)
pem.Encode(
rootBundleWriter,
&pem.Block{
Type: "CERTIFICATE",
Bytes: intCert,
},
)
rootBundle := rootBundleWriter.Bytes()
ecdsax509Key := data.NewECDSAx509PublicKey(rootBundle)
otherKey, err := cs.Create("targets", "docker.io/notary/test", data.ED25519Key)
require.NoError(t, err)
root := data.SignedRoot{
Signatures: make([]data.Signature, 0),
Signed: data.Root{
SignedCommon: data.SignedCommon{
Type: "Root",
Expires: now.Add(time.Hour),
Version: 1,
},
Keys: map[string]data.PublicKey{
ecdsax509Key.ID(): ecdsax509Key,
otherKey.ID(): otherKey,
},
Roles: map[string]*data.RootRole{
"root": &data.RootRole{
KeyIDs: []string{ecdsax509Key.ID()},
Threshold: 1,
},
"targets": &data.RootRole{
KeyIDs: []string{otherKey.ID()},
Threshold: 1,
},
"snapshot": &data.RootRole{
KeyIDs: []string{otherKey.ID()},
Threshold: 1,
},
"timestamp": &data.RootRole{
KeyIDs: []string{otherKey.ID()},
Threshold: 1,
},
},
},
Dirty: true,
}
signedRoot, err := root.ToSigned()
require.NoError(t, err)
err = signed.Sign(cs, signedRoot, []data.PublicKey{ecdsax509Key}, 1, nil)
require.NoError(t, err)
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
defer os.RemoveAll(tempBaseDir)
require.NoError(t, err, "failed to create a temporary directory: %s", err)
// Create a X509Store
trustPath := filepath.Join(tempBaseDir, notary.TrustedCertsDir)
certStore, err := trustmanager.NewX509FilteredFileStore(
trustPath,
trustmanager.FilterCertsExpiredSha1,
)
require.NoError(t, err)
err = ValidateRoot(
certStore,
signedRoot,
"docker.io/notary/test",
TrustPinConfig{
Certs: map[string][]string{
"docker.io/notary/test": {ecdsax509Key.ID()},
},
DisableTOFU: true,
},
)
require.NoError(t, err, "failed to validate certID with intermediate")
}
func TestValidateRootFailuresWithPinnedCert(t *testing.T) { func TestValidateRootFailuresWithPinnedCert(t *testing.T) {
var testSignedRoot data.Signed var testSignedRoot data.Signed
var signedRootBytes bytes.Buffer var signedRootBytes bytes.Buffer