Files
cryptsetup/lib/luks2/luks2_digest_pbkdf2.c
Milan Broz c0dfd1178d Fix some coverity scan issues.
The read in kernel crypto backend is part of user crypto API
encryption call, we have to trust it here.

JSON fix is just one place where return code was not checked
for this particular function.
2023-11-28 18:52:25 +00:00

219 lines
6.4 KiB
C

/*
* LUKS - Linux Unified Key Setup v2, PBKDF2 digest handler (LUKS1 compatible)
*
* Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
* Copyright (C) 2015-2023 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "luks2_internal.h"
#define LUKS_DIGESTSIZE 20 // since SHA1
#define LUKS_SALTSIZE 32
#define LUKS_MKD_ITERATIONS_MS 125
static int PBKDF2_digest_verify(struct crypt_device *cd,
int digest,
const char *volume_key,
size_t volume_key_len)
{
char checkHashBuf[64];
json_object *jobj_digest, *jobj1;
const char *hashSpec;
char *mkDigest = NULL, *mkDigestSalt = NULL;
unsigned int mkDigestIterations;
size_t len;
int r = -EINVAL;
/* This can be done only for internally linked digests */
jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
if (!jobj_digest)
return -EINVAL;
if (!json_object_object_get_ex(jobj_digest, "hash", &jobj1))
return -EINVAL;
hashSpec = json_object_get_string(jobj1);
if (!json_object_object_get_ex(jobj_digest, "iterations", &jobj1))
return -EINVAL;
mkDigestIterations = json_object_get_int64(jobj1);
if (!json_object_object_get_ex(jobj_digest, "salt", &jobj1))
return -EINVAL;
r = crypt_base64_decode(&mkDigestSalt, &len, json_object_get_string(jobj1),
json_object_get_string_len(jobj1));
if (r < 0)
goto out;
if (len != LUKS_SALTSIZE)
goto out;
if (!json_object_object_get_ex(jobj_digest, "digest", &jobj1))
goto out;
r = crypt_base64_decode(&mkDigest, &len, json_object_get_string(jobj1),
json_object_get_string_len(jobj1));
if (r < 0)
goto out;
if (len < LUKS_DIGESTSIZE ||
len > sizeof(checkHashBuf) ||
(len != LUKS_DIGESTSIZE && len != (size_t)crypt_hash_size(hashSpec)))
goto out;
r = -EPERM;
if (crypt_pbkdf(CRYPT_KDF_PBKDF2, hashSpec, volume_key, volume_key_len,
mkDigestSalt, LUKS_SALTSIZE,
checkHashBuf, len,
mkDigestIterations, 0, 0) < 0) {
r = -EINVAL;
} else {
if (crypt_backend_memeq(checkHashBuf, mkDigest, len) == 0)
r = 0;
}
out:
free(mkDigest);
free(mkDigestSalt);
return r;
}
static int PBKDF2_digest_store(struct crypt_device *cd,
int digest,
const char *volume_key,
size_t volume_key_len)
{
json_object *jobj_digest, *jobj_digests;
char salt[LUKS_SALTSIZE], digest_raw[128];
int hmac_size, r;
char *base64_str;
struct luks2_hdr *hdr;
struct crypt_pbkdf_limits pbkdf_limits;
const struct crypt_pbkdf_type *pbkdf_cd;
struct crypt_pbkdf_type pbkdf = {
.type = CRYPT_KDF_PBKDF2,
.time_ms = LUKS_MKD_ITERATIONS_MS,
};
/* Inherit hash from PBKDF setting */
pbkdf_cd = crypt_get_pbkdf_type(cd);
if (pbkdf_cd)
pbkdf.hash = pbkdf_cd->hash;
if (!pbkdf.hash)
pbkdf.hash = DEFAULT_LUKS1_HASH;
log_dbg(cd, "Setting PBKDF2 type key digest %d.", digest);
r = crypt_random_get(cd, salt, LUKS_SALTSIZE, CRYPT_RND_SALT);
if (r < 0)
return r;
r = crypt_pbkdf_get_limits(CRYPT_KDF_PBKDF2, &pbkdf_limits);
if (r < 0)
return r;
if (crypt_get_pbkdf(cd)->flags & CRYPT_PBKDF_NO_BENCHMARK)
pbkdf.iterations = pbkdf_limits.min_iterations;
else {
r = crypt_benchmark_pbkdf_internal(cd, &pbkdf, volume_key_len);
if (r < 0)
return r;
}
hmac_size = crypt_hmac_size(pbkdf.hash);
if (hmac_size < 0 || hmac_size > (int)sizeof(digest_raw))
return -EINVAL;
r = crypt_pbkdf(CRYPT_KDF_PBKDF2, pbkdf.hash, volume_key, volume_key_len,
salt, LUKS_SALTSIZE, digest_raw, hmac_size,
pbkdf.iterations, 0, 0);
if (r < 0)
return r;
jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
jobj_digests = NULL;
if (!jobj_digest) {
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
jobj_digest = json_object_new_object();
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
}
if (!jobj_digest)
return -ENOMEM;
json_object_object_add(jobj_digest, "type", json_object_new_string("pbkdf2"));
json_object_object_add(jobj_digest, "keyslots", json_object_new_array());
json_object_object_add(jobj_digest, "segments", json_object_new_array());
json_object_object_add(jobj_digest, "hash", json_object_new_string(pbkdf.hash));
json_object_object_add(jobj_digest, "iterations", json_object_new_int(pbkdf.iterations));
r = crypt_base64_encode(&base64_str, NULL, salt, LUKS_SALTSIZE);
if (r < 0) {
json_object_put(jobj_digest);
return r;
}
json_object_object_add(jobj_digest, "salt", json_object_new_string(base64_str));
free(base64_str);
r = crypt_base64_encode(&base64_str, NULL, digest_raw, hmac_size);
if (r < 0) {
json_object_put(jobj_digest);
return r;
}
json_object_object_add(jobj_digest, "digest", json_object_new_string(base64_str));
free(base64_str);
if (jobj_digests) {
r = json_object_object_add_by_uint(jobj_digests, digest, jobj_digest);
if (r < 0) {
json_object_put(jobj_digest);
return r;
}
}
JSON_DBG(cd, jobj_digest, "Digest JSON:");
return 0;
}
static int PBKDF2_digest_dump(struct crypt_device *cd, int digest)
{
json_object *jobj_digest, *jobj1;
/* This can be done only for internally linked digests */
jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
if (!jobj_digest)
return -EINVAL;
json_object_object_get_ex(jobj_digest, "hash", &jobj1);
log_std(cd, "\tHash: %s\n", json_object_get_string(jobj1));
json_object_object_get_ex(jobj_digest, "iterations", &jobj1);
log_std(cd, "\tIterations: %" PRIu64 "\n", json_object_get_int64(jobj1));
json_object_object_get_ex(jobj_digest, "salt", &jobj1);
log_std(cd, "\tSalt: ");
hexprint_base64(cd, jobj1, " ", " ");
json_object_object_get_ex(jobj_digest, "digest", &jobj1);
log_std(cd, "\tDigest: ");
hexprint_base64(cd, jobj1, " ", " ");
return 0;
}
const digest_handler PBKDF2_digest = {
.name = "pbkdf2",
.verify = PBKDF2_digest_verify,
.store = PBKDF2_digest_store,
.dump = PBKDF2_digest_dump,
};