Coverage Report

Created: 2025-06-13 06:36

/src/cryptsetup/lib/luks2/luks2_digest_pbkdf2.c
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * LUKS - Linux Unified Key Setup v2, PBKDF2 digest handler (LUKS1 compatible)
4
 *
5
 * Copyright (C) 2015-2025 Red Hat, Inc. All rights reserved.
6
 * Copyright (C) 2015-2025 Milan Broz
7
 */
8
9
#include "luks2_internal.h"
10
11
0
#define LUKS_DIGESTSIZE 20 // since SHA1
12
0
#define LUKS_SALTSIZE 32
13
0
#define LUKS_MKD_ITERATIONS_MS 125
14
15
static int PBKDF2_digest_verify(struct crypt_device *cd,
16
  int digest,
17
  const char *volume_key,
18
  size_t volume_key_len)
19
0
{
20
0
  char checkHashBuf[64];
21
0
  json_object *jobj_digest, *jobj1;
22
0
  const char *hashSpec;
23
0
  char *mkDigest = NULL, *mkDigestSalt = NULL;
24
0
  unsigned int mkDigestIterations;
25
0
  size_t len;
26
0
  int r = -EINVAL;
27
28
  /* This can be done only for internally linked digests */
29
0
  jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
30
0
  if (!jobj_digest)
31
0
    return -EINVAL;
32
33
0
  if (!json_object_object_get_ex(jobj_digest, "hash", &jobj1))
34
0
    return -EINVAL;
35
0
  hashSpec = json_object_get_string(jobj1);
36
37
0
  if (!json_object_object_get_ex(jobj_digest, "iterations", &jobj1))
38
0
    return -EINVAL;
39
0
  mkDigestIterations = json_object_get_int64(jobj1);
40
41
0
  if (!json_object_object_get_ex(jobj_digest, "salt", &jobj1))
42
0
    return -EINVAL;
43
0
  r = crypt_base64_decode(&mkDigestSalt, &len, json_object_get_string(jobj1),
44
0
        json_object_get_string_len(jobj1));
45
0
  if (r < 0)
46
0
    goto out;
47
0
  if (len != LUKS_SALTSIZE)
48
0
    goto out;
49
50
0
  if (!json_object_object_get_ex(jobj_digest, "digest", &jobj1))
51
0
    goto out;
52
0
  r = crypt_base64_decode(&mkDigest, &len, json_object_get_string(jobj1),
53
0
        json_object_get_string_len(jobj1));
54
0
  if (r < 0)
55
0
    goto out;
56
0
  if (len < LUKS_DIGESTSIZE ||
57
0
      len > sizeof(checkHashBuf) ||
58
0
      (len != LUKS_DIGESTSIZE && len != (size_t)crypt_hash_size(hashSpec)))
59
0
    goto out;
60
61
0
  r = -EPERM;
62
0
  if (crypt_pbkdf(CRYPT_KDF_PBKDF2, hashSpec, volume_key, volume_key_len,
63
0
      mkDigestSalt, LUKS_SALTSIZE,
64
0
      checkHashBuf, len,
65
0
      mkDigestIterations, 0, 0) < 0) {
66
0
    r = -EINVAL;
67
0
  } else {
68
0
    if (crypt_backend_memeq(checkHashBuf, mkDigest, len) == 0)
69
0
      r = 0;
70
0
  }
71
0
out:
72
0
  free(mkDigest);
73
0
  free(mkDigestSalt);
74
0
  return r;
75
0
}
76
77
static int PBKDF2_digest_store(struct crypt_device *cd,
78
  int digest,
79
  const char *volume_key,
80
  size_t volume_key_len)
