Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
The Argon2 hash module is based on algorithms and ideas developed
by Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and
Samuel Neves. See: https://github.com/P-H-C/phc-winner-argon2 for
reference.

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.
Expand Down
6 changes: 6 additions & 0 deletions LICENSING
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ source tree. For specific licensing terms consult the files themselves.
* Copyright <vt at altlinux.org>; 0-clause BSD:
crypt-yescrypt.c, test-crypt-yescrypt.c

* Copyright Daniel Dinu et al.; CC0 1.0 Universal/Apache 2.0:
alg-argon2-encoding.c

* Copyright <ferivoz at riseup.net>; 0-clause BSD:
crypt-argon2.c

* Copyright Kevin Cernekee; FSF All Permissive License:
m4/ax_check_vscript.m4

Expand Down
3 changes: 3 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ nodist_noinst_HEADERS = \
crypt-hashes.h \
crypt-symbol-vers.h
noinst_HEADERS = \
lib/alg-argon2-encoding.h \
lib/alg-des.h \
lib/alg-gost3411-2012-const.h \
lib/alg-gost3411-2012-core.h \
Expand Down Expand Up @@ -97,6 +98,7 @@ lib_LTLIBRARIES = \
libcrypt.la

libcrypt_la_SOURCES = \
lib/alg-argon2-encoding.c \
lib/alg-des-tables.c \
lib/alg-des.c \
lib/alg-gost3411-2012-core.c \
Expand All @@ -109,6 +111,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-common.c \
lib/crypt-des.c \
Expand Down
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ libxcrypt NEWS -- history of user-visible changes.
Please send bug reports, questions and suggestions to
<https://github.com/besser82/libxcrypt/issues>.

Version 4.5
* Support for the argon2 family of hashing algorithms added
($argon2d$, $argon2i$, $argon2id$).

