From e587b0427afa557e21545a74d4aa5378a6763f83 Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Fri, 9 Oct 2015 22:16:56 -0700 Subject: [PATCH] test for key rotation Signed-off-by: David Lawrence (github: endophage) --- client/changelist/change.go | 2 + client/client.go | 2 +- client/client_test.go | 92 +++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/client/changelist/change.go b/client/changelist/change.go index 0626b7ccf3..dfdaed5c36 100644 --- a/client/changelist/change.go +++ b/client/changelist/change.go @@ -35,6 +35,8 @@ type TufChange struct { Data []byte `json:"data"` } +// TufRootData represents a modification of the keys associated +// with a role that appears in the root.json type TufRootData struct { Keys []data.TUFKey `json:"keys"` RoleName string `json:"role"` diff --git a/client/client.go b/client/client.go index 7732b09820..ca57a89f1a 100644 --- a/client/client.go +++ b/client/client.go @@ -606,7 +606,7 @@ func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) { ), nil } -// ReplaceKey removes all existing keys associated with role and adds +// RotateKeys removes all existing keys associated with role and adds // the keys specified by keyIDs to the role. These changes are staged // in a changelist until publish is called. func (r *NotaryRepository) RotateKeys() error { diff --git a/client/client_test.go b/client/client_test.go index 082dd0bfd6..d18e8825b6 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -633,3 +633,95 @@ func testPublish(t *testing.T, rootType data.KeyAlgorithm) { assert.NoError(t, err) assert.Equal(t, currentTarget, newCurrentTarget, "current target does not match") } + +func TestRotate(t *testing.T) { + // Temporary directory where test files will be created + tempBaseDir, err := ioutil.TempDir("", "notary-test-") + defer os.RemoveAll(tempBaseDir) + + assert.NoError(t, err, "failed to create a temporary directory: %s", err) + + gun := "docker.com/notary" + + // Set up server + ctx := context.WithValue(context.Background(), "metaStore", storage.NewMemStorage()) + + // Do not pass one of the const KeyAlgorithms here as the value! Passing a + // string is in itself good test that we are handling it correctly as we will + // be receiving a string from the configuration. + ctx = context.WithValue(ctx, "keyAlgorithm", "ecdsa") + + hand := utils.RootHandlerFactory(nil, ctx, + cryptoservice.NewCryptoService("", trustmanager.NewKeyMemoryStore(passphraseRetriever))) + + r := mux.NewRouter() + r.Methods("POST").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/").Handler(hand(handlers.AtomicUpdateHandler, "push", "pull")) + r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler(hand(handlers.GetHandler, "pull")) + r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/timestamp.json").Handler(hand(handlers.GetTimestampHandler, "pull")) + r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/timestamp.key").Handler(hand(handlers.GetTimestampKeyHandler, "push", "pull")) + //r.Methods("POST").Path("/v2/{imageName:" + server.RepositoryNameRegexp + "}/_trust/tuf/{tufRole:(root|targets|timestamp|snapshot)}.json").Handler(hand(handlers.UpdateHandler, "push", "pull")) + r.Methods("DELETE").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/").Handler(hand(handlers.DeleteHandler, "push", "pull")) + + ts := httptest.NewServer(r) + + repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever) + assert.NoError(t, err, "error creating repository: %s", err) + + rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey.String()) + assert.NoError(t, err, "error generating root key: %s", err) + + rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID) + assert.NoError(t, err, "error retreiving root key: %s", err) + + err = repo.Initialize(rootCryptoService) + assert.NoError(t, err, "error creating repository: %s", err) + + // Add fixtures/intermediate-ca.crt as a target. There's no particular reason + // for using this file except that it happens to be available as + // a fixture. + // Adding a target will allow us to confirm the repository is still valid after + // rotating the keys. + latestTarget, err := NewTarget("latest", "../fixtures/intermediate-ca.crt") + assert.NoError(t, err, "error creating target") + err = repo.AddTarget(latestTarget) + assert.NoError(t, err, "error adding target") + + // Publish + err = repo.Publish() + assert.NoError(t, err) + + // Get root.json and capture targets + snapshot key IDs + repo.GetTargetByName("latest") // force a pull + targetsKeyIDs := repo.tufRepo.Root.Signed.Roles["targets"].KeyIDs + snapshotKeyIDs := repo.tufRepo.Root.Signed.Roles["snapshot"].KeyIDs + assert.Len(t, targetsKeyIDs, 1) + assert.Len(t, snapshotKeyIDs, 1) + + // Do rotation + repo.RotateKeys() + + // Publish + err = repo.Publish() + assert.NoError(t, err) + + // Get root.json. Check targets + snapshot keys have changed + // and that they match those found in the changelist. + _, err = repo.GetTargetByName("latest") // force a pull + assert.NoError(t, err) + newTargetsKeyIDs := repo.tufRepo.Root.Signed.Roles["targets"].KeyIDs + newSnapshotKeyIDs := repo.tufRepo.Root.Signed.Roles["snapshot"].KeyIDs + assert.Len(t, newTargetsKeyIDs, 1) + assert.Len(t, newSnapshotKeyIDs, 1) + assert.NotEqual(t, targetsKeyIDs[0], newTargetsKeyIDs[0]) + assert.NotEqual(t, snapshotKeyIDs[0], newSnapshotKeyIDs[0]) + + // Confirm changelist dir empty after publishing changes + // Look for the changelist file + changelistDirPath := filepath.Join(tempBaseDir, "tuf", filepath.FromSlash(gun), "changelist") + changelistDir, err := os.Open(changelistDirPath) + assert.NoError(t, err, "could not open changelist directory") + fileInfos, err := changelistDir.Readdir(0) + assert.NoError(t, err, "could not read changelist directory") + // Should only be one file in the directory + assert.Len(t, fileInfos, 0, "wrong number of changelist files found") +}