Module Name: src Committed By: jhigh Date: Mon Oct 21 02:36:48 UTC 2019
Modified Files: src/external/apache2/argon2/lib/libargon2: Makefile src/external/apache2/argon2/usr.bin/argon2: Makefile src/lib/libcrypt: Makefile crypt.3 crypt.c crypt.h pw_gensalt.c src/usr.bin/pwhash: Makefile pwhash.1 pwhash.c Added Files: src/lib/libcrypt: crypt-argon2.c Log Message: adding argon2 support to libcrypt. argon2 user authentication now available via MKARGON2=yes (3 variants supported; argon2id recommended) before using, please read argon2 paper at https://github.com/P-H-C/phc-winner-argon2 To generate a diff of this commit: cvs rdiff -u -r1.1 -r1.2 src/external/apache2/argon2/lib/libargon2/Makefile cvs rdiff -u -r1.1 -r1.2 src/external/apache2/argon2/usr.bin/argon2/Makefile cvs rdiff -u -r1.25 -r1.26 src/lib/libcrypt/Makefile cvs rdiff -u -r0 -r1.1 src/lib/libcrypt/crypt-argon2.c cvs rdiff -u -r1.27 -r1.28 src/lib/libcrypt/crypt.3 cvs rdiff -u -r1.35 -r1.36 src/lib/libcrypt/crypt.c cvs rdiff -u -r1.4 -r1.5 src/lib/libcrypt/crypt.h cvs rdiff -u -r1.7 -r1.8 src/lib/libcrypt/pw_gensalt.c cvs rdiff -u -r1.7 -r1.8 src/usr.bin/pwhash/Makefile cvs rdiff -u -r1.8 -r1.9 src/usr.bin/pwhash/pwhash.1 cvs rdiff -u -r1.15 -r1.16 src/usr.bin/pwhash/pwhash.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/external/apache2/argon2/lib/libargon2/Makefile diff -u src/external/apache2/argon2/lib/libargon2/Makefile:1.1 src/external/apache2/argon2/lib/libargon2/Makefile:1.2 --- src/external/apache2/argon2/lib/libargon2/Makefile:1.1 Wed Oct 9 13:13:09 2019 +++ src/external/apache2/argon2/lib/libargon2/Makefile Mon Oct 21 02:36:48 2019 @@ -12,15 +12,7 @@ INCSDIR= /usr/include LIB= argon2 SRCS= argon2.c core.c blake2b.c thread.c encoding.c ref.c - -CPPFLAGS= -std=c89 -O3 -Wall -g -I../../dist/phc-winner-argon2/include -Isrc -shared -fPIC - -.ifdef NO_THREADS -CPPFLAGS += -DARGON2_NO_THREADS -.else -CPPFLAGS += -pthread -LDADD+=-lpthread -.endif +CPPFLAGS= -std=c89 -O3 -Wall -g -I../../dist/phc-winner-argon2/include -Isrc -fPIC -DARGON2_NO_THREADS OPTTARGET ?= native OPTTEST := $(shell $(CC) -Iinclude -Isrc -march=$(OPTTARGET) src/opt.c -c \ Index: src/external/apache2/argon2/usr.bin/argon2/Makefile diff -u src/external/apache2/argon2/usr.bin/argon2/Makefile:1.1 src/external/apache2/argon2/usr.bin/argon2/Makefile:1.2 --- src/external/apache2/argon2/usr.bin/argon2/Makefile:1.1 Wed Oct 9 13:13:10 2019 +++ src/external/apache2/argon2/usr.bin/argon2/Makefile Mon Oct 21 02:36:48 2019 @@ -10,16 +10,7 @@ SRCS= SRCS= argon2.c core.c blake2b.c thread.c encoding.c ref.c SRCS+= run.c -.ifdef NO_THREADS -CPPFLAGS += -DARGON2_NO_THREADS -.else -CPPFLAGS += -pthread -LDADD+=-lpthread -.endif - -CPPFLAGS+= -std=c89 -O3 -Wall -g -I../../dist/phc-winner-argon2/include -Isrc #-shared -fPIC -#LDADD+= -L${LIBARGON2} -largon2 -#DPADD+= ${LIBARGON2_SD} +CPPFLAGS+= -DARGON2_NO_THREADS -std=c89 -O3 -Wall -g -I../../dist/phc-winner-argon2/include -Isrc MAN=argon2.1 Index: src/lib/libcrypt/Makefile diff -u src/lib/libcrypt/Makefile:1.25 src/lib/libcrypt/Makefile:1.26 --- src/lib/libcrypt/Makefile:1.25 Sat Aug 10 18:42:29 2013 +++ src/lib/libcrypt/Makefile Mon Oct 21 02:36:48 2019 @@ -1,12 +1,24 @@ -# $NetBSD: Makefile,v 1.25 2013/08/10 18:42:29 dholland Exp $ +# $NetBSD: Makefile,v 1.26 2019/10/21 02:36:48 jhigh Exp $ + +.include <bsd.own.mk> USE_SHLIBDIR= yes +.if (defined(MKARGON2) && ${MKARGON2} != "no") +HAVE_ARGON2=1 +.endif + LIB= crypt SRCS= crypt.c md5crypt.c bcrypt.c crypt-sha1.c util.c pw_gensalt.c SRCS+= hmac_sha1.c +.if defined(HAVE_ARGON2) +SRCS+= crypt-argon2.c +CFLAGS+= -DHAVE_ARGON2 -I../../external/apache2/argon2/dist/phc-winner-argon2/include/ +LDADD+= -largon2 +.endif + WARNS?= 5 MAN= crypt.3 Index: src/lib/libcrypt/crypt.3 diff -u src/lib/libcrypt/crypt.3:1.27 src/lib/libcrypt/crypt.3:1.28 --- src/lib/libcrypt/crypt.3:1.27 Fri Mar 23 18:08:35 2012 +++ src/lib/libcrypt/crypt.3 Mon Oct 21 02:36:48 2019 @@ -1,4 +1,4 @@ -.\" $NetBSD: crypt.3,v 1.27 2012/03/23 18:08:35 njoly Exp $ +.\" $NetBSD: crypt.3,v 1.28 2019/10/21 02:36:48 jhigh Exp $ .\" .\" Copyright (c) 1989, 1991, 1993 .\" The Regents of the University of California. All rights reserved. @@ -241,6 +241,25 @@ A valid password looks like this: The entire password string is passed as .Fa setting for interpretation. + +.Ss Argon2 encryption + +Argon2 is a memory-hard hashing algorithm. crypt() provides all +three variants: argon2i, argon2d, and argon2id. It is recommended +to use argon2id, which provides a hybrid combination using argon2i +on the first pass, and argon2d on the remaining passes. We +parameterize on three variables. First, m_cost (m), specifies the +memory usage in KB. Second, t_cost (t), specfies the number of +iterations. Third, parallelism (p) specifies the number of threads. +A valid Argon2 encoded password looks similar to + +$argon2id$v=19$m=4096,t=6,p=1$qCatF9a1s/6TgcYB$ \ + yeYYrU/rh7E+LI2CAeHTSHVB3iO+OXiNIUHu6NPeTfo + +containing five fields delimited by '$'. The fields, in order, are +variant name, version, parameter set , 128-bit salt, and encoded password. +The complete password string is required to be processed correctly. + .Ss "Blowfish" crypt The .Tn Blowfish @@ -338,20 +357,14 @@ Historically, the functions and .Fn encrypt did not return any value. -They have been provided return values primarily to distinguish +Theyave been provided return values primarily to distinguish implementations where hardware support is provided but not available or where the DES encryption is not available due to the usual political silliness. .Sh SEE ALSO .Xr login 1 , .Xr passwd 1 , -.Xr pwhash 1 , -.Xr getpass 3 , -.Xr md5 3 , -.Xr passwd 5 , -.Xr passwd.conf 5 -.Rs -.%T "Mathematical Cryptology for Computer Scientists and Mathematicians" +.Xr cal Cryptology for Computer Scientists and Mathematicians" .%A Wayne Patterson .%D 1987 .%N ISBN 0-8476-7438-X @@ -363,14 +376,7 @@ usual political silliness. .%J "Communications of the ACM" .%V vol. 22 .%P pp. 594-597 -.%D Nov. 1979 -.Re -.Rs -.%T "DES will be Totally Insecure within Ten Years" -.%A M.E. Hellman -.%J "IEEE Spectrum" -.%V vol. 16 -.%P pp. 32-39 +.%D N pp. 32-39 .%D July 1979 .Re .Sh HISTORY @@ -387,7 +393,7 @@ Dropping the .Em least significant bit in each character of the argument to .Fn des_setkey -is ridiculous. +is ri .Pp The .Fn crypt Index: src/lib/libcrypt/crypt.c diff -u src/lib/libcrypt/crypt.c:1.35 src/lib/libcrypt/crypt.c:1.36 --- src/lib/libcrypt/crypt.c:1.35 Sat Oct 5 18:06:16 2019 +++ src/lib/libcrypt/crypt.c Mon Oct 21 02:36:48 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: crypt.c,v 1.35 2019/10/05 18:06:16 jhigh Exp $ */ +/* $NetBSD: crypt.c,v 1.36 2019/10/21 02:36:48 jhigh Exp $ */ /* * Copyright (c) 1989, 1993 @@ -37,7 +37,7 @@ #if 0 static char sccsid[] = "@(#)crypt.c 8.1.1.1 (Berkeley) 8/18/93"; #else -__RCSID("$NetBSD: crypt.c,v 1.35 2019/10/05 18:06:16 jhigh Exp $"); +__RCSID("$NetBSD: crypt.c,v 1.36 2019/10/21 02:36:48 jhigh Exp $"); #endif #endif /* not lint */ @@ -575,6 +575,18 @@ __crypt(const char *key, const char *set } else if (strcmp(scheme, "1") == 0) { /* $1$ found in pw_gensalt.c:__gensalt_md5 */ return (__md5crypt(key, setting)); +#ifdef HAVE_ARGON2 + /* explicit argon2 variant */ + } else if (strcmp(scheme, "argon2id") == 0) { + /* $argon2id$ found in pw_gensalt.c:__gensalt_argon2 */ + return (__crypt_argon2(key, setting)); + } else if (strcmp(scheme, "argon2i") == 0) { + /* $argon2i$ found in pw_gensalt.c:__gensalt_argon2 */ + return (__crypt_argon2(key, setting)); + } else if (strcmp(scheme, "argon2d") == 0) { + /* $argon2d$ found in pw_gensalt.c:__gensalt_argon2 */ + return (__crypt_argon2(key, setting)); +#endif /* HAVE_ARGON2 */ } else { /* invalid scheme, including empty string */ return NULL; @@ -675,6 +687,7 @@ char * crypt(const char *key, const char *salt) { char *res = __crypt(key, salt); + if (res) return res; /* How do I handle errors ? Return "*0" or "*1" */ Index: src/lib/libcrypt/crypt.h diff -u src/lib/libcrypt/crypt.h:1.4 src/lib/libcrypt/crypt.h:1.5 --- src/lib/libcrypt/crypt.h:1.4 Fri Oct 27 18:22:56 2006 +++ src/lib/libcrypt/crypt.h Mon Oct 21 02:36:48 2019 @@ -1,5 +1,5 @@ /* - * $NetBSD: crypt.h,v 1.4 2006/10/27 18:22:56 drochner Exp $ + * $NetBSD: crypt.h,v 1.5 2019/10/21 02:36:48 jhigh Exp $ */ char *__md5crypt(const char *pw, const char *salt); /* XXX */ char *__bcrypt(const char *, const char *); /* XXX */ @@ -9,6 +9,13 @@ void __hmac_sha1(const unsigned char *, unsigned char *); void __crypt_to64(char *s, u_int32_t v, int n); +#ifdef HAVE_ARGON2 +char *__crypt_argon2(const char *pw, const char *salt); +int __gensalt_argon2id(char *salt, size_t saltsiz, const char *option); +int __gensalt_argon2i(char *salt, size_t saltsiz, const char *option); +int __gensalt_argon2d(char *salt, size_t saltsiz, const char *option); +#endif /* HAVE_ARGON2 */ + int __gensalt_blowfish(char *salt, size_t saltlen, const char *option); int __gensalt_old(char *salt, size_t saltsiz, const char *option); int __gensalt_new(char *salt, size_t saltsiz, const char *option); Index: src/lib/libcrypt/pw_gensalt.c diff -u src/lib/libcrypt/pw_gensalt.c:1.7 src/lib/libcrypt/pw_gensalt.c:1.8 --- src/lib/libcrypt/pw_gensalt.c:1.7 Sun Jan 18 12:15:27 2009 +++ src/lib/libcrypt/pw_gensalt.c Mon Oct 21 02:36:48 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: pw_gensalt.c,v 1.7 2009/01/18 12:15:27 lukem Exp $ */ +/* $NetBSD: pw_gensalt.c,v 1.8 2019/10/21 02:36:48 jhigh Exp $ */ /* * Copyright 1997 Niels Provos <pro...@physnet.uni-hamburg.de> @@ -34,7 +34,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: pw_gensalt.c,v 1.7 2009/01/18 12:15:27 lukem Exp $"); +__RCSID("$NetBSD: pw_gensalt.c,v 1.8 2019/10/21 02:36:48 jhigh Exp $"); #endif /* not lint */ #include <sys/syslimits.h> @@ -53,6 +53,13 @@ __RCSID("$NetBSD: pw_gensalt.c,v 1.7 200 #include "crypt.h" +#ifdef HAVE_ARGON2 +#include <argon2.h> +#define ARGON2_ARGON2_STR "argon2" +#define ARGON2_ARGON2I_STR "argon2i" +#define ARGON2_ARGON2D_STR "argon2d" +#define ARGON2_ARGON2ID_STR "argon2id" +#endif /* HAVE_ARGON2 */ static const struct pw_salt { const char *name; @@ -64,6 +71,13 @@ static const struct pw_salt { { "md5", __gensalt_md5 }, { "sha1", __gensalt_sha1 }, { "blowfish", __gensalt_blowfish }, +#ifdef HAVE_ARGON2 + /* argon2 default to argon2id */ + { "argon2", __gensalt_argon2id}, + { "argon2id", __gensalt_argon2id}, + { "argon2i", __gensalt_argon2i}, + { "argon2d", __gensalt_argon2d}, +#endif /* HAVE_ARGON2 */ { NULL, NULL } }; @@ -171,6 +185,120 @@ __gensalt_sha1(char *salt, size_t saltsi return 0; } +#ifdef HAVE_ARGON2 +static int __gensalt_argon2_decode_option(char * dst, size_t dlen, const char * option) +{ + + char * in = 0;; + char * a = 0; + size_t tmp = 0; + int error = 0; + /* ob buffer: m_cost, t_cost, threads */ + uint32_t ob[3] = {4096, 3, 1}; + + memset(dst, 0, dlen); + + if (option == NULL) { + goto done; + } + + in = (char *)strdup(option); + + while ((a = strsep(&in, ",")) != NULL) { + switch(*a) { + + case 'm': + a += strlen("m="); + if ((getnum(a, &tmp)) == -1) { + --error; + } else { + ob[0] = tmp; + } + + break; + case 't': + a += strlen("t="); + if ((getnum(a, &tmp)) == -1) { + --error; + } else { + ob[1] = tmp; + } + + break; + case 'p': + a += strlen("p="); + if ((getnum(a, &tmp)) == -1) { + --error; + } else { + ob[2] = tmp; + } + + break; + default: + --error; + } + } + + free(in); +done: + snprintf(dst, dlen, "m=%d,t=%d,p=%d", ob[0], ob[1], ob[2]); + + return error; +} + + +static int +__gensalt_argon2(char *salt, size_t saltsiz, const char *option,argon2_type atype) +{ + int rc; + int n; + char buf[64]; + + /* get param, enforcing order and applying defaults */ + if ((rc = __gensalt_argon2_decode_option(buf, sizeof(buf), option)) < 0) { + return 0; + } + + n = snprintf(salt, saltsiz, "$%s$v=%d$%s$", + argon2_type2string(atype,0), ARGON2_VERSION_NUMBER, buf); + + if ((size_t)n + 16 >= saltsiz) { + return 0; + } + + __crypt_to64(&salt[n], arc4random(), 4); + __crypt_to64(&salt[n + 4], arc4random(), 4); + __crypt_to64(&salt[n + 8], arc4random(), 4); + __crypt_to64(&salt[n + 12], arc4random(), 4); + + salt[n + 16] = '$'; + salt[n + 17] = '\0'; + + return 0; +} + +/* argon2 variant-specific hooks to generic */ +int +__gensalt_argon2id(char *salt, size_t saltsiz, const char *option) +{ + return __gensalt_argon2(salt, saltsiz, option, Argon2_id); +} + +int +__gensalt_argon2i(char *salt, size_t saltsiz, const char *option) +{ + return __gensalt_argon2(salt, saltsiz, option, Argon2_i); +} + +int +__gensalt_argon2d(char *salt, size_t saltsiz, const char *option) +{ + return __gensalt_argon2(salt, saltsiz, option, Argon2_d); +} + +#endif /* HAVE_ARGON2 */ + + int pw_gensalt(char *salt, size_t saltlen, const char *type, const char *option) { Index: src/usr.bin/pwhash/Makefile diff -u src/usr.bin/pwhash/Makefile:1.7 src/usr.bin/pwhash/Makefile:1.8 --- src/usr.bin/pwhash/Makefile:1.7 Tue Apr 14 22:15:25 2009 +++ src/usr.bin/pwhash/Makefile Mon Oct 21 02:36:48 2019 @@ -1,10 +1,14 @@ -# $NetBSD: Makefile,v 1.7 2009/04/14 22:15:25 lukem Exp $ +# $NetBSD: Makefile,v 1.8 2019/10/21 02:36:48 jhigh Exp $ # from: @(#)Makefile 8.3 (Berkeley) 4/2/94 .include <bsd.own.mk> PROG= pwhash +.if ( defined(MKARGON2) && ${MKARGON2} != "no" ) +CPPFLAGS+= -DHAVE_ARGON2 +.endif + CPPFLAGS+=-I${.CURDIR} -DLOGIN_CAP DPADD+= ${LIBCRYPT} ${LIBUTIL} Index: src/usr.bin/pwhash/pwhash.1 diff -u src/usr.bin/pwhash/pwhash.1:1.8 src/usr.bin/pwhash/pwhash.1:1.9 --- src/usr.bin/pwhash/pwhash.1:1.8 Tue May 24 06:15:43 2016 +++ src/usr.bin/pwhash/pwhash.1 Mon Oct 21 02:36:48 2019 @@ -1,4 +1,4 @@ -.\" $NetBSD: pwhash.1,v 1.8 2016/05/24 06:15:43 abhinav Exp $ +.\" $NetBSD: pwhash.1,v 1.9 2019/10/21 02:36:48 jhigh Exp $ .\" $OpenBSD: encrypt.1,v 1.16 2000/11/09 17:52:07 aaron Exp $ .\" .\" Copyright (c) 1996, Jason Downs. All rights reserved. @@ -33,6 +33,7 @@ .Sh SYNOPSIS .Nm pwhash .Op Fl km +.Op Fl A Ar variant[,params] .Op Fl b Ar rounds .Op Fl S Ar rounds .Op Fl s Ar salt @@ -64,6 +65,26 @@ Prompt for a single string with echo tur Encrypt the salt with HMAC-SHA1 using the password as key and the specified .Ar rounds as a hint for the number of iterations. +.It Fl A Ar variant[,params] +Encrypt the specified string using Argon2 hashing parameterized using +variant +.Ar variant , +where +.Ar variant +is one of the following: argon2id, argon2i, argon2d. Variant +.Ar argon2id +is recommended. + +Following the required +.Ar variant +name, three optional comma-delimited parameters may be provided, + +t=n Specify the number of iterations to n. The default is 3. + +m=n Specify the memory usage in KB to n. The default is 4096. + +p=n Specify the number of threads to n. The default is 1. + .It Fl s Ar salt Encrypt the string using DES, with the specified .Ar salt . @@ -80,13 +101,19 @@ the algorithm specified in the default c .Pa /etc/passwd.conf will be used. .Pp -For MD5 and Blowfish a new random salt is automatically generated for each +For MD5, Blowfish, and Argon2 a new random salt is automatically generated for each password. .Pp Specifying the .Ar string on the command line should be discouraged; using the standard input is more secure. +.Sh EXAMPLES +The following specifies the argon2id variant, using 1 thread and 4096KB of memory + +pwhash -A argon2id,p=1,m=4096 -p + + .Sh FILES .Bl -tag -width /etc/passwd.conf -compact .It Pa /etc/passwd.conf Index: src/usr.bin/pwhash/pwhash.c diff -u src/usr.bin/pwhash/pwhash.c:1.15 src/usr.bin/pwhash/pwhash.c:1.16 --- src/usr.bin/pwhash/pwhash.c:1.15 Fri Sep 16 15:39:28 2011 +++ src/usr.bin/pwhash/pwhash.c Mon Oct 21 02:36:48 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: pwhash.c,v 1.15 2011/09/16 15:39:28 joerg Exp $ */ +/* $NetBSD: pwhash.c,v 1.16 2019/10/21 02:36:48 jhigh Exp $ */ /* $OpenBSD: encrypt.c,v 1.16 2002/02/16 21:27:45 millert Exp $ */ /* @@ -28,7 +28,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: pwhash.c,v 1.15 2011/09/16 15:39:28 joerg Exp $"); +__RCSID("$NetBSD: pwhash.c,v 1.16 2019/10/21 02:36:48 jhigh Exp $"); #endif #include <sys/types.h> @@ -55,12 +55,26 @@ __RCSID("$NetBSD: pwhash.c,v 1.15 2011/0 #define DO_BLF 3 #define DO_SHA1 4 +#ifdef HAVE_ARGON2 +#define DO_ARGON2 5 +/* + * Argon2 variant may be specified in /etc/passwd.conf + * If not found, default to ARGON2_DEFAULT_VARIANT_STR + * acceptable values are: argon2i, argon2d, argon2id + */ +#define ARGON2_DEFAULT_VARIANT_STR "argon2id" +#endif /* HAVE_ARGON2 */ + __dead static void usage(void) { (void)fprintf(stderr, +#ifdef HAVE_ARGON2 + "Usage: %s [-km] [-A variant[,params]] [-b rounds] [-S rounds] [-s salt] [-p | string]\n", +#else "Usage: %s [-km] [-b rounds] [-S rounds] [-s salt] [-p | string]\n", +#endif /* HAVE_ARGON2 */ getprogname()); exit(1); } @@ -119,6 +133,21 @@ print_passwd(char *string, int operation salt = extra; break; +#ifdef HAVE_ARGON2 + case DO_ARGON2: + /* pwhash -A <variant>[,param]* */ + /* param + * m=<m_cost> + * t=<t_cost> + * p=<threads> + */ + snprintf(option, sizeof(option), "%s", extra); + opt = option; + key = strsep(&opt, ","); + error = pw_gensalt(buf, _PASSWORD_LEN, key, opt); + break; +#endif /* HAVE_ARGON2 */ + default: pw_getconf(option, sizeof(option), "default", "localcipher"); opt = option; @@ -146,7 +175,11 @@ main(int argc, char **argv) if (strcmp(getprogname(), "makekey") == 0) operation = DO_MAKEKEY; +#ifdef HAVE_ARGON2 + while ((opt = getopt(argc, argv, "kmpS:s:b:A:")) != -1) { +#else while ((opt = getopt(argc, argv, "kmpS:s:b:")) != -1) { +#endif /* HAVE_ARGON2 */ switch (opt) { case 'k': /* Stdin/Stdout Unix crypt */ if (operation != -1 || prompt) @@ -188,6 +221,15 @@ main(int argc, char **argv) extra = optarg; break; +#ifdef HAVE_ARGON2 + case 'A': /* Argon2 password hash */ + if (operation != -1) + usage(); + operation = DO_ARGON2; + extra = optarg; + break; +#endif /* HAVE_ARGON2 */ + default: usage(); } Added files: Index: src/lib/libcrypt/crypt-argon2.c diff -u /dev/null src/lib/libcrypt/crypt-argon2.c:1.1 --- /dev/null Mon Oct 21 02:36:49 2019 +++ src/lib/libcrypt/crypt-argon2.c Mon Oct 21 02:36:48 2019 @@ -0,0 +1,254 @@ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <pwd.h> +#include <errno.h> +#include <argon2.h> + +#include <err.h> +#include "crypt.h" + +/* defaults pulled from run.c */ +#define HASHLEN 32 +#define T_COST_DEF 3 +#define LOG_M_COST_DEF 12 /* 2^12 = 4 MiB */ +#define LANES_DEF 1 +#define THREADS_DEF 1 +#define OUTLEN_DEF 32 +#define MAX_PASS_LEN 128 + +#define ARGON2_CONTEXT_INITIALIZER \ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + T_COST_DEF, LOG_M_COST_DEF,\ + LANES_DEF, THREADS_DEF, \ + ARGON2_VERSION_NUMBER, 0, 0, ARGON2_DEFAULT_FLAGS} + +#define ARGON2_ARGON2_STR "argon2" +#define ARGON2_ARGON2I_STR "argon2i" +#define ARGON2_ARGON2D_STR "argon2d" +#define ARGON2_ARGON2ID_STR "argon2id" + +/* getnum also declared in pw_getsalt.c */ +/* maybe move to util.h?? */ +static int +getnum(const char *str, size_t *num) +{ + char *ep; + unsigned long rv; + + if (str == NULL) { + *num = 0; + return 0; + } + + rv = strtoul(str, &ep, 0); + + if (str == ep || *ep) { + errno = EINVAL; + return -1; + } + + if (errno == ERANGE && rv == ULONG_MAX) + return -1; + *num = (size_t)rv; + return 0; +} + +/* process params to argon2 */ +/* we don't force param order as input, */ +/* but we do provide the expected order to argon2 api */ +static int decode_option(argon2_context * ctx, argon2_type * atype, const char * option) +{ + size_t tmp=0; + char * in = 0,*inp;; + char * a=0; + char * p=0; + size_t sl; + int error=0; + + in = (char *)strdup(option); + inp = in; + + if (*inp == '$') inp++; + + a = strsep(&inp, "$"); + + sl = strlen(a); + + if (sl == strlen(ARGON2_ARGON2I_STR) && + !(strcmp(ARGON2_ARGON2I_STR, a))) { + *atype=Argon2_i; + } else if (sl == strlen(ARGON2_ARGON2D_STR) && + !(strcmp(ARGON2_ARGON2D_STR, a))) { + *atype=Argon2_d; + } + else if (sl == strlen(ARGON2_ARGON2ID_STR) && + !(strcmp(ARGON2_ARGON2ID_STR, a))) { + *atype=Argon2_id; + } else { /* default to id, we assume simple mistake */ + /* don't abandon yet */ + *atype=Argon2_id; + } + + a = strsep(&inp, "$"); + + if ((getnum(a, &tmp))<0) { /* on error, default to current */ + /* should start thinking about aborting */ + ctx->version = ARGON2_VERSION_NUMBER; + } else { + ctx->version = tmp; + } + + a = strsep(&inp, "$"); + + /* parse labelled argon2 params */ + /* m_cost (m) + * t_cost (t) + * threads (p) + */ + while ((p = strsep(&a, ","))) { + switch (*p) { + case 'm': + p += strlen("m="); + if ((getnum(p, &tmp)) < 0) { + --error; + } else { + ctx->m_cost = tmp; + } + break; + case 't': + p += strlen("t="); + if ((getnum(p, &tmp)) < 0) { + --error; + } else { + ctx->t_cost = tmp; + } + break; + case 'p': + p += strlen("p="); + if ((getnum(p, &tmp)) < 0) { + --error; + } else { + ctx->threads = tmp; + } + break; + default: + return -1; + + } + } + + a = strsep(&inp, "$"); + + snprintf((char *)ctx->salt,ctx->saltlen, "%s", a); + + a = strsep(&inp, "$"); + + if (*a) { + snprintf((char *)ctx->pwd,ctx->pwdlen, "%s", a); + } else { + /* don't care if passwd hash is missing */ + /* if missing, most likely coming from */ + /* pwhash or similar */ + } + + /* free our token buffer */ + free(in); + + /* 0 on success, <0 otherwise */ + return error; +} + +char * +__crypt_argon2(const char *pw, const char * salt) +{ + /* we use the libargon2 api to generate */ + /* return code */ + int rc=0; + /* output buffer */ + char ebuf[32]; + /* ptr into argon2 encoded buffer */ + char * blkp=0; + /* argon2 variable, default to id */ + argon2_type atype = Argon2_id; + /* default to current argon2 version */ + int version=ARGON2_VERSION_NUMBER; + /* argon2 context to collect params */ + argon2_context ctx = ARGON2_CONTEXT_INITIALIZER; + /* argon2 encoded buffer */ + char encodebuf[256]; + /* argon2 salt buffer */ + char saltbuf[128]; + /* argon2 pwd buffer */ + char pwdbuf[128]; + /* returned static buffer */ + static char rbuf[512]; + + /* clear buffers */ + memset(encodebuf, 0, sizeof(encodebuf)); + memset(saltbuf, 0, sizeof(saltbuf)); + memset(pwdbuf, 0, sizeof(pwdbuf)); + memset(rbuf, 0, sizeof(rbuf)); + + /* we use static buffers to avoid allocation */ + /* and easier cleanup */ + ctx.out = (uint8_t *)ebuf; + ctx.outlen = sizeof(ebuf); + + ctx.out = (uint8_t *)encodebuf; + ctx.outlen = sizeof(encodebuf); + + ctx.salt = (uint8_t *)saltbuf; + ctx.saltlen = sizeof(saltbuf); + + ctx.pwd= (uint8_t *)pwdbuf; + ctx.pwdlen = sizeof(pwdbuf); + + /* decode salt string to argon2 params */ + /* argon2 context for param collection */ + rc = decode_option(&ctx, &atype, salt); + + if (rc < 0) { + /* unable to parse input params */ + return 0; + } + + rc = argon2_hash(ctx.t_cost, ctx.m_cost, + ctx.threads, pw, strlen(pw), ctx.salt, strlen((char*)ctx.salt), + ebuf, sizeof(ebuf), encodebuf, sizeof(encodebuf), atype, ctx.version); + + if (rc != ARGON2_OK) { + fprintf(stderr, "Failed: %s\n", argon2_error_message(rc)); + return 0; + } + + /* get encoded passwd */ + if ((blkp = strrchr(encodebuf, '$')) == NULL) { + return 0; + } + + /* skip over '$' */ + blkp++; + + /* we don't use encoded here because it base64 encodes salt */ + /* same encoding format as argon2 api, but with original salt */ + snprintf(rbuf, sizeof(rbuf)-1, "$%s$v=%d$m=%d,t=%d,p=%d$%s$%s", + argon2_type2string(atype,0), + version, + ctx.m_cost, + ctx.t_cost, + ctx.threads, + ctx.salt, + blkp); + + /* clear buffers */ + memset(encodebuf, 0, sizeof(encodebuf)); + memset(saltbuf, 0, sizeof(saltbuf)); + memset(pwdbuf, 0, sizeof(pwdbuf)); + + /* return encoded str */ + return rbuf; +}