Version 4.4.18
* Fix compilation errors on (Free)BSD (issue #110).

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,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
10 changes: 9 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([m4/zw_automodern.m4])
AC_INIT([xcrypt],
[4.4.18],
[4.5],
[https://github.com/besser82/libxcrypt/issues],
[libxcrypt],
[https://github.com/besser82/libxcrypt])
Expand Down Expand Up @@ -405,6 +405,14 @@ case "$hashes_enabled" in
;;
esac

# The argon2 hashes require libargon2.
AS_CASE(["$hashes_enabled"],
[*argon2*], [
AC_SEARCH_LIBS([argon2_hash], [argon2])
AS_IF([test x"$ac_cv_search_argon2_hash" = xno],
[AC_MSG_ERROR([Argon2 hashes require libargon2, which was not found])])
])

# 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
24 changes: 24 additions & 0 deletions doc/crypt.5
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,30 @@ 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" "6 to 11 (logarithmic)"
.Ss argon2_d
Argon2d is a memory-hard function for password-hashing and proof-of-work
applications. It aims at the highest memory filling rate and effective use
of multiple computing units, while still providing defense against tradeoff
attacks. Argon2d uses data-dependent memory access, which makes it suitable
for cryptocurrencies and proof-of-work applications with no threats from
side-channel timing attacks.
.hash "$argon2d$" "\e$argon2i\e$v=[0-9]{2}\e$m=[0-9]{1,10},t=[0-9]{1,10},p=[0-9]{1,10}\e$[A-Za-z0-9/+]{11,86}$[A-Za-z0-9/+]{43}" unlimited 8 256 256 "64 to 512" "1 to 4,294,967,295"
.Ss argon2_i
argon2_i is a memory-hard function for password-hashing and proof-of-work
applications. It aims at the highest memory filling rate and effective use
of multiple computing units, while still providing defense against tradeoff
attacks. Argon2i uses data-independent memory access, which is preferred
for password hashing and password-based key derivation.
.hash "$argon2i$" "\e$argon2i\e$v=[0-9]{2}\e$m=[0-9]{1,10},t=[0-9]{1,10},p=[0-9]{1,10}\e$[A-Za-z0-9/+]{11,86}$[A-Za-z0-9/+]{43}" unlimited 8 256 256 "64 to 512" "1 to 4,294,967,295"
.Ss argon2_id
Argon2id is a memory-hard function for password-hashing and proof-of-work
applications. It aims at the highest memory filling rate and effective use
of multiple computing units, while still providing defense against tradeoff
attacks. Argon2id works as Argon2i for the first half of the first pass
over the memory, and as Argon2d for the rest, thus providing both
side-channel attack protection and brute-force cost savings due to
time-memory tradeoffs.
.hash "$argon2id$" "\e$argon2i\e$v=[0-9]{2}\e$m=[0-9]{1,10},t=[0-9]{1,10},p=[0-9]{1,10}\e$[A-Za-z0-9/+]{11,86}$[A-Za-z0-9/+]{43}" unlimited 8 256 256 "64 to 512" "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
151 changes: 151 additions & 0 deletions lib/alg-argon2-encoding.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/

#include <argon2.h>

#include "alg-argon2-encoding.h"

/*
* Some macros for constant-time comparisons. These work over values in
* the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
*/
#define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF)
#define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF)
#define GE(x, y) (GT(y, x) ^ 0xFF)
#define LT(x, y) GT(y, x)
#define LE(x, y) GE(y, x)
Comment on lines +20 to +28
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please explain why these conversion functions need to use constant-time comparisons?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In an earlier version of my code I have used a lookup table just like ascii64 in crypto-common.c. Eventually I gave up that code for these reasons:

  • The base64 code used in Argon2 differs from RFC because it skips the padding character '='. My code would therefore not follow RFC either.
  • The file alg-argon2-encoding.c is a direct copy from Argon2 reference implementation.
  • Having a separate file makes it easy to put proper copyright into that file as well, especially since the code in the file is not modified

I would not mind adjusting it if it is requested. Especially since a lookup table is easier to review and complies with rest of the code base.


/*
* Convert value x (0..63) to corresponding Base64 character.
*/
static unsigned b64_byte_to_char(unsigned x) {
return (LT(x, 26) & (x + 'A')) |
(GE(x, 26) & LT(x, 52) & (x + (unsigned)('a' - 26))) |
(GE(x, 52) & LT(x, 62) & (x + (unsigned)('0' - 52))) | (EQ(x, 62) & '+') |
(EQ(x, 63) & '/');
}

/*
* Convert character c to the corresponding 6-bit value. If character c
* is not a Base64 character, then 0xFF (255) is returned.
*/
static unsigned b64_char_to_byte(int c) {
unsigned x;

x = (GE(c, 'A') & LE(c, 'Z') & (unsigned)(c - 'A')) |
(GE(c, 'a') & LE(c, 'z') & (unsigned)(c - ('a' - 26))) |
(GE(c, '0') & LE(c, '9') & (unsigned)(c - ('0' - 52))) | (EQ(c, '+') & 62) |
(EQ(c, '/') & 63);
return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
}

/*
* Convert some bytes to Base64. 'dst_len' is the length (in characters)
* of the output buffer 'dst'; if that buffer is not large enough to
* receive the result (including the terminating 0), then (size_t)-1
* is returned. Otherwise, the zero-terminated Base64 string is written
* in the buffer, and the output length (counted WITHOUT the terminating
* zero) is returned.
*/
size_t argon2_encode64(char *dst, size_t dst_len, const uint8_t *src,
size_t src_len) {
size_t olen;
const unsigned char *buf;
unsigned acc, acc_len;

olen = (src_len / 3) << 2;
switch (src_len % 3) {
case 2:
olen++;
/* fall through */
case 1:
olen += 2;
break;
}
if (dst_len <= olen) {
return (size_t)-1;
}
acc = 0;
acc_len = 0;
buf = (const unsigned char *)src;
while (src_len-- > 0) {
acc = (acc << 8) + (*buf++);
acc_len += 8;
while (acc_len >= 6) {
acc_len -= 6;
*dst++ = (char)b64_byte_to_char((acc >> acc_len) & 0x3F);
}
}
if (acc_len > 0) {
*dst++ = (char)b64_byte_to_char((acc << (6 - acc_len)) & 0x3F);
}
*dst++ = 0;
return olen;
}

/*
* Decode Base64 chars into bytes. The '*dst_len' value must initially
* contain the length of the output buffer '*dst'; when the decoding
* ends, the actual number of decoded bytes is written back in
* '*dst_len'.
*
* Decoding stops when a non-Base64 character is encountered, or when
* the output buffer capacity is exceeded. If an error occurred (output
* buffer is too small, invalid last characters leading to unprocessed
* buffered bits), then NULL is returned; otherwise, the returned value
* points to the first non-Base64 character in the source stream, which
* may be the terminating zero.
*/
const char *argon2_decode64(uint8_t *dst, size_t *dst_len, const char *src) {
size_t len;
unsigned char *buf;
unsigned acc, acc_len;

buf = (unsigned char *)dst;
len = 0;
acc = 0;
acc_len = 0;
for (;;) {
unsigned d;

d = b64_char_to_byte(*src);
if (d == 0xFF) {
break;
}
src++;
acc = (acc << 6) + d;
acc_len += 6;
if (acc_len >= 8) {
acc_len -= 8;
if ((len++) >= *dst_len) {
return NULL;
}
*buf++ = (acc >> acc_len) & 0xFF;
}
}

/*
* If the input length is equal to 1 modulo 4 (which is
* invalid), then there will remain 6 unprocessed bits;
* otherwise, only 0, 2 or 4 bits are buffered. The buffered
* bits must also all be zero.
*/
if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) {
return NULL;
}
*dst_len = len;
return src;
}

25 changes: 25 additions & 0 deletions lib/alg-argon2-encoding.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/

#ifndef ALG_ARGON2_ENCODING_H
#define ALG_ARGON2_ENCODING_H

#include <stdint.h>

size_t argon2_encode64 (char *dst, size_t dst_len, const uint8_t *src,
size_t src_len);
const char *argon2_decode64 (uint8_t *dst, size_t *dst_len, const char *src);

#endif
Loading