2119d97d0e0981ce401174d3c73c1d1a902c27f0
[platform/upstream/cryptsetup.git] / lib / crypto_backend / cipher_check.c
1 /*
2  * Cipher performance check
3  *
4  * Copyright (C) 2018-2020 Red Hat, Inc. All rights reserved.
5  * Copyright (C) 2018-2020 Milan Broz
6  *
7  * This file is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This file 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this file; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include <errno.h>
23 #include <time.h>
24 #include "crypto_backend_internal.h"
25
26 #ifndef CLOCK_MONOTONIC_RAW
27 #define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
28 #endif
29
30 /*
31  * This is not simulating storage, so using disk block causes extreme overhead.
32  * Let's use some fixed block size where results are more reliable...
33  */
34 #define CIPHER_BLOCK_BYTES 65536
35
36 /*
37  * If the measured value is lower, encrypted buffer is probably too small
38  * and calculated values are not reliable.
39  */
40 #define CIPHER_TIME_MIN_MS 0.001
41
42 /*
43  * The whole test depends on Linux kernel usermode crypto API for now.
44  * (The same implementations are used in dm-crypt though.)
45  */
46
47 static int time_ms(struct timespec *start, struct timespec *end, double *ms)
48 {
49         double start_ms, end_ms;
50
51         start_ms = start->tv_sec * 1000.0 + start->tv_nsec / (1000.0 * 1000);
52         end_ms   = end->tv_sec * 1000.0 + end->tv_nsec / (1000.0 * 1000);
53
54         *ms = end_ms - start_ms;
55         return 0;
56 }
57
58 static int cipher_perf_one(const char *name, const char *mode, char *buffer, size_t buffer_size,
59                           const char *key, size_t key_size, const char *iv, size_t iv_size, int enc)
60 {
61         struct crypt_cipher_kernel cipher;
62         size_t done = 0, block = CIPHER_BLOCK_BYTES;
63         int r;
64
65         if (buffer_size < block)
66                 block = buffer_size;
67
68         r = crypt_cipher_init_kernel(&cipher, name, mode, key, key_size);
69         if (r < 0)
70                 return r;
71
72         while (done < buffer_size) {
73                 if ((done + block) > buffer_size)
74                         block = buffer_size - done;
75
76                 if (enc)
77                         r = crypt_cipher_encrypt_kernel(&cipher, &buffer[done], &buffer[done],
78                                                  block, iv, iv_size);
79                 else
80                         r = crypt_cipher_decrypt_kernel(&cipher, &buffer[done], &buffer[done],
81                                                  block, iv, iv_size);
82                 if (r < 0)
83                         break;
84
85                 done += block;
86         }
87
88         crypt_cipher_destroy_kernel(&cipher);
89
90         return r;
91 }
92 static int cipher_measure(const char *name, const char *mode, char *buffer, size_t buffer_size,
93                           const char *key, size_t key_size, const char *iv, size_t iv_size,
94                           int encrypt, double *ms)
95 {
96         struct timespec start, end;
97         int r;
98
99         /*
100          * Using getrusage would be better here but the precision
101          * is not adequate, so better stick with CLOCK_MONOTONIC
102          */
103         if (clock_gettime(CLOCK_MONOTONIC_RAW, &start) < 0)
104                 return -EINVAL;
105
106         r = cipher_perf_one(name, mode, buffer, buffer_size, key, key_size, iv, iv_size, encrypt);
107         if (r < 0)
108                 return r;
109
110         if (clock_gettime(CLOCK_MONOTONIC_RAW, &end) < 0)
111                 return -EINVAL;
112
113         r = time_ms(&start, &end, ms);
114         if (r < 0)
115                 return r;
116
117         if (*ms < CIPHER_TIME_MIN_MS)
118                 return -ERANGE;
119
120         return 0;
121 }
122
123 static double speed_mbs(unsigned long bytes, double ms)
124 {
125         double speed = bytes, s = ms / 1000.;
126
127         return speed / (1024 * 1024) / s;
128 }
129
130 int crypt_cipher_perf_kernel(const char *name, const char *mode, char *buffer, size_t buffer_size,
131                              const char *key, size_t key_size, const char *iv, size_t iv_size,
132                              double *encryption_mbs, double *decryption_mbs)
133 {
134         double ms_enc, ms_dec, ms;
135         int r, repeat_enc, repeat_dec;
136
137         ms_enc = 0.0;
138         repeat_enc = 1;
139         while (ms_enc < 1000.0) {
140                 r = cipher_measure(name, mode, buffer, buffer_size, key, key_size, iv, iv_size, 1, &ms);
141                 if (r < 0)
142                         return r;
143                 ms_enc += ms;
144                 repeat_enc++;
145         }
146
147         ms_dec = 0.0;
148         repeat_dec = 1;
149         while (ms_dec < 1000.0) {
150                 r = cipher_measure(name, mode, buffer, buffer_size, key, key_size, iv, iv_size, 0, &ms);
151                 if (r < 0)
152                         return r;
153                 ms_dec += ms;
154                 repeat_dec++;
155         }
156
157         *encryption_mbs = speed_mbs(buffer_size * repeat_enc, ms_enc);
158         *decryption_mbs = speed_mbs(buffer_size * repeat_dec, ms_dec);
159
160         return  0;
161 }