xref: /netbsd/lib/libcrypt/crypt-argon2.c (revision 322e5cac)
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <time.h>
7 #include <pwd.h>
8 #include <errno.h>
9 #include <argon2.h>
10 
11 #include <err.h>
12 #include "crypt.h"
13 
14 /* defaults pulled from run.c */
15 #define HASHLEN		32
16 #define T_COST_DEF 	3
17 #define LOG_M_COST_DEF 	12 /* 2^12 = 4 MiB */
18 #define LANES_DEF 	1
19 #define THREADS_DEF 	1
20 #define OUTLEN_DEF 	32
21 #define MAX_PASS_LEN 	128
22 
23 #define ARGON2_CONTEXT_INITIALIZER	\
24 	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
25 	T_COST_DEF, LOG_M_COST_DEF,\
26 	LANES_DEF, THREADS_DEF, \
27 	ARGON2_VERSION_NUMBER, 0, 0, ARGON2_DEFAULT_FLAGS}
28 
29 #define ARGON2_ARGON2_STR	"argon2"
30 #define ARGON2_ARGON2I_STR	"argon2i"
31 #define ARGON2_ARGON2D_STR	"argon2d"
32 #define ARGON2_ARGON2ID_STR	"argon2id"
33 
34 
35 /*
36  * Some macros for constant-time comparisons. These work over values in
37  * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
38  */
39 #define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF)
40 #define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF)
41 #define GE(x, y) (GT(y, x) ^ 0xFF)
42 #define LT(x, y) GT(y, x)
43 #define LE(x, y) GE(y, x)
44 
45 static unsigned
46 b64_char_to_byte(int c)
47 {
48     unsigned x;
49 
50     x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
51         (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
52         (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) |
53         (EQ(c, '/') & 63);
54     return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
55 }
56 
57 static const char *
58 from_base64(void *dst, size_t *dst_len, const char *src)
59 {
60 	size_t len;
61 	unsigned char *buf;
62 	unsigned acc, acc_len;
63 
64 	buf = (unsigned char *)dst;
65 	len = 0;
66 	acc = 0;
67 	acc_len = 0;
68 	for (;;) {
69 		unsigned d;
70 
71 		d = b64_char_to_byte(*src);
72 		if (d == 0xFF) {
73 			break;
74 		}
75 		src++;
76 		acc = (acc << 6) + d;
77 		acc_len += 6;
78 		if (acc_len >= 8) {
79 			acc_len -= 8;
80 			if ((len++) >= *dst_len) {
81 				return NULL;
82 			}
83 			*buf++ = (acc >> acc_len) & 0xFF;
84 		}
85 	}
86 
87 	/*
88 	 * If the input length is equal to 1 modulo 4 (which is
89 	 * invalid), then there will remain 6 unprocessed bits;
90 	 * otherwise, only 0, 2 or 4 bits are buffered. The buffered
91 	 * bits must also all be zero.
92 	 */
93 	if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) {
94 		return NULL;
95 	}
96 	*dst_len = len;
97 	return src;
98 }
99 
100 /* process params to argon2 */
101 /* we don't force param order as input, */
102 /* but we do provide the expected order to argon2 api */
103 static int
104 decode_option(argon2_context *ctx, argon2_type *atype, const char *option)
105 {
106 	size_t tmp = 0;
107         char *in = 0, *inp;
108         char *a = 0;
109         char *p = 0;
110 	size_t sl;
111 	int error = 0;
112 
113         in = (char *)strdup(option);
114 	inp = in;
115 
116 	if (*inp == '$') inp++;
117 
118 	a = strsep(&inp, "$");
119 
120 	sl = strlen(a);
121 
122 	if (sl == strlen(ARGON2_ARGON2I_STR) &&
123 	   !(strcmp(ARGON2_ARGON2I_STR, a))) {
124 		*atype=Argon2_i;
125 	} else if (sl == strlen(ARGON2_ARGON2D_STR) &&
126 	        !(strcmp(ARGON2_ARGON2D_STR, a))) {
127 		*atype=Argon2_d;
128 	}
129 	else if (sl == strlen(ARGON2_ARGON2ID_STR) &&
130 	        !(strcmp(ARGON2_ARGON2ID_STR, a))) {
131 		*atype=Argon2_id;
132 	} else { /* default to id, we assume simple mistake */
133 		/* don't abandon yet */
134 		*atype=Argon2_id;
135 	}
136 
137 	a = strsep(&inp, "$");
138 
139 	/* parse the version number of the hash, if it's there */
140 	if (strncmp(a, "v=", 2) == 0) {
141 		a += 2;
142 		if ((getnum(a, &tmp))<0) { /* on error, default to current */
143 			/* should start thinking about aborting */
144 			ctx->version = ARGON2_VERSION_10;
145 		} else {
146 			ctx->version = tmp;
147 		}
148 		a = strsep(&inp, "$");
149 	} else {
150 		/*
151 		 * This is a parameter list, not a version number, use the
152 		 * default version.
153 		 */
154 		ctx->version = ARGON2_VERSION_10;
155 	}
156 
157 	/* parse labelled argon2 params */
158 	/* m_cost (m)
159 	 * t_cost (t)
160 	 * threads (p)
161 	 */
162 	while ((p = strsep(&a, ","))) {
163 		switch (*p) {
164 			case 'm':
165 				p += strlen("m=");
166 				if ((getnum(p, &tmp)) < 0) {
167 					--error;
168 				} else {
169 					ctx->m_cost = tmp;
170 				}
171 				break;
172 			case 't':
173 				p += strlen("t=");
174 				if ((getnum(p, &tmp)) < 0) {
175 					--error;
176 				} else {
177 					ctx->t_cost = tmp;
178 				}
179 				break;
180 			case 'p':
181 				p += strlen("p=");
182 				if ((getnum(p, &tmp)) < 0) {
183 					--error;
184 				} else {
185 					ctx->threads = tmp;
186 				}
187 				break;
188 			default:
189 				return -1;
190 
191 		}
192 	}
193 
194 	a = strsep(&inp, "$");
195 
196 	sl = ctx->saltlen;
197 
198 	if (from_base64(ctx->salt, &sl, a) == NULL)
199 		return -1;
200 
201 	ctx->saltlen = sl;
202 
203 	a = strsep(&inp, "$");
204 
205 	if (a) {
206 		snprintf((char *)ctx->pwd, ctx->pwdlen, "%s", a);
207 	} else {
208 		/* don't care if passwd hash is missing */
209 		/* if missing, most likely coming from */
210 		/* pwhash or similar */
211 	}
212 
213 	/* free our token buffer */
214         free(in);
215 
216 	/* 0 on success, <0 otherwise */
217         return error;
218 }
219 
220 char *
221 __crypt_argon2(const char *pw, const char * salt)
222 {
223 	/* we use the libargon2 api to generate */
224 	/* return code */
225 	int rc = 0;
226 	/* output buffer */
227 	char ebuf[32];
228 	/* argon2 variable, default to id */
229 	argon2_type atype = Argon2_id;
230 	/* default to current argon2 version */
231 	/* argon2 context to collect params */
232 	argon2_context ctx = ARGON2_CONTEXT_INITIALIZER;
233 	/* argon2 encoded buffer */
234 	char encodebuf[256];
235 	/* argon2 salt buffer */
236 	char saltbuf[128];
237 	/* argon2 pwd buffer */
238 	char pwdbuf[128];
239 	/* returned static buffer */
240 	static char rbuf[512];
241 
242 	/* clear buffers */
243 	explicit_memset(rbuf, 0, sizeof(rbuf));
244 
245 	/* we use static buffers to avoid allocation */
246 	/* and easier cleanup */
247 	ctx.out = (uint8_t *)ebuf;
248 	ctx.outlen = sizeof(ebuf);
249 
250 	ctx.out = (uint8_t *)encodebuf;
251 	ctx.outlen = sizeof(encodebuf);
252 
253 	ctx.salt = (uint8_t *)saltbuf;
254 	ctx.saltlen = sizeof(saltbuf);
255 
256 	ctx.pwd = (uint8_t *)pwdbuf;
257 	ctx.pwdlen = sizeof(pwdbuf);
258 
259 	/* decode salt string to argon2 params */
260 	/* argon2 context for param collection */
261 	rc = decode_option(&ctx, &atype, salt);
262 
263 	if (rc < 0) {
264 		/* unable to parse input params */
265 		return 0;
266 	}
267 
268 	rc = argon2_hash(ctx.t_cost, ctx.m_cost,
269 	    ctx.threads, pw, strlen(pw), ctx.salt, ctx.saltlen,
270 	    ebuf, sizeof(ebuf), encodebuf, sizeof(encodebuf),
271 	    atype, ctx.version);
272 
273 	if (rc != ARGON2_OK) {
274 		fprintf(stderr, "argon2: failed: %s\n",
275 		    argon2_error_message(rc));
276 		return 0;
277 	}
278 
279 	puts(encodebuf);
280 	memcpy(rbuf, encodebuf, sizeof(encodebuf));
281 
282 	/* clear buffers */
283 	explicit_memset(ebuf, 0, sizeof(ebuf));
284 	explicit_memset(encodebuf, 0, sizeof(encodebuf));
285 	explicit_memset(saltbuf, 0, sizeof(saltbuf));
286 	explicit_memset(pwdbuf, 0, sizeof(pwdbuf));
287 
288 	/* return encoded str */
289 	return rbuf;
290 }
291