/pidgin/main: 16b1b8711b89: Mxit: partially switch to libpurple'...

Tomasz Wasilczyk twasilczyk at pidgin.im
Wed Apr 2 21:52:39 EDT 2014


Changeset: 16b1b8711b89403ef023a8aaf5297a9b2fe3e0dc
Author:	 Tomasz Wasilczyk <twasilczyk at pidgin.im>
Date:	 2014-04-03 03:52 +0200
Branch:	 default
URL: https://hg.pidgin.im/pidgin/main/rev/16b1b8711b89

Description:

Mxit: partially switch to libpurple's AES

diffstat:

 libpurple/ciphers/aescipher.c     |  102 ++++++++++++++++++++++++++++++-------
 libpurple/protocols/mxit/cipher.c |   64 +++++++++++++----------
 libpurple/protocols/mxit/cipher.h |    3 +-
 3 files changed, 120 insertions(+), 49 deletions(-)

diffs (truncated from 320 to 300 lines):

diff --git a/libpurple/ciphers/aescipher.c b/libpurple/ciphers/aescipher.c
--- a/libpurple/ciphers/aescipher.c
+++ b/libpurple/ciphers/aescipher.c
@@ -58,6 +58,7 @@ typedef struct {
 	guchar key[32];
 	guint key_size;
 	gboolean failure;
+	PurpleCipherBatchMode batch_mode;
 } PurpleAESCipherPrivate;
 
 /******************************************************************************
@@ -82,7 +83,8 @@ static GParamSpec *properties[PROP_LAST]
 
 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 @@ purple_aes_cipher_gnutls_crypt_init(guch
 
 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 @@ purple_aes_cipher_gnutls_encrypt(const g
 
 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 @@ purple_aes_cipher_nss_cleanup(PurpleAESC
 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 @@ purple_aes_cipher_nss_crypt(const guchar
 	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 @@ purple_aes_cipher_encrypt(PurpleCipher *
 	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 @@ purple_aes_cipher_encrypt(PurpleCipher *
 #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 @@ purple_aes_cipher_decrypt(PurpleCipher *
 #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 @@ purple_aes_cipher_set_batch_mode(PurpleC
 {
 	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 @@ purple_aes_cipher_class_init(PurpleAESCi
 
 	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);
diff --git a/libpurple/protocols/mxit/cipher.c b/libpurple/protocols/mxit/cipher.c
--- a/libpurple/protocols/mxit/cipher.c
+++ b/libpurple/protocols/mxit/cipher.c
@@ -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 @@ static char* transport_layer_key( struct
  *  @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 */
+	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);
 
-	/* 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];
+	/* 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);
 
-		Encrypt( (unsigned char*) pass->str + i, (unsigned char*) exkey, (unsigned char*) block );
-		g_string_append_len( encrypted, block, 16 );
-	}
-
-	/* now base64 encode the encrypted password */
-	base64 = purple_base64_encode( (unsigned char*) encrypted->str, encrypted->len );
-	g_string_free( encrypted, TRUE );
-



More information about the Commits mailing list