// 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