pidgin/pidgin

Mxit: partially switch to libpurple's AES

2014-04-03, Tomasz Wasilczyk
16b1b8711b89
Parents d9fdd45925ec
Children 97cb9646c26f
Mxit: partially switch to libpurple's AES
--- a/libpurple/ciphers/aescipher.c Thu Apr 03 00:47:13 2014 +0200
+++ b/libpurple/ciphers/aescipher.c Thu Apr 03 03:52:31 2014 +0200
@@ -58,6 +58,7 @@
guchar key[32];
guint key_size;
gboolean failure;
+ PurpleCipherBatchMode batch_mode;
} PurpleAESCipherPrivate;
/******************************************************************************
@@ -82,7 +83,8 @@
typedef gboolean (*purple_aes_cipher_crypt_func)(
const guchar *input, guchar *output, size_t len,
- guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size);
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
+ PurpleCipherBatchMode batch_mode);
static void
purple_aes_cipher_reset(PurpleCipher *cipher)
@@ -232,11 +234,32 @@
static gboolean
purple_aes_cipher_gnutls_encrypt(const guchar *input, guchar *output, size_t len,
- guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size)
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
+ PurpleCipherBatchMode batch_mode)
{
gnutls_cipher_hd_t handle;
int ret;
+ /* We have to simulate ECB mode, which is not supported by GnuTLS. */
+ if (batch_mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
+ size_t i;
+ for (i = 0; i < len / PURPLE_AES_BLOCK_SIZE; i++) {
+ int offset = i * PURPLE_AES_BLOCK_SIZE;
+ guchar iv_local[PURPLE_AES_BLOCK_SIZE];
+ gboolean succ;
+
+ memcpy(iv_local, iv, sizeof(iv_local));
+ succ = purple_aes_cipher_gnutls_encrypt(
+ input + offset, output + offset,
+ PURPLE_AES_BLOCK_SIZE,
+ iv_local, key, key_size,
+ PURPLE_CIPHER_BATCH_MODE_CBC);
+ if (!succ)
+ return FALSE;
+ }
+ return TRUE;
+ }
+
handle = purple_aes_cipher_gnutls_crypt_init(iv, key, key_size);
if (handle == NULL)
return FALSE;
@@ -255,11 +278,32 @@
static gboolean
purple_aes_cipher_gnutls_decrypt(const guchar *input, guchar *output, size_t len,
- guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size)
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
+ PurpleCipherBatchMode batch_mode)
{
gnutls_cipher_hd_t handle;
int ret;
+ /* We have to simulate ECB mode, which is not supported by GnuTLS. */
+ if (batch_mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
+ size_t i;
+ for (i = 0; i < len / PURPLE_AES_BLOCK_SIZE; i++) {
+ int offset = i * PURPLE_AES_BLOCK_SIZE;
+ guchar iv_local[PURPLE_AES_BLOCK_SIZE];
+ gboolean succ;
+
+ memcpy(iv_local, iv, sizeof(iv_local));
+ succ = purple_aes_cipher_gnutls_decrypt(
+ input + offset, output + offset,
+ PURPLE_AES_BLOCK_SIZE,
+ iv_local, key, key_size,
+ PURPLE_CIPHER_BATCH_MODE_CBC);
+ if (!succ)
+ return FALSE;
+ }
+ return TRUE;
+ }
+
handle = purple_aes_cipher_gnutls_crypt_init(iv, key, key_size);
if (handle == NULL)
return FALSE;
@@ -305,10 +349,9 @@
static gboolean
purple_aes_cipher_nss_crypt(const guchar *input, guchar *output, size_t len,
guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
- CK_ATTRIBUTE_TYPE operation)
+ CK_ATTRIBUTE_TYPE operation, CK_MECHANISM_TYPE cipher_mech)
{
PurpleAESCipherNSSContext context;
- CK_MECHANISM_TYPE cipher_mech = CKM_AES_CBC;
SECItem key_item, iv_item;
SECStatus ret;
int outlen = 0;
@@ -385,28 +428,41 @@
outlen += outlen_tmp;
if (outlen != (int)len) {
purple_debug_error("cipher-aes",
- "resulting length doesn't match: %d (expected: %lu)\n",
- outlen, len);
+ "resulting length doesn't match: %d (expected: %"
+ G_GSIZE_FORMAT ")\n", outlen, len);
return FALSE;
}
return TRUE;
}
+static CK_MECHANISM_TYPE
+purple_aes_cipher_nss_batch_mode(PurpleCipherBatchMode batch_mode)
+{
+ switch (batch_mode) {
+ case PURPLE_CIPHER_BATCH_MODE_CBC:
+ return CKM_AES_CBC;
+ case PURPLE_CIPHER_BATCH_MODE_ECB:
+ return CKM_AES_ECB;
+ }
+}
+
static gboolean
purple_aes_cipher_nss_encrypt(const guchar *input, guchar *output, size_t len,
- guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size)
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
+ PurpleCipherBatchMode batch_mode)
{
return purple_aes_cipher_nss_crypt(input, output, len, iv, key, key_size,
- CKA_ENCRYPT);
+ CKA_ENCRYPT, purple_aes_cipher_nss_batch_mode(batch_mode));
}
static gboolean
purple_aes_cipher_nss_decrypt(const guchar *input, guchar *output, size_t len,
- guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size)
+ guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size,
+ PurpleCipherBatchMode batch_mode)
{
return purple_aes_cipher_nss_crypt(input, output, len, iv, key, key_size,
- CKA_DECRYPT);
+ CKA_DECRYPT, purple_aes_cipher_nss_batch_mode(batch_mode));
}
#endif /* PURPLE_AES_USE_NSS */
@@ -427,7 +483,8 @@
input_padded = purple_aes_cipher_pad_pkcs7(input, in_len, &out_len);
if (out_len > out_size) {
- purple_debug_error("cipher-aes", "Output buffer too small\n");
+ purple_debug_error("cipher-aes",
+ "Output buffer too small (%d > %d)", out_len, out_size);
memset(input_padded, 0, out_len);
g_free(input_padded);
return -1;
@@ -443,7 +500,7 @@
#endif
succ = encrypt_func(input_padded, output, out_len, priv->iv,
- priv->key, priv->key_size);
+ priv->key, priv->key_size, priv->batch_mode);
memset(input_padded, 0, out_len);
g_free(input_padded);
@@ -488,7 +545,7 @@
#endif
succ = decrypt_func(input, output, in_len, priv->iv, priv->key,
- priv->key_size);
+ priv->key_size, priv->batch_mode);
if (!succ) {
memset(output, 0, in_len);
@@ -518,18 +575,24 @@
{
PurpleAESCipherPrivate *priv = PURPLE_AES_CIPHER_GET_PRIVATE(cipher);
- if (mode != PURPLE_CIPHER_BATCH_MODE_CBC) {
+ if (mode != PURPLE_CIPHER_BATCH_MODE_CBC &&
+ mode != PURPLE_CIPHER_BATCH_MODE_ECB)
+ {
purple_debug_error("cipher-aes", "unsupported batch mode\n");
priv->failure = TRUE;
}
+ priv->batch_mode = mode;
+
g_object_notify_by_pspec(G_OBJECT(cipher), properties[PROP_BATCH_MODE]);
}
static PurpleCipherBatchMode
purple_aes_cipher_get_batch_mode(PurpleCipher *cipher)
{
- return PURPLE_CIPHER_BATCH_MODE_CBC;
+ PurpleAESCipherPrivate *priv = PURPLE_AES_CIPHER_GET_PRIVATE(cipher);
+
+ return priv->batch_mode;
}
static size_t
@@ -605,9 +668,10 @@
g_type_class_add_private(klass, sizeof(PurpleAESCipherPrivate));
- properties[PROP_BATCH_MODE] = g_param_spec_enum("batch-mode", "batch-mode",
- "batch-mode", PURPLE_TYPE_CIPHER_BATCH_MODE, 0,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ properties[PROP_BATCH_MODE] = g_param_spec_enum("batch-mode",
+ "batch-mode", "batch-mode", PURPLE_TYPE_CIPHER_BATCH_MODE,
+ PURPLE_CIPHER_BATCH_MODE_CBC,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
properties[PROP_IV] = g_param_spec_string("iv", "iv", "iv", NULL,
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
--- a/libpurple/protocols/mxit/cipher.c Thu Apr 03 00:47:13 2014 +0200
+++ b/libpurple/protocols/mxit/cipher.c Thu Apr 03 03:52:31 2014 +0200
@@ -25,6 +25,8 @@
#include "internal.h"
#include "debug.h"
+#include "libpurple/cipher.h"
+#include "ciphers/aescipher.h"
#include "mxit.h"
#include "cipher.h"
@@ -105,43 +107,47 @@
* @param session The MXit session object
* @return The encrypted & encoded password. Must be g_free'd when no longer needed.
*/
-char* mxit_encrypt_password( struct MXitSession* session )
+gchar *
+mxit_encrypt_password(struct MXitSession* session)
{
- char key[16 + 1];
- char exkey[512];
- GString* pass = NULL;
- GString* encrypted = NULL;
- char* base64;
- unsigned int i;
+ guchar key[16];
+ size_t clientkey_len, header_len, pass_len, plaintext_len;
+ const gchar *plaintext_passwd;
+ guchar *plaintext;
+ guchar encrypted[64]; /* shouldn't be longer than 17 */
+ PurpleCipher *cipher;
+ ssize_t encrypted_size;
- purple_debug_info( MXIT_PLUGIN_ID, "mxit_encrypt_password\n" );
+ purple_debug_info(MXIT_PLUGIN_ID, "mxit_encrypt_password");
/* build the AES encryption key */
- g_strlcpy( key, INITIAL_KEY, sizeof( key ) );
- memcpy( key, session->clientkey, strlen( session->clientkey ) );
- ExpandKey( (unsigned char*) key, (unsigned char*) exkey );
+ g_assert(strlen(INITIAL_KEY) == sizeof(key));
+ memcpy(key, INITIAL_KEY, sizeof(key));
+ clientkey_len = strlen(session->clientkey);
+ if (clientkey_len > sizeof(key))
+ clientkey_len = sizeof(key);
+ memcpy(key, session->clientkey, clientkey_len);
/* build the secret data to be encrypted: SECRET_HEADER + password */
- pass = g_string_new( SECRET_HEADER );
- g_string_append( pass, purple_connection_get_password( session->con ) );
- padding_add( pass ); /* add ISO10126 padding */
-
- /* now encrypt the secret. we encrypt each block separately (ECB mode) */
- encrypted = g_string_sized_new( pass->len );
- for ( i = 0; i < pass->len; i += 16 ) {
- char block[16];
+ plaintext_passwd = purple_connection_get_password(session->con);
+ g_return_val_if_fail(plaintext_passwd, NULL);
+ pass_len = strlen(plaintext_passwd);
+ header_len = strlen(SECRET_HEADER);
+ /* Trailing NUL, just to be safe. But PKCS#7 seems to be enough. */
+ plaintext_len = header_len + pass_len + 1;
+ plaintext = g_new0(guchar, plaintext_len);
+ memcpy(plaintext, SECRET_HEADER, header_len);
+ memcpy(plaintext + header_len, plaintext_passwd, pass_len);
- Encrypt( (unsigned char*) pass->str + i, (unsigned char*) exkey, (unsigned char*) block );
- g_string_append_len( encrypted, block, 16 );
- }
+ /* encrypt */
+ cipher = purple_aes_cipher_new();
+ purple_cipher_set_key(cipher, key, sizeof(key));
+ purple_cipher_set_batch_mode(cipher, PURPLE_CIPHER_BATCH_MODE_ECB);
+ encrypted_size = purple_cipher_encrypt(cipher,
+ plaintext, plaintext_len, encrypted, sizeof(encrypted));
+ g_return_val_if_fail(encrypted_size > 0, NULL);
- /* now base64 encode the encrypted password */
- base64 = purple_base64_encode( (unsigned char*) encrypted->str, encrypted->len );
- g_string_free( encrypted, TRUE );
-
- g_string_free( pass, TRUE );
-
- return base64;
+ return purple_base64_encode(encrypted, encrypted_size);
}
--- a/libpurple/protocols/mxit/cipher.h Thu Apr 03 00:47:13 2014 +0200
+++ b/libpurple/protocols/mxit/cipher.h Thu Apr 03 03:52:31 2014 +0200
@@ -30,7 +30,8 @@
struct MXitSession;
-char* mxit_encrypt_password( struct MXitSession* session );
+gchar *
+mxit_encrypt_password(struct MXitSession* session);
char* mxit_decrypt_message( struct MXitSession* session, char* message );
char* mxit_encrypt_message( struct MXitSession* session, char* message );