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 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 * 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 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 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 * 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