| /* |
| * utils_pbkdf - PBKDF ssettings for libcryptsetup |
| * |
| * Copyright (C) 2009-2017, Red Hat, Inc. All rights reserved. |
| * Copyright (C) 2009-2017, 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 <stdlib.h> |
| #include <errno.h> |
| |
| #include "internal.h" |
| |
| const struct crypt_pbkdf_type default_luks2 = { |
| .type = DEFAULT_LUKS2_PBKDF, |
| .hash = DEFAULT_LUKS1_HASH, |
| .time_ms = DEFAULT_LUKS2_ITER_TIME, |
| .max_memory_kb = DEFAULT_LUKS2_MEMORY_KB, |
| .parallel_threads = DEFAULT_LUKS2_PARALLEL_THREADS |
| }; |
| |
| const struct crypt_pbkdf_type default_luks1 = { |
| .type = CRYPT_KDF_PBKDF2, |
| .hash = DEFAULT_LUKS1_HASH, |
| .time_ms = DEFAULT_LUKS1_ITER_TIME |
| }; |
| |
| static uint32_t adjusted_pbkdf_memory(void) |
| { |
| uint64_t memory_kb = crypt_getphysmemory_kb(); |
| |
| /* Ignore bogus value */ |
| if (memory_kb < (128 * 1024)) |
| return DEFAULT_LUKS2_MEMORY_KB; |
| |
| /* |
| * Never use more than half of physical memory. |
| * OOM killer is too clever... |
| */ |
| memory_kb /= 2; |
| |
| if (memory_kb < DEFAULT_LUKS2_MEMORY_KB) |
| return (uint32_t)memory_kb; |
| |
| return DEFAULT_LUKS2_MEMORY_KB; |
| } |
| |
| /* |
| * PBKDF configuration interface |
| */ |
| int verify_pbkdf_params(struct crypt_device *cd, |
| const struct crypt_pbkdf_type *pbkdf) |
| { |
| const char *pbkdf_type; |
| int r = 0; |
| |
| if (!pbkdf->type || !pbkdf->hash || !pbkdf->time_ms) |
| return -EINVAL; |
| |
| /* TODO: initialise crypto and check the hash and pbkdf are both available */ |
| r = crypt_parse_pbkdf(pbkdf->type, &pbkdf_type); |
| if (r < 0) { |
| log_err(cd, _("Unknown PBKDF type %s.\n"), pbkdf->type); |
| return r; |
| } |
| |
| if (crypt_get_type(cd) && |
| !strcmp(crypt_get_type(cd), CRYPT_LUKS1) && |
| strcmp(pbkdf_type, CRYPT_KDF_PBKDF2)) { |
| log_err(cd, _("Requested PBKDF type is not supported for LUKS1.\n")); |
| return -EINVAL; |
| } |
| |
| if (!strcmp(pbkdf_type, CRYPT_KDF_PBKDF2)) { |
| if (pbkdf->max_memory_kb || pbkdf->parallel_threads) { |
| log_err(cd, _("PBKDF max memory or parallel threads must not be set with pbkdf2.\n")); |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| if (pbkdf->max_memory_kb > MAX_PBKDF_MEMORY) { |
| log_err(cd, _("Requested maximum PBKDF memory cost is too high (maximum is %d kilobytes).\n"), |
| MAX_PBKDF_MEMORY); |
| r = -EINVAL; |
| } |
| if (!pbkdf->max_memory_kb) { |
| log_err(cd, _("Requested maximum PBKDF memory can not be zero.\n")); |
| r = -EINVAL; |
| } |
| if (!pbkdf->parallel_threads) { |
| log_err(cd, _("Requested PBKDF parallel threads can not be zero.\n")); |
| r = -EINVAL; |
| } |
| if (!pbkdf->time_ms) { |
| log_err(cd, _("Requested PBKDF target time can not be zero.\n")); |
| r = -EINVAL; |
| } |
| |
| return r; |
| } |
| |
| int init_pbkdf_type(struct crypt_device *cd, |
| const struct crypt_pbkdf_type *pbkdf, |
| const char *dev_type) |
| { |
| struct crypt_pbkdf_type *cd_pbkdf = crypt_get_pbkdf(cd); |
| const char *hash, *type; |
| unsigned cpus; |
| uint32_t old_flags, memory_kb; |
| int r; |
| |
| if (!pbkdf && dev_type && !strcmp(dev_type, CRYPT_LUKS2)) |
| pbkdf = &default_luks2; |
| else if (!pbkdf) |
| pbkdf = &default_luks1; |
| |
| r = verify_pbkdf_params(cd, pbkdf); |
| if (r) |
| return r; |
| |
| /* |
| * Crypto backend may be not initialized here, |
| * cannot check if algorithms are really available. |
| * It will fail later anyway :-) |
| */ |
| type = strdup(pbkdf->type); |
| hash = strdup(pbkdf->hash); |
| |
| if (!type || !hash) { |
| free(CONST_CAST(void*)type); |
| free(CONST_CAST(void*)hash); |
| return -ENOMEM; |
| } |
| |
| free(CONST_CAST(void*)cd_pbkdf->type); |
| free(CONST_CAST(void*)cd_pbkdf->hash); |
| cd_pbkdf->type = type; |
| cd_pbkdf->hash = hash; |
| |
| old_flags = cd_pbkdf->flags; |
| cd_pbkdf->flags = pbkdf->flags; |
| |
| /* Reset iteration count so benchmark must run again. */ |
| if (cd_pbkdf->flags & CRYPT_PBKDF_NO_BENCHMARK) |
| cd_pbkdf->iterations = pbkdf->iterations; |
| else |
| cd_pbkdf->iterations = 0; |
| |
| if (old_flags & CRYPT_PBKDF_ITER_TIME_SET) |
| cd_pbkdf->flags |= CRYPT_PBKDF_ITER_TIME_SET; |
| else |
| cd_pbkdf->time_ms = pbkdf->time_ms; |
| |
| cd_pbkdf->max_memory_kb = pbkdf->max_memory_kb; |
| cd_pbkdf->parallel_threads = pbkdf->parallel_threads; |
| |
| if (cd_pbkdf->parallel_threads > MAX_PBKDF_THREADS) { |
| log_dbg("Maximum PBKDF threads is %d (requested %d).", |
| MAX_PBKDF_THREADS, cd_pbkdf->parallel_threads); |
| cd_pbkdf->parallel_threads = MAX_PBKDF_THREADS; |
| } |
| |
| if (cd_pbkdf->parallel_threads) { |
| cpus = crypt_cpusonline(); |
| if (cd_pbkdf->parallel_threads > cpus) { |
| log_dbg("Only %u active CPUs detected, " |
| "PBKDF threads decreased from %d to %d.", |
| cpus, cd_pbkdf->parallel_threads, cpus); |
| cd_pbkdf->parallel_threads = cpus; |
| } |
| } |
| |
| if (cd_pbkdf->max_memory_kb) { |
| memory_kb = adjusted_pbkdf_memory(); |
| if (cd_pbkdf->max_memory_kb > memory_kb) { |
| log_dbg("Not enough physical memory detected, " |
| "PBKDF max memory decreased from %dkB to %dkB.", |
| cd_pbkdf->max_memory_kb, memory_kb); |
| cd_pbkdf->max_memory_kb = memory_kb; |
| } |
| } |
| |
| log_dbg("PBKDF %s, hash %s, time_ms %u (iterations %u), max_memory_kb %u, parallel_threads %u.", |
| cd_pbkdf->type ?: "(none)", cd_pbkdf->hash ?: "(none)", cd_pbkdf->time_ms, |
| cd_pbkdf->iterations, cd_pbkdf->max_memory_kb, cd_pbkdf->parallel_threads); |
| |
| return 0; |
| } |
| |
| /* Libcryptsetup API */ |
| |
| int crypt_set_pbkdf_type(struct crypt_device *cd, const struct crypt_pbkdf_type *pbkdf) |
| { |
| if (!cd) |
| return -EINVAL; |
| |
| if (!pbkdf) |
| log_dbg("Resetting pbkdf type to default"); |
| |
| crypt_get_pbkdf(cd)->flags = 0; |
| |
| return init_pbkdf_type(cd, pbkdf, crypt_get_type(cd)); |
| } |
| |
| const struct crypt_pbkdf_type *crypt_get_pbkdf_type(struct crypt_device *cd) |
| { |
| if (!cd) |
| return NULL; |
| |
| return crypt_get_pbkdf(cd)->type ? crypt_get_pbkdf(cd) : NULL; |
| } |
| |
| void crypt_set_iteration_time(struct crypt_device *cd, uint64_t iteration_time_ms) |
| { |
| struct crypt_pbkdf_type *pbkdf; |
| uint32_t old_time_ms; |
| |
| if (!cd || iteration_time_ms > UINT32_MAX) |
| return; |
| |
| pbkdf = crypt_get_pbkdf(cd); |
| old_time_ms = pbkdf->time_ms; |
| pbkdf->time_ms = (uint32_t)iteration_time_ms; |
| |
| if (pbkdf->type && verify_pbkdf_params(cd, pbkdf)) { |
| pbkdf->time_ms = old_time_ms; |
| log_dbg("Invalid iteration time."); |
| return; |
| } |
| |
| pbkdf->flags |= CRYPT_PBKDF_ITER_TIME_SET; |
| |
| /* iterations must be benchmarked now */ |
| pbkdf->flags &= ~(CRYPT_PBKDF_NO_BENCHMARK); |
| pbkdf->iterations = 0; |
| |
| log_dbg("Iteration time set to %" PRIu64 " milliseconds.", iteration_time_ms); |
| } |