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