Mercurial > pidgin
diff libpurple/protocols/mxit/cipher.c @ 32819:2c6510167895 default tip
propagate from branch 'im.pidgin.pidgin.2.x.y' (head 3315c5dfbd0ad16511bdcf865e5b07c02d07df24)
to branch 'im.pidgin.pidgin' (head cbd1eda6bcbf0565ae7766396bb8f6f419cb6a9a)
| author | Elliott Sales de Andrade <qulogic@pidgin.im> |
|---|---|
| date | Sat, 02 Jun 2012 02:30:49 +0000 |
| parents | f75041cb3fec |
| children |
line wrap: on
line diff
--- a/libpurple/protocols/mxit/cipher.c Sat Jun 02 02:30:13 2012 +0000 +++ b/libpurple/protocols/mxit/cipher.c Sat Jun 02 02:30:49 2012 +0000 @@ -1,7 +1,7 @@ /* * MXit Protocol libPurple Plugin * - * -- user password encryption -- + * -- encryption -- * * Pieter Loubser <libpurple@mxit.com> * @@ -31,30 +31,70 @@ #include "aes.h" -/* password encryption */ +/* encryption */ #define INITIAL_KEY "6170383452343567" #define SECRET_HEADER "<mxit/>" +#define ENCRYPT_HEADER "<mxitencrypted ver=\"5.2\"/>" + + +/*------------------------------------------------------------------------ + * Add ISO10126 Padding to the data. + * + * @param data The data to pad. + */ +static void padding_add( GString* data ) +{ + unsigned int blocks = ( data->len / 16 ) + 1; + unsigned int padding = ( blocks * 16 ) - data->len; + + g_string_set_size( data, blocks * 16 ); + data->str[data->len - 1] = padding; +} /*------------------------------------------------------------------------ - * Pad the secret data using ISO10126 Padding. + * Remove ISO10126 Padding from the data. * - * @param secret The data to pad (caller must ensure buffer has enough space for padding) - * @return The total number of 128-bit blocks used + * @param data The data from which to remove padding. */ -static int pad_secret_data( char* secret ) +static void padding_remove( GString* data ) { - int blocks = 0; - int passlen; - int padding; + unsigned int padding; + + if ( data->len == 0 ) + return; + + padding = data->str[data->len - 1]; + g_string_truncate( data, data->len - padding ); +} + - passlen = strlen( secret ); - blocks = ( passlen / 16 ) + 1; - padding = ( blocks * 16 ) - passlen; - secret[passlen] = 0x50; - secret[(blocks * 16) - 1] = padding; +/*------------------------------------------------------------------------ + * Generate the Transport-Layer crypto key. + * (Note: this function is not-thread safe) + * + * @param session The MXit Session object + * @return The transport-layer crypto key. + */ +static char* transport_layer_key( struct MXitSession* session ) +{ + static char key[16 + 1]; + const char* password = purple_account_get_password( session->acc ); + int passlen = strlen( password ); - return blocks; + /* initialize with initial key */ + g_strlcpy( key, INITIAL_KEY, sizeof( key ) ); + + /* client key (8 bytes) */ + memcpy( key, session->clientkey, strlen( session->clientkey ) ); + + /* add last 8 characters of the PIN (no padding if less characters) */ + if ( passlen <= 8 ) + memcpy( key + 8, password, passlen ); + else + memcpy( key + 8, password + ( passlen - 8 ), 8 ); + + return key; } @@ -67,42 +107,131 @@ */ char* mxit_encrypt_password( struct MXitSession* session ) { - char key[64]; + char key[16 + 1]; char exkey[512]; - char pass[64]; + GString* pass = NULL; char encrypted[64]; char* base64; - int blocks; - int size; int i; purple_debug_info( MXIT_PLUGIN_ID, "mxit_encrypt_password\n" ); memset( encrypted, 0x00, sizeof( encrypted ) ); - memset( exkey, 0x00, sizeof( exkey ) ); - memset( pass, 0x58, sizeof( pass ) ); - pass[sizeof( pass ) - 1] = '\0'; - /* build the custom AES encryption key */ + /* 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 ); - /* build the custom data to be encrypted */ - g_strlcpy( pass, SECRET_HEADER, sizeof( pass ) ); - strcat( pass, session->acc->password ); + /* build the secret data to be encrypted: SECRET_HEADER + password */ + pass = g_string_new( SECRET_HEADER ); + g_string_append( pass, purple_account_get_password( session->acc) ); + padding_add( pass ); /* add ISO10126 padding */ - /* pad the secret data */ - blocks = pad_secret_data( pass ); - size = blocks * 16; - - /* now encrypt the password. we encrypt each block separately (ECB mode) */ - for ( i = 0; i < size; i += 16 ) - Encrypt( (unsigned char*) pass + i, (unsigned char*) exkey, (unsigned char*) encrypted + i ); + /* now encrypt the secret. we encrypt each block separately (ECB mode) */ + for ( i = 0; i < pass->len; i += 16 ) + Encrypt( (unsigned char*) pass->str + i, (unsigned char*) exkey, (unsigned char*) encrypted + i ); /* now base64 encode the encrypted password */ - base64 = purple_base64_encode( (unsigned char*) encrypted, size ); + base64 = purple_base64_encode( (unsigned char*) encrypted, pass->len ); + + g_string_free( pass, TRUE ); return base64; } + +/*------------------------------------------------------------------------ + * Decrypt a message using transport-layer encryption. + * + * @param session The MXit session object + * @param message The encrypted message data (is base64-encoded). + * @return The decrypted message. Must be g_free'd when no longer needed. + */ +char* mxit_decrypt_message( struct MXitSession* session, char* message ) +{ + guchar* raw_message; + gsize raw_len; + char exkey[512]; + GString* decoded = NULL; + int i; + + /* remove optional header: <mxitencrypted ver="5.2"/> */ + if ( strncmp( message, ENCRYPT_HEADER, strlen( ENCRYPT_HEADER ) ) == 0 ) + message += strlen( ENCRYPT_HEADER ); + + /* base64 decode the message */ + raw_message = purple_base64_decode( message, &raw_len ); + + /* build the AES key */ + ExpandKey( (unsigned char*) transport_layer_key( session ), (unsigned char*) exkey ); + + /* AES decrypt each block */ + decoded = g_string_sized_new( raw_len ); + for ( i = 0; i < raw_len; i += 16 ) { + char block[16]; + + Decrypt( (unsigned char*) raw_message + i, (unsigned char*) exkey, (unsigned char*) block ); + g_string_append_len( decoded, block, 16 ); + } + g_free( raw_message ); + + /* check that the decrypted message starts with header: <mxit/> */ + if ( strncmp( decoded->str, SECRET_HEADER, strlen( SECRET_HEADER ) != 0 ) ) { + g_string_free( decoded, TRUE ); + return NULL; /* message could not be decrypted */ + } + + /* remove ISO10126 padding */ + padding_remove( decoded ); + + /* remove encryption header */ + g_string_erase( decoded, 0, strlen( SECRET_HEADER ) ); + + return g_string_free( decoded, FALSE ); +} + + +/*------------------------------------------------------------------------ + * Encrypt a message using transport-layer encryption. + * + * @param session The MXit session object + * @param message The message data. + * @return The encrypted message. Must be g_free'd when no longer needed. + */ +char* mxit_encrypt_message( struct MXitSession* session, char* message ) +{ + GString* raw_message = NULL; + char exkey[512]; + GString* encoded = NULL; + gchar* base64; + int i; + + purple_debug_info( MXIT_PLUGIN_ID, "encrypt message: '%s'\n", message ); + + /* append encryption header to message data */ + raw_message = g_string_new( SECRET_HEADER ); + g_string_append( raw_message, message ); + padding_add( raw_message ); /* add ISO10126 padding */ + + /* build the AES key */ + ExpandKey( (unsigned char*) transport_layer_key( session ), (unsigned char*) exkey ); + + /* AES encrypt each block */ + encoded = g_string_sized_new( raw_message->len ); + for ( i = 0; i < raw_message->len; i += 16 ) { + char block[16]; + + Encrypt( (unsigned char*) raw_message->str + i, (unsigned char*) exkey, (unsigned char*) block ); + g_string_append_len( encoded, block, 16 ); + } + g_string_free( raw_message, TRUE ); + + /* base64 encode the encrypted message */ + base64 = purple_base64_encode( (unsigned char *) encoded->str, encoded->len ); + g_string_free( encoded, TRUE ); + + purple_debug_info( MXIT_PLUGIN_ID, "encrypted message: '%s'\n", base64 ); + + return base64; +}
