Implement QUIC key extraction.
Added a new subkey_secret output to crypto::HKDF which is saved by the forward-secure key derivation and used for a new ExportKeyingMaterial method on QuicCryptoStream. This will be used in Chromium for WebRTC on QUIC. Generated some tests by making a straightforward alternative implementation in Python. Written by Daniel Ziegler. Merge internal CL: 72073257 R=agl@chromium.org,dmziegler@chromium.org BUG= Review URL: https://codereview.chromium.org/423333002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@286738 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
@ -5,6 +5,7 @@
|
||||
#include "crypto/hkdf.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "crypto/hmac.h"
|
||||
|
||||
namespace crypto {
|
||||
@ -15,7 +16,8 @@ HKDF::HKDF(const base::StringPiece& secret,
|
||||
const base::StringPiece& salt,
|
||||
const base::StringPiece& info,
|
||||
size_t key_bytes_to_generate,
|
||||
size_t iv_bytes_to_generate) {
|
||||
size_t iv_bytes_to_generate,
|
||||
size_t subkey_secret_bytes_to_generate) {
|
||||
// https://tools.ietf.org/html/rfc5869#section-2.2
|
||||
base::StringPiece actual_salt = salt;
|
||||
char zeros[kSHA256HashLength];
|
||||
@ -40,8 +42,9 @@ HKDF::HKDF(const base::StringPiece& secret,
|
||||
// https://tools.ietf.org/html/rfc5869#section-2.3
|
||||
// Perform the Expand phase to turn the pseudorandom key
|
||||
// and info into the output keying material.
|
||||
const size_t material_length =
|
||||
2*key_bytes_to_generate + 2*iv_bytes_to_generate;
|
||||
const size_t material_length = 2 * key_bytes_to_generate +
|
||||
2 * iv_bytes_to_generate +
|
||||
subkey_secret_bytes_to_generate;
|
||||
const size_t n = (material_length + kSHA256HashLength-1) /
|
||||
kSHA256HashLength;
|
||||
DCHECK_LT(n, 256u);
|
||||
@ -90,6 +93,11 @@ HKDF::HKDF(const base::StringPiece& secret,
|
||||
j += iv_bytes_to_generate;
|
||||
server_write_iv_ = base::StringPiece(reinterpret_cast<char*>(&output_[j]),
|
||||
iv_bytes_to_generate);
|
||||
j += iv_bytes_to_generate;
|
||||
}
|
||||
if (subkey_secret_bytes_to_generate) {
|
||||
subkey_secret_ = base::StringPiece(reinterpret_cast<char*>(&output_[j]),
|
||||
subkey_secret_bytes_to_generate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "build/build_config.h"
|
||||
#include "crypto/crypto_export.h"
|
||||
|
||||
namespace crypto {
|
||||
@ -20,7 +18,7 @@ namespace crypto {
|
||||
// See https://tools.ietf.org/html/rfc5869 for details.
|
||||
class CRYPTO_EXPORT HKDF {
|
||||
public:
|
||||
// |secret|: The input shared secret (or, from RFC 5869, the IKM).
|
||||
// |secret|: the input shared secret (or, from RFC 5869, the IKM).
|
||||
// |salt|: an (optional) public salt / non-secret random value. While
|
||||
// optional, callers are strongly recommended to provide a salt. There is no
|
||||
// added security value in making this larger than the SHA-256 block size of
|
||||
@ -28,13 +26,18 @@ class CRYPTO_EXPORT HKDF {
|
||||
// |info|: an (optional) label to distinguish different uses of HKDF. It is
|
||||
// optional context and application specific information (can be a zero-length
|
||||
// string).
|
||||
// |key_bytes_to_generate|: the number of bytes of key material to generate.
|
||||
// |iv_bytes_to_generate|: the number of bytes of IV to generate.
|
||||
// |key_bytes_to_generate|: the number of bytes of key material to generate
|
||||
// for both client and server.
|
||||
// |iv_bytes_to_generate|: the number of bytes of IV to generate for both
|
||||
// client and server.
|
||||
// |subkey_secret_bytes_to_generate|: the number of bytes of subkey secret to
|
||||
// generate, shared between client and server.
|
||||
HKDF(const base::StringPiece& secret,
|
||||
const base::StringPiece& salt,
|
||||
const base::StringPiece& info,
|
||||
size_t key_bytes_to_generate,
|
||||
size_t iv_bytes_to_generate);
|
||||
size_t iv_bytes_to_generate,
|
||||
size_t subkey_secret_bytes_to_generate);
|
||||
~HKDF();
|
||||
|
||||
base::StringPiece client_write_key() const {
|
||||
@ -49,6 +52,9 @@ class CRYPTO_EXPORT HKDF {
|
||||
base::StringPiece server_write_iv() const {
|
||||
return server_write_iv_;
|
||||
}
|
||||
base::StringPiece subkey_secret() const {
|
||||
return subkey_secret_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<uint8> output_;
|
||||
@ -57,6 +63,7 @@ class CRYPTO_EXPORT HKDF {
|
||||
base::StringPiece server_write_key_;
|
||||
base::StringPiece client_write_iv_;
|
||||
base::StringPiece server_write_iv_;
|
||||
base::StringPiece subkey_secret_;
|
||||
};
|
||||
|
||||
} // namespace crypto
|
||||
|
@ -82,7 +82,7 @@ TEST(HKDFTest, HKDF) {
|
||||
|
||||
// We set the key_length to the length of the expected output and then take
|
||||
// the result from the first key, which is the client write key.
|
||||
HKDF hkdf(key, salt, info, expected.size(), 0);
|
||||
HKDF hkdf(key, salt, info, expected.size(), 0, 0);
|
||||
|
||||
ASSERT_EQ(expected.size(), hkdf.client_write_key().size());
|
||||
EXPECT_EQ(0, memcmp(expected.data(), hkdf.client_write_key().data(),
|
||||
|
@ -100,6 +100,8 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters {
|
||||
QuicTag aead;
|
||||
std::string initial_premaster_secret;
|
||||
std::string forward_secure_premaster_secret;
|
||||
// subkey_secret is used as the PRK input to the HKDF used for key extraction.
|
||||
std::string subkey_secret;
|
||||
CrypterPair initial_crypters;
|
||||
CrypterPair forward_secure_crypters;
|
||||
// Normalized SNI: converted to lower case and trailing '.' removed.
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "url/url_canon.h"
|
||||
|
||||
using base::StringPiece;
|
||||
using std::numeric_limits;
|
||||
using std::string;
|
||||
|
||||
namespace net {
|
||||
@ -83,11 +84,14 @@ bool CryptoUtils::DeriveKeys(StringPiece premaster_secret,
|
||||
StringPiece server_nonce,
|
||||
const string& hkdf_input,
|
||||
Perspective perspective,
|
||||
CrypterPair* out) {
|
||||
out->encrypter.reset(QuicEncrypter::Create(aead));
|
||||
out->decrypter.reset(QuicDecrypter::Create(aead));
|
||||
size_t key_bytes = out->encrypter->GetKeySize();
|
||||
size_t nonce_prefix_bytes = out->encrypter->GetNoncePrefixSize();
|
||||
CrypterPair* crypters,
|
||||
string* subkey_secret) {
|
||||
crypters->encrypter.reset(QuicEncrypter::Create(aead));
|
||||
crypters->decrypter.reset(QuicDecrypter::Create(aead));
|
||||
size_t key_bytes = crypters->encrypter->GetKeySize();
|
||||
size_t nonce_prefix_bytes = crypters->encrypter->GetNoncePrefixSize();
|
||||
size_t subkey_secret_bytes =
|
||||
subkey_secret == NULL ? 0 : premaster_secret.length();
|
||||
|
||||
StringPiece nonce = client_nonce;
|
||||
string nonce_storage;
|
||||
@ -97,24 +101,60 @@ bool CryptoUtils::DeriveKeys(StringPiece premaster_secret,
|
||||
}
|
||||
|
||||
crypto::HKDF hkdf(premaster_secret, nonce, hkdf_input, key_bytes,
|
||||
nonce_prefix_bytes);
|
||||
nonce_prefix_bytes, subkey_secret_bytes);
|
||||
if (perspective == SERVER) {
|
||||
if (!out->encrypter->SetKey(hkdf.server_write_key()) ||
|
||||
!out->encrypter->SetNoncePrefix(hkdf.server_write_iv()) ||
|
||||
!out->decrypter->SetKey(hkdf.client_write_key()) ||
|
||||
!out->decrypter->SetNoncePrefix(hkdf.client_write_iv())) {
|
||||
if (!crypters->encrypter->SetKey(hkdf.server_write_key()) ||
|
||||
!crypters->encrypter->SetNoncePrefix(hkdf.server_write_iv()) ||
|
||||
!crypters->decrypter->SetKey(hkdf.client_write_key()) ||
|
||||
!crypters->decrypter->SetNoncePrefix(hkdf.client_write_iv())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!out->encrypter->SetKey(hkdf.client_write_key()) ||
|
||||
!out->encrypter->SetNoncePrefix(hkdf.client_write_iv()) ||
|
||||
!out->decrypter->SetKey(hkdf.server_write_key()) ||
|
||||
!out->decrypter->SetNoncePrefix(hkdf.server_write_iv())) {
|
||||
if (!crypters->encrypter->SetKey(hkdf.client_write_key()) ||
|
||||
!crypters->encrypter->SetNoncePrefix(hkdf.client_write_iv()) ||
|
||||
!crypters->decrypter->SetKey(hkdf.server_write_key()) ||
|
||||
!crypters->decrypter->SetNoncePrefix(hkdf.server_write_iv())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (subkey_secret != NULL) {
|
||||
hkdf.subkey_secret().CopyToString(subkey_secret);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool CryptoUtils::ExportKeyingMaterial(StringPiece subkey_secret,
|
||||
StringPiece label,
|
||||
StringPiece context,
|
||||
size_t result_len,
|
||||
string* result) {
|
||||
for (size_t i = 0; i < label.length(); i++) {
|
||||
if (label[i] == '\0') {
|
||||
LOG(ERROR) << "ExportKeyingMaterial label may not contain NULs";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Create HKDF info input: null-terminated label + length-prefixed context
|
||||
if (context.length() >= numeric_limits<uint32>::max()) {
|
||||
LOG(ERROR) << "Context value longer than 2^32";
|
||||
return false;
|
||||
}
|
||||
uint32 context_length = static_cast<uint32>(context.length());
|
||||
string info = label.as_string();
|
||||
info.push_back('\0');
|
||||
info.append(reinterpret_cast<char*>(&context_length), sizeof(context_length));
|
||||
info.append(context.data(), context.length());
|
||||
|
||||
crypto::HKDF hkdf(subkey_secret,
|
||||
StringPiece() /* no salt */,
|
||||
info,
|
||||
result_len,
|
||||
0 /* no fixed IV */,
|
||||
0 /* no subkey secret */);
|
||||
hkdf.client_write_key().CopyToString(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
@ -49,19 +49,31 @@ class NET_EXPORT_PRIVATE CryptoUtils {
|
||||
// literals. IsValidSNI() should be called before calling NormalizeHostname().
|
||||
static std::string NormalizeHostname(const char* hostname);
|
||||
|
||||
// DeriveKeys populates |out->encrypter| and |out->decrypter| given the
|
||||
// contents of |premaster_secret|, |client_nonce|, |server_nonce| and
|
||||
// |hkdf_input|. |aead| determines which cipher will be used. |perspective|
|
||||
// controls whether the server's keys are assigned to |encrypter| or
|
||||
// |decrypter|. |server_nonce| is optional and, if non-empty, is mixed into
|
||||
// the key derivation.
|
||||
// DeriveKeys populates |crypters->encrypter|, |crypters->decrypter|, and
|
||||
// |subkey_secret| (optional -- may be null) given the contents of
|
||||
// |premaster_secret|, |client_nonce|, |server_nonce| and |hkdf_input|. |aead|
|
||||
// determines which cipher will be used. |perspective| controls whether the
|
||||
// server's keys are assigned to |encrypter| or |decrypter|. |server_nonce| is
|
||||
// optional and, if non-empty, is mixed into the key derivation.
|
||||
// |subkey_secret| will have the same length as |premaster_secret|.
|
||||
static bool DeriveKeys(base::StringPiece premaster_secret,
|
||||
QuicTag aead,
|
||||
base::StringPiece client_nonce,
|
||||
base::StringPiece server_nonce,
|
||||
const std::string& hkdf_input,
|
||||
Perspective perspective,
|
||||
CrypterPair* out);
|
||||
CrypterPair* crypters,
|
||||
std::string* subkey_secret);
|
||||
|
||||
// Performs key extraction to derive a new secret of |result_len| bytes
|
||||
// dependent on |subkey_secret|, |label|, and |context|. Returns false if the
|
||||
// parameters are invalid (e.g. |label| contains null bytes); returns true on
|
||||
// success.
|
||||
static bool ExportKeyingMaterial(base::StringPiece subkey_secret,
|
||||
base::StringPiece label,
|
||||
base::StringPiece context,
|
||||
size_t result_len,
|
||||
std::string* result);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(CryptoUtils);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "net/quic/crypto/crypto_utils.h"
|
||||
|
||||
#include "net/quic/test_tools/quic_test_utils.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace net {
|
||||
@ -44,6 +45,87 @@ TEST(CryptoUtilsTest, NormalizeHostname) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CryptoUtilsTest, TestExportKeyingMaterial) {
|
||||
const struct TestVector {
|
||||
// Input (strings of hexadecimal digits):
|
||||
const char* subkey_secret;
|
||||
const char* label;
|
||||
const char* context;
|
||||
size_t result_len;
|
||||
|
||||
// Expected output (string of hexadecimal digits):
|
||||
const char* expected; // Null if it should fail.
|
||||
} test_vector[] = {
|
||||
// Try a typical input
|
||||
{ "4823c1189ecc40fce888fbb4cf9ae6254f19ba12e6d9af54788f195a6f509ca3",
|
||||
"e934f78d7a71dd85420fceeb8cea0317",
|
||||
"b8d766b5d3c8aba0009c7ed3de553eba53b4de1030ea91383dcdf724cd8b7217",
|
||||
32,
|
||||
"a9979da0d5f1c1387d7cbe68f5c4163ddb445a03c4ad6ee72cb49d56726d679e"
|
||||
},
|
||||
// Don't let the label contain nulls
|
||||
{ "14fe51e082ffee7d1b4d8d4ab41f8c55",
|
||||
"3132333435363700",
|
||||
"58585858585858585858585858585858",
|
||||
16,
|
||||
NULL
|
||||
},
|
||||
// Make sure nulls in the context are fine
|
||||
{ "d862c2e36b0a42f7827c67ebc8d44df7",
|
||||
"7a5b95e4e8378123",
|
||||
"4142434445464700",
|
||||
16,
|
||||
"12d418c6d0738a2e4d85b2d0170f76e1"
|
||||
},
|
||||
// ... and give a different result than without
|
||||
{ "d862c2e36b0a42f7827c67ebc8d44df7",
|
||||
"7a5b95e4e8378123",
|
||||
"41424344454647",
|
||||
16,
|
||||
"abfa1c479a6e3ffb98a11dee7d196408"
|
||||
},
|
||||
// Try weird lengths
|
||||
{ "d0ec8a34f6cc9a8c96",
|
||||
"49711798cc6251",
|
||||
"933d4a2f30d22f089cfba842791116adc121e0",
|
||||
23,
|
||||
"c9a46ed0757bd1812f1f21b4d41e62125fec8364a21db7"
|
||||
},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(test_vector); i++) {
|
||||
// Decode the test vector.
|
||||
string subkey_secret;
|
||||
string label;
|
||||
string context;
|
||||
ASSERT_TRUE(DecodeHexString(test_vector[i].subkey_secret, &subkey_secret));
|
||||
ASSERT_TRUE(DecodeHexString(test_vector[i].label, &label));
|
||||
ASSERT_TRUE(DecodeHexString(test_vector[i].context, &context));
|
||||
size_t result_len = test_vector[i].result_len;
|
||||
bool expect_ok = test_vector[i].expected != NULL;
|
||||
string expected;
|
||||
if (expect_ok) {
|
||||
ASSERT_TRUE(DecodeHexString(test_vector[i].expected, &expected));
|
||||
}
|
||||
|
||||
string result;
|
||||
bool ok = CryptoUtils::ExportKeyingMaterial(subkey_secret,
|
||||
label,
|
||||
context,
|
||||
result_len,
|
||||
&result);
|
||||
EXPECT_EQ(expect_ok, ok);
|
||||
if (expect_ok) {
|
||||
EXPECT_EQ(result_len, result.length());
|
||||
test::CompareCharArraysWithHexError("HKDF output",
|
||||
result.data(),
|
||||
result.length(),
|
||||
expected.data(),
|
||||
expected.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace net
|
||||
|
@ -515,7 +515,8 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
|
||||
if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret,
|
||||
out_params->aead, out_params->client_nonce,
|
||||
out_params->server_nonce, hkdf_input,
|
||||
CryptoUtils::CLIENT, &crypters)) {
|
||||
CryptoUtils::CLIENT, &crypters,
|
||||
NULL /* subkey secret */)) {
|
||||
*error_details = "Symmetric key setup failed";
|
||||
return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
|
||||
}
|
||||
@ -557,7 +558,8 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello(
|
||||
if (!CryptoUtils::DeriveKeys(
|
||||
out_params->initial_premaster_secret, out_params->aead,
|
||||
out_params->client_nonce, out_params->server_nonce, hkdf_input,
|
||||
CryptoUtils::CLIENT, &out_params->initial_crypters)) {
|
||||
CryptoUtils::CLIENT, &out_params->initial_crypters,
|
||||
NULL /* subkey secret */)) {
|
||||
*error_details = "Symmetric key setup failed";
|
||||
return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
|
||||
}
|
||||
@ -737,7 +739,8 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
|
||||
if (!CryptoUtils::DeriveKeys(
|
||||
out_params->forward_secure_premaster_secret, out_params->aead,
|
||||
out_params->client_nonce, out_params->server_nonce, hkdf_input,
|
||||
CryptoUtils::CLIENT, &out_params->forward_secure_crypters)) {
|
||||
CryptoUtils::CLIENT, &out_params->forward_secure_crypters,
|
||||
&out_params->subkey_secret)) {
|
||||
*error_details = "Symmetric key setup failed";
|
||||
return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
|
||||
}
|
||||
|
@ -54,7 +54,8 @@ string DeriveSourceAddressTokenKey(StringPiece source_address_token_secret) {
|
||||
StringPiece() /* no salt */,
|
||||
"QUIC source address token key",
|
||||
CryptoSecretBoxer::GetKeySize(),
|
||||
0 /* no fixed IV needed */);
|
||||
0 /* no fixed IV needed */,
|
||||
0 /* no subkey secret */);
|
||||
return hkdf.server_write_key().as_string();
|
||||
}
|
||||
|
||||
@ -682,7 +683,8 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
||||
CrypterPair crypters;
|
||||
if (!CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead,
|
||||
info.client_nonce, info.server_nonce,
|
||||
hkdf_input, CryptoUtils::SERVER, &crypters)) {
|
||||
hkdf_input, CryptoUtils::SERVER, &crypters,
|
||||
NULL /* subkey secret */)) {
|
||||
*error_details = "Symmetric key setup failed";
|
||||
return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
|
||||
}
|
||||
@ -723,7 +725,8 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
||||
if (!CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead,
|
||||
info.client_nonce, info.server_nonce, hkdf_input,
|
||||
CryptoUtils::SERVER,
|
||||
¶ms->initial_crypters)) {
|
||||
¶ms->initial_crypters,
|
||||
NULL /* subkey secret */)) {
|
||||
*error_details = "Symmetric key setup failed";
|
||||
return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
|
||||
}
|
||||
@ -756,7 +759,8 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
|
||||
if (!CryptoUtils::DeriveKeys(
|
||||
params->forward_secure_premaster_secret, params->aead,
|
||||
info.client_nonce, info.server_nonce, forward_secure_hkdf_input,
|
||||
CryptoUtils::SERVER, ¶ms->forward_secure_crypters)) {
|
||||
CryptoUtils::SERVER, ¶ms->forward_secure_crypters,
|
||||
¶ms->subkey_secret)) {
|
||||
*error_details = "Symmetric key setup failed";
|
||||
return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "net/quic/crypto/crypto_handshake.h"
|
||||
#include "net/quic/crypto/crypto_utils.h"
|
||||
#include "net/quic/quic_connection.h"
|
||||
#include "net/quic/quic_session.h"
|
||||
#include "net/quic/quic_utils.h"
|
||||
@ -62,6 +63,24 @@ void QuicCryptoStream::SendHandshakeMessage(
|
||||
WriteOrBufferData(string(data.data(), data.length()), false, NULL);
|
||||
}
|
||||
|
||||
bool QuicCryptoStream::ExportKeyingMaterial(
|
||||
StringPiece label,
|
||||
StringPiece context,
|
||||
size_t result_len,
|
||||
string* result) const {
|
||||
if (!handshake_confirmed()) {
|
||||
DLOG(ERROR) << "ExportKeyingMaterial was called before forward-secure"
|
||||
<< "encryption was established.";
|
||||
return false;
|
||||
}
|
||||
return CryptoUtils::ExportKeyingMaterial(
|
||||
crypto_negotiated_params_.subkey_secret,
|
||||
label,
|
||||
context,
|
||||
result_len,
|
||||
result);
|
||||
}
|
||||
|
||||
const QuicCryptoNegotiatedParameters&
|
||||
QuicCryptoStream::crypto_negotiated_params() const {
|
||||
return crypto_negotiated_params_;
|
||||
|
@ -46,6 +46,15 @@ class NET_EXPORT_PRIVATE QuicCryptoStream
|
||||
// TODO(wtc): return a success/failure status.
|
||||
void SendHandshakeMessage(const CryptoHandshakeMessage& message);
|
||||
|
||||
// Performs key extraction to derive a new secret of |result_len| bytes
|
||||
// dependent on |label|, |context|, and the stream's negotiated subkey secret.
|
||||
// Returns false if the handshake has not been confirmed or the parameters are
|
||||
// invalid (e.g. |label| contains null bytes); returns true on success.
|
||||
bool ExportKeyingMaterial(base::StringPiece label,
|
||||
base::StringPiece context,
|
||||
size_t result_len,
|
||||
std::string* result) const;
|
||||
|
||||
bool encryption_established() const { return encryption_established_; }
|
||||
bool handshake_confirmed() const { return handshake_confirmed_; }
|
||||
|
||||
|
@ -452,6 +452,26 @@ void CryptoTestUtils::CompareClientAndServerKeys(
|
||||
StringPiece server_forward_secure_decrypter_iv =
|
||||
server_forward_secure_decrypter->GetNoncePrefix();
|
||||
|
||||
StringPiece client_subkey_secret =
|
||||
client->crypto_negotiated_params().subkey_secret;
|
||||
StringPiece server_subkey_secret =
|
||||
server->crypto_negotiated_params().subkey_secret;
|
||||
|
||||
|
||||
const char kSampleLabel[] = "label";
|
||||
const char kSampleContext[] = "context";
|
||||
const size_t kSampleOutputLength = 32;
|
||||
string client_key_extraction;
|
||||
string server_key_extraction;
|
||||
EXPECT_TRUE(client->ExportKeyingMaterial(kSampleLabel,
|
||||
kSampleContext,
|
||||
kSampleOutputLength,
|
||||
&client_key_extraction));
|
||||
EXPECT_TRUE(server->ExportKeyingMaterial(kSampleLabel,
|
||||
kSampleContext,
|
||||
kSampleOutputLength,
|
||||
&server_key_extraction));
|
||||
|
||||
CompareCharArraysWithHexError("client write key",
|
||||
client_encrypter_key.data(),
|
||||
client_encrypter_key.length(),
|
||||
@ -492,6 +512,16 @@ void CryptoTestUtils::CompareClientAndServerKeys(
|
||||
server_forward_secure_encrypter_iv.length(),
|
||||
client_forward_secure_decrypter_iv.data(),
|
||||
client_forward_secure_decrypter_iv.length());
|
||||
CompareCharArraysWithHexError("subkey secret",
|
||||
client_subkey_secret.data(),
|
||||
client_subkey_secret.length(),
|
||||
server_subkey_secret.data(),
|
||||
server_subkey_secret.length());
|
||||
CompareCharArraysWithHexError("sample key extraction",
|
||||
client_key_extraction.data(),
|
||||
client_key_extraction.length(),
|
||||
server_key_extraction.data(),
|
||||
server_key_extraction.length());
|
||||
}
|
||||
|
||||
// static
|
||||
|
Reference in New Issue
Block a user