summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Wasilczyk <tomkiewicz@cpw.pidgin.im>2013-05-09 20:24:26 +0200
committerTomasz Wasilczyk <tomkiewicz@cpw.pidgin.im>2013-05-09 20:24:26 +0200
commit900cce122eae3a0948143586ab115e8708aa4c81 (patch)
tree6139ab93ee3f5d9c3edb68f4752adb6825003392
parent91f702986e0674e85c823ee4b4e4b1c6f9be16f8 (diff)
downloadpidgin-900cce122eae3a0948143586ab115e8708aa4c81.tar.gz
AES support
-rw-r--r--libpurple/Makefile.am2
-rw-r--r--libpurple/Makefile.mingw1
-rw-r--r--libpurple/cipher.c10
-rw-r--r--libpurple/ciphers/Makefile.am12
-rw-r--r--libpurple/ciphers/aes.c566
-rw-r--r--libpurple/ciphers/ciphers.h25
-rw-r--r--libpurple/plugins/ciphertest.c159
7 files changed, 754 insertions, 21 deletions
diff --git a/libpurple/Makefile.am b/libpurple/Makefile.am
index 8f2f5c4e68..3cd42b71ad 100644
--- a/libpurple/Makefile.am
+++ b/libpurple/Makefile.am
@@ -317,6 +317,8 @@ libpurple_la_LIBADD = \
$(GSTINTERFACES_LIBS) \
$(IDN_LIBS) \
$(JSON_LIBS) \
+ $(GNUTLS_LIBS) \
+ $(NSS_LIBS) \
ciphers/libpurple-ciphers.la \
-lm
diff --git a/libpurple/Makefile.mingw b/libpurple/Makefile.mingw
index 5594f8af38..f6735ede06 100644
--- a/libpurple/Makefile.mingw
+++ b/libpurple/Makefile.mingw
@@ -49,6 +49,7 @@ C_SRC = \
buddyicon.c \
certificate.c \
cipher.c \
+ ciphers/aes.c \
ciphers/des.c \
ciphers/gchecksum.c \
ciphers/hmac.c \
diff --git a/libpurple/cipher.c b/libpurple/cipher.c
index b44fbe39fe..1df779caab 100644
--- a/libpurple/cipher.c
+++ b/libpurple/cipher.c
@@ -248,15 +248,7 @@ purple_ciphers_init() {
purple_value_new(PURPLE_TYPE_SUBTYPE,
PURPLE_SUBTYPE_CIPHER));
- purple_ciphers_register_cipher("md5", purple_md5_cipher_get_ops());
- purple_ciphers_register_cipher("sha1", purple_sha1_cipher_get_ops());
- purple_ciphers_register_cipher("sha256", purple_sha256_cipher_get_ops());
- purple_ciphers_register_cipher("md4", purple_md4_cipher_get_ops());
- 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());
+ purple_ciphers_register_all();
}
void
diff --git a/libpurple/ciphers/Makefile.am b/libpurple/ciphers/Makefile.am
index e43abdbcd7..c2c1e7544b 100644
--- a/libpurple/ciphers/Makefile.am
+++ b/libpurple/ciphers/Makefile.am
@@ -1,7 +1,15 @@
noinst_LTLIBRARIES=libpurple-ciphers.la
# XXX: cipher.lo won't be updated after a change in cipher files
+if USE_NSS
+AES_SOURCE = aes.c
+endif
+if USE_GNUTLS
+AES_SOURCE = aes.c
+endif
+
libpurple_ciphers_la_SOURCES=\
+ $(AES_SOURCE) \
des.c \
gchecksum.c \
hmac.c \
@@ -18,4 +26,6 @@ AM_CPPFLAGS = \
$(INTGG_CFLAGS) \
$(AM_CFLAGS) \
$(GLIB_CFLAGS) \
- $(DEBUG_CFLAGS)
+ $(DEBUG_CFLAGS) \
+ $(GNUTLS_CFLAGS) \
+ $(NSS_CFLAGS)
diff --git a/libpurple/ciphers/aes.c b/libpurple/ciphers/aes.c
new file mode 100644
index 0000000000..ac340eedfd
--- /dev/null
+++ b/libpurple/ciphers/aes.c
@@ -0,0 +1,566 @@
+/*
+ * 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@cpw.pidgin.im>
+ */
+
+#include "internal.h"
+#include "cipher.h"
+#include "ciphers.h"
+#include "debug.h"
+
+#if defined(HAVE_GNUTLS)
+# define PURPLE_AES_USE_GNUTLS 1
+# include <gnutls/gnutls.h>
+# include <gnutls/crypto.h>
+#elif defined(HAVE_NSS)
+# define PURPLE_AES_USE_NSS 1
+# include <nss.h>
+# include <pk11pub.h>
+# include <prerror.h>
+#else
+# error "No GnuTLS or NSS support"
+#endif
+
+/* 128bit */
+#define PURPLE_AES_BLOCK_SIZE 16
+
+typedef struct
+{
+ guchar iv[PURPLE_AES_BLOCK_SIZE];
+ guchar key[32];
+ guint key_size;
+ gboolean failure;
+} AESContext;
+
+typedef gboolean (*purple_aes_crypt_func)(
+ const guchar *input, guchar *output, size_t len,
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size);
+
+static void
+purple_aes_init(PurpleCipherContext *context, void *extra)
+{
+ AESContext *ctx_data;
+
+ ctx_data = g_new0(AESContext, 1);
+ purple_cipher_context_set_data(context, ctx_data);
+
+ purple_cipher_context_reset(context, extra);
+}
+
+static void
+purple_aes_uninit(PurpleCipherContext *context)
+{
+ AESContext *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_aes_reset(PurpleCipherContext *context, void *extra)
+{
+ AESContext *ctx_data = purple_cipher_context_get_data(context);
+
+ g_return_if_fail(ctx_data != NULL);
+
+ memset(ctx_data->iv, 0, sizeof(ctx_data->iv));
+ memset(ctx_data->key, 0, sizeof(ctx_data->key));
+ ctx_data->key_size = 32; /* 256bit */
+ ctx_data->failure = FALSE;
+}
+
+static void
+purple_aes_set_option(PurpleCipherContext *context, const gchar *name,
+ void *value)
+{
+ AESContext *ctx_data = purple_cipher_context_get_data(context);
+
+ purple_debug_error("cipher-aes", "set_option not supported\n");
+ ctx_data->failure = TRUE;
+}
+
+static void
+purple_aes_set_iv(PurpleCipherContext *context, guchar *iv, size_t len)
+{
+ AESContext *ctx_data = purple_cipher_context_get_data(context);
+
+ if ((len > 0 && iv == NULL) ||
+ (len != 0 && len != sizeof(ctx_data->iv))) {
+ purple_debug_error("cipher-aes", "invalid IV length\n");
+ ctx_data->failure = TRUE;
+ return;
+ }
+
+ if (len == 0)
+ memset(ctx_data->iv, 0, sizeof(ctx_data->iv));
+ else
+ memcpy(ctx_data->iv, iv, len);
+}
+
+static void
+purple_aes_set_key(PurpleCipherContext *context, const guchar *key, size_t len)
+{
+ AESContext *ctx_data = purple_cipher_context_get_data(context);
+
+ if ((len > 0 && key == NULL) ||
+ (len != 0 && len != 16 && len != 24 && len != 32)) {
+ purple_debug_error("cipher-aes", "invalid key length\n");
+ ctx_data->failure = TRUE;
+ return;
+ }
+
+ ctx_data->key_size = len;
+ memset(ctx_data->key, 0, sizeof(ctx_data->key));
+ if (len > 0)
+ memcpy(ctx_data->key, key, len);
+}
+
+static guchar *
+purple_aes_pad_pkcs7(const guchar input[], size_t in_len, size_t *out_len)
+{
+ int padding_len, total_len;
+ guchar *padded;
+
+ g_return_val_if_fail(input != NULL, NULL);
+ g_return_val_if_fail(out_len != NULL, NULL);
+
+ padding_len = PURPLE_AES_BLOCK_SIZE - (in_len % PURPLE_AES_BLOCK_SIZE);
+ total_len = in_len + padding_len;
+ g_assert((total_len % PURPLE_AES_BLOCK_SIZE) == 0);
+
+ padded = g_new(guchar, total_len);
+ *out_len = total_len;
+
+ memcpy(padded, input, in_len);
+ memset(padded + in_len, padding_len, padding_len);
+
+ return padded;
+}
+
+static ssize_t
+purple_aes_unpad_pkcs7(guchar input[], size_t in_len)
+{
+ int padding_len, i;
+ size_t out_len;
+
+ g_return_val_if_fail(input != NULL, -1);
+ g_return_val_if_fail(in_len > 0, -1);
+
+ padding_len = input[in_len - 1];
+ if (padding_len <= 0 || padding_len > PURPLE_AES_BLOCK_SIZE ||
+ padding_len > in_len) {
+ purple_debug_warning("cipher-aes",
+ "Invalid padding length: %d (total %d) - "
+ "most probably, the key was invalid\n",
+ padding_len, in_len);
+ return -1;
+ }
+
+ out_len = in_len - padding_len;
+ for (i = 0; i < padding_len; i++) {
+ if (input[out_len + i] != padding_len) {
+ purple_debug_warning("cipher-aes",
+ "Padding doesn't match at pos %d (found %02x, "
+ "expected %02x) - "
+ "most probably, the key was invalid\n",
+ i, input[out_len + i], padding_len);
+ return -1;
+ }
+ }
+
+ memset(input + out_len, 0, padding_len);
+ return out_len;
+}
+
+#ifdef PURPLE_AES_USE_GNUTLS
+
+static gnutls_cipher_hd_t
+purple_aes_crypt_gnutls_init(guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32],
+ guint key_size)
+{
+ gnutls_cipher_hd_t handle;
+ gnutls_cipher_algorithm_t algorithm;
+ gnutls_datum_t key_info, iv_info;
+ int ret;
+
+ if (key_size == 16)
+ algorithm = GNUTLS_CIPHER_AES_128_CBC;
+ else if (key_size == 24)
+ algorithm = GNUTLS_CIPHER_AES_192_CBC;
+ else if (key_size == 32)
+ algorithm = GNUTLS_CIPHER_AES_256_CBC;
+ else
+ g_return_val_if_reached(NULL);
+
+ key_info.data = key;
+ key_info.size = key_size;
+
+ iv_info.data = iv;
+ iv_info.size = PURPLE_AES_BLOCK_SIZE;
+
+ ret = gnutls_cipher_init(&handle, algorithm, &key_info, &iv_info);
+ if (ret != 0) {
+ purple_debug_error("cipher-aes",
+ "gnutls_cipher_init failed: %d\n", ret);
+ return NULL;
+ }
+
+ return handle;
+}
+
+static gboolean
+purple_aes_encrypt_gnutls(const guchar *input, guchar *output, size_t len,
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size)
+{
+ gnutls_cipher_hd_t handle;
+ int ret;
+
+ handle = purple_aes_crypt_gnutls_init(iv, key, key_size);
+ if (handle == NULL)
+ return FALSE;
+
+ ret = gnutls_cipher_encrypt2(handle, input, len, output, len);
+ gnutls_cipher_deinit(handle);
+
+ if (ret != 0) {
+ purple_debug_error("cipher-aes",
+ "gnutls_cipher_encrypt2 failed: %d\n", ret);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+purple_aes_decrypt_gnutls(const guchar *input, guchar *output, size_t len,
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size)
+{
+ gnutls_cipher_hd_t handle;
+ int ret;
+
+ handle = purple_aes_crypt_gnutls_init(iv, key, key_size);
+ if (handle == NULL)
+ return FALSE;
+
+ ret = gnutls_cipher_decrypt2(handle, input, len, output, len);
+ gnutls_cipher_deinit(handle);
+
+ if (ret != 0) {
+ purple_debug_error("cipher-aes",
+ "gnutls_cipher_decrypt2 failed: %d\n", ret);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#endif /* PURPLE_AES_USE_GNUTLS */
+
+#ifdef PURPLE_AES_USE_NSS
+
+typedef struct
+{
+ PK11SlotInfo *slot;
+ PK11SymKey *sym_key;
+ SECItem *sec_param;
+ PK11Context *enc_context;
+} purple_aes_encrypt_nss_context;
+
+static void
+purple_aes_encrypt_nss_context_cleanup(purple_aes_encrypt_nss_context *ctx)
+{
+ g_return_if_fail(ctx != NULL);
+
+ if (ctx->enc_context != NULL)
+ PK11_DestroyContext(ctx->enc_context, TRUE);
+ if (ctx->sec_param != NULL)
+ SECITEM_FreeItem(ctx->sec_param, TRUE);
+ if (ctx->sym_key != NULL)
+ PK11_FreeSymKey(ctx->sym_key);
+ if (ctx->slot != NULL)
+ PK11_FreeSlot(ctx->slot);
+
+ memset(ctx, 0, sizeof(purple_aes_encrypt_nss_context));
+}
+
+static gboolean
+purple_aes_crypt_nss(const guchar *input, guchar *output, size_t len,
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
+ CK_ATTRIBUTE_TYPE operation)
+{
+ purple_aes_encrypt_nss_context ctx;
+ CK_MECHANISM_TYPE cipher_mech = CKM_AES_CBC;
+ SECItem key_item, iv_item;
+ SECStatus ret;
+ int outlen = 0;
+ unsigned int outlen_tmp = 0;
+
+ memset(&ctx, 0, sizeof(purple_aes_encrypt_nss_context));
+
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ purple_debug_error("cipher-aes",
+ "NSS_NoDB_Init failed: %d\n", PR_GetError());
+ return FALSE;
+ }
+
+ ctx.slot = PK11_GetBestSlot(cipher_mech, NULL);
+ if (ctx.slot == NULL) {
+ purple_debug_error("cipher-aes",
+ "PK11_GetBestSlot failed: %d\n", PR_GetError());
+ return FALSE;
+ }
+
+ key_item.type = siBuffer;
+ key_item.data = key;
+ key_item.len = key_size;
+ ctx.sym_key = PK11_ImportSymKey(ctx.slot, cipher_mech,
+ PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL);
+ if (ctx.sym_key == NULL) {
+ purple_debug_error("cipher-aes",
+ "PK11_ImportSymKey failed: %d\n", PR_GetError());
+ purple_aes_encrypt_nss_context_cleanup(&ctx);
+ return FALSE;
+ }
+
+ iv_item.type = siBuffer;
+ iv_item.data = iv;
+ iv_item.len = PURPLE_AES_BLOCK_SIZE;
+ ctx.sec_param = PK11_ParamFromIV(cipher_mech, &iv_item);
+ if (ctx.sec_param == NULL) {
+ purple_debug_error("cipher-aes",
+ "PK11_ParamFromIV failed: %d\n", PR_GetError());
+ purple_aes_encrypt_nss_context_cleanup(&ctx);
+ return FALSE;
+ }
+
+ ctx.enc_context = PK11_CreateContextBySymKey(cipher_mech, operation,
+ ctx.sym_key, ctx.sec_param);
+ if (ctx.enc_context == NULL) {
+ purple_debug_error("cipher-aes",
+ "PK11_CreateContextBySymKey failed: %d\n",
+ PR_GetError());
+ purple_aes_encrypt_nss_context_cleanup(&ctx);
+ return FALSE;
+ }
+
+ ret = PK11_CipherOp(ctx.enc_context, output, &outlen, len, input, len);
+ if (ret != SECSuccess) {
+ purple_debug_error("cipher-aes",
+ "PK11_CipherOp failed: %d\n", PR_GetError());
+ purple_aes_encrypt_nss_context_cleanup(&ctx);
+ return FALSE;
+ }
+
+ ret = PK11_DigestFinal(ctx.enc_context, output + outlen, &outlen_tmp,
+ len - outlen);
+ if (ret != SECSuccess) {
+ purple_debug_error("cipher-aes",
+ "PK11_DigestFinal failed: %d\n", PR_GetError());
+ purple_aes_encrypt_nss_context_cleanup(&ctx);
+ return FALSE;
+ }
+
+ purple_aes_encrypt_nss_context_cleanup(&ctx);
+
+ outlen += outlen_tmp;
+ if (outlen != len) {
+ purple_debug_error("cipher-aes",
+ "resulting length doesn't match: %d (expected: %d)\n",
+ outlen, len);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+purple_aes_encrypt_nss(const guchar *input, guchar *output, size_t len,
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size)
+{
+ return purple_aes_crypt_nss(input, output, len, iv, key, key_size,
+ CKA_ENCRYPT);
+}
+
+static gboolean
+purple_aes_decrypt_nss(const guchar *input, guchar *output, size_t len,
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size)
+{
+ return purple_aes_crypt_nss(input, output, len, iv, key, key_size,
+ CKA_DECRYPT);
+}
+
+#endif /* PURPLE_AES_USE_NSS */
+
+static ssize_t
+purple_aes_encrypt(PurpleCipherContext *context, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ AESContext *ctx_data = purple_cipher_context_get_data(context);
+ purple_aes_crypt_func encrypt_func;
+ guchar *input_padded;
+ size_t out_len = 0;
+ gboolean succ;
+
+ if (ctx_data->failure)
+ return -1;
+
+ input_padded = purple_aes_pad_pkcs7(input, in_len, &out_len);
+
+ if (out_len > out_size) {
+ purple_debug_error("cipher-aes", "Output buffer too small\n");
+ memset(input_padded, 0, out_len);
+ g_free(input_padded);
+ return -1;
+ }
+
+#if defined(PURPLE_AES_USE_GNUTLS)
+ encrypt_func = purple_aes_encrypt_gnutls;
+#elif defined(PURPLE_AES_USE_NSS)
+ encrypt_func = purple_aes_encrypt_nss;
+#else
+# error "No matching encrypt_func"
+#endif
+
+ succ = encrypt_func(input_padded, output, out_len, ctx_data->iv,
+ ctx_data->key, ctx_data->key_size);
+
+ memset(input_padded, 0, out_len);
+ g_free(input_padded);
+
+ if (!succ) {
+ memset(output, 0, out_len);
+ return -1;
+ }
+
+ return out_len;
+}
+
+static ssize_t
+purple_aes_decrypt(PurpleCipherContext *context, const guchar input[],
+ size_t in_len, guchar output[], size_t out_size)
+{
+ AESContext *ctx_data = purple_cipher_context_get_data(context);
+ purple_aes_crypt_func decrypt_func;
+ gboolean succ;
+ ssize_t out_len;
+
+ if (ctx_data->failure)
+ return -1;
+
+ if (in_len > out_size) {
+ purple_debug_error("cipher-aes", "Output buffer too small\n");
+ return -1;
+ }
+
+ if ((in_len % PURPLE_AES_BLOCK_SIZE) != 0 || in_len == 0) {
+ purple_debug_error("cipher-aes", "Malformed data\n");
+ return -1;
+ }
+
+#if defined(PURPLE_AES_USE_GNUTLS)
+ decrypt_func = purple_aes_decrypt_gnutls;
+#elif defined(PURPLE_AES_USE_NSS)
+ decrypt_func = purple_aes_decrypt_nss;
+#else
+# error "No matching encrypt_func"
+#endif
+
+ succ = decrypt_func(input, output, in_len, ctx_data->iv, ctx_data->key,
+ ctx_data->key_size);
+
+ if (!succ) {
+ memset(output, 0, in_len);
+ return -1;
+ }
+
+ out_len = purple_aes_unpad_pkcs7(output, in_len);
+ if (out_len < 0) {
+ memset(output, 0, in_len);
+ return -1;
+ }
+
+ return out_len;
+}
+
+static size_t
+purple_aes_get_key_size(PurpleCipherContext *context)
+{
+ AESContext *ctx_data = purple_cipher_context_get_data(context);
+
+ return ctx_data->key_size;
+}
+
+static void
+purple_aes_set_batch_mode(PurpleCipherContext *context,
+ PurpleCipherBatchMode mode)
+{
+ AESContext *ctx_data = purple_cipher_context_get_data(context);
+
+ if (mode == PURPLE_CIPHER_BATCH_MODE_CBC)
+ return;
+
+ purple_debug_error("cipher-aes", "unsupported batch mode\n");
+ ctx_data->failure = TRUE;
+}
+
+static PurpleCipherBatchMode
+purple_aes_get_batch_mode(PurpleCipherContext *context)
+{
+ return PURPLE_CIPHER_BATCH_MODE_CBC;
+}
+
+static size_t
+purple_aes_get_block_size(PurpleCipherContext *context)
+{
+ return PURPLE_AES_BLOCK_SIZE;
+}
+
+static PurpleCipherOps AESOps = {
+ purple_aes_set_option, /* set_option */
+ NULL, /* get_option */
+ purple_aes_init, /* init */
+ purple_aes_reset, /* reset */
+ NULL, /* reset_state */
+ purple_aes_uninit, /* uninit */
+ purple_aes_set_iv, /* set_iv */
+ NULL, /* append */
+ NULL, /* digest */
+ NULL, /* get_digest_size */
+ purple_aes_encrypt, /* encrypt */
+ purple_aes_decrypt, /* decrypt */
+ NULL, /* set_salt */
+ NULL, /* get_salt_size */
+ purple_aes_set_key, /* set_key */
+ purple_aes_get_key_size, /* get_key_size */
+ purple_aes_set_batch_mode, /* set_batch_mode */
+ purple_aes_get_batch_mode, /* get_batch_mode */
+ purple_aes_get_block_size, /* get_block_size */
+ NULL, NULL, NULL, NULL /* reserved */
+};
+
+PurpleCipherOps *
+purple_aes_cipher_get_ops(void) {
+ return &AESOps;
+}
diff --git a/libpurple/ciphers/ciphers.h b/libpurple/ciphers/ciphers.h
index 34c6b9aa7b..f7bb5e2edf 100644
--- a/libpurple/ciphers/ciphers.h
+++ b/libpurple/ciphers/ciphers.h
@@ -19,6 +19,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+/* aes.c */
+PurpleCipherOps * purple_aes_cipher_get_ops(void);
+
/* des.c */
PurpleCipherOps * purple_des_cipher_get_ops(void);
PurpleCipherOps * purple_des3_cipher_get_ops(void);
@@ -39,3 +42,25 @@ PurpleCipherOps * purple_pbkdf2_cipher_get_ops(void);
/* rc4.c */
PurpleCipherOps * purple_rc4_cipher_get_ops(void);
+
+static inline void purple_ciphers_register_all(void)
+{
+#if defined(HAVE_GNUTLS) || defined(HAVE_NSS)
+ purple_ciphers_register_cipher("aes", purple_aes_cipher_get_ops());
+#endif
+
+ purple_ciphers_register_cipher("des", purple_des_cipher_get_ops());
+ purple_ciphers_register_cipher("des3", purple_des3_cipher_get_ops());
+
+ purple_ciphers_register_cipher("md5", purple_md5_cipher_get_ops());
+ purple_ciphers_register_cipher("sha1", purple_sha1_cipher_get_ops());
+ purple_ciphers_register_cipher("sha256", purple_sha256_cipher_get_ops());
+
+ purple_ciphers_register_cipher("hmac", purple_hmac_cipher_get_ops());
+
+ purple_ciphers_register_cipher("md4", purple_md4_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/plugins/ciphertest.c b/libpurple/plugins/ciphertest.c
index 4b1e278f5a..f99a27b527 100644
--- a/libpurple/plugins/ciphertest.c
+++ b/libpurple/plugins/ciphertest.c
@@ -234,11 +234,13 @@ cipher_test_digest(void)
* PBKDF2 stuff
**************************************************************************/
-#include <nss.h>
-#include <secmod.h>
-#include <pk11func.h>
-#include <prerror.h>
-#include <secerr.h>
+#ifdef HAVE_NSS
+# include <nss.h>
+# include <secmod.h>
+# include <pk11func.h>
+# include <prerror.h>
+# include <secerr.h>
+#endif
typedef struct {
const gchar *hash;
@@ -259,6 +261,8 @@ pbkdf2_test pbkdf2_tests[] = {
{ NULL, 0, NULL, NULL, 0, NULL}
};
+#ifdef HAVE_NSS
+
static gchar*
cipher_pbkdf2_nss_sha1(const gchar *passphrase, const gchar *salt,
guint iter_count, guint out_len)
@@ -353,6 +357,8 @@ cipher_pbkdf2_nss_sha1(const gchar *passphrase, const gchar *salt,
return ret;
}
+#endif /* HAVE_NSS */
+
static void
cipher_test_pbkdf2(void)
{
@@ -364,7 +370,7 @@ cipher_test_pbkdf2(void)
context = purple_cipher_context_new_by_name("pbkdf2", NULL);
- while (pbkdf2_tests[i].answer) {
+ while (!fail && pbkdf2_tests[i].answer) {
pbkdf2_test *test = &pbkdf2_tests[i];
gchar digest[2 * 32 + 1 + 10];
gchar *digest_nss = NULL;
@@ -398,16 +404,25 @@ cipher_test_pbkdf2(void)
if (test->out_len != 16 && test->out_len != 32)
skip_nss = TRUE;
+#ifdef HAVE_NSS
if (!skip_nss) {
digest_nss = cipher_pbkdf2_nss_sha1(test->passphrase,
test->salt, test->iter_count, test->out_len);
}
+#else
+ skip_nss = TRUE;
+#endif
if (!ret) {
purple_debug_info("cipher-test", "\tnss test failed\n");
fail = TRUE;
}
+ purple_debug_info("cipher-test", "\tGot: %s\n", digest);
+ if (digest_nss)
+ purple_debug_info("cipher-test", "\tGot from NSS: %s\n", digest_nss);
+ purple_debug_info("cipher-test", "\tWanted: %s\n", test->answer);
+
if (g_strcmp0(digest, test->answer) == 0 &&
(skip_nss || g_strcmp0(digest, digest_nss) == 0)) {
purple_debug_info("cipher-test", "\tTest OK\n");
@@ -416,11 +431,6 @@ cipher_test_pbkdf2(void)
purple_debug_info("cipher-test", "\twrong answer\n");
fail = TRUE;
}
-
- purple_debug_info("cipher-test", "\tGot: %s\n", digest);
- if (digest_nss)
- purple_debug_info("cipher-test", "\tGot from NSS: %s\n", digest_nss);
- purple_debug_info("cipher-test", "\tWanted: %s\n", test->answer);
}
purple_cipher_context_destroy(context);
@@ -432,6 +442,132 @@ cipher_test_pbkdf2(void)
}
/**************************************************************************
+ * AES stuff
+ **************************************************************************/
+
+typedef struct {
+ const gchar *iv;
+ const gchar *key;
+ const gchar *plaintext;
+ const gchar *cipher;
+} aes_test;
+
+aes_test aes_tests[] = {
+ { NULL, "000102030405060708090a0b0c0d0e0f", "plaintext", "152e5b950e5f28fafadee9e96fcc59c9" },
+ { NULL, "000102030405060708090a0b0c0d0e0f", NULL, "954f64f2e4e86e9eee82d20216684899" },
+ { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f", NULL, "35d14e6d3e3a279cf01e343e34e7ded3" },
+ { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f", "plaintext", "19d1996e8c098cf3c94bba5dcf5bc57e" },
+ { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f1011121314151617", "plaintext", "e8fba0deae94f63fe72ae9b8ef128aed" },
+ { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "plaintext", "e2dc50f6c60b33ac3b5953b6503cb684" },
+ { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "W Szczebrzeszynie chrzaszcz brzmi w trzcinie i Szczebrzeszyn z tego slynie", "8fcc068964e3505f0c2fac61c24592e5c8a9582d3a3264217cf605e9fd1cb056e679e159c4ac3110e8ce6c76c6630d42658c566ba3750c0e6da385b3a9baaa8b3a735b4c1ecaacf58037c8c281e523d2" },
+ { NULL, NULL, NULL, NULL }
+};
+
+static void
+cipher_test_aes(void)
+{
+ PurpleCipherContext *context;
+ int i = 0;
+ gboolean fail = FALSE;
+
+ purple_debug_info("cipher-test", "Running AES tests\n");
+
+ context = purple_cipher_context_new_by_name("aes", NULL);
+ if (context == NULL) {
+ purple_debug_error("cipher-test", "AES cipher not found\n");
+ fail = TRUE;
+ }
+
+ while (!fail && aes_tests[i].cipher) {
+ aes_test *test = &aes_tests[i];
+ gsize key_size;
+ guchar *key;
+ guchar cipher[1024], decipher[1024];
+ ssize_t cipher_len, decipher_len;
+ gchar *cipher_b16, *deciphered;
+
+ purple_debug_info("cipher-test", "Test %02d:\n", i);
+ purple_debug_info("cipher-test", "\tTesting '%s' (%dbit) \n",
+ test->plaintext ? test->plaintext : "(null)",
+ strlen(test->key) * 8 / 2);
+
+ i++;
+
+ purple_cipher_context_reset(context, NULL);
+
+ if (test->iv) {
+ gsize iv_size;
+ guchar *iv = purple_base16_decode(test->iv, &iv_size);
+ g_assert(iv != NULL);
+ purple_cipher_context_set_iv(context, iv, iv_size);
+ g_free(iv);
+ }
+
+ key = purple_base16_decode(test->key, &key_size);
+ g_assert(key != NULL);
+ purple_cipher_context_set_key(context, key, key_size);
+ g_free(key);
+
+ if (purple_cipher_context_get_key_size(context) != key_size) {
+ purple_debug_info("cipher-test", "\tinvalid key size\n");
+ fail = TRUE;
+ continue;
+ }
+
+ cipher_len = purple_cipher_context_encrypt(context,
+ (const guchar*)(test->plaintext ? test->plaintext : ""),
+ test->plaintext ? (strlen(test->plaintext) + 1) : 0,
+ cipher, sizeof(cipher));
+ if (cipher_len < 0) {
+ purple_debug_info("cipher-test", "\tencryption failed\n");
+ fail = TRUE;
+ continue;
+ }
+
+ cipher_b16 = purple_base16_encode(cipher, cipher_len);
+
+ purple_debug_info("cipher-test", "\tGot: %s\n", cipher_b16);
+ purple_debug_info("cipher-test", "\tWanted: %s\n", test->cipher);
+
+ if (g_strcmp0(cipher_b16, test->cipher) != 0) {
+ purple_debug_info("cipher-test",
+ "\tencrypted data doesn't match\n");
+ g_free(cipher_b16);
+ fail = TRUE;
+ continue;
+ }
+ g_free(cipher_b16);
+
+ decipher_len = purple_cipher_context_decrypt(context,
+ cipher, cipher_len, decipher, sizeof(decipher));
+ if (decipher_len < 0) {
+ purple_debug_info("cipher-test", "\tdecryption failed\n");
+ fail = TRUE;
+ continue;
+ }
+
+ deciphered = (decipher_len > 0) ? (gchar*)decipher : NULL;
+
+ if (g_strcmp0(deciphered, test->plaintext) != 0) {
+ purple_debug_info("cipher-test",
+ "\tdecrypted data doesn't match\n");
+ fail = TRUE;
+ continue;
+ }
+
+ purple_debug_info("cipher-test", "\tTest OK\n");
+ }
+
+ if (context != NULL)
+ purple_cipher_context_destroy(context);
+
+ if (fail)
+ purple_debug_info("cipher-test", "AES tests FAILED\n\n");
+ else
+ purple_debug_info("cipher-test", "AES tests completed successfully\n\n");
+}
+
+/**************************************************************************
* Plugin stuff
**************************************************************************/
static gboolean
@@ -440,6 +576,7 @@ plugin_load(PurplePlugin *plugin) {
cipher_test_sha1();
cipher_test_digest();
cipher_test_pbkdf2();
+ cipher_test_aes();
return TRUE;
}