2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2016 The Claws Mail Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "claws-features.h"
25 #ifdef PASSWORD_CRYPTO_GNUTLS
26 # include <gnutls/gnutls.h>
27 # include <gnutls/crypto.h>
31 #include <glib/gi18n.h>
36 #elif defined G_OS_WIN32
41 #include "common/passcrypt.h"
42 #include "common/plugin.h"
43 #include "common/pkcs5_pbkdf2.h"
44 #include "common/timing.h"
45 #include "common/utils.h"
47 #include "alertpanel.h"
48 #include "inputdialog.h"
50 #include "passwordstore.h"
51 #include "prefs_common.h"
53 #ifndef PASSWORD_CRYPTO_OLD
54 static gchar *_master_passphrase = NULL;
56 /* Length of stored key derivation, before base64. */
59 /* Length of randomly generated and saved salt, used for key derivation.
60 * Also before base64. */
61 #define KD_SALT_LENGTH 64
63 static void _generate_salt()
67 #elif defined G_OS_WIN32
71 guchar salt[KD_SALT_LENGTH];
73 if (prefs_common_get_prefs()->master_passphrase_salt != NULL) {
74 g_free(prefs_common_get_prefs()->master_passphrase_salt);
77 /* Prepare our source of random data. */
79 rnd = open("/dev/urandom", O_RDONLY);
81 perror("fopen on /dev/urandom");
82 #elif defined G_OS_WIN32
83 if (!CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, 0) &&
84 !CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
85 debug_print("Could not acquire a CSP handle.\n");
91 ret = read(rnd, salt, KD_SALT_LENGTH);
92 if (ret != KD_SALT_LENGTH) {
93 perror("read into salt");
95 #elif defined G_OS_WIN32
96 if (!CryptGenRandom(rnd, KD_SALT_LENGTH, salt)) {
97 debug_print("Could not read random data for salt\n");
98 CryptReleaseContext(rnd, 0);
103 #if defined G_OS_UNIX
105 #elif defined G_OS_WIN32
106 CryptReleaseContext(rnd, 0);
109 prefs_common_get_prefs()->master_passphrase_salt =
110 g_base64_encode(salt, KD_SALT_LENGTH);
113 #undef KD_SALT_LENGTH
115 static guchar *_make_key_deriv(const gchar *passphrase, guint rounds)
118 gchar *saltpref = prefs_common_get_prefs()->master_passphrase_salt;
122 /* Grab our salt, generating and saving a new random one if needed. */
123 if (saltpref == NULL || strlen(saltpref) == 0) {
125 saltpref = prefs_common_get_prefs()->master_passphrase_salt;
127 salt = g_base64_decode(saltpref, &saltlen);
128 kd = g_malloc0(KD_LENGTH);
130 START_TIMING("PBKDF2");
131 ret = pkcs5_pbkdf2(passphrase, strlen(passphrase), salt, saltlen,
132 kd, KD_LENGTH, rounds);
145 static const gchar *master_passphrase()
148 gboolean end = FALSE;
150 if (!prefs_common_get_prefs()->use_master_passphrase) {
151 return PASSCRYPT_KEY;
154 if (_master_passphrase != NULL) {
155 debug_print("Master passphrase is in memory, offering it.\n");
156 return _master_passphrase;
160 input = input_dialog_with_invisible(_("Input master passphrase"),
161 _("Input master passphrase"), NULL);
164 debug_print("Cancel pressed at master passphrase dialog.\n");
168 if (master_passphrase_is_correct(input)) {
169 debug_print("Entered master passphrase seems to be correct, remembering it.\n");
170 _master_passphrase = input;
173 alertpanel_error(_("Incorrect master passphrase."));
177 return _master_passphrase;
180 const gboolean master_passphrase_is_set()
182 if (prefs_common_get_prefs()->master_passphrase == NULL
183 || strlen(prefs_common_get_prefs()->master_passphrase) == 0)
189 const gboolean master_passphrase_is_correct(const gchar *input)
191 guchar *kd, *input_kd;
193 gchar *stored_kd = prefs_common_get_prefs()->master_passphrase;
198 g_return_val_if_fail(stored_kd != NULL && strlen(stored_kd) > 0, FALSE);
199 g_return_val_if_fail(input != NULL, FALSE);
201 if (stored_kd == NULL)
204 tokens = g_strsplit_set(stored_kd, "{}", 3);
205 if (tokens[0] == NULL ||
206 strlen(tokens[0]) != 0 || /* nothing before { */
208 strncmp(tokens[1], "PBKDF2-HMAC-SHA1,", 17) || /* correct tag */
209 strlen(tokens[1]) <= 17 || /* something after , */
210 (rounds = atoi(tokens[1] + 17)) <= 0 || /* valid rounds # */
212 strlen(tokens[2]) == 0) { /* string continues after } */
213 debug_print("Mangled master_passphrase format in config, can not use it.\n");
218 stored_kd = tokens[2];
219 kd = g_base64_decode(stored_kd, &kd_len); /* should be 64 */
222 if (kd_len != KD_LENGTH) {
223 debug_print("master_passphrase is %ld bytes long, should be %d.\n",
229 input_kd = _make_key_deriv(input, rounds);
230 ret = memcmp(kd, input_kd, kd_len);
241 gboolean master_passphrase_is_entered()
243 return (_master_passphrase == NULL) ? FALSE : TRUE;
246 void master_passphrase_forget()
248 /* If master passphrase is currently in memory (entered by user),
249 * get rid of it. User will have to enter the new one again. */
250 if (_master_passphrase != NULL) {
251 memset(_master_passphrase, 0, strlen(_master_passphrase));
252 g_free(_master_passphrase);
253 _master_passphrase = NULL;
257 void master_passphrase_change(const gchar *oldp, const gchar *newp)
261 guint rounds = prefs_common_get_prefs()->master_passphrase_pbkdf2_rounds;
263 g_return_if_fail(rounds > 0);
266 /* If oldp is NULL, make sure the user has to enter the
267 * current master passphrase before being able to change it. */
268 master_passphrase_forget();
269 oldp = master_passphrase();
271 g_return_if_fail(oldp != NULL);
273 /* Update master passphrase hash in prefs */
274 if (prefs_common_get_prefs()->master_passphrase != NULL)
275 g_free(prefs_common_get_prefs()->master_passphrase);
278 debug_print("Storing key derivation of new master passphrase\n");
279 kd = _make_key_deriv(newp, rounds);
280 base64_kd = g_base64_encode(kd, 64);
281 prefs_common_get_prefs()->master_passphrase =
282 g_strdup_printf("{PBKDF2-HMAC-SHA1,%d}%s", rounds, base64_kd);
286 debug_print("Setting master_passphrase to NULL\n");
287 prefs_common_get_prefs()->master_passphrase = NULL;
290 /* Now go over all accounts, reencrypting their passwords using
291 * the new master passphrase. */
294 oldp = PASSCRYPT_KEY;
296 newp = PASSCRYPT_KEY;
298 debug_print("Reencrypting all account passwords...\n");
299 passwd_store_reencrypt_all(oldp, newp);
301 /* Now reencrypt all plugins passwords fields
302 * FIXME: Unloaded plugins won't be able to update their stored passwords
304 plugins_master_passphrase_change(oldp, newp);
306 master_passphrase_forget();
310 gchar *password_encrypt_old(const gchar *password)
312 if (!password || strlen(password) == 0) {
316 gchar *encrypted = g_strdup(password);
317 gchar *encoded, *result;
318 gsize len = strlen(password);
320 passcrypt_encrypt(encrypted, len);
321 encoded = g_base64_encode(encrypted, len);
323 result = g_strconcat("!", encoded, NULL);
329 gchar *password_decrypt_old(const gchar *password)
331 if (!password || strlen(password) == 0) {
335 if (*password != '!' || strlen(password) < 2) {
340 gchar *decrypted = g_base64_decode(password + 1, &len);
342 passcrypt_decrypt(decrypted, len);
346 #ifdef PASSWORD_CRYPTO_GNUTLS
349 /* Since we can't count on having GnuTLS new enough to have
350 * gnutls_cipher_get_iv_size(), we hardcode the IV length for now. */
353 gchar *password_encrypt_gnutls(const gchar *password,
354 const gchar *encryption_passphrase)
356 /* Another, slightly inferior combination is AES-128-CBC + SHA-256.
357 * Any block cipher in CBC mode with keysize N and a hash algo with
358 * digest length 2*N would do. */
359 gnutls_cipher_algorithm_t algo = GNUTLS_CIPHER_AES_256_CBC;
360 gnutls_digest_algorithm_t digest = GNUTLS_DIG_SHA512;
361 gnutls_cipher_hd_t handle;
362 gnutls_datum_t key, iv;
363 int keylen, digestlen, blocklen, ret, i;
364 unsigned char hashbuf[BUFSIZE], *buf, *encbuf, *base, *output;
365 #if defined G_OS_UNIX
367 #elif defined G_OS_WIN32
371 g_return_val_if_fail(password != NULL, NULL);
372 g_return_val_if_fail(encryption_passphrase != NULL, NULL);
374 /* ivlen = gnutls_cipher_get_iv_size(algo);*/
375 keylen = gnutls_cipher_get_key_size(algo);
376 blocklen = gnutls_cipher_get_block_size(algo);
377 digestlen = gnutls_hash_get_len(digest);
379 /* Prepare key for cipher - first half of hash of passkey XORed with
381 memset(&hashbuf, 0, BUFSIZE);
382 if ((ret = gnutls_hash_fast(digest, encryption_passphrase,
383 strlen(encryption_passphrase), &hashbuf)) < 0) {
384 debug_print("Hashing passkey failed: %s\n", gnutls_strerror(ret));
387 for (i = 0; i < digestlen/2; i++) {
388 hashbuf[i] = hashbuf[i] ^ hashbuf[i+digestlen/2];
391 key.data = malloc(keylen);
392 memcpy(key.data, &hashbuf, keylen);
395 /* Prepare our source of random data. */
396 #if defined G_OS_UNIX
397 rnd = open("/dev/urandom", O_RDONLY);
399 perror("fopen on /dev/urandom");
400 #elif defined G_OS_WIN32
401 if (!CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, 0) &&
402 !CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
403 debug_print("Could not acquire a CSP handle.\n");
409 /* Prepare random IV for cipher */
410 iv.data = malloc(IVLEN);
412 #if defined G_OS_UNIX
413 ret = read(rnd, iv.data, IVLEN);
415 perror("read into iv");
417 #elif defined G_OS_WIN32
418 if (!CryptGenRandom(rnd, IVLEN, iv.data)) {
419 debug_print("Could not read random data for IV\n");
420 CryptReleaseContext(rnd, 0);
427 /* Initialize the encryption */
428 ret = gnutls_cipher_init(&handle, algo, &key, &iv);
432 #if defined G_OS_UNIX
434 #elif defined G_OS_WIN32
435 CryptReleaseContext(rnd, 0);
440 /* Fill buf with one block of random data, our password, pad the
441 * rest with zero bytes. */
442 buf = malloc(BUFSIZE + blocklen);
443 memset(buf, 0, BUFSIZE);
444 #if defined G_OS_UNIX
445 ret = read(rnd, buf, blocklen);
446 if (ret != blocklen) {
447 perror("read into buffer");
449 #elif defined G_OS_WIN32
450 if (!CryptGenRandom(rnd, blocklen, buf)) {
451 debug_print("Could not read random data for IV\n");
452 CryptReleaseContext(rnd, 0);
457 gnutls_cipher_deinit(handle);
461 /* We don't need any more random data. */
462 #if defined G_OS_UNIX
464 #elif defined G_OS_WIN32
465 CryptReleaseContext(rnd, 0);
468 memcpy(buf + blocklen, password, strlen(password));
470 /* Encrypt into encbuf */
471 encbuf = malloc(BUFSIZE + blocklen);
472 memset(encbuf, 0, BUFSIZE + blocklen);
473 ret = gnutls_cipher_encrypt2(handle, buf, BUFSIZE + blocklen,
474 encbuf, BUFSIZE + blocklen);
480 gnutls_cipher_deinit(handle);
485 gnutls_cipher_deinit(handle);
490 /* And finally prepare the resulting string:
491 * "{algorithm}base64encodedciphertext" */
492 base = g_base64_encode(encbuf, BUFSIZE);
494 output = g_strdup_printf("{%s}%s", gnutls_cipher_get_name(algo), base);
500 gchar *password_decrypt_gnutls(const gchar *password,
501 const gchar *decryption_passphrase)
503 gchar **tokens, *tmp;
504 gnutls_cipher_algorithm_t algo;
505 gnutls_digest_algorithm_t digest = GNUTLS_DIG_UNKNOWN;
506 gnutls_cipher_hd_t handle;
507 gnutls_datum_t key, iv;
508 int keylen, digestlen, blocklen, ret, i;
510 unsigned char hashbuf[BUFSIZE], *buf;
511 #if defined G_OS_UNIX
513 #elif defined G_OS_WIN32
517 g_return_val_if_fail(password != NULL, NULL);
518 g_return_val_if_fail(decryption_passphrase != NULL, NULL);
520 tokens = g_strsplit_set(password, "{}", 3);
522 /* Parse the string, retrieving algorithm and encrypted data.
523 * We expect "{algorithm}base64encodedciphertext". */
524 if (strlen(tokens[0]) != 0 ||
525 (algo = gnutls_cipher_get_id(tokens[1])) == GNUTLS_CIPHER_UNKNOWN ||
526 strlen(tokens[2]) == 0)
529 /* Our hash algo needs to have digest length twice as long as our
530 * cipher algo's key length. */
531 if (algo == GNUTLS_CIPHER_AES_256_CBC) {
532 debug_print("Using AES-256-CBC + SHA-512 for decryption\n");
533 digest = GNUTLS_DIG_SHA512;
534 } else if (algo == GNUTLS_CIPHER_AES_128_CBC) {
535 debug_print("Using AES-128-CBC + SHA-256 for decryption\n");
536 digest = GNUTLS_DIG_SHA256;
538 if (digest == GNUTLS_DIG_UNKNOWN) {
539 debug_print("Password is encrypted with unsupported cipher, giving up.\n");
544 /* ivlen = gnutls_cipher_get_iv_size(algo); */
545 keylen = gnutls_cipher_get_key_size(algo);
546 blocklen = gnutls_cipher_get_block_size(algo);
547 digestlen = gnutls_hash_get_len(digest);
549 /* Prepare key for cipher - first half of hash of passkey XORed with
550 * the second. AES-256 has key length 32 and length of SHA-512 hash
551 * is exactly twice that, 64. */
552 memset(&hashbuf, 0, BUFSIZE);
553 if ((ret = gnutls_hash_fast(digest, decryption_passphrase,
554 strlen(decryption_passphrase), &hashbuf)) < 0) {
555 debug_print("Hashing passkey failed: %s\n", gnutls_strerror(ret));
559 for (i = 0; i < digestlen/2; i++) {
560 hashbuf[i] = hashbuf[i] ^ hashbuf[i+digestlen/2];
563 key.data = malloc(keylen);
564 memcpy(key.data, &hashbuf, keylen);
567 /* Prepare our source of random data. */
568 #if defined G_OS_UNIX
569 rnd = open("/dev/urandom", O_RDONLY);
571 perror("fopen on /dev/urandom");
572 #elif defined G_OS_WIN32
573 if (!CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, 0) &&
574 !CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
575 debug_print("Could not acquire a CSP handle.\n");
582 /* Prepare random IV for cipher */
583 iv.data = malloc(IVLEN);
585 #if defined G_OS_UNIX
586 ret = read(rnd, iv.data, IVLEN);
588 perror("read into iv");
590 #elif defined G_OS_WIN32
591 if (!CryptGenRandom(rnd, IVLEN, iv.data)) {
592 debug_print("Could not read random data for IV\n");
593 CryptReleaseContext(rnd, 0);
601 /* We don't need any more random data. */
602 #if defined G_OS_UNIX
604 #elif defined G_OS_WIN32
605 CryptReleaseContext(rnd, 0);
608 /* Prepare encrypted password string for decryption. */
609 tmp = g_base64_decode(tokens[2], &len);
612 /* Initialize the decryption */
613 ret = gnutls_cipher_init(&handle, algo, &key, &iv);
615 debug_print("Cipher init failed: %s\n", gnutls_strerror(ret));
621 buf = malloc(BUFSIZE + blocklen);
622 memset(buf, 0, BUFSIZE + blocklen);
623 ret = gnutls_cipher_decrypt2(handle, tmp, len,
624 buf, BUFSIZE + blocklen);
626 debug_print("Decryption failed: %s\n", gnutls_strerror(ret));
630 gnutls_cipher_deinit(handle);
635 gnutls_cipher_deinit(handle);
639 tmp = g_strndup(buf + blocklen, MIN(strlen(buf + blocklen), BUFSIZE));
648 gchar *password_encrypt(const gchar *password,
649 const gchar *encryption_passphrase)
651 if (password == NULL || strlen(password) == 0) {
655 #ifndef PASSWORD_CRYPTO_OLD
656 if (encryption_passphrase == NULL)
657 encryption_passphrase = master_passphrase();
659 return password_encrypt_real(password, encryption_passphrase);
661 return password_encrypt_old(password);
665 gchar *password_decrypt(const gchar *password,
666 const gchar *decryption_passphrase)
668 if (password == NULL || strlen(password) == 0) {
672 /* First, check if the password was possibly decrypted using old,
674 if (*password == '!') {
675 debug_print("Trying to decrypt password using the old method...\n");
676 return password_decrypt_old(password);
679 /* Try available crypto backend */
680 #ifndef PASSWORD_CRYPTO_OLD
681 if (decryption_passphrase == NULL)
682 decryption_passphrase = master_passphrase();
684 if (*password == '{') {
685 debug_print("Trying to decrypt password...\n");
686 return password_decrypt_real(password, decryption_passphrase);
690 /* Fallback, in case the configuration is really old and
691 * stored password in plaintext */
692 debug_print("Assuming password was stored plaintext, returning it unchanged\n");
693 return g_strdup(password);