static GBytes *
gkm_gnome2_private_key_real_save (GkmSerializable *base, GkmSecret *login)
{
	GkmGnome2PrivateKey *self = GKM_GNOME2_PRIVATE_KEY (base);
	const gchar *password = NULL;
	gsize n_password;
	GkmSexp *sexp;
	GBytes *result;

	g_return_val_if_fail (GKM_IS_GNOME2_PRIVATE_KEY (self), FALSE);

	sexp = gkm_gnome2_private_key_real_acquire_crypto_sexp (GKM_SEXP_KEY (self), NULL);
	g_return_val_if_fail (sexp, FALSE);

	if (login != NULL)
		password = gkm_secret_get_password (login, &n_password);
	if (password == NULL) {
		result = gkm_data_der_write_private_pkcs8_plain (gkm_sexp_get (sexp));
	} else {
		result = gkm_data_der_write_private_pkcs8_crypted (gkm_sexp_get (sexp), password,
		                                                   n_password);
	}

	gkm_sexp_unref (sexp);
	return result;
}
static gboolean
gkm_mate2_private_key_real_save (GkmSerializable *base, GkmSecret *login, gpointer *data, gsize *n_data)
{
	GkmMate2PrivateKey *self = GKM_MATE2_PRIVATE_KEY (base);
	const gchar *password;
	gsize n_password;
	GkmSexp *sexp;
	guchar *key;

	g_return_val_if_fail (GKM_IS_MATE2_PRIVATE_KEY (self), FALSE);
	g_return_val_if_fail (data, FALSE);
	g_return_val_if_fail (n_data, FALSE);

	sexp = gkm_mate2_private_key_real_acquire_crypto_sexp (GKM_SEXP_KEY (self), NULL);
	g_return_val_if_fail (sexp, FALSE);

	password = gkm_secret_get_password (login, &n_password);
	if (password == NULL) {
		key = gkm_data_der_write_private_pkcs8_plain (gkm_sexp_get (sexp), n_data);

		/*
		 * Caller is expecting normal memory buffer, which makes sense since
		 * this is being written to disk, and won't be 'secure' anyway.
		 */
		*data = g_memdup (key, *n_data);
		egg_secure_free (key);
	} else {
		*data = gkm_data_der_write_private_pkcs8_crypted (gkm_sexp_get (sexp), password,
		                                                  n_password, n_data);
	}

	gkm_sexp_unref (sexp);
	return *data != NULL;
}
static gboolean
decrypt_buffer (EggBuffer *buffer, GkmSecret *master,
		guchar salt[8], int iterations)
{
	const gchar *password = NULL;
	gcry_cipher_hd_t cih;
	gcry_error_t gerr;
        guchar *key, *iv;
        gsize n_password = 0;
	size_t pos;

	g_assert (buffer->len % 16 == 0);
	g_assert (16 == gcry_cipher_get_algo_blklen (GCRY_CIPHER_AES128));
	g_assert (16 == gcry_cipher_get_algo_keylen (GCRY_CIPHER_AES128));

	/* No password is set, try an null password */
	if (master == NULL) {
		password = NULL;
		n_password = 0;
	} else {
		password = gkm_secret_get_password (master, &n_password);
	}

	if (!egg_symkey_generate_simple (GCRY_CIPHER_AES128, GCRY_MD_SHA256,
	                                 password, n_password, salt, 8, iterations, &key, &iv))
		return FALSE;

	gerr = gcry_cipher_open (&cih, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0);
	if (gerr) {
		g_warning ("couldn't create aes cipher context: %s",
			   gcry_strerror (gerr));
		egg_secure_free (key);
		g_free (iv);
		return FALSE;
	}

	/* 16 = 128 bits */
	gerr = gcry_cipher_setkey (cih, key, 16);
	g_return_val_if_fail (!gerr, FALSE);
	egg_secure_free (key);

	/* 16 = 128 bits */
	gerr = gcry_cipher_setiv (cih, iv, 16);
	g_return_val_if_fail (!gerr, FALSE);
	g_free (iv);

	for (pos = 0; pos < buffer->len; pos += 16) {
		/* In place encryption */
		gerr = gcry_cipher_decrypt (cih, buffer->buf + pos, 16, NULL, 0);
		g_return_val_if_fail (!gerr, FALSE);
	}

	gcry_cipher_close (cih);

	return TRUE;
}
const gchar*
gkm_credential_get_password (GkmCredential *self, gsize *n_password)
{
	g_return_val_if_fail (GKM_IS_CREDENTIAL (self), NULL);
	g_return_val_if_fail (n_password, NULL);

	if (!self->pv->secret) {
		*n_password = 0;
		return NULL;
	}

	return gkm_secret_get_password (self->pv->secret, n_password);
}
static GkmSexp*
gkm_gnome2_private_key_real_acquire_crypto_sexp (GkmSexpKey *base, GkmSession *unused)
{
	GkmGnome2PrivateKey *self = GKM_GNOME2_PRIVATE_KEY (base);
	gcry_sexp_t sexp;
	GkmDataResult res;
	const gchar *password;
	gsize n_password;

	/* Non encrypted case */
	if (self->private_sexp)
		return gkm_sexp_ref (self->private_sexp);

	g_return_val_if_fail (self->login, NULL);
	g_return_val_if_fail (self->is_encrypted, NULL);

	password = gkm_secret_get_password (self->login, &n_password);
	res = gkm_data_der_read_private_pkcs8 (self->private_bytes, password, n_password, &sexp);
	g_return_val_if_fail (res == GKM_DATA_SUCCESS, NULL);

	return gkm_sexp_new (sexp);
}
static gboolean
gkm_gnome2_private_key_real_load (GkmSerializable *base,
                                  GkmSecret *login,
                                  GBytes *data)
{
	GkmGnome2PrivateKey *self = GKM_GNOME2_PRIVATE_KEY (base);
	GkmDataResult res;
	gcry_sexp_t sexp, pub;
	GkmSexp *wrapper;
	const gchar *password;
	gsize n_password;

	if (g_bytes_get_size (data) == 0)
		return FALSE;

	res = gkm_data_der_read_private_pkcs8 (data, NULL, 0, &sexp);

	/* An unencrypted pkcs8 file */
	if (res == GKM_DATA_SUCCESS) {
		self->is_encrypted = FALSE;

	/* If it's locked, then use our token password */
	} else if (res == GKM_DATA_LOCKED) {
		self->is_encrypted = TRUE;

		if (!login) {
			g_message ("encountered private key but no private key present");
			return FALSE;
		}

		password = gkm_secret_get_password (login, &n_password);
		res = gkm_data_der_read_private_pkcs8 (data, password, n_password, &sexp);
	}

	switch (res) {
	case GKM_DATA_LOCKED:
		g_message ("private key is encrypted with wrong password");
		return FALSE;
	case GKM_DATA_FAILURE:
		g_message ("couldn't parse private key");
		return FALSE;
	case GKM_DATA_UNRECOGNIZED:
		g_message ("invalid or unrecognized private key");
		return FALSE;
	case GKM_DATA_SUCCESS:
		break;
	default:
		g_assert_not_reached();
	}

	/* Calculate a public key as our 'base' */
	if (!gkm_sexp_key_to_public (sexp, &pub))
		g_return_val_if_reached (FALSE);

	/* Keep the public part of the key around for answering queries */
	wrapper = gkm_sexp_new (pub);
	gkm_sexp_key_set_base (GKM_SEXP_KEY (self), wrapper);
	gkm_sexp_unref (wrapper);

	/* Encrypted private key, keep login and data */
	if (self->is_encrypted) {
		if (self->private_bytes)
			g_bytes_unref (self->private_bytes);
		self->private_bytes = g_bytes_ref (data);

		g_object_ref (login);
		if (self->login)
			g_object_unref (self->login);
		self->login = login;

		/* Don't need the private key any more */
		gcry_sexp_release (sexp);

	/* Not encrypted, just keep the parsed key */
	} else {
		wrapper = gkm_sexp_new (sexp);
		if (self->private_sexp)
			gkm_sexp_unref (self->private_sexp);
		self->private_sexp = wrapper;

		if (self->login)
			g_object_unref (login);
		self->login = NULL;
	}

	return TRUE;
}