Add support for Argon2 from libgcrypt.

Argon2 is available since version 1.10, but we need version
that allows empty passwords (1.11).
This commit is contained in:
Milan Broz
2022-08-12 22:21:42 +02:00
parent bc426bba67
commit 34953cb10f
6 changed files with 207 additions and 10 deletions

View File

@@ -267,6 +267,9 @@ AC_DEFUN([CONFIGURE_GCRYPT], [
GCRYPT_REQ_VERSION=1.1.42 GCRYPT_REQ_VERSION=1.1.42
fi fi
use_internal_pbkdf2=0
use_internal_argon2=1
dnl libgcrypt rejects to use pkgconfig, use AM_PATH_LIBGCRYPT from gcrypt-devel here. dnl libgcrypt rejects to use pkgconfig, use AM_PATH_LIBGCRYPT from gcrypt-devel here.
dnl Do not require gcrypt-devel if other crypto backend is used. dnl Do not require gcrypt-devel if other crypto backend is used.
m4_ifdef([AM_PATH_LIBGCRYPT],[ m4_ifdef([AM_PATH_LIBGCRYPT],[
@@ -290,7 +293,24 @@ AC_DEFUN([CONFIGURE_GCRYPT], [
NO_FIPS([]) NO_FIPS([])
fi fi
m4_ifdef([AM_PATH_LIBGCRYPT],[
AC_ARG_ENABLE([gcrypt-argon2],
dnl Check if we can use gcrypt Argon2 (1.11.0 supports empty password)
AS_HELP_STRING([--disable-gcrypt-argon2], [force disable internal gcrypt Argon2]),
[],
[AM_PATH_LIBGCRYPT([1.11.0], [use_internal_argon2=0], [use_internal_argon2=1])])
AM_PATH_LIBGCRYPT($GCRYPT_REQ_VERSION,,[AC_MSG_ERROR([You need the gcrypt library.])])],
AC_MSG_ERROR([Missing support for gcrypt: install gcrypt and regenerate configure.]))
AC_MSG_CHECKING([if internal cryptsetup Argon2 is compiled-in])
if test $use_internal_argon2 = 0; then
AC_MSG_RESULT([no])
else
AC_MSG_RESULT([yes])
fi
AC_CHECK_DECLS([GCRY_CIPHER_MODE_XTS], [], [], [#include <gcrypt.h>]) AC_CHECK_DECLS([GCRY_CIPHER_MODE_XTS], [], [], [#include <gcrypt.h>])
AC_CHECK_DECLS([GCRY_KDF_ARGON2], [], [], [#include <gcrypt.h>])
if test "x$enable_static_cryptsetup" = "xyes"; then if test "x$enable_static_cryptsetup" = "xyes"; then
saved_LIBS=$LIBS saved_LIBS=$LIBS
@@ -315,6 +335,7 @@ AC_DEFUN([CONFIGURE_OPENSSL], [
CRYPTO_CFLAGS=$LIBCRYPTO_CFLAGS CRYPTO_CFLAGS=$LIBCRYPTO_CFLAGS
CRYPTO_LIBS=$LIBCRYPTO_LIBS CRYPTO_LIBS=$LIBCRYPTO_LIBS
use_internal_pbkdf2=0 use_internal_pbkdf2=0
use_internal_argon2=1
if test "x$enable_static_cryptsetup" = "xyes"; then if test "x$enable_static_cryptsetup" = "xyes"; then
saved_PKG_CONFIG=$PKG_CONFIG saved_PKG_CONFIG=$PKG_CONFIG
@@ -343,6 +364,7 @@ AC_DEFUN([CONFIGURE_NSS], [
CRYPTO_CFLAGS=$NSS_CFLAGS CRYPTO_CFLAGS=$NSS_CFLAGS
CRYPTO_LIBS=$NSS_LIBS CRYPTO_LIBS=$NSS_LIBS
use_internal_pbkdf2=1 use_internal_pbkdf2=1
use_internal_argon2=1
NO_FIPS([]) NO_FIPS([])
]) ])
@@ -353,6 +375,7 @@ AC_DEFUN([CONFIGURE_KERNEL], [
# [AC_MSG_ERROR([You need Linux kernel with userspace crypto interface.])], # [AC_MSG_ERROR([You need Linux kernel with userspace crypto interface.])],
# [#include <sys/socket.h>]) # [#include <sys/socket.h>])
use_internal_pbkdf2=1 use_internal_pbkdf2=1
use_internal_argon2=1
NO_FIPS([]) NO_FIPS([])
]) ])
@@ -369,6 +392,7 @@ AC_DEFUN([CONFIGURE_NETTLE], [
CRYPTO_STATIC_LIBS=$CRYPTO_LIBS CRYPTO_STATIC_LIBS=$CRYPTO_LIBS
use_internal_pbkdf2=0 use_internal_pbkdf2=0
use_internal_argon2=1
NO_FIPS([]) NO_FIPS([])
]) ])
@@ -493,7 +517,15 @@ AC_ARG_ENABLE([internal-argon2],
AC_ARG_ENABLE([libargon2], AC_ARG_ENABLE([libargon2],
AS_HELP_STRING([--enable-libargon2], [enable external libargon2 (PHC) library (disables internal bundled version)])) AS_HELP_STRING([--enable-libargon2], [enable external libargon2 (PHC) library (disables internal bundled version)]))
if test "x$enable_libargon2" = "xyes" ; then if test $use_internal_argon2 = 0 -o "x$enable_internal_argon2" = "xno" ; then
if test "x$enable_internal_argon2" = "xyes" -o "x$enable_libargon" = "xyes"; then
AC_MSG_WARN([Argon2 in $with_crypto_backend lib is used; internal Argon2 options are ignored.])
fi
enable_internal_argon2=no
enable_internal_sse_argon2=no
enable_libargon2=no
use_internal_argon2=0
elif test "x$enable_libargon2" = "xyes" ; then
AC_CHECK_HEADERS(argon2.h,, AC_CHECK_HEADERS(argon2.h,,
[AC_MSG_ERROR([You need libargon2 development library installed.])]) [AC_MSG_ERROR([You need libargon2 development library installed.])])
AC_CHECK_DECL(Argon2_id,,[AC_MSG_ERROR([You need more recent Argon2 library with support for Argon2id.])], [#include <argon2.h>]) AC_CHECK_DECL(Argon2_id,,[AC_MSG_ERROR([You need more recent Argon2 library with support for Argon2id.])], [#include <argon2.h>])
@@ -517,11 +549,10 @@ else
fi fi
fi fi
if test "x$enable_internal_argon2" = "xyes"; then
AC_DEFINE(USE_INTERNAL_ARGON2, 1, [Use internal Argon2])
fi
AM_CONDITIONAL(CRYPTO_INTERNAL_ARGON2, test "x$enable_internal_argon2" = "xyes") AM_CONDITIONAL(CRYPTO_INTERNAL_ARGON2, test "x$enable_internal_argon2" = "xyes")
AM_CONDITIONAL(CRYPTO_INTERNAL_SSE_ARGON2, test "x$enable_internal_sse_argon2" = "xyes") AM_CONDITIONAL(CRYPTO_INTERNAL_SSE_ARGON2, test "x$enable_internal_sse_argon2" = "xyes")
dnl If libargon is in use, we have defined HAVE_ARGON2_H
AC_DEFINE_UNQUOTED(USE_INTERNAL_ARGON2, [$use_internal_argon2], [Use internal Argon2])
dnl Link with blkid to check for other device types dnl Link with blkid to check for other device types
AC_ARG_ENABLE([blkid], AC_ARG_ENABLE([blkid],

View File

@@ -23,6 +23,7 @@
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include <gcrypt.h> #include <gcrypt.h>
#include <pthread.h>
#include "crypto_backend_internal.h" #include "crypto_backend_internal.h"
static int crypto_backend_initialised = 0; static int crypto_backend_initialised = 0;
@@ -386,6 +387,130 @@ static int pbkdf2(const char *hash,
#endif /* USE_INTERNAL_PBKDF2 */ #endif /* USE_INTERNAL_PBKDF2 */
} }
#if HAVE_DECL_GCRY_KDF_ARGON2 && !USE_INTERNAL_ARGON2
struct gcrypt_thread_job
{
pthread_t thread;
struct job_thread_param {
gcry_kdf_job_fn_t job;
void *p;
} work;
};
struct gcrypt_threads
{
pthread_attr_t attr;
unsigned int num_threads;
unsigned int max_threads;
struct gcrypt_thread_job *jobs_ctx;
};
static void *gcrypt_job_thread(void *p)
{
struct job_thread_param *param = p;
param->job(param->p);
pthread_exit(NULL);
}
static int gcrypt_wait_all_jobs(void *ctx)
{
int i;
struct gcrypt_threads *threads = ctx;
for (i = 0; i < threads->num_threads; i++) {
pthread_join(threads->jobs_ctx[i].thread, NULL);
threads->jobs_ctx[i].thread = 0;
}
threads->num_threads = 0;
return 0;
}
static int gcrypt_dispatch_job(void *ctx, gcry_kdf_job_fn_t job, void *p)
{
struct gcrypt_threads *threads = ctx;
if (threads->num_threads >= threads->max_threads)
return -1;
threads->jobs_ctx[threads->num_threads].work.job = job;
threads->jobs_ctx[threads->num_threads].work.p = p;
if (pthread_create(&threads->jobs_ctx[threads->num_threads].thread, &threads->attr,
gcrypt_job_thread, &threads->jobs_ctx[threads->num_threads].work))
return -1;
threads->num_threads++;
return 0;
}
static int gcrypt_argon2(const char *type,
const char *password, size_t password_length,
const char *salt, size_t salt_length,
char *key, size_t key_length,
uint32_t iterations, uint32_t memory, uint32_t parallel)
{
gcry_kdf_hd_t hd;
int atype, r = -EINVAL;
unsigned long param[4];
struct gcrypt_threads threads = {
.max_threads = parallel,
.num_threads = 0
};
const gcry_kdf_thread_ops_t ops = {
.jobs_context = &threads,
.dispatch_job = gcrypt_dispatch_job,
.wait_all_jobs = gcrypt_wait_all_jobs
};
if (!strcmp(type, "argon2i"))
atype = GCRY_KDF_ARGON2I;
else if (!strcmp(type, "argon2id"))
atype = GCRY_KDF_ARGON2ID;
else
return -EINVAL;
param[0] = key_length;
param[1] = iterations;
param[2] = memory;
param[3] = parallel;
if (gcry_kdf_open(&hd, GCRY_KDF_ARGON2, atype, param, 4,
password, password_length, salt, salt_length,
NULL, 0, NULL, 0)) {
free(threads.jobs_ctx);
return -EINVAL;
}
if (parallel == 1) {
/* Do not use threads here */
if (gcry_kdf_compute(hd, NULL))
goto out;
} else {
threads.jobs_ctx = calloc(threads.max_threads,
sizeof(struct gcrypt_thread_job));
if (!threads.jobs_ctx)
goto out;
if (pthread_attr_init(&threads.attr))
goto out;
if (gcry_kdf_compute(hd, &ops))
goto out;
}
if (gcry_kdf_final(hd, key_length, key))
goto out;
r = 0;
out:
gcry_kdf_close(hd);
pthread_attr_destroy(&threads.attr);
free(threads.jobs_ctx);
return r;
}
#endif
/* PBKDF */ /* PBKDF */
int crypt_pbkdf(const char *kdf, const char *hash, int crypt_pbkdf(const char *kdf, const char *hash,
const char *password, size_t password_length, const char *password, size_t password_length,
@@ -400,8 +525,13 @@ int crypt_pbkdf(const char *kdf, const char *hash,
return pbkdf2(hash, password, password_length, salt, salt_length, return pbkdf2(hash, password, password_length, salt, salt_length,
key, key_length, iterations); key, key_length, iterations);
else if (!strncmp(kdf, "argon2", 6)) else if (!strncmp(kdf, "argon2", 6))
#if HAVE_DECL_GCRY_KDF_ARGON2 && !USE_INTERNAL_ARGON2
return gcrypt_argon2(kdf, password, password_length, salt, salt_length,
key, key_length, iterations, memory, parallel);
#else
return argon2(kdf, password, password_length, salt, salt_length, return argon2(kdf, password, password_length, salt, salt_length,
key, key_length, iterations, memory, parallel); key, key_length, iterations, memory, parallel);
#endif
return -EINVAL; return -EINVAL;
} }

View File

@@ -1,4 +1,6 @@
subdir('argon2') if use_internal_argon2
subdir('argon2')
endif
libcrypto_backend_dependencies = [ libcrypto_backend_dependencies = [
crypto_backend_library, crypto_backend_library,
@@ -25,7 +27,7 @@ if use_internal_pbkdf2
libcrypto_backend_sources += files('pbkdf2_generic.c') libcrypto_backend_sources += files('pbkdf2_generic.c')
endif endif
if get_option('argon-implementation') == 'internal' if use_internal_argon2 and get_option('argon-implementation') == 'internal'
libcrypto_backend_link_with += libargon2 libcrypto_backend_link_with += libargon2
elif get_option('argon-implementation') == 'libargon2' elif get_option('argon-implementation') == 'libargon2'
libcrypto_backend_dependencies += libargon2_external libcrypto_backend_dependencies += libargon2_external

View File

@@ -444,6 +444,7 @@ endif
crypto_backend_library = [] crypto_backend_library = []
use_internal_pbkdf2 = false use_internal_pbkdf2 = false
use_internal_argon2 = true
if get_option('crypto-backend') == 'gcrypt' if get_option('crypto-backend') == 'gcrypt'
req_version = '1.1.42' req_version = '1.1.42'
@@ -471,16 +472,36 @@ if get_option('crypto-backend') == 'gcrypt'
error('Using internal cryptsetup PBKDF2 is not compatible with FIPS.') error('Using internal cryptsetup PBKDF2 is not compatible with FIPS.')
endif endif
if get_option('gcrypt-argon2').auto()
# Check if we can use gcrypt Argon2 (1.11.0 supports empty password)
gcrypt_with_empty_password = dependency('libgcrypt',
version: '>=1.11.0',
required: false,
static: enable_static)
if gcrypt_with_empty_password.found()
req_version = '1.11.0'
use_internal_argon2 = false
else
use_internal_argon2 = true
endif
else
use_internal_argon2 = get_option('gcrypt-argon2').disabled()
endif
crypto_backend_library = dependency('libgcrypt', crypto_backend_library = dependency('libgcrypt',
version: '>=@0@'.format(req_version), version: '>=@0@'.format(req_version),
static: enable_static) static: enable_static)
conf.set10('HAVE_DECL_GCRY_CIPHER_MODE_XTS', conf.set10('HAVE_DECL_GCRY_CIPHER_MODE_XTS',
cc.has_header_symbol('gcrypt.h', 'GCRY_CIPHER_MODE_XTS', cc.has_header_symbol('gcrypt.h', 'GCRY_CIPHER_MODE_XTS',
dependencies: crypto_backend_library)) dependencies: crypto_backend_library))
conf.set10('HAVE_DECL_GCRY_KDF_ARGON2',
cc.has_header_symbol('gcrypt.h', 'GCRY_KDF_ARGON2',
dependencies: crypto_backend_library))
conf.set_quoted('GCRYPT_REQ_VERSION', req_version, conf.set_quoted('GCRYPT_REQ_VERSION', req_version,
description: 'Requested gcrypt version') description: 'Requested gcrypt version')
elif get_option('crypto-backend') == 'openssl' elif get_option('crypto-backend') == 'openssl'
use_internal_pbkdf2 = false use_internal_pbkdf2 = false
use_internal_argon2 = true
crypto_backend_library = dependency('libcrypto', crypto_backend_library = dependency('libcrypto',
version: '>=0.9.8', version: '>=0.9.8',
static: enable_static) static: enable_static)
@@ -494,6 +515,7 @@ elif get_option('crypto-backend') == 'nss'
warning('NSS backend does NOT provide backward compatibility (missing ripemd160 hash).') warning('NSS backend does NOT provide backward compatibility (missing ripemd160 hash).')
use_internal_pbkdf2 = true use_internal_pbkdf2 = true
use_internal_argon2 = true
crypto_backend_library = dependency('nss', crypto_backend_library = dependency('nss',
static: enable_static) static: enable_static)
@@ -505,6 +527,7 @@ elif get_option('crypto-backend') == 'kernel'
error('kernel crypto backend is not supported with FIPS enabled') error('kernel crypto backend is not supported with FIPS enabled')
endif endif
use_internal_pbkdf2 = true use_internal_pbkdf2 = true
use_internal_argon2 = true
assert(cc.has_header('linux/if_alg.h'), assert(cc.has_header('linux/if_alg.h'),
'You need Linux kernel headers with userspace crypto interface.') 'You need Linux kernel headers with userspace crypto interface.')
elif get_option('crypto-backend') == 'nettle' elif get_option('crypto-backend') == 'nettle'
@@ -518,6 +541,7 @@ elif get_option('crypto-backend') == 'nettle'
crypto_backend_library = dependency('nettle', crypto_backend_library = dependency('nettle',
static: enable_static) static: enable_static)
use_internal_pbkdf2 = false use_internal_pbkdf2 = false
use_internal_argon2 = true
assert(cc.has_function('nettle_pbkdf2_hmac_sha256', assert(cc.has_function('nettle_pbkdf2_hmac_sha256',
dependencies: crypto_backend_library), dependencies: crypto_backend_library),
'You need Nettle library version 2.6 or more recent.') 'You need Nettle library version 2.6 or more recent.')
@@ -527,7 +551,13 @@ conf.set10('USE_INTERNAL_PBKDF2', use_internal_pbkdf2)
libargon2_external = [] libargon2_external = []
threads = [] threads = []
use_internal_sse_argon2 = false use_internal_sse_argon2 = false
if get_option('argon-implementation') == 'internal' if not use_internal_argon2 or get_option('argon-implementation') == 'none'
if get_option('argon-implementation') == 'internal' or get_option('argon-implementation') == 'libargon2'
warning('Argon2 in crypto library is used; internal Argon2 options are ignored.')
endif
conf.set10('USE_INTERNAL_ARGON2', false,
description: 'Use internal Argon2.')
elif get_option('argon-implementation') == 'internal'
warning('Argon2 bundled (slow) reference implementation will be used, please consider using system library with -Dargon-implementation=libargon2') warning('Argon2 bundled (slow) reference implementation will be used, please consider using system library with -Dargon-implementation=libargon2')
if get_option('internal-sse-argon2') if get_option('internal-sse-argon2')

View File

@@ -39,6 +39,7 @@ option('fips', type : 'boolean', description : 'enable FIPS mode restrictions',
option('fuzzing-engine', type : 'string', description : 'specify LDFLAGS for linking with fuzzing engine (in OSS-Fuzz, LIB_FUZZING_ENGINE variable should be passed via this argument)') option('fuzzing-engine', type : 'string', description : 'specify LDFLAGS for linking with fuzzing engine (in OSS-Fuzz, LIB_FUZZING_ENGINE variable should be passed via this argument)')
option('fuzz-targets', type : 'boolean', description : 'enable building fuzz targets', value : false) option('fuzz-targets', type : 'boolean', description : 'enable building fuzz targets', value : false)
option('gcrypt-pbkdf2', type : 'feature', description : 'enable internal gcrypt PBKDF2', value : 'auto') option('gcrypt-pbkdf2', type : 'feature', description : 'enable internal gcrypt PBKDF2', value : 'auto')
option('gcrypt-argon2', type : 'feature', description : 'enable internal gcrypt Argon2', value : 'auto')
option('integritysetup', type : 'boolean', description : 'integritysetup Support', value : true) option('integritysetup', type : 'boolean', description : 'integritysetup Support', value : true)
option('internal-sse-argon2', type : 'boolean', description : 'use internal SSE implementation of Argon2 PBKDF', value : false) option('internal-sse-argon2', type : 'boolean', description : 'use internal SSE implementation of Argon2 PBKDF', value : false)
option('kernel_crypto', type : 'boolean', description : 'kernel userspace crypto (no benchmark and tcrypt)', value : true) option('kernel_crypto', type : 'boolean', description : 'kernel userspace crypto (no benchmark and tcrypt)', value : true)

View File

@@ -1029,7 +1029,10 @@ static int pbkdf_test_vectors(void)
for (i = 0; i < ARRAY_SIZE(kdf_test_vectors); i++) { for (i = 0; i < ARRAY_SIZE(kdf_test_vectors); i++) {
crypt_backend_memzero(result, sizeof(result)); crypt_backend_memzero(result, sizeof(result));
vec = &kdf_test_vectors[i]; vec = &kdf_test_vectors[i];
printf("PBKDF vector %02d %s ", i, vec->type); if (vec->hash)
printf("PBKDF vector %02d %s-%s ", i, vec->type, vec->hash);
else
printf("PBKDF vector %02d %s ", i, vec->type);
if (vec->hash && crypt_hmac_size(vec->hash) < 0) { if (vec->hash && crypt_hmac_size(vec->hash) < 0) {
printf("[%s N/A]\n", vec->hash); printf("[%s N/A]\n", vec->hash);
continue; continue;
@@ -1039,8 +1042,8 @@ static int pbkdf_test_vectors(void)
vec->salt, vec->salt_length, vec->salt, vec->salt_length,
result, vec->output_length, result, vec->output_length,
vec->iterations, vec->memory, vec->parallelism) < 0) { vec->iterations, vec->memory, vec->parallelism) < 0) {
printf("[%s-%s N/A]\n", vec->type, vec->hash); printf("[API FAILED]\n");
continue; return EXIT_FAILURE;
} }
if (memcmp(result, vec->output, vec->output_length)) { if (memcmp(result, vec->output, vec->output_length)) {
printf("[FAILED]\n"); printf("[FAILED]\n");