Expose key derivation through a simple interface for use in WebRTC.
This change just wraps the openssl key derivation functions in a simple interface in a similar way to how we do it for messagedigest.h so we aren't coupled to openssl in the core implementation. Bug: webrtc:9917 Change-Id: I8556bd6e38b7da34d93abbe29415c3366f6532ba Reviewed-on: https://webrtc-review.googlesource.com/c/107981 Reviewed-by: Qingsi Wang <qingsi@webrtc.org> Commit-Queue: Benjamin Wright <benwright@webrtc.org> Cr-Commit-Position: refs/heads/master@{#25440}
This commit is contained in:

committed by
Commit Bot

parent
1a92cd7312
commit
b3f887b823
@ -749,6 +749,8 @@ rtc_static_library("rtc_base") {
|
||||
"ipaddress.cc",
|
||||
"ipaddress.h",
|
||||
"keep_ref_until_done.h",
|
||||
"key_derivation.cc",
|
||||
"key_derivation.h",
|
||||
"mdns_responder_interface.h",
|
||||
"messagedigest.cc",
|
||||
"messagedigest.h",
|
||||
@ -769,6 +771,8 @@ rtc_static_library("rtc_base") {
|
||||
"nullsocketserver.cc",
|
||||
"nullsocketserver.h",
|
||||
"openssl.h",
|
||||
"openssl_key_derivation_hkdf.cc",
|
||||
"openssl_key_derivation_hkdf.h",
|
||||
"openssladapter.cc",
|
||||
"openssladapter.h",
|
||||
"opensslcertificate.cc",
|
||||
@ -1261,6 +1265,7 @@ if (rtc_include_tests) {
|
||||
}
|
||||
if (is_posix || is_fuchsia) {
|
||||
sources += [
|
||||
"openssl_key_derivation_hkdf_unittest.cc",
|
||||
"openssladapter_unittest.cc",
|
||||
"opensslsessioncache_unittest.cc",
|
||||
"opensslutility_unittest.cc",
|
||||
|
31
rtc_base/key_derivation.cc
Normal file
31
rtc_base/key_derivation.cc
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "rtc_base/key_derivation.h"
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "rtc_base/openssl_key_derivation_hkdf.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
KeyDerivation::KeyDerivation() = default;
|
||||
KeyDerivation::~KeyDerivation() = default;
|
||||
|
||||
// static
|
||||
std::unique_ptr<KeyDerivation> KeyDerivation::Create(
|
||||
KeyDerivationAlgorithm key_derivation_algorithm) {
|
||||
switch (key_derivation_algorithm) {
|
||||
case KeyDerivationAlgorithm::HKDF_SHA256:
|
||||
return absl::make_unique<OpenSSLKeyDerivationHKDF>();
|
||||
}
|
||||
RTC_NOTREACHED();
|
||||
}
|
||||
|
||||
} // namespace rtc
|
70
rtc_base/key_derivation.h
Normal file
70
rtc_base/key_derivation.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef RTC_BASE_KEY_DERIVATION_H_
|
||||
#define RTC_BASE_KEY_DERIVATION_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/constructormagic.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
// Defines the set of key derivation algorithms that are supported. It is ideal
|
||||
// to keep this list as small as possible.
|
||||
enum class KeyDerivationAlgorithm {
|
||||
// This algorithm is not suitable to generate a key from a password. Please
|
||||
// only use with a cryptographically random master secret.
|
||||
HKDF_SHA256
|
||||
};
|
||||
|
||||
// KeyDerivation provides a generic interface for deriving keys in WebRTC. This
|
||||
// class should be used over directly accessing openssl or boringssl primitives
|
||||
// so that we can maintain seperate implementations.
|
||||
// Example:
|
||||
// auto kd = KeyDerivation::Create(KeyDerivationAlgorithm::HDKF_SHA526);
|
||||
// if (kd == nullptr) return;
|
||||
// auto derived_key_or = kd->DeriveKey(secret, salt, label);
|
||||
// if (!derived_key_or.ok()) return;
|
||||
// DoSomethingWithKey(derived_key_or.value());
|
||||
class KeyDerivation {
|
||||
public:
|
||||
KeyDerivation();
|
||||
virtual ~KeyDerivation();
|
||||
|
||||
// Derives a new key from existing key material.
|
||||
// secret - The random secret value you wish to derive a key from.
|
||||
// salt - Optional but recommended (non secret) cryptographically random.
|
||||
// label - A non secret but unique label value to determine the derivation.
|
||||
// derived_key_byte_size - This must be at least 128 bits.
|
||||
// return - An optional ZeroOnFreeBuffer containing the derived key or
|
||||
// absl::nullopt. Nullopt indicates a failure in derivation.
|
||||
virtual absl::optional<ZeroOnFreeBuffer<uint8_t>> DeriveKey(
|
||||
rtc::ArrayView<const uint8_t> secret,
|
||||
rtc::ArrayView<const uint8_t> salt,
|
||||
rtc::ArrayView<const uint8_t> label,
|
||||
size_t derived_key_byte_size) = 0;
|
||||
|
||||
// Static factory that will return an implementation that is capable of
|
||||
// handling the key derivation with the requested algorithm. If no
|
||||
// implementation is available nullptr will be returned.
|
||||
static std::unique_ptr<KeyDerivation> Create(
|
||||
KeyDerivationAlgorithm key_derivation_algorithm);
|
||||
|
||||
private:
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(KeyDerivation);
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // RTC_BASE_KEY_DERIVATION_H_
|
70
rtc_base/openssl_key_derivation_hkdf.cc
Normal file
70
rtc_base/openssl_key_derivation_hkdf.cc
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "rtc_base/openssl_key_derivation_hkdf.h"
|
||||
|
||||
#include <openssl/digest.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/hkdf.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/openssl.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
OpenSSLKeyDerivationHKDF::OpenSSLKeyDerivationHKDF() = default;
|
||||
OpenSSLKeyDerivationHKDF::~OpenSSLKeyDerivationHKDF() = default;
|
||||
|
||||
const size_t OpenSSLKeyDerivationHKDF::kMinKeyByteSize = 16;
|
||||
const size_t OpenSSLKeyDerivationHKDF::kMaxKeyByteSize =
|
||||
255 * SHA256_DIGEST_LENGTH;
|
||||
const size_t OpenSSLKeyDerivationHKDF::kMinSecretByteSize = 16;
|
||||
|
||||
absl::optional<ZeroOnFreeBuffer<uint8_t>> OpenSSLKeyDerivationHKDF::DeriveKey(
|
||||
rtc::ArrayView<const uint8_t> secret,
|
||||
rtc::ArrayView<const uint8_t> salt,
|
||||
rtc::ArrayView<const uint8_t> label,
|
||||
size_t derived_key_byte_size) {
|
||||
// Prevent deriving less than 128 bits of key material or more than the max.
|
||||
if (derived_key_byte_size < kMinKeyByteSize ||
|
||||
derived_key_byte_size > kMaxKeyByteSize) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
// The secret must reach the minimum number of bits to be secure.
|
||||
if (secret.data() == nullptr || secret.size() < kMinSecretByteSize) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
// Empty labels are always invalid in derivation.
|
||||
if (label.data() == nullptr || label.size() == 0) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
// If a random salt is not provided use all zeros.
|
||||
rtc::Buffer salt_buffer;
|
||||
if (salt.data() == nullptr || salt.size() == 0) {
|
||||
salt_buffer.SetSize(SHA256_DIGEST_LENGTH);
|
||||
std::fill(salt_buffer.begin(), salt_buffer.end(), 0);
|
||||
salt = salt_buffer;
|
||||
}
|
||||
// This buffer will erase itself on release.
|
||||
ZeroOnFreeBuffer<uint8_t> derived_key_buffer(derived_key_byte_size, 0);
|
||||
if (!HKDF(derived_key_buffer.data(), derived_key_buffer.size(), EVP_sha256(),
|
||||
secret.data(), secret.size(), salt.data(), salt.size(),
|
||||
label.data(), label.size())) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
return absl::optional<ZeroOnFreeBuffer<uint8_t>>(
|
||||
std::move(derived_key_buffer));
|
||||
}
|
||||
|
||||
} // namespace rtc
|
54
rtc_base/openssl_key_derivation_hkdf.h
Normal file
54
rtc_base/openssl_key_derivation_hkdf.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef RTC_BASE_OPENSSL_KEY_DERIVATION_HKDF_H_
|
||||
#define RTC_BASE_OPENSSL_KEY_DERIVATION_HKDF_H_
|
||||
|
||||
#include "rtc_base/constructormagic.h"
|
||||
#include "rtc_base/key_derivation.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
// OpenSSLKeyDerivationHKDF provides a concrete implementation of the
|
||||
// KeyDerivation interface to support the HKDF algorithm using the
|
||||
// OpenSSL/BoringSSL internal implementation.
|
||||
class OpenSSLKeyDerivationHKDF final : public KeyDerivation {
|
||||
public:
|
||||
OpenSSLKeyDerivationHKDF();
|
||||
~OpenSSLKeyDerivationHKDF() override;
|
||||
|
||||
// General users shouldn't be generating keys smaller than 128 bits.
|
||||
static const size_t kMinKeyByteSize;
|
||||
// The maximum available derivation size 255*DIGEST_LENGTH
|
||||
static const size_t kMaxKeyByteSize;
|
||||
// The minimum acceptable secret size.
|
||||
static const size_t kMinSecretByteSize;
|
||||
|
||||
// Derives a new key from existing key material using HKDF.
|
||||
// secret - The random secret value you wish to derive a key from.
|
||||
// salt - Optional (non secret) cryptographically random value.
|
||||
// label - A non secret but unique label value to determine the derivation.
|
||||
// derived_key_byte_size - The size of the derived key.
|
||||
// return - A ZeroOnFreeBuffer containing the derived key or an error
|
||||
// condition. Checking error codes is explicit in the API and error should
|
||||
// never be ignored.
|
||||
absl::optional<ZeroOnFreeBuffer<uint8_t>> DeriveKey(
|
||||
rtc::ArrayView<const uint8_t> secret,
|
||||
rtc::ArrayView<const uint8_t> salt,
|
||||
rtc::ArrayView<const uint8_t> label,
|
||||
size_t derived_key_byte_size) override;
|
||||
|
||||
private:
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLKeyDerivationHKDF);
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // RTC_BASE_OPENSSL_KEY_DERIVATION_HKDF_H_
|
107
rtc_base/openssl_key_derivation_hkdf_unittest.cc
Normal file
107
rtc_base/openssl_key_derivation_hkdf_unittest.cc
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "rtc_base/openssl_key_derivation_hkdf.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "test/gmock.h"
|
||||
|
||||
namespace rtc {
|
||||
namespace {
|
||||
|
||||
// Validates that a basic valid call works correctly.
|
||||
TEST(OpenSSLKeyDerivationHKDF, DerivationBasicTest) {
|
||||
rtc::Buffer secret(32);
|
||||
rtc::Buffer salt(32);
|
||||
rtc::Buffer label(32);
|
||||
const size_t derived_key_byte_size = 16;
|
||||
|
||||
OpenSSLKeyDerivationHKDF hkdf;
|
||||
auto key_or = hkdf.DeriveKey(secret, salt, label, derived_key_byte_size);
|
||||
EXPECT_TRUE(key_or.has_value());
|
||||
ZeroOnFreeBuffer<uint8_t> key = std::move(key_or.value());
|
||||
EXPECT_EQ(derived_key_byte_size, key.size());
|
||||
}
|
||||
|
||||
// Derivation fails if output is too small.
|
||||
TEST(OpenSSLKeyDerivationHKDF, DerivationFailsIfOutputIsTooSmall) {
|
||||
rtc::Buffer secret(32);
|
||||
rtc::Buffer salt(32);
|
||||
rtc::Buffer label(32);
|
||||
const size_t derived_key_byte_size = 15;
|
||||
|
||||
OpenSSLKeyDerivationHKDF hkdf;
|
||||
auto key_or = hkdf.DeriveKey(secret, salt, label, derived_key_byte_size);
|
||||
EXPECT_FALSE(key_or.has_value());
|
||||
}
|
||||
|
||||
// Derivation fails if output is too large.
|
||||
TEST(OpenSSLKeyDerivationHKDF, DerivationFailsIfOutputIsTooLarge) {
|
||||
rtc::Buffer secret(32);
|
||||
rtc::Buffer salt(32);
|
||||
rtc::Buffer label(32);
|
||||
const size_t derived_key_byte_size = 256 * 32;
|
||||
|
||||
OpenSSLKeyDerivationHKDF hkdf;
|
||||
auto key_or = hkdf.DeriveKey(secret, salt, label, derived_key_byte_size);
|
||||
EXPECT_FALSE(key_or.has_value());
|
||||
}
|
||||
|
||||
// Validates that too little key material causes a failure.
|
||||
TEST(OpenSSLKeyDerivationHKDF, DerivationFailsWithInvalidSecret) {
|
||||
rtc::Buffer secret(15);
|
||||
rtc::Buffer salt(32);
|
||||
rtc::Buffer label(32);
|
||||
const size_t derived_key_byte_size = 16;
|
||||
|
||||
OpenSSLKeyDerivationHKDF hkdf;
|
||||
auto key_or_0 = hkdf.DeriveKey(secret, salt, label, derived_key_byte_size);
|
||||
EXPECT_FALSE(key_or_0.has_value());
|
||||
|
||||
auto key_or_1 = hkdf.DeriveKey(nullptr, salt, label, derived_key_byte_size);
|
||||
EXPECT_FALSE(key_or_1.has_value());
|
||||
|
||||
rtc::Buffer secret_empty;
|
||||
auto key_or_2 =
|
||||
hkdf.DeriveKey(secret_empty, salt, label, derived_key_byte_size);
|
||||
EXPECT_FALSE(key_or_2.has_value());
|
||||
}
|
||||
|
||||
// Validates that HKDF works without a salt being set.
|
||||
TEST(OpenSSLKeyDerivationHKDF, DerivationWorksWithNoSalt) {
|
||||
rtc::Buffer secret(32);
|
||||
rtc::Buffer label(32);
|
||||
const size_t derived_key_byte_size = 16;
|
||||
|
||||
OpenSSLKeyDerivationHKDF hkdf;
|
||||
auto key_or = hkdf.DeriveKey(secret, nullptr, label, derived_key_byte_size);
|
||||
EXPECT_TRUE(key_or.has_value());
|
||||
}
|
||||
|
||||
// Validates that a label is required to work correctly.
|
||||
TEST(OpenSSLKeyDerivationHKDF, DerivationRequiresLabel) {
|
||||
rtc::Buffer secret(32);
|
||||
rtc::Buffer salt(32);
|
||||
rtc::Buffer label(1);
|
||||
const size_t derived_key_byte_size = 16;
|
||||
|
||||
OpenSSLKeyDerivationHKDF hkdf;
|
||||
auto key_or_0 = hkdf.DeriveKey(secret, salt, label, derived_key_byte_size);
|
||||
EXPECT_TRUE(key_or_0.has_value());
|
||||
ZeroOnFreeBuffer<uint8_t> key = std::move(key_or_0.value());
|
||||
EXPECT_EQ(key.size(), derived_key_byte_size);
|
||||
|
||||
auto key_or_1 = hkdf.DeriveKey(secret, salt, nullptr, derived_key_byte_size);
|
||||
EXPECT_FALSE(key_or_1.has_value());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace rtc
|
Reference in New Issue
Block a user