1 // Copyright (c) 2013 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 "base/strings/string_number_conversions.h" 6 #include "net/quic/crypto/crypto_server_config.h" 7 #include "net/quic/crypto/crypto_utils.h" 8 #include "net/quic/crypto/quic_random.h" 9 #include "net/quic/test_tools/crypto_test_utils.h" 10 #include "net/quic/test_tools/mock_clock.h" 11 #include "testing/gtest/include/gtest/gtest.h" 12 13 using base::StringPiece; 14 using std::string; 15 16 namespace net { 17 namespace test { 18 19 class CryptoServerTest : public ::testing::Test { 20 public: 21 CryptoServerTest() 22 : rand_(QuicRandom::GetInstance()), 23 config_(QuicCryptoServerConfig::TESTING, rand_), 24 addr_(ParseIPLiteralToNumber("192.0.2.33", &ip_) ? 25 ip_ : IPAddressNumber(), 1) { 26 config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting()); 27 } 28 29 virtual void SetUp() { 30 scoped_ptr<CryptoHandshakeMessage> msg( 31 config_.AddDefaultConfig(rand_, &clock_, 32 QuicCryptoServerConfig::ConfigOptions())); 33 34 StringPiece orbit; 35 CHECK(msg->GetStringPiece(kORBT, &orbit)); 36 CHECK_EQ(sizeof(orbit_), orbit.size()); 37 memcpy(orbit_, orbit.data(), orbit.size()); 38 39 char public_value[32]; 40 memset(public_value, 42, sizeof(public_value)); 41 42 const string nonce_str = GenerateNonce(); 43 nonce_hex_ = "#" + base::HexEncode(nonce_str.data(), nonce_str.size()); 44 pub_hex_ = "#" + base::HexEncode(public_value, sizeof(public_value)); 45 46 CryptoHandshakeMessage client_hello = CryptoTestUtils::Message( 47 "CHLO", 48 "AEAD", "AESG", 49 "KEXS", "C255", 50 "PUBS", pub_hex_.c_str(), 51 "NONC", nonce_hex_.c_str(), 52 "$padding", static_cast<int>(kClientHelloMinimumSize), 53 NULL); 54 ShouldSucceed(client_hello); 55 // The message should be rejected because the source-address token is 56 // missing. 57 ASSERT_EQ(kREJ, out_.tag()); 58 59 StringPiece srct; 60 ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct)); 61 srct_hex_ = "#" + base::HexEncode(srct.data(), srct.size()); 62 63 StringPiece scfg; 64 ASSERT_TRUE(out_.GetStringPiece(kSCFG, &scfg)); 65 server_config_.reset(CryptoFramer::ParseMessage(scfg)); 66 67 StringPiece scid; 68 ASSERT_TRUE(server_config_->GetStringPiece(kSCID, &scid)); 69 scid_hex_ = "#" + base::HexEncode(scid.data(), scid.size()); 70 } 71 72 void ShouldSucceed(const CryptoHandshakeMessage& message) { 73 string error_details; 74 QuicErrorCode error = config_.ProcessClientHello( 75 message, QuicVersionMax(), 1 /* GUID */, addr_, 76 &clock_, rand_, ¶ms_, &out_, &error_details); 77 78 ASSERT_EQ(error, QUIC_NO_ERROR) 79 << "Message failed with error " << error_details << ": " 80 << message.DebugString(); 81 } 82 83 void ShouldFailMentioning(const char* error_substr, 84 const CryptoHandshakeMessage& message) { 85 string error_details; 86 QuicErrorCode error = config_.ProcessClientHello( 87 message, QuicVersionMax(), 1 /* GUID */, addr_, 88 &clock_, rand_, ¶ms_, &out_, &error_details); 89 90 ASSERT_NE(error, QUIC_NO_ERROR) 91 << "Message didn't fail: " << message.DebugString(); 92 93 EXPECT_TRUE(error_details.find(error_substr) != string::npos) 94 << error_substr << " not in " << error_details; 95 } 96 97 CryptoHandshakeMessage InchoateClientHello(const char* message_tag, ...) { 98 va_list ap; 99 va_start(ap, message_tag); 100 101 CryptoHandshakeMessage message = 102 CryptoTestUtils::BuildMessage(message_tag, ap); 103 va_end(ap); 104 105 message.SetStringPiece(kPAD, string(kClientHelloMinimumSize, '-')); 106 return message; 107 } 108 109 string GenerateNonce() { 110 string nonce; 111 CryptoUtils::GenerateNonce( 112 clock_.WallNow(), rand_, 113 StringPiece(reinterpret_cast<const char*>(orbit_), sizeof(orbit_)), 114 &nonce); 115 return nonce; 116 } 117 118 protected: 119 QuicRandom* const rand_; 120 MockClock clock_; 121 QuicCryptoServerConfig config_; 122 QuicCryptoNegotiatedParameters params_; 123 CryptoHandshakeMessage out_; 124 IPAddressNumber ip_; 125 IPEndPoint addr_; 126 uint8 orbit_[kOrbitSize]; 127 128 // These strings contain hex escaped values from the server suitable for 129 // passing to |InchoateClientHello| when constructing client hello messages. 130 string nonce_hex_, pub_hex_, srct_hex_, scid_hex_; 131 scoped_ptr<CryptoHandshakeMessage> server_config_; 132 }; 133 134 TEST_F(CryptoServerTest, BadSNI) { 135 static const char* kBadSNIs[] = { 136 "", 137 "foo", 138 "#00", 139 "#ff00", 140 "127.0.0.1", 141 "ffee::1", 142 }; 143 144 for (size_t i = 0; i < arraysize(kBadSNIs); i++) { 145 ShouldFailMentioning("SNI", InchoateClientHello( 146 "CHLO", 147 "SNI", kBadSNIs[i], 148 NULL)); 149 } 150 } 151 152 // TODO(rtenneti): Enable the DefaultCert test after implementing ProofSource. 153 TEST_F(CryptoServerTest, DISABLED_DefaultCert) { 154 // Check that the server replies with a default certificate when no SNI is 155 // specified. 156 ShouldSucceed(InchoateClientHello( 157 "CHLO", 158 "AEAD", "AESG", 159 "KEXS", "C255", 160 "SCID", scid_hex_.c_str(), 161 "#004b5453", srct_hex_.c_str(), 162 "PUBS", pub_hex_.c_str(), 163 "NONC", nonce_hex_.c_str(), 164 "$padding", static_cast<int>(kClientHelloMinimumSize), 165 "PDMD", "X509", 166 NULL)); 167 168 StringPiece cert, proof; 169 EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert)); 170 EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof)); 171 EXPECT_NE(0u, cert.size()); 172 EXPECT_NE(0u, proof.size()); 173 } 174 175 TEST_F(CryptoServerTest, TooSmall) { 176 ShouldFailMentioning("too small", CryptoTestUtils::Message( 177 "CHLO", 178 NULL)); 179 } 180 181 TEST_F(CryptoServerTest, BadSourceAddressToken) { 182 // Invalid source-address tokens should be ignored. 183 static const char* kBadSourceAddressTokens[] = { 184 "", 185 "foo", 186 "#0000", 187 "#0000000000000000000000000000000000000000", 188 }; 189 190 for (size_t i = 0; i < arraysize(kBadSourceAddressTokens); i++) { 191 ShouldSucceed(InchoateClientHello( 192 "CHLO", 193 "STK", kBadSourceAddressTokens[i], 194 NULL)); 195 } 196 } 197 198 TEST_F(CryptoServerTest, BadClientNonce) { 199 // Invalid nonces should be ignored. 200 static const char* kBadNonces[] = { 201 "", 202 "#0000", 203 "#0000000000000000000000000000000000000000", 204 }; 205 206 for (size_t i = 0; i < arraysize(kBadNonces); i++) { 207 ShouldSucceed(InchoateClientHello( 208 "CHLO", 209 "NONC", kBadNonces[i], 210 NULL)); 211 } 212 } 213 214 TEST_F(CryptoServerTest, ReplayProtection) { 215 // This tests that disabling replay protection works. 216 CryptoHandshakeMessage msg = CryptoTestUtils::Message( 217 "CHLO", 218 "AEAD", "AESG", 219 "KEXS", "C255", 220 "SCID", scid_hex_.c_str(), 221 "#004b5453", srct_hex_.c_str(), 222 "PUBS", pub_hex_.c_str(), 223 "NONC", nonce_hex_.c_str(), 224 "$padding", static_cast<int>(kClientHelloMinimumSize), 225 NULL); 226 ShouldSucceed(msg); 227 // The message should be rejected because the strike-register is still 228 // quiescent. 229 ASSERT_EQ(kREJ, out_.tag()); 230 231 config_.set_replay_protection(false); 232 233 ShouldSucceed(msg); 234 // The message should be accepted now. 235 ASSERT_EQ(kSHLO, out_.tag()); 236 237 ShouldSucceed(msg); 238 // The message should accepted twice when replay protection is off. 239 ASSERT_EQ(kSHLO, out_.tag()); 240 } 241 242 class CryptoServerTestNoConfig : public CryptoServerTest { 243 public: 244 virtual void SetUp() { 245 // Deliberately don't add a config so that we can test this situation. 246 } 247 }; 248 249 TEST_F(CryptoServerTestNoConfig, DontCrash) { 250 ShouldFailMentioning("No config", InchoateClientHello( 251 "CHLO", 252 NULL)); 253 } 254 255 } // namespace test 256 } // namespace net 257