# HG changeset patch
# Node ID d5989d03fc81
# Parent  b9890765178e
diff --git a/libpurple/plugins/keyrings/internalkeyring.c b/libpurple/plugins/keyrings/internalkeyring.c
deleted file mode 100644
--- a/libpurple/plugins/keyrings/internalkeyring.c
+++ /dev/null
@@ -1,1051 +0,0 @@
-/**
- * @file internalkeyring.c internal keyring
- * @ingroup plugins
- */
-
-/* purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program ; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#include <glib/gi18n-lib.h>
-
-#include <purple.h>
-
-#include <nettle/aes.h>
-#include <nettle/cbc.h>
-#include <nettle/pbkdf2.h>
-
-#define INTKEYRING_NAME N_("Internal keyring")
-#define INTKEYRING_DESCRIPTION N_("This plugin provides the default password " \
-	"storage behaviour for libpurple.")
-#define INTKEYRING_AUTHORS { "Tomek Wasilczyk <twasilczyk@pidgin.im>",NULL }
-#define INTKEYRING_ID PURPLE_DEFAULT_KEYRING
-#define INTKEYRING_DOMAIN (g_quark_from_static_string(INTKEYRING_ID))
-
-#define INTKEYRING_VERIFY_STR "[verification-string]"
-#define INTKEYRING_PBKDF2_ITERATIONS 10000
-#define INTKEYRING_PBKDF2_ITERATIONS_MIN 1000
-#define INTKEYRING_PBKDF2_ITERATIONS_MAX 1000000000
-#define INTKEYRING_KEY_LEN AES256_KEY_SIZE
-#define INTKEYRING_ENCRYPT_BUFF_LEN 1000
-#define INTKEYRING_ENCRYPTED_MIN_LEN 50
-#define INTKEYRING_ENCRYPTION_METHOD "pbkdf2-sha256-aes256"
-
-#define INTKEYRING_PREFS "/plugins/keyrings/internal/"
-
-/* win32 build defines such macro to override read() routine */
-#undef read
-
-typedef struct
-{
-	enum
-	{
-		INTKEYRING_REQUEST_READ,
-		INTKEYRING_REQUEST_SAVE
-	} type;
-	PurpleAccount *account;
-	gchar *password;
-	union
-	{
-		PurpleKeyringReadCallback read;
-		PurpleKeyringSaveCallback save;
-	} cb;
-	gpointer cb_data;
-} intkeyring_request;
-
-typedef struct
-{
-	guchar *data;
-	size_t len;
-} intkeyring_buff_t;
-
-static intkeyring_buff_t *intkeyring_key;
-static GHashTable *intkeyring_passwords = NULL;
-static GHashTable *intkeyring_ciphertexts = NULL;
-
-static gboolean intkeyring_opened = FALSE;
-static gboolean intkeyring_unlocked = FALSE;
-
-static GList *intkeyring_pending_requests = NULL;
-static void *intkeyring_masterpw_uirequest = NULL;
-
-static PurpleKeyring *keyring_handler = NULL;
-
-static void
-intkeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
-	gpointer data);
-static void
-intkeyring_save(PurpleAccount *account, const gchar *password,
-	PurpleKeyringSaveCallback cb, gpointer data);
-static void
-intkeyring_reencrypt_passwords(void);
-static void
-intkeyring_unlock(const gchar *message);
-
-static void
-intkeyring_request_free(intkeyring_request *req)
-{
-	g_return_if_fail(req != NULL);
-
-	purple_str_wipe(req->password);
-	g_free(req);
-}
-
-static intkeyring_buff_t *
-intkeyring_buff_new(guchar *data, size_t len)
-{
-	intkeyring_buff_t *ret = g_new(intkeyring_buff_t, 1);
-
-	ret->data = data;
-	ret->len = len;
-
-	return ret;
-}
-
-static void
-intkeyring_buff_free(intkeyring_buff_t *buff)
-{
-	if (buff == NULL)
-		return;
-
-	memset(buff->data, 0, buff->len);
-	g_free(buff->data);
-	g_free(buff);
-}
-
-static intkeyring_buff_t *
-intkeyring_buff_from_base64(const gchar *base64)
-{
-	guchar *data;
-	gsize len;
-
-	data = g_base64_decode(base64, &len);
-
-	return intkeyring_buff_new(data, len);
-}
-
-/************************************************************************/
-/* Generic encryption stuff                                             */
-/************************************************************************/
-
-static intkeyring_buff_t *
-intkeyring_derive_key(const gchar *passphrase, intkeyring_buff_t *salt)
-{
-	intkeyring_buff_t *ret;
-
-	g_return_val_if_fail(passphrase != NULL, NULL);
-
-	ret = intkeyring_buff_new(g_new(guchar, INTKEYRING_KEY_LEN),
-		INTKEYRING_KEY_LEN);
-
-	pbkdf2_hmac_sha256(strlen(passphrase), (const uint8_t *)passphrase,
-		purple_prefs_get_int(INTKEYRING_PREFS"pbkdf2_iterations"),
-		salt->len, salt->data, ret->len, ret->data);
-
-	return ret;
-}
-
-static intkeyring_buff_t *
-intkeyring_gen_salt(size_t len)
-{
-	intkeyring_buff_t *ret;
-	size_t filled = 0;
-
-	g_return_val_if_fail(len > 0, NULL);
-
-	ret = intkeyring_buff_new(g_new(guchar, len), len);
-
-	while (filled < len) {
-		guint32 r = g_random_int();
-		int i;
-
-		for (i = 0; i < 4; i++) {
-			ret->data[filled++] = r & 0xFF;
-			if (filled >= len)
-				break;
-			r >>= 8;
-		}
-	}
-
-	return ret;
-}
-
-/**
- * Encrypts a plaintext using the specified key.
- *
- * Random IV will be generated and stored with ciphertext.
- *
- * Encryption scheme:
- * [ IV ] ++ AES( [ plaintext ] ++ [ min length padding ] ++
- *                [ control string ] ++ [ pkcs7 padding ] )
- * where:
- *  IV:                 Random, 128bit IV.
- *  plaintext:          The plaintext.
- *  min length padding: The padding used to hide the rough length of short
- *                      plaintexts, may have a length of 0.
- *  control string:     Constant string, verifies corectness of decryption.
- *  pkcs7 padding:      The padding used to determine total length of encrypted
- *                      content (also provides some verification).
- *
- * @param key The AES key.
- * @param str The NUL-terminated plaintext.
- * @return    The ciphertext with IV, encoded as base64. Must be g_free'd.
- */
-static gchar *
-intkeyring_encrypt(intkeyring_buff_t *key, const gchar *str)
-{
-	struct CBC_CTX(struct aes256_ctx, AES_BLOCK_SIZE) ctx;
-	intkeyring_buff_t *iv;
-	guchar plaintext[INTKEYRING_ENCRYPT_BUFF_LEN];
-	size_t plaintext_len, text_len, verify_len;
-	int padding_len;
-	guchar encrypted_raw[INTKEYRING_ENCRYPT_BUFF_LEN];
-	ssize_t encrypted_size;
-
-	g_return_val_if_fail(key != NULL, NULL);
-	g_return_val_if_fail(str != NULL, NULL);
-
-	text_len = strlen(str);
-	verify_len = strlen(INTKEYRING_VERIFY_STR);
-	plaintext_len = INTKEYRING_ENCRYPTED_MIN_LEN;
-	if (plaintext_len < text_len)
-		plaintext_len = text_len;
-
-	g_return_val_if_fail(plaintext_len + verify_len <= sizeof(plaintext),
-		NULL);
-
-	memset(plaintext, 0, plaintext_len);
-	memcpy(plaintext, str, text_len);
-	memcpy(plaintext + plaintext_len, INTKEYRING_VERIFY_STR, verify_len);
-	plaintext_len += verify_len;
-
-	/* Pad PKCS7 */
-	padding_len = AES_BLOCK_SIZE - (plaintext_len % AES_BLOCK_SIZE);
-
-	if (plaintext_len + padding_len > INTKEYRING_ENCRYPT_BUFF_LEN) {
-		purple_debug_error("keyring-internal",
-			"Internal keyring encrypt buffer too small");
-		return NULL;
-	}
-
-	memset(plaintext + plaintext_len, padding_len, padding_len);
-	plaintext_len += padding_len;
-
-	/* Encrypt */
-	iv = intkeyring_gen_salt(AES_BLOCK_SIZE);
-	g_return_val_if_fail(iv != NULL, NULL);
-	aes256_set_encrypt_key(&ctx.ctx, key->data);
-	CBC_SET_IV(&ctx, iv->data);
-
-	memcpy(encrypted_raw, iv->data, iv->len);
-
-	CBC_ENCRYPT(&ctx, aes256_encrypt, plaintext_len,
-		encrypted_raw + iv->len, plaintext);
-	encrypted_size = plaintext_len;
-	encrypted_size += iv->len;
-
-	memset(plaintext, 0, plaintext_len);
-	intkeyring_buff_free(iv);
-
-	if (encrypted_size < 0)
-		return NULL;
-
-	return g_base64_encode(encrypted_raw, encrypted_size);
-
-}
-
-static gchar *
-intkeyring_decrypt(intkeyring_buff_t *key, const gchar *str)
-{
-	struct CBC_CTX(struct aes256_ctx, AES_BLOCK_SIZE) ctx;
-	guchar *encrypted_raw;
-	gsize encrypted_size;
-	size_t iv_len, verify_len, text_len;
-	guchar plaintext[INTKEYRING_ENCRYPT_BUFF_LEN];
-	const gchar *verify_str = NULL;
-	size_t plaintext_len;
-	guint padding_len;
-	guint i;
-	gchar *ret;
-
-	g_return_val_if_fail(key != NULL, NULL);
-	g_return_val_if_fail(str != NULL, NULL);
-
-	encrypted_raw = g_base64_decode(str, &encrypted_size);
-	g_return_val_if_fail(encrypted_raw != NULL, NULL);
-
-	iv_len = AES_BLOCK_SIZE;
-	if (encrypted_size < iv_len) {
-		g_free(encrypted_raw);
-		return NULL;
-	}
-
-	/* Decrypt */
-	aes256_set_decrypt_key(&ctx.ctx, key->data);
-	CBC_SET_IV(&ctx, encrypted_raw);
-	CBC_DECRYPT(&ctx, aes256_decrypt, encrypted_size - iv_len,
-		plaintext, encrypted_raw + iv_len);
-	plaintext_len = encrypted_size - iv_len;
-	g_free(encrypted_raw);
-
-	/* Unpad PKCS7 */
-	padding_len = plaintext[plaintext_len - 1];
-	if (padding_len == 0 || padding_len > AES_BLOCK_SIZE ||
-			padding_len > plaintext_len) {
-		purple_debug_warning("internal-keyring",
-			"Invalid padding length: %d (total %" G_GSIZE_FORMAT
-			") - most probably, the key was invalid\n",
-			padding_len, plaintext_len);
-		return NULL;
-	}
-
-	plaintext_len -= padding_len;
-	for (i = 0; i < padding_len; ++i) {
-		if (plaintext[plaintext_len + i] != padding_len) {
-			purple_debug_warning("internal-keyring",
-				"Padding doesn't match at pos %d (found %02x, "
-				"expected %02x) - "
-				"most probably, the key was invalid\n",
-				i, plaintext[plaintext_len + i], padding_len);
-			return NULL;
-		}
-	}
-
-	memset(plaintext + plaintext_len, 0, padding_len);
-
-	/* Verify */
-	verify_len = strlen(INTKEYRING_VERIFY_STR);
-	/* Don't remove the len > 0 check! */
-	if (plaintext_len > 0 && (gsize)plaintext_len > verify_len &&
-		plaintext[plaintext_len] == '\0')
-	{
-		verify_str = (gchar*)plaintext + plaintext_len - verify_len;
-	}
-
-	if (g_strcmp0(verify_str, INTKEYRING_VERIFY_STR) != 0) {
-		purple_debug_warning("keyring-internal",
-			"Verification failed on decryption\n");
-		memset(plaintext, 0, sizeof(plaintext));
-		return NULL;
-	}
-	g_assert(plaintext_len > 0);
-
-	text_len = plaintext_len - verify_len;
-	ret = g_new(gchar, text_len + 1);
-	memcpy(ret, plaintext, text_len);
-	memset(plaintext, 0, plaintext_len);
-	ret[text_len] = '\0';
-
-	return ret;
-}
-
-/************************************************************************/
-/* Password encryption                                                  */
-/************************************************************************/
-
-static gboolean
-intkeyring_change_masterpw(const gchar *new_password)
-{
-	intkeyring_buff_t *salt, *key;
-	gchar *verifier = NULL, *salt_b64 = NULL;
-	int old_iter;
-	gboolean succ = TRUE;;
-
-	g_return_val_if_fail(intkeyring_unlocked, FALSE);
-
-	old_iter = purple_prefs_get_int(INTKEYRING_PREFS "pbkdf2_iterations");
-	purple_prefs_set_int(INTKEYRING_PREFS "pbkdf2_iterations",
-		purple_prefs_get_int(INTKEYRING_PREFS
-			"pbkdf2_desired_iterations"));
-
-	salt = intkeyring_gen_salt(32);
-	key = intkeyring_derive_key(new_password, salt);
-
-	if (salt && key && key->len == INTKEYRING_KEY_LEN) {
-		/* In fact, verify str will be concatenated twice before
-		 * encryption (it's used as a suffix in encryption routine),
-		 * but it's not a problem.
-		 */
-		verifier = intkeyring_encrypt(key, INTKEYRING_VERIFY_STR);
-		salt_b64 = g_base64_encode(salt->data, salt->len);
-	}
-
-	if (!verifier || !salt_b64) {
-		purple_debug_error("keyring-internal", "Failed to change "
-			"master password\n");
-		succ = FALSE;
-		purple_prefs_set_int(INTKEYRING_PREFS "pbkdf2_iterations",
-			old_iter);
-	} else {
-		purple_prefs_set_string(INTKEYRING_PREFS "pbkdf2_salt",
-			salt_b64);
-		purple_prefs_set_string(INTKEYRING_PREFS "key_verifier",
-			verifier);
-
-		intkeyring_buff_free(intkeyring_key);
-		intkeyring_key = key;
-		key = NULL;
-
-		intkeyring_reencrypt_passwords();
-
-		purple_signal_emit(purple_keyring_get_handle(),
-			"password-migration", NULL);
-	}
-
-	g_free(salt_b64);
-	g_free(verifier);
-	intkeyring_buff_free(salt);
-	intkeyring_buff_free(key);
-
-	return succ;
-}
-
-static void
-intkeyring_process_queue(void)
-{
-	GList *requests, *it;
-	gboolean open = intkeyring_unlocked;
-
-	requests = g_list_first(intkeyring_pending_requests);
-	intkeyring_pending_requests = NULL;
-
-	for (it = requests; it != NULL; it = g_list_next(it)) {
-		intkeyring_request *req = it->data;
-
-		if (open && req->type == INTKEYRING_REQUEST_READ) {
-			intkeyring_read(req->account, req->cb.read,
-				req->cb_data);
-		} else if (open && req->type == INTKEYRING_REQUEST_SAVE) {
-			intkeyring_save(req->account, req->password,
-				req->cb.save, req->cb_data);
-		} else if (open)
-			g_assert_not_reached();
-		else if (req->cb.read != NULL /* || req->cb.write != NULL */ ) {
-			GError *error = g_error_new_literal(PURPLE_KEYRING_ERROR,
-			                                    PURPLE_KEYRING_ERROR_CANCELLED,
-			                                    _("Operation cancelled."));
-			if (req->type == INTKEYRING_REQUEST_READ) {
-				req->cb.read(req->account, NULL, error,
-					req->cb_data);
-			} else if (req->type == INTKEYRING_REQUEST_SAVE)
-				req->cb.save(req->account, error, req->cb_data);
-			else
-				g_assert_not_reached();
-			g_error_free(error);
-		}
-
-		intkeyring_request_free(req);
-	}
-	g_list_free(requests);
-}
-
-static void
-intkeyring_decrypt_password(PurpleAccount *account, const gchar *ciphertext)
-{
-	gchar *plaintext;
-
-	plaintext = intkeyring_decrypt(intkeyring_key, ciphertext);
-	if (plaintext == NULL) {
-		purple_debug_warning("keyring-internal",
-			"Failed to decrypt a password\n");
-		return;
-	}
-
-	g_hash_table_replace(intkeyring_passwords, account, plaintext);
-}
-
-static void
-intkeyring_encrypt_password_if_needed(PurpleAccount *account)
-{
-	const gchar *plaintext;
-	gchar *ciphertext;
-
-	if (intkeyring_key == NULL) {
-		g_hash_table_remove(intkeyring_ciphertexts, account);
-		return;
-	}
-
-	ciphertext = g_hash_table_lookup(intkeyring_ciphertexts, account);
-	if (ciphertext != NULL)
-		return;
-
-	plaintext = g_hash_table_lookup(intkeyring_passwords, account);
-	if (plaintext == NULL)
-		return;
-
-	ciphertext = intkeyring_encrypt(intkeyring_key, plaintext);
-	g_return_if_fail(ciphertext != NULL);
-
-	g_hash_table_replace(intkeyring_ciphertexts, account, ciphertext);
-}
-
-static void
-intkeyring_encrypt_passwords_if_needed_it(gpointer account, gpointer plaintext,
-	gpointer _unused)
-{
-	intkeyring_encrypt_password_if_needed(account);
-}
-
-static void
-intkeyring_reencrypt_passwords(void)
-{
-	g_hash_table_remove_all(intkeyring_ciphertexts);
-	g_hash_table_foreach(intkeyring_passwords,
-		intkeyring_encrypt_passwords_if_needed_it, NULL);
-}
-
-static void
-intkeyring_unlock_decrypt(gpointer account, gpointer ciphertext,
-	gpointer _unused)
-{
-	intkeyring_decrypt_password(account, ciphertext);
-}
-
-/************************************************************************/
-/* Opening and unlocking keyring                                        */
-/************************************************************************/
-
-static void
-intkeyring_unlock_ok(gpointer _unused,
-	PurpleRequestFields *fields)
-{
-	const gchar *masterpw;
-	gchar *verifier;
-	intkeyring_buff_t *salt, *key;
-
-	intkeyring_masterpw_uirequest = NULL;
-
-	if (g_strcmp0(purple_prefs_get_string(INTKEYRING_PREFS
-		"encryption_method"), INTKEYRING_ENCRYPTION_METHOD) != 0)
-	{
-		purple_notify_error(NULL,
-			_("Unlocking internal keyring"),
-			_("Selected encryption method is not supported."),
-			_("Most probably, your passwords were encrypted with "
-			"newer Pidgin/libpurple version, please update."),
-			NULL);
-		return;
-	}
-
-	masterpw = purple_request_fields_get_string(fields, "password");
-
-	if (masterpw == NULL || masterpw[0] == '\0') {
-		intkeyring_unlock(_("No password entered."));
-		return;
-	}
-
-	salt = intkeyring_buff_from_base64(purple_prefs_get_string(
-		INTKEYRING_PREFS "pbkdf2_salt"));
-	key = intkeyring_derive_key(masterpw, salt);
-	intkeyring_buff_free(salt);
-
-	verifier = intkeyring_decrypt(key, purple_prefs_get_string(
-		INTKEYRING_PREFS "key_verifier"));
-
-	if (g_strcmp0(verifier, INTKEYRING_VERIFY_STR) != 0) {
-		g_free(verifier);
-		intkeyring_buff_free(key);
-		intkeyring_unlock(_("Invalid master password entered, "
-			"try again."));
-		return;
-	}
-
-	g_free(verifier);
-	intkeyring_key = key;
-	intkeyring_unlocked = TRUE;
-
-	g_hash_table_foreach(intkeyring_ciphertexts,
-		intkeyring_unlock_decrypt, NULL);
-
-	intkeyring_process_queue();
-}
-
-static void
-intkeyring_unlock_cancel(gpointer _unused,
-	PurpleRequestFields *fields)
-{
-	intkeyring_masterpw_uirequest = NULL;
-	intkeyring_process_queue();
-}
-
-static void
-intkeyring_unlock(const gchar *message)
-{
-	PurpleRequestFields *fields;
-	PurpleRequestFieldGroup *group;
-	PurpleRequestField *field;
-	const gchar *primary_msg, *secondary_msg = NULL;
-
-	if (intkeyring_unlocked || intkeyring_masterpw_uirequest != NULL)
-		return;
-
-	if (!purple_prefs_get_bool(INTKEYRING_PREFS "encrypt_passwords")) {
-		intkeyring_unlocked = TRUE;
-		intkeyring_process_queue();
-		return;
-	}
-
-	fields = purple_request_fields_new();
-	group = purple_request_field_group_new(NULL);
-	purple_request_fields_add_group(fields, group);
-
-	field = purple_request_field_string_new("password",
-		_("Master password"), "", FALSE);
-	purple_request_field_string_set_masked(field, TRUE);
-	purple_request_field_group_add_field(group, field);
-
-	primary_msg = _("Please, enter master password");
-	if (message) {
-		secondary_msg = primary_msg;
-		primary_msg = message;
-	}
-
-	intkeyring_masterpw_uirequest = purple_request_fields(NULL,
-		_("Unlocking internal keyring"),
-		primary_msg, secondary_msg, fields,
-		_("OK"), G_CALLBACK(intkeyring_unlock_ok),
-		_("Cancel"), G_CALLBACK(intkeyring_unlock_cancel),
-		NULL, NULL);
-}
-
-static void
-intkeyring_open(void)
-{
-	if (intkeyring_opened)
-		return;
-	intkeyring_opened = TRUE;
-
-	intkeyring_passwords = g_hash_table_new_full(g_direct_hash,
-		g_direct_equal, NULL, (GDestroyNotify)purple_str_wipe);
-	intkeyring_ciphertexts = g_hash_table_new_full(g_direct_hash,
-		g_direct_equal, NULL, g_free);
-}
-
-/************************************************************************/
-/* Keyring interface implementation                                     */
-/************************************************************************/
-
-static void
-intkeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
-	gpointer data)
-{
-	const char *password;
-	GError *error = NULL;
-
-	intkeyring_open();
-
-	if (!intkeyring_unlocked && g_hash_table_lookup(intkeyring_ciphertexts,
-		account) != NULL)
-	{
-		intkeyring_request *req = g_new0(intkeyring_request, 1);
-
-		req->type = INTKEYRING_REQUEST_READ;
-		req->account = account;
-		req->cb.read = cb;
-		req->cb_data = data;
-		intkeyring_pending_requests =
-			g_list_append(intkeyring_pending_requests, req);
-
-		intkeyring_unlock(NULL);
-		return;
-	}
-
-	password = g_hash_table_lookup(intkeyring_passwords, account);
-
-	if (password != NULL) {
-		purple_debug_misc("keyring-internal",
-			"Got password for account %s (%s).\n",
-			purple_account_get_username(account),
-			purple_account_get_protocol_id(account));
-		if (cb != NULL)
-			cb(account, password, NULL, data);
-	} else {
-		if (purple_debug_is_verbose()) {
-			purple_debug_misc("keyring-internal",
-				"No password for account %s (%s).\n",
-				purple_account_get_username(account),
-				purple_account_get_protocol_id(account));
-		}
-		error = g_error_new_literal(PURPLE_KEYRING_ERROR,
-		                            PURPLE_KEYRING_ERROR_NOPASSWORD,
-		                            _("Password not found."));
-		if (cb != NULL)
-			cb(account, NULL, error, data);
-		g_error_free(error);
-	}
-}
-
-static void
-intkeyring_save(PurpleAccount *account, const gchar *password,
-	PurpleKeyringSaveCallback cb, gpointer data)
-{
-	void *old_password;
-
-	intkeyring_open();
-
-	if (!intkeyring_unlocked) {
-		intkeyring_request *req;
-
-		if (password == NULL) {
-			g_hash_table_remove(intkeyring_ciphertexts, account);
-			g_hash_table_remove(intkeyring_passwords, account);
-			if (cb)
-				cb(account, NULL, data);
-			return;
-		}
-
-		req = g_new0(intkeyring_request, 1);
-		req->type = INTKEYRING_REQUEST_SAVE;
-		req->account = account;
-		req->password = g_strdup(password);
-		req->cb.save = cb;
-		req->cb_data = data;
-		intkeyring_pending_requests =
-			g_list_append(intkeyring_pending_requests, req);
-
-		intkeyring_unlock(NULL);
-		return;
-	}
-
-	g_hash_table_remove(intkeyring_ciphertexts, account);
-
-	old_password = g_hash_table_lookup(intkeyring_passwords, account);
-
-	if (password == NULL)
-		g_hash_table_remove(intkeyring_passwords, account);
-	else {
-		g_hash_table_replace(intkeyring_passwords, account,
-			g_strdup(password));
-	}
-
-	intkeyring_encrypt_password_if_needed(account);
-
-	if (!(password == NULL && old_password == NULL)) {
-		purple_debug_misc("keyring-internal",
-			"Password %s for account %s (%s).\n",
-			(password == NULL ? "removed" : (old_password == NULL ?
-				"saved" : "updated")),
-			purple_account_get_username(account),
-			purple_account_get_protocol_id(account));
-	} else if (purple_debug_is_verbose()) {
-		purple_debug_misc("keyring-internal",
-			"Password for account %s (%s) was already removed.\n",
-			purple_account_get_username(account),
-			purple_account_get_protocol_id(account));
-	}
-
-	if (cb != NULL)
-		cb(account, NULL, data);
-}
-
-static void
-intkeyring_close(void)
-{
-	if (!intkeyring_opened)
-		return;
-	intkeyring_opened = FALSE;
-	intkeyring_unlocked = FALSE;
-
-	if (intkeyring_masterpw_uirequest) {
-		purple_request_close(PURPLE_REQUEST_FIELDS,
-			intkeyring_masterpw_uirequest);
-	}
-	g_warn_if_fail(intkeyring_masterpw_uirequest == NULL);
-	g_warn_if_fail(intkeyring_pending_requests == NULL);
-
-	intkeyring_buff_free(intkeyring_key);
-	intkeyring_key = NULL;
-	g_hash_table_destroy(intkeyring_passwords);
-	intkeyring_passwords = NULL;
-	g_hash_table_destroy(intkeyring_ciphertexts);
-	intkeyring_ciphertexts = NULL;
-}
-
-static gboolean
-intkeyring_import_password(PurpleAccount *account, const char *mode,
-	const char *data, GError **error)
-{
-	g_return_val_if_fail(account != NULL, FALSE);
-	g_return_val_if_fail(data != NULL, FALSE);
-
-	intkeyring_open();
-
-	if (mode == NULL)
-		mode = "cleartext";
-
-	if (g_strcmp0(mode, "cleartext") == 0) {
-		g_hash_table_replace(intkeyring_passwords, account,
-			g_strdup(data));
-		return TRUE;
-	} else if (g_strcmp0(mode, "ciphertext") == 0) {
-		if (intkeyring_unlocked)
-			intkeyring_decrypt_password(account, data);
-		else {
-			g_hash_table_replace(intkeyring_ciphertexts, account,
-				g_strdup(data));
-		}
-		return TRUE;
-	} else {
-		g_set_error_literal(error, PURPLE_KEYRING_ERROR,
-		                    PURPLE_KEYRING_ERROR_BACKENDFAIL,
-		                    _("Invalid password storage mode."));
-		return FALSE;
-	}
-}
-
-static gboolean
-intkeyring_export_password(PurpleAccount *account, const char **mode,
-	char **data, GError **error, GDestroyNotify *destroy)
-{
-	gchar *ciphertext = NULL;
-	intkeyring_open();
-
-	if (!purple_prefs_get_bool(INTKEYRING_PREFS "encrypt_passwords")) {
-		gchar *cleartext = g_hash_table_lookup(intkeyring_passwords,
-			account);
-
-		if (cleartext == NULL)
-			return FALSE;
-
-		*mode = "cleartext";
-		*data = g_strdup(cleartext);
-		*destroy = (GDestroyNotify)purple_str_wipe;
-		return TRUE;
-	}
-
-	ciphertext = g_strdup(g_hash_table_lookup(intkeyring_ciphertexts,
-		account));
-
-	if (ciphertext == NULL && intkeyring_unlocked) {
-		gchar *plaintext = g_hash_table_lookup(intkeyring_passwords,
-			account);
-
-		if (plaintext == NULL)
-			return FALSE;
-
-		purple_debug_warning("keyring-internal", "Encrypted password "
-			"is missing at export (it shouldn't happen)\n");
-		ciphertext = intkeyring_encrypt(intkeyring_key, plaintext);
-	}
-
-	if (ciphertext == NULL)
-		return FALSE;
-
-	*mode = "ciphertext";
-	*data = ciphertext;
-	*destroy = g_free;
-	return TRUE;
-}
-
-static PurpleRequestFields *
-intkeyring_read_settings(void)
-{
-	PurpleRequestFields *fields;
-	PurpleRequestFieldGroup *group;
-	PurpleRequestField *field;
-
-	fields = purple_request_fields_new();
-	group = purple_request_field_group_new(NULL);
-	purple_request_fields_add_group(fields, group);
-
-	field = purple_request_field_bool_new("encrypt_passwords",
-		_("Encrypt passwords"), purple_prefs_get_bool(
-			INTKEYRING_PREFS "encrypt_passwords"));
-	purple_request_field_group_add_field(group, field);
-
-	group = purple_request_field_group_new(_("Master password"));
-	purple_request_fields_add_group(fields, group);
-
-	field = purple_request_field_string_new("passphrase1",
-		_("New passphrase:"), "", FALSE);
-	purple_request_field_string_set_masked(field, TRUE);
-	purple_request_field_group_add_field(group, field);
-
-	field = purple_request_field_string_new("passphrase2",
-		_("New passphrase (again):"), "", FALSE);
-	purple_request_field_string_set_masked(field, TRUE);
-	purple_request_field_group_add_field(group, field);
-
-	group = purple_request_field_group_new(_("Advanced settings"));
-	purple_request_fields_add_group(fields, group);
-
-	field = purple_request_field_int_new("pbkdf2_desired_iterations",
-		_("Number of PBKDF2 iterations:"), purple_prefs_get_int(
-			INTKEYRING_PREFS "pbkdf2_desired_iterations"),
-		INTKEYRING_PBKDF2_ITERATIONS_MIN,
-		INTKEYRING_PBKDF2_ITERATIONS_MAX);
-	purple_request_field_group_add_field(group, field);
-
-	return fields;
-}
-
-static gboolean
-intkeyring_apply_settings(void *notify_handle,
-	PurpleRequestFields *fields)
-{
-	const gchar *passphrase, *passphrase2;
-
-	intkeyring_unlock(_("You have to unlock the keyring first."));
-	if (!intkeyring_unlocked)
-		return FALSE;
-
-	passphrase = purple_request_fields_get_string(fields, "passphrase1");
-	if (g_strcmp0(passphrase, "") == 0)
-		passphrase = NULL;
-	passphrase2 = purple_request_fields_get_string(fields, "passphrase2");
-	if (g_strcmp0(passphrase2, "") == 0)
-		passphrase2 = NULL;
-
-	if (g_strcmp0(passphrase, passphrase2) != 0) {
-		purple_notify_error(notify_handle,
-			_("Internal keyring settings"),
-			_("Passphrases do not match"), NULL, NULL);
-		return FALSE;
-	}
-
-	if (purple_request_fields_get_bool(fields, "encrypt_passwords") &&
-		!passphrase && !intkeyring_key)
-	{
-		purple_notify_error(notify_handle,
-			_("Internal keyring settings"),
-			_("You have to set up a Master password, if you want "
-			"to enable encryption"), NULL, NULL);
-		return FALSE;
-	}
-
-	if (!purple_request_fields_get_bool(fields, "encrypt_passwords") &&
-		passphrase)
-	{
-		purple_notify_error(notify_handle,
-			_("Internal keyring settings"),
-			_("You don't need any master password, if you won't "
-			"enable passwords encryption"), NULL, NULL);
-		return FALSE;
-	}
-
-	purple_prefs_set_string(INTKEYRING_PREFS "encryption_method",
-		INTKEYRING_ENCRYPTION_METHOD);
-
-	purple_prefs_set_int(INTKEYRING_PREFS "pbkdf2_desired_iterations",
-		purple_request_fields_get_integer(fields,
-			"pbkdf2_desired_iterations"));
-
-	if (passphrase != NULL) {
-		if (!intkeyring_change_masterpw(passphrase))
-			return FALSE;
-	}
-
-	purple_prefs_set_bool(INTKEYRING_PREFS "encrypt_passwords",
-		purple_request_fields_get_bool(fields, "encrypt_passwords"));
-
-	purple_signal_emit(purple_keyring_get_handle(), "password-migration",
-		NULL);
-
-
-	return TRUE;
-}
-
-static PurplePluginInfo *
-plugin_query(GError **error)
-{
-	const gchar * const authors[] = INTKEYRING_AUTHORS;
-
-	return purple_plugin_info_new(
-		"id",           INTKEYRING_ID,
-		"name",         INTKEYRING_NAME,
-		"version",      DISPLAY_VERSION,
-		"category",     N_("Keyring"),
-		"summary",      "Internal Keyring Plugin",
-		"description",  INTKEYRING_DESCRIPTION,
-		"authors",      authors,
-		"website",      PURPLE_WEBSITE,
-		"abi-version",  PURPLE_ABI_VERSION,
-		"flags",        PURPLE_PLUGIN_INFO_FLAGS_INTERNAL,
-		NULL
-	);
-}
-
-static gboolean
-plugin_load(PurplePlugin *plugin, GError **error)
-{
-	purple_prefs_add_none("/plugins/keyrings");
-	purple_prefs_add_none("/plugins/keyrings/internal");
-	purple_prefs_add_bool(INTKEYRING_PREFS "encrypt_passwords", FALSE);
-	purple_prefs_add_string(INTKEYRING_PREFS "encryption_method",
-		INTKEYRING_ENCRYPTION_METHOD);
-	purple_prefs_add_int(INTKEYRING_PREFS "pbkdf2_desired_iterations",
-		INTKEYRING_PBKDF2_ITERATIONS);
-	purple_prefs_add_int(INTKEYRING_PREFS "pbkdf2_iterations",
-		INTKEYRING_PBKDF2_ITERATIONS);
-	purple_prefs_add_string(INTKEYRING_PREFS "pbkdf2_salt", "");
-	purple_prefs_add_string(INTKEYRING_PREFS "key_verifier", "");
-
-	keyring_handler = purple_keyring_new();
-
-	purple_keyring_set_name(keyring_handler, _(INTKEYRING_NAME));
-	purple_keyring_set_id(keyring_handler, INTKEYRING_ID);
-	purple_keyring_set_read_password(keyring_handler,
-		intkeyring_read);
-	purple_keyring_set_save_password(keyring_handler,
-		intkeyring_save);
-	purple_keyring_set_close_keyring(keyring_handler,
-		intkeyring_close);
-	purple_keyring_set_import_password(keyring_handler,
-		intkeyring_import_password);
-	purple_keyring_set_export_password(keyring_handler,
-		intkeyring_export_password);
-	purple_keyring_set_read_settings(keyring_handler,
-		intkeyring_read_settings);
-	purple_keyring_set_apply_settings(keyring_handler,
-		intkeyring_apply_settings);
-
-	purple_keyring_register(keyring_handler);
-
-	return TRUE;
-}
-
-static gboolean
-plugin_unload(PurplePlugin *plugin, GError **error)
-{
-	if (purple_keyring_get_inuse() == keyring_handler) {
-		g_set_error(error, INTKEYRING_DOMAIN, 0, "The keyring is currently "
-			"in use.");
-		purple_debug_warning("keyring-internal",
-			"keyring in use, cannot unload\n");
-		return FALSE;
-	}
-
-	intkeyring_close();
-
-	purple_keyring_unregister(keyring_handler);
-	purple_keyring_free(keyring_handler);
-	keyring_handler = NULL;
-
-	if (intkeyring_key != NULL) {
-		purple_debug_warning("keyring-internal", "Master key should be "
-			"cleaned up at this point\n");
-		intkeyring_buff_free(intkeyring_key);
-		intkeyring_key = NULL;
-	}
-
-	return TRUE;
-}
-
-PURPLE_PLUGIN_INIT(internal_keyring, plugin_query, plugin_load, plugin_unload);
diff --git a/libpurple/plugins/keyrings/meson.build b/libpurple/plugins/keyrings/meson.build
deleted file mode 100644
--- a/libpurple/plugins/keyrings/meson.build
+++ /dev/null
@@ -1,12 +0,0 @@
-if nettle.found()
-	internalkeyring_plugin = library('internalkeyring', 'internalkeyring.c',
-	    c_args : ['-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Purple-InternalKeyring"'],
-	    dependencies : [nettle, libpurple_dep],
-	    name_prefix : '',
-	    build_by_default : false,
-	    install : false, install_dir : PURPLE_PLUGINDIR)
-endif
-
-if nettle.found() or secretservice.found()
-	devenv.append('PURPLE_PLUGIN_PATH', meson.current_build_dir())
-endif
diff --git a/libpurple/plugins/meson.build b/libpurple/plugins/meson.build
--- a/libpurple/plugins/meson.build
+++ b/libpurple/plugins/meson.build
@@ -1,5 +1,4 @@
 subdir('keychain-access')
-subdir('keyrings')
 subdir('kwallet')
 subdir('libsecret')
 subdir('notification-sound')
diff --git a/meson.build b/meson.build
--- a/meson.build
+++ b/meson.build
@@ -562,13 +562,6 @@
 endif
 
 #######################################################################
-# Check for Nettle (Crypto Library)
-#######################################################################
-
-nettle = dependency('nettle', version : '>= 3.0', required : get_option('nettle'))
-conf.set('HAVE_NETTLE', nettle.found())
-
-#######################################################################
 # Check for Cyrus-SASL (for xmpp/irc)
 #######################################################################
 foreach func : ['snprintf']
@@ -725,7 +718,6 @@
 }, section: 'Protocol Support', bool_yn: true, list_sep: ', ')
 
 summary({
-    'Internal credential provider (via Nettle)': nettle.found(),
     'KWallet credential provider': kwallet.found(),
     'libsecret credential provider': libsecret.found(),
     'Unity integration': enable_unity,
