From 900cce122eae3a0948143586ab115e8708aa4c81 Mon Sep 17 00:00:00 2001 From: Tomasz Wasilczyk Date: Thu, 9 May 2013 20:24:26 +0200 Subject: AES support --- libpurple/Makefile.am | 2 + libpurple/Makefile.mingw | 1 + libpurple/cipher.c | 10 +- libpurple/ciphers/Makefile.am | 12 +- libpurple/ciphers/aes.c | 566 +++++++++++++++++++++++++++++++++++++++++ libpurple/ciphers/ciphers.h | 25 ++ libpurple/plugins/ciphertest.c | 159 +++++++++++- 7 files changed, 754 insertions(+), 21 deletions(-) create mode 100644 libpurple/ciphers/aes.c 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 + */ + +#include "internal.h" +#include "cipher.h" +#include "ciphers.h" +#include "debug.h" + +#if defined(HAVE_GNUTLS) +# define PURPLE_AES_USE_GNUTLS 1 +# include +# include +#elif defined(HAVE_NSS) +# define PURPLE_AES_USE_NSS 1 +# include +# include +# include +#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 -#include -#include -#include -#include +#ifdef HAVE_NSS +# include +# include +# include +# include +# include +#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); @@ -431,6 +441,132 @@ cipher_test_pbkdf2(void) purple_debug_info("cipher-test", "PBKDF2 tests completed successfully\n\n"); } +/************************************************************************** + * 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 **************************************************************************/ @@ -440,6 +576,7 @@ plugin_load(PurplePlugin *plugin) { cipher_test_sha1(); cipher_test_digest(); cipher_test_pbkdf2(); + cipher_test_aes(); return TRUE; } -- cgit v1.2.1