4ee0c03331bd52191d2b92a7a81e065ffd6db7be
[platform/upstream/cryptsetup.git] / lib / utils_benchmark.c
1 /*
2  * libcryptsetup - cryptsetup library, cipher bechmark
3  *
4  * Copyright (C) 2012, Red Hat, Inc. All rights reserved.
5  * Copyright (C) 2012, Milan Broz
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * version 2 as published by the Free Software Foundation.
10  *
11  * This program 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <sys/time.h>
24 #include <sys/resource.h>
25
26 #include "internal.h"
27
28 /*
29  * This is not simulating storage, so using disk block causes extreme overhead.
30  * Let's use some fixed block size where results are more reliable...
31  */
32 #define CIPHER_BLOCK_BYTES 65536
33
34 /*
35  * The whole test depends on Linux kernel usermode crypto API for now.
36  * (The same implementations are used in dm-crypt though.)
37  */
38
39 struct cipher_perf {
40         char name[32];
41         char mode[32];
42         char *key;
43         size_t key_length;
44         char *iv;
45         size_t iv_length;
46         size_t buffer_size;
47 };
48
49 static long time_ms(struct rusage *start, struct rusage *end)
50 {
51         long ms = 0;
52
53         /* For kernel backend, we need to measure only tim in kernel.
54         ms = (end->ru_utime.tv_sec - start->ru_utime.tv_sec) * 1000;
55         ms += (end->ru_utime.tv_usec - start->ru_utime.tv_usec) / 1000;
56         */
57
58         ms += (end->ru_stime.tv_sec - start->ru_stime.tv_sec) * 1000;
59         ms += (end->ru_stime.tv_usec - start->ru_stime.tv_usec) / 1000;
60
61         return ms;
62 }
63
64 static int cipher_perf_one(struct cipher_perf *cp, char *buf,
65                            size_t buf_size, int enc)
66 {
67         struct crypt_cipher *cipher = NULL;
68         size_t done = 0, block = CIPHER_BLOCK_BYTES;
69         int r;
70
71         if (buf_size < block)
72                 block = buf_size;
73
74         r = crypt_cipher_init(&cipher, cp->name, cp->mode, cp->key, cp->key_length);
75         if (r < 0) {
76                 log_dbg("Cannot initialise cipher %s, mode %s.", cp->name, cp->mode);
77                 return r;
78         }
79
80         while (done < buf_size) {
81                 if ((done + block) > buf_size)
82                         block = buf_size - done;
83
84                 if (enc)
85                         r = crypt_cipher_encrypt(cipher, &buf[done], &buf[done],
86                                                  block, cp->iv, cp->iv_length);
87                 else
88                         r = crypt_cipher_decrypt(cipher, &buf[done], &buf[done],
89                                                  block, cp->iv, cp->iv_length);
90                 if (r < 0)
91                         break;
92
93                 done += block;
94         }
95
96         crypt_cipher_destroy(cipher);
97
98         return r;
99 }
100 static long cipher_measure(struct cipher_perf *cp, char *buf,
101                            size_t buf_size, int encrypt)
102 {
103         struct rusage rstart, rend;
104         int r;
105
106         if (getrusage(RUSAGE_SELF, &rstart) < 0)
107                 return -EINVAL;
108
109         r = cipher_perf_one(cp, buf, buf_size, encrypt);
110         if (r < 0)
111                 return r;
112
113         if (getrusage(RUSAGE_SELF, &rend) < 0)
114                 return -EINVAL;
115
116         return time_ms(&rstart, &rend);
117 }
118
119 static double speed_mbs(unsigned long bytes, unsigned long ms)
120 {
121         double speed = bytes, s = ms / 1000.;
122
123         return speed / (1024 * 1024) / s;
124 }
125
126 static int cipher_perf(struct cipher_perf *cp,
127         double *encryption_mbs, double *decryption_mbs)
128 {
129         long ms_enc, ms_dec, ms;
130         int repeat_enc, repeat_dec;
131         void *buf = NULL;
132
133         if (posix_memalign(&buf, crypt_getpagesize(), cp->buffer_size))
134                 return -ENOMEM;
135
136         ms_enc = 0;
137         repeat_enc = 1;
138         while (ms_enc < 1000) {
139                 ms = cipher_measure(cp, buf, cp->buffer_size, 1);
140                 if (ms < 0) {
141                         free(buf);
142                         return (int)ms;
143                 }
144                 ms_enc += ms;
145                 repeat_enc++;
146         }
147
148         ms_dec = 0;
149         repeat_dec = 1;
150         while (ms_dec < 1000) {
151                 ms = cipher_measure(cp, buf, cp->buffer_size, 0);
152                 if (ms < 0) {
153                         free(buf);
154                         return (int)ms;
155                 }
156                 ms_dec += ms;
157                 repeat_dec++;
158         }
159
160         free(buf);
161
162         *encryption_mbs = speed_mbs(cp->buffer_size * repeat_enc, ms_enc);
163         *decryption_mbs = speed_mbs(cp->buffer_size * repeat_dec, ms_dec);
164
165         return  0;
166 }
167
168 int crypt_benchmark(struct crypt_device *cd,
169         const char *cipher,
170         const char *cipher_mode,
171         size_t volume_key_size,
172         size_t iv_size,
173         size_t buffer_size,
174         double *encryption_mbs,
175         double *decryption_mbs)
176 {
177         struct cipher_perf cp = {
178                 .key_length = volume_key_size,
179                 .iv_length = iv_size,
180                 .buffer_size = buffer_size,
181         };
182         char *c;
183         int r;
184
185         if (!cipher || !cipher_mode || !volume_key_size)
186                 return -EINVAL;
187
188         r = init_crypto(cd);
189         if (r < 0)
190                 return r;
191
192         r = -ENOMEM;
193         if (iv_size) {
194                 cp.iv = malloc(iv_size);
195                 if (!cp.iv)
196                         goto out;
197                 crypt_random_get(cd, cp.iv, iv_size, CRYPT_RND_NORMAL);
198         }
199
200         cp.key = malloc(volume_key_size);
201         if (!cp.key)
202                 goto out;
203
204         crypt_random_get(cd, cp.key, volume_key_size, CRYPT_RND_NORMAL);
205         strncpy(cp.name, cipher, sizeof(cp.name)-1);
206         strncpy(cp.mode, cipher_mode, sizeof(cp.mode)-1);
207
208         /* Ignore IV generator */
209         if ((c  = strchr(cp.mode, '-')))
210                 *c = '\0';
211
212         r = cipher_perf(&cp, encryption_mbs, decryption_mbs);
213 out:
214         free(cp.key);
215         free(cp.iv);
216         return r;
217 }
218
219 int crypt_benchmark_kdf(struct crypt_device *cd,
220         const char *kdf,
221         const char *hash,
222         const char *password,
223         size_t password_size,
224         const char *salt,
225         size_t salt_size,
226         uint64_t *iterations_sec)
227 {
228         int r;
229
230         if (!iterations_sec)
231                 return -EINVAL;
232
233         r = init_crypto(cd);
234         if (r < 0)
235                 return r;
236
237         if (!strncmp(kdf, "pbkdf2", 6))
238                 r = crypt_pbkdf_check(kdf, hash, password, password_size,
239                                       salt, salt_size, iterations_sec);
240         else
241                 r = -EINVAL;
242
243         if (!r)
244                 log_dbg("KDF %s, hash %s: %" PRIu64 " iterations per second.",
245                         kdf, hash, *iterations_sec);
246         return r;
247 }