Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(662)

Side by Side Diff: net/quic/test_tools/crypto_test_utils.cc

Issue 992733002: Remove //net (except for Android test stuff) and sdch (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "net/quic/test_tools/crypto_test_utils.h"
6
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_test_utils.h"
21 #include "net/quic/test_tools/simple_quic_framer.h"
22
23 using base::StringPiece;
24 using std::make_pair;
25 using std::pair;
26 using std::string;
27 using std::vector;
28
29 namespace net {
30 namespace test {
31
32 namespace {
33
34 const char kServerHostname[] = "test.example.com";
35 const uint16 kServerPort = 80;
36
37 // CryptoFramerVisitor is a framer visitor that records handshake messages.
38 class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
39 public:
40 CryptoFramerVisitor()
41 : error_(false) {
42 }
43
44 void OnError(CryptoFramer* framer) override { error_ = true; }
45
46 void OnHandshakeMessage(const CryptoHandshakeMessage& message) override {
47 messages_.push_back(message);
48 }
49
50 bool error() const {
51 return error_;
52 }
53
54 const vector<CryptoHandshakeMessage>& messages() const {
55 return messages_;
56 }
57
58 private:
59 bool error_;
60 vector<CryptoHandshakeMessage> messages_;
61 };
62
63 // MovePackets parses crypto handshake messages from packet number
64 // |*inout_packet_index| through to the last packet (or until a packet fails to
65 // decrypt) and has |dest_stream| process them. |*inout_packet_index| is updated
66 // with an index one greater than the last packet processed.
67 void MovePackets(PacketSavingConnection* source_conn,
68 size_t *inout_packet_index,
69 QuicCryptoStream* dest_stream,
70 PacketSavingConnection* dest_conn) {
71 SimpleQuicFramer framer(source_conn->supported_versions());
72 CryptoFramer crypto_framer;
73 CryptoFramerVisitor crypto_visitor;
74
75 // In order to properly test the code we need to perform encryption and
76 // decryption so that the crypters latch when expected. The crypters are in
77 // |dest_conn|, but we don't want to try and use them there. Instead we swap
78 // them into |framer|, perform the decryption with them, and then swap them
79 // back.
80 QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer());
81
82 crypto_framer.set_visitor(&crypto_visitor);
83
84 size_t index = *inout_packet_index;
85 for (; index < source_conn->encrypted_packets_.size(); index++) {
86 if (!framer.ProcessPacket(*source_conn->encrypted_packets_[index])) {
87 // The framer will be unable to decrypt forward-secure packets sent after
88 // the handshake is complete. Don't treat them as handshake packets.
89 break;
90 }
91
92 for (vector<QuicStreamFrame>::const_iterator
93 i = framer.stream_frames().begin();
94 i != framer.stream_frames().end(); ++i) {
95 scoped_ptr<string> frame_data(i->GetDataAsString());
96 ASSERT_TRUE(crypto_framer.ProcessInput(*frame_data));
97 ASSERT_FALSE(crypto_visitor.error());
98 }
99 }
100 *inout_packet_index = index;
101
102 QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer());
103
104 ASSERT_EQ(0u, crypto_framer.InputBytesRemaining());
105
106 for (vector<CryptoHandshakeMessage>::const_iterator
107 i = crypto_visitor.messages().begin();
108 i != crypto_visitor.messages().end(); ++i) {
109 dest_stream->OnHandshakeMessage(*i);
110 }
111 }
112
113 // HexChar parses |c| as a hex character. If valid, it sets |*value| to the
114 // value of the hex character and returns true. Otherwise it returns false.
115 bool HexChar(char c, uint8* value) {
116 if (c >= '0' && c <= '9') {
117 *value = c - '0';
118 return true;
119 }
120 if (c >= 'a' && c <= 'f') {
121 *value = c - 'a' + 10;
122 return true;
123 }
124 if (c >= 'A' && c <= 'F') {
125 *value = c - 'A' + 10;
126 return true;
127 }
128 return false;
129 }
130
131 // A ChannelIDSource that works in asynchronous mode unless the |callback|
132 // argument to GetChannelIDKey is nullptr.
133 class AsyncTestChannelIDSource : public ChannelIDSource,
134 public CryptoTestUtils::CallbackSource {
135 public:
136 // Takes ownership of |sync_source|, a synchronous ChannelIDSource.
137 explicit AsyncTestChannelIDSource(ChannelIDSource* sync_source)
138 : sync_source_(sync_source) {}
139 ~AsyncTestChannelIDSource() override {}
140
141 // ChannelIDSource implementation.
142 QuicAsyncStatus GetChannelIDKey(const string& hostname,
143 scoped_ptr<ChannelIDKey>* channel_id_key,
144 ChannelIDSourceCallback* callback) override {
145 // Synchronous mode.
146 if (!callback) {
147 return sync_source_->GetChannelIDKey(hostname, channel_id_key, nullptr);
148 }
149
150 // Asynchronous mode.
151 QuicAsyncStatus status =
152 sync_source_->GetChannelIDKey(hostname, &channel_id_key_, nullptr);
153 if (status != QUIC_SUCCESS) {
154 return QUIC_FAILURE;
155 }
156 callback_.reset(callback);
157 return QUIC_PENDING;
158 }
159
160 // CallbackSource implementation.
161 void RunPendingCallbacks() override {
162 if (callback_.get()) {
163 callback_->Run(&channel_id_key_);
164 callback_.reset();
165 }
166 }
167
168 private:
169 scoped_ptr<ChannelIDSource> sync_source_;
170 scoped_ptr<ChannelIDSourceCallback> callback_;
171 scoped_ptr<ChannelIDKey> channel_id_key_;
172 };
173
174 } // anonymous namespace
175
176 CryptoTestUtils::FakeClientOptions::FakeClientOptions()
177 : dont_verify_certs(false),
178 channel_id_enabled(false),
179 channel_id_source_async(false) {
180 }
181
182 // static
183 int CryptoTestUtils::HandshakeWithFakeServer(
184 PacketSavingConnection* client_conn,
185 QuicCryptoClientStream* client) {
186 PacketSavingConnection* server_conn =
187 new PacketSavingConnection(true, client_conn->supported_versions());
188 TestSession server_session(server_conn, DefaultQuicConfig());
189 server_session.InitializeSession();
190 QuicCryptoServerConfig crypto_config(QuicCryptoServerConfig::TESTING,
191 QuicRandom::GetInstance());
192
193 SetupCryptoServerConfigForTest(
194 server_session.connection()->clock(),
195 server_session.connection()->random_generator(),
196 server_session.config(), &crypto_config);
197
198 QuicCryptoServerStream server(crypto_config, &server_session);
199 server_session.SetCryptoStream(&server);
200
201 // The client's handshake must have been started already.
202 CHECK_NE(0u, client_conn->encrypted_packets_.size());
203
204 CommunicateHandshakeMessages(client_conn, client, server_conn, &server);
205
206 CompareClientAndServerKeys(client, &server);
207
208 return client->num_sent_client_hellos();
209 }
210
211 // static
212 int CryptoTestUtils::HandshakeWithFakeClient(
213 PacketSavingConnection* server_conn,
214 QuicCryptoServerStream* server,
215 const FakeClientOptions& options) {
216 PacketSavingConnection* client_conn = new PacketSavingConnection(false);
217 // Advance the time, because timers do not like uninitialized times.
218 client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1));
219 TestClientSession client_session(client_conn, DefaultQuicConfig());
220 QuicCryptoClientConfig crypto_config;
221
222 if (!options.dont_verify_certs) {
223 // TODO(wtc): replace this with ProofVerifierForTesting() when we have
224 // a working ProofSourceForTesting().
225 crypto_config.SetProofVerifier(FakeProofVerifierForTesting());
226 }
227 bool is_https = false;
228 AsyncTestChannelIDSource* async_channel_id_source = nullptr;
229 if (options.channel_id_enabled) {
230 is_https = true;
231
232 ChannelIDSource* source = ChannelIDSourceForTesting();
233 if (options.channel_id_source_async) {
234 async_channel_id_source = new AsyncTestChannelIDSource(source);
235 source = async_channel_id_source;
236 }
237 crypto_config.SetChannelIDSource(source);
238 }
239 QuicServerId server_id(kServerHostname, kServerPort, is_https,
240 PRIVACY_MODE_DISABLED);
241 QuicCryptoClientStream client(server_id, &client_session,
242 ProofVerifyContextForTesting(),
243 &crypto_config);
244 client_session.SetCryptoStream(&client);
245
246 client.CryptoConnect();
247 CHECK_EQ(1u, client_conn->encrypted_packets_.size());
248
249 CommunicateHandshakeMessagesAndRunCallbacks(
250 client_conn, &client, server_conn, server, async_channel_id_source);
251
252 CompareClientAndServerKeys(&client, server);
253
254 if (options.channel_id_enabled) {
255 scoped_ptr<ChannelIDKey> channel_id_key;
256 QuicAsyncStatus status = crypto_config.channel_id_source()->GetChannelIDKey(
257 kServerHostname, &channel_id_key, nullptr);
258 EXPECT_EQ(QUIC_SUCCESS, status);
259 EXPECT_EQ(channel_id_key->SerializeKey(),
260 server->crypto_negotiated_params().channel_id);
261 EXPECT_EQ(options.channel_id_source_async,
262 client.WasChannelIDSourceCallbackRun());
263 }
264
265 return client.num_sent_client_hellos();
266 }
267
268 // static
269 void CryptoTestUtils::SetupCryptoServerConfigForTest(
270 const QuicClock* clock,
271 QuicRandom* rand,
272 QuicConfig* config,
273 QuicCryptoServerConfig* crypto_config) {
274 QuicCryptoServerConfig::ConfigOptions options;
275 options.channel_id_enabled = true;
276 scoped_ptr<CryptoHandshakeMessage> scfg(
277 crypto_config->AddDefaultConfig(rand, clock, options));
278 }
279
280 // static
281 void CryptoTestUtils::CommunicateHandshakeMessages(
282 PacketSavingConnection* a_conn,
283 QuicCryptoStream* a,
284 PacketSavingConnection* b_conn,
285 QuicCryptoStream* b) {
286 CommunicateHandshakeMessagesAndRunCallbacks(a_conn, a, b_conn, b, nullptr);
287 }
288
289 // static
290 void CryptoTestUtils::CommunicateHandshakeMessagesAndRunCallbacks(
291 PacketSavingConnection* a_conn,
292 QuicCryptoStream* a,
293 PacketSavingConnection* b_conn,
294 QuicCryptoStream* b,
295 CallbackSource* callback_source) {
296 size_t a_i = 0, b_i = 0;
297 while (!a->handshake_confirmed()) {
298 ASSERT_GT(a_conn->encrypted_packets_.size(), a_i);
299 VLOG(1) << "Processing " << a_conn->encrypted_packets_.size() - a_i
300 << " packets a->b";
301 MovePackets(a_conn, &a_i, b, b_conn);
302 if (callback_source) {
303 callback_source->RunPendingCallbacks();
304 }
305
306 ASSERT_GT(b_conn->encrypted_packets_.size(), b_i);
307 VLOG(1) << "Processing " << b_conn->encrypted_packets_.size() - b_i
308 << " packets b->a";
309 MovePackets(b_conn, &b_i, a, a_conn);
310 if (callback_source) {
311 callback_source->RunPendingCallbacks();
312 }
313 }
314 }
315
316 // static
317 pair<size_t, size_t> CryptoTestUtils::AdvanceHandshake(
318 PacketSavingConnection* a_conn,
319 QuicCryptoStream* a,
320 size_t a_i,
321 PacketSavingConnection* b_conn,
322 QuicCryptoStream* b,
323 size_t b_i) {
324 VLOG(1) << "Processing " << a_conn->encrypted_packets_.size() - a_i
325 << " packets a->b";
326 MovePackets(a_conn, &a_i, b, b_conn);
327
328 VLOG(1) << "Processing " << b_conn->encrypted_packets_.size() - b_i
329 << " packets b->a";
330 if (b_conn->encrypted_packets_.size() - b_i == 2) {
331 VLOG(1) << "here";
332 }
333 MovePackets(b_conn, &b_i, a, a_conn);
334
335 return std::make_pair(a_i, b_i);
336 }
337
338 // static
339 string CryptoTestUtils::GetValueForTag(const CryptoHandshakeMessage& message,
340 QuicTag tag) {
341 QuicTagValueMap::const_iterator it = message.tag_value_map().find(tag);
342 if (it == message.tag_value_map().end()) {
343 return string();
344 }
345 return it->second;
346 }
347
348 class MockCommonCertSets : public CommonCertSets {
349 public:
350 MockCommonCertSets(StringPiece cert, uint64 hash, uint32 index)
351 : cert_(cert.as_string()),
352 hash_(hash),
353 index_(index) {
354 }
355
356 StringPiece GetCommonHashes() const override {
357 CHECK(false) << "not implemented";
358 return StringPiece();
359 }
360
361 StringPiece GetCert(uint64 hash, uint32 index) const override {
362 if (hash == hash_ && index == index_) {
363 return cert_;
364 }
365 return StringPiece();
366 }
367
368 bool MatchCert(StringPiece cert,
369 StringPiece common_set_hashes,
370 uint64* out_hash,
371 uint32* out_index) const override {
372 if (cert != cert_) {
373 return false;
374 }
375
376 if (common_set_hashes.size() % sizeof(uint64) != 0) {
377 return false;
378 }
379 bool client_has_set = false;
380 for (size_t i = 0; i < common_set_hashes.size(); i += sizeof(uint64)) {
381 uint64 hash;
382 memcpy(&hash, common_set_hashes.data() + i, sizeof(hash));
383 if (hash == hash_) {
384 client_has_set = true;
385 break;
386 }
387 }
388
389 if (!client_has_set) {
390 return false;
391 }
392
393 *out_hash = hash_;
394 *out_index = index_;
395 return true;
396 }
397
398 private:
399 const string cert_;
400 const uint64 hash_;
401 const uint32 index_;
402 };
403
404 CommonCertSets* CryptoTestUtils::MockCommonCertSets(StringPiece cert,
405 uint64 hash,
406 uint32 index) {
407 return new class MockCommonCertSets(cert, hash, index);
408 }
409
410 void CryptoTestUtils::CompareClientAndServerKeys(
411 QuicCryptoClientStream* client,
412 QuicCryptoServerStream* server) {
413 const QuicEncrypter* client_encrypter(
414 client->session()->connection()->encrypter(ENCRYPTION_INITIAL));
415 const QuicDecrypter* client_decrypter(
416 client->session()->connection()->decrypter());
417 const QuicEncrypter* client_forward_secure_encrypter(
418 client->session()->connection()->encrypter(ENCRYPTION_FORWARD_SECURE));
419 const QuicDecrypter* client_forward_secure_decrypter(
420 client->session()->connection()->alternative_decrypter());
421 const QuicEncrypter* server_encrypter(
422 server->session()->connection()->encrypter(ENCRYPTION_INITIAL));
423 const QuicDecrypter* server_decrypter(
424 server->session()->connection()->decrypter());
425 const QuicEncrypter* server_forward_secure_encrypter(
426 server->session()->connection()->encrypter(ENCRYPTION_FORWARD_SECURE));
427 const QuicDecrypter* server_forward_secure_decrypter(
428 server->session()->connection()->alternative_decrypter());
429
430 StringPiece client_encrypter_key = client_encrypter->GetKey();
431 StringPiece client_encrypter_iv = client_encrypter->GetNoncePrefix();
432 StringPiece client_decrypter_key = client_decrypter->GetKey();
433 StringPiece client_decrypter_iv = client_decrypter->GetNoncePrefix();
434 StringPiece client_forward_secure_encrypter_key =
435 client_forward_secure_encrypter->GetKey();
436 StringPiece client_forward_secure_encrypter_iv =
437 client_forward_secure_encrypter->GetNoncePrefix();
438 StringPiece client_forward_secure_decrypter_key =
439 client_forward_secure_decrypter->GetKey();
440 StringPiece client_forward_secure_decrypter_iv =
441 client_forward_secure_decrypter->GetNoncePrefix();
442 StringPiece server_encrypter_key = server_encrypter->GetKey();
443 StringPiece server_encrypter_iv = server_encrypter->GetNoncePrefix();
444 StringPiece server_decrypter_key = server_decrypter->GetKey();
445 StringPiece server_decrypter_iv = server_decrypter->GetNoncePrefix();
446 StringPiece server_forward_secure_encrypter_key =
447 server_forward_secure_encrypter->GetKey();
448 StringPiece server_forward_secure_encrypter_iv =
449 server_forward_secure_encrypter->GetNoncePrefix();
450 StringPiece server_forward_secure_decrypter_key =
451 server_forward_secure_decrypter->GetKey();
452 StringPiece server_forward_secure_decrypter_iv =
453 server_forward_secure_decrypter->GetNoncePrefix();
454
455 StringPiece client_subkey_secret =
456 client->crypto_negotiated_params().subkey_secret;
457 StringPiece server_subkey_secret =
458 server->crypto_negotiated_params().subkey_secret;
459
460
461 const char kSampleLabel[] = "label";
462 const char kSampleContext[] = "context";
463 const size_t kSampleOutputLength = 32;
464 string client_key_extraction;
465 string server_key_extraction;
466 EXPECT_TRUE(client->ExportKeyingMaterial(kSampleLabel,
467 kSampleContext,
468 kSampleOutputLength,
469 &client_key_extraction));
470 EXPECT_TRUE(server->ExportKeyingMaterial(kSampleLabel,
471 kSampleContext,
472 kSampleOutputLength,
473 &server_key_extraction));
474
475 CompareCharArraysWithHexError("client write key",
476 client_encrypter_key.data(),
477 client_encrypter_key.length(),
478 server_decrypter_key.data(),
479 server_decrypter_key.length());
480 CompareCharArraysWithHexError("client write IV",
481 client_encrypter_iv.data(),
482 client_encrypter_iv.length(),
483 server_decrypter_iv.data(),
484 server_decrypter_iv.length());
485 CompareCharArraysWithHexError("server write key",
486 server_encrypter_key.data(),
487 server_encrypter_key.length(),
488 client_decrypter_key.data(),
489 client_decrypter_key.length());
490 CompareCharArraysWithHexError("server write IV",
491 server_encrypter_iv.data(),
492 server_encrypter_iv.length(),
493 client_decrypter_iv.data(),
494 client_decrypter_iv.length());
495 CompareCharArraysWithHexError("client forward secure write key",
496 client_forward_secure_encrypter_key.data(),
497 client_forward_secure_encrypter_key.length(),
498 server_forward_secure_decrypter_key.data(),
499 server_forward_secure_decrypter_key.length());
500 CompareCharArraysWithHexError("client forward secure write IV",
501 client_forward_secure_encrypter_iv.data(),
502 client_forward_secure_encrypter_iv.length(),
503 server_forward_secure_decrypter_iv.data(),
504 server_forward_secure_decrypter_iv.length());
505 CompareCharArraysWithHexError("server forward secure write key",
506 server_forward_secure_encrypter_key.data(),
507 server_forward_secure_encrypter_key.length(),
508 client_forward_secure_decrypter_key.data(),
509 client_forward_secure_decrypter_key.length());
510 CompareCharArraysWithHexError("server forward secure write IV",
511 server_forward_secure_encrypter_iv.data(),
512 server_forward_secure_encrypter_iv.length(),
513 client_forward_secure_decrypter_iv.data(),
514 client_forward_secure_decrypter_iv.length());
515 CompareCharArraysWithHexError("subkey secret",
516 client_subkey_secret.data(),
517 client_subkey_secret.length(),
518 server_subkey_secret.data(),
519 server_subkey_secret.length());
520 CompareCharArraysWithHexError("sample key extraction",
521 client_key_extraction.data(),
522 client_key_extraction.length(),
523 server_key_extraction.data(),
524 server_key_extraction.length());
525 }
526
527 // static
528 QuicTag CryptoTestUtils::ParseTag(const char* tagstr) {
529 const size_t len = strlen(tagstr);
530 CHECK_NE(0u, len);
531
532 QuicTag tag = 0;
533
534 if (tagstr[0] == '#') {
535 CHECK_EQ(static_cast<size_t>(1 + 2*4), len);
536 tagstr++;
537
538 for (size_t i = 0; i < 8; i++) {
539 tag <<= 4;
540
541 uint8 v = 0;
542 CHECK(HexChar(tagstr[i], &v));
543 tag |= v;
544 }
545
546 return tag;
547 }
548
549 CHECK_LE(len, 4u);
550 for (size_t i = 0; i < 4; i++) {
551 tag >>= 8;
552 if (i < len) {
553 tag |= static_cast<uint32>(tagstr[i]) << 24;
554 }
555 }
556
557 return tag;
558 }
559
560 // static
561 CryptoHandshakeMessage CryptoTestUtils::Message(const char* message_tag, ...) {
562 va_list ap;
563 va_start(ap, message_tag);
564
565 CryptoHandshakeMessage message = BuildMessage(message_tag, ap);
566 va_end(ap);
567 return message;
568 }
569
570 // static
571 CryptoHandshakeMessage CryptoTestUtils::BuildMessage(const char* message_tag,
572 va_list ap) {
573 CryptoHandshakeMessage msg;
574 msg.set_tag(ParseTag(message_tag));
575
576 for (;;) {
577 const char* tagstr = va_arg(ap, const char*);
578 if (tagstr == nullptr) {
579 break;
580 }
581
582 if (tagstr[0] == '$') {
583 // Special value.
584 const char* const special = tagstr + 1;
585 if (strcmp(special, "padding") == 0) {
586 const int min_bytes = va_arg(ap, int);
587 msg.set_minimum_size(min_bytes);
588 } else {
589 CHECK(false) << "Unknown special value: " << special;
590 }
591
592 continue;
593 }
594
595 const QuicTag tag = ParseTag(tagstr);
596 const char* valuestr = va_arg(ap, const char*);
597
598 size_t len = strlen(valuestr);
599 if (len > 0 && valuestr[0] == '#') {
600 valuestr++;
601 len--;
602
603 CHECK_EQ(0u, len % 2);
604 scoped_ptr<uint8[]> buf(new uint8[len/2]);
605
606 for (size_t i = 0; i < len/2; i++) {
607 uint8 v = 0;
608 CHECK(HexChar(valuestr[i*2], &v));
609 buf[i] = v << 4;
610 CHECK(HexChar(valuestr[i*2 + 1], &v));
611 buf[i] |= v;
612 }
613
614 msg.SetStringPiece(
615 tag, StringPiece(reinterpret_cast<char*>(buf.get()), len/2));
616 continue;
617 }
618
619 msg.SetStringPiece(tag, valuestr);
620 }
621
622 // The CryptoHandshakeMessage needs to be serialized and parsed to ensure
623 // that any padding is included.
624 scoped_ptr<QuicData> bytes(CryptoFramer::ConstructHandshakeMessage(msg));
625 scoped_ptr<CryptoHandshakeMessage> parsed(
626 CryptoFramer::ParseMessage(bytes->AsStringPiece()));
627 CHECK(parsed.get());
628
629 return *parsed;
630 }
631
632 } // namespace test
633 } // namespace net
OLDNEW
« no previous file with comments | « net/quic/test_tools/crypto_test_utils.h ('k') | net/quic/test_tools/crypto_test_utils_chromium.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698