blob: 439b1f0511115ecc5facde85a80333e51ce240dd [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cryptohome/crypto/hkdf.h"
#include <base/logging.h>
#include <crypto/scoped_openssl_types.h>
#include <openssl/evp.h>
#include <openssl/kdf.h>
#include <openssl/sha.h>
#include "cryptohome/crypto/error_util.h"
namespace cryptohome {
namespace {
// Derives HKDF from `key`. The parameters `info` and `salt` are optional
// depending on the mode. For EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND both `info`
// and `salt` must be provided, otherwise for EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY
// only `salt` and for EVP_PKEY_HKDEF_MODE_EXPAND_ONLY only `info`. If
// `result_len` is zero, the resulting key length will be equal to hash size.
// Returns false if error occurred.
bool HkdfInternal(HkdfHash hash,
const brillo::SecureBlob& key,
const brillo::SecureBlob* info,
const brillo::SecureBlob* salt,
int mode,
size_t result_len,
brillo::SecureBlob* result) {
crypto::ScopedEVP_PKEY_CTX context(
EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr));
if (!context) {
LOG(ERROR) << "Failed to initialize EVP_PKEY_CTX: " << GetOpenSSLErrors();
return false;
}
if (EVP_PKEY_derive_init(context.get()) <= 0) {
LOG(ERROR) << "Failed to initialize HKDF: " << GetOpenSSLErrors();
return false;
}
if (EVP_PKEY_CTX_hkdf_mode(context.get(), mode) <= 0) {
LOG(ERROR) << "Failed to set HKDF mode: " << GetOpenSSLErrors();
return false;
}
const EVP_MD* md = nullptr;
switch (hash) {
case HkdfHash::kSha256:
md = EVP_sha256();
break;
}
if (!md) {
LOG(ERROR) << "Invalid HKDF hash type: " << static_cast<int>(hash);
return false;
}
if (result_len == 0) {
// Assign resulting key length to hash size.
result_len = EVP_MD_size(md);
}
if (EVP_PKEY_CTX_set_hkdf_md(context.get(), md) <= 0) {
LOG(ERROR) << "Failed to set HKDF message digest: " << GetOpenSSLErrors();
return false;
}
if (EVP_PKEY_CTX_set1_hkdf_key(context.get(), key.data(), key.size()) <= 0) {
LOG(ERROR) << "Failed to set HKDF key: " << GetOpenSSLErrors();
return false;
}
if (salt && EVP_PKEY_CTX_set1_hkdf_salt(context.get(), salt->data(),
salt->size()) <= 0) {
LOG(ERROR) << "Failed to set HKDF salt: " << GetOpenSSLErrors();
return false;
}
if (info && EVP_PKEY_CTX_add1_hkdf_info(context.get(), info->data(),
info->size()) <= 0) {
LOG(ERROR) << "Failed to set HKDF info: " << GetOpenSSLErrors();
return false;
}
// Derive HKDF.
result->resize(result_len);
if (EVP_PKEY_derive(context.get(), result->data(), &result_len) <= 0) {
LOG(ERROR) << "Failed to derive HKDF: " << GetOpenSSLErrors();
return false;
}
if (result_len != result->size()) {
LOG(ERROR) << "Failed to derive HKDF of length " << result->size()
<< ", the resulting length " << result_len;
return false;
}
return true;
}
} // namespace
bool Hkdf(HkdfHash hash,
const brillo::SecureBlob& key,
const brillo::SecureBlob& info,
const brillo::SecureBlob& salt,
size_t result_len,
brillo::SecureBlob* result) {
return HkdfInternal(hash, key, &info, &salt,
EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND, result_len,
result);
}
bool HkdfExpand(HkdfHash hash,
const brillo::SecureBlob& key,
const brillo::SecureBlob& info,
size_t result_len,
brillo::SecureBlob* result) {
return HkdfInternal(hash, key, &info, /*salt=*/nullptr,
EVP_PKEY_HKDEF_MODE_EXPAND_ONLY, result_len, result);
}
bool HkdfExtract(HkdfHash hash,
const brillo::SecureBlob& key,
const brillo::SecureBlob& salt,
brillo::SecureBlob* result) {
return HkdfInternal(hash, key, /*info=*/nullptr, &salt,
EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY, /*result_len=*/0,
result);
}
} // namespace cryptohome