// If there is one key, asking to remove it will ask for confirmation. Passing // 'yes'/'y' response will continue the deletion. func TestRemoveOneKeyConfirm(t *testing.T) { setUp(t) yesses := []string{"yes", " Y "} for _, yesAnswer := range yesses { store := trustmanager.NewKeyMemoryStore(ret) key, err := utils.GenerateED25519Key(rand.Reader) require.NoError(t, err) err = store.AddKey(trustmanager.KeyInfo{Role: data.CanonicalRootRole, Gun: ""}, key) require.NoError(t, err) var out bytes.Buffer in := bytes.NewBuffer([]byte(yesAnswer + "\n")) err = removeKeyInteractively( []trustmanager.KeyStore{store}, key.ID(), in, &out) require.NoError(t, err) text, err := ioutil.ReadAll(&out) require.NoError(t, err) output := string(text) require.Contains(t, output, "Are you sure") require.Contains(t, output, "Deleted "+key.ID()) require.Len(t, store.ListKeys(), 0) } }
// If there is more than one key, removeKeyInteractively will ask which key to // delete. Then it will confirm whether they want to delete, and the user can // abort at that confirmation. func TestRemoveMultikeysAbortChoice(t *testing.T) { setUp(t) in := bytes.NewBuffer([]byte("1\nn\n")) key, err := utils.GenerateED25519Key(rand.Reader) require.NoError(t, err) stores := []trustmanager.KeyStore{ trustmanager.NewKeyMemoryStore(ret), trustmanager.NewKeyMemoryStore(ret), } err = stores[0].AddKey(trustmanager.KeyInfo{Role: data.CanonicalRootRole, Gun: ""}, key) require.NoError(t, err) err = stores[1].AddKey(trustmanager.KeyInfo{Role: data.CanonicalTargetsRole, Gun: "gun"}, key) require.NoError(t, err) var out bytes.Buffer err = removeKeyInteractively(stores, key.ID(), in, &out) require.NoError(t, err) // no error to abort deleting text, err := ioutil.ReadAll(&out) require.NoError(t, err) require.Len(t, stores[0].ListKeys(), 1) require.Len(t, stores[1].ListKeys(), 1) // It should have listed the keys, asked whether the user really wanted to // delete, and then aborted. output := string(text) require.Contains(t, output, "Found the following matching keys") require.Contains(t, output, "Are you sure") require.Contains(t, output, "Aborting action") }
// If there is one key, asking to remove it will ask for confirmation. Passing // anything other than 'yes'/'y'/'' response will abort the deletion and // not delete the key. func TestRemoveOneKeyAbort(t *testing.T) { setUp(t) nos := []string{"no", "NO", "AAAARGH", " N "} store := trustmanager.NewKeyMemoryStore(ret) key, err := utils.GenerateED25519Key(rand.Reader) require.NoError(t, err) err = store.AddKey(trustmanager.KeyInfo{Role: data.CanonicalRootRole, Gun: ""}, key) require.NoError(t, err) stores := []trustmanager.KeyStore{store} for _, noAnswer := range nos { var out bytes.Buffer in := bytes.NewBuffer([]byte(noAnswer + "\n")) err := removeKeyInteractively(stores, key.ID(), in, &out) require.NoError(t, err) text, err := ioutil.ReadAll(&out) require.NoError(t, err) output := string(text) require.Contains(t, output, "Are you sure") require.Contains(t, output, "Aborting action") require.Len(t, store.ListKeys(), 1) } }
// AddGetKeyCryptoServiceInterfaceBehaviorTests tests expected behavior for // adding keys in a signed.CryptoService and other read operations on the // crypto service after keys are present // 1. Adding a key succeeds // 2. Getting the key should return the same key, without error // 3. Removing the key succeeds func AddGetKeyCryptoServiceInterfaceBehaviorTests(t *testing.T, cs signed.CryptoService, algo string) { expectedRolesToKeys := make(map[string]string) for i := 0; i < 2; i++ { var ( addedPrivKey data.PrivateKey err error ) role := data.BaseRoles[i+1] switch algo { case data.RSAKey: addedPrivKey, err = utils.GenerateRSAKey(rand.Reader, 2048) case data.ECDSAKey: addedPrivKey, err = utils.GenerateECDSAKey(rand.Reader) case data.ED25519Key: addedPrivKey, err = utils.GenerateED25519Key(rand.Reader) default: require.FailNow(t, "invalid algorithm %s", algo) } require.NoError(t, err) require.NotNil(t, addedPrivKey) require.NoError(t, cs.AddKey(role, "docker.io/notary", addedPrivKey)) expectedRolesToKeys[role] = addedPrivKey.ID() } testGetKey(t, cs, expectedRolesToKeys, algo) }
// Given a list of key stores, the keys should be pretty-printed with their // roles, locations, IDs, and guns first in sorted order in the key store func TestPrettyPrintRootAndSigningKeys(t *testing.T) { ret := passphrase.ConstantRetriever("pass") keyStores := []trustmanager.KeyStore{ trustmanager.NewKeyMemoryStore(ret), &otherMemoryStore{GenericKeyStore: *trustmanager.NewKeyMemoryStore(ret)}, } longNameShortened := "..." + strings.Repeat("z", 37) keys := make([]data.PrivateKey, 4) for i := 0; i < 4; i++ { key, err := utils.GenerateED25519Key(rand.Reader) require.NoError(t, err) keys[i] = key } root := data.CanonicalRootRole // add keys to the key stores require.NoError(t, keyStores[0].AddKey(trustmanager.KeyInfo{Role: root, Gun: ""}, keys[0])) require.NoError(t, keyStores[1].AddKey(trustmanager.KeyInfo{Role: root, Gun: ""}, keys[0])) require.NoError(t, keyStores[0].AddKey(trustmanager.KeyInfo{Role: data.CanonicalTargetsRole, Gun: strings.Repeat("/a", 30)}, keys[1])) require.NoError(t, keyStores[1].AddKey(trustmanager.KeyInfo{Role: data.CanonicalSnapshotRole, Gun: "short/gun"}, keys[1])) require.NoError(t, keyStores[0].AddKey(trustmanager.KeyInfo{Role: "targets/a", Gun: ""}, keys[3])) require.NoError(t, keyStores[0].AddKey(trustmanager.KeyInfo{Role: "invalidRole", Gun: ""}, keys[2])) expected := [][]string{ // root always comes first {root, keys[0].ID(), keyStores[0].Name()}, {root, keys[0].ID(), longNameShortened}, // these have no gun, so they come first {"invalidRole", keys[2].ID(), keyStores[0].Name()}, {"targets/a", keys[3].ID(), keyStores[0].Name()}, // these have guns, and are sorted then by guns {data.CanonicalTargetsRole, "..." + strings.Repeat("/a", 11), keys[1].ID(), keyStores[0].Name()}, {data.CanonicalSnapshotRole, "short/gun", keys[1].ID(), longNameShortened}, } var b bytes.Buffer prettyPrintKeys(keyStores, &b) text, err := ioutil.ReadAll(&b) require.NoError(t, err) lines := strings.Split(strings.TrimSpace(string(text)), "\n") require.Len(t, lines, len(expected)+2) // starts with headers require.True(t, reflect.DeepEqual(strings.Fields(lines[0]), []string{"ROLE", "GUN", "KEY", "ID", "LOCATION"})) require.Equal(t, "----", lines[1][:4]) for i, line := range lines[2:] { // we are purposely not putting spaces in test data so easier to split splitted := strings.Fields(line) for j, v := range splitted { require.Equal(t, expected[i][j], strings.TrimSpace(v)) } } }
// Create generates a new key and returns the public part func (e *Ed25519) Create(role, gun, algorithm string) (data.PublicKey, error) { if algorithm != data.ED25519Key { return nil, errors.New("only ED25519 supported by this cryptoservice") } private, err := utils.GenerateED25519Key(rand.Reader) if err != nil { return nil, err } e.addKey(role, private) return data.PublicKeyFromPrivate(private), nil }
// If there is more than one key, removeKeyInteractively will ask which key to // delete. Then it will confirm whether they want to delete, and if the user // confirms, will remove it from the correct key store. func TestRemoveMultikeysRemoveOnlyChosenKey(t *testing.T) { setUp(t) in := bytes.NewBuffer([]byte("1\ny\n")) key, err := utils.GenerateED25519Key(rand.Reader) require.NoError(t, err) stores := []trustmanager.KeyStore{ trustmanager.NewKeyMemoryStore(ret), trustmanager.NewKeyMemoryStore(ret), } err = stores[0].AddKey(trustmanager.KeyInfo{Role: data.CanonicalRootRole, Gun: ""}, key) require.NoError(t, err) err = stores[1].AddKey(trustmanager.KeyInfo{Role: data.CanonicalTargetsRole, Gun: "gun"}, key) require.NoError(t, err) var out bytes.Buffer err = removeKeyInteractively(stores, key.ID(), in, &out) require.NoError(t, err) text, err := ioutil.ReadAll(&out) require.NoError(t, err) // It should have listed the keys, asked whether the user really wanted to // delete, and then deleted. output := string(text) require.Contains(t, output, "Found the following matching keys") require.Contains(t, output, "Are you sure") require.Contains(t, output, "Deleted "+key.ID()) // figure out which one we picked to delete, and assert it was deleted for _, line := range strings.Split(output, "\n") { if strings.HasPrefix(line, "\t1.") { // we picked the first item if strings.Contains(line, "root") { // first key store require.Len(t, stores[0].ListKeys(), 0) require.Len(t, stores[1].ListKeys(), 1) } else { require.Len(t, stores[0].ListKeys(), 1) require.Len(t, stores[1].ListKeys(), 0) } } } }
// If there is more than one key, removeKeyInteractively will ask which key to // delete and will do so over and over until the user quits if the answer is // invalid. func TestRemoveMultikeysInvalidInput(t *testing.T) { setUp(t) in := bytes.NewBuffer([]byte("notanumber\n9999\n-3\n0")) key, err := utils.GenerateED25519Key(rand.Reader) require.NoError(t, err) stores := []trustmanager.KeyStore{ trustmanager.NewKeyMemoryStore(ret), trustmanager.NewKeyMemoryStore(ret), } err = stores[0].AddKey(trustmanager.KeyInfo{Role: data.CanonicalRootRole, Gun: ""}, key) require.NoError(t, err) err = stores[1].AddKey(trustmanager.KeyInfo{Role: data.CanonicalTargetsRole, Gun: "gun"}, key) require.NoError(t, err) var out bytes.Buffer err = removeKeyInteractively(stores, key.ID(), in, &out) require.Error(t, err) text, err := ioutil.ReadAll(&out) require.NoError(t, err) require.Len(t, stores[0].ListKeys(), 1) require.Len(t, stores[1].ListKeys(), 1) // It should have listed the keys over and over, asking which key the user // wanted to delete output := string(text) require.Contains(t, output, "Found the following matching keys") var rootCount, targetCount int for _, line := range strings.Split(output, "\n") { if strings.Contains(line, key.ID()) { if strings.Contains(line, "target") { targetCount++ } else { rootCount++ } } } require.Equal(t, rootCount, targetCount) require.Equal(t, 5, rootCount) // original + 1 for each of the 4 invalid inputs }
// helper function to generate private keys for the signer databases - does not implement RSA since that is not // supported by the signer func generatePrivateKey(algorithm string) (data.PrivateKey, error) { var privKey data.PrivateKey var err error switch algorithm { case data.ECDSAKey: privKey, err = utils.GenerateECDSAKey(rand.Reader) if err != nil { return nil, fmt.Errorf("failed to generate EC key: %v", err) } case data.ED25519Key: privKey, err = utils.GenerateED25519Key(rand.Reader) if err != nil { return nil, fmt.Errorf("failed to generate ED25519 key: %v", err) } default: return nil, fmt.Errorf("private key type not supported for key generation: %s", algorithm) } return privKey, nil }
// Create is used to generate keys for targets, snapshots and timestamps func (cs *CryptoService) Create(role, gun, algorithm string) (data.PublicKey, error) { var privKey data.PrivateKey var err error switch algorithm { case data.RSAKey: privKey, err = utils.GenerateRSAKey(rand.Reader, notary.MinRSABitSize) if err != nil { return nil, fmt.Errorf("failed to generate RSA key: %v", err) } case data.ECDSAKey: privKey, err = utils.GenerateECDSAKey(rand.Reader) if err != nil { return nil, fmt.Errorf("failed to generate EC key: %v", err) } case data.ED25519Key: privKey, err = utils.GenerateED25519Key(rand.Reader) if err != nil { return nil, fmt.Errorf("failed to generate ED25519 key: %v", err) } default: return nil, fmt.Errorf("private key type not supported for key generation: %s", algorithm) } logrus.Debugf("generated new %s key for role: %s and keyID: %s", algorithm, role, privKey.ID()) // Store the private key into our keystore for _, ks := range cs.keyStores { err = ks.AddKey(trustmanager.KeyInfo{Role: role, Gun: gun}, privKey) if err == nil { return data.PublicKeyFromPrivate(privKey), nil } } if err != nil { return nil, fmt.Errorf("failed to add key to filestore: %v", err) } return nil, fmt.Errorf("keystores would not accept new private keys for unknown reasons") }