Mercurial > pidgin
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 32818:01ff09d4a463 | 32819:2c6510167895 |
|---|---|
| 1 /* | 1 /* |
| 2 * MXit Protocol libPurple Plugin | 2 * MXit Protocol libPurple Plugin |
| 3 * | 3 * |
| 4 * -- user password encryption -- | 4 * -- encryption -- |
| 5 * | 5 * |
| 6 * Pieter Loubser <libpurple@mxit.com> | 6 * Pieter Loubser <libpurple@mxit.com> |
| 7 * | 7 * |
| 8 * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd. | 8 * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd. |
| 9 * <http://www.mxitlifestyle.com> | 9 * <http://www.mxitlifestyle.com> |
| 29 #include "mxit.h" | 29 #include "mxit.h" |
| 30 #include "cipher.h" | 30 #include "cipher.h" |
| 31 #include "aes.h" | 31 #include "aes.h" |
| 32 | 32 |
| 33 | 33 |
| 34 /* password encryption */ | 34 /* encryption */ |
| 35 #define INITIAL_KEY "6170383452343567" | 35 #define INITIAL_KEY "6170383452343567" |
| 36 #define SECRET_HEADER "<mxit/>" | 36 #define SECRET_HEADER "<mxit/>" |
| 37 | 37 #define ENCRYPT_HEADER "<mxitencrypted ver=\"5.2\"/>" |
| 38 | 38 |
| 39 /*------------------------------------------------------------------------ | 39 |
| 40 * Pad the secret data using ISO10126 Padding. | 40 /*------------------------------------------------------------------------ |
| 41 * | 41 * Add ISO10126 Padding to the data. |
| 42 * @param secret The data to pad (caller must ensure buffer has enough space for padding) | 42 * |
| 43 * @return The total number of 128-bit blocks used | 43 * @param data The data to pad. |
| 44 */ | 44 */ |
| 45 static int pad_secret_data( char* secret ) | 45 static void padding_add( GString* data ) |
| 46 { | 46 { |
| 47 int blocks = 0; | 47 unsigned int blocks = ( data->len / 16 ) + 1; |
| 48 int passlen; | 48 unsigned int padding = ( blocks * 16 ) - data->len; |
| 49 int padding; | 49 |
| 50 | 50 g_string_set_size( data, blocks * 16 ); |
| 51 passlen = strlen( secret ); | 51 data->str[data->len - 1] = padding; |
| 52 blocks = ( passlen / 16 ) + 1; | 52 } |
| 53 padding = ( blocks * 16 ) - passlen; | 53 |
| 54 secret[passlen] = 0x50; | 54 |
| 55 secret[(blocks * 16) - 1] = padding; | 55 /*------------------------------------------------------------------------ |
| 56 | 56 * Remove ISO10126 Padding from the data. |
| 57 return blocks; | 57 * |
| 58 * @param data The data from which to remove padding. | |
| 59 */ | |
| 60 static void padding_remove( GString* data ) | |
| 61 { | |
| 62 unsigned int padding; | |
| 63 | |
| 64 if ( data->len == 0 ) | |
| 65 return; | |
| 66 | |
| 67 padding = data->str[data->len - 1]; | |
| 68 g_string_truncate( data, data->len - padding ); | |
| 69 } | |
| 70 | |
| 71 | |
| 72 /*------------------------------------------------------------------------ | |
| 73 * Generate the Transport-Layer crypto key. | |
| 74 * (Note: this function is not-thread safe) | |
| 75 * | |
| 76 * @param session The MXit Session object | |
| 77 * @return The transport-layer crypto key. | |
| 78 */ | |
| 79 static char* transport_layer_key( struct MXitSession* session ) | |
| 80 { | |
| 81 static char key[16 + 1]; | |
| 82 const char* password = purple_account_get_password( session->acc ); | |
| 83 int passlen = strlen( password ); | |
| 84 | |
| 85 /* initialize with initial key */ | |
| 86 g_strlcpy( key, INITIAL_KEY, sizeof( key ) ); | |
| 87 | |
| 88 /* client key (8 bytes) */ | |
| 89 memcpy( key, session->clientkey, strlen( session->clientkey ) ); | |
| 90 | |
| 91 /* add last 8 characters of the PIN (no padding if less characters) */ | |
| 92 if ( passlen <= 8 ) | |
| 93 memcpy( key + 8, password, passlen ); | |
| 94 else | |
| 95 memcpy( key + 8, password + ( passlen - 8 ), 8 ); | |
| 96 | |
| 97 return key; | |
| 58 } | 98 } |
| 59 | 99 |
| 60 | 100 |
| 61 /*------------------------------------------------------------------------ | 101 /*------------------------------------------------------------------------ |
| 62 * Encrypt the user's cleartext password using the AES 128-bit (ECB) | 102 * Encrypt the user's cleartext password using the AES 128-bit (ECB) |
| 65 * @param session The MXit session object | 105 * @param session The MXit session object |
| 66 * @return The encrypted & encoded password. Must be g_free'd when no longer needed. | 106 * @return The encrypted & encoded password. Must be g_free'd when no longer needed. |
| 67 */ | 107 */ |
| 68 char* mxit_encrypt_password( struct MXitSession* session ) | 108 char* mxit_encrypt_password( struct MXitSession* session ) |
| 69 { | 109 { |
| 70 char key[64]; | 110 char key[16 + 1]; |
| 71 char exkey[512]; | 111 char exkey[512]; |
| 72 char pass[64]; | 112 GString* pass = NULL; |
| 73 char encrypted[64]; | 113 char encrypted[64]; |
| 74 char* base64; | 114 char* base64; |
| 75 int blocks; | |
| 76 int size; | |
| 77 int i; | 115 int i; |
| 78 | 116 |
| 79 purple_debug_info( MXIT_PLUGIN_ID, "mxit_encrypt_password\n" ); | 117 purple_debug_info( MXIT_PLUGIN_ID, "mxit_encrypt_password\n" ); |
| 80 | 118 |
| 81 memset( encrypted, 0x00, sizeof( encrypted ) ); | 119 memset( encrypted, 0x00, sizeof( encrypted ) ); |
| 82 memset( exkey, 0x00, sizeof( exkey ) ); | 120 |
| 83 memset( pass, 0x58, sizeof( pass ) ); | 121 /* build the AES encryption key */ |
| 84 pass[sizeof( pass ) - 1] = '\0'; | |
| 85 | |
| 86 /* build the custom AES encryption key */ | |
| 87 g_strlcpy( key, INITIAL_KEY, sizeof( key ) ); | 122 g_strlcpy( key, INITIAL_KEY, sizeof( key ) ); |
| 88 memcpy( key, session->clientkey, strlen( session->clientkey ) ); | 123 memcpy( key, session->clientkey, strlen( session->clientkey ) ); |
| 89 ExpandKey( (unsigned char*) key, (unsigned char*) exkey ); | 124 ExpandKey( (unsigned char*) key, (unsigned char*) exkey ); |
| 90 | 125 |
| 91 /* build the custom data to be encrypted */ | 126 /* build the secret data to be encrypted: SECRET_HEADER + password */ |
| 92 g_strlcpy( pass, SECRET_HEADER, sizeof( pass ) ); | 127 pass = g_string_new( SECRET_HEADER ); |
| 93 strcat( pass, session->acc->password ); | 128 g_string_append( pass, purple_account_get_password( session->acc) ); |
| 94 | 129 padding_add( pass ); /* add ISO10126 padding */ |
| 95 /* pad the secret data */ | 130 |
| 96 blocks = pad_secret_data( pass ); | 131 /* now encrypt the secret. we encrypt each block separately (ECB mode) */ |
| 97 size = blocks * 16; | 132 for ( i = 0; i < pass->len; i += 16 ) |
| 98 | 133 Encrypt( (unsigned char*) pass->str + i, (unsigned char*) exkey, (unsigned char*) encrypted + i ); |
| 99 /* now encrypt the password. we encrypt each block separately (ECB mode) */ | |
| 100 for ( i = 0; i < size; i += 16 ) | |
| 101 Encrypt( (unsigned char*) pass + i, (unsigned char*) exkey, (unsigned char*) encrypted + i ); | |
| 102 | 134 |
| 103 /* now base64 encode the encrypted password */ | 135 /* now base64 encode the encrypted password */ |
| 104 base64 = purple_base64_encode( (unsigned char*) encrypted, size ); | 136 base64 = purple_base64_encode( (unsigned char*) encrypted, pass->len ); |
| 137 | |
| 138 g_string_free( pass, TRUE ); | |
| 105 | 139 |
| 106 return base64; | 140 return base64; |
| 107 } | 141 } |
| 108 | 142 |
| 143 | |
| 144 /*------------------------------------------------------------------------ | |
| 145 * Decrypt a message using transport-layer encryption. | |
| 146 * | |
| 147 * @param session The MXit session object | |
| 148 * @param message The encrypted message data (is base64-encoded). | |
| 149 * @return The decrypted message. Must be g_free'd when no longer needed. | |
| 150 */ | |
| 151 char* mxit_decrypt_message( struct MXitSession* session, char* message ) | |
| 152 { | |
| 153 guchar* raw_message; | |
| 154 gsize raw_len; | |
| 155 char exkey[512]; | |
| 156 GString* decoded = NULL; | |
| 157 int i; | |
| 158 | |
| 159 /* remove optional header: <mxitencrypted ver="5.2"/> */ | |
| 160 if ( strncmp( message, ENCRYPT_HEADER, strlen( ENCRYPT_HEADER ) ) == 0 ) | |
| 161 message += strlen( ENCRYPT_HEADER ); | |
| 162 | |
| 163 /* base64 decode the message */ | |
| 164 raw_message = purple_base64_decode( message, &raw_len ); | |
| 165 | |
| 166 /* build the AES key */ | |
| 167 ExpandKey( (unsigned char*) transport_layer_key( session ), (unsigned char*) exkey ); | |
| 168 | |
| 169 /* AES decrypt each block */ | |
| 170 decoded = g_string_sized_new( raw_len ); | |
| 171 for ( i = 0; i < raw_len; i += 16 ) { | |
| 172 char block[16]; | |
| 173 | |
| 174 Decrypt( (unsigned char*) raw_message + i, (unsigned char*) exkey, (unsigned char*) block ); | |
| 175 g_string_append_len( decoded, block, 16 ); | |
| 176 } | |
| 177 g_free( raw_message ); | |
| 178 | |
| 179 /* check that the decrypted message starts with header: <mxit/> */ | |
| 180 if ( strncmp( decoded->str, SECRET_HEADER, strlen( SECRET_HEADER ) != 0 ) ) { | |
| 181 g_string_free( decoded, TRUE ); | |
| 182 return NULL; /* message could not be decrypted */ | |
| 183 } | |
| 184 | |
| 185 /* remove ISO10126 padding */ | |
| 186 padding_remove( decoded ); | |
| 187 | |
| 188 /* remove encryption header */ | |
| 189 g_string_erase( decoded, 0, strlen( SECRET_HEADER ) ); | |
| 190 | |
| 191 return g_string_free( decoded, FALSE ); | |
| 192 } | |
| 193 | |
| 194 | |
| 195 /*------------------------------------------------------------------------ | |
| 196 * Encrypt a message using transport-layer encryption. | |
| 197 * | |
| 198 * @param session The MXit session object | |
| 199 * @param message The message data. | |
| 200 * @return The encrypted message. Must be g_free'd when no longer needed. | |
| 201 */ | |
| 202 char* mxit_encrypt_message( struct MXitSession* session, char* message ) | |
| 203 { | |
| 204 GString* raw_message = NULL; | |
| 205 char exkey[512]; | |
| 206 GString* encoded = NULL; | |
| 207 gchar* base64; | |
| 208 int i; | |
| 209 | |
| 210 purple_debug_info( MXIT_PLUGIN_ID, "encrypt message: '%s'\n", message ); | |
| 211 | |
| 212 /* append encryption header to message data */ | |
| 213 raw_message = g_string_new( SECRET_HEADER ); | |
| 214 g_string_append( raw_message, message ); | |
| 215 padding_add( raw_message ); /* add ISO10126 padding */ | |
| 216 | |
| 217 /* build the AES key */ | |
| 218 ExpandKey( (unsigned char*) transport_layer_key( session ), (unsigned char*) exkey ); | |
| 219 | |
| 220 /* AES encrypt each block */ | |
| 221 encoded = g_string_sized_new( raw_message->len ); | |
| 222 for ( i = 0; i < raw_message->len; i += 16 ) { | |
| 223 char block[16]; | |
| 224 | |
| 225 Encrypt( (unsigned char*) raw_message->str + i, (unsigned char*) exkey, (unsigned char*) block ); | |
| 226 g_string_append_len( encoded, block, 16 ); | |
| 227 } | |
| 228 g_string_free( raw_message, TRUE ); | |
| 229 | |
| 230 /* base64 encode the encrypted message */ | |
| 231 base64 = purple_base64_encode( (unsigned char *) encoded->str, encoded->len ); | |
| 232 g_string_free( encoded, TRUE ); | |
| 233 | |
| 234 purple_debug_info( MXIT_PLUGIN_ID, "encrypted message: '%s'\n", base64 ); | |
| 235 | |
| 236 return base64; | |
| 237 } |
