Skip to content

Commit

Permalink
Support password hash (#2783)
Browse files Browse the repository at this point in the history
* Support password hash

* Add rpc signature and verify pwd hash

---------

Co-authored-by: yangheran <[email protected]>
  • Loading branch information
feiniks and yangheran authored Oct 16, 2024
1 parent 6df037c commit f5d8b38
Show file tree
Hide file tree
Showing 19 changed files with 565 additions and 118 deletions.
27 changes: 25 additions & 2 deletions common/commit-mgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ seaf_commit_free (SeafCommit *commit)
g_free (commit->client_version);
g_free (commit->magic);
g_free (commit->random_key);
g_free (commit->pwd_hash);
g_free (commit->pwd_hash_algo);
g_free (commit->pwd_hash_params);
g_free (commit);
}

Expand Down Expand Up @@ -630,12 +633,17 @@ commit_to_json_object (SeafCommit *commit)

if (commit->encrypted) {
json_object_set_int_member (object, "enc_version", commit->enc_version);
if (commit->enc_version >= 1)
if (commit->enc_version >= 1 && !commit->pwd_hash)
json_object_set_string_member (object, "magic", commit->magic);
if (commit->enc_version >= 2)
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->pwd_hash) {
json_object_set_string_member (object, "pwd_hash", commit->pwd_hash);
json_object_set_string_member (object, "pwd_hash_algo", commit->pwd_hash_algo);
json_object_set_string_member (object, "pwd_hash_params", commit->pwd_hash_params);
}
}
if (commit->no_local_history)
json_object_set_int_member (object, "no_local_history", 1);
Expand Down Expand Up @@ -672,6 +680,9 @@ commit_from_json_object (const char *commit_id, json_t *object)
const char *magic = NULL;
const char *random_key = NULL;
const char *salt = NULL;
const char *pwd_hash = NULL;
const char *pwd_hash_algo = NULL;
const char *pwd_hash_params = NULL;
int no_local_history = 0;
int version = 0;
int conflict = 0, new_merge = 0;
Expand Down Expand Up @@ -706,6 +717,9 @@ commit_from_json_object (const char *commit_id, json_t *object)
&& json_object_has_member (object, "enc_version")) {
enc_version = json_object_get_int_member (object, "enc_version");
magic = json_object_get_string_member (object, "magic");
pwd_hash = json_object_get_string_member (object, "pwd_hash");
pwd_hash_algo = json_object_get_string_member (object, "pwd_hash_algo");
pwd_hash_params = json_object_get_string_member (object, "pwd_hash_params");
}

if (enc_version >= 2)
Expand Down Expand Up @@ -736,6 +750,10 @@ commit_from_json_object (const char *commit_id, json_t *object)
(second_parent_id && !is_object_id_valid(second_parent_id)))
return commit;

// If pwd_hash is set, the magic field is no longer included in the commit of the newly created repo.
if (!magic)
magic = pwd_hash;

