summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Ruprecht <cmaiku@gmail.com>2017-06-18 12:31:42 -0500
committerMike Ruprecht <cmaiku@gmail.com>2017-06-18 12:31:42 -0500
commitbf6fe426f5c930a5b8c66d02b4c2b53721a1db22 (patch)
treeaffccdc19345938383426977cc79ae522f50e337
parentc3cb5331c2a9bf3ca2a1cbcbc887c9351b152a30 (diff)
downloadpidgin-bf6fe426f5c930a5b8c66d02b4c2b53721a1db22.tar.gz
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.
-rw-r--r--libpurple/plugins/keyrings/Makefile.am14
-rw-r--r--libpurple/plugins/keyrings/internalkeyring.c115
2 files changed, 75 insertions, 54 deletions
diff --git a/libpurple/plugins/keyrings/Makefile.am b/libpurple/plugins/keyrings/Makefile.am
index d780a91d3e..44479071c5 100644
--- a/libpurple/plugins/keyrings/Makefile.am
+++ b/libpurple/plugins/keyrings/Makefile.am
@@ -5,10 +5,14 @@ CLEANFILES =
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 @@ endif
if PLUGINS
-plugin_LTLIBRARIES = \
+plugin_LTLIBRARIES =
+
+if ENABLE_NETTLE
+plugin_LTLIBRARIES += \
internalkeyring.la
+endif
if ENABLE_SECRETSERVICE
plugin_LTLIBRARIES += \
diff --git a/libpurple/plugins/keyrings/internalkeyring.c b/libpurple/plugins/keyrings/internalkeyring.c
index ea662eade5..7492b83023 100644
--- a/libpurple/plugins/keyrings/internalkeyring.c
+++ b/libpurple/plugins/keyrings/internalkeyring.c
@@ -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 @@ intkeyring_buff_from_base64(const gchar *base64)
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 @@ intkeyring_gen_salt(size_t len)
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 @@ intkeyring_encrypt(intkeyring_buff_t *key, const gchar *str)
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 @@ intkeyring_encrypt(intkeyring_buff_t *key, const gchar *str)
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 &&