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