blob: 72297102197157088aba89bf63662f19c4e4cb93 [file] [log] [blame]
Vitaly Buka4615e0d2015-10-14 15:35:12 -07001// Copyright 2015 The Weave Authors. All rights reserved.
Vitaly Buka7ce499f2015-06-09 08:04:11 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Stefan Sauer2d16dfa2015-09-25 17:08:35 +02005#include "src/privet/security_manager.h"
Vitaly Buka7ce499f2015-06-09 08:04:11 -07006
7#include <algorithm>
8#include <cctype>
9#include <functional>
10#include <memory>
11#include <string>
12#include <utility>
13#include <vector>
14
15#include <base/bind.h>
Vitaly Buka7ce499f2015-06-09 08:04:11 -070016#include <base/logging.h>
Vitaly Buka7ce499f2015-06-09 08:04:11 -070017#include <base/rand_util.h>
18#include <base/strings/string_number_conversions.h>
19#include <base/strings/string_util.h>
Vitaly Buka7ce499f2015-06-09 08:04:11 -070020#include <gmock/gmock.h>
21#include <gtest/gtest.h>
Vitaly Buka727f3e62015-09-25 17:33:43 -070022#include <weave/provider/test/fake_task_runner.h>
Vitaly Buka7ce499f2015-06-09 08:04:11 -070023
Stefan Sauer2d16dfa2015-09-25 17:08:35 +020024#include "src/data_encoding.h"
25#include "src/privet/openssl_utils.h"
Vitaly Buka9e5b6832015-10-14 15:57:14 -070026#include "third_party/chromium/crypto/p224_spake.h"
Vitaly Buka7ce499f2015-06-09 08:04:11 -070027
28using testing::Eq;
29using testing::_;
30
Vitaly Bukab6f015a2015-07-09 14:59:23 -070031namespace weave {
32namespace privet {
Vitaly Buka7ce499f2015-06-09 08:04:11 -070033
34namespace {
35
36bool IsBase64Char(char c) {
37 return isalnum(c) || (c == '+') || (c == '/') || (c == '=');
38}
39
40bool IsBase64(const std::string& text) {
41 return !text.empty() &&
42 !std::any_of(text.begin(), text.end(),
43 std::not1(std::ref(IsBase64Char)));
44}
45
46class MockPairingCallbacks {
47 public:
48 MOCK_METHOD3(OnPairingStart,
49 void(const std::string& session_id,
50 PairingType pairing_type,
51 const std::vector<uint8_t>& code));
52 MOCK_METHOD1(OnPairingEnd, void(const std::string& session_id));
53};
54
Vitaly Buka7ce499f2015-06-09 08:04:11 -070055} // namespace
56
57class SecurityManagerTest : public testing::Test {
58 public:
59 void SetUp() override {
Vitaly Bukaa04405e2015-08-13 18:28:14 -070060 std::vector<uint8_t> fingerprint;
Vitaly Buka8589b052015-09-29 00:46:14 -070061 fingerprint.resize(32);
Vitaly Buka7ce499f2015-06-09 08:04:11 -070062 base::RandBytes(fingerprint.data(), fingerprint.size());
63 security_.SetCertificateFingerprint(fingerprint);
Vitaly Buka7ce499f2015-06-09 08:04:11 -070064 }
65
Vitaly Buka7ce499f2015-06-09 08:04:11 -070066 protected:
67 void PairAndAuthenticate(std::string* fingerprint, std::string* signature) {
68 std::string session_id;
69 std::string device_commitment_base64;
70
71 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
72 CryptoType::kSpake_p224, &session_id,
73 &device_commitment_base64, nullptr));
74 EXPECT_FALSE(session_id.empty());
75 EXPECT_FALSE(device_commitment_base64.empty());
76
77 crypto::P224EncryptedKeyExchange spake{
78 crypto::P224EncryptedKeyExchange::kPeerTypeClient, "1234"};
79
Vitaly Buka7d556392015-08-13 20:06:48 -070080 std::string client_commitment_base64{Base64Encode(spake.GetNextMessage())};
Vitaly Buka7ce499f2015-06-09 08:04:11 -070081
82 EXPECT_TRUE(security_.ConfirmPairing(session_id, client_commitment_base64,
83 fingerprint, signature, nullptr));
84 EXPECT_TRUE(IsBase64(*fingerprint));
85 EXPECT_TRUE(IsBase64(*signature));
86
Vitaly Bukaa04405e2015-08-13 18:28:14 -070087 std::vector<uint8_t> device_commitment;
Vitaly Buka7d556392015-08-13 20:06:48 -070088 ASSERT_TRUE(Base64Decode(device_commitment_base64, &device_commitment));
Vitaly Buka7ce499f2015-06-09 08:04:11 -070089 spake.ProcessMessage(
Vitaly Buka24d6fd52015-08-13 23:22:48 -070090 std::string(device_commitment.begin(), device_commitment.end()));
Vitaly Buka7ce499f2015-06-09 08:04:11 -070091
Vitaly Bukaa04405e2015-08-13 18:28:14 -070092 const std::string& key = spake.GetUnverifiedKey();
93 std::vector<uint8_t> auth_code{
94 HmacSha256(std::vector<uint8_t>{key.begin(), key.end()},
95 std::vector<uint8_t>{session_id.begin(), session_id.end()})};
Vitaly Buka7ce499f2015-06-09 08:04:11 -070096
Vitaly Buka7d556392015-08-13 20:06:48 -070097 std::string auth_code_base64{Base64Encode(auth_code)};
Vitaly Buka7ce499f2015-06-09 08:04:11 -070098
99 EXPECT_TRUE(security_.IsValidPairingCode(auth_code_base64));
100 }
101
102 const base::Time time_ = base::Time::FromTimeT(1410000000);
Vitaly Buka727f3e62015-09-25 17:33:43 -0700103 provider::test::FakeTaskRunner task_runner_;
Vitaly Buka8589b052015-09-29 00:46:14 -0700104 SecurityManager security_{{},
105 {PairingType::kEmbeddedCode},
Vitaly Buka8cb91d72015-08-16 00:40:51 -0700106 "1234",
107 false,
108 &task_runner_};
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700109};
110
Vitaly Buka8589b052015-09-29 00:46:14 -0700111TEST_F(SecurityManagerTest, RandomSecret) {
112 EXPECT_GE(security_.GetSecret().size(), 32);
113 EXPECT_TRUE(IsBase64(security_.GetSecret()));
114}
115
116TEST_F(SecurityManagerTest, DifferentSecret) {
117 SecurityManager security{{}, {}, "", false, &task_runner_};
118 EXPECT_NE(security_.GetSecret(), security.GetSecret());
119}
120
121TEST_F(SecurityManagerTest, ExternalSecret) {
122 const std::string kSecret = "T1SDv9CVGNO82zHKeRrUSzpAzjb1hmRyzXGotsn1gcU=";
123 SecurityManager security{kSecret, {}, "", false, &task_runner_};
124 EXPECT_EQ(kSecret, security.GetSecret());
125}
126
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700127TEST_F(SecurityManagerTest, IsBase64) {
128 EXPECT_TRUE(IsBase64(
129 security_.CreateAccessToken(UserInfo{AuthScope::kUser, 7}, time_)));
130}
131
132TEST_F(SecurityManagerTest, CreateSameToken) {
133 EXPECT_EQ(
134 security_.CreateAccessToken(UserInfo{AuthScope::kViewer, 555}, time_),
135 security_.CreateAccessToken(UserInfo{AuthScope::kViewer, 555}, time_));
136}
137
138TEST_F(SecurityManagerTest, CreateTokenDifferentScope) {
139 EXPECT_NE(
140 security_.CreateAccessToken(UserInfo{AuthScope::kViewer, 456}, time_),
141 security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456}, time_));
142}
143
144TEST_F(SecurityManagerTest, CreateTokenDifferentUser) {
145 EXPECT_NE(
146 security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456}, time_),
147 security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 789}, time_));
148}
149
150TEST_F(SecurityManagerTest, CreateTokenDifferentTime) {
151 EXPECT_NE(
152 security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 567}, time_),
153 security_.CreateAccessToken(UserInfo{AuthScope::kOwner, 567},
154 base::Time::FromTimeT(1400000000)));
155}
156
157TEST_F(SecurityManagerTest, CreateTokenDifferentInstance) {
158 EXPECT_NE(security_.CreateAccessToken(UserInfo{AuthScope::kUser, 123}, time_),
Vitaly Buka8589b052015-09-29 00:46:14 -0700159 SecurityManager({}, {}, "", false, &task_runner_)
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700160 .CreateAccessToken(UserInfo{AuthScope::kUser, 123}, time_));
161}
162
163TEST_F(SecurityManagerTest, ParseAccessToken) {
164 // Multiple attempts with random secrets.
165 for (size_t i = 0; i < 1000; ++i) {
Vitaly Buka8589b052015-09-29 00:46:14 -0700166 SecurityManager security{{}, {}, "", false, &task_runner_};
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700167
168 std::string token =
169 security.CreateAccessToken(UserInfo{AuthScope::kUser, 5}, time_);
170 base::Time time2;
171 EXPECT_EQ(AuthScope::kUser,
172 security.ParseAccessToken(token, &time2).scope());
173 EXPECT_EQ(5u, security.ParseAccessToken(token, &time2).user_id());
174 // Token timestamp resolution is one second.
175 EXPECT_GE(1, std::abs((time_ - time2).InSeconds()));
176 }
177}
178
179TEST_F(SecurityManagerTest, PairingNoSession) {
180 std::string fingerprint;
181 std::string signature;
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700182 ErrorPtr error;
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700183 ASSERT_FALSE(
184 security_.ConfirmPairing("123", "345", &fingerprint, &signature, &error));
185 EXPECT_EQ("unknownSession", error->GetCode());
186}
187
188TEST_F(SecurityManagerTest, Pairing) {
189 std::vector<std::pair<std::string, std::string> > fingerprints(2);
190 for (auto& it : fingerprints) {
191 PairAndAuthenticate(&it.first, &it.second);
192 }
193
194 // Same certificate.
195 EXPECT_EQ(fingerprints.front().first, fingerprints.back().first);
196
197 // Signed with different secret.
198 EXPECT_NE(fingerprints.front().second, fingerprints.back().second);
199}
200
201TEST_F(SecurityManagerTest, NotifiesListenersOfSessionStartAndEnd) {
202 testing::StrictMock<MockPairingCallbacks> callbacks;
203 security_.RegisterPairingListeners(
204 base::Bind(&MockPairingCallbacks::OnPairingStart,
205 base::Unretained(&callbacks)),
206 base::Bind(&MockPairingCallbacks::OnPairingEnd,
207 base::Unretained(&callbacks)));
208 for (auto commitment_suffix :
209 std::vector<std::string>{"", "invalid_commitment"}) {
210 // StartPairing should notify us that a new session has begun.
211 std::string session_id;
212 std::string device_commitment;
213 EXPECT_CALL(callbacks, OnPairingStart(_, PairingType::kEmbeddedCode, _));
214 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
215 CryptoType::kSpake_p224, &session_id,
216 &device_commitment, nullptr));
217 EXPECT_FALSE(session_id.empty());
218 EXPECT_FALSE(device_commitment.empty());
219 testing::Mock::VerifyAndClearExpectations(&callbacks);
220
221 // ConfirmPairing should notify us that the session has ended.
222 EXPECT_CALL(callbacks, OnPairingEnd(Eq(session_id)));
223 crypto::P224EncryptedKeyExchange spake{
224 crypto::P224EncryptedKeyExchange::kPeerTypeServer, "1234"};
Vitaly Buka7d556392015-08-13 20:06:48 -0700225 std::string client_commitment = Base64Encode(spake.GetNextMessage());
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700226 std::string fingerprint, signature;
227 // Regardless of whether the commitment is valid or not, we should get a
228 // callback indicating that the pairing session is gone.
229 security_.ConfirmPairing(session_id, client_commitment + commitment_suffix,
230 &fingerprint, &signature, nullptr);
231 testing::Mock::VerifyAndClearExpectations(&callbacks);
232 }
233}
234
235TEST_F(SecurityManagerTest, CancelPairing) {
236 testing::StrictMock<MockPairingCallbacks> callbacks;
237 security_.RegisterPairingListeners(
238 base::Bind(&MockPairingCallbacks::OnPairingStart,
239 base::Unretained(&callbacks)),
240 base::Bind(&MockPairingCallbacks::OnPairingEnd,
241 base::Unretained(&callbacks)));
242 std::string session_id;
243 std::string device_commitment;
244 EXPECT_CALL(callbacks, OnPairingStart(_, PairingType::kEmbeddedCode, _));
245 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
246 CryptoType::kSpake_p224, &session_id,
247 &device_commitment, nullptr));
248 EXPECT_CALL(callbacks, OnPairingEnd(Eq(session_id)));
249 EXPECT_TRUE(security_.CancelPairing(session_id, nullptr));
250}
251
252TEST_F(SecurityManagerTest, ThrottlePairing) {
253 auto pair = [this]() {
254 std::string session_id;
255 std::string device_commitment;
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700256 ErrorPtr error;
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700257 bool result = security_.StartPairing(PairingType::kEmbeddedCode,
258 CryptoType::kSpake_p224, &session_id,
259 &device_commitment, &error);
260 EXPECT_TRUE(result || error->GetCode() == "deviceBusy");
261 return result;
262 };
263
264 EXPECT_TRUE(pair());
265 EXPECT_TRUE(pair());
266 EXPECT_TRUE(pair());
267 EXPECT_FALSE(pair());
268 EXPECT_GT(security_.block_pairing_until_, base::Time::Now());
269 EXPECT_LE(security_.block_pairing_until_,
270 base::Time::Now() + base::TimeDelta::FromMinutes(15));
271
272 // Wait timeout.
273 security_.block_pairing_until_ =
274 base::Time::Now() - base::TimeDelta::FromMinutes(1);
275
276 // Allow exactly one attempt.
277 EXPECT_TRUE(pair());
278 EXPECT_FALSE(pair());
279
280 // Wait timeout.
281 security_.block_pairing_until_ =
282 base::Time::Now() - base::TimeDelta::FromMinutes(1);
283
284 // Completely unblock by successfully pairing.
285 std::string fingerprint;
286 std::string signature;
287 PairAndAuthenticate(&fingerprint, &signature);
288
289 // Now we have 3 attempts again.
290 EXPECT_TRUE(pair());
291 EXPECT_TRUE(pair());
292 EXPECT_TRUE(pair());
293 EXPECT_FALSE(pair());
294}
295
296TEST_F(SecurityManagerTest, DontBlockForCanceledSessions) {
297 for (int i = 0; i < 20; ++i) {
298 std::string session_id;
299 std::string device_commitment;
300 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
301 CryptoType::kSpake_p224, &session_id,
302 &device_commitment, nullptr));
303 EXPECT_TRUE(security_.CancelPairing(session_id, nullptr));
304 }
305}
306
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700307} // namespace privet
308} // namespace weave