Home | History | Annotate | Line # | Download | only in libcrypt
crypt-argon2.c revision 1.10
      1 /*
      2  * Copyright (c) 2009 The NetBSD Foundation, Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     15  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     16  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     24  * POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include <stdlib.h>
     28 #include <stdio.h>
     29 #include <unistd.h>
     30 #include <stdio.h>
     31 #include <string.h>
     32 #include <time.h>
     33 #include <pwd.h>
     34 #include <errno.h>
     35 #include <argon2.h>
     36 
     37 #include <err.h>
     38 #include "crypt.h"
     39 
     40 /* defaults pulled from run.c */
     41 #define HASHLEN		32
     42 #define T_COST_DEF 	3
     43 #define LOG_M_COST_DEF 	12 /* 2^12 = 4 MiB */
     44 #define LANES_DEF 	1
     45 #define THREADS_DEF 	1
     46 #define OUTLEN_DEF 	32
     47 #define MAX_PASS_LEN 	128
     48 
     49 #define ARGON2_CONTEXT_INITIALIZER	\
     50 	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
     51 	T_COST_DEF, LOG_M_COST_DEF,\
     52 	LANES_DEF, THREADS_DEF, \
     53 	ARGON2_VERSION_NUMBER, 0, 0, ARGON2_DEFAULT_FLAGS}
     54 
     55 #define ARGON2_ARGON2_STR	"argon2"
     56 #define ARGON2_ARGON2I_STR	"argon2i"
     57 #define ARGON2_ARGON2D_STR	"argon2d"
     58 #define ARGON2_ARGON2ID_STR	"argon2id"
     59 
     60 
     61 /*
     62  * Some macros for constant-time comparisons. These work over values in
     63  * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
     64  */
     65 #define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF)
     66 #define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF)
     67 #define GE(x, y) (GT(y, x) ^ 0xFF)
     68 #define LT(x, y) GT(y, x)
     69 #define LE(x, y) GE(y, x)
     70 
     71 static unsigned
     72 b64_char_to_byte(int c)
     73 {
     74     unsigned x;
     75 
     76     x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
     77         (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
     78         (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) |
     79         (EQ(c, '/') & 63);
     80     return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
     81 }
     82 
     83 static const char *
     84 from_base64(void *dst, size_t *dst_len, const char *src)
     85 {
     86 	size_t len;
     87 	unsigned char *buf;
     88 	unsigned acc, acc_len;
     89 
     90 	buf = (unsigned char *)dst;
     91 	len = 0;
     92 	acc = 0;
     93 	acc_len = 0;
     94 	for (;;) {
     95 		unsigned d;
     96 
     97 		d = b64_char_to_byte(*src);
     98 		if (d == 0xFF) {
     99 			break;
    100 		}
    101 		src++;
    102 		acc = (acc << 6) + d;
    103 		acc_len += 6;
    104 		if (acc_len >= 8) {
    105 			acc_len -= 8;
    106 			if ((len++) >= *dst_len) {
    107 				return NULL;
    108 			}
    109 			*buf++ = (acc >> acc_len) & 0xFF;
    110 		}
    111 	}
    112 
    113 	/*
    114 	 * If the input length is equal to 1 modulo 4 (which is
    115 	 * invalid), then there will remain 6 unprocessed bits;
    116 	 * otherwise, only 0, 2 or 4 bits are buffered. The buffered
    117 	 * bits must also all be zero.
    118 	 */
    119 	if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) {
    120 		return NULL;
    121 	}
    122 	*dst_len = len;
    123 	return src;
    124 }
    125 
    126 /* process params to argon2 */
    127 /* we don't force param order as input, */
    128 /* but we do provide the expected order to argon2 api */
    129 static int
    130 decode_option(argon2_context *ctx, argon2_type *atype, const char *option)
    131 {
    132 	size_t tmp = 0;
    133         char *in = 0, *inp;
    134         char *a = 0;
    135         char *p = 0;
    136 	size_t sl;
    137 	int error = 0;
    138 
    139         in = (char *)strdup(option);
    140 	inp = in;
    141 
    142 	if (*inp == '$') inp++;
    143 
    144 	a = strsep(&inp, "$");
    145 
    146 	sl = strlen(a);
    147 
    148 	if (sl == strlen(ARGON2_ARGON2I_STR) &&
    149 	   !(strcmp(ARGON2_ARGON2I_STR, a))) {
    150 		*atype=Argon2_i;
    151 	} else if (sl == strlen(ARGON2_ARGON2D_STR) &&
    152 	        !(strcmp(ARGON2_ARGON2D_STR, a))) {
    153 		*atype=Argon2_d;
    154 	}
    155 	else if (sl == strlen(ARGON2_ARGON2ID_STR) &&
    156 	        !(strcmp(ARGON2_ARGON2ID_STR, a))) {
    157 		*atype=Argon2_id;
    158 	} else { /* default to id, we assume simple mistake */
    159 		/* don't abandon yet */
    160 		*atype=Argon2_id;
    161 	}
    162 
    163 	a = strsep(&inp, "$");
    164 
    165 	/* parse the version number of the hash, if it's there */
    166 	if (strncmp(a, "v=", 2) == 0) {
    167 		a += 2;
    168 		if ((getnum(a, &tmp))<0) { /* on error, default to current */
    169 			/* should start thinking about aborting */
    170 			ctx->version = ARGON2_VERSION_10;
    171 		} else {
    172 			ctx->version = tmp;
    173 		}
    174 		a = strsep(&inp, "$");
    175 	} else {
    176 		/*
    177 		 * This is a parameter list, not a version number, use the
    178 		 * default version.
    179 		 */
    180 		ctx->version = ARGON2_VERSION_10;
    181 	}
    182 
    183 	/* parse labelled argon2 params */
    184 	/* m_cost (m)
    185 	 * t_cost (t)
    186 	 * threads (p)
    187 	 */
    188 	while ((p = strsep(&a, ","))) {
    189 		switch (*p) {
    190 			case 'm':
    191 				p += strlen("m=");
    192 				if ((getnum(p, &tmp)) < 0) {
    193 					--error;
    194 				} else {
    195 					ctx->m_cost = tmp;
    196 				}
    197 				break;
    198 			case 't':
    199 				p += strlen("t=");
    200 				if ((getnum(p, &tmp)) < 0) {
    201 					--error;
    202 				} else {
    203 					ctx->t_cost = tmp;
    204 				}
    205 				break;
    206 			case 'p':
    207 				p += strlen("p=");
    208 				if ((getnum(p, &tmp)) < 0) {
    209 					--error;
    210 				} else {
    211 					ctx->threads = tmp;
    212 				}
    213 				break;
    214 			default:
    215 				return -1;
    216 
    217 		}
    218 	}
    219 
    220 	a = strsep(&inp, "$");
    221 
    222 	sl = ctx->saltlen;
    223 
    224 	if (from_base64(ctx->salt, &sl, a) == NULL)
    225 		return -1;
    226 
    227 	ctx->saltlen = sl;
    228 
    229 	a = strsep(&inp, "$");
    230 
    231 	if (a) {
    232 		snprintf((char *)ctx->pwd, ctx->pwdlen, "%s", a);
    233 	} else {
    234 		/* don't care if passwd hash is missing */
    235 		/* if missing, most likely coming from */
    236 		/* pwhash or similar */
    237 	}
    238 
    239 	/* free our token buffer */
    240         free(in);
    241 
    242 	/* 0 on success, <0 otherwise */
    243         return error;
    244 }
    245 
    246 crypt_private char *
    247 __crypt_argon2(const char *pw, const char * salt)
    248 {
    249 	/* we use the libargon2 api to generate */
    250 	/* return code */
    251 	int rc = 0;
    252 	/* output buffer */
    253 	char ebuf[32];
    254 	/* argon2 variable, default to id */
    255 	argon2_type atype = Argon2_id;
    256 	/* default to current argon2 version */
    257 	/* argon2 context to collect params */
    258 	argon2_context ctx = ARGON2_CONTEXT_INITIALIZER;
    259 	/* argon2 encoded buffer */
    260 	char encodebuf[256];
    261 	/* argon2 salt buffer */
    262 	char saltbuf[128];
    263 	/* argon2 pwd buffer */
    264 	char pwdbuf[128];
    265 	/* returned static buffer */
    266 	static char rbuf[512];
    267 
    268 	/* clear buffers */
    269 	explicit_memset(rbuf, 0, sizeof(rbuf));
    270 
    271 	/* we use static buffers to avoid allocation */
    272 	/* and easier cleanup */
    273 	ctx.out = (uint8_t *)ebuf;
    274 	ctx.outlen = sizeof(ebuf);
    275 
    276 	ctx.out = (uint8_t *)encodebuf;
    277 	ctx.outlen = sizeof(encodebuf);
    278 
    279 	ctx.salt = (uint8_t *)saltbuf;
    280 	ctx.saltlen = sizeof(saltbuf);
    281 
    282 	ctx.pwd = (uint8_t *)pwdbuf;
    283 	ctx.pwdlen = sizeof(pwdbuf);
    284 
    285 	/* decode salt string to argon2 params */
    286 	/* argon2 context for param collection */
    287 	rc = decode_option(&ctx, &atype, salt);
    288 
    289 	if (rc < 0) {
    290 		/* unable to parse input params */
    291 		return 0;
    292 	}
    293 
    294 	rc = argon2_hash(ctx.t_cost, ctx.m_cost,
    295 	    ctx.threads, pw, strlen(pw), ctx.salt, ctx.saltlen,
    296 	    ebuf, sizeof(ebuf), encodebuf, sizeof(encodebuf),
    297 	    atype, ctx.version);
    298 
    299 	if (rc != ARGON2_OK) {
    300 		fprintf(stderr, "argon2: failed: %s\n",
    301 		    argon2_error_message(rc));
    302 		return 0;
    303 	}
    304 
    305 	memcpy(rbuf, encodebuf, sizeof(encodebuf));
    306 
    307 	/* clear buffers */
    308 	explicit_memset(ebuf, 0, sizeof(ebuf));
    309 	explicit_memset(encodebuf, 0, sizeof(encodebuf));
    310 	explicit_memset(saltbuf, 0, sizeof(saltbuf));
    311 	explicit_memset(pwdbuf, 0, sizeof(pwdbuf));
    312 
    313 	/* return encoded str */
    314 	return rbuf;
    315 }
    316