// 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) { in := bytes.NewBuffer([]byte("1\nn\n")) key, err := trustmanager.GenerateED25519Key(rand.Reader) assert.NoError(t, err) stores := []trustmanager.KeyStore{ trustmanager.NewKeyMemoryStore(ret), trustmanager.NewKeyMemoryStore(ret), } err = stores[0].AddKey(key.ID(), "root", key) assert.NoError(t, err) err = stores[1].AddKey("gun/"+key.ID(), "target", key) assert.NoError(t, err) var out bytes.Buffer err = removeKeyInteractively(stores, key.ID(), in, &out) assert.NoError(t, err) // no error to abort deleting text, err := ioutil.ReadAll(&out) assert.NoError(t, err) assert.Len(t, stores[0].ListKeys(), 1) assert.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) assert.Contains(t, output, "Found the following matching keys") assert.Contains(t, output, "Are you sure") assert.Contains(t, output, "Aborting action") }
// Importing an existing key succeeds, but doesn't actually add the key, nor // does it write it to backup. func TestYubiImportExistingKey(t *testing.T) { if !YubikeyAccessible() { t.Skip("Must have Yubikey access.") } clearAllKeys(t) SetYubikeyKeyMode(KeymodeNone) defer func() { SetYubikeyKeyMode(KeymodeTouch | KeymodePinOnce) }() store, err := NewYubiKeyStore(trustmanager.NewKeyMemoryStore(ret), ret) assert.NoError(t, err) key, err := testAddKey(t, store) backup := trustmanager.NewKeyMemoryStore(ret) newStore, err := NewYubiKeyStore(backup, ret) assert.NoError(t, err) // for sanity, ensure that the key is already in the Yubikey k, _, err := newStore.GetKey(key.ID()) assert.NoError(t, err) assert.NotNil(t, k) // import the key, which should have already been added to the yubikey pemBytes, err := trustmanager.EncryptPrivateKey(key, "passphrase") assert.NoError(t, err) err = newStore.ImportKey(pemBytes, "root") assert.NoError(t, err) // key is not in backup store _, _, err = backup.GetKey(key.ID()) assert.Error(t, err) }
// If, when adding a key to the Yubikey, and it already exists, we succeed // without adding it to the backup store. func TestYubiAddDuplicateKeySucceedsButDoesNotBackup(t *testing.T) { if !IsAccessible() { t.Skip("Must have Yubikey access.") } clearAllKeys(t) SetYubikeyKeyMode(KeymodeNone) defer func() { SetYubikeyKeyMode(KeymodeTouch | KeymodePinOnce) }() origStore, err := NewYubiStore(trustmanager.NewKeyMemoryStore(ret), ret) require.NoError(t, err) key, err := testAddKey(t, origStore) require.NoError(t, err) backup := trustmanager.NewKeyMemoryStore(ret) cleanStore, err := NewYubiStore(backup, ret) require.NoError(t, err) require.Len(t, cleanStore.ListKeys(), 1) err = cleanStore.AddKey(trustmanager.KeyInfo{Role: data.CanonicalRootRole, Gun: ""}, key) require.NoError(t, err) // there should be just 1 key on the yubikey require.Len(t, cleanListKeys(t), 1) // nothing was added to the backup require.Len(t, backup.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, when adding a key to the Yubikey, and it already exists, we succeed // without adding it to the backup store. func TestYubiAddDuplicateKeySucceedsButDoesNotBackup(t *testing.T) { if !YubikeyAccessible() { t.Skip("Must have Yubikey access.") } clearAllKeys(t) SetYubikeyKeyMode(KeymodeNone) defer func() { SetYubikeyKeyMode(KeymodeTouch | KeymodePinOnce) }() origStore, err := NewYubiKeyStore(trustmanager.NewKeyMemoryStore(ret), ret) assert.NoError(t, err) key, err := testAddKey(t, origStore) assert.NoError(t, err) backup := trustmanager.NewKeyMemoryStore(ret) cleanStore, err := NewYubiKeyStore(backup, ret) assert.NoError(t, err) assert.Len(t, cleanStore.ListKeys(), 1) err = cleanStore.AddKey(key.ID(), "root", key) assert.NoError(t, err) // there should be just 1 key on the yubikey assert.Len(t, cleanListKeys(t), 1) // nothing was added to the backup assert.Len(t, backup.ListKeys(), 0) }
// If some random key in the middle was removed, adding a key will work (keys // do not have to be deleted/added in order) func TestYubiAddKeyCanAddToMiddleSlot(t *testing.T) { if !IsAccessible() { t.Skip("Must have Yubikey access.") } clearAllKeys(t) SetYubikeyKeyMode(KeymodeNone) defer func() { SetYubikeyKeyMode(KeymodeTouch | KeymodePinOnce) }() // create 4 keys on the original store backup := trustmanager.NewKeyMemoryStore(ret) store, err := NewYubiStore(backup, ret) require.NoError(t, err) keys := addMaxKeys(t, store) // delete one of the middle keys, and assert we can still create a new key keyIDToDelete := keys[numSlots/2] err = store.RemoveKey(keyIDToDelete) require.NoError(t, err) newKey, err := testAddKey(t, store) require.NoError(t, err) // create a new store, since we want to be sure the original store's cache // is not masking any issues cleanStore, err := NewYubiStore(trustmanager.NewKeyMemoryStore(ret), ret) require.NoError(t, err) // The new key should be in the original store, in the new clean store, and // in the backup store. The old key should not be in the original store, // or the new clean store. for _, store := range []trustmanager.KeyStore{store, cleanStore, backup} { // new key should appear in all stores gottenKey, _, err := store.GetKey(newKey.ID()) require.NoError(t, err) require.Equal(t, gottenKey.ID(), newKey.ID()) listedKeys := store.ListKeys() _, ok := listedKeys[newKey.ID()] require.True(t, ok) // old key should not be in the non-backup stores if store != backup { _, _, err := store.GetKey(keyIDToDelete) require.Error(t, err) _, ok = listedKeys[keyIDToDelete] require.False(t, ok) } } }
// 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 := trustmanager.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) } }
// initialize a repo with keys, so they can be rotated func setUpRepo(t *testing.T, tempBaseDir, gun string, ret notary.PassRetriever) ( *httptest.Server, map[string]string) { // 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") // Eat the logs instead of spewing them out l := logrus.New() l.Out = bytes.NewBuffer(nil) ctx = ctxu.WithLogger(ctx, logrus.NewEntry(l)) cryptoService := cryptoservice.NewCryptoService(trustmanager.NewKeyMemoryStore(ret)) ts := httptest.NewServer(server.RootHandler(nil, ctx, cryptoService, nil, nil, nil)) repo, err := client.NewNotaryRepository( tempBaseDir, gun, ts.URL, http.DefaultTransport, ret, trustpinning.TrustPinConfig{}) require.NoError(t, err, "error creating repo: %s", err) rootPubKey, err := repo.CryptoService.Create("root", "", data.ECDSAKey) require.NoError(t, err, "error generating root key: %s", err) err = repo.Initialize(rootPubKey.ID()) require.NoError(t, err) return ts, repo.CryptoService.ListAllKeys() }
// 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) } }
func TestUnlockedSigner(t *testing.T) { privKey, err := trustmanager.GenerateECDSAKey(rand.Reader) assert.NoError(t, err, "could not generate key") keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) err = keyStore.AddKey(privKey.ID(), "root", privKey) assert.NoError(t, err, "could not add key to store") cryptoService := NewCryptoService("", keyStore) uCryptoService := NewUnlockedCryptoService(privKey, cryptoService) // Check ID method assert.Equal(t, privKey.ID(), uCryptoService.ID()) // Check Public method assert.Equal(t, privKey.Public(), uCryptoService.PublicKey().Public()) assert.Equal(t, privKey.ID(), uCryptoService.PublicKey().ID()) // Check GenerateCertificate method gun := "docker.com/notary" cert, err := uCryptoService.GenerateCertificate(gun) assert.NoError(t, err, "could not generate certificate") // Check public key ecdsaPrivateKey, err := x509.ParseECPrivateKey(privKey.Private()) assert.NoError(t, err) ecdsaPublicKey := ecdsaPrivateKey.Public() assert.Equal(t, ecdsaPublicKey, cert.PublicKey) // Check CommonName assert.Equal(t, cert.Subject.CommonName, gun) }
// If there are multiple keystores, ensure that a key is only added to one - // the first in the list of keyStores (which is in order of preference) func (c CryptoServiceTester) TestCreateAndGetWhenMultipleKeystores(t *testing.T) { cryptoService := c.cryptoServiceFactory() cryptoService.keyStores = append(cryptoService.keyStores, trustmanager.NewKeyMemoryStore(passphraseRetriever)) // Test Create tufKey, err := cryptoService.Create(c.role, c.keyAlgo) assert.NoError(t, err, c.errorMsg("error creating key")) // Only the first keystore should have the key keyPath := tufKey.ID() if c.role != data.CanonicalRootRole && cryptoService.gun != "" { keyPath = filepath.Join(cryptoService.gun, keyPath) } _, _, err = cryptoService.keyStores[0].GetKey(keyPath) assert.NoError(t, err, c.errorMsg( "First keystore does not have the key %s", keyPath)) _, _, err = cryptoService.keyStores[1].GetKey(keyPath) assert.Error(t, err, c.errorMsg( "Second keystore has the key %s", keyPath)) // GetKey works across multiple keystores retrievedKey := cryptoService.GetKey(tufKey.ID()) assert.NotNil(t, retrievedKey, c.errorMsg("Could not find key ID %s", tufKey.ID())) }
func testCryptoService(t *testing.T, gun string) { getTestingCryptoService := func() *CryptoService { return NewCryptoService( gun, trustmanager.NewKeyMemoryStore(passphraseRetriever)) } roles := []string{ data.CanonicalRootRole, data.CanonicalTargetsRole, data.CanonicalSnapshotRole, data.CanonicalTimestampRole, } for _, role := range roles { for algo := range algoToSigType { cst := CryptoServiceTester{ cryptoServiceFactory: getTestingCryptoService, role: role, keyAlgo: algo, } cst.TestCreateAndGetKey(t) cst.TestCreateAndGetWhenMultipleKeystores(t) cst.TestGetNonexistentKey(t) cst.TestSignWithKey(t) cst.TestSignNoMatchingKeys(t) cst.TestSignWhenMultipleKeystores(t) cst.TestRemoveCreatedKey(t) cst.TestRemoveFromMultipleKeystores(t) cst.TestListFromMultipleKeystores(t) } } }
// Importing a key not as root fails, and it is not added to the backup store func TestYubiImportNonRootKey(t *testing.T) { if !YubikeyAccessible() { t.Skip("Must have Yubikey access.") } clearAllKeys(t) SetYubikeyKeyMode(KeymodeNone) defer func() { SetYubikeyKeyMode(KeymodeTouch | KeymodePinOnce) }() backup := trustmanager.NewKeyMemoryStore(ret) store, err := NewYubiKeyStore(backup, ret) assert.NoError(t, err) // generate key and import it privKey, err := trustmanager.GenerateECDSAKey(rand.Reader) assert.NoError(t, err) pemBytes, err := trustmanager.EncryptPrivateKey(privKey, "passphrase") assert.NoError(t, err) err = store.ImportKey(pemBytes, privKey.ID()) assert.Error(t, err) // key is not in backup store _, _, err = backup.GetKey(privKey.ID()) assert.Error(t, err) }
func init() { pr = func(string, string, bool, int) (string, bool, error) { return "passphrase", false, nil } keyStore := trustmanager.NewKeyMemoryStore(pr) cryptoService := cryptoservice.NewCryptoService("", keyStore) cryptoServices := signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService} void = &pb.Void{} fakeHealth := func() map[string]string { return health } //server setup kms := &api.KeyManagementServer{CryptoServices: cryptoServices, HealthChecker: fakeHealth} ss := &api.SignerServer{CryptoServices: cryptoServices, HealthChecker: fakeHealth} grpcServer = grpc.NewServer() pb.RegisterKeyManagementServer(grpcServer, kms) pb.RegisterSignerServer(grpcServer, ss) lis, err := net.Listen("tcp", "127.0.0.1:7899") if err != nil { log.Fatalf("failed to listen %v", err) } go grpcServer.Serve(lis) //client setup conn, err := grpc.Dial("127.0.0.1:7899", grpc.WithInsecure()) if err != nil { log.Fatalf("fail to dial: %v", err) } kmClient = pb.NewKeyManagementClient(conn) sClient = pb.NewSignerClient(conn) }
func testCryptoService(t *testing.T, keyAlgo data.KeyAlgorithm, verifier signed.Verifier) { content := []byte("this is a secret") keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cryptoService := NewCryptoService("", keyStore) // Test Create tufKey, err := cryptoService.Create("", keyAlgo) assert.NoError(t, err, "error creating key") // Test Sign signatures, err := cryptoService.Sign([]string{tufKey.ID()}, content) assert.NoError(t, err, "signing failed") assert.Len(t, signatures, 1, "wrong number of signatures") err = verifier.Verify(tufKey, signatures[0].Signature, content) assert.NoError(t, err, "verification failed") // Test GetKey retrievedKey := cryptoService.GetKey(tufKey.ID()) assert.Equal(t, tufKey.Public(), retrievedKey.Public(), "retrieved key didn't match") assert.Nil(t, cryptoService.GetKey("boguskeyid"), "non-nil result for bogus keyid") // Test RemoveKey err = cryptoService.RemoveKey(tufKey.ID()) assert.NoError(t, err, "could not remove key") retrievedKey = cryptoService.GetKey(tufKey.ID()) assert.Nil(t, retrievedKey, "remove didn't work") }
// 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) { nos := []string{"no", "NO", "AAAARGH", " N "} store := trustmanager.NewKeyMemoryStore(ret) key, err := trustmanager.GenerateED25519Key(rand.Reader) assert.NoError(t, err) err = store.AddKey(key.ID(), "root", key) assert.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) assert.NoError(t, err) text, err := ioutil.ReadAll(&out) assert.NoError(t, err) output := string(text) assert.Contains(t, output, "Are you sure") assert.Contains(t, output, "Aborting action") assert.Len(t, store.ListKeys(), 1) } }
// 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) { yesses := []string{"yes", " Y ", "yE", " ", ""} for _, yesAnswer := range yesses { store := trustmanager.NewKeyMemoryStore(ret) key, err := trustmanager.GenerateED25519Key(rand.Reader) assert.NoError(t, err) err = store.AddKey(key.ID(), "root", key) assert.NoError(t, err) var out bytes.Buffer in := bytes.NewBuffer([]byte(yesAnswer + "\n")) err = removeKeyInteractively( []trustmanager.KeyStore{store}, key.ID(), in, &out) assert.NoError(t, err) text, err := ioutil.ReadAll(&out) assert.NoError(t, err) output := string(text) assert.Contains(t, output, "Are you sure") assert.Contains(t, output, "Deleted "+key.ID()) assert.Len(t, store.ListKeys(), 0) } }
func TestSoftwareSignHandler(t *testing.T) { keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cryptoService := cryptoservice.NewCryptoService("", keyStore) setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService}) tufKey, err := cryptoService.Create("", data.ED25519Key) assert.Nil(t, err) sigRequest := &pb.SignatureRequest{KeyID: &pb.KeyID{ID: tufKey.ID()}, Content: make([]byte, 10)} requestJson, _ := json.Marshal(sigRequest) reader = strings.NewReader(string(requestJson)) request, err := http.NewRequest("POST", signBaseURL, reader) assert.Nil(t, err) res, err := http.DefaultClient.Do(request) assert.Nil(t, err) assert.Equal(t, 200, res.StatusCode) jsonBlob, err := ioutil.ReadAll(res.Body) assert.Nil(t, err) var sig *pb.Signature err = json.Unmarshal(jsonBlob, &sig) assert.Nil(t, err) assert.Equal(t, tufKey.ID(), sig.KeyInfo.KeyID.ID) }
// If there are no keys, removeKeyInteractively will just return an error about // there not being any key func TestRemoveIfNoKey(t *testing.T) { var buf bytes.Buffer stores := []trustmanager.KeyStore{trustmanager.NewKeyMemoryStore(nil)} err := removeKeyInteractively(stores, "12345", &buf, &buf) assert.Error(t, err) assert.Contains(t, err.Error(), "No key with ID") }
func TestKeyInfoHandler(t *testing.T) { keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cryptoService := cryptoservice.NewCryptoService("", keyStore) setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService}) tufKey, _ := cryptoService.Create("", data.ED25519Key) assert.NotNil(t, tufKey) keyInfoURL := fmt.Sprintf("%s/%s", keyInfoBaseURL, tufKey.ID()) request, err := http.NewRequest("GET", keyInfoURL, nil) assert.Nil(t, err) res, err := http.DefaultClient.Do(request) assert.Nil(t, err) jsonBlob, err := ioutil.ReadAll(res.Body) assert.Nil(t, err) var pubKey *pb.PublicKey err = json.Unmarshal(jsonBlob, &pubKey) assert.Nil(t, err) assert.Equal(t, tufKey.ID(), pubKey.KeyInfo.KeyID.ID) assert.Equal(t, 200, res.StatusCode) }
// Get a YubiPrivateKey. Check that it has the right algorithm, etc, and // specifically that you cannot get the private bytes out. Assume we can // sign something. func TestYubiKeyAndSign(t *testing.T) { if !IsAccessible() { t.Skip("Must have Yubikey access.") } clearAllKeys(t) SetYubikeyKeyMode(KeymodeNone) defer func() { SetYubikeyKeyMode(KeymodeTouch | KeymodePinOnce) }() store, err := NewYubiStore(trustmanager.NewKeyMemoryStore(ret), ret) require.NoError(t, err) ecdsaPrivateKey, err := testAddKey(t, store) require.NoError(t, err) yubiPrivateKey, _, err := store.GetKey(ecdsaPrivateKey.ID()) require.NoError(t, err) require.Equal(t, data.ECDSAKey, yubiPrivateKey.Algorithm()) require.Equal(t, data.ECDSASignature, yubiPrivateKey.SignatureAlgorithm()) require.Equal(t, ecdsaPrivateKey.Public(), yubiPrivateKey.Public()) require.Nil(t, yubiPrivateKey.Private()) // The signature should be verified, but the importing the verifiers causes // an import cycle. A bigger refactor needs to be done to fix it. msg := []byte("Hello there") _, err = yubiPrivateKey.Sign(rand.Reader, msg, nil) require.NoError(t, err) }
func TestYubiGetKeyCleansUpOnError(t *testing.T) { if !IsAccessible() { t.Skip("Must have Yubikey access.") } clearAllKeys(t) SetYubikeyKeyMode(KeymodeNone) defer func() { SetYubikeyKeyMode(KeymodeTouch | KeymodePinOnce) }() store, err := NewYubiStore(trustmanager.NewKeyMemoryStore(ret), ret) require.NoError(t, err) key, err := testAddKey(t, store) require.NoError(t, err) var _getkey = func() error { _, _, err := store.GetKey(key.ID()) return err } // all the PKCS11 functions GetKey depends on testYubiFunctionCleansUpOnSpecifiedErrors(t, store, _getkey, append( setupErrors, "FindObjectsInit", "FindObjects", "FindObjectsFinal", "GetAttributeValue", ), true) }
func TestYubiImportKeyCleansUpOnError(t *testing.T) { if !YubikeyAccessible() { t.Skip("Must have Yubikey access.") } clearAllKeys(t) SetYubikeyKeyMode(KeymodeNone) defer func() { SetYubikeyKeyMode(KeymodeTouch | KeymodePinOnce) }() store, err := NewYubiKeyStore(trustmanager.NewKeyMemoryStore(ret), ret) assert.NoError(t, err) privKey, err := trustmanager.GenerateECDSAKey(rand.Reader) assert.NoError(t, err) pemBytes, err := trustmanager.EncryptPrivateKey(privKey, "passphrase") assert.NoError(t, err) var _importkey = func() error { return store.ImportKey(pemBytes, "root") } testYubiFunctionCleansUpOnLoginError(t, store, _importkey) // all the PKCS11 functions ImportKey depends on that aren't the login/logout testYubiFunctionCleansUpOnSpecifiedErrors(t, store, _importkey, append( setupErrors, "FindObjectsInit", "FindObjects", "FindObjectsFinal", "CreateObject", ), true) // given that everything should have errored, there should be no keys on // the yubikey assert.Len(t, cleanListKeys(t), 0) // Logout should not cause a function failure - it s a cleanup failure, // which shouldn't break anything, and it should clean up after itself. // The key should be added to both stores testYubiFunctionCleansUpOnSpecifiedErrors(t, store, _importkey, []string{"Logout"}, false) listedKeys := cleanListKeys(t) assert.Len(t, listedKeys, 1) // Currently, if GetAttributeValue fails, the function succeeds, because if // we can't get the attribute value of an object, we don't know what slot // it's in, we assume its occupied slot is free (hence this failure will // cause the previous key to be overwritten). This behavior may need to // be revisited. for k := range listedKeys { err := store.RemoveKey(k) assert.NoError(t, err) } testYubiFunctionCleansUpOnSpecifiedErrors(t, store, _importkey, []string{"GetAttributeValue"}, false) assert.Len(t, cleanListKeys(t), 1) }
// 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{KeyMemoryStore: *trustmanager.NewKeyMemoryStore(ret)}, } longNameShortened := "..." + strings.Repeat("z", 37) keys := make([]data.PrivateKey, 4) for i := 0; i < 4; i++ { key, err := trustmanager.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)) } } }
// Signer conforms to the signed.CryptoService interface behavior func TestCryptoSignerInterfaceBehavior(t *testing.T) { signer := setUpSigner(t, trustmanager.NewKeyMemoryStore(ret)) interfaces.EmptyCryptoServiceInterfaceBehaviorTests(t, &signer) interfaces.CreateGetKeyCryptoServiceInterfaceBehaviorTests(t, &signer, data.ECDSAKey, false) // can't test AddKey, because the signer does not support adding keys, and can't test listing // keys because the signer doesn't support listing keys. Signer also doesn't support tracking // roles }
// 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 := trustmanager.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 }
// 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 := trustmanager.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) } } } }
// create a new store for clearing out keys, because we don't want to pollute // any cache func clearAllKeys(t *testing.T) { store, err := NewYubiStore(trustmanager.NewKeyMemoryStore(ret), ret) require.NoError(t, err) for k := range store.ListKeys() { err := store.RemoveKey(k) require.NoError(t, err) } }
func TestSignRootOldKeyCertMissing(t *testing.T) { gun := "docker/test-sign-root" referenceTime := time.Now() cs := cryptoservice.NewCryptoService(trustmanager.NewKeyMemoryStore( passphrase.ConstantRetriever("password"))) rootPublicKey, err := cs.Create(data.CanonicalRootRole, gun, data.ECDSAKey) require.NoError(t, err) rootPrivateKey, _, err := cs.GetPrivateKey(rootPublicKey.ID()) require.NoError(t, err) oldRootCert, err := cryptoservice.GenerateCertificate(rootPrivateKey, gun, referenceTime.AddDate(-9, 0, 0), referenceTime.AddDate(1, 0, 0)) require.NoError(t, err) oldRootCertKey := trustmanager.CertToKey(oldRootCert) repo := initRepoWithRoot(t, cs, oldRootCertKey) // Create a first signature, using the old key. signedRoot, err := repo.SignRoot(data.DefaultExpires(data.CanonicalRootRole)) require.NoError(t, err) verifySignatureList(t, signedRoot, oldRootCertKey) err = verifyRootSignatureAgainstKey(t, signedRoot, oldRootCertKey) require.NoError(t, err) // Create a new certificate newRootCert, err := cryptoservice.GenerateCertificate(rootPrivateKey, gun, referenceTime, referenceTime.AddDate(10, 0, 0)) require.NoError(t, err) newRootCertKey := trustmanager.CertToKey(newRootCert) require.NotEqual(t, oldRootCertKey.ID(), newRootCertKey.ID()) // Only trust the new certificate err = repo.ReplaceBaseKeys(data.CanonicalRootRole, newRootCertKey) require.NoError(t, err) updatedRootRole, err := repo.GetBaseRole(data.CanonicalRootRole) require.NoError(t, err) updatedRootKeyIDs := updatedRootRole.ListKeyIDs() require.Equal(t, 1, len(updatedRootKeyIDs)) require.Equal(t, newRootCertKey.ID(), updatedRootKeyIDs[0]) // Now forget all about the old certificate: drop it from the Root carried keys delete(repo.Root.Signed.Keys, oldRootCertKey.ID()) repo2 := NewRepo(cs) repo2.Root = repo.Root repo2.originalRootRole = updatedRootRole // Create a second signature signedRoot, err = repo2.SignRoot(data.DefaultExpires(data.CanonicalRootRole)) require.NoError(t, err) verifySignatureList(t, signedRoot, newRootCertKey) // Without oldRootCertKey // Verify that the signature can be verified when trusting the new certificate err = verifyRootSignatureAgainstKey(t, signedRoot, newRootCertKey) require.NoError(t, err) err = verifyRootSignatureAgainstKey(t, signedRoot, oldRootCertKey) require.Error(t, err) }
// 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{KeyMemoryStore: *trustmanager.NewKeyMemoryStore(ret)}, } longNameShortened := "..." + strings.Repeat("z", 37) // just use the same key for testing key, err := trustmanager.GenerateED25519Key(rand.Reader) assert.NoError(t, err) root := data.CanonicalRootRole // add keys to the key stores err = keyStores[0].AddKey(key.ID(), root, key) assert.NoError(t, err) err = keyStores[1].AddKey(key.ID(), root, key) assert.NoError(t, err) err = keyStores[0].AddKey(strings.Repeat("a/", 30)+key.ID(), "targets", key) assert.NoError(t, err) err = keyStores[1].AddKey("short/gun/"+key.ID(), "snapshot", key) assert.NoError(t, err) expected := [][]string{ {root, key.ID(), keyStores[0].Name()}, {root, key.ID(), longNameShortened}, {"targets", "..." + strings.Repeat("/a", 11), key.ID(), keyStores[0].Name()}, {"snapshot", "short/gun", key.ID(), longNameShortened}, } var b bytes.Buffer prettyPrintKeys(keyStores, &b) text, err := ioutil.ReadAll(&b) assert.NoError(t, err) lines := strings.Split(strings.TrimSpace(string(text)), "\n") assert.Len(t, lines, len(expected)+2) // starts with headers assert.True(t, reflect.DeepEqual(strings.Fields(lines[0]), []string{"ROLE", "GUN", "KEY", "ID", "LOCATION"})) assert.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 { assert.Equal(t, expected[i][j], strings.TrimSpace(v)) } } }