81
0
{
82
0
  json_object *jobj_digest, *jobj_digests;
83
0
  char salt[LUKS_SALTSIZE], digest_raw[128];
84
0
  int hmac_size, r;
85
0
  char *base64_str;
86
0
  struct luks2_hdr *hdr;
87
0
  struct crypt_pbkdf_limits pbkdf_limits;
88
0
  const struct crypt_pbkdf_type *pbkdf_cd;
89
0
  struct crypt_pbkdf_type pbkdf = {
90
0
    .type = CRYPT_KDF_PBKDF2,
91
0
    .time_ms = LUKS_MKD_ITERATIONS_MS,
92
0
  };
93
94
  /* Inherit hash from PBKDF setting */
95
0
  pbkdf_cd = crypt_get_pbkdf_type(cd);
96
0
  if (pbkdf_cd)
97
0
    pbkdf.hash = pbkdf_cd->hash;
98
0
  if (!pbkdf.hash)
99
0
    pbkdf.hash = DEFAULT_LUKS1_HASH;
100
101
0
  log_dbg(cd, "Setting PBKDF2 type key digest %d.", digest);
102
103
0
  r = crypt_random_get(cd, salt, LUKS_SALTSIZE, CRYPT_RND_SALT);
104
0
  if (r < 0)
105
0
    return r;
106
107
0
  r = crypt_pbkdf_get_limits(CRYPT_KDF_PBKDF2, &pbkdf_limits);
108
0
  if (r < 0)
109
0
    return r;
110
111
0
  if (crypt_get_pbkdf(cd)->flags & CRYPT_PBKDF_NO_BENCHMARK)
112
0
    pbkdf.iterations = pbkdf_limits.min_iterations;
113
0
  else {
114
0
    r = crypt_benchmark_pbkdf_internal(cd, &pbkdf, volume_key_len);
115
0
    if (r < 0)
116
0
      return r;
117
0
  }
118
119
0
  hmac_size = crypt_hmac_size(pbkdf.hash);
120
0
  if (hmac_size < 0 || hmac_size > (int)sizeof(digest_raw))
121
0
    return -EINVAL;
122
123
0
  r = crypt_pbkdf(CRYPT_KDF_PBKDF2, pbkdf.hash, volume_key, volume_key_len,
124
0
      salt, LUKS_SALTSIZE, digest_raw, hmac_size,
125
0
      pbkdf.iterations, 0, 0);
126
0
  if (r < 0)
127
0
    return r;
128
129
0
  jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
130
0
  jobj_digests = NULL;
131
0
  if (!jobj_digest) {
132
0
    hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
133
0
    jobj_digest = json_object_new_object();
134
0
    json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
135
0
  }
136
137
0
  if (!jobj_digest)
138
0
    return -ENOMEM;
139
140
0
  json_object_object_add(jobj_digest, "type", json_object_new_string("pbkdf2"));
141
0
  json_object_object_add(jobj_digest, "keyslots", json_object_new_array());
142
0
  json_object_object_add(jobj_digest, "segments", json_object_new_array());
143
0
  json_object_object_add(jobj_digest, "hash", json_object_new_string(pbkdf.hash));
144
0
  json_object_object_add(jobj_digest, "iterations", json_object_new_int(pbkdf.iterations));
145
146
0
  r = crypt_base64_encode(&base64_str, NULL, salt, LUKS_SALTSIZE);
147
0
  if (r < 0) {
148
0
    json_object_put(jobj_digest);
149
0
    return r;
150
0
  }
151
0
  json_object_object_add(jobj_digest, "salt", json_object_new_string(base64_str));
152
0
  free(base64_str);
153
154
0
  r = crypt_base64_encode(&base64_str, NULL, digest_raw, hmac_size);
155
0
  if (r < 0) {
156
0
    json_object_put(jobj_digest);
157
0
    return r;
158
0
  }
159
0
  json_object_object_add(jobj_digest, "digest", json_object_new_string(base64_str));
160
0
  free(base64_str);
161
162
0
  if (jobj_digests) {
163
0
    r = json_object_object_add_by_uint(jobj_digests, digest, jobj_digest);
164
0
    if (r < 0) {
165
0
      json_object_put(jobj_digest);
166
0
      return r;
167
0
    }
168
0
  }
169
170
0
  JSON_DBG(cd, jobj_digest, "Digest JSON:");
171
0
  return 0;
172
0
}
173
174
static int PBKDF2_digest_dump(struct crypt_device *cd, int digest)
175
0
{
176
0
  json_object *jobj_digest, *jobj1;
177
178
  /* This can be done only for internally linked digests */
179
0
  jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
180
0
  if (!jobj_digest)
181
0
    return -EINVAL;
182
183
0
  json_object_object_get_ex(jobj_digest, "hash", &jobj1);
184
0
  log_std(cd, "\tHash:       %s\n", json_object_get_string(jobj1));
185
186
0
  json_object_object_get_ex(jobj_digest, "iterations", &jobj1);
187
0
  log_std(cd, "\tIterations: %" PRIu64 "\n", json_object_get_int64(jobj1));
188
189
0
  json_object_object_get_ex(jobj_digest, "salt", &jobj1);
190
0
  log_std(cd, "\tSalt:       ");
191
0
  hexprint_base64(cd, jobj1, " ", "            ");
192
193
0
  json_object_object_get_ex(jobj_digest, "digest", &jobj1);
194
0
  log_std(cd, "\tDigest:     ");
195
0
  hexprint_base64(cd, jobj1, " ", "            ");
196
197
0
  return 0;
198
0
}
199
200
const digest_handler PBKDF2_digest = {
201
  .name   = "pbkdf2",
202
  .verify = PBKDF2_digest_verify,
203
  .store  = PBKDF2_digest_store,
204
  .dump   = PBKDF2_digest_dump,
205
};