Coverage Report

Created: 2025-12-10 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cryptsetup/lib/crypto_backend/cipher_check.c
Line
Count
Source
1
// SPDX-License-Identifier: LGPL-2.1-or-later
2
/*
3
 * Cipher performance check
4
 *
5
 * Copyright (C) 2018-2025 Red Hat, Inc. All rights reserved.
6
 * Copyright (C) 2018-2025 Milan Broz
7
 */
8
9
#include <errno.h>
10
#include <time.h>
11
#include "crypto_backend_internal.h"
12
13
#ifndef CLOCK_MONOTONIC_RAW
14
#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
15
#endif
16
17
/*
18
 * This is not simulating storage, so using disk block causes extreme overhead.
19
 * Let's use some fixed block size where results are more reliable...
20
 */
21
0
#define CIPHER_BLOCK_BYTES 65536
22
23
/*
24
 * If the measured value is lower, encrypted buffer is probably too small
25
 * and calculated values are not reliable.
26
 */
27
0
#define CIPHER_TIME_MIN_MS 0.001
28
29
/*
30
 * The whole test depends on Linux kernel usermode crypto API for now.
31
 * (The same implementations are used in dm-crypt though.)
32
 */
33
34
static int time_ms(struct timespec *start, struct timespec *end, double *ms)
35
0
{
36
0
  double start_ms, end_ms;
37
38
0
  start_ms = start->tv_sec * 1000.0 + start->tv_nsec / (1000.0 * 1000);
39
0
  end_ms   = end->tv_sec * 1000.0 + end->tv_nsec / (1000.0 * 1000);
40
41
0
  *ms = end_ms - start_ms;
42
0
  return 0;
43
0
}
44
45
static int cipher_perf_one(struct crypt_cipher_kernel *cipher, char *buffer, size_t buffer_size,
46
         const char *iv, size_t iv_size, int enc)
47
0
{
48
0
  size_t done = 0, block = CIPHER_BLOCK_BYTES;
49
0
  int r;
50
51
0
  if (buffer_size < block)
52
0
    block = buffer_size;
53
54
0
  while (done < buffer_size) {
55
0
    if ((done + block) > buffer_size)
56
0
      block = buffer_size - done;
57
58
0
    if (enc)
59
0
      r = crypt_cipher_encrypt_kernel(cipher, &buffer[done], &buffer[done],
60
0
             block, iv, iv_size);
61
0
    else
62
0
      r = crypt_cipher_decrypt_kernel(cipher, &buffer[done], &buffer[done],
63
0
             block, iv, iv_size);
64
0
    if (r < 0)
65
0
      return r;
66
67
0
    done += block;
68
0
  }
69
70
0
  return 0;
71
0
}
72
73
static int cipher_measure(struct crypt_cipher_kernel *cipher, char *buffer, size_t buffer_size,
74
        const char *iv, size_t iv_size, int encrypt, double *ms)
75
0
{
76
0
  struct timespec start, end;
77
0
  int r;
78
79
  /*
80
   * Using getrusage would be better here but the precision
81
   * is not adequate, so better stick with CLOCK_MONOTONIC
82
   */
83
0
  if (clock_gettime(CLOCK_MONOTONIC_RAW, &start) < 0)
84
0
    return -EINVAL;
85
86
0
  r = cipher_perf_one(cipher, buffer, buffer_size, iv, iv_size, encrypt);
87
0
  if (r < 0)
88
0
    return r;
89
90
0
  if (clock_gettime(CLOCK_MONOTONIC_RAW, &end) < 0)
91
0
    return -EINVAL;
92
93
0
  r = time_ms(&start, &end, ms);
94
0
  if (r < 0)
95
0
    return r;
96
97
0
  if (*ms < CIPHER_TIME_MIN_MS)
98
0
    return -ERANGE;
99
100
0
  return 0;
101
0
}
102
103
static double speed_mbs(unsigned long bytes, double ms)
104
0
{
105
0
  double speed = bytes, s = ms / 1000.;
106
107
0
  return speed / (1024 * 1024) / s;
108
0
}
109
110
int crypt_cipher_perf_kernel(const char *name, const char *mode, char *buffer, size_t buffer_size,
111
           const char *key, size_t key_size, const char *iv, size_t iv_size,
112
           double *encryption_mbs, double *decryption_mbs)
113
0
{
114
0
  struct crypt_cipher_kernel cipher;
115
0
  double ms_enc, ms_dec, ms;
116
0
  int r, repeat_enc, repeat_dec;
117
118
0
  r = crypt_cipher_init_kernel(&cipher, name, mode, key, key_size);
119
0
  if (r < 0)
120
0
    return r;
121
122
0
  ms_enc = 0.0;
123
0
  repeat_enc = 1;
124
0
  while (ms_enc < 1000.0) {
125
0
    r = cipher_measure(&cipher, buffer, buffer_size, iv, iv_size, 1, &ms);
126
0
    if (r < 0)
127
0
      goto out;
128
0
    ms_enc += ms;
129
0
    repeat_enc++;
130
0
  }
131
132
0
  ms_dec = 0.0;
133
0
  repeat_dec = 1;
134
0
  while (ms_dec < 1000.0) {
135
0
    r = cipher_measure(&cipher, buffer, buffer_size, iv, iv_size, 0, &ms);
136
0
    if (r < 0)
137
0
      goto out;
138
0
    ms_dec += ms;
139
0
    repeat_dec++;
140
0
  }
141
142
0
  *encryption_mbs = speed_mbs(buffer_size * repeat_enc, ms_enc);
143
0
  *decryption_mbs = speed_mbs(buffer_size * repeat_dec, ms_dec);
144
145
0
  r = 0;
146
0
out:
147
0
  crypt_cipher_destroy_kernel(&cipher);
148
0
  return r;
149
0
}