Example #1
0
func TestValidateRootWithPinnedCert(t *testing.T) {
	var testSignedRoot data.Signed
	var signedRootBytes bytes.Buffer

	// Temporary directory where test files will be created
	tempBaseDir, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir)
	require.NoError(t, err, "failed to create a temporary directory: %s", err)

	// Execute our template
	templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
	templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot})

	// Unmarshal our signedroot
	json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)
	typedSignedRoot, err := data.RootFromSigned(&testSignedRoot)
	require.NoError(t, err)

	// This call to trustpinning.ValidateRoot should succeed with the correct Cert ID (same as root public key ID)
	validatedSignedRoot, err := trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {rootPubKeyID}}, DisableTOFU: true})
	require.NoError(t, err)
	generateRootKeyIDs(typedSignedRoot)
	require.Equal(t, validatedSignedRoot, typedSignedRoot)

	// This call to trustpinning.ValidateRoot should also succeed with the correct Cert ID (same as root public key ID), even though we passed an extra bad one
	validatedSignedRoot, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {rootPubKeyID, "invalidID"}}, DisableTOFU: true})
	require.NoError(t, err)
	require.Equal(t, validatedSignedRoot, typedSignedRoot)
}
Example #2
0
// loadRoot loads a root if one has not been loaded
func (rb *repoBuilder) loadRoot(content []byte, minVersion int, allowExpired bool) error {
	roleName := data.CanonicalRootRole

	signedObj, err := rb.bytesToSigned(content, data.CanonicalRootRole)
	if err != nil {
		return err
	}
	// ValidateRoot validates against the previous root's role, as well as validates that the root
	// itself is self-consistent with its own signatures and thresholds.
	// This assumes that ValidateRoot calls data.RootFromSigned, which validates
	// the metadata, rather than just unmarshalling signedObject into a SignedRoot object itself.
	signedRoot, err := trustpinning.ValidateRoot(rb.prevRoot, signedObj, rb.gun, rb.trustpin)
	if err != nil {
		return err
	}

	if err := signed.VerifyVersion(&(signedRoot.Signed.SignedCommon), minVersion); err != nil {
		return err
	}

	if !allowExpired { // check must go at the end because all other validation should pass
		if err := signed.VerifyExpiry(&(signedRoot.Signed.SignedCommon), roleName); err != nil {
			return err
		}
	}

	rootRole, err := signedRoot.BuildBaseRole(data.CanonicalRootRole)
	if err != nil { // this should never happen since the root has been validated
		return err
	}
	rb.repo.Root = signedRoot
	rb.repo.originalRootRole = rootRole
	return nil
}
Example #3
0
func TestValidateRootFailuresWithPinnedCert(t *testing.T) {
	var testSignedRoot data.Signed
	var signedRootBytes bytes.Buffer

	// Temporary directory where test files will be created
	tempBaseDir, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir)
	require.NoError(t, err, "failed to create a temporary directory: %s", err)

	// Execute our template
	templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
	templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot})

	// Unmarshal our signedroot
	json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)
	typedSignedRoot, err := data.RootFromSigned(&testSignedRoot)
	require.NoError(t, err)

	// This call to trustpinning.ValidateRoot should fail due to an incorrect cert ID
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {"ABSOLUTELY NOT A CERT ID"}}, DisableTOFU: true})
	require.Error(t, err)

	// This call to trustpinning.ValidateRoot should fail due to an empty cert ID
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {""}}, DisableTOFU: true})
	require.Error(t, err)

	// This call to trustpinning.ValidateRoot should fail due to an invalid GUN (even though the cert ID is correct), and TOFUS is set to false
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{Certs: map[string][]string{"not_a_gun": {rootPubKeyID}}, DisableTOFU: true})
	require.Error(t, err)

	// This call to trustpinning.ValidateRoot should fail due to an invalid cert ID, even though it's a valid key ID for targets
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {targetsPubKeyID}}, DisableTOFU: true})
	require.Error(t, err)

	// This call to trustpinning.ValidateRoot should succeed because we fall through to TOFUS because we have no matching GUNs under Certs
	validatedRoot, err := trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{Certs: map[string][]string{"not_a_gun": {rootPubKeyID}}, DisableTOFU: false})
	require.NoError(t, err)
	generateRootKeyIDs(typedSignedRoot)
	require.Equal(t, typedSignedRoot, validatedRoot)
}
Example #4
0
// validateRoot MUST only be used during bootstrapping. It will only validate
// signatures of the root based on known keys, not expiry or other metadata.
// This is so that an out of date root can be loaded to be used in a rotation
// should the TUF update process detect a problem.
func (r *NotaryRepository) validateRoot(rootJSON []byte) (*data.SignedRoot, error) {
	// can't just unmarshal into SignedRoot because validate root
	// needs the root.Signed field to still be []byte for signature
	// validation
	root := &data.Signed{}
	err := json.Unmarshal(rootJSON, root)
	if err != nil {
		return nil, err
	}

	err = trustpinning.ValidateRoot(r.CertStore, root, r.gun, r.trustPinning)
	if err != nil {
		return nil, err
	}

	return data.RootFromSigned(root)
}
Example #5
0
func TestValidateRootWithoutTOFUS(t *testing.T) {
	var testSignedRoot data.Signed
	var signedRootBytes bytes.Buffer

	// Temporary directory where test files will be created
	tempBaseDir, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir)
	require.NoError(t, err, "failed to create a temporary directory: %s", err)

	// Execute our template
	templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
	templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot})

	// Unmarshal our signedroot
	json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)

	// This call to trustpinning.ValidateRoot will fail since we are explicitly disabling TOFU and have no local certs
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{DisableTOFU: true})
	require.Error(t, err)
}
Example #6
0
func testValidateRootRotationMissingNewSig(t *testing.T, keyAlg, rootKeyType string) {
	gun := "docker.com/notary"

	memKeyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
	cs := cryptoservice.NewCryptoService(memKeyStore)

	// TUF key with PEM-encoded x509 certificate
	origRootKey, err := testutils.CreateKey(cs, gun, data.CanonicalRootRole, keyAlg)
	require.NoError(t, err)

	origRootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{origRootKey.ID()}, nil)
	require.NoError(t, err)

	origTestRoot, err := data.NewRoot(
		map[string]data.PublicKey{origRootKey.ID(): origRootKey},
		map[string]*data.RootRole{
			data.CanonicalRootRole:      &origRootRole.RootRole,
			data.CanonicalTargetsRole:   &origRootRole.RootRole,
			data.CanonicalSnapshotRole:  &origRootRole.RootRole,
			data.CanonicalTimestampRole: &origRootRole.RootRole,
		},
		false,
	)
	origTestRoot.Signed.Version = 1
	require.NoError(t, err, "Failed to create new root")

	signedOrigTestRoot, err := origTestRoot.ToSigned()
	require.NoError(t, err)

	err = signed.Sign(cs, signedOrigTestRoot, []data.PublicKey{origRootKey}, 1, nil)
	require.NoError(t, err)
	prevRoot, err := data.RootFromSigned(signedOrigTestRoot)
	require.NoError(t, err)

	// TUF key with PEM-encoded x509 certificate
	replRootKey, err := testutils.CreateKey(cs, gun, data.CanonicalRootRole, keyAlg)
	require.NoError(t, err)

	rootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{replRootKey.ID()}, nil)
	require.NoError(t, err)

	testRoot, err := data.NewRoot(
		map[string]data.PublicKey{replRootKey.ID(): replRootKey},
		map[string]*data.RootRole{
			data.CanonicalRootRole:      &rootRole.RootRole,
			data.CanonicalTargetsRole:   &rootRole.RootRole,
			data.CanonicalSnapshotRole:  &rootRole.RootRole,
			data.CanonicalTimestampRole: &rootRole.RootRole,
		},
		false,
	)
	require.NoError(t, err, "Failed to create new root")

	signedTestRoot, err := testRoot.ToSigned()
	require.NoError(t, err)

	// We only sign with the old key, and not with the new one
	err = signed.Sign(cs, signedTestRoot, []data.PublicKey{origRootKey}, 1, nil)
	require.NoError(t, err)

	// This call to trustpinning.ValidateRoot will succeed since we are using a valid PEM
	// encoded certificate, and have no other certificates for this CN
	_, err = trustpinning.ValidateRoot(prevRoot, signedTestRoot, gun, trustpinning.TrustPinConfig{})
	require.Error(t, err, "insuficient signatures on root")
}
Example #7
0
func TestValidateRoot(t *testing.T) {
	var testSignedRoot data.Signed
	var signedRootBytes bytes.Buffer

	// Temporary directory where test files will be created
	tempBaseDir, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir)
	require.NoError(t, err, "failed to create a temporary directory: %s", err)

	// Execute our template
	templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
	templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot})

	// Unmarshal our signedroot
	json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)

	// This call to trustpinning.ValidateRoot will succeed since we are using a valid PEM
	// encoded certificate, and have no other certificates for this CN
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{})
	require.NoError(t, err)

	// This call to trustpinning.ValidateRoot will fail since we are passing in a dnsName that
	// doesn't match the CN of the certificate.
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "diogomonica.com/notary", trustpinning.TrustPinConfig{})
	require.Error(t, err, "An error was expected")
	require.Equal(t, err, &trustpinning.ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"})

	//
	// This call to trustpinning.ValidateRoot will fail since we are passing an unparsable RootSigned
	//
	// Execute our template deleting the old buffer first
	signedRootBytes.Reset()
	templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
	templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: "------ ABSOLUTELY NOT A PEM -------"})
	// Unmarshal our signedroot
	json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)

	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{})
	require.Error(t, err, "illegal base64 data at input byte")

	//
	// This call to trustpinning.ValidateRoot will fail since we are passing an invalid PEM cert
	//
	// Execute our template deleting the old buffer first
	signedRootBytes.Reset()
	templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
	templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: "LS0tLS1CRUdJTiBDRVJU"})
	// Unmarshal our signedroot
	json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)

	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{})
	require.Error(t, err, "An error was expected")
	require.Equal(t, err, &trustpinning.ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"})

	//
	// This call to trustpinning.ValidateRoot will fail since we are passing only CA certificate
	// This will fail due to the lack of a leaf certificate
	//
	// Execute our template deleting the old buffer first
	signedRootBytes.Reset()
	templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
	templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validCAPEMEncodeRSARoot})
	// Unmarshal our signedroot
	json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)

	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{})
	require.Error(t, err, "An error was expected")
	require.Equal(t, err, &trustpinning.ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"})

	//
	// This call to trustpinning.ValidateRoot could succeed in getting to the TUF validation, since
	// we are using a valid PEM encoded certificate chain of intermediate + leaf cert
	// that are signed by a trusted root authority and the leaf cert has a correct CN.
	// It will, however, fail to validate, because the leaf cert does not precede the
	// intermediate in the certificate bundle
	//
	// Execute our template deleting the old buffer first
	signedRootBytes.Reset()
	templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
	templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validIntermediateAndCertRSA})

	// Unmarshal our signedroot
	json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)

	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "secure.example.com", trustpinning.TrustPinConfig{})
	require.Error(t, err, "An error was expected")
	require.Equal(t, err, &trustpinning.ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"})
}
Example #8
0
func TestValidateRootWithPinnedCA(t *testing.T) {
	var testSignedRoot data.Signed
	var signedRootBytes bytes.Buffer

	// Temporary directory where test files will be created
	tempBaseDir, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir)
	require.NoError(t, err, "failed to create a temporary directory: %s", err)

	templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate)
	templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot})
	// Unmarshal our signedRoot
	json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot)
	typedSignedRoot, err := data.RootFromSigned(&testSignedRoot)
	require.NoError(t, err)

	// This call to trustpinning.ValidateRoot will fail because we have an invalid path for the CA
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{CA: map[string]string{"docker.com/notary": filepath.Join(tempBaseDir, "nonexistent")}})
	require.Error(t, err)

	// This call to trustpinning.ValidateRoot will fail because we have no valid GUNs to use, and TOFUS is disabled
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{CA: map[string]string{"othergun": filepath.Join(tempBaseDir, "nonexistent")}, DisableTOFU: true})
	require.Error(t, err)

	// This call to trustpinning.ValidateRoot will succeed because we have no valid GUNs to use and we fall back to enabled TOFUS
	validatedRoot, err := trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{CA: map[string]string{"othergun": filepath.Join(tempBaseDir, "nonexistent")}, DisableTOFU: false})
	require.NoError(t, err)
	generateRootKeyIDs(typedSignedRoot)
	require.Equal(t, typedSignedRoot, validatedRoot)

	// Write an invalid CA cert (not even a PEM) to the tempDir and ensure validation fails when using it
	invalidCAFilepath := filepath.Join(tempBaseDir, "invalid.ca")
	require.NoError(t, ioutil.WriteFile(invalidCAFilepath, []byte("ABSOLUTELY NOT A PEM"), 0644))

	// Using this invalid CA cert should fail on trustpinning.ValidateRoot
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{CA: map[string]string{"docker.com/notary": invalidCAFilepath}, DisableTOFU: true})
	require.Error(t, err)

	validCAFilepath := "../fixtures/root-ca.crt"

	// If we pass an invalid Certs entry in addition to this valid CA entry, since Certs has priority for pinning we will fail
	_, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {"invalidID"}}, CA: map[string]string{"docker.com/notary": validCAFilepath}, DisableTOFU: true})
	require.Error(t, err)

	// 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)

	intermediateCert, err := trustmanager.LoadCertFromFile("../fixtures/intermediate-ca.crt")
	require.NoError(t, err)

	pemChainBytes, err := trustmanager.CertChainToPEM([]*x509.Certificate{leafCert, intermediateCert})
	require.NoError(t, err)

	newRootKey := data.NewPublicKey(data.RSAx509Key, pemChainBytes)

	rootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{newRootKey.ID()}, nil)
	require.NoError(t, err)

	testRoot, err := data.NewRoot(
		map[string]data.PublicKey{newRootKey.ID(): newRootKey},
		map[string]*data.RootRole{
			data.CanonicalRootRole:      &rootRole.RootRole,
			data.CanonicalTimestampRole: &rootRole.RootRole,
			data.CanonicalTargetsRole:   &rootRole.RootRole,
			data.CanonicalSnapshotRole:  &rootRole.RootRole},
		false,
	)
	testRoot.Signed.Version = 1
	require.NoError(t, err, "Failed to create new root")

	keyReader, err := os.Open("../fixtures/notary-signer.key")
	require.NoError(t, err, "could not open key file")
	pemBytes, err := ioutil.ReadAll(keyReader)
	require.NoError(t, err, "could not read key file")
	privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "")
	require.NoError(t, err)

	store, err := trustmanager.NewKeyFileStore(tempBaseDir, passphraseRetriever)
	require.NoError(t, err)
	cs := cryptoservice.NewCryptoService(store)

	err = store.AddKey(trustmanager.KeyInfo{Role: data.CanonicalRootRole, Gun: "notary-signer"}, privKey)
	require.NoError(t, err)

	newTestSignedRoot, err := testRoot.ToSigned()
	require.NoError(t, err)

	err = signed.Sign(cs, newTestSignedRoot, []data.PublicKey{newRootKey}, 1, nil)
	require.NoError(t, err)

	newTypedSignedRoot, err := data.RootFromSigned(newTestSignedRoot)
	require.NoError(t, err)

	// Check that we validate correctly against a pinned CA and provided bundle
	validatedRoot, err = trustpinning.ValidateRoot(nil, newTestSignedRoot, "notary-signer", trustpinning.TrustPinConfig{CA: map[string]string{"notary-signer": validCAFilepath}, DisableTOFU: true})
	require.NoError(t, err)
	generateRootKeyIDs(newTypedSignedRoot)
	require.Equal(t, newTypedSignedRoot, validatedRoot)

	// Add an expired CA for the same gun to our previous pinned bundle, ensure that we still validate correctly
	goodRootCABundle, err := trustmanager.LoadCertBundleFromFile(validCAFilepath)
	require.NoError(t, err)
	memKeyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
	cryptoService := cryptoservice.NewCryptoService(memKeyStore)
	testPubKey, err := cryptoService.Create("root", "notary-signer", data.ECDSAKey)
	require.NoError(t, err)
	testPrivKey, _, err := memKeyStore.GetKey(testPubKey.ID())
	require.NoError(t, err)
	expiredCert, err := generateExpiredTestingCertificate(testPrivKey, "notary-signer")
	require.NoError(t, err)
	bundleWithExpiredCert, err := trustmanager.CertChainToPEM(append(goodRootCABundle, expiredCert))
	require.NoError(t, err)
	bundleWithExpiredCertPath := filepath.Join(tempBaseDir, "bundle_with_expired_cert.pem")
	require.NoError(t, ioutil.WriteFile(bundleWithExpiredCertPath, bundleWithExpiredCert, 0644))

	// Check that we validate correctly against a pinned CA and provided bundle
	validatedRoot, err = trustpinning.ValidateRoot(nil, newTestSignedRoot, "notary-signer", trustpinning.TrustPinConfig{CA: map[string]string{"notary-signer": bundleWithExpiredCertPath}, DisableTOFU: true})
	require.NoError(t, err)
	require.Equal(t, newTypedSignedRoot, validatedRoot)

	testPubKey2, err := cryptoService.Create("root", "notary-signer", data.ECDSAKey)
	require.NoError(t, err)
	testPrivKey2, _, err := memKeyStore.GetKey(testPubKey2.ID())
	require.NoError(t, err)
	expiredCert2, err := generateExpiredTestingCertificate(testPrivKey2, "notary-signer")
	require.NoError(t, err)
	allExpiredCertBundle, err := trustmanager.CertChainToPEM([]*x509.Certificate{expiredCert, expiredCert2})
	require.NoError(t, err)
	allExpiredCertPath := filepath.Join(tempBaseDir, "all_expired_cert.pem")
	require.NoError(t, ioutil.WriteFile(allExpiredCertPath, allExpiredCertBundle, 0644))
	// Now only use expired certs in the bundle, we should fail
	_, err = trustpinning.ValidateRoot(nil, newTestSignedRoot, "notary-signer", trustpinning.TrustPinConfig{CA: map[string]string{"notary-signer": allExpiredCertPath}, DisableTOFU: true})
	require.Error(t, err)

	// Add a CA cert for a that won't validate against the root leaf certificate
	testPubKey3, err := cryptoService.Create("root", "notary-signer", data.ECDSAKey)
	require.NoError(t, err)
	testPrivKey3, _, err := memKeyStore.GetKey(testPubKey3.ID())
	require.NoError(t, err)
	validCert, err := cryptoservice.GenerateCertificate(testPrivKey3, "notary-signer", time.Now(), time.Now().AddDate(1, 0, 0))
	require.NoError(t, err)
	bundleWithWrongCert, err := trustmanager.CertChainToPEM([]*x509.Certificate{validCert})
	require.NoError(t, err)
	bundleWithWrongCertPath := filepath.Join(tempBaseDir, "bundle_with_expired_cert.pem")
	require.NoError(t, ioutil.WriteFile(bundleWithWrongCertPath, bundleWithWrongCert, 0644))
	_, err = trustpinning.ValidateRoot(nil, newTestSignedRoot, "notary-signer", trustpinning.TrustPinConfig{CA: map[string]string{"notary-signer": bundleWithWrongCertPath}, DisableTOFU: true})
	require.Error(t, err)
}
Example #9
0
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": {
					KeyIDs:    []string{ecdsax509Key.ID()},
					Threshold: 1,
				},
				"targets": {
					KeyIDs:    []string{otherKey.ID()},
					Threshold: 1,
				},
				"snapshot": {
					KeyIDs:    []string{otherKey.ID()},
					Threshold: 1,
				},
				"timestamp": {
					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)

	typedSignedRoot, err := data.RootFromSigned(signedRoot)
	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)

	validatedRoot, err := trustpinning.ValidateRoot(
		nil,
		signedRoot,
		"docker.io/notary/test",
		trustpinning.TrustPinConfig{
			Certs: map[string][]string{
				"docker.io/notary/test": {ecdsax509Key.ID()},
			},
			DisableTOFU: true,
		},
	)
	require.NoError(t, err, "failed to validate certID with intermediate")
	generateRootKeyIDs(typedSignedRoot)
	require.Equal(t, typedSignedRoot, validatedRoot)
}