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