Skip to content

Commit

Permalink
Import only one root key, and add ability to import root cert
Browse files Browse the repository at this point in the history
Signed-off-by: David Wake <[email protected]>
  • Loading branch information
David Wake committed Jul 1, 2016
1 parent 42f1e10 commit f01a1bd
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 118 deletions.
69 changes: 54 additions & 15 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package client

import (
"bytes"
"crypto/rand"
"encoding/json"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -168,19 +169,53 @@ func rootCertKey(gun string, privKey data.PrivateKey) (data.PublicKey, error) {
return x509PublicKey, nil
}

// Initialize creates a new repository by using rootKey as the root Key for the
// Initialize creates a new repository by using rootKeyID to identify the root Key for the
// TUF repository. The server must be reachable (and is asked to generate a
// 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(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)
func (r *NotaryRepository) Initialize(rootKeyID string, serverManagedRoles ...string) error {
return r.InitializeWithCert(rootKeyID, (data.PublicKey)(nil), serverManagedRoles...)
}

func verifyPublicKeyMatchesPrivateKey(privKey data.PrivateKey, pubKey data.PublicKey) error {
// generate a random message
msgLength := 64
msg := make([]byte, msgLength)
_, err := rand.Read(msg)
if err != nil {
return fmt.Errorf("failed to generate random test message: %s", err)
}

// sign the message with the private key
signatureBytes, err := privKey.Sign(rand.Reader, msg, nil)
if err != nil {
return fmt.Errorf("Failed to sign test message:", err)
}

signature := data.Signature{
KeyID: privKey.ID(),
Method: privKey.SignatureAlgorithm(),
Signature: signatureBytes,
}

// verify the signature with the public key
if err := signed.VerifySignature(msg, signature, pubKey); err != nil {
return fmt.Errorf("Private Key did not match Public Key: %s", err)
}
return nil
}

// InitializeWithCert creates a new repository by using rootKeyID to identify the root Key for the
// TUF repository. If rootPublicKey is non-nil, it must match the private key identified by rootKeyID or an error is thrown
// The server must be reachable (and is asked to generate a
// 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) InitializeWithCert(rootKeyID string, rootPublicKey data.PublicKey, serverManagedRoles ...string) error {
privKey, _, err := r.CryptoService.GetPrivateKey(rootKeyID)
if err != nil {
return err
}

// currently we only support server managing timestamps and snapshots, and
Expand Down Expand Up @@ -210,20 +245,24 @@ func (r *NotaryRepository) Initialize(rootKeyIDs []string, serverManagedRoles ..
}
}

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

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

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

err := r.tufRepo.InitRoot(
err = r.tufRepo.InitRoot(
rootRole,
timestampRole,
snapshotRole,
Expand Down
46 changes: 13 additions & 33 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([]string{rootPubKeyID}, serverManagedRoles...)
err = repo.Initialize(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([]string{rootPubKeyID}, data.CanonicalRootRole)
err = repo.Initialize(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([]string{rootPubKeyID}, "randomrole")
err = repo.Initialize(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([]string{rootPubKeyID}, data.CanonicalTargetsRole)
err = repo.Initialize(rootPubKeyID, data.CanonicalTargetsRole)
require.Error(t, err)
require.IsType(t, ErrInvalidRemoteRole{}, err)
// no key creation happened
Expand All @@ -298,27 +298,7 @@ func TestInitRepositoryManagedRolesIncludingTimestamp(t *testing.T) {

repo, rec, rootPubKeyID := createRepoAndKey(
t, data.ECDSAKey, tempBaseDir, "docker.com/notary", ts.URL)
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)
err = repo.Initialize(rootPubKeyID, data.CanonicalTimestampRole)
require.NoError(t, err)
// generates the target role, the snapshot role
rec.requireCreated(t, []string{data.CanonicalTargetsRole, data.CanonicalSnapshotRole})
Expand All @@ -337,7 +317,7 @@ func TestInitRepositoryNeedsRemoteTimestampKey(t *testing.T) {

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

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

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

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

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

require.NoError(t, err, "error initializing repository")
Expand Down Expand Up @@ -583,7 +563,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([]string{rootPubKey.ID()})
err = repo.Initialize(rootPubKey.ID())
require.EqualError(t, err, trustmanager.ErrAttemptsExceeded{}.Error())
}

Expand Down Expand Up @@ -620,7 +600,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([]string{rootPubKey.ID()})
err = repo.Initialize(rootPubKey.ID())
require.EqualError(t, err, trustmanager.ErrPasswordInvalid{}.Error())
}

Expand Down Expand Up @@ -1670,7 +1650,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([]string{rootPubKey.ID()}))
require.NoError(t, repo.Initialize(rootPubKey.ID()))

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

repo.CryptoService = cannotCreateKeys{CryptoService: cs}

err = repo.Initialize([]string{rootPubKey.ID()}, data.CanonicalSnapshotRole)
err = repo.Initialize(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
40 changes: 0 additions & 40 deletions cmd/notary/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,46 +81,6 @@ func setupServer() *httptest.Server {
return httptest.NewServer(setupServerHandler(storage.NewMemStorage()))
}

// Initializes a repo with multiple existing keys
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)
assertNumKeys(t, tempDir, 1, 0, true)
_, err = runCommand(t, tempDir, "key", "generate", data.ECDSAKey)
require.NoError(t, err)
rootIDs, _ := assertNumKeys(t, tempDir, 2, 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", "--rootkeys", strings.Join(rootFiles, ","))
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([]string{rootPubKey.ID()})
err = repo.Initialize(rootPubKey.ID())
require.NoError(t, err)

return ts, repo.CryptoService.ListAllKeys()
Expand Down
Loading

0 comments on commit f01a1bd

Please sign in to comment.