OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2016 The WebRTC Project Authors. All rights reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include <string> | |
12 #include <vector> | |
13 | |
14 #include "webrtc/p2p/quic/quicconnectionhelper.h" | |
15 #include "webrtc/p2p/quic/quicsession.h" | |
16 #include "webrtc/p2p/quic/reliablequicstream.h" | |
17 | |
18 #include "net/base/ip_endpoint.h" | |
19 #include "net/quic/crypto/crypto_server_config_protobuf.h" | |
20 #include "net/quic/crypto/quic_random.h" | |
21 #include "net/quic/crypto/proof_source.h" | |
22 #include "net/quic/crypto/proof_verifier.h" | |
23 #include "net/quic/crypto/quic_crypto_client_config.h" | |
24 #include "net/quic/crypto/quic_crypto_server_config.h" | |
25 #include "net/quic/quic_crypto_client_stream.h" | |
26 #include "net/quic/quic_crypto_server_stream.h" | |
27 | |
28 #include "webrtc/base/common.h" | |
29 #include "webrtc/base/gunit.h" | |
30 | |
31 #include "webrtc/p2p/base/faketransportcontroller.h" | |
32 | |
33 using net::IPAddressNumber; | |
34 using net::IPEndPoint; | |
35 using net::Perspective; | |
36 using net::ProofVerifyContext; | |
37 using net::ProofVerifyDetails; | |
38 using net::QuicByteCount; | |
39 using net::QuicClock; | |
40 using net::QuicConfig; | |
41 using net::QuicConnection; | |
42 using net::QuicCryptoClientConfig; | |
43 using net::QuicCryptoServerConfig; | |
44 using net::QuicCryptoClientStream; | |
45 using net::QuicCryptoServerStream; | |
46 using net::QuicCryptoStream; | |
47 using net::QuicErrorCode; | |
48 using net::QuicPacketWriter; | |
49 using net::QuicRandom; | |
50 using net::QuicServerConfigProtobuf; | |
51 using net::QuicServerId; | |
52 using net::QuicStreamId; | |
53 using net::WriteResult; | |
54 using net::WriteStatus; | |
55 | |
56 using cricket::FakeTransportChannel; | |
57 using cricket::QuicConnectionHelper; | |
58 using cricket::QuicSession; | |
59 using cricket::ReliableQuicStream; | |
60 using cricket::TransportChannel; | |
61 | |
62 using rtc::Thread; | |
63 | |
64 // Timeout for running asynchronous operations within unit tests | |
65 const int kTimeoutMs = 1000; | |
66 // Testing SpdyPriority value for creating outgoing ReliableQuicStream | |
67 const uint8 kDefaultPriority = 3; | |
68 // TExport keying material function | |
69 const char kExporterLabel[] = "label"; | |
70 const char kExporterContext[] = "context"; | |
71 const size_t kExporterContextLen = sizeof(kExporterContext); | |
72 // Identifies QUIC server session | |
73 const QuicServerId kServerId("www.google.com", 443); | |
74 | |
75 // Used by QuicCryptoServerConfig to provide server credentials, returning a | |
76 // canned response equal to |success| | |
77 class FakeProofSource : public net::ProofSource { | |
78 public: | |
79 explicit FakeProofSource(bool success) : success_(success) {} | |
80 ~FakeProofSource() override {} | |
81 | |
82 // ProofSource override | |
83 bool GetProof(const net::IPAddressNumber& server_ip, | |
84 const std::string& hostname, | |
85 const std::string& server_config, | |
86 bool ecdsa_ok, | |
87 const std::vector<std::string>** out_certs, | |
88 std::string* out_signature, | |
89 std::string* out_leaf_cert_sct) override { | |
90 if (success_) { | |
91 std::vector<std::string>* certs = new std::vector<std::string>(); | |
92 certs->push_back("Required to establish handshake"); | |
93 std::string signature("Signature"); | |
94 | |
95 *out_certs = certs; | |
96 *out_signature = signature; | |
97 } | |
98 return success_; | |
99 } | |
100 | |
101 private: | |
102 // Whether or not obtaining proof source succeeds | |
103 bool success_; | |
104 }; | |
105 | |
106 // Used by QuicCryptoClientConfig to verify server credentials, returning a | |
107 // canned response of QUIC_SUCCESS if |success| is true | |
108 class FakeProofVerifier : public net::ProofVerifier { | |
109 public: | |
110 explicit FakeProofVerifier(bool success) : success_(success) {} | |
111 ~FakeProofVerifier() override {} | |
112 | |
113 // ProofVerifier override | |
114 net::QuicAsyncStatus VerifyProof( | |
115 const std::string& hostname, | |
116 const std::string& server_config, | |
117 const std::vector<std::string>& certs, | |
118 const std::string& cert_sct, | |
119 const std::string& signature, | |
120 const net::ProofVerifyContext* verify_context, | |
121 std::string* error_details, | |
122 scoped_ptr<net::ProofVerifyDetails>* verify_details, | |
123 net::ProofVerifierCallback* callback) override { | |
124 return success_ ? net::QUIC_SUCCESS : net::QUIC_FAILURE; | |
125 } | |
126 | |
127 private: | |
128 // Whether or not proof verification succeeds | |
129 bool success_; | |
130 }; | |
131 | |
132 // Writes QUIC packets to a fake transport channel that simulates a network | |
133 class FakeQuicPacketWriter : public QuicPacketWriter { | |
134 public: | |
135 explicit FakeQuicPacketWriter(FakeTransportChannel* fake_channel) | |
136 : fake_channel_(fake_channel) {} | |
137 | |
138 virtual ~FakeQuicPacketWriter() {} | |
139 | |
140 // Sends packets across the network | |
141 WriteResult WritePacket(const char* buffer, | |
142 size_t buf_len, | |
143 const IPAddressNumber& self_address, | |
144 const IPEndPoint& peer_address) override { | |
145 rtc::PacketOptions packet_options; | |
146 int rv = fake_channel_->SendPacket(buffer, buf_len, packet_options, 0); | |
147 net::WriteStatus status; | |
148 if (rv > 0) { | |
149 status = net::WRITE_STATUS_OK; | |
150 } else if (fake_channel_->GetError() == EWOULDBLOCK) { | |
151 status = net::WRITE_STATUS_BLOCKED; | |
152 } else { | |
153 status = net::WRITE_STATUS_ERROR; | |
154 } | |
155 return net::WriteResult(status, rv); | |
156 } | |
157 | |
158 // Returns true if the writer buffers and subsequently rewrites data | |
159 // when an attempt to write results in the underlying socket becoming | |
160 // write blocked. | |
161 bool IsWriteBlockedDataBuffered() const override { return true; } | |
162 | |
163 // Returns true if the network socket is not writable. | |
164 bool IsWriteBlocked() const override { return !fake_channel_->writable(); } | |
165 | |
166 // Records that the socket has become writable, for example when an EPOLLOUT | |
167 // is received or an asynchronous write completes. | |
168 void SetWritable() override { fake_channel_->SetWritable(true); } | |
169 | |
170 // Returns the maximum size of the packet which can be written using this | |
171 // writer for the supplied peer address. This size may actually exceed the | |
172 // size of a valid QUIC packet. | |
173 QuicByteCount GetMaxPacketSize( | |
174 const IPEndPoint& peer_address) const override { | |
175 return net::kMaxPacketSize; | |
176 } | |
177 | |
178 private: | |
179 FakeTransportChannel* fake_channel_; | |
180 }; | |
181 | |
182 // Creates a FakePacketWriter for a given QuicConnection instance | |
183 class FakePacketWriterFactory : public QuicConnection::PacketWriterFactory { | |
184 public: | |
185 explicit FakePacketWriterFactory(FakeTransportChannel* channel) | |
186 : channel_(channel) {} | |
187 ~FakePacketWriterFactory() override {} | |
188 | |
189 QuicPacketWriter* Create(QuicConnection* connection) const override { | |
190 return new FakeQuicPacketWriter(channel_); | |
191 } | |
192 | |
193 private: | |
194 FakeTransportChannel* channel_; | |
195 }; | |
196 | |
197 // Wrapper for QuicSession and transport channel that stores incoming data | |
198 class QuicSessionForTest : public QuicSession { | |
199 public: | |
200 QuicSessionForTest(scoped_ptr<net::QuicConnection> connection, | |
201 const net::QuicConfig& config, | |
202 scoped_ptr<FakeTransportChannel> channel) | |
203 : QuicSession(std::move(connection), config), | |
204 channel_(channel.release()) { | |
205 channel_->SignalReadPacket.connect( | |
206 this, &QuicSessionForTest::OnChannelReadPacket); | |
207 } | |
208 | |
209 // Called when channel has packets to read | |
210 void OnChannelReadPacket(TransportChannel* channel, | |
211 const char* data, | |
212 size_t size, | |
213 const rtc::PacketTime& packet_time, | |
214 int flags) { | |
215 OnReadPacket(data, size); | |
216 } | |
217 | |
218 // Called when peer receives incoming stream from another peer | |
219 void OnIncomingStream(ReliableQuicStream* stream) { | |
220 stream->SignalDataReceived.connect(this, | |
221 &QuicSessionForTest::OnDataReceived); | |
222 last_incoming_stream_ = stream; | |
223 } | |
224 | |
225 // Called when peer has data to read from incoming stream | |
226 void OnDataReceived(net::QuicStreamId id, const char* data, size_t length) { | |
227 last_received_data_ = std::string(data, length); | |
228 } | |
229 | |
230 std::string data() { return last_received_data_; } | |
231 | |
232 bool has_data() { return data().size() > 0; } | |
233 | |
234 FakeTransportChannel* channel() { return channel_.get(); } | |
235 | |
236 ReliableQuicStream* incoming_stream() { return last_incoming_stream_; } | |
237 | |
238 private: | |
239 // Transports QUIC packets to/from peer | |
240 scoped_ptr<FakeTransportChannel> channel_; | |
241 // Stores data received by peer once it is sent from the other peer | |
242 std::string last_received_data_ = ""; | |
243 // Handles incoming streams from sender | |
244 ReliableQuicStream* last_incoming_stream_; | |
245 }; | |
246 | |
247 // Simulates data transfer between two peers using QUIC | |
248 class QuicSessionTest : public ::testing::Test, | |
249 public QuicCryptoClientStream::ProofHandler { | |
250 public: | |
251 QuicSessionTest() : quic_helper_(rtc::Thread::Current()) {} | |
252 | |
253 ~QuicSessionTest() override {} | |
254 | |
255 // Instantiates |client_peer_| and |server_peer_| | |
256 void CreateClientAndServerSessions(); | |
257 | |
258 scoped_ptr<QuicSessionForTest> CreateSession( | |
259 scoped_ptr<FakeTransportChannel> channel, | |
260 Perspective perspective); | |
261 | |
262 QuicCryptoClientStream* CreateCryptoClientStream(QuicSessionForTest* session, | |
263 bool handshake_success); | |
264 QuicCryptoServerStream* CreateCryptoServerStream(QuicSessionForTest* session, | |
265 bool handshake_success); | |
266 | |
267 scoped_ptr<QuicConnection> CreateConnection(FakeTransportChannel* channel, | |
268 Perspective perspective); | |
269 | |
270 void StartHandshake(bool client_handshake_success, | |
271 bool server_handshake_success); | |
272 | |
273 // Test handshake establishment and sending/receiving of data | |
274 void TestStreamConnection(QuicSessionForTest* from_session, | |
275 QuicSessionForTest* to_session); | |
276 // Test that client and server are not connected after handshake failure | |
277 void TestDisconnectAfterFailedHandshake(); | |
278 | |
279 // QuicCryptoClientStream::ProofHelper overrides. | |
280 void OnProofValid( | |
281 const QuicCryptoClientConfig::CachedState& cached) override {} | |
282 void OnProofVerifyDetailsAvailable( | |
283 const ProofVerifyDetails& verify_details) override {} | |
284 | |
285 protected: | |
286 QuicConnectionHelper quic_helper_; | |
287 QuicConfig config_; | |
288 QuicClock clock_; | |
289 | |
290 scoped_ptr<QuicSessionForTest> client_peer_; | |
291 scoped_ptr<QuicSessionForTest> server_peer_; | |
292 }; | |
293 | |
294 // Initializes "client peer" who begins crypto handshake and "server peer" who | |
295 // establishes encryption with client | |
296 void QuicSessionTest::CreateClientAndServerSessions() { | |
297 scoped_ptr<FakeTransportChannel> channel1( | |
298 new FakeTransportChannel(nullptr, "channel1", 0)); | |
299 scoped_ptr<FakeTransportChannel> channel2( | |
300 new FakeTransportChannel(nullptr, "channel2", 0)); | |
301 | |
302 // Prevent channel1->OnReadPacket and channel2->OnReadPacket from calling | |
303 // themselves in a loop, which causes to future packets to be recursively | |
304 // consumed while the current thread blocks consumption of current ones | |
305 channel2->SetAsync(true); | |
306 | |
307 // Configure peers to send packets to each other | |
308 channel1->Connect(); | |
309 channel2->Connect(); | |
310 channel1->SetDestination(channel2.get()); | |
311 | |
312 client_peer_ = CreateSession(std::move(channel1), Perspective::IS_CLIENT); | |
313 server_peer_ = CreateSession(std::move(channel2), Perspective::IS_SERVER); | |
314 } | |
315 | |
316 scoped_ptr<QuicSessionForTest> QuicSessionTest::CreateSession( | |
317 scoped_ptr<FakeTransportChannel> channel, | |
318 Perspective perspective) { | |
319 scoped_ptr<QuicConnection> quic_connection = | |
320 CreateConnection(channel.get(), perspective); | |
321 return scoped_ptr<QuicSessionForTest>(new QuicSessionForTest( | |
322 std::move(quic_connection), config_, std::move(channel))); | |
323 } | |
324 | |
325 QuicCryptoClientStream* QuicSessionTest::CreateCryptoClientStream( | |
326 QuicSessionForTest* session, | |
327 bool handshake_success) { | |
328 QuicCryptoClientConfig* client_config = | |
329 new QuicCryptoClientConfig(new FakeProofVerifier(handshake_success)); | |
330 return new QuicCryptoClientStream( | |
331 kServerId, session, new ProofVerifyContext(), client_config, this); | |
332 } | |
333 | |
334 QuicCryptoServerStream* QuicSessionTest::CreateCryptoServerStream( | |
335 QuicSessionForTest* session, | |
336 bool handshake_success) { | |
337 QuicCryptoServerConfig* server_config = | |
338 new QuicCryptoServerConfig("TESTING", QuicRandom::GetInstance(), | |
339 new FakeProofSource(handshake_success)); | |
340 // Provide server with serialized config string to prove ownership | |
341 QuicCryptoServerConfig::ConfigOptions options; | |
342 QuicServerConfigProtobuf* primary_config = server_config->GenerateConfig( | |
343 QuicRandom::GetInstance(), &clock_, options); | |
344 server_config->AddConfig(primary_config, clock_.WallNow()); | |
345 return new QuicCryptoServerStream(server_config, session); | |
346 } | |
347 | |
348 scoped_ptr<QuicConnection> QuicSessionTest::CreateConnection( | |
349 FakeTransportChannel* channel, | |
350 Perspective perspective) { | |
351 FakePacketWriterFactory writer_factory(channel); | |
352 | |
353 IPAddressNumber ip(net::kIPv4AddressSize, 0); | |
354 | |
355 return scoped_ptr<QuicConnection>(new QuicConnection( | |
356 0, net::IPEndPoint(ip, 0), &quic_helper_, writer_factory, | |
357 true /* owns_writer */, perspective, net::QuicSupportedVersions())); | |
358 } | |
359 | |
360 void QuicSessionTest::StartHandshake(bool client_handshake_success, | |
361 bool server_handshake_success) { | |
362 server_peer_->StartServerHandshake( | |
363 CreateCryptoServerStream(server_peer_.get(), server_handshake_success)); | |
364 client_peer_->StartClientHandshake( | |
365 CreateCryptoClientStream(client_peer_.get(), client_handshake_success)); | |
366 } | |
367 | |
368 void QuicSessionTest::TestStreamConnection(QuicSessionForTest* from_session, | |
369 QuicSessionForTest* to_session) { | |
370 // Wait for crypto handshake to finish then check if encryption established | |
371 ASSERT_TRUE_WAIT(from_session->IsCryptoHandshakeConfirmed() && | |
372 to_session->IsCryptoHandshakeConfirmed(), | |
373 kTimeoutMs); | |
374 | |
375 EXPECT_TRUE(from_session->IsEncryptionEstablished()); | |
376 EXPECT_TRUE(to_session->IsEncryptionEstablished()); | |
pthatcher1
2016/02/03 23:27:24
These should be ASSERT_TRUE
mikescarlett
2016/02/05 21:10:29
Agreed.
| |
377 | |
378 string from_out; | |
379 string to_out; | |
pthatcher1
2016/02/03 23:27:23
Should this be from_key and to_key?
mikescarlett
2016/02/05 21:10:29
Done. That is more descriptive.
| |
380 | |
381 bool from_success = from_session->ExportKeyingMaterial( | |
382 kExporterLabel, kExporterContext, kExporterContextLen, &from_out); | |
383 EXPECT_TRUE(from_success); | |
384 bool to_success = to_session->ExportKeyingMaterial( | |
385 kExporterLabel, kExporterContext, kExporterContextLen, &to_out); | |
386 EXPECT_TRUE(to_success); | |
pthatcher1
2016/02/03 23:27:24
I think these should be ASSERT_TRUEs, and then you
mikescarlett
2016/02/05 21:10:29
Agreed and done.
| |
387 | |
388 if (!from_success || !to_success) { | |
389 return; | |
390 } | |
391 | |
392 EXPECT_EQ(from_out.size(), kExporterContextLen); | |
393 EXPECT_EQ(0, from_out.compare(to_out)); | |
394 | |
395 // Now we can establish encrypted outgoing stream | |
396 ReliableQuicStream* outgoing_stream = | |
397 from_session->CreateOutgoingDynamicStream(kDefaultPriority); | |
398 ASSERT_NE(nullptr, outgoing_stream); | |
399 EXPECT_TRUE(from_session->HasOpenDynamicStreams()); | |
400 | |
401 outgoing_stream->SignalDataReceived.connect( | |
402 from_session, &QuicSessionForTest::OnDataReceived); | |
403 to_session->SignalIncomingStream.connect( | |
404 to_session, &QuicSessionForTest::OnIncomingStream); | |
405 | |
406 // Send a test message from peer 1 to peer 2 | |
407 const char kTestMessage[] = "Hello, World!"; | |
408 outgoing_stream->Write(kTestMessage, strlen(kTestMessage)); | |
409 | |
410 // Wait for peer 2 to receive messages | |
411 ASSERT_TRUE_WAIT(to_session->has_data(), kTimeoutMs); | |
412 | |
413 ReliableQuicStream* incoming = to_session->incoming_stream(); | |
414 ASSERT_TRUE(incoming); | |
415 EXPECT_TRUE(to_session->HasOpenDynamicStreams()); | |
416 | |
417 EXPECT_EQ(0, to_session->data().compare(kTestMessage)); | |
418 | |
419 // Send a test message from peer 2 to peer 1 | |
420 const char kTestResponse[] = "Response"; | |
421 incoming->Write(kTestResponse, strlen(kTestResponse)); | |
422 | |
423 // Wait for peer 1 to receive messages | |
424 ASSERT_TRUE_WAIT(from_session->has_data(), kTimeoutMs); | |
425 | |
426 EXPECT_EQ(0, from_session->data().compare(kTestResponse)); | |
427 } | |
428 | |
429 // Client and server should disconnect when proof verification fails | |
430 void QuicSessionTest::TestDisconnectAfterFailedHandshake() { | |
431 EXPECT_TRUE_WAIT(!client_peer_->connection()->connected(), kTimeoutMs); | |
432 EXPECT_TRUE_WAIT(!server_peer_->connection()->connected(), kTimeoutMs); | |
433 | |
434 EXPECT_FALSE(client_peer_->IsEncryptionEstablished()); | |
435 EXPECT_FALSE(client_peer_->IsCryptoHandshakeConfirmed()); | |
436 | |
437 EXPECT_FALSE(server_peer_->IsEncryptionEstablished()); | |
438 EXPECT_FALSE(server_peer_->IsCryptoHandshakeConfirmed()); | |
439 } | |
440 | |
441 // Establish encryption then send message from client to server | |
442 TEST_F(QuicSessionTest, ClientToServer) { | |
443 CreateClientAndServerSessions(); | |
444 StartHandshake(true, true); | |
445 TestStreamConnection(client_peer_.get(), server_peer_.get()); | |
446 } | |
447 | |
448 // Establish encryption then send message from server to client | |
449 TEST_F(QuicSessionTest, ServerToClient) { | |
450 CreateClientAndServerSessions(); | |
451 StartHandshake(true, true); | |
452 TestStreamConnection(server_peer_.get(), client_peer_.get()); | |
453 } | |
454 | |
455 // Make client fail to verify proof from server | |
456 TEST_F(QuicSessionTest, ClientRejection) { | |
457 CreateClientAndServerSessions(); | |
458 StartHandshake(false, true); | |
459 TestDisconnectAfterFailedHandshake(); | |
460 } | |
461 | |
462 // Make server fail to give proof to client | |
463 TEST_F(QuicSessionTest, ServerRejection) { | |
464 CreateClientAndServerSessions(); | |
465 StartHandshake(true, false); | |
466 TestDisconnectAfterFailedHandshake(); | |
467 } | |
468 | |
469 // Test that data streams are not created before handshake | |
470 TEST_F(QuicSessionTest, CannotCreateDataStreamBeforeHandshake) { | |
471 CreateClientAndServerSessions(); | |
472 EXPECT_EQ(nullptr, server_peer_->CreateOutgoingDynamicStream(5)); | |
473 EXPECT_EQ(nullptr, client_peer_->CreateOutgoingDynamicStream(5)); | |
474 } | |
OLD | NEW |