qemu/crypto/pbkdf.c
<<
>>
Prefs
   1/*
   2 * QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
   3 *
   4 * Copyright (c) 2015-2016 Red Hat, Inc.
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2.1 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 *
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "qapi/error.h"
  23#include "crypto/pbkdf.h"
  24#ifndef _WIN32
  25#include <sys/resource.h>
  26#endif
  27#ifdef CONFIG_DARWIN
  28#include <mach/mach_init.h>
  29#include <mach/thread_act.h>
  30#include <mach/mach_port.h>
  31#endif
  32
  33
  34static int qcrypto_pbkdf2_get_thread_cpu(unsigned long long *val_ms,
  35                                         Error **errp)
  36{
  37#ifdef _WIN32
  38    FILETIME creation_time, exit_time, kernel_time, user_time;
  39    ULARGE_INTEGER thread_time;
  40
  41    if (!GetThreadTimes(GetCurrentThread(), &creation_time, &exit_time,
  42                        &kernel_time, &user_time)) {
  43        error_setg(errp, "Unable to get thread CPU usage");
  44        return -1;
  45    }
  46
  47    thread_time.LowPart = user_time.dwLowDateTime;
  48    thread_time.HighPart = user_time.dwHighDateTime;
  49
  50    /* QuadPart is units of 100ns and we want ms as unit */
  51    *val_ms = thread_time.QuadPart / 10000ll;
  52    return 0;
  53#elif defined(CONFIG_DARWIN)
  54    mach_port_t thread;
  55    kern_return_t kr;
  56    mach_msg_type_number_t count;
  57    thread_basic_info_data_t info;
  58
  59    thread = mach_thread_self();
  60    count = THREAD_BASIC_INFO_COUNT;
  61    kr = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&info, &count);
  62    mach_port_deallocate(mach_task_self(), thread);
  63    if (kr != KERN_SUCCESS || (info.flags & TH_FLAGS_IDLE) != 0) {
  64        error_setg_errno(errp, errno, "Unable to get thread CPU usage");
  65        return -1;
  66    }
  67
  68    *val_ms = ((info.user_time.seconds * 1000ll) +
  69               (info.user_time.microseconds / 1000));
  70    return 0;
  71#elif defined(RUSAGE_THREAD)
  72    struct rusage ru;
  73    if (getrusage(RUSAGE_THREAD, &ru) < 0) {
  74        error_setg_errno(errp, errno, "Unable to get thread CPU usage");
  75        return -1;
  76    }
  77
  78    *val_ms = ((ru.ru_utime.tv_sec * 1000ll) +
  79               (ru.ru_utime.tv_usec / 1000));
  80    return 0;
  81#else
  82    *val_ms = 0;
  83    error_setg(errp, "Unable to calculate thread CPU usage on this platform");
  84    return -1;
  85#endif
  86}
  87
  88uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash,
  89                                    const uint8_t *key, size_t nkey,
  90                                    const uint8_t *salt, size_t nsalt,
  91                                    size_t nout,
  92                                    Error **errp)
  93{
  94    uint64_t ret = -1;
  95    g_autofree uint8_t *out = g_new(uint8_t, nout);
  96    uint64_t iterations = (1 << 15);
  97    unsigned long long delta_ms, start_ms, end_ms;
  98
  99    while (1) {
 100        if (qcrypto_pbkdf2_get_thread_cpu(&start_ms, errp) < 0) {
 101            goto cleanup;
 102        }
 103        if (qcrypto_pbkdf2(hash,
 104                           key, nkey,
 105                           salt, nsalt,
 106                           iterations,
 107                           out, nout,
 108                           errp) < 0) {
 109            goto cleanup;
 110        }
 111        if (qcrypto_pbkdf2_get_thread_cpu(&end_ms, errp) < 0) {
 112            goto cleanup;
 113        }
 114
 115        delta_ms = end_ms - start_ms;
 116
 117        if (delta_ms > 500) {
 118            break;
 119        } else if (delta_ms < 100) {
 120            iterations = iterations * 10;
 121        } else {
 122            iterations = (iterations * 1000 / delta_ms);
 123        }
 124    }
 125
 126    iterations = iterations * 1000 / delta_ms;
 127
 128    ret = iterations;
 129
 130 cleanup:
 131    memset(out, 0, nout);
 132    return ret;
 133}
 134