blob: 7085e830106c9a6353c0942570adb9052aeb7667 [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"
Vitaly Bukaf08caeb2015-12-02 13:47:48 -080025#include "src/privet/auth_manager.h"
Stefan Sauer2d16dfa2015-09-25 17:08:35 +020026#include "src/privet/openssl_utils.h"
Vitaly Buka41aa8092015-12-09 20:04:34 -080027#include "src/test/mock_clock.h"
Vitaly Buka9e5b6832015-10-14 15:57:14 -070028#include "third_party/chromium/crypto/p224_spake.h"
Vitaly Buka7ce499f2015-06-09 08:04:11 -070029
Vitaly Buka7ce499f2015-06-09 08:04:11 -070030using testing::_;
Vitaly Buka41aa8092015-12-09 20:04:34 -080031using testing::Eq;
32using testing::Return;
Vitaly Buka7ce499f2015-06-09 08:04:11 -070033
Vitaly Bukab6f015a2015-07-09 14:59:23 -070034namespace weave {
35namespace privet {
Vitaly Buka7ce499f2015-06-09 08:04:11 -070036
37namespace {
38
39bool IsBase64Char(char c) {
40 return isalnum(c) || (c == '+') || (c == '/') || (c == '=');
41}
42
43bool IsBase64(const std::string& text) {
44 return !text.empty() &&
45 !std::any_of(text.begin(), text.end(),
46 std::not1(std::ref(IsBase64Char)));
47}
48
49class MockPairingCallbacks {
50 public:
51 MOCK_METHOD3(OnPairingStart,
52 void(const std::string& session_id,
53 PairingType pairing_type,
54 const std::vector<uint8_t>& code));
55 MOCK_METHOD1(OnPairingEnd, void(const std::string& session_id));
56};
57
Vitaly Buka7ce499f2015-06-09 08:04:11 -070058} // namespace
59
60class SecurityManagerTest : public testing::Test {
Vitaly Buka7ce499f2015-06-09 08:04:11 -070061 protected:
Vitaly Buka41aa8092015-12-09 20:04:34 -080062 void SetUp() override {
63 EXPECT_CALL(clock_, Now())
64 .WillRepeatedly(Return(base::Time::FromTimeT(1410000000)));
65 }
66
Vitaly Buka7ce499f2015-06-09 08:04:11 -070067 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
Vitaly Bukafd2ef682015-12-17 20:57:01 -080099 std::string token;
100 AuthScope scope;
101 base::TimeDelta ttl;
102 EXPECT_TRUE(security_.CreateAccessToken(AuthType::kPairing,
103 auth_code_base64, AuthScope::kOwner,
104 &token, &scope, &ttl, nullptr));
105 EXPECT_EQ(AuthScope::kOwner, scope);
106 EXPECT_EQ(base::TimeDelta::FromHours(1), ttl);
107
108 UserInfo info;
109 EXPECT_TRUE(security_.ParseAccessToken(token, &info, nullptr));
110 EXPECT_EQ(AuthScope::kOwner, info.scope());
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700111 }
112
113 const base::Time time_ = base::Time::FromTimeT(1410000000);
Vitaly Buka727f3e62015-09-25 17:33:43 -0700114 provider::test::FakeTaskRunner task_runner_;
Vitaly Buka41aa8092015-12-09 20:04:34 -0800115 test::MockClock clock_;
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800116 AuthManager auth_manager_{
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800117 {22, 47, 23, 77, 42, 98, 96, 25, 83, 16, 9, 14, 91, 44, 15, 75, 60, 62,
118 10, 18, 82, 35, 88, 100, 30, 45, 7, 46, 67, 84, 58, 85},
Vitaly Buka229113e2015-12-13 23:12:42 -0800119 {
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800120 59, 47, 77, 247, 129, 187, 188, 158, 172, 105, 246, 93, 102, 83, 8,
121 138, 176, 141, 37, 63, 223, 40, 153, 121, 134, 23, 120, 106, 24, 205,
122 7, 135,
Vitaly Buka229113e2015-12-13 23:12:42 -0800123 },
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800124 {22, 47, 23, 77, 42, 98, 96, 25, 83, 16, 9, 14, 91, 44, 15, 75, 60, 62,
125 10, 18, 82, 35, 88, 100, 30, 45, 7, 46, 67, 84, 58, 85},
Vitaly Buka41aa8092015-12-09 20:04:34 -0800126 &clock_};
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800127 SecurityManager security_{&auth_manager_,
Vitaly Buka8589b052015-09-29 00:46:14 -0700128 {PairingType::kEmbeddedCode},
Vitaly Buka8cb91d72015-08-16 00:40:51 -0700129 "1234",
130 false,
131 &task_runner_};
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700132};
133
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800134TEST_F(SecurityManagerTest, CreateAccessToken) {
Vitaly Bukafd2ef682015-12-17 20:57:01 -0800135 std::string token;
136 AuthScope scope;
137 base::TimeDelta ttl;
138 EXPECT_TRUE(security_.CreateAccessToken(AuthType::kAnonymous, "",
139 AuthScope::kUser, &token, &scope,
140 &ttl, nullptr));
141 EXPECT_EQ("Cfz+qcndz8/Mo3ytgYlD7zn8qImkkdPsJVUNBmSOiXwyOjE6MTQxMDAwMzYwMA==",
142 token);
143 EXPECT_EQ(AuthScope::kUser, scope);
144 EXPECT_EQ(base::TimeDelta::FromHours(1), ttl);
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700145}
146
147TEST_F(SecurityManagerTest, ParseAccessToken) {
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800148 UserInfo info;
149 EXPECT_TRUE(security_.ParseAccessToken(
150 "MMe7FE+EMyG4OnD2457dF5Nqh9Uiaq2iRWRzkSOW+SAzOjk6MTQxMDAwMDkwMA==", &info,
151 nullptr));
152 EXPECT_EQ(AuthScope::kManager, info.scope());
153 EXPECT_EQ(9u, info.user_id());
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700154}
155
156TEST_F(SecurityManagerTest, PairingNoSession) {
157 std::string fingerprint;
158 std::string signature;
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700159 ErrorPtr error;
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700160 ASSERT_FALSE(
161 security_.ConfirmPairing("123", "345", &fingerprint, &signature, &error));
162 EXPECT_EQ("unknownSession", error->GetCode());
163}
164
165TEST_F(SecurityManagerTest, Pairing) {
166 std::vector<std::pair<std::string, std::string> > fingerprints(2);
167 for (auto& it : fingerprints) {
168 PairAndAuthenticate(&it.first, &it.second);
169 }
170
171 // Same certificate.
172 EXPECT_EQ(fingerprints.front().first, fingerprints.back().first);
173
174 // Signed with different secret.
175 EXPECT_NE(fingerprints.front().second, fingerprints.back().second);
176}
177
178TEST_F(SecurityManagerTest, NotifiesListenersOfSessionStartAndEnd) {
179 testing::StrictMock<MockPairingCallbacks> callbacks;
180 security_.RegisterPairingListeners(
181 base::Bind(&MockPairingCallbacks::OnPairingStart,
182 base::Unretained(&callbacks)),
183 base::Bind(&MockPairingCallbacks::OnPairingEnd,
184 base::Unretained(&callbacks)));
185 for (auto commitment_suffix :
186 std::vector<std::string>{"", "invalid_commitment"}) {
187 // StartPairing should notify us that a new session has begun.
188 std::string session_id;
189 std::string device_commitment;
190 EXPECT_CALL(callbacks, OnPairingStart(_, PairingType::kEmbeddedCode, _));
191 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
192 CryptoType::kSpake_p224, &session_id,
193 &device_commitment, nullptr));
194 EXPECT_FALSE(session_id.empty());
195 EXPECT_FALSE(device_commitment.empty());
196 testing::Mock::VerifyAndClearExpectations(&callbacks);
197
198 // ConfirmPairing should notify us that the session has ended.
199 EXPECT_CALL(callbacks, OnPairingEnd(Eq(session_id)));
200 crypto::P224EncryptedKeyExchange spake{
201 crypto::P224EncryptedKeyExchange::kPeerTypeServer, "1234"};
Vitaly Buka7d556392015-08-13 20:06:48 -0700202 std::string client_commitment = Base64Encode(spake.GetNextMessage());
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700203 std::string fingerprint, signature;
204 // Regardless of whether the commitment is valid or not, we should get a
205 // callback indicating that the pairing session is gone.
206 security_.ConfirmPairing(session_id, client_commitment + commitment_suffix,
207 &fingerprint, &signature, nullptr);
208 testing::Mock::VerifyAndClearExpectations(&callbacks);
209 }
210}
211
212TEST_F(SecurityManagerTest, CancelPairing) {
213 testing::StrictMock<MockPairingCallbacks> callbacks;
214 security_.RegisterPairingListeners(
215 base::Bind(&MockPairingCallbacks::OnPairingStart,
216 base::Unretained(&callbacks)),
217 base::Bind(&MockPairingCallbacks::OnPairingEnd,
218 base::Unretained(&callbacks)));
219 std::string session_id;
220 std::string device_commitment;
221 EXPECT_CALL(callbacks, OnPairingStart(_, PairingType::kEmbeddedCode, _));
222 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
223 CryptoType::kSpake_p224, &session_id,
224 &device_commitment, nullptr));
225 EXPECT_CALL(callbacks, OnPairingEnd(Eq(session_id)));
226 EXPECT_TRUE(security_.CancelPairing(session_id, nullptr));
227}
228
229TEST_F(SecurityManagerTest, ThrottlePairing) {
230 auto pair = [this]() {
231 std::string session_id;
232 std::string device_commitment;
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700233 ErrorPtr error;
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700234 bool result = security_.StartPairing(PairingType::kEmbeddedCode,
235 CryptoType::kSpake_p224, &session_id,
236 &device_commitment, &error);
237 EXPECT_TRUE(result || error->GetCode() == "deviceBusy");
238 return result;
239 };
240
241 EXPECT_TRUE(pair());
242 EXPECT_TRUE(pair());
243 EXPECT_TRUE(pair());
244 EXPECT_FALSE(pair());
Vitaly Buka41aa8092015-12-09 20:04:34 -0800245 EXPECT_GT(security_.block_pairing_until_, clock_.Now());
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700246 EXPECT_LE(security_.block_pairing_until_,
Vitaly Buka41aa8092015-12-09 20:04:34 -0800247 clock_.Now() + base::TimeDelta::FromMinutes(15));
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700248
249 // Wait timeout.
250 security_.block_pairing_until_ =
Vitaly Buka41aa8092015-12-09 20:04:34 -0800251 clock_.Now() - base::TimeDelta::FromMinutes(1);
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700252
253 // Allow exactly one attempt.
254 EXPECT_TRUE(pair());
255 EXPECT_FALSE(pair());
256
257 // Wait timeout.
258 security_.block_pairing_until_ =
Vitaly Buka41aa8092015-12-09 20:04:34 -0800259 clock_.Now() - base::TimeDelta::FromMinutes(1);
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700260
261 // Completely unblock by successfully pairing.
262 std::string fingerprint;
263 std::string signature;
264 PairAndAuthenticate(&fingerprint, &signature);
265
266 // Now we have 3 attempts again.
267 EXPECT_TRUE(pair());
268 EXPECT_TRUE(pair());
269 EXPECT_TRUE(pair());
270 EXPECT_FALSE(pair());
271}
272
273TEST_F(SecurityManagerTest, DontBlockForCanceledSessions) {
274 for (int i = 0; i < 20; ++i) {
275 std::string session_id;
276 std::string device_commitment;
277 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
278 CryptoType::kSpake_p224, &session_id,
279 &device_commitment, nullptr));
280 EXPECT_TRUE(security_.CancelPairing(session_id, nullptr));
281 }
282}
283
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700284} // namespace privet
285} // namespace weave