Skip to content

Commit

Permalink
Add support for Argon2 (i, d, id, and ds variants)
Browse files Browse the repository at this point in the history
Signed-off-by: Mattias Andrée <[email protected]>
  • Loading branch information
maandree committed Feb 15, 2022
1 parent a2129ae commit 1b88359
Show file tree
Hide file tree
Showing 13 changed files with 469 additions and 23 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ test/bigcrypt
test/byteorder
test/checksalt
test/compile-strong-alias
test/crypt-argon2
test/crypt-badargs
test/crypt-bcrypt
test/crypt-des
Expand Down
9 changes: 9 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ The yescrypt code comes from yescrypt by Solar Designer <solar at
openwall.com>. It builds upon Colin Percival's scrypt.
See: http://openwall.com/yescrypt/ for reference.

The Argon2 hash module use an independent implementation by Mattias
Andrée of the Argon2 algorithm by Alex Biryukov, Daniel Dinu, and
Dmitry Khovratovich (see https://github.com/P-H-C/phc-winner-argon2
for referenced) as well as the underlying BLAKE2b algorithm by
Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, Christian
Winnerlein. The Argon2 hash modules uses libar2simplified and
libar2 which depend on libblake for the BLAKE2b implementation.
These libraries are licensed under the ISC license.

The bcrypt hash module comes from crypt_blowfish, originally written
by Solar Designer <solar at openwall.com> and based on algorithms and
ideas by Niels Provos <provos at citi.umich.edu> and David Mazieres
Expand Down
3 changes: 3 additions & 0 deletions LICENSING
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ source tree. For specific licensing terms consult the files themselves.
alg-md4.h, alg-md4.c, alg-md5.h, alg-md5.c,
crypt-bcrypt.c, crypt-gensalt.c, test-crypt-bcrypt.c

* Copyright Mattias Andrée; ISC:
crypt-argon2.c

* Copyright Solar Designer, Colin Percival; 0-clause BSD:
alg-yescrypt-common.c, alg-yescrypt-platform.c

Expand Down
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ libcrypt_la_SOURCES = \
lib/alg-sha512.c \
lib/alg-yescrypt-common.c \
lib/alg-yescrypt-opt.c \
lib/crypt-argon2.c \
lib/crypt-bcrypt.c \
lib/crypt-des.c \
lib/crypt-gensalt-static.c \
Expand Down Expand Up @@ -369,6 +370,7 @@ check_PROGRAMS = \
test/byteorder \
test/checksalt \
test/compile-strong-alias \
test/crypt-argon2 \
test/crypt-badargs \
test/crypt-gost-yescrypt \
test/explicit-bzero \
Expand Down
4 changes: 3 additions & 1 deletion NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ libxcrypt NEWS -- history of user-visible changes.
Please send bug reports, questions and suggestions to
<https://github.com/besser82/libxcrypt/issues>.

Version 4.4.29
Version 4.5.0
* Implement Argon2 ($argon2i$, $argon2d$, $argon2id$, $argon2ds$)
hashing algorithms.

Version 4.4.28
* Add glibc-on-or1k (OpenRISC 1000) entry to libcrypt.minver.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ README for libxcrypt

libxcrypt is a modern library for one-way hashing of passwords. It
supports a wide variety of both modern and historical hashing methods:
yescrypt, gost-yescrypt, scrypt, bcrypt, sha512crypt, sha256crypt,
argon2i, yescrypt, gost-yescrypt, scrypt, bcrypt, sha512crypt, sha256crypt,
md5crypt, SunMD5, sha1crypt, NT, bsdicrypt, bigcrypt, and descrypt.
It provides the traditional Unix `crypt` and `crypt_r` interfaces, as
well as a set of extended interfaces pioneered by Openwall Linux,
Expand Down
1 change: 0 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ It was last updated 20 October 2018.
whether we can use these.

* Additional hashing methods
* Argon2 <https://password-hashing.net/>
* ...?

