1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/quic/test_tools/crypto_test_utils.h"
7 #include "net/quic/crypto/channel_id.h"
8 #include "net/quic/crypto/common_cert_set.h"
9 #include "net/quic/crypto/crypto_handshake.h"
10 #include "net/quic/crypto/quic_crypto_server_config.h"
11 #include "net/quic/crypto/quic_decrypter.h"
12 #include "net/quic/crypto/quic_encrypter.h"
13 #include "net/quic/crypto/quic_random.h"
14 #include "net/quic/quic_clock.h"
15 #include "net/quic/quic_crypto_client_stream.h"
16 #include "net/quic/quic_crypto_server_stream.h"
17 #include "net/quic/quic_crypto_stream.h"
18 #include "net/quic/quic_server_id.h"
19 #include "net/quic/test_tools/quic_connection_peer.h"
20 #include "net/quic/test_tools/quic_framer_peer.h"
21 #include "net/quic/test_tools/quic_test_utils.h"
22 #include "net/quic/test_tools/simple_quic_framer.h"
24 using base::StringPiece
;
35 const char kServerHostname
[] = "test.example.com";
36 const uint16 kServerPort
= 80;
38 // CryptoFramerVisitor is a framer visitor that records handshake messages.
39 class CryptoFramerVisitor
: public CryptoFramerVisitorInterface
{
45 void OnError(CryptoFramer
* framer
) override
{ error_
= true; }
47 void OnHandshakeMessage(const CryptoHandshakeMessage
& message
) override
{
48 messages_
.push_back(message
);
55 const vector
<CryptoHandshakeMessage
>& messages() const {
61 vector
<CryptoHandshakeMessage
> messages_
;
64 // MovePackets parses crypto handshake messages from packet number
65 // |*inout_packet_index| through to the last packet (or until a packet fails to
66 // decrypt) and has |dest_stream| process them. |*inout_packet_index| is updated
67 // with an index one greater than the last packet processed.
68 void MovePackets(PacketSavingConnection
* source_conn
,
69 size_t *inout_packet_index
,
70 QuicCryptoStream
* dest_stream
,
71 PacketSavingConnection
* dest_conn
) {
72 SimpleQuicFramer
framer(source_conn
->supported_versions());
73 CryptoFramer crypto_framer
;
74 CryptoFramerVisitor crypto_visitor
;
76 // In order to properly test the code we need to perform encryption and
77 // decryption so that the crypters latch when expected. The crypters are in
78 // |dest_conn|, but we don't want to try and use them there. Instead we swap
79 // them into |framer|, perform the decryption with them, and then swap them
81 QuicConnectionPeer::SwapCrypters(dest_conn
, framer
.framer());
83 crypto_framer
.set_visitor(&crypto_visitor
);
85 size_t index
= *inout_packet_index
;
86 for (; index
< source_conn
->encrypted_packets_
.size(); index
++) {
87 if (!framer
.ProcessPacket(*source_conn
->encrypted_packets_
[index
])) {
88 // The framer will be unable to decrypt forward-secure packets sent after
89 // the handshake is complete. Don't treat them as handshake packets.
93 for (const QuicStreamFrame
& stream_frame
: framer
.stream_frames()) {
94 ASSERT_TRUE(crypto_framer
.ProcessInput(stream_frame
.data
));
95 ASSERT_FALSE(crypto_visitor
.error());
98 *inout_packet_index
= index
;
100 QuicConnectionPeer::SwapCrypters(dest_conn
, framer
.framer());
102 ASSERT_EQ(0u, crypto_framer
.InputBytesRemaining());
104 for (const CryptoHandshakeMessage
& message
: crypto_visitor
.messages()) {
105 dest_stream
->OnHandshakeMessage(message
);
109 // HexChar parses |c| as a hex character. If valid, it sets |*value| to the
110 // value of the hex character and returns true. Otherwise it returns false.
111 bool HexChar(char c
, uint8
* value
) {
112 if (c
>= '0' && c
<= '9') {
116 if (c
>= 'a' && c
<= 'f') {
117 *value
= c
- 'a' + 10;
120 if (c
>= 'A' && c
<= 'F') {
121 *value
= c
- 'A' + 10;
127 // A ChannelIDSource that works in asynchronous mode unless the |callback|
128 // argument to GetChannelIDKey is nullptr.
129 class AsyncTestChannelIDSource
: public ChannelIDSource
,
130 public CryptoTestUtils::CallbackSource
{
132 // Takes ownership of |sync_source|, a synchronous ChannelIDSource.
133 explicit AsyncTestChannelIDSource(ChannelIDSource
* sync_source
)
134 : sync_source_(sync_source
) {}
135 ~AsyncTestChannelIDSource() override
{}
137 // ChannelIDSource implementation.
138 QuicAsyncStatus
GetChannelIDKey(const string
& hostname
,
139 scoped_ptr
<ChannelIDKey
>* channel_id_key
,
140 ChannelIDSourceCallback
* callback
) override
{
143 return sync_source_
->GetChannelIDKey(hostname
, channel_id_key
, nullptr);
146 // Asynchronous mode.
147 QuicAsyncStatus status
=
148 sync_source_
->GetChannelIDKey(hostname
, &channel_id_key_
, nullptr);
149 if (status
!= QUIC_SUCCESS
) {
152 callback_
.reset(callback
);
156 // CallbackSource implementation.
157 void RunPendingCallbacks() override
{
158 if (callback_
.get()) {
159 callback_
->Run(&channel_id_key_
);
165 scoped_ptr
<ChannelIDSource
> sync_source_
;
166 scoped_ptr
<ChannelIDSourceCallback
> callback_
;
167 scoped_ptr
<ChannelIDKey
> channel_id_key_
;
170 } // anonymous namespace
172 CryptoTestUtils::FakeClientOptions::FakeClientOptions()
173 : dont_verify_certs(false),
174 channel_id_enabled(false),
175 channel_id_source_async(false) {
179 int CryptoTestUtils::HandshakeWithFakeServer(
180 PacketSavingConnection
* client_conn
,
181 QuicCryptoClientStream
* client
) {
182 PacketSavingConnection
* server_conn
= new PacketSavingConnection(
183 Perspective::IS_SERVER
, client_conn
->supported_versions());
185 QuicConfig config
= DefaultQuicConfig();
186 QuicCryptoServerConfig
crypto_config(QuicCryptoServerConfig::TESTING
,
187 QuicRandom::GetInstance());
188 SetupCryptoServerConfigForTest(server_conn
->clock(),
189 server_conn
->random_generator(), &config
,
192 TestQuicSpdyServerSession
server_session(server_conn
, config
, &crypto_config
);
194 // The client's handshake must have been started already.
195 CHECK_NE(0u, client_conn
->encrypted_packets_
.size());
197 CommunicateHandshakeMessages(client_conn
, client
, server_conn
,
198 server_session
.GetCryptoStream());
200 CompareClientAndServerKeys(client
, server_session
.GetCryptoStream());
202 return client
->num_sent_client_hellos();
206 int CryptoTestUtils::HandshakeWithFakeClient(
207 PacketSavingConnection
* server_conn
,
208 QuicCryptoServerStream
* server
,
209 const FakeClientOptions
& options
) {
210 PacketSavingConnection
* client_conn
=
211 new PacketSavingConnection(Perspective::IS_CLIENT
);
212 // Advance the time, because timers do not like uninitialized times.
213 client_conn
->AdvanceTime(QuicTime::Delta::FromSeconds(1));
215 QuicCryptoClientConfig crypto_config
;
216 bool is_https
= false;
217 AsyncTestChannelIDSource
* async_channel_id_source
= nullptr;
218 if (options
.channel_id_enabled
) {
221 ChannelIDSource
* source
= ChannelIDSourceForTesting();
222 if (options
.channel_id_source_async
) {
223 async_channel_id_source
= new AsyncTestChannelIDSource(source
);
224 source
= async_channel_id_source
;
226 crypto_config
.SetChannelIDSource(source
);
228 QuicServerId
server_id(kServerHostname
, kServerPort
, is_https
,
229 PRIVACY_MODE_DISABLED
);
230 if (!options
.dont_verify_certs
) {
231 #if defined(USE_OPENSSL)
232 crypto_config
.SetProofVerifier(ProofVerifierForTesting());
234 // TODO(rch): Implement a NSS proof source.
235 crypto_config
.SetProofVerifier(FakeProofVerifierForTesting());
238 TestQuicSpdyClientSession
client_session(client_conn
, DefaultQuicConfig(),
239 server_id
, &crypto_config
);
241 client_session
.GetCryptoStream()->CryptoConnect();
242 CHECK_EQ(1u, client_conn
->encrypted_packets_
.size());
244 CommunicateHandshakeMessagesAndRunCallbacks(
245 client_conn
, client_session
.GetCryptoStream(), server_conn
, server
,
246 async_channel_id_source
);
248 CompareClientAndServerKeys(client_session
.GetCryptoStream(), server
);
250 if (options
.channel_id_enabled
) {
251 scoped_ptr
<ChannelIDKey
> channel_id_key
;
252 QuicAsyncStatus status
= crypto_config
.channel_id_source()->GetChannelIDKey(
253 kServerHostname
, &channel_id_key
, nullptr);
254 EXPECT_EQ(QUIC_SUCCESS
, status
);
255 EXPECT_EQ(channel_id_key
->SerializeKey(),
256 server
->crypto_negotiated_params().channel_id
);
258 options
.channel_id_source_async
,
259 client_session
.GetCryptoStream()->WasChannelIDSourceCallbackRun());
262 return client_session
.GetCryptoStream()->num_sent_client_hellos();
266 void CryptoTestUtils::SetupCryptoServerConfigForTest(
267 const QuicClock
* clock
,
270 QuicCryptoServerConfig
* crypto_config
) {
271 QuicCryptoServerConfig::ConfigOptions options
;
272 options
.channel_id_enabled
= true;
273 scoped_ptr
<CryptoHandshakeMessage
> scfg(
274 crypto_config
->AddDefaultConfig(rand
, clock
, options
));
278 void CryptoTestUtils::CommunicateHandshakeMessages(
279 PacketSavingConnection
* a_conn
,
281 PacketSavingConnection
* b_conn
,
282 QuicCryptoStream
* b
) {
283 CommunicateHandshakeMessagesAndRunCallbacks(a_conn
, a
, b_conn
, b
, nullptr);
287 void CryptoTestUtils::CommunicateHandshakeMessagesAndRunCallbacks(
288 PacketSavingConnection
* a_conn
,
290 PacketSavingConnection
* b_conn
,
292 CallbackSource
* callback_source
) {
293 size_t a_i
= 0, b_i
= 0;
294 while (!a
->handshake_confirmed()) {
295 ASSERT_GT(a_conn
->encrypted_packets_
.size(), a_i
);
296 VLOG(1) << "Processing " << a_conn
->encrypted_packets_
.size() - a_i
298 MovePackets(a_conn
, &a_i
, b
, b_conn
);
299 if (callback_source
) {
300 callback_source
->RunPendingCallbacks();
303 ASSERT_GT(b_conn
->encrypted_packets_
.size(), b_i
);
304 VLOG(1) << "Processing " << b_conn
->encrypted_packets_
.size() - b_i
306 MovePackets(b_conn
, &b_i
, a
, a_conn
);
307 if (callback_source
) {
308 callback_source
->RunPendingCallbacks();
314 pair
<size_t, size_t> CryptoTestUtils::AdvanceHandshake(
315 PacketSavingConnection
* a_conn
,
318 PacketSavingConnection
* b_conn
,
321 VLOG(1) << "Processing " << a_conn
->encrypted_packets_
.size() - a_i
323 MovePackets(a_conn
, &a_i
, b
, b_conn
);
325 VLOG(1) << "Processing " << b_conn
->encrypted_packets_
.size() - b_i
327 if (b_conn
->encrypted_packets_
.size() - b_i
== 2) {
330 MovePackets(b_conn
, &b_i
, a
, a_conn
);
332 return std::make_pair(a_i
, b_i
);
336 string
CryptoTestUtils::GetValueForTag(const CryptoHandshakeMessage
& message
,
338 QuicTagValueMap::const_iterator it
= message
.tag_value_map().find(tag
);
339 if (it
== message
.tag_value_map().end()) {
345 class MockCommonCertSets
: public CommonCertSets
{
347 MockCommonCertSets(StringPiece cert
, uint64 hash
, uint32 index
)
348 : cert_(cert
.as_string()),
353 StringPiece
GetCommonHashes() const override
{
354 CHECK(false) << "not implemented";
355 return StringPiece();
358 StringPiece
GetCert(uint64 hash
, uint32 index
) const override
{
359 if (hash
== hash_
&& index
== index_
) {
362 return StringPiece();
365 bool MatchCert(StringPiece cert
,
366 StringPiece common_set_hashes
,
368 uint32
* out_index
) const override
{
373 if (common_set_hashes
.size() % sizeof(uint64
) != 0) {
376 bool client_has_set
= false;
377 for (size_t i
= 0; i
< common_set_hashes
.size(); i
+= sizeof(uint64
)) {
379 memcpy(&hash
, common_set_hashes
.data() + i
, sizeof(hash
));
381 client_has_set
= true;
386 if (!client_has_set
) {
401 CommonCertSets
* CryptoTestUtils::MockCommonCertSets(StringPiece cert
,
404 return new class MockCommonCertSets(cert
, hash
, index
);
407 void CryptoTestUtils::CompareClientAndServerKeys(
408 QuicCryptoClientStream
* client
,
409 QuicCryptoServerStream
* server
) {
410 QuicFramer
* client_framer
=
411 QuicConnectionPeer::GetFramer(client
->session()->connection());
412 QuicFramer
* server_framer
=
413 QuicConnectionPeer::GetFramer(server
->session()->connection());
414 const QuicEncrypter
* client_encrypter(
415 QuicFramerPeer::GetEncrypter(client_framer
, ENCRYPTION_INITIAL
));
416 const QuicDecrypter
* client_decrypter(
417 client
->session()->connection()->decrypter());
418 const QuicEncrypter
* client_forward_secure_encrypter(
419 QuicFramerPeer::GetEncrypter(client_framer
, ENCRYPTION_FORWARD_SECURE
));
420 const QuicDecrypter
* client_forward_secure_decrypter(
421 client
->session()->connection()->alternative_decrypter());
422 const QuicEncrypter
* server_encrypter(
423 QuicFramerPeer::GetEncrypter(server_framer
, ENCRYPTION_INITIAL
));
424 const QuicDecrypter
* server_decrypter(
425 server
->session()->connection()->decrypter());
426 const QuicEncrypter
* server_forward_secure_encrypter(
427 QuicFramerPeer::GetEncrypter(server_framer
, ENCRYPTION_FORWARD_SECURE
));
428 const QuicDecrypter
* server_forward_secure_decrypter(
429 server
->session()->connection()->alternative_decrypter());
431 StringPiece client_encrypter_key
= client_encrypter
->GetKey();
432 StringPiece client_encrypter_iv
= client_encrypter
->GetNoncePrefix();
433 StringPiece client_decrypter_key
= client_decrypter
->GetKey();
434 StringPiece client_decrypter_iv
= client_decrypter
->GetNoncePrefix();
435 StringPiece client_forward_secure_encrypter_key
=
436 client_forward_secure_encrypter
->GetKey();
437 StringPiece client_forward_secure_encrypter_iv
=
438 client_forward_secure_encrypter
->GetNoncePrefix();
439 StringPiece client_forward_secure_decrypter_key
=
440 client_forward_secure_decrypter
->GetKey();
441 StringPiece client_forward_secure_decrypter_iv
=
442 client_forward_secure_decrypter
->GetNoncePrefix();
443 StringPiece server_encrypter_key
= server_encrypter
->GetKey();
444 StringPiece server_encrypter_iv
= server_encrypter
->GetNoncePrefix();
445 StringPiece server_decrypter_key
= server_decrypter
->GetKey();
446 StringPiece server_decrypter_iv
= server_decrypter
->GetNoncePrefix();
447 StringPiece server_forward_secure_encrypter_key
=
448 server_forward_secure_encrypter
->GetKey();
449 StringPiece server_forward_secure_encrypter_iv
=
450 server_forward_secure_encrypter
->GetNoncePrefix();
451 StringPiece server_forward_secure_decrypter_key
=
452 server_forward_secure_decrypter
->GetKey();
453 StringPiece server_forward_secure_decrypter_iv
=
454 server_forward_secure_decrypter
->GetNoncePrefix();
456 StringPiece client_subkey_secret
=
457 client
->crypto_negotiated_params().subkey_secret
;
458 StringPiece server_subkey_secret
=
459 server
->crypto_negotiated_params().subkey_secret
;
462 const char kSampleLabel
[] = "label";
463 const char kSampleContext
[] = "context";
464 const size_t kSampleOutputLength
= 32;
465 string client_key_extraction
;
466 string server_key_extraction
;
467 EXPECT_TRUE(client
->ExportKeyingMaterial(kSampleLabel
,
470 &client_key_extraction
));
471 EXPECT_TRUE(server
->ExportKeyingMaterial(kSampleLabel
,
474 &server_key_extraction
));
476 CompareCharArraysWithHexError("client write key",
477 client_encrypter_key
.data(),
478 client_encrypter_key
.length(),
479 server_decrypter_key
.data(),
480 server_decrypter_key
.length());
481 CompareCharArraysWithHexError("client write IV",
482 client_encrypter_iv
.data(),
483 client_encrypter_iv
.length(),
484 server_decrypter_iv
.data(),
485 server_decrypter_iv
.length());
486 CompareCharArraysWithHexError("server write key",
487 server_encrypter_key
.data(),
488 server_encrypter_key
.length(),
489 client_decrypter_key
.data(),
490 client_decrypter_key
.length());
491 CompareCharArraysWithHexError("server write IV",
492 server_encrypter_iv
.data(),
493 server_encrypter_iv
.length(),
494 client_decrypter_iv
.data(),
495 client_decrypter_iv
.length());
496 CompareCharArraysWithHexError("client forward secure write key",
497 client_forward_secure_encrypter_key
.data(),
498 client_forward_secure_encrypter_key
.length(),
499 server_forward_secure_decrypter_key
.data(),
500 server_forward_secure_decrypter_key
.length());
501 CompareCharArraysWithHexError("client forward secure write IV",
502 client_forward_secure_encrypter_iv
.data(),
503 client_forward_secure_encrypter_iv
.length(),
504 server_forward_secure_decrypter_iv
.data(),
505 server_forward_secure_decrypter_iv
.length());
506 CompareCharArraysWithHexError("server forward secure write key",
507 server_forward_secure_encrypter_key
.data(),
508 server_forward_secure_encrypter_key
.length(),
509 client_forward_secure_decrypter_key
.data(),
510 client_forward_secure_decrypter_key
.length());
511 CompareCharArraysWithHexError("server forward secure write IV",
512 server_forward_secure_encrypter_iv
.data(),
513 server_forward_secure_encrypter_iv
.length(),
514 client_forward_secure_decrypter_iv
.data(),
515 client_forward_secure_decrypter_iv
.length());
516 CompareCharArraysWithHexError("subkey secret",
517 client_subkey_secret
.data(),
518 client_subkey_secret
.length(),
519 server_subkey_secret
.data(),
520 server_subkey_secret
.length());
521 CompareCharArraysWithHexError("sample key extraction",
522 client_key_extraction
.data(),
523 client_key_extraction
.length(),
524 server_key_extraction
.data(),
525 server_key_extraction
.length());
529 QuicTag
CryptoTestUtils::ParseTag(const char* tagstr
) {
530 const size_t len
= strlen(tagstr
);
535 if (tagstr
[0] == '#') {
536 CHECK_EQ(static_cast<size_t>(1 + 2*4), len
);
539 for (size_t i
= 0; i
< 8; i
++) {
543 CHECK(HexChar(tagstr
[i
], &v
));
551 for (size_t i
= 0; i
< 4; i
++) {
554 tag
|= static_cast<uint32
>(tagstr
[i
]) << 24;
562 CryptoHandshakeMessage
CryptoTestUtils::Message(const char* message_tag
, ...) {
564 va_start(ap
, message_tag
);
566 CryptoHandshakeMessage msg
;
567 msg
.set_tag(ParseTag(message_tag
));
570 const char* tagstr
= va_arg(ap
, const char*);
571 if (tagstr
== nullptr) {
575 if (tagstr
[0] == '$') {
577 const char* const special
= tagstr
+ 1;
578 if (strcmp(special
, "padding") == 0) {
579 const int min_bytes
= va_arg(ap
, int);
580 msg
.set_minimum_size(min_bytes
);
582 CHECK(false) << "Unknown special value: " << special
;
588 const QuicTag tag
= ParseTag(tagstr
);
589 const char* valuestr
= va_arg(ap
, const char*);
591 size_t len
= strlen(valuestr
);
592 if (len
> 0 && valuestr
[0] == '#') {
596 CHECK_EQ(0u, len
% 2);
597 scoped_ptr
<uint8
[]> buf(new uint8
[len
/2]);
599 for (size_t i
= 0; i
< len
/2; i
++) {
601 CHECK(HexChar(valuestr
[i
*2], &v
));
603 CHECK(HexChar(valuestr
[i
*2 + 1], &v
));
608 tag
, StringPiece(reinterpret_cast<char*>(buf
.get()), len
/2));
612 msg
.SetStringPiece(tag
, valuestr
);
615 // The CryptoHandshakeMessage needs to be serialized and parsed to ensure
616 // that any padding is included.
617 scoped_ptr
<QuicData
> bytes(CryptoFramer::ConstructHandshakeMessage(msg
));
618 scoped_ptr
<CryptoHandshakeMessage
> parsed(
619 CryptoFramer::ParseMessage(bytes
->AsStringPiece()));