libpurple/ciphers/aes.c

branch
soc.2008.masterpassword
changeset 34182
087c0fbac984
child 34248
28750ae09061
child 34546
5c2f894fe4d6
equal deleted inserted replaced
34181:e46890ebd08f 34182:087c0fbac984
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 }

mercurial