switch (enc_version) {
case 0:
break;
Expand Down Expand Up @@ -791,12 +809,17 @@ commit_from_json_object (const char *commit_id, json_t *object)

if (commit->encrypted) {
commit->enc_version = enc_version;
if (enc_version >= 1)
if (enc_version >= 1 && !pwd_hash)
commit->magic = g_strdup(magic);
if (enc_version >= 2)
commit->random_key = g_strdup (random_key);
if (enc_version >= 3)
commit->salt = g_strdup(salt);
if (pwd_hash) {
commit->pwd_hash = g_strdup (pwd_hash);
commit->pwd_hash_algo = g_strdup (pwd_hash_algo);
commit->pwd_hash_params = g_strdup (pwd_hash_params);
}
}
if (no_local_history)
commit->no_local_history = TRUE;
Expand Down
3 changes: 3 additions & 0 deletions common/commit-mgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ struct _SeafCommit {
char *magic;
char *random_key;
char *salt;
char *pwd_hash;
char *pwd_hash_algo;
char *pwd_hash_params;
gboolean no_local_history;

int version;
Expand Down
167 changes: 167 additions & 0 deletions common/password-hash.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

#include <string.h>
#include <glib.h>
#include <argon2.h>
#include "password-hash.h"
#include "seafile-crypt.h"
#include <openssl/rand.h>

#include "utils.h"
#include "log.h"

// pbkdf2
typedef struct Pbkdf2Params {
int iteration;
} Pbkdf2Params;

static Pbkdf2Params *
parse_pbkdf2_sha256_params (const char *params_str)
{
Pbkdf2Params *params = NULL;
if (!params_str) {
params = g_new0 (Pbkdf2Params, 1);
params->iteration = 1000;
return params;
}
int iteration;
iteration = atoi (params_str);
if (iteration <= 0) {
iteration = 1000;
}

params = g_new0 (Pbkdf2Params, 1);
params->iteration = iteration;
return params;
}

static int
pbkdf2_sha256_derive_key (const char *data_in, int in_len,
const char *salt,
Pbkdf2Params *params,
unsigned char *key)
{
int iteration = params->iteration;

unsigned char salt_bin[32] = {0};
hex_to_rawdata (salt, salt_bin, 32);

PKCS5_PBKDF2_HMAC (data_in, in_len,
salt_bin, sizeof(salt_bin),
iteration,
EVP_sha256(),
32, key);
return 0;
}

// argon2id
typedef struct Argon2idParams{
gint64 time_cost;
gint64 memory_cost;
gint64 parallelism;
} Argon2idParams;

// The arguments to argon2 are separated by commas.
// Example arguments format:
// 2,102400,8
// The parameters are time_cost, memory_cost, parallelism from left to right.
static Argon2idParams *
parse_argon2id_params (const char *params_str)
{
char **params;
Argon2idParams *argon2_params = g_new0 (Argon2idParams, 1);
if (params_str)
params = g_strsplit (params_str, ",", 3);
if (!params_str || g_strv_length(params) != 3) {
if (params_str)
g_strfreev (params);
argon2_params->time_cost = 2; // 2-pass computation
argon2_params->memory_cost = 102400; // 100 mebibytes memory usage
argon2_params->parallelism = 8; // number of threads and lanes
return argon2_params;
}

char *p = NULL;
p = g_strstrip (params[0]);
argon2_params->time_cost = atoll (p);
if (argon2_params->time_cost <= 0) {
argon2_params->time_cost = 2;
}

p = g_strstrip (params[1]);
argon2_params->memory_cost = atoll (p);
if (argon2_params->memory_cost <= 0) {
argon2_params->memory_cost = 102400;
}

p = g_strstrip (params[2]);
argon2_params->parallelism = atoll (p);
if (argon2_params->parallelism <= 0) {
argon2_params->parallelism = 8;
}

g_strfreev (params);
return argon2_params;
}

static int
argon2id_derive_key (const char *data_in, int in_len,
const char *salt,
Argon2idParams *params,
unsigned char *key)
{
unsigned char salt_bin[32] = {0};
hex_to_rawdata (salt, salt_bin, 32);

argon2id_hash_raw(params->time_cost, params->memory_cost, params->parallelism,
data_in, in_len,
salt_bin, sizeof(salt_bin),
key, 32);

return 0;
}

// parse_pwd_hash_params is used to parse default pwd hash algorithms.
void
parse_pwd_hash_params (const char *algo, const char *params_str, PwdHashParams *params)
{
if (g_strcmp0 (algo, PWD_HASH_PDKDF2) == 0) {
params->algo = g_strdup (PWD_HASH_PDKDF2);
if (params_str)
params->params_str = g_strdup (params_str);
else
params->params_str = g_strdup ("1000");
} else if (g_strcmp0 (algo, PWD_HASH_ARGON2ID) == 0) {
params->algo = g_strdup (PWD_HASH_ARGON2ID);
if (params_str)
params->params_str = g_strdup (params_str);
else
params->params_str = g_strdup ("2,102400,8");
} else {
params->algo = NULL;
}

seaf_message ("password hash algorithms: %s, params: %s\n ", params->algo, params->params_str);
}

int
pwd_hash_derive_key (const char *data_in, int in_len,
const char *salt,
const char *algo, const char *params_str,
unsigned char *key)
{
int ret = 0;
if (g_strcmp0 (algo, PWD_HASH_ARGON2ID) == 0) {
Argon2idParams *algo_params = parse_argon2id_params (params_str);
ret = argon2id_derive_key (data_in, in_len,
salt, algo_params, key);
g_free (algo_params);
return ret;
} else {
Pbkdf2Params *algo_params = parse_pbkdf2_sha256_params (params_str);
ret = pbkdf2_sha256_derive_key (data_in, in_len,
salt, algo_params, key);
g_free (algo_params);
return ret;
}
}
23 changes: 23 additions & 0 deletions common/password-hash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

#ifndef _PASSWORD_HASH_H
#define _PASSWORD_HASH_H

#define PWD_HASH_PDKDF2 "pbkdf2_sha256"
#define PWD_HASH_ARGON2ID "argon2id"

typedef struct _PwdHashParams {
char *algo;
char *params_str;
} PwdHashParams;

void
parse_pwd_hash_params (const char *algo, const char *params_str, PwdHashParams *params);

int
pwd_hash_derive_key (const char *data_in, int in_len,
const char *repo_salt,
const char *algo, const char *params_str,
unsigned char *key);

#endif
12 changes: 11 additions & 1 deletion common/rpc-service.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "seafile-config.h"
#include "seafile-object.h"
#include "seafile-error-impl.h"
#include "password-hash.h"
#define DEBUG_FLAG SEAFILE_DEBUG_OTHER
#include "log.h"

Expand Down Expand Up @@ -863,6 +864,8 @@ GObject *
seafile_generate_magic_and_random_key(int enc_version,
const char* repo_id,
const char *passwd,
const char *pwd_hash_algo,
const char *pwd_hash_params,
GError **error)
{
if (!repo_id || !passwd) {
Expand All @@ -872,13 +875,19 @@ seafile_generate_magic_and_random_key(int enc_version,

gchar salt[65] = {0};
gchar magic[65] = {0};
gchar pwd_hash[65] = {0};
gchar random_key[97] = {0};

if (enc_version >= 3 && seafile_generate_repo_salt (salt) < 0) {
return NULL;
}

seafile_generate_magic (enc_version, repo_id, passwd, salt, magic);
if (g_strcmp0 (pwd_hash_algo, PWD_HASH_PDKDF2) == 0 ||
g_strcmp0 (pwd_hash_algo, PWD_HASH_ARGON2ID) == 0) {
seafile_generate_pwd_hash (enc_version, repo_id, passwd, salt, pwd_hash_algo, pwd_hash_params, pwd_hash);
} else {
seafile_generate_magic (enc_version, repo_id, passwd, salt, magic);
}
if (seafile_generate_random_key (passwd, enc_version, salt, random_key) < 0) {
return NULL;
}
Expand All @@ -889,6 +898,7 @@ seafile_generate_magic_and_random_key(int enc_version,
"passwd", passwd,
"enc_version", enc_version,
"magic", magic,
"pwd_hash", pwd_hash,
"random_key", random_key,
NULL);

Expand Down
Loading

0 comments on commit f5d8b38

Please sign in to comment.