diff options
author | Daniel P. Berrange <berrange@redhat.com> | 2015-10-14 13:14:04 +0100 |
---|---|---|
committer | Daniel P. Berrange <berrange@redhat.com> | 2016-03-17 14:41:07 +0000 |
commit | 37788f253a4a9ad5f27dae68aee261c784e1fa17 (patch) | |
tree | 24b95c924f88bf059300342bbb2a9e172478f607 /crypto | |
parent | b917da4cbd13dae4cda3852d5bdf3725202103ab (diff) |
crypto: add support for PBKDF2 algorithm
The LUKS data format includes use of PBKDF2 (Password-Based
Key Derivation Function). The Nettle library can provide
an implementation of this, but we don't want code directly
depending on a specific crypto library backend. Introduce
a new include/crypto/pbkdf.h header which defines a QEMU
API for invoking PBKDK2. The initial implementations are
backed by nettle & gcrypt, which are commonly available
with distros shipping GNUTLS.
The test suite data is taken from the cryptsetup codebase
under the LGPLv2.1+ license. This merely aims to verify
that whatever backend we provide for this function in QEMU
will comply with the spec.
Reviewed-by: Fam Zheng <famz@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/Makefile.objs | 4 | ||||
-rw-r--r-- | crypto/pbkdf-gcrypt.c | 68 | ||||
-rw-r--r-- | crypto/pbkdf-nettle.c | 65 | ||||
-rw-r--r-- | crypto/pbkdf-stub.c | 42 | ||||
-rw-r--r-- | crypto/pbkdf.c | 109 |
5 files changed, 288 insertions, 0 deletions
diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs index 967418c67a..7122cc8e54 100644 --- a/crypto/Makefile.objs +++ b/crypto/Makefile.objs @@ -10,8 +10,12 @@ crypto-obj-y += tlssession.o crypto-obj-y += secret.o crypto-obj-$(CONFIG_GCRYPT) += random-gcrypt.o crypto-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS_RND)) += random-gnutls.o +crypto-obj-y += pbkdf.o +crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o +crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT_KDF)) += pbkdf-gcrypt.o # Let the userspace emulators avoid linking gnutls/etc crypto-aes-obj-y = aes.o stub-obj-y += random-stub.o +stub-obj-y += pbkdf-stub.o diff --git a/crypto/pbkdf-gcrypt.c b/crypto/pbkdf-gcrypt.c new file mode 100644 index 0000000000..885614dfcc --- /dev/null +++ b/crypto/pbkdf-gcrypt.c @@ -0,0 +1,68 @@ +/* + * QEMU Crypto PBKDF support (Password-Based Key Derivation Function) + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "crypto/pbkdf.h" +#include "gcrypt.h" + +bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash) +{ + switch (hash) { + case QCRYPTO_HASH_ALG_MD5: + case QCRYPTO_HASH_ALG_SHA1: + case QCRYPTO_HASH_ALG_SHA256: + return true; + default: + return false; + } +} + +int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + const uint8_t *salt, size_t nsalt, + unsigned int iterations, + uint8_t *out, size_t nout, + Error **errp) +{ + static const int hash_map[QCRYPTO_HASH_ALG__MAX] = { + [QCRYPTO_HASH_ALG_MD5] = GCRY_MD_MD5, + [QCRYPTO_HASH_ALG_SHA1] = GCRY_MD_SHA1, + [QCRYPTO_HASH_ALG_SHA256] = GCRY_MD_SHA256, + }; + int ret; + + if (hash >= G_N_ELEMENTS(hash_map) || + hash_map[hash] == GCRY_MD_NONE) { + error_setg(errp, "Unexpected hash algorithm %d", hash); + return -1; + } + + ret = gcry_kdf_derive(key, nkey, GCRY_KDF_PBKDF2, + hash_map[hash], + salt, nsalt, iterations, + nout, out); + if (ret != 0) { + error_setg(errp, "Cannot derive password: %s", + gcry_strerror(ret)); + return -1; + } + + return 0; +} diff --git a/crypto/pbkdf-nettle.c b/crypto/pbkdf-nettle.c new file mode 100644 index 0000000000..1aa7395ea5 --- /dev/null +++ b/crypto/pbkdf-nettle.c @@ -0,0 +1,65 @@ +/* + * QEMU Crypto PBKDF support (Password-Based Key Derivation Function) + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "crypto/pbkdf.h" +#include "nettle/pbkdf2.h" + + +bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash) +{ + switch (hash) { + case QCRYPTO_HASH_ALG_SHA1: + case QCRYPTO_HASH_ALG_SHA256: + return true; + default: + return false; + } +} + +int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + const uint8_t *salt, size_t nsalt, + unsigned int iterations, + uint8_t *out, size_t nout, + Error **errp) +{ + switch (hash) { + case QCRYPTO_HASH_ALG_SHA1: + pbkdf2_hmac_sha1(nkey, key, + iterations, + nsalt, salt, + nout, out); + break; + + case QCRYPTO_HASH_ALG_SHA256: + pbkdf2_hmac_sha256(nkey, key, + iterations, + nsalt, salt, + nout, out); + break; + + default: + error_setg_errno(errp, ENOSYS, + "PBKDF does not support hash algorithm %d", hash); + return -1; + } + return 0; +} diff --git a/crypto/pbkdf-stub.c b/crypto/pbkdf-stub.c new file mode 100644 index 0000000000..cfc30d3c2a --- /dev/null +++ b/crypto/pbkdf-stub.c @@ -0,0 +1,42 @@ +/* + * QEMU Crypto PBKDF support (Password-Based Key Derivation Function) + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "crypto/pbkdf.h" + +bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash G_GNUC_UNUSED) +{ + return false; +} + +int qcrypto_pbkdf2(QCryptoHashAlgorithm hash G_GNUC_UNUSED, + const uint8_t *key G_GNUC_UNUSED, + size_t nkey G_GNUC_UNUSED, + const uint8_t *salt G_GNUC_UNUSED, + size_t nsalt G_GNUC_UNUSED, + unsigned int iterations G_GNUC_UNUSED, + uint8_t *out G_GNUC_UNUSED, + size_t nout G_GNUC_UNUSED, + Error **errp) +{ + error_setg_errno(errp, ENOSYS, + "No crypto library supporting PBKDF in this build"); + return -1; +} diff --git a/crypto/pbkdf.c b/crypto/pbkdf.c new file mode 100644 index 0000000000..90721d85d6 --- /dev/null +++ b/crypto/pbkdf.c @@ -0,0 +1,109 @@ +/* + * QEMU Crypto PBKDF support (Password-Based Key Derivation Function) + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "crypto/pbkdf.h" +#ifndef _WIN32 +#include <sys/resource.h> +#endif + + +static int qcrypto_pbkdf2_get_thread_cpu(unsigned long long *val_ms, + Error **errp) +{ +#ifdef _WIN32 + FILETIME creation_time, exit_time, kernel_time, user_time; + ULARGE_INTEGER thread_time; + + if (!GetThreadTimes(GetCurrentThread(), &creation_time, &exit_time, + &kernel_time, &user_time)) { + error_setg(errp, "Unable to get thread CPU usage"); + return -1; + } + + thread_time.LowPart = user_time.dwLowDateTime; + thread_time.HighPart = user_time.dwHighDateTime; + + /* QuadPart is units of 100ns and we want ms as unit */ + *val_ms = thread_time.QuadPart / 10000ll; + return 0; +#elif defined(RUSAGE_THREAD) + struct rusage ru; + if (getrusage(RUSAGE_THREAD, &ru) < 0) { + error_setg_errno(errp, errno, "Unable to get thread CPU usage"); + return -1; + } + + *val_ms = ((ru.ru_utime.tv_sec * 1000ll) + + (ru.ru_utime.tv_usec / 1000)); + return 0; +#else + *val_ms = 0; + error_setg(errp, "Unable to calculate thread CPU usage on this platform"); + return -1; +#endif +} + +int qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, + const uint8_t *key, size_t nkey, + const uint8_t *salt, size_t nsalt, + Error **errp) +{ + uint8_t out[32]; + long long int iterations = (1 << 15); + unsigned long long delta_ms, start_ms, end_ms; + + while (1) { + if (qcrypto_pbkdf2_get_thread_cpu(&start_ms, errp) < 0) { + return -1; + } + if (qcrypto_pbkdf2(hash, + key, nkey, + salt, nsalt, + iterations, + out, sizeof(out), + errp) < 0) { + return -1; + } + if (qcrypto_pbkdf2_get_thread_cpu(&end_ms, errp) < 0) { + return -1; + } + + delta_ms = end_ms - start_ms; + + if (delta_ms > 500) { + break; + } else if (delta_ms < 100) { + iterations = iterations * 10; + } else { + iterations = (iterations * 1000 / delta_ms); + } + } + + iterations = iterations * 1000 / delta_ms; + + if (iterations > INT32_MAX) { + error_setg(errp, "Iterations %lld too large for a 32-bit int", + iterations); + return -1; + } + + return iterations; +} |