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 QCryptoHashAlgorithm 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 QCryptoHashAlgorithm 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
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 if (delta_ms == 0) { /* sanity check */
135 error_setg(errp, "Unable to get accurate CPU usage");
136 goto cleanup;
137 } else if (delta_ms > 500) {
138 break;
139 } else if (delta_ms < 100) {
140 iterations = iterations * 10;
141 } else {
142 iterations = (iterations * 1000 / delta_ms);
143 }
144 }
145
146 iterations = iterations * 1000 / delta_ms;
147
148 ret = iterations;
149
150 cleanup:
151 memset(out, 0, nout);
152 iters_data->iterations = ret;
153 return NULL;
154 }
155
qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash,const uint8_t * key,size_t nkey,const uint8_t * salt,size_t nsalt,size_t nout,Error ** errp)156 uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash,
157 const uint8_t *key, size_t nkey,
158 const uint8_t *salt, size_t nsalt,
159 size_t nout,
160 Error **errp)
161 {
162 CountItersData data = {
163 hash, key, nkey, salt, nsalt, nout, 0, errp
164 };
165 QemuThread thread;
166
167 qemu_thread_create(&thread, "pbkdf2", threaded_qcrypto_pbkdf2_count_iters,
168 &data, QEMU_THREAD_JOINABLE);
169 qemu_thread_join(&thread);
170
171 return data.iterations;
172 }
173