Skip to content

Commit

Permalink
Support passing in root keys on repo initialization
Browse files Browse the repository at this point in the history
Signed-off-by: Evan Cordell <[email protected]>
  • Loading branch information
ecordell committed Jul 5, 2016
1 parent 0517d57 commit 18d2ad4
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 29 deletions.
26 changes: 17 additions & 9 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,14 @@ func rootCertKey(gun string, privKey data.PrivateKey) (data.PublicKey, error) {
// timestamp key and possibly other serverManagedRoles), but the created repository
// result is only stored on local disk, not published to the server. To do that,
// use r.Publish() eventually.
func (r *NotaryRepository) Initialize(rootKeyID string, serverManagedRoles ...string) error {
privKey, _, err := r.CryptoService.GetPrivateKey(rootKeyID)
if err != nil {
return err
func (r *NotaryRepository) Initialize(rootKeyIDs []string, serverManagedRoles ...string) error {
privKeys := []data.PrivateKey{}
for _, keyID := range rootKeyIDs {
privKey, _, err := r.CryptoService.GetPrivateKey(keyID)
if err != nil {
return err
}
privKeys = append(privKeys, privKey)
}

// currently we only support server managing timestamps and snapshots, and
Expand Down Expand Up @@ -206,16 +210,20 @@ func (r *NotaryRepository) Initialize(rootKeyID string, serverManagedRoles ...st
}
}

rootKey, err := rootCertKey(r.gun, privKey)
if err != nil {
return err
rootKeys := []data.PublicKey{}
for _, privKey := range privKeys {
rootKey, err := rootCertKey(r.gun, privKey)
if err != nil {
return err
}
rootKeys = append(rootKeys, rootKey)
}

var (
rootRole = data.NewBaseRole(
data.CanonicalRootRole,
notary.MinThreshold,
rootKey,
rootKeys...,
)
timestampRole data.BaseRole
snapshotRole data.BaseRole
Expand Down Expand Up @@ -271,7 +279,7 @@ func (r *NotaryRepository) Initialize(rootKeyID string, serverManagedRoles ...st

r.tufRepo = tuf.NewRepo(r.CryptoService)

err = r.tufRepo.InitRoot(
err := r.tufRepo.InitRoot(
rootRole,
timestampRole,
snapshotRole,
Expand Down
46 changes: 33 additions & 13 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func initializeRepo(t *testing.T, rootType, gun, url string,

repo, rec, rootPubKeyID := createRepoAndKey(t, rootType, tempBaseDir, gun, url)

err = repo.Initialize(rootPubKeyID, serverManagedRoles...)
err = repo.Initialize([]string{rootPubKeyID}, serverManagedRoles...)
if err != nil {
os.RemoveAll(tempBaseDir)
}
Expand Down Expand Up @@ -241,7 +241,7 @@ func TestInitRepositoryManagedRolesIncludingRoot(t *testing.T) {

repo, rec, rootPubKeyID := createRepoAndKey(
t, data.ECDSAKey, tempBaseDir, "docker.com/notary", "http://localhost")
err = repo.Initialize(rootPubKeyID, data.CanonicalRootRole)
err = repo.Initialize([]string{rootPubKeyID}, data.CanonicalRootRole)
require.Error(t, err)
require.IsType(t, ErrInvalidRemoteRole{}, err)
// Just testing the error message here in this one case
Expand All @@ -261,7 +261,7 @@ func TestInitRepositoryManagedRolesInvalidRole(t *testing.T) {

repo, rec, rootPubKeyID := createRepoAndKey(
t, data.ECDSAKey, tempBaseDir, "docker.com/notary", "http://localhost")
err = repo.Initialize(rootPubKeyID, "randomrole")
err = repo.Initialize([]string{rootPubKeyID}, "randomrole")
require.Error(t, err)
require.IsType(t, ErrInvalidRemoteRole{}, err)
// no key creation happened
Expand All @@ -278,7 +278,7 @@ func TestInitRepositoryManagedRolesIncludingTargets(t *testing.T) {

repo, rec, rootPubKeyID := createRepoAndKey(
t, data.ECDSAKey, tempBaseDir, "docker.com/notary", "http://localhost")
err = repo.Initialize(rootPubKeyID, data.CanonicalTargetsRole)
err = repo.Initialize([]string{rootPubKeyID}, data.CanonicalTargetsRole)
require.Error(t, err)
require.IsType(t, ErrInvalidRemoteRole{}, err)
// no key creation happened
Expand All @@ -298,7 +298,27 @@ func TestInitRepositoryManagedRolesIncludingTimestamp(t *testing.T) {

repo, rec, rootPubKeyID := createRepoAndKey(
t, data.ECDSAKey, tempBaseDir, "docker.com/notary", ts.URL)
err = repo.Initialize(rootPubKeyID, data.CanonicalTimestampRole)
err = repo.Initialize([]string{rootPubKeyID}, data.CanonicalTimestampRole)
require.NoError(t, err)
// generates the target role, the snapshot role
rec.requireCreated(t, []string{data.CanonicalTargetsRole, data.CanonicalSnapshotRole})
}

func TestInitRepositoryMultipleRootKeys(t *testing.T) {
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("/tmp", "notary-test-")
require.NoError(t, err, "failed to create a temporary directory")
defer os.RemoveAll(tempBaseDir)

ts, _, _ := simpleTestServer(t)
defer ts.Close()

repo, rec, rootPubKeyID := createRepoAndKey(
t, data.ECDSAKey, tempBaseDir, "docker.com/notary", ts.URL)
rootPubKey2, err := repo.CryptoService.Create("root", repo.gun, data.ECDSAKey)
require.NoError(t, err, "error generating second root key: %s", err)

err = repo.Initialize([]string{rootPubKeyID, rootPubKey2.ID()}, data.CanonicalTimestampRole)
require.NoError(t, err)
// generates the target role, the snapshot role
rec.requireCreated(t, []string{data.CanonicalTargetsRole, data.CanonicalSnapshotRole})
Expand All @@ -317,7 +337,7 @@ func TestInitRepositoryNeedsRemoteTimestampKey(t *testing.T) {

repo, rec, rootPubKeyID := createRepoAndKey(
t, data.ECDSAKey, tempBaseDir, "docker.com/notary", ts.URL)
err = repo.Initialize(rootPubKeyID, data.CanonicalTimestampRole)
err = repo.Initialize([]string{rootPubKeyID}, data.CanonicalTimestampRole)
require.Error(t, err)
require.IsType(t, store.ErrMetaNotFound{}, err)

Expand All @@ -339,7 +359,7 @@ func TestInitRepositoryNeedsRemoteSnapshotKey(t *testing.T) {

repo, rec, rootPubKeyID := createRepoAndKey(
t, data.ECDSAKey, tempBaseDir, "docker.com/notary", ts.URL)
err = repo.Initialize(rootPubKeyID, data.CanonicalSnapshotRole)
err = repo.Initialize([]string{rootPubKeyID}, data.CanonicalSnapshotRole)
require.Error(t, err)
require.IsType(t, store.ErrMetaNotFound{}, err)

Expand Down Expand Up @@ -516,9 +536,9 @@ func testInitRepoSigningKeys(t *testing.T, rootType string, serverManagesSnapsho
repo, rec := newRepoToTestRepo(t, repo, false)

if serverManagesSnapshot {
err = repo.Initialize(rootPubKeyID, data.CanonicalSnapshotRole)
err = repo.Initialize([]string{rootPubKeyID}, data.CanonicalSnapshotRole)
} else {
err = repo.Initialize(rootPubKeyID)
err = repo.Initialize([]string{rootPubKeyID})
}

require.NoError(t, err, "error initializing repository")
Expand Down Expand Up @@ -563,7 +583,7 @@ func testInitRepoAttemptsExceeded(t *testing.T, rootType string) {
// private key unlocking we need a new repo instance.
repo, err = NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, retriever, trustpinning.TrustPinConfig{})
require.NoError(t, err, "error creating repo: %s", err)
err = repo.Initialize(rootPubKey.ID())
err = repo.Initialize([]string{rootPubKey.ID()})
require.EqualError(t, err, trustmanager.ErrAttemptsExceeded{}.Error())
}

Expand Down Expand Up @@ -600,7 +620,7 @@ func testInitRepoPasswordInvalid(t *testing.T, rootType string) {
// private key unlocking we need a new repo instance.
repo, err = NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, giveUpPassphraseRetriever, trustpinning.TrustPinConfig{})
require.NoError(t, err, "error creating repo: %s", err)
err = repo.Initialize(rootPubKey.ID())
err = repo.Initialize([]string{rootPubKey.ID()})
require.EqualError(t, err, trustmanager.ErrPasswordInvalid{}.Error())
}

Expand Down Expand Up @@ -1650,7 +1670,7 @@ func TestPublishUninitializedRepo(t *testing.T) {
rootPubKey, err := repo.CryptoService.Create("root", repo.gun, data.ECDSAKey)
require.NoError(t, err, "error generating root key: %s", err)

require.NoError(t, repo.Initialize(rootPubKey.ID()))
require.NoError(t, repo.Initialize([]string{rootPubKey.ID()}))

// now metadata is created
requireRepoHasExpectedMetadata(t, repo, data.CanonicalRootRole, true)
Expand Down Expand Up @@ -2011,7 +2031,7 @@ func TestPublishSnapshotLocalKeysCreatedFirst(t *testing.T) {

repo.CryptoService = cannotCreateKeys{CryptoService: cs}

err = repo.Initialize(rootPubKey.ID(), data.CanonicalSnapshotRole)
err = repo.Initialize([]string{rootPubKey.ID()}, data.CanonicalSnapshotRole)
require.Error(t, err)
require.Contains(t, err.Error(), "Oh no I cannot create keys")
require.False(t, requestMade)
Expand Down
37 changes: 37 additions & 0 deletions cmd/notary/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,43 @@ func setupServer() *httptest.Server {
return httptest.NewServer(setupServerHandler(storage.NewMemStorage()))
}

// Initializes a repo with existing key
func TestInitWithMultipleRootKeys(t *testing.T) {
// -- setup --
setUp(t)

tempDir := tempDirWithConfig(t, "{}")
defer os.RemoveAll(tempDir)

server := setupServer()
defer server.Close()

tempFile, err := ioutil.TempFile("", "targetfile")
require.NoError(t, err)
tempFile.Close()
defer os.Remove(tempFile.Name())

// -- tests --

// generate root key produces a single root key and no other keys
_, err = runCommand(t, tempDir, "key", "generate", data.ECDSAKey)
require.NoError(t, err)
rootIDs, _ := assertNumKeys(t, tempDir, 1, 0, true)
var rootFiles []string
for _, rID := range rootIDs {
rootFiles = append(rootFiles, filepath.Join(
tempDir, "private", "root_keys", rID+".key"))
}

// init repo
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun", "--rootkey", rootFiles[0])
require.NoError(t, err)

// publish repo
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
require.NoError(t, err)
}

// Initializes a repo, adds a target, publishes the target, lists the target,
// verifies the target, and then removes the target.
func TestClientTUFInteraction(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion cmd/notary/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ func setUpRepo(t *testing.T, tempBaseDir, gun string, ret notary.PassRetriever)
rootPubKey, err := repo.CryptoService.Create("root", "", data.ECDSAKey)
require.NoError(t, err, "error generating root key: %s", err)

err = repo.Initialize(rootPubKey.ID())
err = repo.Initialize([]string{rootPubKey.ID()})
require.NoError(t, err)

return ts, repo.CryptoService.ListAllKeys()
Expand Down
50 changes: 44 additions & 6 deletions cmd/notary/tuf.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bufio"
"encoding/hex"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
Expand All @@ -19,6 +20,8 @@ import (
"github.com/docker/go-connections/tlsconfig"
"github.com/docker/notary"
notaryclient "github.com/docker/notary/client"
"github.com/docker/notary/cryptoservice"
"github.com/docker/notary/trustmanager"
"github.com/docker/notary/trustpinning"
"github.com/docker/notary/tuf/data"
"github.com/docker/notary/utils"
Expand Down Expand Up @@ -86,17 +89,21 @@ type tufCommander struct {
retriever notary.PassRetriever

// these are for command line parsing - no need to set
roles []string
sha256 string
sha512 string
roles []string
sha256 string
sha512 string
rootKey string

input string
output string
quiet bool
}

func (t *tufCommander) AddToCommand(cmd *cobra.Command) {
cmd.AddCommand(cmdTUFInitTemplate.ToCommand(t.tufInit))
cmdTUFInit := cmdTUFInitTemplate.ToCommand(t.tufInit)
cmdTUFInit.Flags().StringVar(&t.rootKey, "rootkey", "", "Root key to initialize the repository with")
cmd.AddCommand(cmdTUFInit)

cmd.AddCommand(cmdTUFStatusTemplate.ToCommand(t.tufStatus))
cmd.AddCommand(cmdTUFPublishTemplate.ToCommand(t.tufPublish))
cmd.AddCommand(cmdTUFLookupTemplate.ToCommand(t.tufLookup))
Expand Down Expand Up @@ -268,7 +275,38 @@ func (t *tufCommander) tufInit(cmd *cobra.Command, args []string) error {
return err
}

rootKeyList := nRepo.CryptoService.ListKeys(data.CanonicalRootRole)
var rootKeyList []string

if t.rootKey != "" {
keyFile, err := os.Open(t.rootKey)
if err != nil {
return fmt.Errorf("Opening file for import: %v", err)
}
defer keyFile.Close()

pemBytes, err := ioutil.ReadAll(keyFile)
if err != nil {
return fmt.Errorf("Error reading input file: %v", err)
}
if err = cryptoservice.CheckRootKeyIsEncrypted(pemBytes); err != nil {
return err
}

privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "")
if err != nil {
privKey, _, err = trustmanager.GetPasswdDecryptBytes(t.retriever, pemBytes, "", "imported root")
if err != nil {
return err
}
}
err = nRepo.CryptoService.AddKey(data.CanonicalRootRole, "", privKey)
if err != nil {
return fmt.Errorf("Error importing key: %v", err)
}
rootKeyList = []string{nRepo.CryptoService.GetKey(privKey.ID()).ID()}
} else {
rootKeyList = nRepo.CryptoService.ListKeys(data.CanonicalRootRole)
}

var rootKeyID string
if len(rootKeyList) < 1 {
Expand All @@ -285,7 +323,7 @@ func (t *tufCommander) tufInit(cmd *cobra.Command, args []string) error {
cmd.Printf("Root key found, using: %s\n", rootKeyID)
}

if err = nRepo.Initialize(rootKeyID); err != nil {
if err = nRepo.Initialize([]string{rootKeyID}); err != nil {
return err
}
return nil
Expand Down

0 comments on commit 18d2ad4

Please sign in to comment.