internal-keyring: Port to Nettle

Sun, 18 Jun 2017 12:31:42 -0500

author
Mike Ruprecht <cmaiku@gmail.com>
date
Sun, 18 Jun 2017 12:31:42 -0500
changeset 38408
567447af7532
parent 38407
6e9983f3663d
child 38409
751fa9e1fc8e

internal-keyring: Port to Nettle

This patch ports the internal keyring plugin to use the Nettle
cryptography library instead of the PurpleCipher API. The PKCS7
padding code from the AES PurpleCipher is used. It also makes the
kerying itself optional in case Nettle support is disabled.

libpurple/plugins/keyrings/Makefile.am file | annotate | diff | comparison | revisions
libpurple/plugins/keyrings/internalkeyring.c file | annotate | diff | comparison | revisions
--- a/libpurple/plugins/keyrings/Makefile.am	Sun Jun 18 12:26:55 2017 -0500
+++ b/libpurple/plugins/keyrings/Makefile.am	Sun Jun 18 12:31:42 2017 -0500
@@ -5,10 +5,14 @@
 
 plugindir = @PURPLE_PLUGINDIR@
 
-internalkeyring_la_CFLAGS  = $(AM_CPPFLAGS)
+if ENABLE_NETTLE
+
+internalkeyring_la_CFLAGS  = $(AM_CPPFLAGS) $(NETTLE_CFLAGS)
 internalkeyring_la_LDFLAGS = -module @PLUGIN_LDFLAGS@
 internalkeyring_la_SOURCES = internalkeyring.c
-internalkeyring_la_LIBADD  = @PURPLE_LIBS@
+internalkeyring_la_LIBADD  = @PURPLE_LIBS@ $(NETTLE_LIBS)
+
+endif
 
 if ENABLE_SECRETSERVICE
 
@@ -56,8 +60,12 @@
 
 if PLUGINS
 
-plugin_LTLIBRARIES = \
+plugin_LTLIBRARIES =
+
+if ENABLE_NETTLE
+plugin_LTLIBRARIES += \
 	internalkeyring.la
+endif
 
 if ENABLE_SECRETSERVICE
 plugin_LTLIBRARIES += \
--- a/libpurple/plugins/keyrings/internalkeyring.c	Sun Jun 18 12:26:55 2017 -0500
+++ b/libpurple/plugins/keyrings/internalkeyring.c	Sun Jun 18 12:31:42 2017 -0500
@@ -31,8 +31,9 @@
 #include "plugins.h"
 #include "version.h"
 
-#include "ciphers/aescipher.h"
-#include "ciphers/pbkdf2cipher.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 " \
@@ -45,7 +46,7 @@
 #define INTKEYRING_PBKDF2_ITERATIONS 10000
 #define INTKEYRING_PBKDF2_ITERATIONS_MIN 1000
 #define INTKEYRING_PBKDF2_ITERATIONS_MAX 1000000000
-#define INTKEYRING_KEY_LEN (256/8)
+#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"
@@ -150,33 +151,16 @@
 static intkeyring_buff_t *
 intkeyring_derive_key(const gchar *passphrase, intkeyring_buff_t *salt)
 {
-	PurpleCipher *cipher;
-	gboolean succ;
 	intkeyring_buff_t *ret;
 
 	g_return_val_if_fail(passphrase != NULL, NULL);
 
-	cipher = purple_pbkdf2_cipher_new(G_CHECKSUM_SHA256);
-
-	g_object_set(G_OBJECT(cipher), "iter_count",
-		GUINT_TO_POINTER(purple_prefs_get_int(INTKEYRING_PREFS
-		"pbkdf2_iterations")), NULL);
-	g_object_set(G_OBJECT(cipher), "out_len", GUINT_TO_POINTER(
-		INTKEYRING_KEY_LEN), NULL);
-	purple_cipher_set_salt(cipher, salt->data, salt->len);
-	purple_cipher_set_key(cipher, (const guchar*)passphrase,
-		strlen(passphrase));
-
 	ret = intkeyring_buff_new(g_new(guchar, INTKEYRING_KEY_LEN),
 		INTKEYRING_KEY_LEN);
-	succ = purple_cipher_digest(cipher, ret->data, ret->len);
 
-	g_object_unref(cipher);
-
-	if (!succ) {
-		intkeyring_buff_free(ret);
-		return NULL;
-	}
+	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;
 }
@@ -230,10 +214,11 @@
 static gchar *
 intkeyring_encrypt(intkeyring_buff_t *key, const gchar *str)
 {
-	PurpleCipher *cipher;
+	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;
 
@@ -249,31 +234,38 @@
 	g_return_val_if_fail(plaintext_len + verify_len <= sizeof(plaintext),
 		NULL);
 
-	cipher = purple_aes_cipher_new();
-	g_return_val_if_fail(cipher != NULL, NULL);
-
 	memset(plaintext, 0, plaintext_len);
 	memcpy(plaintext, str, text_len);
 	memcpy(plaintext + plaintext_len, INTKEYRING_VERIFY_STR, verify_len);
 	plaintext_len += verify_len;
 
-	iv = intkeyring_gen_salt(purple_cipher_get_block_size(cipher));
+	/* 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);
-	purple_cipher_set_iv(cipher, iv->data, iv->len);
-	purple_cipher_set_key(cipher, key->data, key->len);
-	purple_cipher_set_batch_mode(cipher,
-		PURPLE_CIPHER_BATCH_MODE_CBC);
+	aes256_set_encrypt_key(&ctx.ctx, key->data);
+	CBC_SET_IV(&ctx, iv->data);
 
 	memcpy(encrypted_raw, iv->data, iv->len);
 
-	encrypted_size = purple_cipher_encrypt(cipher,
-		plaintext, plaintext_len, encrypted_raw + iv->len,
-		sizeof(encrypted_raw) - 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);
-	g_object_unref(cipher);
 
 	if (encrypted_size < 0)
 		return NULL;
@@ -285,42 +277,63 @@
 static gchar *
 intkeyring_decrypt(intkeyring_buff_t *key, const gchar *str)
 {
-	PurpleCipher *cipher;
+	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;
-	ssize_t plaintext_len;
+	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);
 
-	cipher = purple_aes_cipher_new();
-	g_return_val_if_fail(cipher != NULL, NULL);
-
 	encrypted_raw = g_base64_decode(str, &encrypted_size);
 	g_return_val_if_fail(encrypted_raw != NULL, NULL);
 
-	iv_len = purple_cipher_get_block_size(cipher);
+	iv_len = AES_BLOCK_SIZE;
 	if (encrypted_size < iv_len) {
 		g_free(encrypted_raw);
 		return NULL;
 	}
 
-	purple_cipher_set_iv(cipher, encrypted_raw, iv_len);
-	purple_cipher_set_key(cipher, key->data, key->len);
-	purple_cipher_set_batch_mode(cipher,
-		PURPLE_CIPHER_BATCH_MODE_CBC);
+	/* 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);
 
-	plaintext_len = purple_cipher_decrypt(cipher,
-		encrypted_raw + iv_len, encrypted_size - iv_len,
-		plaintext, sizeof(plaintext));
+	/* 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;
+	}
 
-	g_free(encrypted_raw);
-	g_object_unref(cipher);
+	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 &&

mercurial