1
0
mirror of https://github.com/haiwen/seafile-server.git synced 2025-05-08 16:16:32 +00:00

Support argon2id password hash algo ()

* Support argon2id password hash algo

* CI install argon2 deps

* Go support argonid algo

* RPC add pwd_hash_algo and pwd_hash_params

* Support password hash algo

* Don't set magic when pwd_hash is set

* Fix ci error

---------

Co-authored-by: 杨赫然 <heran.yang@seafile.com>
This commit is contained in:
feiniks 2024-05-22 18:49:57 +08:00 committed by GitHub
parent 9f5fcdfe4d
commit 96996b79e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 611 additions and 76 deletions

View File

@ -12,7 +12,7 @@ sudo systemctl start mysql.service
sudo apt-get update
sudo apt-get install -y intltool libarchive-dev libcurl4-openssl-dev libevent-dev \
libfuse-dev libglib2.0-dev libjansson-dev libmysqlclient-dev libonig-dev \
sqlite3 libsqlite3-dev libtool net-tools uuid-dev valac
sqlite3 libsqlite3-dev libtool net-tools uuid-dev valac libargon2-dev
sudo systemctl start mysql.service
pip install -r requirements.txt

View File

@ -7,6 +7,7 @@ proc_headers = \
noinst_HEADERS = \
diff-simple.h \
seafile-crypt.h \
password-hash.h \
common.h \
branch-mgr.h \
fs-mgr.h \

View File

@ -170,6 +170,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);
}
@ -633,12 +636,18 @@ 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 pwd_hash is set, the magic field is no longer included in the commit of the newly created repo.
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);
@ -675,6 +684,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;
@ -709,6 +721,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)
@ -739,6 +754,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;
@ -794,12 +813,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;

View File

@ -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;

167
common/password-hash.c Normal file
View File

@ -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];
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];
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
common/password-hash.h Normal file
View File

@ -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

View File

