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