// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/internal_auth.h" #include <algorithm> #include "base/lazy_instance.h" #include "base/message_loop/message_loop.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" namespace chrome { class InternalAuthTest : public ::testing::Test { public: InternalAuthTest() { long_string_ = "seed"; for (int i = 20; i--;) long_string_ += long_string_; } virtual ~InternalAuthTest() {} virtual void SetUp() { } virtual void TearDown() { } base::MessageLoop message_loop_; std::string long_string_; }; TEST_F(InternalAuthTest, BasicGeneration) { std::map<std::string, std::string> map; map["key"] = "value"; std::string token = InternalAuthGeneration::GeneratePassport( "zapata", map); ASSERT_GT(token.size(), 10u); // short token is insecure. map["key2"] = "value2"; token = InternalAuthGeneration::GeneratePassport("zapata", map); ASSERT_GT(token.size(), 10u); } TEST_F(InternalAuthTest, DoubleGeneration) { std::map<std::string, std::string> map; map["key"] = "value"; std::string token1 = InternalAuthGeneration::GeneratePassport( "zapata", map); ASSERT_GT(token1.size(), 10u); std::string token2 = InternalAuthGeneration::GeneratePassport( "zapata", map); ASSERT_GT(token2.size(), 10u); // tokens are different even if credentials coincide. ASSERT_NE(token1, token2); } TEST_F(InternalAuthTest, BadGeneration) { std::map<std::string, std::string> map; map["key"] = "value"; // Trying huge domain. std::string token = InternalAuthGeneration::GeneratePassport( long_string_, map); ASSERT_TRUE(token.empty()); ASSERT_FALSE(InternalAuthVerification::VerifyPassport( token, long_string_, map)); // Trying empty domain. token = InternalAuthGeneration::GeneratePassport(std::string(), map); ASSERT_TRUE(token.empty()); ASSERT_FALSE( InternalAuthVerification::VerifyPassport(token, std::string(), map)); std::string dummy("abcdefghij"); for (size_t i = 1000; i--;) { std::string key = dummy; std::next_permutation(dummy.begin(), dummy.end()); std::string value = dummy; std::next_permutation(dummy.begin(), dummy.end()); map[key] = value; } // Trying huge var=value map. token = InternalAuthGeneration::GeneratePassport("zapata", map); ASSERT_TRUE(token.empty()); ASSERT_FALSE(InternalAuthVerification::VerifyPassport(token, "zapata", map)); map.clear(); map[std::string()] = "value"; // Trying empty key. token = InternalAuthGeneration::GeneratePassport("zapata", map); ASSERT_TRUE(token.empty()); ASSERT_FALSE(InternalAuthVerification::VerifyPassport(token, "zapata", map)); } TEST_F(InternalAuthTest, BasicVerification) { std::map<std::string, std::string> map; map["key"] = "value"; std::string token = InternalAuthGeneration::GeneratePassport("zapata", map); ASSERT_GT(token.size(), 10u); ASSERT_TRUE(InternalAuthVerification::VerifyPassport(token, "zapata", map)); // Passport can not be reused. for (int i = 1000; i--;) { ASSERT_FALSE(InternalAuthVerification::VerifyPassport( token, "zapata", map)); } } TEST_F(InternalAuthTest, BruteForce) { std::map<std::string, std::string> map; map["key"] = "value"; std::string token = InternalAuthGeneration::GeneratePassport("zapata", map); ASSERT_GT(token.size(), 10u); // Trying bruteforce. std::string dummy = token; for (size_t i = 100; i--;) { std::next_permutation(dummy.begin(), dummy.end()); ASSERT_FALSE(InternalAuthVerification::VerifyPassport( dummy, "zapata", map)); } dummy = token; for (size_t i = 100; i--;) { std::next_permutation(dummy.begin(), dummy.begin() + dummy.size() / 2); ASSERT_FALSE(InternalAuthVerification::VerifyPassport( dummy, "zapata", map)); } // We brute forced just too little, so original token must not expire yet. ASSERT_TRUE(InternalAuthVerification::VerifyPassport(token, "zapata", map)); } TEST_F(InternalAuthTest, ExpirationAndBruteForce) { int kCustomVerificationWindow = 2; InternalAuthVerification::set_verification_window_seconds( kCustomVerificationWindow); std::map<std::string, std::string> map; map["key"] = "value"; std::string token = InternalAuthGeneration::GeneratePassport("zapata", map); ASSERT_GT(token.size(), 10u); // We want to test token expiration, so we need to wait some amount of time, // so we are brute-forcing during this time. base::Time timestamp = base::Time::Now(); std::string dummy1 = token; std::string dummy2 = token; for (;;) { for (size_t i = 100; i--;) { std::next_permutation(dummy1.begin(), dummy1.end()); ASSERT_FALSE(InternalAuthVerification::VerifyPassport( dummy1, "zapata", map)); } for (size_t i = 100; i--;) { std::next_permutation(dummy2.begin(), dummy2.begin() + dummy2.size() / 2); ASSERT_FALSE(InternalAuthVerification::VerifyPassport( dummy2, "zapata", map)); } if (base::Time::Now() - timestamp > base::TimeDelta::FromSeconds( kCustomVerificationWindow + 1)) { break; } } ASSERT_FALSE(InternalAuthVerification::VerifyPassport(token, "zapata", map)); // Reset verification window to default. InternalAuthVerification::set_verification_window_seconds(0); } TEST_F(InternalAuthTest, ChangeKey) { std::map<std::string, std::string> map; map["key"] = "value"; std::string token = InternalAuthGeneration::GeneratePassport("zapata", map); ASSERT_GT(token.size(), 10u); InternalAuthGeneration::GenerateNewKey(); // Passport should survive key change. ASSERT_TRUE(InternalAuthVerification::VerifyPassport(token, "zapata", map)); token = InternalAuthGeneration::GeneratePassport("zapata", map); ASSERT_GT(token.size(), 10u); for (int i = 20; i--;) InternalAuthGeneration::GenerateNewKey(); // Passport should not survive series of key changes. ASSERT_FALSE(InternalAuthVerification::VerifyPassport(token, "zapata", map)); } } // namespace chrome