Fix some problems found by Coverity scan.
[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         size_t alignment;
132         void *buf = NULL;
133
134         alignment = crypt_getpagesize();
135         if (alignment < 0)
136                 return -EINVAL;
137
138         if (posix_memalign(&buf, alignment, cp->buffer_size))
139                 return -ENOMEM;
140
141         ms_enc = 0;
142         repeat_enc = 1;
143         while (ms_enc < 1000) {
144                 ms = cipher_measure(cp, buf, cp->buffer_size, 1);
145                 if (ms < 0) {
146                         free(buf);
147                         return (int)ms;
148                 }
149                 ms_enc += ms;
150                 repeat_enc++;
151         }
152
153         ms_dec = 0;
154         repeat_dec = 1;
155         while (ms_dec < 1000) {
156                 ms = cipher_measure(cp, buf, cp->buffer_size, 0);
157                 if (ms < 0) {
158                         free(buf);
159                         return (int)ms;
160                 }
161                 ms_dec += ms;
162                 repeat_dec++;
163         }
164
165         free(buf);
166
167         *encryption_mbs = speed_mbs(cp->buffer_size * repeat_enc, ms_enc);
168         *decryption_mbs = speed_mbs(cp->buffer_size * repeat_dec, ms_dec);
169
170         return  0;
171 }
172
173 int crypt_benchmark(struct crypt_device *cd,
174         const char *cipher,
175         const char *cipher_mode,
176         size_t volume_key_size,
177         size_t iv_size,
178         size_t buffer_size,
179         double *encryption_mbs,
180         double *decryption_mbs)
181 {
182         struct cipher_perf cp = {
183                 .key_length = volume_key_size,
184                 .iv_length = iv_size,
185                 .buffer_size = buffer_size,
186         };
187         char *c;
188         int r;
189
190         if (!cipher || !cipher_mode || !volume_key_size)
191                 return -EINVAL;
192
193         r = init_crypto(cd);
194         if (r < 0)
195                 return r;
196
197         r = -ENOMEM;
198         if (iv_size) {
199                 cp.iv = malloc(iv_size);
200                 if (!cp.iv)
201                         goto out;
202                 crypt_random_get(cd, cp.iv, iv_size, CRYPT_RND_NORMAL);
203         }
204
205         cp.key = malloc(volume_key_size);
206         if (!cp.key)
207                 goto out;
208
209         crypt_random_get(cd, cp.key, volume_key_size, CRYPT_RND_NORMAL);
210         strncpy(cp.name, cipher, sizeof(cp.name)-1);
211         strncpy(cp.mode, cipher_mode, sizeof(cp.mode)-1);
212
213         /* Ignore IV generator */
214         if ((c  = strchr(cp.mode, '-')))
215                 *c = '\0';
216
217         r = cipher_perf(&cp, encryption_mbs, decryption_mbs);
218 out:
219         free(cp.key);
220         free(cp.iv);
221         return r;
222 }
223
224 int crypt_benchmark_kdf(struct crypt_device *cd,
225         const char *kdf,
226         const char *hash,
227         const char *password,
228         size_t password_size,
229         const char *salt,
230         size_t salt_size,
231         uint64_t *iterations_sec)
232 {
233         int r;
234
235         if (!iterations_sec)
236                 return -EINVAL;
237
238         r = init_crypto(cd);
239         if (r < 0)
240                 return r;
241
242         if (!strncmp(kdf, "pbkdf2", 6))
243                 r = crypt_pbkdf_check(kdf, hash, password, password_size,
244                                       salt, salt_size, iterations_sec);
245         else
246                 r = -EINVAL;
247
248         if (!r)
249                 log_dbg("KDF %s, hash %s: %" PRIu64 " iterations per second.",
250                         kdf, hash, *iterations_sec);
251         return r;
252 }