1 // Copyright 2015 The Weave 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 "src/privet/security_manager.h"
6
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>
16 #include <base/logging.h>
17 #include <base/rand_util.h>
18 #include <base/strings/string_number_conversions.h>
19 #include <base/strings/string_util.h>
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22 #include <weave/provider/test/fake_task_runner.h>
23 #include <weave/provider/test/mock_config_store.h>
24
25 #include "src/config.h"
26 #include "src/data_encoding.h"
27 #include "src/privet/auth_manager.h"
28 #include "src/privet/mock_delegates.h"
29 #include "src/privet/openssl_utils.h"
30 #include "src/test/mock_clock.h"
31 #include "third_party/chromium/crypto/p224_spake.h"
32
33 using testing::_;
34 using testing::Eq;
35 using testing::Return;
36
37 namespace weave {
38 namespace privet {
39
40 namespace {
41
IsBase64Char(char c)42 bool IsBase64Char(char c) {
43 return isalnum(c) || (c == '+') || (c == '/') || (c == '=');
44 }
45
IsBase64(const std::string & text)46 bool IsBase64(const std::string& text) {
47 return !text.empty() &&
48 !std::any_of(text.begin(), text.end(),
49 std::not1(std::ref(IsBase64Char)));
50 }
51
52 class MockPairingCallbacks {
53 public:
54 MOCK_METHOD3(OnPairingStart,
55 void(const std::string& session_id,
56 PairingType pairing_type,
57 const std::vector<uint8_t>& code));
58 MOCK_METHOD1(OnPairingEnd, void(const std::string& session_id));
59 };
60
61 } // namespace
62
63 class SecurityManagerConfigStore : public provider::test::MockConfigStore {
64 public:
SecurityManagerConfigStore()65 SecurityManagerConfigStore() {
66 EXPECT_CALL(*this, LoadDefaults(_))
67 .WillRepeatedly(testing::Invoke([](Settings* settings) {
68 settings->embedded_code = "1234";
69 settings->pairing_modes = {PairingType::kEmbeddedCode};
70 settings->client_id = "TEST_CLIENT_ID";
71 settings->client_secret = "TEST_CLIENT_SECRET";
72 settings->api_key = "TEST_API_KEY";
73 settings->oem_name = "TEST_OEM";
74 settings->model_name = "TEST_MODEL";
75 settings->model_id = "ABCDE";
76 settings->name = "TEST_NAME";
77 return true;
78 }));
79 }
80 };
81
82 class SecurityManagerTest : public testing::Test {
83 protected:
SetUp()84 void SetUp() override {
85 EXPECT_CALL(clock_, Now())
86 .WillRepeatedly(Return(base::Time::FromTimeT(1410000000)));
87 }
88
PairAndAuthenticate(std::string * fingerprint,std::string * signature)89 void PairAndAuthenticate(std::string* fingerprint, std::string* signature) {
90 std::string session_id;
91 std::string device_commitment_base64;
92
93 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
94 CryptoType::kSpake_p224, &session_id,
95 &device_commitment_base64, nullptr));
96 EXPECT_FALSE(session_id.empty());
97 EXPECT_FALSE(device_commitment_base64.empty());
98
99 crypto::P224EncryptedKeyExchange spake{
100 crypto::P224EncryptedKeyExchange::kPeerTypeClient, "1234"};
101
102 std::string client_commitment_base64{Base64Encode(spake.GetNextMessage())};
103
104 EXPECT_TRUE(security_.ConfirmPairing(session_id, client_commitment_base64,
105 fingerprint, signature, nullptr));
106 EXPECT_TRUE(IsBase64(*fingerprint));
107 EXPECT_TRUE(IsBase64(*signature));
108
109 std::vector<uint8_t> device_commitment;
110 ASSERT_TRUE(Base64Decode(device_commitment_base64, &device_commitment));
111 spake.ProcessMessage(
112 std::string(device_commitment.begin(), device_commitment.end()));
113
114 const std::string& key = spake.GetUnverifiedKey();
115 std::vector<uint8_t> auth_code{
116 HmacSha256(std::vector<uint8_t>{key.begin(), key.end()},
117 std::vector<uint8_t>{session_id.begin(), session_id.end()})};
118
119 std::string auth_code_base64{Base64Encode(auth_code)};
120
121 std::string token;
122 AuthScope scope;
123 base::TimeDelta ttl;
124 EXPECT_TRUE(security_.CreateAccessToken(AuthType::kPairing,
125 auth_code_base64, AuthScope::kOwner,
126 &token, &scope, &ttl, nullptr));
127 EXPECT_EQ(AuthScope::kOwner, scope);
128 EXPECT_EQ(base::TimeDelta::FromHours(1), ttl);
129
130 UserInfo info;
131 EXPECT_TRUE(security_.ParseAccessToken(token, &info, nullptr));
132 EXPECT_EQ(AuthScope::kOwner, info.scope());
133 }
134
135 const base::Time time_ = base::Time::FromTimeT(1410000000);
136 provider::test::FakeTaskRunner task_runner_;
137 test::MockClock clock_;
138 SecurityManagerConfigStore config_store_;
139 Config config_{&config_store_};
140 AuthManager auth_manager_{
141 {22, 47, 23, 77, 42, 98, 96, 25, 83, 16, 9, 14, 91, 44, 15, 75, 60, 62,
142 10, 18, 82, 35, 88, 100, 30, 45, 7, 46, 67, 84, 58, 85},
143 {
144 59, 47, 77, 247, 129, 187, 188, 158, 172, 105, 246, 93, 102, 83, 8,
145 138, 176, 141, 37, 63, 223, 40, 153, 121, 134, 23, 120, 106, 24, 205,
146 7, 135,
147 },
148 {22, 47, 23, 77, 42, 98, 96, 25, 83, 16, 9, 14, 91, 44, 15, 75, 60, 62,
149 10, 18, 82, 35, 88, 100, 30, 45, 7, 46, 67, 84, 58, 85},
150 &clock_};
151
152 SecurityManager security_{&config_, &auth_manager_, &task_runner_};
153 };
154
TEST_F(SecurityManagerTest,AccessToken)155 TEST_F(SecurityManagerTest, AccessToken) {
156 AuthScope scopes[] = {
157 AuthScope::kViewer, AuthScope::kUser, AuthScope::kManager,
158 AuthScope::kOwner,
159 };
160 for (size_t i = 1; i < 100; ++i) {
161 const AuthScope requested_scope = scopes[i % arraysize(scopes)];
162 std::string token;
163 AuthScope scope;
164 base::TimeDelta ttl;
165 EXPECT_TRUE(security_.CreateAccessToken(AuthType::kAnonymous, "",
166 requested_scope, &token, &scope,
167 &ttl, nullptr));
168 EXPECT_EQ(requested_scope, scope);
169 EXPECT_EQ(base::TimeDelta::FromHours(1), ttl);
170
171 UserInfo info;
172 EXPECT_TRUE(security_.ParseAccessToken(token, &info, nullptr));
173 EXPECT_EQ(requested_scope, info.scope());
174 EXPECT_EQ(TestUserId{std::to_string(i)}, info.id());
175 }
176 }
177
TEST_F(SecurityManagerTest,PairingNoSession)178 TEST_F(SecurityManagerTest, PairingNoSession) {
179 std::string fingerprint;
180 std::string signature;
181 ErrorPtr error;
182 ASSERT_FALSE(
183 security_.ConfirmPairing("123", "345", &fingerprint, &signature, &error));
184 EXPECT_EQ("unknownSession", error->GetCode());
185 }
186
TEST_F(SecurityManagerTest,Pairing)187 TEST_F(SecurityManagerTest, Pairing) {
188 std::vector<std::pair<std::string, std::string> > fingerprints(2);
189 for (auto& it : fingerprints) {
190 PairAndAuthenticate(&it.first, &it.second);
191 }
192
193 // Same certificate.
194 EXPECT_EQ(fingerprints.front().first, fingerprints.back().first);
195
196 // Signed with different secret.
197 EXPECT_NE(fingerprints.front().second, fingerprints.back().second);
198 }
199
TEST_F(SecurityManagerTest,NotifiesListenersOfSessionStartAndEnd)200 TEST_F(SecurityManagerTest, NotifiesListenersOfSessionStartAndEnd) {
201 testing::StrictMock<MockPairingCallbacks> callbacks;
202 security_.RegisterPairingListeners(
203 base::Bind(&MockPairingCallbacks::OnPairingStart,
204 base::Unretained(&callbacks)),
205 base::Bind(&MockPairingCallbacks::OnPairingEnd,
206 base::Unretained(&callbacks)));
207 for (auto commitment_suffix :
208 std::vector<std::string>{"", "invalid_commitment"}) {
209 // StartPairing should notify us that a new session has begun.
210 std::string session_id;
211 std::string device_commitment;
212 EXPECT_CALL(callbacks, OnPairingStart(_, PairingType::kEmbeddedCode, _));
213 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
214 CryptoType::kSpake_p224, &session_id,
215 &device_commitment, nullptr));
216 EXPECT_FALSE(session_id.empty());
217 EXPECT_FALSE(device_commitment.empty());
218 testing::Mock::VerifyAndClearExpectations(&callbacks);
219
220 // ConfirmPairing should notify us that the session has ended.
221 EXPECT_CALL(callbacks, OnPairingEnd(Eq(session_id)));
222 crypto::P224EncryptedKeyExchange spake{
223 crypto::P224EncryptedKeyExchange::kPeerTypeServer, "1234"};
224 std::string client_commitment = Base64Encode(spake.GetNextMessage());
225 std::string fingerprint, signature;
226 // Regardless of whether the commitment is valid or not, we should get a
227 // callback indicating that the pairing session is gone.
228 security_.ConfirmPairing(session_id, client_commitment + commitment_suffix,
229 &fingerprint, &signature, nullptr);
230 testing::Mock::VerifyAndClearExpectations(&callbacks);
231 }
232 }
233
TEST_F(SecurityManagerTest,CancelPairing)234 TEST_F(SecurityManagerTest, CancelPairing) {
235 testing::StrictMock<MockPairingCallbacks> callbacks;
236 security_.RegisterPairingListeners(
237 base::Bind(&MockPairingCallbacks::OnPairingStart,
238 base::Unretained(&callbacks)),
239 base::Bind(&MockPairingCallbacks::OnPairingEnd,
240 base::Unretained(&callbacks)));
241 std::string session_id;
242 std::string device_commitment;
243 EXPECT_CALL(callbacks, OnPairingStart(_, PairingType::kEmbeddedCode, _));
244 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
245 CryptoType::kSpake_p224, &session_id,
246 &device_commitment, nullptr));
247 EXPECT_CALL(callbacks, OnPairingEnd(Eq(session_id)));
248 EXPECT_TRUE(security_.CancelPairing(session_id, nullptr));
249 }
250
TEST_F(SecurityManagerTest,ThrottlePairing)251 TEST_F(SecurityManagerTest, ThrottlePairing) {
252 auto pair = [this]() {
253 std::string session_id;
254 std::string device_commitment;
255 ErrorPtr error;
256 bool result = security_.StartPairing(PairingType::kEmbeddedCode,
257 CryptoType::kSpake_p224, &session_id,
258 &device_commitment, &error);
259 EXPECT_TRUE(result || error->GetCode() == "deviceBusy");
260 return result;
261 };
262
263 EXPECT_TRUE(pair());
264 EXPECT_TRUE(pair());
265 EXPECT_TRUE(pair());
266 EXPECT_FALSE(pair());
267 EXPECT_GT(security_.block_pairing_until_, clock_.Now());
268 EXPECT_LE(security_.block_pairing_until_,
269 clock_.Now() + base::TimeDelta::FromMinutes(15));
270
271 // Wait timeout.
272 security_.block_pairing_until_ =
273 clock_.Now() - base::TimeDelta::FromMinutes(1);
274
275 // Allow exactly one attempt.
276 EXPECT_TRUE(pair());
277 EXPECT_FALSE(pair());
278
279 // Wait timeout.
280 security_.block_pairing_until_ =
281 clock_.Now() - base::TimeDelta::FromMinutes(1);
282
283 // Completely unblock by successfully pairing.
284 std::string fingerprint;
285 std::string signature;
286 PairAndAuthenticate(&fingerprint, &signature);
287
288 // Now we have 3 attempts again.
289 EXPECT_TRUE(pair());
290 EXPECT_TRUE(pair());
291 EXPECT_TRUE(pair());
292 EXPECT_FALSE(pair());
293 }
294
TEST_F(SecurityManagerTest,DontBlockForCanceledSessions)295 TEST_F(SecurityManagerTest, DontBlockForCanceledSessions) {
296 for (int i = 0; i < 20; ++i) {
297 std::string session_id;
298 std::string device_commitment;
299 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
300 CryptoType::kSpake_p224, &session_id,
301 &device_commitment, nullptr));
302 EXPECT_TRUE(security_.CancelPairing(session_id, nullptr));
303 }
304 }
305
306 } // namespace privet
307 } // namespace weave
308