| |
1 /* |
| |
2 * purple |
| |
3 * |
| |
4 * Purple is the legal property of its developers, whose names are too numerous |
| |
5 * to list here. Please refer to the COPYRIGHT file distributed with this |
| |
6 * source distribution. |
| |
7 * |
| |
8 * This program is free software; you can redistribute it and/or modify |
| |
9 * it under the terms of the GNU General Public License as published by |
| |
10 * the Free Software Foundation; either version 2 of the License, or |
| |
11 * (at your option) any later version. |
| |
12 * |
| |
13 * This program is distributed in the hope that it will be useful, |
| |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
16 * GNU General Public License for more details. |
| |
17 * |
| |
18 * You should have received a copy of the GNU General Public License |
| |
19 * along with this program; if not, write to the Free Software |
| |
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
| |
21 * |
| |
22 * Written by Tomek Wasilczyk <tomkiewicz@cpw.pidgin.im> |
| |
23 */ |
| |
24 |
| |
25 #include "internal.h" |
| |
26 #include "cipher.h" |
| |
27 #include "ciphers.h" |
| |
28 #include "debug.h" |
| |
29 |
| |
30 #if defined(HAVE_GNUTLS) |
| |
31 # define PURPLE_AES_USE_GNUTLS 1 |
| |
32 # include <gnutls/gnutls.h> |
| |
33 # include <gnutls/crypto.h> |
| |
34 #elif defined(HAVE_NSS) |
| |
35 # define PURPLE_AES_USE_NSS 1 |
| |
36 # include <nss.h> |
| |
37 # include <pk11pub.h> |
| |
38 # include <prerror.h> |
| |
39 #else |
| |
40 # error "No GnuTLS or NSS support" |
| |
41 #endif |
| |
42 |
| |
43 /* 128bit */ |
| |
44 #define PURPLE_AES_BLOCK_SIZE 16 |
| |
45 |
| |
46 typedef struct |
| |
47 { |
| |
48 guchar iv[PURPLE_AES_BLOCK_SIZE]; |
| |
49 guchar key[32]; |
| |
50 guint key_size; |
| |
51 gboolean failure; |
| |
52 } AESContext; |
| |
53 |
| |
54 typedef gboolean (*purple_aes_crypt_func)( |
| |
55 const guchar *input, guchar *output, size_t len, |
| |
56 guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size); |
| |
57 |
| |
58 static void |
| |
59 purple_aes_init(PurpleCipherContext *context, void *extra) |
| |
60 { |
| |
61 AESContext *ctx_data; |
| |
62 |
| |
63 ctx_data = g_new0(AESContext, 1); |
| |
64 purple_cipher_context_set_data(context, ctx_data); |
| |
65 |
| |
66 purple_cipher_context_reset(context, extra); |
| |
67 } |
| |
68 |
| |
69 static void |
| |
70 purple_aes_uninit(PurpleCipherContext *context) |
| |
71 { |
| |
72 AESContext *ctx_data; |
| |
73 |
| |
74 purple_cipher_context_reset(context, NULL); |
| |
75 |
| |
76 ctx_data = purple_cipher_context_get_data(context); |
| |
77 g_free(ctx_data); |
| |
78 purple_cipher_context_set_data(context, NULL); |
| |
79 } |
| |
80 |
| |
81 static void |
| |
82 purple_aes_reset(PurpleCipherContext *context, void *extra) |
| |
83 { |
| |
84 AESContext *ctx_data = purple_cipher_context_get_data(context); |
| |
85 |
| |
86 g_return_if_fail(ctx_data != NULL); |
| |
87 |
| |
88 memset(ctx_data->iv, 0, sizeof(ctx_data->iv)); |
| |
89 memset(ctx_data->key, 0, sizeof(ctx_data->key)); |
| |
90 ctx_data->key_size = 32; /* 256bit */ |
| |
91 ctx_data->failure = FALSE; |
| |
92 } |
| |
93 |
| |
94 static void |
| |
95 purple_aes_set_option(PurpleCipherContext *context, const gchar *name, |
| |
96 void *value) |
| |
97 { |
| |
98 AESContext *ctx_data = purple_cipher_context_get_data(context); |
| |
99 |
| |
100 purple_debug_error("cipher-aes", "set_option not supported\n"); |
| |
101 ctx_data->failure = TRUE; |
| |
102 } |
| |
103 |
| |
104 static void |
| |
105 purple_aes_set_iv(PurpleCipherContext *context, guchar *iv, size_t len) |
| |
106 { |
| |
107 AESContext *ctx_data = purple_cipher_context_get_data(context); |
| |
108 |
| |
109 if ((len > 0 && iv == NULL) || |
| |
110 (len != 0 && len != sizeof(ctx_data->iv))) { |
| |
111 purple_debug_error("cipher-aes", "invalid IV length\n"); |
| |
112 ctx_data->failure = TRUE; |
| |
113 return; |
| |
114 } |
| |
115 |
| |
116 if (len == 0) |
| |
117 memset(ctx_data->iv, 0, sizeof(ctx_data->iv)); |
| |
118 else |
| |
119 memcpy(ctx_data->iv, iv, len); |
| |
120 } |
| |
121 |
| |
122 static void |
| |
123 purple_aes_set_key(PurpleCipherContext *context, const guchar *key, size_t len) |
| |
124 { |
| |
125 AESContext *ctx_data = purple_cipher_context_get_data(context); |
| |
126 |
| |
127 if ((len > 0 && key == NULL) || |
| |
128 (len != 0 && len != 16 && len != 24 && len != 32)) { |
| |
129 purple_debug_error("cipher-aes", "invalid key length\n"); |
| |
130 ctx_data->failure = TRUE; |
| |
131 return; |
| |
132 } |
| |
133 |
| |
134 ctx_data->key_size = len; |
| |
135 memset(ctx_data->key, 0, sizeof(ctx_data->key)); |
| |
136 if (len > 0) |
| |
137 memcpy(ctx_data->key, key, len); |
| |
138 } |
| |
139 |
| |
140 static guchar * |
| |
141 purple_aes_pad_pkcs7(const guchar input[], size_t in_len, size_t *out_len) |
| |
142 { |
| |
143 int padding_len, total_len; |
| |
144 guchar *padded; |
| |
145 |
| |
146 g_return_val_if_fail(input != NULL, NULL); |
| |
147 g_return_val_if_fail(out_len != NULL, NULL); |
| |
148 |
| |
149 padding_len = PURPLE_AES_BLOCK_SIZE - (in_len % PURPLE_AES_BLOCK_SIZE); |
| |
150 total_len = in_len + padding_len; |
| |
151 g_assert((total_len % PURPLE_AES_BLOCK_SIZE) == 0); |
| |
152 |
| |
153 padded = g_new(guchar, total_len); |
| |
154 *out_len = total_len; |
| |
155 |
| |
156 memcpy(padded, input, in_len); |
| |
157 memset(padded + in_len, padding_len, padding_len); |
| |
158 |
| |
159 return padded; |
| |
160 } |
| |
161 |
| |
162 static ssize_t |
| |
163 purple_aes_unpad_pkcs7(guchar input[], size_t in_len) |
| |
164 { |
| |
165 int padding_len, i; |
| |
166 size_t out_len; |
| |
167 |
| |
168 g_return_val_if_fail(input != NULL, -1); |
| |
169 g_return_val_if_fail(in_len > 0, -1); |
| |
170 |
| |
171 padding_len = input[in_len - 1]; |
| |
172 if (padding_len <= 0 || padding_len > PURPLE_AES_BLOCK_SIZE || |
| |
173 padding_len > in_len) { |
| |
174 purple_debug_warning("cipher-aes", |
| |
175 "Invalid padding length: %d (total %d) - " |
| |
176 "most probably, the key was invalid\n", |
| |
177 padding_len, in_len); |
| |
178 return -1; |
| |
179 } |
| |
180 |
| |
181 out_len = in_len - padding_len; |
| |
182 for (i = 0; i < padding_len; i++) { |
| |
183 if (input[out_len + i] != padding_len) { |
| |
184 purple_debug_warning("cipher-aes", |
| |
185 "Padding doesn't match at pos %d (found %02x, " |
| |
186 "expected %02x) - " |
| |
187 "most probably, the key was invalid\n", |
| |
188 i, input[out_len + i], padding_len); |
| |
189 return -1; |
| |
190 } |
| |
191 } |
| |
192 |
| |
193 memset(input + out_len, 0, padding_len); |
| |
194 return out_len; |
| |
195 } |
| |
196 |
| |
197 #ifdef PURPLE_AES_USE_GNUTLS |
| |
198 |
| |
199 static gnutls_cipher_hd_t |
| |
200 purple_aes_crypt_gnutls_init(guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], |
| |
201 guint key_size) |
| |
202 { |
| |
203 gnutls_cipher_hd_t handle; |
| |
204 gnutls_cipher_algorithm_t algorithm; |
| |
205 gnutls_datum_t key_info, iv_info; |
| |
206 int ret; |
| |
207 |
| |
208 if (key_size == 16) |
| |
209 algorithm = GNUTLS_CIPHER_AES_128_CBC; |
| |
210 else if (key_size == 24) |
| |
211 algorithm = GNUTLS_CIPHER_AES_192_CBC; |
| |
212 else if (key_size == 32) |
| |
213 algorithm = GNUTLS_CIPHER_AES_256_CBC; |
| |
214 else |
| |
215 g_return_val_if_reached(NULL); |
| |
216 |
| |
217 key_info.data = key; |
| |
218 key_info.size = key_size; |
| |
219 |
| |
220 iv_info.data = iv; |
| |
221 iv_info.size = PURPLE_AES_BLOCK_SIZE; |
| |
222 |
| |
223 ret = gnutls_cipher_init(&handle, algorithm, &key_info, &iv_info); |
| |
224 if (ret != 0) { |
| |
225 purple_debug_error("cipher-aes", |
| |
226 "gnutls_cipher_init failed: %d\n", ret); |
| |
227 return NULL; |
| |
228 } |
| |
229 |
| |
230 return handle; |
| |
231 } |
| |
232 |
| |
233 static gboolean |
| |
234 purple_aes_encrypt_gnutls(const guchar *input, guchar *output, size_t len, |
| |
235 guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size) |
| |
236 { |
| |
237 gnutls_cipher_hd_t handle; |
| |
238 int ret; |
| |
239 |
| |
240 handle = purple_aes_crypt_gnutls_init(iv, key, key_size); |
| |
241 if (handle == NULL) |
| |
242 return FALSE; |
| |
243 |
| |
244 ret = gnutls_cipher_encrypt2(handle, input, len, output, len); |
| |
245 gnutls_cipher_deinit(handle); |
| |
246 |
| |
247 if (ret != 0) { |
| |
248 purple_debug_error("cipher-aes", |
| |
249 "gnutls_cipher_encrypt2 failed: %d\n", ret); |
| |
250 return FALSE; |
| |
251 } |
| |
252 |
| |
253 return TRUE; |
| |
254 } |
| |
255 |
| |
256 static gboolean |
| |
257 purple_aes_decrypt_gnutls(const guchar *input, guchar *output, size_t len, |
| |
258 guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size) |
| |
259 { |
| |
260 gnutls_cipher_hd_t handle; |
| |
261 int ret; |
| |
262 |
| |
263 handle = purple_aes_crypt_gnutls_init(iv, key, key_size); |
| |
264 if (handle == NULL) |
| |
265 return FALSE; |
| |
266 |
| |
267 ret = gnutls_cipher_decrypt2(handle, input, len, output, len); |
| |
268 gnutls_cipher_deinit(handle); |
| |
269 |
| |
270 if (ret != 0) { |
| |
271 purple_debug_error("cipher-aes", |
| |
272 "gnutls_cipher_decrypt2 failed: %d\n", ret); |
| |
273 return FALSE; |
| |
274 } |
| |
275 |
| |
276 return TRUE; |
| |
277 } |
| |
278 |
| |
279 #endif /* PURPLE_AES_USE_GNUTLS */ |
| |
280 |
| |
281 #ifdef PURPLE_AES_USE_NSS |
| |
282 |
| |
283 typedef struct |
| |
284 { |
| |
285 PK11SlotInfo *slot; |
| |
286 PK11SymKey *sym_key; |
| |
287 SECItem *sec_param; |
| |
288 PK11Context *enc_context; |
| |
289 } purple_aes_encrypt_nss_context; |
| |
290 |
| |
291 static void |
| |
292 purple_aes_encrypt_nss_context_cleanup(purple_aes_encrypt_nss_context *ctx) |
| |
293 { |
| |
294 g_return_if_fail(ctx != NULL); |
| |
295 |
| |
296 if (ctx->enc_context != NULL) |
| |
297 PK11_DestroyContext(ctx->enc_context, TRUE); |
| |
298 if (ctx->sec_param != NULL) |
| |
299 SECITEM_FreeItem(ctx->sec_param, TRUE); |
| |
300 if (ctx->sym_key != NULL) |
| |
301 PK11_FreeSymKey(ctx->sym_key); |
| |
302 if (ctx->slot != NULL) |
| |
303 PK11_FreeSlot(ctx->slot); |
| |
304 |
| |
305 memset(ctx, 0, sizeof(purple_aes_encrypt_nss_context)); |
| |
306 } |
| |
307 |
| |
308 static gboolean |
| |
309 purple_aes_crypt_nss(const guchar *input, guchar *output, size_t len, |
| |
310 guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size, |
| |
311 CK_ATTRIBUTE_TYPE operation) |
| |
312 { |
| |
313 purple_aes_encrypt_nss_context ctx; |
| |
314 CK_MECHANISM_TYPE cipher_mech = CKM_AES_CBC; |
| |
315 SECItem key_item, iv_item; |
| |
316 SECStatus ret; |
| |
317 int outlen = 0; |
| |
318 unsigned int outlen_tmp = 0; |
| |
319 |
| |
320 memset(&ctx, 0, sizeof(purple_aes_encrypt_nss_context)); |
| |
321 |
| |
322 if (NSS_NoDB_Init(NULL) != SECSuccess) { |
| |
323 purple_debug_error("cipher-aes", |
| |
324 "NSS_NoDB_Init failed: %d\n", PR_GetError()); |
| |
325 return FALSE; |
| |
326 } |
| |
327 |
| |
328 ctx.slot = PK11_GetBestSlot(cipher_mech, NULL); |
| |
329 if (ctx.slot == NULL) { |
| |
330 purple_debug_error("cipher-aes", |
| |
331 "PK11_GetBestSlot failed: %d\n", PR_GetError()); |
| |
332 return FALSE; |
| |
333 } |
| |
334 |
| |
335 key_item.type = siBuffer; |
| |
336 key_item.data = key; |
| |
337 key_item.len = key_size; |
| |
338 ctx.sym_key = PK11_ImportSymKey(ctx.slot, cipher_mech, |
| |
339 PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL); |
| |
340 if (ctx.sym_key == NULL) { |
| |
341 purple_debug_error("cipher-aes", |
| |
342 "PK11_ImportSymKey failed: %d\n", PR_GetError()); |
| |
343 purple_aes_encrypt_nss_context_cleanup(&ctx); |
| |
344 return FALSE; |
| |
345 } |
| |
346 |
| |
347 iv_item.type = siBuffer; |
| |
348 iv_item.data = iv; |
| |
349 iv_item.len = PURPLE_AES_BLOCK_SIZE; |
| |
350 ctx.sec_param = PK11_ParamFromIV(cipher_mech, &iv_item); |
| |
351 if (ctx.sec_param == NULL) { |
| |
352 purple_debug_error("cipher-aes", |
| |
353 "PK11_ParamFromIV failed: %d\n", PR_GetError()); |
| |
354 purple_aes_encrypt_nss_context_cleanup(&ctx); |
| |
355 return FALSE; |
| |
356 } |
| |
357 |
| |
358 ctx.enc_context = PK11_CreateContextBySymKey(cipher_mech, operation, |
| |
359 ctx.sym_key, ctx.sec_param); |
| |
360 if (ctx.enc_context == NULL) { |
| |
361 purple_debug_error("cipher-aes", |
| |
362 "PK11_CreateContextBySymKey failed: %d\n", |
| |
363 PR_GetError()); |
| |
364 purple_aes_encrypt_nss_context_cleanup(&ctx); |
| |
365 return FALSE; |
| |
366 } |
| |
367 |
| |
368 ret = PK11_CipherOp(ctx.enc_context, output, &outlen, len, input, len); |
| |
369 if (ret != SECSuccess) { |
| |
370 purple_debug_error("cipher-aes", |
| |
371 "PK11_CipherOp failed: %d\n", PR_GetError()); |
| |
372 purple_aes_encrypt_nss_context_cleanup(&ctx); |
| |
373 return FALSE; |
| |
374 } |
| |
375 |
| |
376 ret = PK11_DigestFinal(ctx.enc_context, output + outlen, &outlen_tmp, |
| |
377 len - outlen); |
| |
378 if (ret != SECSuccess) { |
| |
379 purple_debug_error("cipher-aes", |
| |
380 "PK11_DigestFinal failed: %d\n", PR_GetError()); |
| |
381 purple_aes_encrypt_nss_context_cleanup(&ctx); |
| |
382 return FALSE; |
| |
383 } |
| |
384 |
| |
385 purple_aes_encrypt_nss_context_cleanup(&ctx); |
| |
386 |
| |
387 outlen += outlen_tmp; |
| |
388 if (outlen != len) { |
| |
389 purple_debug_error("cipher-aes", |
| |
390 "resulting length doesn't match: %d (expected: %d)\n", |
| |
391 outlen, len); |
| |
392 return FALSE; |
| |
393 } |
| |
394 |
| |
395 return TRUE; |
| |
396 } |
| |
397 |
| |
398 static gboolean |
| |
399 purple_aes_encrypt_nss(const guchar *input, guchar *output, size_t len, |
| |
400 guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size) |
| |
401 { |
| |
402 return purple_aes_crypt_nss(input, output, len, iv, key, key_size, |
| |
403 CKA_ENCRYPT); |
| |
404 } |
| |
405 |
| |
406 static gboolean |
| |
407 purple_aes_decrypt_nss(const guchar *input, guchar *output, size_t len, |
| |
408 guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size) |
| |
409 { |
| |
410 return purple_aes_crypt_nss(input, output, len, iv, key, key_size, |
| |
411 CKA_DECRYPT); |
| |
412 } |
| |
413 |
| |
414 #endif /* PURPLE_AES_USE_NSS */ |
| |
415 |
| |
416 static ssize_t |
| |
417 purple_aes_encrypt(PurpleCipherContext *context, const guchar input[], |
| |
418 size_t in_len, guchar output[], size_t out_size) |
| |
419 { |
| |
420 AESContext *ctx_data = purple_cipher_context_get_data(context); |
| |
421 purple_aes_crypt_func encrypt_func; |
| |
422 guchar *input_padded; |
| |
423 size_t out_len = 0; |
| |
424 gboolean succ; |
| |
425 |
| |
426 if (ctx_data->failure) |
| |
427 return -1; |
| |
428 |
| |
429 input_padded = purple_aes_pad_pkcs7(input, in_len, &out_len); |
| |
430 |
| |
431 if (out_len > out_size) { |
| |
432 purple_debug_error("cipher-aes", "Output buffer too small\n"); |
| |
433 memset(input_padded, 0, out_len); |
| |
434 g_free(input_padded); |
| |
435 return -1; |
| |
436 } |
| |
437 |
| |
438 #if defined(PURPLE_AES_USE_GNUTLS) |
| |
439 encrypt_func = purple_aes_encrypt_gnutls; |
| |
440 #elif defined(PURPLE_AES_USE_NSS) |
| |
441 encrypt_func = purple_aes_encrypt_nss; |
| |
442 #else |
| |
443 # error "No matching encrypt_func" |
| |
444 #endif |
| |
445 |
| |
446 succ = encrypt_func(input_padded, output, out_len, ctx_data->iv, |
| |
447 ctx_data->key, ctx_data->key_size); |
| |
448 |
| |
449 memset(input_padded, 0, out_len); |
| |
450 g_free(input_padded); |
| |
451 |
| |
452 if (!succ) { |
| |
453 memset(output, 0, out_len); |
| |
454 return -1; |
| |
455 } |
| |
456 |
| |
457 return out_len; |
| |
458 } |
| |
459 |
| |
460 static ssize_t |
| |
461 purple_aes_decrypt(PurpleCipherContext *context, const guchar input[], |
| |
462 size_t in_len, guchar output[], size_t out_size) |
| |
463 { |
| |
464 AESContext *ctx_data = purple_cipher_context_get_data(context); |
| |
465 purple_aes_crypt_func decrypt_func; |
| |
466 gboolean succ; |
| |
467 ssize_t out_len; |
| |
468 |
| |
469 if (ctx_data->failure) |
| |
470 return -1; |
| |
471 |
| |
472 if (in_len > out_size) { |
| |
473 purple_debug_error("cipher-aes", "Output buffer too small\n"); |
| |
474 return -1; |
| |
475 } |
| |
476 |
| |
477 if ((in_len % PURPLE_AES_BLOCK_SIZE) != 0 || in_len == 0) { |
| |
478 purple_debug_error("cipher-aes", "Malformed data\n"); |
| |
479 return -1; |
| |
480 } |
| |
481 |
| |
482 #if defined(PURPLE_AES_USE_GNUTLS) |
| |
483 decrypt_func = purple_aes_decrypt_gnutls; |
| |
484 #elif defined(PURPLE_AES_USE_NSS) |
| |
485 decrypt_func = purple_aes_decrypt_nss; |
| |
486 #else |
| |
487 # error "No matching encrypt_func" |
| |
488 #endif |
| |
489 |
| |
490 succ = decrypt_func(input, output, in_len, ctx_data->iv, ctx_data->key, |
| |
491 ctx_data->key_size); |
| |
492 |
| |
493 if (!succ) { |
| |
494 memset(output, 0, in_len); |
| |
495 return -1; |
| |
496 } |
| |
497 |
| |
498 out_len = purple_aes_unpad_pkcs7(output, in_len); |
| |
499 if (out_len < 0) { |
| |
500 memset(output, 0, in_len); |
| |
501 return -1; |
| |
502 } |
| |
503 |
| |
504 return out_len; |
| |
505 } |
| |
506 |
| |
507 static size_t |
| |
508 purple_aes_get_key_size(PurpleCipherContext *context) |
| |
509 { |
| |
510 AESContext *ctx_data = purple_cipher_context_get_data(context); |
| |
511 |
| |
512 return ctx_data->key_size; |
| |
513 } |
| |
514 |
| |
515 static void |
| |
516 purple_aes_set_batch_mode(PurpleCipherContext *context, |
| |
517 PurpleCipherBatchMode mode) |
| |
518 { |
| |
519 AESContext *ctx_data = purple_cipher_context_get_data(context); |
| |
520 |
| |
521 if (mode == PURPLE_CIPHER_BATCH_MODE_CBC) |
| |
522 return; |
| |
523 |
| |
524 purple_debug_error("cipher-aes", "unsupported batch mode\n"); |
| |
525 ctx_data->failure = TRUE; |
| |
526 } |
| |
527 |
| |
528 static PurpleCipherBatchMode |
| |
529 purple_aes_get_batch_mode(PurpleCipherContext *context) |
| |
530 { |
| |
531 return PURPLE_CIPHER_BATCH_MODE_CBC; |
| |
532 } |
| |
533 |
| |
534 static size_t |
| |
535 purple_aes_get_block_size(PurpleCipherContext *context) |
| |
536 { |
| |
537 return PURPLE_AES_BLOCK_SIZE; |
| |
538 } |
| |
539 |
| |
540 static PurpleCipherOps AESOps = { |
| |
541 purple_aes_set_option, /* set_option */ |
| |
542 NULL, /* get_option */ |
| |
543 purple_aes_init, /* init */ |
| |
544 purple_aes_reset, /* reset */ |
| |
545 NULL, /* reset_state */ |
| |
546 purple_aes_uninit, /* uninit */ |
| |
547 purple_aes_set_iv, /* set_iv */ |
| |
548 NULL, /* append */ |
| |
549 NULL, /* digest */ |
| |
550 NULL, /* get_digest_size */ |
| |
551 purple_aes_encrypt, /* encrypt */ |
| |
552 purple_aes_decrypt, /* decrypt */ |
| |
553 NULL, /* set_salt */ |
| |
554 NULL, /* get_salt_size */ |
| |
555 purple_aes_set_key, /* set_key */ |
| |
556 purple_aes_get_key_size, /* get_key_size */ |
| |
557 purple_aes_set_batch_mode, /* set_batch_mode */ |
| |
558 purple_aes_get_batch_mode, /* get_batch_mode */ |
| |
559 purple_aes_get_block_size, /* get_block_size */ |
| |
560 NULL, NULL, NULL, NULL /* reserved */ |
| |
561 }; |
| |
562 |
| |
563 PurpleCipherOps * |
| |
564 purple_aes_cipher_get_ops(void) { |
| |
565 return &AESOps; |
| |
566 } |