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 "chrome/browser/internal_auth.h"
6
7#include <algorithm>
8
9#include "base/lazy_instance.h"
10#include "base/message_loop/message_loop.h"
11#include "base/time/time.h"
12#include "testing/gtest/include/gtest/gtest.h"
13
14namespace chrome {
15
16class InternalAuthTest : public ::testing::Test {
17 public:
18  InternalAuthTest() {
19    long_string_ = "seed";
20    for (int i = 20; i--;)
21      long_string_ += long_string_;
22  }
23  virtual ~InternalAuthTest() {}
24
25  virtual void SetUp() {
26  }
27
28  virtual void TearDown() {
29  }
30
31  base::MessageLoop message_loop_;
32  std::string long_string_;
33};
34
35TEST_F(InternalAuthTest, BasicGeneration) {
36  std::map<std::string, std::string> map;
37  map["key"] = "value";
38  std::string token = InternalAuthGeneration::GeneratePassport(
39      "zapata", map);
40  ASSERT_GT(token.size(), 10u);  // short token is insecure.
41
42  map["key2"] = "value2";
43  token = InternalAuthGeneration::GeneratePassport("zapata", map);
44  ASSERT_GT(token.size(), 10u);
45}
46
47TEST_F(InternalAuthTest, DoubleGeneration) {
48  std::map<std::string, std::string> map;
49  map["key"] = "value";
50  std::string token1 = InternalAuthGeneration::GeneratePassport(
51      "zapata", map);
52  ASSERT_GT(token1.size(), 10u);
53
54  std::string token2 = InternalAuthGeneration::GeneratePassport(
55      "zapata", map);
56  ASSERT_GT(token2.size(), 10u);
57  // tokens are different even if credentials coincide.
58  ASSERT_NE(token1, token2);
59}
60
61TEST_F(InternalAuthTest, BadGeneration) {
62  std::map<std::string, std::string> map;
63  map["key"] = "value";
64  // Trying huge domain.
65  std::string token = InternalAuthGeneration::GeneratePassport(
66      long_string_, map);
67  ASSERT_TRUE(token.empty());
68  ASSERT_FALSE(InternalAuthVerification::VerifyPassport(
69      token, long_string_, map));
70
71  // Trying empty domain.
72  token = InternalAuthGeneration::GeneratePassport(std::string(), map);
73  ASSERT_TRUE(token.empty());
74  ASSERT_FALSE(
75      InternalAuthVerification::VerifyPassport(token, std::string(), map));
76
77  std::string dummy("abcdefghij");
78  for (size_t i = 1000; i--;) {
79    std::string key = dummy;
80    std::next_permutation(dummy.begin(), dummy.end());
81    std::string value = dummy;
82    std::next_permutation(dummy.begin(), dummy.end());
83    map[key] = value;
84  }
85  // Trying huge var=value map.
86  token = InternalAuthGeneration::GeneratePassport("zapata", map);
87  ASSERT_TRUE(token.empty());
88  ASSERT_FALSE(InternalAuthVerification::VerifyPassport(token, "zapata", map));
89
90  map.clear();
91  map[std::string()] = "value";
92  // Trying empty key.
93  token = InternalAuthGeneration::GeneratePassport("zapata", map);
94  ASSERT_TRUE(token.empty());
95  ASSERT_FALSE(InternalAuthVerification::VerifyPassport(token, "zapata", map));
96}
97
98TEST_F(InternalAuthTest, BasicVerification) {
99  std::map<std::string, std::string> map;
100  map["key"] = "value";
101  std::string token = InternalAuthGeneration::GeneratePassport("zapata", map);
102  ASSERT_GT(token.size(), 10u);
103  ASSERT_TRUE(InternalAuthVerification::VerifyPassport(token, "zapata", map));
104  // Passport can not be reused.
105  for (int i = 1000; i--;) {
106    ASSERT_FALSE(InternalAuthVerification::VerifyPassport(
107        token, "zapata", map));
108  }
109}
110
111TEST_F(InternalAuthTest, BruteForce) {
112  std::map<std::string, std::string> map;
113  map["key"] = "value";
114  std::string token = InternalAuthGeneration::GeneratePassport("zapata", map);
115  ASSERT_GT(token.size(), 10u);
116
117  // Trying bruteforce.
118  std::string dummy = token;
119  for (size_t i = 100; i--;) {
120    std::next_permutation(dummy.begin(), dummy.end());
121    ASSERT_FALSE(InternalAuthVerification::VerifyPassport(
122        dummy, "zapata", map));
123  }
124  dummy = token;
125  for (size_t i = 100; i--;) {
126    std::next_permutation(dummy.begin(), dummy.begin() + dummy.size() / 2);
127    ASSERT_FALSE(InternalAuthVerification::VerifyPassport(
128        dummy, "zapata", map));
129  }
130  // We brute forced just too little, so original token must not expire yet.
131  ASSERT_TRUE(InternalAuthVerification::VerifyPassport(token, "zapata", map));
132}
133
134TEST_F(InternalAuthTest, ExpirationAndBruteForce) {
135  int kCustomVerificationWindow = 2;
136  InternalAuthVerification::set_verification_window_seconds(
137      kCustomVerificationWindow);
138
139  std::map<std::string, std::string> map;
140  map["key"] = "value";
141  std::string token = InternalAuthGeneration::GeneratePassport("zapata", map);
142  ASSERT_GT(token.size(), 10u);
143
144  // We want to test token expiration, so we need to wait some amount of time,
145  // so we are brute-forcing during this time.
146  base::Time timestamp = base::Time::Now();
147  std::string dummy1 = token;
148  std::string dummy2 = token;
149  for (;;) {
150    for (size_t i = 100; i--;) {
151      std::next_permutation(dummy1.begin(), dummy1.end());
152      ASSERT_FALSE(InternalAuthVerification::VerifyPassport(
153          dummy1, "zapata", map));
154    }
155    for (size_t i = 100; i--;) {
156      std::next_permutation(dummy2.begin(), dummy2.begin() + dummy2.size() / 2);
157      ASSERT_FALSE(InternalAuthVerification::VerifyPassport(
158          dummy2, "zapata", map));
159    }
160    if (base::Time::Now() - timestamp > base::TimeDelta::FromSeconds(
161        kCustomVerificationWindow + 1)) {
162      break;
163    }
164  }
165  ASSERT_FALSE(InternalAuthVerification::VerifyPassport(token, "zapata", map));
166  // Reset verification window to default.
167  InternalAuthVerification::set_verification_window_seconds(0);
168}
169
170TEST_F(InternalAuthTest, ChangeKey) {
171  std::map<std::string, std::string> map;
172  map["key"] = "value";
173  std::string token = InternalAuthGeneration::GeneratePassport("zapata", map);
174  ASSERT_GT(token.size(), 10u);
175
176  InternalAuthGeneration::GenerateNewKey();
177  // Passport should survive key change.
178  ASSERT_TRUE(InternalAuthVerification::VerifyPassport(token, "zapata", map));
179
180  token = InternalAuthGeneration::GeneratePassport("zapata", map);
181  ASSERT_GT(token.size(), 10u);
182  for (int i = 20; i--;)
183    InternalAuthGeneration::GenerateNewKey();
184  // Passport should not survive series of key changes.
185  ASSERT_FALSE(InternalAuthVerification::VerifyPassport(token, "zapata", map));
186}
187
188}  // namespace chrome
189