/cpw/tomkiewicz/masterpassword: 4712cf749699: PBKDF2 support

Tomasz Wasilczyk tomkiewicz at cpw.pidgin.im
Tue May 7 12:11:24 EDT 2013


Changeset: 4712cf749699cf7e9702c21b90c17d720e157171
Author:	 Tomasz Wasilczyk <tomkiewicz at cpw.pidgin.im>
Date:	 2013-05-07 18:11 +0200
Branch:	 soc.2008.masterpassword
URL: https://hg.pidgin.im/cpw/tomkiewicz/masterpassword/rev/4712cf749699

Description:

PBKDF2 support

diffstat:

 libpurple/cipher.c             |    1 +
 libpurple/ciphers/Makefile.am  |    2 +
 libpurple/ciphers/ciphers.h    |    3 +
 libpurple/ciphers/pbkdf2.c     |  323 +++++++++++++++++++++++++++++++++++++++++
 libpurple/plugins/Makefile.am  |    6 +-
 libpurple/plugins/ciphertest.c |  202 +++++++++++++++++++++++++
 6 files changed, 536 insertions(+), 1 deletions(-)

diffs (truncated from 602 to 300 lines):

diff --git a/libpurple/cipher.c b/libpurple/cipher.c
--- a/libpurple/cipher.c
+++ b/libpurple/cipher.c
@@ -255,6 +255,7 @@ purple_ciphers_init() {
 	purple_ciphers_register_cipher("hmac", purple_hmac_cipher_get_ops());
 	purple_ciphers_register_cipher("des", purple_des_cipher_get_ops());
 	purple_ciphers_register_cipher("des3", purple_des3_cipher_get_ops());
+	purple_ciphers_register_cipher("pbkdf2", purple_pbkdf2_cipher_get_ops());
 	purple_ciphers_register_cipher("rc4", purple_rc4_cipher_get_ops());
 }
 
diff --git a/libpurple/ciphers/Makefile.am b/libpurple/ciphers/Makefile.am
--- a/libpurple/ciphers/Makefile.am
+++ b/libpurple/ciphers/Makefile.am
@@ -1,10 +1,12 @@
 noinst_LTLIBRARIES=libpurple-ciphers.la
+# XXX: cipher.lo won't be updated after a change in cipher files
 
 libpurple_ciphers_la_SOURCES=\
 	des.c \
 	gchecksum.c \
 	hmac.c \
 	md4.c \
+	pbkdf2.c \
 	rc4.c
 
 noinst_HEADERS =\
diff --git a/libpurple/ciphers/ciphers.h b/libpurple/ciphers/ciphers.h
--- a/libpurple/ciphers/ciphers.h
+++ b/libpurple/ciphers/ciphers.h
@@ -34,5 +34,8 @@ PurpleCipherOps * purple_hmac_cipher_get
 /* md4.c */
 PurpleCipherOps * purple_md4_cipher_get_ops(void);
 
+/* pbkdf2.c */
+PurpleCipherOps * purple_pbkdf2_cipher_get_ops(void);
+
 /* rc4.c */
 PurpleCipherOps * purple_rc4_cipher_get_ops(void);
