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 }