@ -55,6 +55,8 @@ convert_repo (SeafRepo *r)
g_object_set (repo, "id", r->id, "name", r->name,
"desc", r->desc, "encrypted", r->encrypted,
"magic", r->magic, "enc_version", r->enc_version,
"pwd_hash", r->pwd_hash,
"pwd_hash_algo", r->pwd_hash_algo, "pwd_hash_params", r->pwd_hash_params,
"head_cmmt_id", r->head ? r->head->commit_id : NULL,
"root", r->root_id,
"version", r->version, "last_modify", r->last_modify,
@ -705,25 +707,47 @@ 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);
const char *algo = NULL;
const char *params = NULL;
algo = seafile_crypt_get_default_pwd_hash_algo ();
params = seafile_crypt_get_default_pwd_hash_params ();
if (algo != NULL) {
seafile_generate_pwd_hash (repo_id, passwd, salt, algo, 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;
}
SeafileEncryptionInfo *sinfo;
sinfo = g_object_new (SEAFILE_TYPE_ENCRYPTION_INFO,
"repo_id", repo_id,
"passwd", passwd,
"enc_version", enc_version,
"magic", magic,
"random_key", random_key,
NULL);
if (algo != NULL) {
sinfo = g_object_new (SEAFILE_TYPE_ENCRYPTION_INFO,
"repo_id", repo_id,
"passwd", passwd,
"enc_version", enc_version,
"pwd_hash", pwd_hash,
"pwd_hash_algo", algo,
"pwd_hash_params", params,
"random_key", random_key,
NULL);
} else {
sinfo = g_object_new (SEAFILE_TYPE_ENCRYPTION_INFO,
"repo_id", repo_id,
"passwd", passwd,
"enc_version", enc_version,
"magic", magic,
"random_key", random_key,
NULL);
}
if (enc_version >= 3)
g_object_set (sinfo, "salt", salt, NULL);
@ -1040,10 +1064,18 @@ retry:
return -1;
}
if (seafile_verify_repo_passwd (repo_id, old_passwd, repo->magic,
repo->enc_version, repo->salt) < 0) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Incorrect password");
return -1;
if (repo->pwd_hash_algo) {
if (seafile_pwd_hash_verify_repo_passwd (repo_id, old_passwd, repo->salt,
repo->pwd_hash, repo->pwd_hash_algo, repo->pwd_hash_params) < 0) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Incorrect password");
return -1;
}
} else {
if (seafile_verify_repo_passwd (repo_id, old_passwd, repo->magic,
repo->enc_version, repo->salt) < 0) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS, "Incorrect password");
return -1;
}
}
parent = seaf_commit_manager_get_commit (seaf->commit_mgr,
@ -1056,9 +1088,15 @@ retry:
goto out;
}
char new_magic[65], new_random_key[97];
char new_magic[65], new_pwd_hash[65], new_random_key[97];
seafile_generate_magic (repo->enc_version, repo_id, new_passwd, repo->salt, new_magic);
if (repo->pwd_hash_algo) {
seafile_generate_pwd_hash (repo_id, new_passwd, repo->salt,
repo->pwd_hash_algo, repo->pwd_hash_params, new_pwd_hash);
} else {
seafile_generate_magic (repo->enc_version, repo_id, new_passwd, repo->salt,
new_magic);
}
if (seafile_update_random_key (old_passwd, repo->random_key,
new_passwd, new_random_key,
repo->enc_version, repo->salt) < 0) {
@ -1066,7 +1104,11 @@ retry:
goto out;
}
memcpy (repo->magic, new_magic, 64);
if (repo->pwd_hash_algo) {
memcpy (repo->pwd_hash, new_pwd_hash, 64);
} else {
memcpy (repo->magic, new_magic, 64);
}
memcpy (repo->random_key, new_random_key, 96);
commit = seaf_commit_new (NULL,
@ -3064,6 +3106,9 @@ seafile_create_enc_repo (const char *repo_id,
const char *random_key,
const char *salt,
int enc_version,
const char *pwd_hash,
const char *pwd_hash_algo,
const char *pwd_hash_params,
GError **error)
{
if (!repo_id || !repo_name || !repo_desc || !owner_email) {
@ -3076,7 +3121,9 @@ seafile_create_enc_repo (const char *repo_id,
ret = seaf_repo_manager_create_enc_repo (seaf->repo_mgr,
repo_id, repo_name, repo_desc,
owner_email,
magic, random_key, salt, enc_version,
magic, random_key, salt,
enc_version,
pwd_hash, pwd_hash_algo, pwd_hash_params,
error);
return ret;
}

View File

@ -3,6 +3,7 @@
#include <string.h>
#include <glib.h>
#include "seafile-crypt.h"
#include "password-hash.h"
#include <openssl/rand.h>
#include "utils.h"
@ -22,6 +23,14 @@
/* Should generate random salt for each repo. */
static unsigned char salt[8] = { 0xda, 0x90, 0x45, 0xc3, 0x06, 0xc7, 0xcc, 0x26 };
static PwdHashParams default_params;
void
seafile_crypt_init (const char *algo, const char *params)
{
parse_pwd_hash_params (algo, params, &default_params);
}
SeafileCrypt *
seafile_crypt_new (int version, unsigned char *key, unsigned char *iv)
{
@ -35,6 +44,18 @@ seafile_crypt_new (int version, unsigned char *key, unsigned char *iv)
return crypt;
}
const char *
seafile_crypt_get_default_pwd_hash_algo ()
{
return default_params.algo;
}
const char *
seafile_crypt_get_default_pwd_hash_params ()
{
return default_params.params_str;
}
int
seafile_derive_key (const char *data_in, int in_len, int version,
const char *repo_salt,
@ -156,6 +177,29 @@ seafile_generate_magic (int version, const char *repo_id,
rawdata_to_hex (key, magic, 32);
}
void
seafile_generate_pwd_hash (const char *repo_id,
const char *passwd,
const char *repo_salt,
const char *algo,
const char *params_str,
char *pwd_hash)
{
GString *buf = g_string_new (NULL);
unsigned char key[32];
/* Compute a "pwd_hash" string from repo_id and passwd.
* This is used to verify the password given by user before decrypting
* data.
*/
g_string_append_printf (buf, "%s%s", repo_id, passwd);
pwd_hash_derive_key (buf->str, buf->len, repo_salt, algo, params_str, key);
g_string_free (buf, TRUE);
rawdata_to_hex (key, pwd_hash, 32);
}
int
seafile_verify_repo_passwd (const char *repo_id,
const char *passwd,
@ -189,6 +233,31 @@ seafile_verify_repo_passwd (const char *repo_id,
return -1;
}
int
seafile_pwd_hash_verify_repo_passwd (const char *repo_id,
const char *passwd,
const char *repo_salt,
const char *pwd_hash,
const char *algo,
const char *params_str)
{
GString *buf = g_string_new (NULL);
unsigned char key[32];
char hex[65];
g_string_append_printf (buf, "%s%s", repo_id, passwd);
pwd_hash_derive_key (buf->str, buf->len, repo_salt, algo, params_str, key);
g_string_free (buf, TRUE);
rawdata_to_hex (key, hex, 32);
if (g_strcmp0 (hex, pwd_hash) == 0)
return 0;
else
return -1;
}
int
seafile_decrypt_repo_enc_key (int enc_version,
const char *passwd, const char *random_key,

View File

@ -27,9 +27,18 @@ struct SeafileCrypt {
typedef struct SeafileCrypt SeafileCrypt;
void
seafile_crypt_init (const char *algo, const char *params);
SeafileCrypt *
seafile_crypt_new (int version, unsigned char *key, unsigned char *iv);
const char *
seafile_crypt_get_default_pwd_hash_algo ();
const char *
seafile_crypt_get_default_pwd_hash_params ();
/*
Derive key and iv used by AES encryption from @data_in.
key and iv is 16 bytes for version 1, and 32 bytes for version 2.
@ -77,6 +86,14 @@ seafile_generate_magic (int version, const char *repo_id,
const char *repo_salt,
char *magic);
void
seafile_generate_pwd_hash (const char *repo_id,
const char *passwd,
const char *repo_salt,
const char *algo,
const char *params_str,
char *pwd_hash);
int
seafile_verify_repo_passwd (const char *repo_id,
const char *passwd,
@ -84,6 +101,14 @@ seafile_verify_repo_passwd (const char *repo_id,
int version,
const char *repo_salt);
int
seafile_pwd_hash_verify_repo_passwd (const char *repo_id,
const char *passwd,
const char *repo_salt,
const char *pwd_hash,
const char *algo,
const char *params_str);
int
seafile_decrypt_repo_enc_key (int enc_version,
const char *passwd, const char *random_key,

View File

@ -274,6 +274,10 @@ PKG_CHECK_MODULES(JWT, [libjwt])
AC_SUBST(JWT_CFLAGS)
AC_SUBST(JWT_LIBS)
PKG_CHECK_MODULES(ARGON2, [libargon2])
AC_SUBST(ARGON2_CFLAGS)
AC_SUBST(ARGON2_LIBS)
if test x${compile_python} = xyes; then
AM_PATH_PYTHON([2.6])
if test "$bwin32" = true; then

View File

@ -36,6 +36,9 @@ type Commit struct {
Magic string `json:"magic,omitempty"`
RandomKey string `json:"key,omitempty"`
Salt string `json:"salt,omitempty"`
PwdHash string `json:"pwd_hash,omitempty"`
PwdHashAlgo string `json:"pwd_hash_algo,omitempty"`
PwdHashParams string `json:"pwd_hash_params,omitempty"`
Version int `json:"version,omitempty"`
Conflict int `json:"conflict,omitempty"`
NewMerge int `json:"new_merge,omitempty"`

View File

@ -37,12 +37,15 @@ type Repo struct {
StoreID string
// Encrypted repo info
IsEncrypted bool
EncVersion int
Magic string
RandomKey string
Salt string
Version int
IsEncrypted bool
EncVersion int
Magic string
RandomKey string
Salt string
PwdHash string
PwdHashAlgo string
PwdHashParams string
Version int
}
// VRepoInfo contains virtual repo information.
@ -132,20 +135,25 @@ func Get(id string) *Repo {
if commit.Encrypted == "true" {
repo.IsEncrypted = true
repo.EncVersion = commit.EncVersion
if repo.EncVersion == 1 {
if repo.EncVersion == 1 && commit.PwdHash == "" {
repo.Magic = commit.Magic
} else if repo.EncVersion == 2 {
repo.Magic = commit.Magic
repo.RandomKey = commit.RandomKey
} else if repo.EncVersion == 3 {
repo.Magic = commit.Magic
repo.RandomKey = commit.RandomKey
repo.Salt = commit.Salt
} else if repo.EncVersion == 4 {
repo.Magic = commit.Magic
repo.RandomKey = commit.RandomKey
repo.Salt = commit.Salt
}
if repo.EncVersion >= 2 && commit.PwdHash == "" {
repo.Magic = commit.Magic
}
if commit.PwdHash != "" {
repo.PwdHash = commit.PwdHash
repo.PwdHashAlgo = commit.PwdHashAlgo
repo.PwdHashParams = commit.PwdHashParams
}
}
return repo
@ -158,20 +166,25 @@ func RepoToCommit(repo *Repo, commit *commitmgr.Commit) {
if repo.IsEncrypted {
commit.Encrypted = "true"
commit.EncVersion = repo.EncVersion
if repo.EncVersion == 1 {
if repo.EncVersion == 1 && repo.PwdHash == "" {
commit.Magic = repo.Magic
} else if repo.EncVersion == 2 {
commit.Magic = repo.Magic
commit.RandomKey = repo.RandomKey
} else if repo.EncVersion == 3 {
commit.Magic = repo.Magic
commit.RandomKey = repo.RandomKey
commit.Salt = repo.Salt
} else if repo.EncVersion == 4 {
commit.Magic = repo.Magic
commit.RandomKey = repo.RandomKey
commit.Salt = repo.Salt
}
if repo.EncVersion >= 2 && repo.PwdHash == "" {
commit.Magic = repo.Magic
}
if repo.PwdHash != "" {
commit.PwdHash = repo.PwdHash
commit.PwdHashAlgo = repo.PwdHashAlgo
commit.PwdHashParams = repo.PwdHashParams
}
} else {
commit.Encrypted = "false"
}
@ -260,6 +273,15 @@ func GetEx(id string) *Repo {
repo.Magic = commit.Magic
repo.RandomKey = commit.RandomKey
repo.Salt = commit.Salt
} else if repo.EncVersion == 4 {
repo.Magic = commit.Magic
repo.RandomKey = commit.RandomKey
repo.Salt = commit.Salt
}
if commit.PwdHash != "" {
repo.PwdHash = commit.PwdHash
repo.PwdHashAlgo = commit.PwdHashAlgo
repo.PwdHashParams = commit.PwdHashParams
}
}

View File

@ -36,11 +36,12 @@ seaf_fuse_SOURCES = seaf-fuse.c \
../common/obj-store.c \
../common/obj-backend-fs.c \
../common/obj-backend-riak.c \
../common/seafile-crypt.c
../common/seafile-crypt.c \
../common/password-hash.c
seaf_fuse_LDADD = @GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_RT@ @LIB_UUID@ \
-lsqlite3 @LIBEVENT_LIBS@ \
$(top_builddir)/common/cdc/libcdc.la \
@SEARPC_LIBS@ @JANSSON_LIBS@ @FUSE_LIBS@ @ZLIB_LIBS@ \
@LDAP_LIBS@ @MYSQL_LIBS@ -lsqlite3
@LDAP_LIBS@ @MYSQL_LIBS@ -lsqlite3 @ARGON2_LIBS@

View File

@ -923,6 +923,9 @@ seafile_create_enc_repo (const char *repo_id,
const char *random_key,
const char *salt,
int enc_version,
const char *pwd_hash,
const char *pwd_hash_algo,
const char *pwd_hash_params,
GError **error);
char *

View File

@ -56,6 +56,9 @@ public class Repo : Object {
public int enc_version { get; set; }
public string random_key { get; set; }
public string salt { get; set; }
public string pwd_hash { get; set; }
public string pwd_hash_algo { get; set; }
public string pwd_hash_params { get; set; }
// Section 3: Client only information
// Should be set for all client repo objects
@ -202,6 +205,9 @@ public class EncryptionInfo: Object {
public string magic { get; set; }
public string random_key { get; set; }
public string salt { get; set; }
public string pwd_hash { get; set; }
public string pwd_hash_algo { get; set; }
public string pwd_hash_params { get; set; }
}
public class UserQuotaUsage: Object {

View File

@ -68,6 +68,7 @@ func_table = [
[ "string", ["string", "string", "string", "string", "string", "string", "string", "int"] ],
[ "string", ["string", "string", "string", "string", "string", "string", "string", "int64"] ],
[ "string", ["string", "string", "string", "string", "string", "string", "string", "string", "string"] ],
[ "string", ["string", "string", "string", "string", "string", "string", "string", "int", "string", "string", "string"] ],
[ "string", ["string", "int", "string", "string", "string", "string", "string", "string", "string", "string", "string", "string", "int", "string"] ],
[ "string", ["string", "int", "string", "int", "int"] ],
[ "string", ["string", "int", "string", "string", "string"] ],
@ -103,6 +104,7 @@ func_table = [
[ "object", ["string", "string", "string"] ],
[ "object", ["string", "int", "string"] ],
[ "object", ["int", "string", "string"] ],
[ "object", ["int", "string", "string", "string", "string"] ],
[ "object", ["string", "string", "int", "int"] ],
[ "object", ["string", "string", "string", "int"] ],
[ "object", ["string", "string", "string", "string", "string", "string", "string", "int", "int"] ],

View File

@ -11,8 +11,8 @@ class SeafServerThreadedRpcClient(NamedPipeClient):
pass
create_repo = seafile_create_repo
@searpc_func("string", ["string", "string", "string", "string", "string", "string", "string", "int"])
def seafile_create_enc_repo(repo_id, name, desc, owner_email, magic, random_key, salt, enc_version):
@searpc_func("string", ["string", "string", "string", "string", "string", "string", "string", "int", "string", "string", "string"])
def seafile_create_enc_repo(repo_id, name, desc, owner_email, magic, random_key, salt, enc_version, pwd_hash, pwd_hash_algo, pwd_hash_params):
pass
create_enc_repo = seafile_create_enc_repo

View File

@ -89,8 +89,8 @@ class SeafileAPI(object):
def create_repo(self, name, desc, username, passwd=None, enc_version=2, storage_id=None):
return seafserv_threaded_rpc.create_repo(name, desc, username, passwd, enc_version)
def create_enc_repo(self, repo_id, name, desc, username, magic, random_key, salt, enc_version):
return seafserv_threaded_rpc.create_enc_repo(repo_id, name, desc, username, magic, random_key, salt, enc_version)
def create_enc_repo(self, repo_id, name, desc, username, magic, random_key, salt, enc_version, pwd_hash=None, pwd_hash_algo=None, pwd_hash_params=None):
return seafserv_threaded_rpc.create_enc_repo(repo_id, name, desc, username, magic, random_key, salt, enc_version, pwd_hash, pwd_hash_algo, pwd_hash_params)
def get_repos_by_id_prefix(self, id_prefix, start=-1, limit=-1):
"""

View File

@ -69,6 +69,7 @@ seaf_server_SOURCES = \
../common/obj-store.c \
../common/obj-backend-fs.c \
../common/seafile-crypt.c \
../common/password-hash.c \
../common/diff-simple.c \
../common/mq-mgr.c \
../common/user-mgr.c \
@ -86,4 +87,4 @@ seaf_server_LDADD = $(top_builddir)/lib/libseafile_common.la \
@SEARPC_LIBS@ @JANSSON_LIBS@ ${LIB_WS32} @ZLIB_LIBS@ \
@LIBARCHIVE_LIBS@ @LIB_ICONV@ \
@LDAP_LIBS@ @MYSQL_LIBS@ -lsqlite3 \
@CURL_LIBS@ @JWT_LIBS@
@CURL_LIBS@ @JWT_LIBS@ @ARGON2_LIBS@

View File

@ -36,6 +36,7 @@ common_sources = \
../../common/obj-store.c \
../../common/obj-backend-fs.c \
../../common/seafile-crypt.c \
../../common/password-hash.c \
../../common/config-mgr.c
seafserv_gc_SOURCES = \
@ -48,7 +49,7 @@ seafserv_gc_LDADD = $(top_builddir)/common/cdc/libcdc.la \
$(top_builddir)/lib/libseafile_common.la \
@GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_RT@ @LIB_UUID@ -lsqlite3 @LIBEVENT_LIBS@ \
@SEARPC_LIBS@ @JANSSON_LIBS@ ${LIB_WS32} @ZLIB_LIBS@ \
@MYSQL_LIBS@ -lsqlite3
@MYSQL_LIBS@ -lsqlite3 @ARGON2_LIBS@
seaf_fsck_SOURCES = \
seaf-fsck.c \
@ -59,4 +60,4 @@ seaf_fsck_LDADD = $(top_builddir)/common/cdc/libcdc.la \
$(top_builddir)/lib/libseafile_common.la \
@GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_RT@ @LIB_UUID@ -lsqlite3 @LIBEVENT_LIBS@ \
@SEARPC_LIBS@ @JANSSON_LIBS@ ${LIB_WS32} @ZLIB_LIBS@ \
@MYSQL_LIBS@ -lsqlite3
@MYSQL_LIBS@ -lsqlite3 @ARGON2_LIBS@

View File

@ -125,12 +125,23 @@ seaf_passwd_manager_set_passwd (SeafPasswdManager *mgr,
return -1;
}
if (seafile_verify_repo_passwd (repo->id, passwd,
repo->magic, repo->enc_version, repo->salt) < 0) {
seaf_repo_unref (repo);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Incorrect password");
return -1;
if (repo->pwd_hash_algo) {
if (seafile_pwd_hash_verify_repo_passwd (repo->id, passwd, repo->salt,
repo->pwd_hash, repo->pwd_hash_algo, repo->pwd_hash_params) < 0) {
seaf_repo_unref (repo);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Incorrect password");
return -1;
}
} else {
if (seafile_verify_repo_passwd (repo->id, passwd,
repo->magic,
repo->enc_version, repo->salt) < 0) {
seaf_repo_unref (repo);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Incorrect password");
return -1;
}
}
crypt_key = g_new0 (DecryptKey, 1);

View File

@ -18,6 +18,7 @@
#include "fs-mgr.h"
#include "seafile-error.h"
#include "seafile-crypt.h"
#include "password-hash.h"
#include "seaf-db.h"
#include "seaf-utils.h"
@ -89,6 +90,8 @@ seaf_repo_free (SeafRepo *repo)
if (repo->virtual_info)
seaf_virtual_repo_info_free (repo->virtual_info);
g_free (repo->last_modifier);
g_free (repo->pwd_hash_algo);
g_free (repo->pwd_hash_params);
g_free (repo);
}
@ -137,20 +140,25 @@ seaf_repo_from_commit (SeafRepo *repo, SeafCommit *commit)
memcpy (repo->root_id, commit->root_id, 40);
if (repo->encrypted) {
repo->enc_version = commit->enc_version;
if (repo->enc_version == 1)
if (repo->enc_version == 1 && !commit->pwd_hash_algo)
memcpy (repo->magic, commit->magic, 32);
else if (repo->enc_version == 2) {
memcpy (repo->magic, commit->magic, 64);
memcpy (repo->random_key, commit->random_key, 96);
} else if (repo->enc_version == 3) {
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) {
memcpy (repo->magic, commit->magic, 64);
memcpy (repo->random_key, commit->random_key, 96);
memcpy (repo->salt, commit->salt, 64);
}
if (repo->enc_version >= 2 && !commit->pwd_hash_algo) {
memcpy (repo->magic, commit->magic, 64);
}
if (commit->pwd_hash_algo) {
memcpy (repo->pwd_hash, commit->pwd_hash, 64);
repo->pwd_hash_algo = g_strdup (commit->pwd_hash_algo);
repo->pwd_hash_params = g_strdup (commit->pwd_hash_params);
}
}
repo->no_local_history = commit->no_local_history;
repo->version = commit->version;
@ -166,20 +174,25 @@ seaf_repo_to_commit (SeafRepo *repo, SeafCommit *commit)
commit->repaired = repo->repaired;
if (commit->encrypted) {
commit->enc_version = repo->enc_version;
if (commit->enc_version == 1)
if (commit->enc_version == 1 && !repo->pwd_hash_algo)
commit->magic = g_strdup (repo->magic);
else if (commit->enc_version == 2) {
commit->magic = g_strdup (repo->magic);
commit->random_key = g_strdup (repo->random_key);
} else if (commit->enc_version == 3) {
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) {
commit->magic = g_strdup (repo->magic);
commit->random_key = g_strdup (repo->random_key);
commit->salt = g_strdup (repo->salt);
}
if (commit->enc_version >= 2 && !repo->pwd_hash_algo) {
commit->magic = g_strdup (repo->magic);
}
if (repo->pwd_hash_algo) {
commit->pwd_hash = g_strdup (repo->pwd_hash);
commit->pwd_hash_algo = g_strdup (repo->pwd_hash_algo);
commit->pwd_hash_params = g_strdup (repo->pwd_hash_params);
}
}
commit->no_local_history = repo->no_local_history;
commit->version = repo->version;
@ -3728,16 +3741,37 @@ seaf_repo_manager_is_valid_filename (SeafRepoManager *mgr,
return 1;
}
typedef struct _RepoCryptCompat {
const char *magic;
const char *pwd_hash;
const char *pwd_hash_algo;
const char *pwd_hash_params;
} RepoCryptInfo;
static
RepoCryptInfo *
repo_crypt_info_new (const char *magic, const char *pwd_hash,
const char *algo, const char *params)
{
RepoCryptInfo *crypt_info = g_new0 (RepoCryptInfo, 1);
crypt_info->magic = magic;
crypt_info->pwd_hash = pwd_hash;
crypt_info->pwd_hash_algo = algo;
crypt_info->pwd_hash_params = params;
return crypt_info;
}
static int
create_repo_common (SeafRepoManager *mgr,
const char *repo_id,
const char *repo_name,
const char *repo_desc,
const char *user,
const char *magic,
const char *random_key,
const char *salt,
int enc_version,
RepoCryptInfo *crypt_info,
GError **error)
{
SeafRepo *repo = NULL;
@ -3751,9 +3785,27 @@ create_repo_common (SeafRepoManager *mgr,
"Unsupported encryption version");
return -1;
}
if (crypt_info && crypt_info->pwd_hash_algo) {
if (g_strcmp0 (crypt_info->pwd_hash_algo, PWD_HASH_PDKDF2) != 0 &&
g_strcmp0 (crypt_info->pwd_hash_algo, PWD_HASH_ARGON2ID) !=0)
{
seaf_warning ("Unsupported enc algothrims %s.\n", crypt_info->pwd_hash_algo);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
"Unsupported encryption algothrims");
return -1;
}
if (!crypt_info->pwd_hash || strlen(crypt_info->pwd_hash) != 64) {
seaf_warning ("Bad pwd_hash.\n");
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
"Bad pwd_hash");
return -1;
}
}
if (enc_version >= 2) {
if (!magic || strlen(magic) != 64) {
if (!crypt_info->pwd_hash_algo && (!crypt_info->magic || strlen(crypt_info->magic) != 64)) {
seaf_warning ("Bad magic.\n");
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
"Bad magic");
@ -3778,15 +3830,24 @@ create_repo_common (SeafRepoManager *mgr,
repo = seaf_repo_new (repo_id, repo_name, repo_desc);
repo->no_local_history = TRUE;
if (enc_version >= 2) {
repo->encrypted = TRUE;
repo->enc_version = enc_version;
memcpy (repo->magic, magic, 64);
if (!crypt_info->pwd_hash_algo)
memcpy (repo->magic, crypt_info->magic, 64);
memcpy (repo->random_key, random_key, 96);
}
if (enc_version >= 3)
memcpy (repo->salt, salt, 64);
if (crypt_info && crypt_info->pwd_hash_algo) {
// set pwd_hash fields here.
memcpy (repo->pwd_hash, crypt_info->pwd_hash, 64);
repo->pwd_hash_algo = g_strdup (crypt_info->pwd_hash_algo);
repo->pwd_hash_params = g_strdup (crypt_info->pwd_hash_params);
}
repo->version = CURRENT_REPO_VERSION;
memcpy (repo->store_id, repo_id, 36);
@ -3851,7 +3912,9 @@ seaf_repo_manager_create_new_repo (SeafRepoManager *mgr,
GError **error)
{
char *repo_id = NULL;
char salt[65], magic[65], random_key[97];
char salt[65], magic[65], pwd_hash[65], random_key[97];
const char *algo = seafile_crypt_get_default_pwd_hash_algo ();
const char *params = seafile_crypt_get_default_pwd_hash_params ();
repo_id = gen_uuid ();
@ -3859,19 +3922,26 @@ seaf_repo_manager_create_new_repo (SeafRepoManager *mgr,
if (seafile_generate_repo_salt (salt) < 0) {
goto bad;
}
seafile_generate_magic (enc_version, repo_id, passwd, salt, magic);
if (algo != NULL) {
seafile_generate_pwd_hash (repo_id, passwd, salt, algo, 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) {
goto bad;
}
}
int rc;
if (passwd)
if (passwd) {
RepoCryptInfo *crypt_info = repo_crypt_info_new (magic, pwd_hash, algo, params);
rc = create_repo_common (mgr, repo_id, repo_name, repo_desc, owner_email,
magic, random_key, salt, enc_version, error);
random_key, salt, enc_version, crypt_info, error);
g_free (crypt_info);
}
else
rc = create_repo_common (mgr, repo_id, repo_name, repo_desc, owner_email,
NULL, NULL, NULL, -1, error);
NULL, NULL, -1, NULL, error);
if (rc < 0)
goto bad;
@ -3900,6 +3970,9 @@ seaf_repo_manager_create_enc_repo (SeafRepoManager *mgr,
const char *random_key,
const char *salt,
int enc_version,
const char *pwd_hash,
const char *pwd_hash_algo,
const char *pwd_hash_params,
GError **error)
{
if (!repo_id || !is_uuid_valid (repo_id)) {
@ -3916,9 +3989,13 @@ seaf_repo_manager_create_enc_repo (SeafRepoManager *mgr,
return NULL;
}
RepoCryptInfo *crypt_info = repo_crypt_info_new (magic, pwd_hash, pwd_hash_algo, pwd_hash_params);
if (create_repo_common (mgr, repo_id, repo_name, repo_desc, owner_email,
magic, random_key, salt, enc_version, error) < 0)
random_key, salt, enc_version, crypt_info, error) < 0) {
g_free (crypt_info);
return NULL;
}
g_free (crypt_info);
if (seaf_repo_manager_set_repo_owner (mgr, repo_id, owner_email) < 0) {
seaf_warning ("Failed to set repo owner.\n");

View File

@ -33,6 +33,9 @@ struct _SeafRepo {
gboolean encrypted;
int enc_version;
gchar magic[65]; /* hash(repo_id + passwd), key stretched. */
gchar pwd_hash[65]; /* hash(repo_id + passwd), key stretched. */
gchar *pwd_hash_algo;
gchar *pwd_hash_params;
gchar random_key[97];
gchar salt[65];
gboolean no_local_history;
@ -513,6 +516,9 @@ seaf_repo_manager_create_enc_repo (SeafRepoManager *mgr,
const char *random_key,
const char *salt,
int enc_version,
const char *pwd_hash,
const char *pwd_hash_algo,
const char *pwd_hash_params,
GError **error);
/* Give a repo and a path in this repo, returns a list of commits, where every

View File

@ -227,7 +227,7 @@ static void start_rpc_service (const char *seafile_dir,
searpc_server_register_function ("seafserv-threaded-rpcserver",
seafile_create_enc_repo,
"seafile_create_enc_repo",
searpc_signature_string__string_string_string_string_string_string_string_int());
searpc_signature_string__string_string_string_string_string_string_string_int_string_string_string());
searpc_server_register_function ("seafserv-threaded-rpcserver",
seafile_get_commit,

View File

@ -122,6 +122,8 @@ seafile_session_new(const char *central_config_dir,
char *notif_server = NULL;
int notif_port = 8083;
char *private_key = NULL;
char *pwd_hash_algo = NULL;
char *pwd_hash_params = NULL;
abs_ccnet_dir = ccnet_expand_path (ccnet_dir);
abs_seafile_dir = ccnet_expand_path (seafile_dir);
@ -208,6 +210,17 @@ seafile_session_new(const char *central_config_dir,
session->private_key = private_key;
}
pwd_hash_algo = g_key_file_get_string (config,
"password_hash", "pwd_hash_algo",
NULL);
pwd_hash_params = g_key_file_get_string (config,
"password_hash", "pwd_hash_params",
NULL);
seafile_crypt_init (pwd_hash_algo, pwd_hash_params);
g_free (pwd_hash_algo);
g_free (pwd_hash_params);
if (load_database_config (session) < 0) {
seaf_warning ("Failed to load database config.\n");
goto onerror;
@ -309,6 +322,8 @@ onerror:
free (abs_seafile_dir);
free (abs_ccnet_dir);
g_free (tmp_file_dir);
g_free (pwd_hash_algo);
g_free (pwd_hash_params);
g_free (session);
return NULL;
}

View File

@ -85,7 +85,17 @@ do_create_virtual_repo (SeafRepoManager *mgr,
repo->enc_version = origin_repo->enc_version;
if (repo->enc_version >= 3)
memcpy (repo->salt, origin_repo->salt, 64);
seafile_generate_magic (repo->enc_version, repo_id, passwd, repo->salt, repo->magic);
if (origin_repo->pwd_hash_algo)
repo->pwd_hash_algo = g_strdup (origin_repo->pwd_hash_algo);
if (origin_repo->pwd_hash_params)
repo->pwd_hash_params = g_strdup (origin_repo->pwd_hash_params);
if (repo->pwd_hash_algo) {
seafile_generate_pwd_hash (repo_id, passwd, repo->salt,
repo->pwd_hash_algo, repo->pwd_hash_params, repo->pwd_hash);
memcpy (repo->magic, repo->pwd_hash, 32);
} else
seafile_generate_magic (repo->enc_version, repo_id, passwd, repo->salt,
repo->magic);
if (repo->enc_version >= 2)
memcpy (repo->random_key, origin_repo->random_key, 96);
}
@ -220,15 +230,29 @@ create_virtual_repo_common (SeafRepoManager *mgr,
return NULL;
}
if (seafile_verify_repo_passwd (origin_repo_id,
passwd,
origin_repo->magic,
origin_repo->enc_version,
origin_repo->salt) < 0) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Incorrect password");
seaf_repo_unref (origin_repo);
return NULL;
if (origin_repo->pwd_hash_algo) {
if (seafile_pwd_hash_verify_repo_passwd (origin_repo_id,
passwd,
origin_repo->salt,
origin_repo->pwd_hash,
origin_repo->pwd_hash_algo,
origin_repo->pwd_hash_params) < 0) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Incorrect password");
seaf_repo_unref (origin_repo);
return NULL;
}
} else {
if (seafile_verify_repo_passwd (origin_repo_id,
passwd,
origin_repo->magic,
origin_repo->enc_version,
origin_repo->salt) < 0) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Incorrect password");
seaf_repo_unref (origin_repo);
return NULL;
}
}
}