void seafile_generate_random_key (const char *passwd, char *random_key) { SeafileCrypt *crypt; unsigned char secret_key[32], *rand_key; int outlen; unsigned char key[32], iv[16]; if (RAND_bytes (secret_key, sizeof(secret_key)) != 1) { seaf_warning ("Failed to generate secret key for repo encryption " "with RAND_bytes(), use RAND_pseudo_bytes().\n"); RAND_pseudo_bytes (secret_key, sizeof(secret_key)); } seafile_derive_key (passwd, strlen(passwd), 2, key, iv); crypt = seafile_crypt_new (2, key, iv); seafile_encrypt ((char **)&rand_key, &outlen, (char *)secret_key, sizeof(secret_key), crypt); rawdata_to_hex (rand_key, random_key, 48); g_free (crypt); g_free (rand_key); }
static int decrypt_token (CcnetProcessor *processor) { USE_PRIV; int hex_len, encrypted_len, token_len; char *encrypted_token = NULL; SeafileCrypt *crypt = NULL; unsigned char key[16], iv[16]; char *token = NULL; int ret = 0; /* raw data is half the length of hexidecimal */ hex_len = strlen(priv->token); if (hex_len % 2 != 0) { seaf_warning ("[check tx slave v3] invalid length of encrypted token\n"); ret = -1; goto out; } encrypted_len = hex_len / 2; encrypted_token = g_malloc (encrypted_len); hex_to_rawdata (priv->token, (unsigned char *)encrypted_token, encrypted_len); EVP_BytesToKey (EVP_aes_128_cbc(), /* cipher mode */ EVP_sha1(), /* message digest */ NULL, /* slat */ (unsigned char*)priv->session_key, strlen(priv->session_key), 1, /* iteration times */ key, /* the derived key */ iv); /* IV, initial vector */ crypt = seafile_crypt_new (1, key, iv); if (seafile_decrypt (&token, &token_len, encrypted_token, encrypted_len, crypt) < 0) { seaf_warning ("[check tx slave v3] failed to decrypt token\n"); ret = -1; goto out; } g_free (priv->token); /* we can use the decrypted data directly, since the trailing null byte is * also included when encrypting in the client */ priv->token = token; out: g_free (crypt); g_free (encrypted_token); return ret; }
static int real_merge (SeafRepo *repo, SeafCommit *head, CloneTask *task) { struct merge_options opts; char index_path[SEAF_PATH_MAX]; struct index_state istate; char *root_id = NULL; int clean; memset (&istate, 0, sizeof(istate)); snprintf (index_path, SEAF_PATH_MAX, "%s/%s", repo->manager->index_dir, repo->id); if (read_index_from (&istate, index_path) < 0) { seaf_warning ("Failed to load index.\n"); return -1; } init_merge_options (&opts); opts.index = &istate; opts.worktree = task->worktree; opts.ancestor = "common ancestor"; opts.branch1 = seaf->session->base.user_name; opts.branch2 = head->creator_name; opts.remote_head = head->commit_id; /* Don't need to check locked files on windows. */ opts.force_merge = TRUE; if (repo->encrypted) { opts.crypt = seafile_crypt_new (repo->enc_version, repo->enc_key, repo->enc_iv); } /* Merge the downloaded branch with the current worktree contents. * EMPTY_SHA1 represents an empty common ancestor tree. */ merge_recursive (&opts, task->root_id, head->root_id, EMPTY_SHA1, &clean, &root_id); g_free (root_id); if (update_index (&istate, index_path) < 0) { seaf_warning ("Failed to update index.\n"); return -1; } /* We only update the worktree and index, but don't commit. * The next auto-commit cycle will check and do that for us. */ discard_index (&istate); g_free (opts.crypt); clear_merge_options (&opts); return 0; }
int seafile_update_random_key (const char *old_passwd, const char *old_random_key, const char *new_passwd, char *new_random_key) { unsigned char key[32], iv[16]; unsigned char random_key_raw[48], *secret_key, *new_random_key_raw; int secret_key_len, random_key_len; SeafileCrypt *crypt; /* First, use old_passwd to decrypt secret key from old_random_key. */ seafile_derive_key (old_passwd, strlen(old_passwd), 2, key, iv); hex_to_rawdata (old_random_key, random_key_raw, 48); crypt = seafile_crypt_new (2, key, iv); if (seafile_decrypt ((char **)&secret_key, &secret_key_len, (char *)random_key_raw, 48, crypt) < 0) { seaf_warning ("Failed to decrypt random key.\n"); g_free (crypt); return -1; } g_free (crypt); /* Second, use new_passwd to encrypt secret key. */ seafile_derive_key (new_passwd, strlen(new_passwd), 2, key, iv); crypt = seafile_crypt_new (2, key, iv); seafile_encrypt ((char **)&new_random_key_raw, &random_key_len, (char *)secret_key, secret_key_len, crypt); rawdata_to_hex (new_random_key_raw, new_random_key, 48); g_free (secret_key); g_free (new_random_key_raw); g_free (crypt); return 0; }
/* token -> AES encrypt with session key -> rawdata_to_hex -> output */ static char * encrypt_token (CcnetProcessor *processor, const char *token) { CcnetPeer *peer = NULL; char *enc_out = NULL; SeafileCrypt *crypt = NULL; unsigned char key[16], iv[16]; int len; char *output = NULL; if (!token) goto out; peer = ccnet_get_peer(seaf->ccnetrpc_client, processor->peer_id); if (!peer || !peer->session_key) { seaf_warning ("[check tx v3] peer or peer session key not exist\n"); goto out; } EVP_BytesToKey (EVP_aes_128_cbc(), /* cipher mode */ EVP_sha1(), /* message digest */ NULL, /* slat */ (unsigned char*)peer->session_key, strlen(peer->session_key), 1, /* iteration times */ key, /* the derived key */ iv); /* IV, initial vector */ crypt = seafile_crypt_new (CURRENT_ENC_VERSION, key, iv); /* encrypt the token with session key, including the trailing null byte */ if (seafile_encrypt (&enc_out, &len, token, strlen(token) + 1, crypt) < 0) { seaf_warning ("[check tx v3] failed to encrypt token\n"); goto out; } output = g_malloc (len * 2 + 1); rawdata_to_hex ((unsigned char *)enc_out, output, len); output[len * 2] = '\0'; out: g_free (crypt); g_free (enc_out); if (peer) g_object_unref(peer); return output; }
int seafile_decrypt_repo_enc_key (int enc_version, const char *passwd, const char *random_key, unsigned char *key_out, unsigned char *iv_out) { unsigned char key[32], iv[16]; seafile_derive_key (passwd, strlen(passwd), enc_version, key, iv); if (enc_version == 1) { memcpy (key_out, key, 16); memcpy (iv_out, iv, 16); return 0; } else if (enc_version == 2) { unsigned char enc_random_key[48], *dec_random_key; int outlen; SeafileCrypt *crypt; if (random_key == NULL || random_key[0] == 0) { seaf_warning ("Empty random key.\n"); return -1; } hex_to_rawdata (random_key, enc_random_key, 48); crypt = seafile_crypt_new (enc_version, key, iv); if (seafile_decrypt ((char **)&dec_random_key, &outlen, (char *)enc_random_key, 48, crypt) < 0) { seaf_warning ("Failed to decrypt random key.\n"); g_free (crypt); return -1; } g_free (crypt); seafile_derive_key ((char *)dec_random_key, 32, enc_version, key, iv); memcpy (key_out, key, 32); memcpy (iv_out, iv, 16); g_free (dec_random_key); return 0; } return -1; }
/* token -> AES encrypt with session key -> rawdata_to_hex -> output */ static char * encrypt_token (CcnetProcessor *processor, const char *token) { CcnetPeer *peer = NULL; char *enc_out = NULL; SeafileCrypt *crypt = NULL; unsigned char key[16], iv[16]; int len; char *output = NULL; if (!token) goto out; peer = ccnet_get_peer(seaf->ccnetrpc_client, processor->peer_id); if (!peer || !peer->session_key) { seaf_warning ("[check tx v2] peer or peer session key not exist\n"); goto out; } seafile_generate_enc_key (peer->session_key, strlen(peer->session_key), CURRENT_ENC_VERSION, key, iv); crypt = seafile_crypt_new (CURRENT_ENC_VERSION, key, iv); /* encrypt the token with session key, including the trailing null byte */ if (seafile_encrypt (&enc_out, &len, token, strlen(token) + 1, crypt) < 0) { seaf_warning ("[check tx v2] failed to encrypt token\n"); goto out; } output = g_malloc (len * 2 + 1); rawdata_to_hex ((unsigned char *)enc_out, output, len); output[len * 2] = '\0'; out: g_free (crypt); g_free (enc_out); if (peer) g_object_unref(peer); return output; }
static int fast_forward_checkout (SeafRepo *repo, SeafCommit *head, CloneTask *task) { SeafRepoManager *mgr = repo->manager; char index_path[SEAF_PATH_MAX]; struct tree_desc trees[2]; struct unpack_trees_options topts; struct index_state istate; int ret = 0; if (strcmp (head->root_id, task->root_id) == 0) return 0; memset (&istate, 0, sizeof(istate)); snprintf (index_path, SEAF_PATH_MAX, "%s/%s", mgr->index_dir, repo->id); if (read_index_from (&istate, index_path) < 0) { seaf_warning ("Failed to load index.\n"); return -1; } repo->index_corrupted = FALSE; fill_tree_descriptor (&trees[0], task->root_id); fill_tree_descriptor (&trees[1], head->root_id); memset(&topts, 0, sizeof(topts)); topts.base = task->worktree; topts.head_idx = -1; topts.src_index = &istate; topts.update = 1; topts.merge = 1; topts.fn = twoway_merge; if (repo->encrypted) { topts.crypt = seafile_crypt_new (repo->enc_version, repo->enc_key, repo->enc_iv); } if (unpack_trees (2, trees, &topts) < 0) { seaf_warning ("Failed to merge commit %s with work tree.\n", head->commit_id); ret = -1; goto out; } if (update_worktree (&topts, FALSE, head->commit_id, head->creator_name, NULL) < 0) { seaf_warning ("Failed to update worktree.\n"); ret = -1; goto out; } discard_index (&istate); istate = topts.result; if (update_index (&istate, index_path) < 0) { seaf_warning ("Failed to update index.\n"); } out: tree_desc_free (&trees[0]); tree_desc_free (&trees[1]); g_free (topts.crypt); discard_index (&istate); return ret; }
static int decrypt_token (CcnetProcessor *processor) { USE_PRIV; int hex_len, encrypted_len, token_len; char *encrypted_token = NULL; SeafileCrypt *crypt = NULL; unsigned char key[16], iv[16]; char *token = NULL; int ret = 0; /* raw data is half the length of hexidecimal */ hex_len = strlen(priv->token); if (hex_len % 2 != 0) { seaf_warning ("[check tx slave v2] invalid length of encrypted token\n"); ret = -1; goto out; } token = seaf_repo_manager_get_decrypted_token (seaf->repo_mgr, priv->token, priv->session_key); if (token) goto found; encrypted_len = hex_len / 2; encrypted_token = g_malloc (encrypted_len); hex_to_rawdata (priv->token, (unsigned char *)encrypted_token, encrypted_len); seafile_generate_enc_key (priv->session_key, strlen(priv->session_key), CURRENT_ENC_VERSION, key, iv); crypt = seafile_crypt_new (CURRENT_ENC_VERSION, key, iv); if (seafile_decrypt (&token, &token_len, encrypted_token, encrypted_len, crypt) < 0) { seaf_warning ("[check tx slave v2] failed to decrypt token\n"); ret = -1; goto out; } /* Add to cache. */ seaf_repo_manager_add_decrypted_token (seaf->repo_mgr, priv->token, priv->session_key, token); found: g_free (priv->token); /* we can use the decrypted data directly, since the trailing null byte is * also included when encrypting in the client */ priv->token = token; out: g_free (crypt); g_free (encrypted_token); return ret; }
static int do_real_merge (SeafRepo *repo, SeafBranch *head_branch, SeafCommit *head, SeafBranch *remote_branch, SeafCommit *remote, SeafCommit *common, gboolean recover_merge, char **error) { struct merge_options opts; char index_path[SEAF_PATH_MAX]; struct index_state istate; char *root_id = NULL; SeafCommit *merged; int ret = 0, clean; memset (&istate, 0, sizeof(istate)); snprintf (index_path, SEAF_PATH_MAX, "%s/%s", repo->manager->index_dir, repo->id); if (read_index_from (&istate, index_path) < 0) { g_warning ("Failed to load index.\n"); *error = g_strdup ("Internal error.\n"); return -1; } init_merge_options (&opts); opts.index = &istate; opts.worktree = repo->worktree; opts.ancestor = "common ancestor"; opts.branch1 = seaf->session->base.user_name; opts.branch2 = remote->creator_name; opts.remote_head = remote->commit_id; opts.recover_merge = recover_merge; if (repo->encrypted) { opts.crypt = seafile_crypt_new (repo->enc_version, repo->enc_key, repo->enc_iv); } ret = merge_recursive (&opts, head->root_id, remote->root_id, common->root_id, &clean, &root_id); if (ret < 0) goto out; if (update_index (&istate, index_path) < 0) { *error = g_strdup ("Internal error.\n"); ret = -1; goto out; } if (clean) { merged = seaf_commit_new (NULL, repo->id, root_id, repo->email ? repo->email : seaf->session->base.user_name, seaf->session->base.id, "Auto merge by seafile system", 0); merged->parent_id = g_strdup(head->commit_id); merged->second_parent_id = g_strdup(remote->commit_id); seaf_repo_to_commit (repo, merged); if (seaf_commit_manager_add_commit (seaf->commit_mgr, merged) < 0) { seaf_commit_unref (merged); *error = g_strdup ("Internal error.\n"); ret = -1; goto out; } seaf_branch_set_commit (head_branch, merged->commit_id); seaf_branch_manager_update_branch (seaf->branch_mgr, head_branch); g_debug ("Auto merged.\n"); seaf_commit_unref (merged); } else { ret = -1; g_debug ("Auto merge failed.\n"); } out: if (root_id) g_free (root_id); g_free (opts.crypt); clear_merge_options (&opts); discard_index (&istate); return ret; }