diff --git a/common/commit-mgr.c b/common/commit-mgr.c index dc1e2b56d..5313d6dc9 100644 --- a/common/commit-mgr.c +++ b/common/commit-mgr.c @@ -636,6 +636,8 @@ commit_to_json_object (SeafCommit *commit) json_object_set_string_member (object, "key", commit->random_key); if (commit->enc_version >= 3) json_object_set_string_member (object, "salt", commit->salt); + if (commit->enc_version >= 5) + json_object_set_int_member (object, "key_iter", commit->key_iter); } if (commit->no_local_history) json_object_set_int_member (object, "no_local_history", 1); @@ -672,6 +674,7 @@ commit_from_json_object (const char *commit_id, json_t *object) const char *magic = NULL; const char *random_key = NULL; const char *salt = NULL; + int key_iter; int no_local_history = 0; int version = 0; int conflict = 0, new_merge = 0; @@ -712,6 +715,8 @@ commit_from_json_object (const char *commit_id, json_t *object) random_key = json_object_get_string_member (object, "key"); if (enc_version >= 3) salt = json_object_get_string_member (object, "salt"); + if (enc_version >= 5) + key_iter = json_object_get_int_member (object, "key_iter"); if (json_object_has_member (object, "no_local_history")) no_local_history = json_object_get_int_member (object, "no_local_history"); @@ -765,6 +770,16 @@ commit_from_json_object (const char *commit_id, json_t *object) if (!salt || strlen(salt) != 64) return NULL; break; + case 5: + if (!magic || strlen(magic) != 64) + return NULL; + if (!random_key || strlen(random_key) != 96) + return NULL; + if (!salt || strlen(salt) != 64) + return NULL; + if (key_iter <= 0) + return NULL; + break; default: seaf_warning ("Unknown encryption version %d.\n", enc_version); return NULL; @@ -797,6 +812,8 @@ commit_from_json_object (const char *commit_id, json_t *object) commit->random_key = g_strdup (random_key); if (enc_version >= 3) commit->salt = g_strdup(salt); + if (enc_version >= 5) + commit->key_iter = key_iter; } if (no_local_history) commit->no_local_history = TRUE; diff --git a/common/commit-mgr.h b/common/commit-mgr.h index f430e7b0f..0ef7feeb4 100644 --- a/common/commit-mgr.h +++ b/common/commit-mgr.h @@ -36,6 +36,7 @@ struct _SeafCommit { char *magic; char *random_key; char *salt; + int key_iter; gboolean no_local_history; int version; diff --git a/common/seafile-crypt.c b/common/seafile-crypt.c index 6b3adb5b5..8f7a1b504 100644 --- a/common/seafile-crypt.c +++ b/common/seafile-crypt.c @@ -49,6 +49,7 @@ seafile_crypt_new (int version, unsigned char *key, unsigned char *iv) int seafile_derive_key (const char *data_in, int in_len, int version, const char *repo_salt, + int iter, unsigned char *key, unsigned char *iv) { #ifdef USE_GPL_CRYPTO @@ -64,13 +65,16 @@ seafile_derive_key (const char *data_in, int in_len, int version, return 0; #else if (version >= 3) { + int key_iter = KEYGEN_ITERATION2; + if (version >= 5) + key_iter = iter; unsigned char repo_salt_bin[32]; hex_to_rawdata (repo_salt, repo_salt_bin, 32); PKCS5_PBKDF2_HMAC (data_in, in_len, repo_salt_bin, sizeof(repo_salt_bin), - KEYGEN_ITERATION2, + key_iter, EVP_sha256(), 32, key); PKCS5_PBKDF2_HMAC ((char *)key, 32, @@ -145,6 +149,7 @@ seafile_generate_random_key (const char *passwd, unsigned char secret_key[32], *rand_key; int outlen; unsigned char key[32], iv[16]; + int iter = KEYGEN_ITERATION2; #ifdef USE_GPL_CRYPTO if (gnutls_rnd (GNUTLS_RND_RANDOM, secret_key, sizeof(secret_key)) < 0) { @@ -158,7 +163,7 @@ seafile_generate_random_key (const char *passwd, } #endif - seafile_derive_key (passwd, strlen(passwd), version, repo_salt, key, iv); + seafile_derive_key (passwd, strlen(passwd), version, repo_salt, iter, key, iv); crypt = seafile_crypt_new (version, key, iv); @@ -181,6 +186,7 @@ seafile_generate_magic (int version, const char *repo_id, { GString *buf = g_string_new (NULL); unsigned char key[32], iv[16]; + int iter = KEYGEN_ITERATION2; /* Compute a "magic" string from repo_id and passwd. * This is used to verify the password given by user before decrypting @@ -188,7 +194,7 @@ seafile_generate_magic (int version, const char *repo_id, */ g_string_append_printf (buf, "%s%s", repo_id, passwd); - seafile_derive_key (buf->str, buf->len, version, repo_salt, key, iv); + seafile_derive_key (buf->str, buf->len, version, repo_salt, iter, key, iv); g_string_free (buf, TRUE); rawdata_to_hex (key, magic, 32); @@ -199,13 +205,14 @@ seafile_verify_repo_passwd (const char *repo_id, const char *passwd, const char *magic, int version, - const char *repo_salt) + const char *repo_salt, + int iter) { GString *buf = g_string_new (NULL); unsigned char key[32], iv[16]; char hex[65]; - if (version != 1 && version != 2 && version != 3 && version != 4) { + if (version != 1 && version != 2 && version != 3 && version != 4 && version != 5) { seaf_warning ("Unsupported enc_version %d.\n", version); return -1; } @@ -213,7 +220,7 @@ seafile_verify_repo_passwd (const char *repo_id, /* Recompute the magic and compare it with the one comes with the repo. */ g_string_append_printf (buf, "%s%s", repo_id, passwd); - seafile_derive_key (buf->str, buf->len, version, repo_salt, key, iv); + seafile_derive_key (buf->str, buf->len, version, repo_salt, iter, key, iv); g_string_free (buf, TRUE); @@ -232,11 +239,12 @@ int seafile_decrypt_repo_enc_key (int enc_version, const char *passwd, const char *random_key, const char *repo_salt, + int iter, unsigned char *key_out, unsigned char *iv_out) { unsigned char key[32], iv[16]; - seafile_derive_key (passwd, strlen(passwd), enc_version, repo_salt, key, iv); + seafile_derive_key (passwd, strlen(passwd), enc_version, repo_salt, iter, key, iv); if (enc_version == 1) { memcpy (key_out, key, 16); @@ -266,6 +274,7 @@ seafile_decrypt_repo_enc_key (int enc_version, seafile_derive_key ((char *)dec_random_key, 32, enc_version, repo_salt, + iter, key, iv); memcpy (key_out, key, 32); memcpy (iv_out, iv, 16); @@ -286,10 +295,11 @@ seafile_update_random_key (const char *old_passwd, const char *old_random_key, unsigned char random_key_raw[48], *secret_key, *new_random_key_raw; int secret_key_len, random_key_len; SeafileCrypt *crypt; + int iter = KEYGEN_ITERATION2; /* First, use old_passwd to decrypt secret key from old_random_key. */ seafile_derive_key (old_passwd, strlen(old_passwd), enc_version, - repo_salt, key, iv); + repo_salt, iter, key, iv); hex_to_rawdata (old_random_key, random_key_raw, 48); @@ -305,7 +315,7 @@ seafile_update_random_key (const char *old_passwd, const char *old_random_key, /* Second, use new_passwd to encrypt secret key. */ seafile_derive_key (new_passwd, strlen(new_passwd), enc_version, - repo_salt, key, iv); + repo_salt, iter, key, iv); crypt = seafile_crypt_new (enc_version, key, iv); seafile_encrypt ((char **)&new_random_key_raw, &random_key_len, diff --git a/common/seafile-crypt.h b/common/seafile-crypt.h index a001eedf3..5bc7c9f46 100644 --- a/common/seafile-crypt.h +++ b/common/seafile-crypt.h @@ -43,6 +43,7 @@ seafile_crypt_new (int version, unsigned char *key, unsigned char *iv); int seafile_derive_key (const char *data_in, int in_len, int version, const char *repo_salt, + int iter, unsigned char *key, unsigned char *iv); /* @salt must be an char array of size 65 bytes. */ @@ -70,12 +71,14 @@ seafile_verify_repo_passwd (const char *repo_id, const char *passwd, const char *magic, int version, - const char *repo_salt); + const char *repo_salt, + int iter); int seafile_decrypt_repo_enc_key (int enc_version, const char *passwd, const char *random_key, const char *repo_salt, + int iter, unsigned char *key_out, unsigned char *iv_out); int diff --git a/daemon/clone-mgr.c b/daemon/clone-mgr.c index 40ebe9059..29e3fa3be 100644 --- a/daemon/clone-mgr.c +++ b/daemon/clone-mgr.c @@ -631,6 +631,7 @@ save_task_to_db (SeafCloneManager *mgr, CloneTask *task) json_object_set_new (object, "is_readonly", json_integer (task->is_readonly)); if (task->server_url) json_object_set_new (object, "server_url", json_string(task->server_url)); + json_object_set_new (object, "key_iter", json_integer (task->key_iter)); info = json_dumps (object, 0); json_decref (object); @@ -1030,6 +1031,8 @@ add_task_common (SeafCloneManager *mgr, task->repo_salt = g_strdup (json_string_value (repo_salt)); integer = json_object_get (object, "resync_enc_repo"); task->resync_enc_repo = json_integer_value (integer); + integer = json_object_get (object, "key_iter"); + task->key_iter = json_integer_value (integer); json_decref (object); } @@ -1056,7 +1059,7 @@ add_task_common (SeafCloneManager *mgr, static gboolean check_encryption_args (const char *magic, int enc_version, const char *random_key, - const char *repo_salt, + const char *repo_salt, int key_iter, GError **error) { if (!magic) { @@ -1065,7 +1068,7 @@ check_encryption_args (const char *magic, int enc_version, const char *random_ke return FALSE; } - if (enc_version != 1 && enc_version != 2 && enc_version != 3 && enc_version != 4) { + if (enc_version != 1 && enc_version != 2 && enc_version != 3 && enc_version != 4 && enc_version != 5) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Unsupported enc version"); return FALSE; @@ -1082,6 +1085,11 @@ check_encryption_args (const char *magic, int enc_version, const char *random_ke "Repo salt not specified"); return FALSE; } + if (enc_version >= 5 && key_iter <= 0) { + g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, + "Repo key iteration times not specified"); + return FALSE; + } } return TRUE; @@ -1117,6 +1125,7 @@ seaf_clone_manager_add_task (SeafCloneManager *mgr, char *ret = NULL; gboolean sync_wt_name = FALSE; char *repo_salt = NULL; + int key_iter; gboolean resync_enc_repo = FALSE; if (!seaf->started) { @@ -1147,11 +1156,13 @@ seaf_clone_manager_add_task (SeafCloneManager *mgr, repo_salt = g_strdup (json_string_value (string)); json_t *integer = json_object_get (object, "resync_enc_repo"); resync_enc_repo = json_integer_value (integer); + integer = json_object_get (object, "key_iter"); + key_iter = json_integer_value (integer); json_decref (object); } if (passwd && - !check_encryption_args (magic, enc_version, random_key, repo_salt, error)) { + !check_encryption_args (magic, enc_version, random_key, repo_salt, key_iter, error)) { goto out; } /* After a repo was unsynced, the sync task may still be blocked in the @@ -1181,7 +1192,7 @@ seaf_clone_manager_add_task (SeafCloneManager *mgr, } if (passwd && - seafile_verify_repo_passwd(repo_id, passwd, magic, enc_version, repo_salt) < 0) { + seafile_verify_repo_passwd(repo_id, passwd, magic, enc_version, repo_salt, key_iter) < 0) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Incorrect password"); goto out; @@ -1269,6 +1280,7 @@ seaf_clone_manager_add_download_task (SeafCloneManager *mgr, char *worktree = NULL; char *ret = NULL; char *repo_salt = NULL; + int key_iter; if (!seaf->started) { seaf_message ("System not started, skip adding clone task.\n"); @@ -1296,11 +1308,13 @@ seaf_clone_manager_add_download_task (SeafCloneManager *mgr, json_t *string = json_object_get (object, "repo_salt"); if (string) repo_salt = g_strdup (json_string_value (string)); - json_decref (object); + json_t *integer = json_object_get (object, "key_iter"); + key_iter = json_integer_value (integer); + json_decref (object); } if (passwd && - !check_encryption_args (magic, enc_version, random_key, repo_salt, error)) { + !check_encryption_args (magic, enc_version, random_key, repo_salt, key_iter, error)) { goto out; } @@ -1331,7 +1345,7 @@ seaf_clone_manager_add_download_task (SeafCloneManager *mgr, } if (passwd && - seafile_verify_repo_passwd(repo_id, passwd, magic, enc_version, repo_salt) < 0) { + seafile_verify_repo_passwd(repo_id, passwd, magic, enc_version, repo_salt, key_iter) < 0) { g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Incorrect password"); goto out; diff --git a/daemon/clone-mgr.h b/daemon/clone-mgr.h index ed11fb4bf..54739b41a 100644 --- a/daemon/clone-mgr.h +++ b/daemon/clone-mgr.h @@ -47,6 +47,7 @@ struct _CloneTask { char *repo_salt; char *random_key; gboolean resync_enc_repo; + int key_iter; char root_id[41]; gboolean is_readonly; /* Set to true when the local folder name is the same as library name. diff --git a/daemon/repo-mgr.c b/daemon/repo-mgr.c index 22df93f20..634ecb6c8 100644 --- a/daemon/repo-mgr.c +++ b/daemon/repo-mgr.c @@ -1184,11 +1184,15 @@ seaf_repo_from_commit (SeafRepo *repo, SeafCommit *commit) memcpy (repo->magic, commit->magic, 64); memcpy (repo->random_key, commit->random_key, 96); memcpy (repo->salt, commit->salt, 64); - } - else if (repo->enc_version == 4) { + } else if (repo->enc_version == 4) { + memcpy (repo->magic, commit->magic, 64); + memcpy (repo->random_key, commit->random_key, 96); + memcpy (repo->salt, commit->salt, 64); + } else if (repo->enc_version == 5) { memcpy (repo->magic, commit->magic, 64); memcpy (repo->random_key, commit->random_key, 96); memcpy (repo->salt, commit->salt, 64); + repo->key_iter = commit->key_iter; } } repo->no_local_history = commit->no_local_history; @@ -1213,11 +1217,15 @@ seaf_repo_to_commit (SeafRepo *repo, SeafCommit *commit) commit->magic = g_strdup (repo->magic); commit->random_key = g_strdup (repo->random_key); commit->salt = g_strdup (repo->salt); - } - else if (commit->enc_version == 4) { + } else if (commit->enc_version == 4) { + commit->magic = g_strdup (repo->magic); + commit->random_key = g_strdup (repo->random_key); + commit->salt = g_strdup (repo->salt); + } else if (commit->enc_version == 5) { commit->magic = g_strdup (repo->magic); commit->random_key = g_strdup (repo->random_key); commit->salt = g_strdup (repo->salt); + commit->key_iter = repo->key_iter; } } commit->no_local_history = repo->no_local_history; @@ -5902,6 +5910,7 @@ seaf_repo_fetch_and_checkout (HttpTxTask *http_task, const char *remote_head_id) passwd, remote_head->random_key, remote_head->salt, + remote_head->key_iter, enc_key, enc_iv); crypt = seafile_crypt_new (remote_head->enc_version, enc_key, enc_iv); @@ -7343,6 +7352,7 @@ seaf_repo_manager_set_repo_passwd (SeafRepoManager *manager, if (seafile_decrypt_repo_enc_key (repo->enc_version, passwd, repo->random_key, repo->salt, + repo->key_iter, repo->enc_key, repo->enc_iv) < 0) return -1; diff --git a/daemon/repo-mgr.h b/daemon/repo-mgr.h index a4c9f392c..b9c2d5156 100644 --- a/daemon/repo-mgr.h +++ b/daemon/repo-mgr.h @@ -45,6 +45,7 @@ struct _SeafRepo { gchar salt[65]; gchar magic[65]; /* hash(repo_id + passwd), key stretched. */ gchar random_key[97]; /* key length is 48 after encryption */ + int key_iter; gboolean no_local_history; gint64 last_modify; diff --git a/daemon/sync-mgr.c b/daemon/sync-mgr.c index ea7206c69..37d4b1e2f 100644 --- a/daemon/sync-mgr.c +++ b/daemon/sync-mgr.c @@ -1397,6 +1397,7 @@ resync_repo (SeafRepo *repo) rawdata_to_hex (repo->enc_iv, iv, 16); } json_object_set_int_member (obj, "resync_enc_repo", TRUE); + json_object_set_int_member (obj, "key_iter", repo->key_iter); } more_info = json_dumps (obj, 0);