Home | History | Annotate | Line # | Download | only in cgdconfig
      1 /*	$NetBSD: argon2_utils.c,v 1.1 2021/11/22 14:34:35 nia Exp $ */
      2 /*-
      3  * Copyright (c) 2021 The NetBSD Foundation, Inc.
      4  * All rights reserved.
      5  *
      6  * This code is derived from software contributed to The NetBSD Foundation
      7  * by Nia Alarie.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     28  * POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include <sys/resource.h>
     32 #include <sys/sysctl.h>
     33 #include <argon2.h>
     34 #include <stdio.h>
     35 #include <stdlib.h>
     36 #include <time.h>
     37 #include <util.h>
     38 #include <err.h>
     39 
     40 #include "argon2_utils.h"
     41 
     42 #ifndef lint
     43 __RCSID("$NetBSD: argon2_utils.c,v 1.1 2021/11/22 14:34:35 nia Exp $");
     44 #endif
     45 
     46 static size_t
     47 get_cpucount(void)
     48 {
     49 	const int mib[] = { CTL_HW, HW_NCPUONLINE };
     50 	int ncpuonline = 1;
     51 	size_t ncpuonline_len = sizeof(ncpuonline);
     52 
     53 	if (sysctl(mib, __arraycount(mib),
     54 	    &ncpuonline, &ncpuonline_len, NULL, 0) == -1) {
     55 		return 1;
     56 	}
     57 	return ncpuonline;
     58 }
     59 
     60 static uint64_t
     61 get_usermem(void)
     62 {
     63 	const int mib[] = { CTL_HW, HW_USERMEM64 };
     64 	uint64_t usermem64 = 0;
     65 	size_t usermem64_len = sizeof(usermem64);
     66 	struct rlimit rlim;
     67 
     68 	if (sysctl(mib, __arraycount(mib),
     69 	    &usermem64, &usermem64_len, NULL, 0) == -1) {
     70 		return 0;
     71 	}
     72 
     73 	if (getrlimit(RLIMIT_AS, &rlim) == -1)
     74 		return usermem64;
     75 	if (usermem64 > rlim.rlim_cur && rlim.rlim_cur != RLIM_INFINITY)
     76 		usermem64 = rlim.rlim_cur;
     77 	return usermem64; /* bytes */
     78 }
     79 
     80 void
     81 argon2id_calibrate(size_t keylen, size_t saltlen,
     82     size_t *iterations, size_t *memory, size_t *parallelism)
     83 {
     84 	size_t mem = ARGON2_MIN_MEMORY; /* kilobytes */
     85 	size_t time;
     86 	const size_t ncpus = get_cpucount();
     87 	const uint64_t usermem = get_usermem(); /* bytes */
     88 	struct timespec tp1, tp2;
     89 	struct timespec delta;
     90 	unsigned int limit = 0;
     91 	uint8_t *key = NULL, *salt = NULL;
     92 	uint8_t tmp_pwd[17]; /* just random data for testing */
     93 	int ret = ARGON2_OK;
     94 
     95 	key = emalloc(keylen);
     96 	salt = emalloc(saltlen);
     97 
     98 	arc4random_buf(tmp_pwd, sizeof(tmp_pwd));
     99 	arc4random_buf(salt, saltlen);
    100 
    101 	/* 1kb to argon2 per 100kb of user memory */
    102 	mem = usermem / 100000;
    103 
    104 	/* 256k: reasonable lower bound from the argon2 test suite */
    105 	if (mem < 256)
    106 		mem = 256;
    107 
    108 	fprintf(stderr, "calibrating argon2id parameters...");
    109 
    110 	/* Decrease 'mem' if it slows down computation too much */
    111 
    112 	do {
    113 		if (clock_gettime(CLOCK_MONOTONIC, &tp1) == -1)
    114 			goto error;
    115 		if ((ret = argon2_hash(ARGON2_MIN_TIME, mem, ncpus,
    116 		    tmp_pwd, sizeof(tmp_pwd),
    117 		    salt, saltlen,
    118 		    key, keylen,
    119 		    NULL, 0,
    120 		    Argon2_id, ARGON2_VERSION_NUMBER)) != ARGON2_OK) {
    121 			goto error_argon2;
    122 		}
    123 		fprintf(stderr, ".");
    124 		if (clock_gettime(CLOCK_MONOTONIC, &tp2) == -1)
    125 			goto error;
    126 		if (timespeccmp(&tp1, &tp2, >))
    127 			goto error_clock;
    128 		timespecsub(&tp2, &tp1, &delta);
    129 		if (delta.tv_sec >= 1)
    130 			mem /= 2;
    131 		if (mem < ARGON2_MIN_MEMORY) {
    132 			mem = ARGON2_MIN_MEMORY;
    133 			break;
    134 		}
    135 	} while (delta.tv_sec >= 1 && (limit++) < 3);
    136 
    137 	delta.tv_sec = 0;
    138 	delta.tv_nsec = 0;
    139 
    140 	/* Increase 'time' until we reach a second */
    141 
    142 	for (time = ARGON2_MIN_TIME; delta.tv_sec < 1 &&
    143 	    time < ARGON2_MAX_TIME; time <<= 1) {
    144 		if (clock_gettime(CLOCK_MONOTONIC, &tp1) == -1)
    145 			goto error;
    146 		if ((ret = argon2_hash(time, mem, ncpus,
    147 		    tmp_pwd, sizeof(tmp_pwd),
    148 		    salt, saltlen,
    149 		    key, keylen,
    150 		    NULL, 0,
    151 		    Argon2_id, ARGON2_VERSION_NUMBER)) != ARGON2_OK) {
    152 			goto error_argon2;
    153 		}
    154 		fprintf(stderr, ".");
    155 		if (clock_gettime(CLOCK_MONOTONIC, &tp2) == -1)
    156 			goto error;
    157 		if (timespeccmp(&tp1, &tp2, >))
    158 			goto error_clock;
    159 		timespecsub(&tp2, &tp1, &delta);
    160 	}
    161 
    162 	if (time > ARGON2_MIN_TIME)
    163 		time >>= 1;
    164 
    165 	fprintf(stderr, " done\n");
    166 
    167 	free(key);
    168 	free(salt);
    169 	*iterations = time;
    170 	*memory = mem;
    171 	*parallelism = ncpus;
    172 	return;
    173 
    174 error_argon2:
    175 	errx(EXIT_FAILURE,
    176 	    " failed to calculate Argon2 hash, error code %d", ret);
    177 error_clock:
    178 	errx(EXIT_FAILURE,
    179 	    " failed to calibrate hash parameters: broken monotonic clock?");
    180 error:
    181 	err(EXIT_FAILURE, " failed to calibrate hash parameters");
    182 }
    183