1 /*        $NetBSD: crypt-argon2.c,v 1.22 2024/07/23 22:37:11 riastradh Exp $    */
2 
3 /*
4  * Copyright (c) 2009 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: crypt-argon2.c,v 1.22 2024/07/23 22:37:11 riastradh Exp $");
31 
32 #include <sys/resource.h>
33 #include <sys/param.h>
34 #include <sys/sysctl.h>
35 #include <sys/syslimits.h>
36 
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <unistd.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <time.h>
43 #include <pwd.h>
44 #include <errno.h>
45 #include <argon2.h>
46 
47 #include <err.h>
48 #include "crypt.h"
49 
50 crypt_private int
51 estimate_argon2_params(argon2_type, uint32_t *,
52     uint32_t *, uint32_t *);
53 
54 /* defaults pulled from run.c */
55 #define HASHLEN               32
56 #define T_COST_DEF  3
57 #define LOG_M_COST_DEF        12 /* 2^12 = 4 MiB */
58 #define LANES_DEF   1
59 #define THREADS_DEF           1
60 #define OUTLEN_DEF  32
61 #define MAX_PASS_LEN          128
62 
63 #define ARGON2_CONTEXT_INITIALIZER      \
64           {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
65           T_COST_DEF, LOG_M_COST_DEF,\
66           LANES_DEF, THREADS_DEF, \
67           ARGON2_VERSION_NUMBER, 0, 0, ARGON2_DEFAULT_FLAGS}
68 
69 #define ARGON2_ARGON2_STR     "argon2"
70 #define ARGON2_ARGON2I_STR    "argon2i"
71 #define ARGON2_ARGON2D_STR    "argon2d"
72 #define ARGON2_ARGON2ID_STR   "argon2id"
73 
74 /*
75  * Unpadded Base64 calculations are taken from the Apache2/CC-0
76  * licensed libargon2 for compatibility
77  */
78 
79 /*
80  * Some macros for constant-time comparisons. These work over values in
81  * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
82  */
83 #define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF)
84 #define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF)
85 #define GE(x, y) (GT(y, x) ^ 0xFF)
86 #define LT(x, y) GT(y, x)
87 #define LE(x, y) GE(y, x)
88 
89 static unsigned
b64_char_to_byte(int c)90 b64_char_to_byte(int c)
91 {
92     unsigned x;
93 
94     x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
95         (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
96         (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) |
97         (EQ(c, '/') & 63);
98     return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
99 }
100 
101 static const char *
from_base64(void * dst,size_t * dst_len,const char * src)102 from_base64(void *dst, size_t *dst_len, const char *src)
103 {
104           size_t len;
105           unsigned char *buf;
106           unsigned acc, acc_len;
107 
108           buf = (unsigned char *)dst;
109           len = 0;
110           acc = 0;
111           acc_len = 0;
112           for (;;) {
113                     unsigned d;
114 
115                     d = b64_char_to_byte(*src);
116                     if (d == 0xFF) {
117                               break;
118                     }
119                     src++;
120                     acc = (acc << 6) + d;
121                     acc_len += 6;
122                     if (acc_len >= 8) {
123                               acc_len -= 8;
124                               if ((len++) >= *dst_len) {
125                                         return NULL;
126                               }
127                               *buf++ = (acc >> acc_len) & 0xFF;
128                     }
129           }
130 
131           /*
132            * If the input length is equal to 1 modulo 4 (which is
133            * invalid), then there will remain 6 unprocessed bits;
134            * otherwise, only 0, 2 or 4 bits are buffered. The buffered
135            * bits must also all be zero.
136            */
137           if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) {
138                     return NULL;
139           }
140           *dst_len = len;
141           return src;
142 }
143 
144 /*
145  * Used to find default parameters that perform well on the host
146  * machine.  Inputs should dereference to either 0 (to estimate),
147  * or desired value.
148  */
149 crypt_private int
estimate_argon2_params(argon2_type atype,uint32_t * etime,uint32_t * ememory,uint32_t * ethreads)150 estimate_argon2_params(argon2_type atype, uint32_t *etime,
151     uint32_t *ememory, uint32_t *ethreads)
152 {
153           const int mib[] = { CTL_HW, HW_USERMEM64 };
154           struct timespec tp1, tp2, delta;
155           char tmp_salt[16];
156           char tmp_pwd[16];
157           uint32_t tmp_hash[32];
158           char tmp_encoded[256];
159           struct rlimit rlim;
160           uint64_t max_mem; /* usermem64 returns bytes */
161           size_t max_mem_sz = sizeof(max_mem);
162           /* low values from argon2 test suite... */
163           uint32_t memory = 256; /* 256k; argon2 wants kilobytes */
164           uint32_t time = 3;
165           uint32_t threads = 1;
166 
167           if (*ememory < ARGON2_MIN_MEMORY) {
168                     /*
169                      * attempt to find a reasonble bound for memory use
170                      */
171                     if (sysctl(mib, __arraycount(mib),
172                         &max_mem, &max_mem_sz, NULL, 0) < 0) {
173                               goto error;
174                     }
175                     if (getrlimit(RLIMIT_AS, &rlim) < 0)
176                               goto error;
177                     if (max_mem > rlim.rlim_cur && rlim.rlim_cur != RLIM_INFINITY)
178                               max_mem = rlim.rlim_cur;
179 
180                     /*
181                      * Note that adding memory also greatly slows the algorithm.
182                      * Do we need to be concerned about memory usage during
183                      * concurrent connections?
184                      */
185                     max_mem /= 1000000; /* bytes down to mb */
186                     if (max_mem > 30000) {
187                               memory = 32768;
188                     } else if (max_mem > 15000) {
189                               memory = 16384;
190                     } else if (max_mem > 7000) {
191                               memory = 8192;
192                     } else if (max_mem > 3000) {
193                               memory = 4096;
194                     } else if (max_mem > 900) {
195                               memory = 1024;
196                     } else if (max_mem > 24) {
197                               memory = 256;
198                     } else {
199                               memory = ARGON2_MIN_MEMORY;
200                     }
201           } else {
202                     memory = *ememory;
203           }
204 
205           if (*etime < ARGON2_MIN_TIME) {
206                     /*
207                      * just fill these with random stuff since we'll immediately
208                      * discard them after calculating hashes for 1 second
209                      */
210                     arc4random_buf(tmp_pwd, sizeof(tmp_pwd));
211                     arc4random_buf(tmp_salt, sizeof(tmp_salt));
212 
213                     if (clock_gettime(CLOCK_MONOTONIC, &tp1) == -1)
214                               goto error;
215                     for (; time < ARGON2_MAX_TIME; ++time) {
216                               if (argon2_hash(time, memory, threads,
217                                   tmp_pwd, sizeof(tmp_pwd),
218                                   tmp_salt, sizeof(tmp_salt),
219                                   tmp_hash, sizeof(tmp_hash),
220                                   tmp_encoded, sizeof(tmp_encoded),
221                                   atype, ARGON2_VERSION_NUMBER) != ARGON2_OK) {
222                                         goto reset;
223                               }
224                               if (clock_gettime(CLOCK_MONOTONIC, &tp2) == -1)
225                                         break;
226                               if (timespeccmp(&tp1, &tp2, >))
227                                         break; /* broken system... */
228                               timespecsub(&tp2, &tp1, &delta);
229                               if (delta.tv_sec >= 1)
230                                         break;
231                     }
232           } else {
233                     time = *etime;
234           }
235 
236 error:
237           *etime = time;
238           *ememory = memory;
239           *ethreads = threads;
240           return 0;
241 reset:
242           time = 3;
243           memory = 256;
244           threads = 1;
245           goto error;
246 }
247 
248 
249 /* process params to argon2 */
250 /* we don't force param order as input, */
251 /* but we do provide the expected order to argon2 api */
252 static int
decode_option(argon2_context * ctx,argon2_type * atype,const char * option)253 decode_option(argon2_context *ctx, argon2_type *atype, const char *option)
254 {
255           size_t tmp = 0;
256         char *in = 0, *inp;
257         char *a = 0;
258         char *p = 0;
259           size_t sl;
260           int error = 0;
261 
262         in = (char *)strdup(option);
263           inp = in;
264 
265           if (*inp == '$') inp++;
266 
267           a = strsep(&inp, "$");
268 
269           sl = strlen(a);
270 
271           if (sl == strlen(ARGON2_ARGON2I_STR) &&
272              !(strcmp(ARGON2_ARGON2I_STR, a))) {
273                     *atype=Argon2_i;
274           } else if (sl == strlen(ARGON2_ARGON2D_STR) &&
275                   !(strcmp(ARGON2_ARGON2D_STR, a))) {
276                     *atype=Argon2_d;
277           }
278           else if (sl == strlen(ARGON2_ARGON2ID_STR) &&
279                   !(strcmp(ARGON2_ARGON2ID_STR, a))) {
280                     *atype=Argon2_id;
281           } else { /* default to id, we assume simple mistake */
282                     /* don't abandon yet */
283                     *atype=Argon2_id;
284           }
285 
286           a = strsep(&inp, "$");
287 
288           /* parse the version number of the hash, if it's there */
289           if (strncmp(a, "v=", 2) == 0) {
290                     a += 2;
291                     if ((getnum(a, &tmp))<0) { /* on error, default to current */
292                               /* should start thinking about aborting */
293                               ctx->version = ARGON2_VERSION_10;
294                     } else {
295                               ctx->version = tmp;
296                     }
297                     a = strsep(&inp, "$");
298           } else {
299                     /*
300                      * This is a parameter list, not a version number, use the
301                      * default version.
302                      */
303                     ctx->version = ARGON2_VERSION_10;
304           }
305 
306           /* parse labelled argon2 params */
307           /* m_cost (m)
308            * t_cost (t)
309            * threads (p)
310            */
311           while ((p = strsep(&a, ","))) {
312                     switch (*p) {
313                               case 'm':
314                                         p += strlen("m=");
315                                         if ((getnum(p, &tmp)) < 0) {
316                                                   --error;
317                                         } else {
318                                                   ctx->m_cost = tmp;
319                                         }
320                                         break;
321                               case 't':
322                                         p += strlen("t=");
323                                         if ((getnum(p, &tmp)) < 0) {
324                                                   --error;
325                                         } else {
326                                                   ctx->t_cost = tmp;
327                                         }
328                                         break;
329                               case 'p':
330                                         p += strlen("p=");
331                                         if ((getnum(p, &tmp)) < 0) {
332                                                   --error;
333                                         } else {
334                                                   ctx->threads = tmp;
335                                         }
336                                         break;
337                               default:
338                                 free(in);
339                                         return -1;
340 
341                     }
342           }
343 
344           a = strsep(&inp, "$");
345           if (a == NULL) {
346                     free(in);
347                     return -1;
348           }
349 
350           sl = ctx->saltlen;
351 
352           if (from_base64(ctx->salt, &sl, a) == NULL) {
353                     free(in);
354                     return -1;
355           }
356 
357           ctx->saltlen = sl;
358 
359           a = strsep(&inp, "$");
360 
361           if (a) {
362                     snprintf((char *)ctx->pwd, ctx->pwdlen, "%s", a);
363           } else {
364                     /* don't care if passwd hash is missing */
365                     /* if missing, most likely coming from */
366                     /* pwhash or similar */
367           }
368 
369           /* free our token buffer */
370         free(in);
371 
372           /* 0 on success, <0 otherwise */
373         return error;
374 }
375 
376 crypt_private char *
__crypt_argon2(const char * pw,const char * salt)377 __crypt_argon2(const char *pw, const char * salt)
378 {
379           /* we use the libargon2 api to generate */
380           /* return code */
381           int rc = 0;
382           /* output buffer */
383           char ebuf[32];
384           /* argon2 variable, default to id */
385           argon2_type atype = Argon2_id;
386           /* default to current argon2 version */
387           /* argon2 context to collect params */
388           argon2_context ctx = ARGON2_CONTEXT_INITIALIZER;
389           /* argon2 encoded buffer */
390           char encodebuf[256];
391           /* argon2 salt buffer */
392           char saltbuf[128];
393           /* argon2 pwd buffer */
394           char pwdbuf[128];
395           /* returned static buffer */
396           static char rbuf[512];
397 
398           /* clear buffers */
399           explicit_memset(rbuf, 0, sizeof(rbuf));
400 
401           /* we use static buffers to avoid allocation */
402           /* and easier cleanup */
403           ctx.out = (uint8_t *)encodebuf;
404           ctx.outlen = sizeof(encodebuf);
405 
406           ctx.salt = (uint8_t *)saltbuf;
407           ctx.saltlen = sizeof(saltbuf);
408 
409           ctx.pwd = (uint8_t *)pwdbuf;
410           ctx.pwdlen = sizeof(pwdbuf);
411 
412           /* decode salt string to argon2 params */
413           /* argon2 context for param collection */
414           rc = decode_option(&ctx, &atype, salt);
415 
416           if (rc < 0) {
417                     /* unable to parse input params */
418                     return NULL;
419           }
420 
421           rc = argon2_hash(ctx.t_cost, ctx.m_cost,
422               ctx.threads, pw, strlen(pw), ctx.salt, ctx.saltlen,
423               ebuf, sizeof(ebuf), encodebuf, sizeof(encodebuf),
424               atype, ctx.version);
425 
426           if (rc != ARGON2_OK) {
427                     fprintf(stderr, "argon2: failed: %s\n",
428                         argon2_error_message(rc));
429                     return NULL;
430           }
431 
432           memcpy(rbuf, encodebuf, sizeof(encodebuf));
433 
434           /* clear buffers */
435           explicit_memset(ebuf, 0, sizeof(ebuf));
436           explicit_memset(encodebuf, 0, sizeof(encodebuf));
437           explicit_memset(saltbuf, 0, sizeof(saltbuf));
438           explicit_memset(pwdbuf, 0, sizeof(pwdbuf));
439 
440           /* return encoded str */
441           return rbuf;
442 }
443