Example #1
0
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)
}
Example #2
0
// 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)
}
Example #3
0
// 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)
}
Example #4
0
// Tests import/export root key only
func TestClientKeyImportExportRootOnly(t *testing.T) {
	// -- setup --
	cleanup := setUp(t)
	defer cleanup()

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

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

	var (
		target    = "sdgkadga"
		rootKeyID string
	)

	tempFile, err := ioutil.TempFile("/tmp", "pemfile")
	assert.NoError(t, err)
	// close later, because we might need to write to it
	defer os.Remove(tempFile.Name())

	// -- tests --

	if rootOnHardware() {
		t.Log("Cannot export a key from hardware. Will generate one to import.")

		privKey, err := trustmanager.GenerateECDSAKey(rand.Reader)
		assert.NoError(t, err)

		pemBytes, err := trustmanager.EncryptPrivateKey(privKey, "root", testPassphrase)
		assert.NoError(t, err)

		nBytes, err := tempFile.Write(pemBytes)
		assert.NoError(t, err)
		tempFile.Close()
		assert.Equal(t, len(pemBytes), nBytes)
		rootKeyID = privKey.ID()
	} else {
		tempFile.Close()
		rootKeyID = exportRoot(t, tempFile.Name())
	}

	// import the key
	_, err = runCommand(t, tempDir, "key", "import", tempFile.Name())
	assert.NoError(t, err)

	// if there is hardware available, root will only be on hardware, and not
	// on disk
	newRoot, _ := assertNumKeys(t, tempDir, 1, 0, !rootOnHardware())
	assert.Equal(t, rootKeyID, newRoot[0])

	// Just to make sure, init a repo and publish
	_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun")
	assert.NoError(t, err)
	assertNumKeys(t, tempDir, 1, 2, !rootOnHardware())
	assertSuccessfullyPublish(
		t, tempDir, server.URL, "gun", target, tempFile.Name())
}
Example #5
0
// ImportKey imports a key as root without adding it to the backup store
func TestYubiImportNewKey(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, "root")
	assert.NoError(t, err)

	// key is not in backup store
	_, _, err = backup.GetKey(privKey.ID())
	assert.Error(t, err)

	// create a new store, since we want to be sure the original store's cache
	// is not masking any issues
	cleanStore, err := NewYubiKeyStore(trustmanager.NewKeyMemoryStore(ret), ret)
	assert.NoError(t, err)
	for _, store := range []*YubiKeyStore{store, cleanStore} {
		gottenKey, role, err := store.GetKey(privKey.ID())
		assert.NoError(t, err)
		assert.Equal(t, data.CanonicalRootRole, role)
		assert.Equal(t, privKey.Public(), gottenKey.Public())
	}
}
// Tests importing and exporting keys for all different roles and GUNs
func TestClientKeyImportExportAllRoles(t *testing.T) {
	if rootOnHardware() {
		t.Log("Cannot import or export a non-root key from hardware. Will skip test.")
		return
	}
	// -- setup --
	setUp(t)

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

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

	// -- tests --
	_, err := runCommand(t, tempDir, "-s", server.URL, "init", "gun")
	assert.NoError(t, err)

	testRoles := append(data.BaseRoles, "targets/releases")
	// Test importing and exporting keys to all base roles and delegation role
	for _, role := range testRoles {
		// Do this while importing keys that have the PEM header role set or have --role set on import
		for _, setKeyRole := range []bool{true, false} {
			// Make a new key for this role
			privKey, err := trustmanager.GenerateECDSAKey(rand.Reader)
			assert.NoError(t, err)

			// Make a tempfile for importing
			tempFile, err := ioutil.TempFile("", "pemfile")
			assert.NoError(t, err)

			// Specify the role in the PEM header
			pemBytes, err := trustmanager.EncryptPrivateKey(privKey, role, testPassphrase)
			assert.NoError(t, err)
			ioutil.WriteFile(tempFile.Name(), pemBytes, 0644)

			// If we need to set the key role with the --role flag, do so on import
			if setKeyRole {
				// If it's targets/snapshot we must specify the GUN
				if role == data.CanonicalTargetsRole || role == data.CanonicalSnapshotRole {
					_, err = runCommand(t, tempDir, "key", "import", tempFile.Name(), "--gun", "gun", "--role", role)
				} else {
					_, err = runCommand(t, tempDir, "key", "import", tempFile.Name(), "--role", role)
				}
			} else {
				// If it's targets/snapshot we must specify the GUN
				if role == data.CanonicalTargetsRole || role == data.CanonicalSnapshotRole {
					_, err = runCommand(t, tempDir, "key", "import", tempFile.Name(), "--gun", "gun")
				} else {
					_, err = runCommand(t, tempDir, "key", "import", tempFile.Name())
				}
			}
			assert.NoError(t, err)

			// Test that we imported correctly
			keySubdir := getKeySubdir(role, "gun")
			_, err = os.Stat(filepath.Join(tempDir, keySubdir, privKey.ID()+".key"))
			assert.Nil(t, err)

			// Remove the input file so we can test exporting
			assert.NoError(t, os.Remove(tempFile.Name()))

			// Make a tempfile for exporting to
			tempFile, err = ioutil.TempFile("", "pemfile")
			assert.NoError(t, err)

			// Ensure exporting this key by ID gets the same key
			_, err = runCommand(t, tempDir, "key", "export", privKey.ID(), tempFile.Name())
			assert.NoError(t, err)
			// Compare the bytes of the exported file and the root key file in the repo
			exportedBytes, err := ioutil.ReadFile(tempFile.Name())
			assert.NoError(t, err)
			repoBytes, err := ioutil.ReadFile(filepath.Join(tempDir, keySubdir, privKey.ID()+".key"))
			assert.NoError(t, err)
			assert.Equal(t, repoBytes, exportedBytes)

			// Ensure exporting this key and changing the passphrase works
			_, err = runCommand(t, tempDir, "key", "export", privKey.ID(), tempFile.Name(), "-p")
			assert.NoError(t, err)

			// Remove the export file for cleanup
			assert.NoError(t, os.Remove(tempFile.Name()))
		}
	}
}
Example #7
0
// Tests import/export root key only
func TestClientKeyImportExportRootOnly(t *testing.T) {
	// -- setup --
	setUp(t)

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

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

	var (
		target    = "sdgkadga"
		rootKeyID string
	)

	tempFile, err := ioutil.TempFile("", "pemfile")
	require.NoError(t, err)
	// close later, because we might need to write to it
	defer os.Remove(tempFile.Name())

	// -- tests --

	if rootOnHardware() {
		t.Log("Cannot export a key from hardware. Will generate one to import.")

		privKey, err := trustmanager.GenerateECDSAKey(rand.Reader)
		require.NoError(t, err)

		pemBytes, err := trustmanager.EncryptPrivateKey(privKey, "root", testPassphrase)
		require.NoError(t, err)

		nBytes, err := tempFile.Write(pemBytes)
		require.NoError(t, err)
		tempFile.Close()
		require.Equal(t, len(pemBytes), nBytes)
		rootKeyID = privKey.ID()
	} else {
		tempFile.Close()
		rootKeyID = exportRoot(t, tempFile.Name())
	}

	// import the key
	_, err = runCommand(t, tempDir, "key", "import", tempFile.Name())
	require.NoError(t, err)

	// if there is hardware available, root will only be on hardware, and not
	// on disk
	newRoot, _ := assertNumKeys(t, tempDir, 1, 0, !rootOnHardware())
	require.Equal(t, rootKeyID, newRoot[0])

	// Just to make sure, init a repo and publish
	_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun")
	require.NoError(t, err)
	assertNumKeys(t, tempDir, 1, 2, !rootOnHardware())
	assertSuccessfullyPublish(
		t, tempDir, server.URL, "gun", target, tempFile.Name())

	// Now assert that bad root keys give an error
	// Try importing an unencrypted root key:
	privKey, err := trustmanager.GenerateECDSAKey(rand.Reader)
	require.NoError(t, err)
	decryptedPEMBytes, err := trustmanager.KeyToPEM(privKey, data.CanonicalRootRole)
	decryptedKeyFile, err := ioutil.TempFile("", "decryptedPem")
	require.NoError(t, err)
	// close later, because we might need to write to it
	defer os.Remove(decryptedKeyFile.Name())

	nBytes, err := decryptedKeyFile.Write(decryptedPEMBytes)
	require.NoError(t, err)
	decryptedKeyFile.Close()
	require.Equal(t, len(decryptedPEMBytes), nBytes)
	// import the key
	_, err = runCommand(t, tempDir, "key", "import", decryptedKeyFile.Name())
	require.Error(t, err)

	// Now try importing an invalid PEM as a root key
	invalidPEMBytes := []byte("this is not PEM")
	invalidPEMFile, err := ioutil.TempFile("", "invalidPem")
	require.NoError(t, err)
	// close later, because we might need to write to it
	defer os.Remove(invalidPEMFile.Name())

	nBytes, err = invalidPEMFile.Write(invalidPEMBytes)
	require.NoError(t, err)
	invalidPEMFile.Close()
	require.Equal(t, len(invalidPEMBytes), nBytes)
	// import the key
	_, err = runCommand(t, tempDir, "key", "import", invalidPEMFile.Name())
	require.Error(t, err)
}