-
Notifications
You must be signed in to change notification settings - Fork 511
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support passing in root keys on repo initialization #801
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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{} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above comment for |
||
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 | ||
|
@@ -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, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
} | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -298,12 +298,37 @@ 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we verify that both keys were included in the root metadata? Something like: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitely, will update |
||
require.NoError(t, err) | ||
|
||
// generates the target role, the snapshot role | ||
rec.requireCreated(t, []string{data.CanonicalTargetsRole, data.CanonicalSnapshotRole}) | ||
|
||
// has two root keys | ||
require.Len(t, repo.tufRepo.Root.Signed.Roles[data.CanonicalRootRole].KeyIDs, 2) | ||
} | ||
|
||
// Initializing a new repo fails if unable to get the timestamp key, even if | ||
// the snapshot key is available | ||
func TestInitRepositoryNeedsRemoteTimestampKey(t *testing.T) { | ||
|
@@ -317,7 +342,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) | ||
|
||
|
@@ -339,7 +364,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) | ||
|
||
|
@@ -516,9 +541,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") | ||
|
@@ -563,7 +588,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()) | ||
} | ||
|
||
|
@@ -600,7 +625,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()) | ||
} | ||
|
||
|
@@ -1650,7 +1675,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) | ||
|
@@ -2011,7 +2036,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) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,6 +81,97 @@ func setupServer() *httptest.Server { | |
return httptest.NewServer(setupServerHandler(storage.NewMemStorage())) | ||
} | ||
|
||
// Initializes a repo with existing key | ||
func TestInitWithRootKey(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 -- | ||
|
||
// create encrypted root key | ||
privKey, err := trustmanager.GenerateECDSAKey(rand.Reader) | ||
require.NoError(t, err) | ||
encryptedPEMPrivKey, err := trustmanager.EncryptPrivateKey(privKey, data.CanonicalRootRole, testPassphrase) | ||
require.NoError(t, err) | ||
encryptedPEMKeyFilename := filepath.Join(tempDir, "encrypted_key.key") | ||
err = ioutil.WriteFile(encryptedPEMKeyFilename, encryptedPEMPrivKey, 0644) | ||
require.NoError(t, err) | ||
|
||
// init repo | ||
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun", "--rootkey", encryptedPEMKeyFilename) | ||
require.NoError(t, err) | ||
|
||
// publish repo | ||
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") | ||
require.NoError(t, err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be awesome to add a check to this test that this initialized repo used the specified key from the flag (especially if we had more rootkeys in the Unfortunately I can't think of a way to verify which root key was used without manually unmarshalling metadata (unless we wanted to bring in a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apologies for the trouble - would it also be possible to add some or all of the following negative test cases? Similar test cases may already exist for importing a root key.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No problem; we already have the key id here so we can just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry @cyli GitHub caching caused me to miss your comment. The integration tests seem to only test the "happy path" - not saying that's all they should test, just noting that it's in keeping with the current style. IIRC the cases you listed are tested at the api levels, because the addition in this PR uses all the same code paths for key loading. I will double check to make sure. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ecordell You are definitely right in that the utility functions you use for loading the key should all have be tested already. I agree with you that it's pointless to re-test this functionality here; I mainly wanted to test that particular loading checks (like ensuring that we only allow importing encrypted keys - we did not have to include this check, after all) are called and that their errors are propagated (for instance that we don't just fall back on generating a random root key if importing a key fails), and wanted some way to trigger said failures. For instance, for case 3, it's to test that the root key is required to be encrypted - you could for instance completely skip that check and allow importing an unencrypted root key (which may or may not be a valid choice). Also, for case 4, cryptoservice's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @cyli. I updated with integration tests for the cases you enumerated - deciding whether or not to change the behavior in those cases is, as you point out, up for discussion, but at least we can track those changes now with the tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 Thanks so much! :) |
||
|
||
// check that the root key used for init is the one listed as root key | ||
output, err := runCommand(t, tempDir, "key", "list") | ||
require.NoError(t, err) | ||
require.True(t, strings.Contains(output, data.PublicKeyFromPrivate(privKey).ID())) | ||
|
||
// check error if file doesn't exist | ||
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun2", "--rootkey", "bad_file") | ||
require.Error(t, err, "Init with nonexistent key file should error") | ||
|
||
// check error if file is invalid format | ||
badKeyFilename := filepath.Join(tempDir, "bad_key.key") | ||
nonPEMKey := []byte("thisisnotapemkey") | ||
err = ioutil.WriteFile(badKeyFilename, nonPEMKey, 0644) | ||
require.NoError(t, err) | ||
|
||
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun2", "--rootkey", badKeyFilename) | ||
require.Error(t, err, "Init with non-PEM key should error") | ||
|
||
// check error if unencrypted PEM used | ||
unencryptedPrivKey, err := trustmanager.GenerateECDSAKey(rand.Reader) | ||
require.NoError(t, err) | ||
unencryptedPEMPrivKey, err := trustmanager.KeyToPEM(unencryptedPrivKey, data.CanonicalRootRole) | ||
require.NoError(t, err) | ||
unencryptedPEMKeyFilename := filepath.Join(tempDir, "unencrypted_key.key") | ||
err = ioutil.WriteFile(unencryptedPEMKeyFilename, unencryptedPEMPrivKey, 0644) | ||
require.NoError(t, err) | ||
|
||
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun2", "--rootkey", unencryptedPEMKeyFilename) | ||
require.Error(t, err, "Init with unencrypted PEM key should error") | ||
|
||
// check error if invalid password used | ||
// instead of using a new retriever, we create a new key with a different pass | ||
badPassPrivKey, err := trustmanager.GenerateECDSAKey(rand.Reader) | ||
require.NoError(t, err) | ||
badPassPEMPrivKey, err := trustmanager.EncryptPrivateKey(badPassPrivKey, data.CanonicalRootRole, "bad_pass") | ||
require.NoError(t, err) | ||
badPassPEMKeyFilename := filepath.Join(tempDir, "badpass_key.key") | ||
err = ioutil.WriteFile(badPassPEMKeyFilename, badPassPEMPrivKey, 0644) | ||
require.NoError(t, err) | ||
|
||
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun2", "--rootkey", badPassPEMKeyFilename) | ||
require.Error(t, err, "Init with wrong password should error") | ||
|
||
// check error if wrong role specified | ||
snapshotPrivKey, err := trustmanager.GenerateECDSAKey(rand.Reader) | ||
require.NoError(t, err) | ||
snapshotPEMPrivKey, err := trustmanager.KeyToPEM(snapshotPrivKey, data.CanonicalSnapshotRole) | ||
require.NoError(t, err) | ||
snapshotPEMKeyFilename := filepath.Join(tempDir, "snapshot_key.key") | ||
err = ioutil.WriteFile(snapshotPEMKeyFilename, snapshotPEMPrivKey, 0644) | ||
require.NoError(t, err) | ||
|
||
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun2", "--rootkey", snapshotPEMKeyFilename) | ||
require.Error(t, err, "Init with wrong role should error") | ||
} | ||
|
||
// 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) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ import ( | |
"bufio" | ||
"encoding/hex" | ||
"fmt" | ||
"io/ioutil" | ||
"net" | ||
"net/http" | ||
"net/url" | ||
|
@@ -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" | ||
|
@@ -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)) | ||
|
@@ -268,7 +275,36 @@ func (t *tufCommander) tufInit(cmd *cobra.Command, args []string) error { | |
return err | ||
} | ||
|
||
rootKeyList := nRepo.CryptoService.ListKeys(data.CanonicalRootRole) | ||
var rootKeyList []string | ||
|
||
if t.rootKey != "" { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This addition is pushing this function right up to the cyclomatic complexity threshold before goreportcard marks it as a problem. The interior of this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitely, and I have a note on my desk to make this exact change and just forgot. I can make a new PR if appropriate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
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.GetPasswdDecryptBytes(t.retriever, pemBytes, "", data.CanonicalRootRole) | ||
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{data.PublicKeyFromPrivate(privKey).ID()} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should just be able to do |
||
} else { | ||
rootKeyList = nRepo.CryptoService.ListKeys(data.CanonicalRootRole) | ||
} | ||
|
||
var rootKeyID string | ||
if len(rootKeyList) < 1 { | ||
|
@@ -285,7 +321,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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If other root keys already exist on this host, this Edit: this is resolved now in above logic |
||
return err | ||
} | ||
return nil | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In all likelihood
privKeys
will have the same length asrootKeyIDs
so we should initialize appropriately:privKeys := make([]data.PrivateKey, 0, len(rootKeyIDs))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not so sure - I think you should be able to initialize with one private key and multiple public keys, so that these two don't match up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not implemented in this PR though. Although @dnwake's PR for
--rootcert
may make my comment moot.