libpurple/ciphers/aescipher.c

changeset 35677
16b1b8711b89
parent 35537
82c3165b6201
child 35683
8b6b8a3b5039
--- 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);

mercurial