diff --git a/libpurple/ciphers/pbkdf2.c b/libpurple/ciphers/pbkdf2.c
new file mode 100644
--- /dev/null
+++ b/libpurple/ciphers/pbkdf2.c
@@ -0,0 +1,323 @@
+/*
+ * 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
+ *
+ * Written by Tomek Wasilczyk <tomkiewicz at cpw.pidgin.im>
+ */
+
+#include "internal.h"
+#include "cipher.h"
+#include "ciphers.h"
+#include "debug.h"
+
+/* 1024bit */
+#define PBKDF2_HASH_MAX_LEN 128
+
+typedef struct
+{
+	gchar *hash_func;
+	guint iter_count;
+	size_t out_len;
+
+	guchar *salt;
+	size_t salt_len;
+	guchar *passphrase;
+	size_t passphrase_len;
+} Pbkdf2Context;
+
+static void
+purple_pbkdf2_init(PurpleCipherContext *context, void *extra)
+{
+	Pbkdf2Context *ctx_data;
+
+	ctx_data = g_new0(Pbkdf2Context, 1);
+	purple_cipher_context_set_data(context, ctx_data);
+
+	purple_cipher_context_reset(context, extra);
+}
+
+static void
+purple_pbkdf2_uninit(PurpleCipherContext *context)
+{
+	Pbkdf2Context *ctx_data;
+
+	purple_cipher_context_reset(context, NULL);
+
+	ctx_data = purple_cipher_context_get_data(context);
+	g_free(ctx_data);
+	purple_cipher_context_set_data(context, NULL);
+}
+
+static void
+purple_pbkdf2_reset(PurpleCipherContext *context, void *extra)
+{
+	Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context);
+
+	g_return_if_fail(ctx_data != NULL);
+
+	g_free(ctx_data->hash_func);
+	ctx_data->hash_func = NULL;
+	ctx_data->iter_count = 1;
+	ctx_data->out_len = 256;
+
+	purple_cipher_context_reset_state(context, extra);
+}
+
+static void
+purple_pbkdf2_reset_state(PurpleCipherContext *context, void *extra)
+{
+	Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context);
+
+	g_return_if_fail(ctx_data != NULL);
+
+	purple_cipher_context_set_salt(context, NULL, 0);
+	purple_cipher_context_set_key(context, NULL, 0);
+}
+
+static void
+purple_pbkdf2_set_option(PurpleCipherContext *context, const gchar *name,
+	void *value)
+{
+	Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context);
+
+	g_return_if_fail(ctx_data != NULL);
+
+	if (g_strcmp0(name, "hash") == 0) {
+		g_free(ctx_data->hash_func);
+		ctx_data->hash_func = g_strdup(value);
+		return;
+	}
+
+	if (g_strcmp0(name, "iter_count") == 0) {
+		ctx_data->iter_count = GPOINTER_TO_UINT(value);
+		return;
+	}
+
+	if (g_strcmp0(name, "out_len") == 0) {
+		ctx_data->out_len = GPOINTER_TO_UINT(value);
+		return;
+	}
+
+	purple_debug_warning("pbkdf2", "Unknown option: %s\n",
+		name ? name : "(null)");
+}
+
+static void *
+purple_pbkdf2_get_option(PurpleCipherContext *context, const gchar *name)
+{
+	Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context);
+
+	g_return_val_if_fail(ctx_data != NULL, NULL);
+
+	if (g_strcmp0(name, "hash") == 0)
+		return ctx_data->hash_func;
+
+	if (g_strcmp0(name, "iter_count") == 0)
+		return GUINT_TO_POINTER(ctx_data->iter_count);
+
+	if (g_strcmp0(name, "out_len") == 0)
+		return GUINT_TO_POINTER(ctx_data->out_len);
+
+	purple_debug_warning("pbkdf2", "Unknown option: %s\n",
+		name ? name : "(null)");
+	return NULL;
+}
+
+static size_t
+purple_pbkdf2_get_digest_size(PurpleCipherContext *context)
+{
+	Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context);
+
+	g_return_val_if_fail(ctx_data != NULL, 0);
+
+	return ctx_data->out_len;
+}
+
+static void
+purple_pbkdf2_set_salt(PurpleCipherContext *context, const guchar *salt, size_t len)
+{
+	Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context);
+
+	g_return_if_fail(ctx_data != NULL);
+
+	g_free(ctx_data->salt);
+	ctx_data->salt = NULL;
+	ctx_data->salt_len = 0;
+
+	if (len == 0)
+		return;
+	g_return_if_fail(salt != NULL);
+
+	ctx_data->salt = g_memdup(salt, len);
+	ctx_data->salt_len = len;
+}
+
+static void
+purple_pbkdf2_set_key(PurpleCipherContext *context, const guchar *key,
+	size_t len)
+{
+	Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context);
+
+	g_return_if_fail(ctx_data != NULL);
+
+	if (ctx_data->passphrase != NULL) {
+		memset(ctx_data->passphrase, 0, ctx_data->passphrase_len);
+		g_free(ctx_data->passphrase);
+		ctx_data->passphrase = NULL;
+	}
+	ctx_data->passphrase_len = 0;
+
+	if (len == 0)
+		return;
+	g_return_if_fail(key != NULL);
+
+	ctx_data->passphrase = g_memdup(key, len);
+	ctx_data->passphrase_len = len;
+}
+
+/* inspired by gnutls 3.1.10, pbkdf2-sha1.c */
+static gboolean
+purple_pbkdf2_digest(PurpleCipherContext *context, guchar digest[], size_t len)
+{
+	Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context);
+	guchar halfkey[PBKDF2_HASH_MAX_LEN], halfkey_hash[PBKDF2_HASH_MAX_LEN];
+	guint halfkey_len, halfkey_count, halfkey_pad, halfkey_no;
+	guchar *salt_ext;
+	size_t salt_ext_len;
+	guint iter_no;
+	PurpleCipherContext *hash;
+
+	g_return_val_if_fail(ctx_data != NULL, FALSE);
+	g_return_val_if_fail(digest != NULL, FALSE);
+	g_return_val_if_fail(len >= ctx_data->out_len, FALSE);
+
+	g_return_val_if_fail(ctx_data->hash_func != NULL, FALSE);
+	g_return_val_if_fail(ctx_data->iter_count > 0, FALSE);
+	g_return_val_if_fail(ctx_data->passphrase != NULL ||
+		ctx_data->passphrase_len == 0, FALSE);
+	g_return_val_if_fail(ctx_data->salt != NULL || ctx_data->salt_len == 0,
+		FALSE);
+	g_return_val_if_fail(ctx_data->out_len > 0, FALSE);
+	g_return_val_if_fail(ctx_data->out_len < 0xFFFFFFFFU, FALSE);
+
+	salt_ext_len = ctx_data->salt_len + 4;
+
+	hash = purple_cipher_context_new_by_name("hmac", NULL);
+	if (hash == NULL) {
+		purple_debug_error("pbkdf2", "Couldn't create new hmac "
+			"context\n");
+		return FALSE;
+	}
+	purple_cipher_context_set_option(hash, "hash",
+		(void*)ctx_data->hash_func);
+	purple_cipher_context_set_key(hash, (const guchar*)ctx_data->passphrase,
+		ctx_data->passphrase_len);
+
+	halfkey_len = purple_cipher_context_get_digest_size(hash);
+	if (halfkey_len <= 0 || halfkey_len > PBKDF2_HASH_MAX_LEN) {
+		purple_debug_error("pbkdf2", "Unsupported hash function: %s "
+			"(digest size: %d)\n",
+			ctx_data->hash_func ? ctx_data->hash_func : "(null)",
+			halfkey_len);
+		return FALSE;
+	}
+
+	halfkey_count = ((ctx_data->out_len - 1) / halfkey_len) + 1;
+	halfkey_pad = ctx_data->out_len - (halfkey_count - 1) * halfkey_len;
+
+	salt_ext = g_new(guchar, salt_ext_len);
+	memcpy(salt_ext, ctx_data->salt, ctx_data->salt_len);
+
+	for (halfkey_no = 1; halfkey_no <= halfkey_count; halfkey_no++) {
+		memset(halfkey, 0, halfkey_len);
+
+		for (iter_no = 1; iter_no <= ctx_data->iter_count; iter_no++) {
+			int i;
+
+			purple_cipher_context_reset_state(hash, NULL);
+
+			if (iter_no == 1) {



More information about the Commits mailing list