1 /*
2 * QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
3 *
4 * Copyright (c) 2015-2016 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #include "qemu/osdep.h"
22 #include "qemu/thread.h"
23 #include "qapi/error.h"
24 #include "crypto/pbkdf.h"
25 #ifndef _WIN32
26 #include <sys/resource.h>
27 #endif
28 #ifdef CONFIG_DARWIN
29 #include <mach/mach_init.h>
30 #include <mach/thread_act.h>
31 #include <mach/mach_port.h>
32 #endif
33
34
qcrypto_pbkdf2_get_thread_cpu(unsigned long long * val_ms,Error ** errp)35 static int qcrypto_pbkdf2_get_thread_cpu(unsigned long long *val_ms,
36 Error **errp)
37 {
38 #ifdef _WIN32
39 FILETIME creation_time, exit_time, kernel_time, user_time;
40 ULARGE_INTEGER thread_time;
41
42 if (!GetThreadTimes(GetCurrentThread(), &creation_time, &exit_time,
43 &kernel_time, &user_time)) {
44 error_setg(errp, "Unable to get thread CPU usage");
45 return -1;
46 }
47
48 thread_time.LowPart = user_time.dwLowDateTime;
49 thread_time.HighPart = user_time.dwHighDateTime;
50
51 /* QuadPart is units of 100ns and we want ms as unit */
52 *val_ms = thread_time.QuadPart / 10000ll;
53 return 0;
54 #elif defined(CONFIG_DARWIN)
55 mach_port_t thread;
56 kern_return_t kr;
57 mach_msg_type_number_t count;
58 thread_basic_info_data_t info;
59
60 thread = mach_thread_self();
61 count = THREAD_BASIC_INFO_COUNT;
62 kr = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&info, &count);
63 mach_port_deallocate(mach_task_self(), thread);
64 if (kr != KERN_SUCCESS || (info.flags & TH_FLAGS_IDLE) != 0) {
65 error_setg_errno(errp, errno, "Unable to get thread CPU usage");
66 return -1;
67 }
68
69 *val_ms = ((info.user_time.seconds * 1000ll) +
70 (info.user_time.microseconds / 1000));
71 return 0;
72 #elif defined(RUSAGE_THREAD)
73 struct rusage ru;
74 if (getrusage(RUSAGE_THREAD, &ru) < 0) {
75 error_setg_errno(errp, errno, "Unable to get thread CPU usage");
76 return -1;
77 }
78
79 *val_ms = ((ru.ru_utime.tv_sec * 1000ll) +
80 (ru.ru_utime.tv_usec / 1000));
81 return 0;
82 #else
83 *val_ms = 0;
84 error_setg(errp, "Unable to calculate thread CPU usage on this platform");
85 return -1;
86 #endif
87 }
88
89 typedef struct CountItersData {
90 QCryptoHashAlgo hash;
91 const uint8_t *key;
92 size_t nkey;
93 const uint8_t *salt;
94 size_t nsalt;
95 size_t nout;
96 uint64_t iterations;
97 Error **errp;
98 } CountItersData;
99
threaded_qcrypto_pbkdf2_count_iters(void * data)100 static void *threaded_qcrypto_pbkdf2_count_iters(void *data)
101 {
102 CountItersData *iters_data = (CountItersData *) data;
103 QCryptoHashAlgo hash = iters_data->hash;
104 const uint8_t *key = iters_data->key;
105 size_t nkey = iters_data->nkey;
106 const uint8_t *salt = iters_data->salt;
107 size_t nsalt = iters_data->nsalt;
108 size_t nout = iters_data->nout;
109 Error **errp = iters_data->errp;
110 size_t scaled = 0;
111 uint64_t ret = -1;
112 g_autofree uint8_t *out = g_new(uint8_t, nout);
113 uint64_t iterations = (1 << 15);
114 unsigned long long delta_ms, start_ms, end_ms;
115
116 while (1) {
117 if (qcrypto_pbkdf2_get_thread_cpu(&start_ms, errp) < 0) {
118 goto cleanup;
119 }
120 if (qcrypto_pbkdf2(hash,
121 key, nkey,
122 salt, nsalt,
123 iterations,
124 out, nout,
125 errp) < 0) {
126 goto cleanup;
127 }
128 if (qcrypto_pbkdf2_get_thread_cpu(&end_ms, errp) < 0) {
129 goto cleanup;
130 }
131
132 delta_ms = end_ms - start_ms;
133
134 /*
135 * For very small 'iterations' values, CPU (or crypto
136 * accelerator) might be fast enough that the scheduler
137 * hasn't incremented getrusage() data, or incremented
138 * it by a very small amount, resulting in delta_ms == 0.
139 * Once we've scaled 'iterations' x10, 5 times, we really
140 * should be seeing delta_ms != 0, so sanity check at
141 * that point.
142 */
143 if (scaled > 5 &&
144 delta_ms == 0) { /* sanity check */
145 error_setg(errp, "Unable to get accurate CPU usage");
146 goto cleanup;
147 } else if (delta_ms > 500) {
148 break;
149 } else if (delta_ms < 100) {
150 iterations = iterations * 10;
151 } else {
152 iterations = (iterations * 1000 / delta_ms);
153 }
154 scaled++;
155 }
156
157 iterations = iterations * 1000 / delta_ms;
158
159 ret = iterations;
160
161 cleanup:
162 memset(out, 0, nout);
163 iters_data->iterations = ret;
164 return NULL;
165 }
166
qcrypto_pbkdf2_count_iters(QCryptoHashAlgo hash,const uint8_t * key,size_t nkey,const uint8_t * salt,size_t nsalt,size_t nout,Error ** errp)167 uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgo hash,
168 const uint8_t *key, size_t nkey,
169 const uint8_t *salt, size_t nsalt,
170 size_t nout,
171 Error **errp)
172 {
173 CountItersData data = {
174 hash, key, nkey, salt, nsalt, nout, 0, errp
175 };
176 QemuThread thread;
177
178 qemu_thread_create(&thread, "pbkdf2", threaded_qcrypto_pbkdf2_count_iters,
179 &data, QEMU_THREAD_JOINABLE);
180 qemu_thread_join(&thread);
181
182 return data.iterations;
183 }
184