mirror of https://github.com/docker/docs.git
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:
parent
94a2e3a741
commit
4776700215
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue