Home | History | Annotate | Download | only in crypto
      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_, &params_, &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_, &params_, &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