+/* Length of stored key derivation, before base64. */
+#define KD_LENGTH 64
+
+/* Length of randomly generated and saved salt, used for key derivation.
+ * Also before base64. */
+#define KD_SALT_LENGTH 64
+
+static void _generate_salt()
+{
+#if defined G_OS_UNIX
+ int rnd;
+#elif defined G_OS_WIN32
+ HCRYPTPROV rnd;
+#endif
+ gint ret;
+ guchar salt[KD_SALT_LENGTH];
+
+ if (prefs_common_get_prefs()->master_passphrase_salt != NULL) {
+ g_free(prefs_common_get_prefs()->master_passphrase_salt);
+ }
+
+ /* Prepare our source of random data. */
+#if defined G_OS_UNIX
+ rnd = open("/dev/urandom", O_RDONLY);
+ if (rnd == -1) {
+ perror("fopen on /dev/urandom");
+#elif defined G_OS_WIN32
+ if (!CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, 0) &&
+ !CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
+ debug_print("Could not acquire a CSP handle.\n");
+#endif
+ return;
+ }
+
+#if defined G_OS_UNIX
+ ret = read(rnd, salt, KD_SALT_LENGTH);
+ if (ret != KD_SALT_LENGTH) {
+ perror("read into salt");
+ close(rnd);
+#elif defined G_OS_WIN32
+ if (!CryptGenRandom(rnd, KD_SALT_LENGTH, salt)) {
+ debug_print("Could not read random data for salt\n");
+ CryptReleaseContext(rnd, 0);
+#endif
+ return;
+ }
+
+ prefs_common_get_prefs()->master_passphrase_salt =
+ g_base64_encode(salt, KD_SALT_LENGTH);
+}
+
+#undef KD_SALT_LENGTH
+
+static guchar *_make_key_deriv(const gchar *passphrase, guint rounds)
+{
+ guchar *kd, *salt;
+ gchar *saltpref = prefs_common_get_prefs()->master_passphrase_salt;
+ gsize saltlen;
+ gint ret;
+
+ /* Grab our salt, generating and saving a new random one if needed. */
+ if (saltpref == NULL || strlen(saltpref) == 0) {
+ _generate_salt();
+ saltpref = prefs_common_get_prefs()->master_passphrase_salt;
+ }
+ salt = g_base64_decode(saltpref, &saltlen);
+ kd = g_malloc0(KD_LENGTH);
+
+ ret = pkcs5_pbkdf2(passphrase, strlen(passphrase), salt, saltlen,
+ kd, KD_LENGTH, rounds);
+
+ g_free(salt);
+
+ if (ret == 0) {
+ return kd;
+ }
+
+ g_free(kd);
+ return NULL;
+}
+