summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-31 11:36:37 +0000
committerwtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-31 11:36:37 +0000
commit2fe8b630617b48ec4590b04dccf70b87db0a84a0 (patch)
tree8299aa8c9ffebd0de620e7fe73c3c779ec91bd10
parentcc405e47896d2b031f8f42a93337538e162ab1e4 (diff)
downloadchromium_src-2fe8b630617b48ec4590b04dccf70b87db0a84a0.zip
chromium_src-2fe8b630617b48ec4590b04dccf70b87db0a84a0.tar.gz
chromium_src-2fe8b630617b48ec4590b04dccf70b87db0a84a0.tar.bz2
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
-rw-r--r--crypto/hkdf.cc14
-rw-r--r--crypto/hkdf.h19
-rw-r--r--crypto/hkdf_unittest.cc2
-rw-r--r--net/quic/crypto/crypto_handshake.h2
-rw-r--r--net/quic/crypto/crypto_utils.cc68
-rw-r--r--net/quic/crypto/crypto_utils.h26
-rw-r--r--net/quic/crypto/crypto_utils_test.cc82
-rw-r--r--net/quic/crypto/quic_crypto_client_config.cc9
-rw-r--r--net/quic/crypto/quic_crypto_server_config.cc12
-rw-r--r--net/quic/quic_crypto_stream.cc19
-rw-r--r--net/quic/quic_crypto_stream.h9
-rw-r--r--net/quic/test_tools/crypto_test_utils.cc30
12 files changed, 254 insertions, 38 deletions
diff --git a/crypto/hkdf.cc b/crypto/hkdf.cc
index 1cd8468..82aae24 100644
--- a/crypto/hkdf.cc
+++ b/crypto/hkdf.cc
@@ -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);
}
}
diff --git a/crypto/hkdf.h b/crypto/hkdf.h
index 1d7a876..e91bccf 100644
--- a/crypto/hkdf.h
+++ b/crypto/hkdf.h
@@ -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
diff --git a/crypto/hkdf_unittest.cc b/crypto/hkdf_unittest.cc
index dc369d1..bcb19c5 100644
--- a/crypto/hkdf_unittest.cc
+++ b/crypto/hkdf_unittest.cc
@@ -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(),
diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h
index 3839a21..cc25570 100644
--- a/net/quic/crypto/crypto_handshake.h
+++ b/net/quic/crypto/crypto_handshake.h
@@ -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.
diff --git a/net/quic/crypto/crypto_utils.cc b/net/quic/crypto/crypto_utils.cc
index 2943396..041c284 100644
--- a/net/quic/crypto/crypto_utils.cc
+++ b/net/quic/crypto/crypto_utils.cc
@@ -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,23 +101,59 @@ 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;
}
diff --git a/net/quic/crypto/crypto_utils.h b/net/quic/crypto/crypto_utils.h
index ec6384c..af3ecbb 100644
--- a/net/quic/crypto/crypto_utils.h
+++ b/net/quic/crypto/crypto_utils.h
@@ -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);
diff --git a/net/quic/crypto/crypto_utils_test.cc b/net/quic/crypto/crypto_utils_test.cc
index 17eb192..37ac583 100644
--- a/net/quic/crypto/crypto_utils_test.cc
+++ b/net/quic/crypto/crypto_utils_test.cc
@@ -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
diff --git a/net/quic/crypto/quic_crypto_client_config.cc b/net/quic/crypto/quic_crypto_client_config.cc
index ce2705d1..93b11ef 100644
--- a/net/quic/crypto/quic_crypto_client_config.cc
+++ b/net/quic/crypto/quic_crypto_client_config.cc
@@ -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;
}
diff --git a/net/quic/crypto/quic_crypto_server_config.cc b/net/quic/crypto/quic_crypto_server_config.cc
index 6bddf2b..3659270 100644
--- a/net/quic/crypto/quic_crypto_server_config.cc
+++ b/net/quic/crypto/quic_crypto_server_config.cc
@@ -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,
- &params->initial_crypters)) {
+ &params->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, &params->forward_secure_crypters)) {
+ CryptoUtils::SERVER, &params->forward_secure_crypters,
+ &params->subkey_secret)) {
*error_details = "Symmetric key setup failed";
return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
}
diff --git a/net/quic/quic_crypto_stream.cc b/net/quic/quic_crypto_stream.cc
index 36b7c45..d53b736 100644
--- a/net/quic/quic_crypto_stream.cc
+++ b/net/quic/quic_crypto_stream.cc
@@ -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_;
diff --git a/net/quic/quic_crypto_stream.h b/net/quic/quic_crypto_stream.h
index 2c61a18..e9d575c 100644
--- a/net/quic/quic_crypto_stream.h
+++ b/net/quic/quic_crypto_stream.h
@@ -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_; }
diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc
index ca0681a..0e3a815 100644
--- a/net/quic/test_tools/crypto_test_utils.cc
+++ b/net/quic/test_tools/crypto_test_utils.cc
@@ -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