* Runtime configurability (in progress on the [crypt.conf branch][])
Expand Down
24 changes: 23 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Process this file with autoconf to produce a configure script.
m4_include([build-aux/m4/zw_automodern.m4])
AC_INIT([xcrypt],
[4.4.29],
[4.5.0],
[https://github.com/besser82/libxcrypt/issues],
[libxcrypt],
[https://github.com/besser82/libxcrypt])
Expand Down Expand Up @@ -427,6 +427,28 @@ case "$hashes_enabled" in
;;
esac


# The Argon2 hash module requires libar2simplified, libar2, libblake, and libpthread.
case "$hashes_enabled" in
*,argon2*)
AC_SEARCH_LIBS([libar2simplified_init_context], [ar2simplified])
if test x"$ac_cv_search_libar2simplified_init_context" = xno; then
AC_MSG_ERROR([Could not find libar2simplified version 1.1+, required for the Argon2 hash module])
fi
AC_SEARCH_LIBS([libar2_hash_buf_size], [ar2])
if test x"$ac_cv_search_libar2_hash_buf_size" = xno; then
AC_MSG_ERROR([Could not find libar2 version 1.1+, required for the Argon2 hash module])
fi
AC_SEARCH_LIBS([libblake_blake2b_init], [blake])
if test x"$ac_cv_search_libblake_blake2b_init" = xno; then
AC_MSG_ERROR([Could not find libblake, required for the Argon2 hash module])
fi
AX_PTHREAD
;;
esac



# If the obsolete APIs are disabled, the stubs implicitly disabled as well.
if test x"$COMPAT_ABI" = xno && test x"$enable_obsolete_api_enosys" = x1; then
AC_MSG_WARN(
Expand Down
22 changes: 22 additions & 0 deletions doc/crypt.5
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,28 @@ The algorithm was specifically designed to make it costly to perform
large-scale custom hardware attacks by requiring large amounts of memory.
In 2016, the scrypt algorithm was published by IETF as RFC 7914.
.hash "$7$" "\e$7\e$[./A-Za-z0-9]{11,97}\e$[./A-Za-z0-9]{43}" unlimited 8 256 256 "up to 512 (128+ recommended)" "6 to 11 (logarithmic)"
.Ss argon2_d
argon2_d is one of the variants of Argon2 key derivation functions that won
the Password Hashing Competition 2015. argon2d is designed for environments
taht are safe from side-channel attacks. Acceptable for new hashes, but
not widely supported.
.hash "$argon2d$" "\e$argon2d\e$(v=(16|19)\e$)?m=[1-9][0-9]*,t=[1-9][0-9]*,p=[1-9][0-9]*\e$[A-Za-z0-9+/]{11,}\e$[A-Za-z0-9+/]{6,}" 4,294,967,295 8 "32 to 34,359,738,360 (256 default)" "32 to 34,359,738,360 (256 default)" "64 to 34,359,738,360 (128+ recommended)" "1 to 4,294,967,295"
.Ss argon2_i
argon2_i is one of the variants of Argon2 key derivation functions that won
the Password Hashing Competition 2015. argon2i is designed for environments
taht are not safe from side-channel attacks. Acceptable for new hashes, but
not widely supported.
.hash "$argon2i$" "\e$argon2i\e$(v=(16|19)\e$)?m=[1-9][0-9]*,t=[1-9][0-9]*,p=[1-9][0-9]*\e$[A-Za-z0-9+/]{11,}\e$[A-Za-z0-9+/]{6,}" 4,294,967,295 8 "32 to 34,359,738,360 (256 default)" "32 to 34,359,738,360 (256 default)" "64 to 34,359,738,360 (128+ recommended)" "1 to 4,294,967,295"
.Ss argon2_id
argon2_id is one of the variants of Argon2 key derivation functions that won
the Password Hashing Competition 2015. argon2id is a hybrid construction
between argon2i and argon2d. Acceptable for new hashes, but not widely supported.
.hash "$argon2id$" "\e$argon2id\e$(v=(16|19)\e$)?m=[1-9][0-9]*,t=[1-9][0-9]*,p=[1-9][0-9]*\e$[A-Za-z0-9+/]{11,}\e$[A-Za-z0-9+/]{6,}" 4,294,967,295 8 "32 to 34,359,738,360 (256 default)" "32 to 34,359,738,360 (256 default)" "64 to 34,359,738,360 (128+ recommended)" "1 to 4,294,967,295"
.Ss argon2_ds
argon2_ds is a later addition to the Argon2 family of key derivation functions.
It adds a substitution-box mechanism to argon2d. Acceptable for new hashes, but
not widely supported.
.hash "$argon2ds$" "\e$argon2ds\e$(v=(16|19)\e$)?m=[1-9][0-9]*,t=[1-9][0-9]*,p=[1-9][0-9]*\e$[A-Za-z0-9+/]{11,}\e$[A-Za-z0-9+/]{6,}" 4,294,967,295 8 "32 to 34,359,738,360 (256 default)" "32 to 34,359,738,360 (256 default)" "64 to 34,359,738,360 (128+ recommended)" "1 to 4,294,967,295"
.Ss bcrypt
A hash based on the Blowfish block cipher,
modified to have an extra-expensive key schedule.
Expand Down
232 changes: 232 additions & 0 deletions lib/crypt-argon2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
/**
* ISC License
*
* © 2022 Mattias Andrée <[email protected]>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "crypt-port.h"
#include "crypt-hashes.h"

#if INCLUDE_argon2_d || INCLUDE_argon2_i || INCLUDE_argon2_id || INCLUDE_argon2_ds

#include <libar2simplified.h>
#include <libar2.h>

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#define REMOVE_CONST(X)\
(*(void **)(void *)&(X))

#define DEFAULT_TAG_SIZE 32
#define DEFAULT_VERSION LIBAR2_ARGON2_VERSION_13
#define DEFAULT_M_COST 4096
#define DEFAULT_LANES 1

static void
crypt_argon2_rn (const char *prefix,
const char *phrase, size_t phr_size,
const char *setting, size_t set_size,
uint8_t *output, size_t o_size,
void *scratch, size_t s_size)
{
struct libar2_argon2_parameters *params = NULL;
struct libar2_context ctx;
char *settings_end;
int free_scratch = 0;
size_t required_s_size;
size_t required_o_size;
size_t offset;

if (strncmp (setting, prefix, strlen (prefix)) || !set_size)
goto einval;

params = libar2simplified_decode (setting, NULL, &settings_end, NULL);
if (!params)
goto fail;
if (*settings_end)
goto einval;
if (!params->hashlen)
params->hashlen = DEFAULT_TAG_SIZE;

required_o_size = libar2_encode_params (NULL, params) - 1;
required_o_size += libar2_encode_base64 (NULL, NULL, params->hashlen);
if (o_size < required_o_size)
goto einval;

libar2simplified_init_context (&ctx);
ctx.autoerase_message = 0; /* allows `phrase` to be read-only */
required_s_size = libar2_hash_buf_size (params);
if (required_s_size > s_size)
{
if (free_scratch)
free (scratch);
scratch = malloc (required_s_size);
if (!scratch)
{
errno = ENOMEM;
goto fail;
}
free_scratch = 1;
}

if (libar2_hash (scratch, REMOVE_CONST (phrase), phr_size, params, &ctx))
goto fail;

offset = libar2_encode_params ((char *) output, params) - 1;
libar2_encode_base64 ((char *) &output[offset], scratch, params->hashlen);

free(params);
errno = 0;
return;

einval:
errno = EINVAL;
fail:
free (params);
if (free_scratch)
{
free (scratch);
}
return;
}

static void
gensalt_argon2_rn (enum libar2_argon2_type type,
unsigned long count,
const uint8_t *rbytes, size_t nrbytes,
uint8_t *output, size_t o_size)
{
struct libar2_argon2_parameters params;

if (count >> 31 > 1)
goto erange;

memset (&params, 0, sizeof (params));
params.type = type;
params.version = DEFAULT_VERSION;
params.t_cost = (uint_least32_t) count;
params.m_cost = DEFAULT_M_COST;
params.lanes = DEFAULT_LANES;
params.salt = REMOVE_CONST (rbytes); /* libar2 does not use `const` because
* it has an option to erase the salt */
params.saltlen = nrbytes;
params.hashlen = DEFAULT_TAG_SIZE;
if (libar2_validate_params (&params, NULL) != LIBAR2_OK)
goto erange;

if (o_size < libar2_encode_params (NULL, &params))
goto erange;

if (libar2_encode_params ((char *) output, &params) > o_size)
abort();

return;

erange:
errno = ERANGE;
return;
}

#if INCLUDE_argon2_d
void
crypt_argon2_d_rn (const char *phrase, size_t phr_size,
const char *setting, size_t set_size,
uint8_t *output, size_t o_size,
void *scratch, size_t s_size)
{
crypt_argon2_rn ("$argon2d$", phrase, phr_size, setting,
set_size, output, o_size, scratch, s_size);
}
#endif /* INCLUDE_argon2_d */

#if INCLUDE_argon2_i
void
crypt_argon2_i_rn (const char *phrase, size_t phr_size,
const char *setting, size_t set_size,
uint8_t *output, size_t o_size,
void *scratch, size_t s_size)
{
crypt_argon2_rn ("$argon2i$", phrase, phr_size, setting,
set_size, output, o_size, scratch, s_size);
}
#endif /* INCLUDE_argon2_i */

#if INCLUDE_argon2_id
void
crypt_argon2_id_rn (const char *phrase, size_t phr_size,
const char *setting, size_t set_size,
uint8_t *output, size_t o_size,
void *scratch, size_t s_size)
{
crypt_argon2_rn ("$argon2id$", phrase, phr_size, setting,
set_size, output, o_size, scratch, s_size);
}
#endif /* INCLUDE_argon2_id */

#if INCLUDE_argon2_ds
void
crypt_argon2_ds_rn (const char *phrase, size_t phr_size,
const char *setting, size_t set_size,
uint8_t *output, size_t o_size,
void *scratch, size_t s_size)
{
crypt_argon2_rn ("$argon2ds$", phrase, phr_size, setting,
set_size, output, o_size, scratch, s_size);
}
#endif /* INCLUDE_argon2_ds */

#if INCLUDE_argon2_d
void
gensalt_argon2_d_rn (unsigned long count,
const uint8_t *rbytes, size_t nrbytes,
uint8_t *output, size_t o_size)
{
gensalt_argon2_rn(LIBAR2_ARGON2D, count, rbytes, nrbytes, output, o_size);
}
#endif /* INCLUDE_argon2_d */

#if INCLUDE_argon2_i
void
gensalt_argon2_i_rn (unsigned long count,
const uint8_t *rbytes, size_t nrbytes,
uint8_t *output, size_t o_size)
{
gensalt_argon2_rn(LIBAR2_ARGON2I, count, rbytes, nrbytes, output, o_size);
}
#endif /* INCLUDE_argon2_i */

#if INCLUDE_argon2_id
void
gensalt_argon2_id_rn (unsigned long count,
const uint8_t *rbytes, size_t nrbytes,
uint8_t *output, size_t o_size)
{
gensalt_argon2_rn(LIBAR2_ARGON2ID, count, rbytes, nrbytes, output, o_size);
}
#endif /* INCLUDE_argon2_ds */

#if INCLUDE_argon2_ds
void
gensalt_argon2_ds_rn (unsigned long count,
const uint8_t *rbytes, size_t nrbytes,
uint8_t *output, size_t o_size)
{
gensalt_argon2_rn(LIBAR2_ARGON2DS, count, rbytes, nrbytes, output, o_size);
}
#endif /* INCLUDE_argon2_ds */

#endif /* INCLUDE_argon2_d || INCLUDE_argon2_i || INCLUDE_argon2_id || INCLUDE_argon2_ds */
Loading

0 comments on commit 1b88359

Please sign in to comment.