• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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