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