blob: 43b7f0071d7bd12188fa6d499b83a61c020e6307 [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 Buka20896ab2015-12-22 15:06:10 -080023#include <weave/provider/test/mock_config_store.h>
Vitaly Buka7ce499f2015-06-09 08:04:11 -070024
Vitaly Buka20896ab2015-12-22 15:06:10 -080025#include "src/config.h"
Stefan Sauer2d16dfa2015-09-25 17:08:35 +020026#include "src/data_encoding.h"
Vitaly Bukaf08caeb2015-12-02 13:47:48 -080027#include "src/privet/auth_manager.h"
Stefan Sauer2d16dfa2015-09-25 17:08:35 +020028#include "src/privet/openssl_utils.h"
Vitaly Buka41aa8092015-12-09 20:04:34 -080029#include "src/test/mock_clock.h"
Vitaly Buka9e5b6832015-10-14 15:57:14 -070030#include "third_party/chromium/crypto/p224_spake.h"
Vitaly Buka7ce499f2015-06-09 08:04:11 -070031
Vitaly Buka7ce499f2015-06-09 08:04:11 -070032using testing::_;
Vitaly Buka41aa8092015-12-09 20:04:34 -080033using testing::Eq;
34using testing::Return;
Vitaly Buka7ce499f2015-06-09 08:04:11 -070035
Vitaly Bukab6f015a2015-07-09 14:59:23 -070036namespace weave {
37namespace privet {
Vitaly Buka7ce499f2015-06-09 08:04:11 -070038
39namespace {
40
41bool IsBase64Char(char c) {
42 return isalnum(c) || (c == '+') || (c == '/') || (c == '=');
43}
44
45bool IsBase64(const std::string& text) {
46 return !text.empty() &&
47 !std::any_of(text.begin(), text.end(),
48 std::not1(std::ref(IsBase64Char)));
49}
50
51class MockPairingCallbacks {
52 public:
53 MOCK_METHOD3(OnPairingStart,
54 void(const std::string& session_id,
55 PairingType pairing_type,
56 const std::vector<uint8_t>& code));
57 MOCK_METHOD1(OnPairingEnd, void(const std::string& session_id));
58};
59
Vitaly Buka7ce499f2015-06-09 08:04:11 -070060} // namespace
61
Vitaly Buka20896ab2015-12-22 15:06:10 -080062class SecurityManagerConfigStore : public provider::test::MockConfigStore {
63 public:
64 SecurityManagerConfigStore() {
65 EXPECT_CALL(*this, LoadDefaults(_))
66 .WillRepeatedly(testing::Invoke([](Settings* settings) {
67 settings->embedded_code = "1234";
68 settings->pairing_modes = {PairingType::kEmbeddedCode};
69 settings->client_id = "TEST_CLIENT_ID";
70 settings->client_secret = "TEST_CLIENT_SECRET";
71 settings->api_key = "TEST_API_KEY";
72 settings->oem_name = "TEST_OEM";
73 settings->model_name = "TEST_MODEL";
74 settings->model_id = "ABCDE";
75 settings->name = "TEST_NAME";
76 return true;
77 }));
78 }
79};
80
Vitaly Buka7ce499f2015-06-09 08:04:11 -070081class SecurityManagerTest : public testing::Test {
Vitaly Buka7ce499f2015-06-09 08:04:11 -070082 protected:
Vitaly Buka41aa8092015-12-09 20:04:34 -080083 void SetUp() override {
84 EXPECT_CALL(clock_, Now())
85 .WillRepeatedly(Return(base::Time::FromTimeT(1410000000)));
86 }
87
Vitaly Buka7ce499f2015-06-09 08:04:11 -070088 void PairAndAuthenticate(std::string* fingerprint, std::string* signature) {
89 std::string session_id;
90 std::string device_commitment_base64;
91
92 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
93 CryptoType::kSpake_p224, &session_id,
94 &device_commitment_base64, nullptr));
95 EXPECT_FALSE(session_id.empty());
96 EXPECT_FALSE(device_commitment_base64.empty());
97
98 crypto::P224EncryptedKeyExchange spake{
99 crypto::P224EncryptedKeyExchange::kPeerTypeClient, "1234"};
100
Vitaly Buka7d556392015-08-13 20:06:48 -0700101 std::string client_commitment_base64{Base64Encode(spake.GetNextMessage())};
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700102
103 EXPECT_TRUE(security_.ConfirmPairing(session_id, client_commitment_base64,
104 fingerprint, signature, nullptr));
105 EXPECT_TRUE(IsBase64(*fingerprint));
106 EXPECT_TRUE(IsBase64(*signature));
107
Vitaly Bukaa04405e2015-08-13 18:28:14 -0700108 std::vector<uint8_t> device_commitment;
Vitaly Buka7d556392015-08-13 20:06:48 -0700109 ASSERT_TRUE(Base64Decode(device_commitment_base64, &device_commitment));
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700110 spake.ProcessMessage(
Vitaly Buka24d6fd52015-08-13 23:22:48 -0700111 std::string(device_commitment.begin(), device_commitment.end()));
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700112
Vitaly Bukaa04405e2015-08-13 18:28:14 -0700113 const std::string& key = spake.GetUnverifiedKey();
114 std::vector<uint8_t> auth_code{
115 HmacSha256(std::vector<uint8_t>{key.begin(), key.end()},
116 std::vector<uint8_t>{session_id.begin(), session_id.end()})};
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700117
Vitaly Buka7d556392015-08-13 20:06:48 -0700118 std::string auth_code_base64{Base64Encode(auth_code)};
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700119
Vitaly Bukafd2ef682015-12-17 20:57:01 -0800120 std::string token;
121 AuthScope scope;
122 base::TimeDelta ttl;
123 EXPECT_TRUE(security_.CreateAccessToken(AuthType::kPairing,
124 auth_code_base64, AuthScope::kOwner,
125 &token, &scope, &ttl, nullptr));
126 EXPECT_EQ(AuthScope::kOwner, scope);
127 EXPECT_EQ(base::TimeDelta::FromHours(1), ttl);
128
129 UserInfo info;
130 EXPECT_TRUE(security_.ParseAccessToken(token, &info, nullptr));
131 EXPECT_EQ(AuthScope::kOwner, info.scope());
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700132 }
133
134 const base::Time time_ = base::Time::FromTimeT(1410000000);
Vitaly Buka727f3e62015-09-25 17:33:43 -0700135 provider::test::FakeTaskRunner task_runner_;
Vitaly Buka41aa8092015-12-09 20:04:34 -0800136 test::MockClock clock_;
Vitaly Buka20896ab2015-12-22 15:06:10 -0800137 SecurityManagerConfigStore config_store_;
138 Config config_{&config_store_};
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800139 AuthManager auth_manager_{
Vitaly Bukaa0a81342015-12-17 13:42:13 -0800140 {22, 47, 23, 77, 42, 98, 96, 25, 83, 16, 9, 14, 91, 44, 15, 75, 60, 62,
141 10, 18, 82, 35, 88, 100, 30, 45, 7, 46, 67, 84, 58, 85},
Vitaly Buka229113e2015-12-13 23:12:42 -0800142 {
Vitaly Bukaf08caeb2015-12-02 13:47:48 -0800143 59, 47, 77, 247, 129, 187, 188, 158, 172, 105, 246, 93, 102, 83, 8,
144 138, 176, 141, 37, 63, 223, 40, 153, 121, 134, 23, 120, 106, 24, 205,
145 7, 135,
Vitaly Buka229113e2015-12-13 23:12:42 -0800146 },
Vitaly Buka0bc02ed2015-12-18 14:13:12 -0800147 {22, 47, 23, 77, 42, 98, 96, 25, 83, 16, 9, 14, 91, 44, 15, 75, 60, 62,
148 10, 18, 82, 35, 88, 100, 30, 45, 7, 46, 67, 84, 58, 85},
Vitaly Buka41aa8092015-12-09 20:04:34 -0800149 &clock_};
Vitaly Buka20896ab2015-12-22 15:06:10 -0800150
151 SecurityManager security_{&config_, &auth_manager_, &task_runner_};
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700152};
153
Vitaly Buka66f46b82015-12-18 15:36:01 -0800154TEST_F(SecurityManagerTest, AccessToken) {
155 AuthScope scopes[] = {
156 AuthScope::kViewer, AuthScope::kUser, AuthScope::kManager,
157 AuthScope::kOwner,
158 };
159 for (size_t i = 1; i < 100; ++i) {
160 const AuthScope requested_scope = scopes[i % arraysize(scopes)];
161 std::string token;
162 AuthScope scope;
163 base::TimeDelta ttl;
164 EXPECT_TRUE(security_.CreateAccessToken(AuthType::kAnonymous, "",
165 requested_scope, &token, &scope,
166 &ttl, nullptr));
167 EXPECT_EQ(requested_scope, scope);
168 EXPECT_EQ(base::TimeDelta::FromHours(1), ttl);
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700169
Vitaly Buka66f46b82015-12-18 15:36:01 -0800170 UserInfo info;
171 EXPECT_TRUE(security_.ParseAccessToken(token, &info, nullptr));
172 EXPECT_EQ(requested_scope, info.scope());
173 EXPECT_EQ("0/" + std::to_string(i), info.user_id());
174 }
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700175}
176
177TEST_F(SecurityManagerTest, PairingNoSession) {
178 std::string fingerprint;
179 std::string signature;
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700180 ErrorPtr error;
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700181 ASSERT_FALSE(
182 security_.ConfirmPairing("123", "345", &fingerprint, &signature, &error));
183 EXPECT_EQ("unknownSession", error->GetCode());
184}
185
186TEST_F(SecurityManagerTest, Pairing) {
187 std::vector<std::pair<std::string, std::string> > fingerprints(2);
188 for (auto& it : fingerprints) {
189 PairAndAuthenticate(&it.first, &it.second);
190 }
191
192 // Same certificate.
193 EXPECT_EQ(fingerprints.front().first, fingerprints.back().first);
194
195 // Signed with different secret.
196 EXPECT_NE(fingerprints.front().second, fingerprints.back().second);
197}
198
199TEST_F(SecurityManagerTest, NotifiesListenersOfSessionStartAndEnd) {
200 testing::StrictMock<MockPairingCallbacks> callbacks;
201 security_.RegisterPairingListeners(
202 base::Bind(&MockPairingCallbacks::OnPairingStart,
203 base::Unretained(&callbacks)),
204 base::Bind(&MockPairingCallbacks::OnPairingEnd,
205 base::Unretained(&callbacks)));
206 for (auto commitment_suffix :
207 std::vector<std::string>{"", "invalid_commitment"}) {
208 // StartPairing should notify us that a new session has begun.
209 std::string session_id;
210 std::string device_commitment;
211 EXPECT_CALL(callbacks, OnPairingStart(_, PairingType::kEmbeddedCode, _));
212 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
213 CryptoType::kSpake_p224, &session_id,
214 &device_commitment, nullptr));
215 EXPECT_FALSE(session_id.empty());
216 EXPECT_FALSE(device_commitment.empty());
217 testing::Mock::VerifyAndClearExpectations(&callbacks);
218
219 // ConfirmPairing should notify us that the session has ended.
220 EXPECT_CALL(callbacks, OnPairingEnd(Eq(session_id)));
221 crypto::P224EncryptedKeyExchange spake{
222 crypto::P224EncryptedKeyExchange::kPeerTypeServer, "1234"};
Vitaly Buka7d556392015-08-13 20:06:48 -0700223 std::string client_commitment = Base64Encode(spake.GetNextMessage());
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700224 std::string fingerprint, signature;
225 // Regardless of whether the commitment is valid or not, we should get a
226 // callback indicating that the pairing session is gone.
227 security_.ConfirmPairing(session_id, client_commitment + commitment_suffix,
228 &fingerprint, &signature, nullptr);
229 testing::Mock::VerifyAndClearExpectations(&callbacks);
230 }
231}
232
233TEST_F(SecurityManagerTest, CancelPairing) {
234 testing::StrictMock<MockPairingCallbacks> callbacks;
235 security_.RegisterPairingListeners(
236 base::Bind(&MockPairingCallbacks::OnPairingStart,
237 base::Unretained(&callbacks)),
238 base::Bind(&MockPairingCallbacks::OnPairingEnd,
239 base::Unretained(&callbacks)));
240 std::string session_id;
241 std::string device_commitment;
242 EXPECT_CALL(callbacks, OnPairingStart(_, PairingType::kEmbeddedCode, _));
243 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
244 CryptoType::kSpake_p224, &session_id,
245 &device_commitment, nullptr));
246 EXPECT_CALL(callbacks, OnPairingEnd(Eq(session_id)));
247 EXPECT_TRUE(security_.CancelPairing(session_id, nullptr));
248}
249
250TEST_F(SecurityManagerTest, ThrottlePairing) {
251 auto pair = [this]() {
252 std::string session_id;
253 std::string device_commitment;
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700254 ErrorPtr error;
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700255 bool result = security_.StartPairing(PairingType::kEmbeddedCode,
256 CryptoType::kSpake_p224, &session_id,
257 &device_commitment, &error);
258 EXPECT_TRUE(result || error->GetCode() == "deviceBusy");
259 return result;
260 };
261
262 EXPECT_TRUE(pair());
263 EXPECT_TRUE(pair());
264 EXPECT_TRUE(pair());
265 EXPECT_FALSE(pair());
Vitaly Buka41aa8092015-12-09 20:04:34 -0800266 EXPECT_GT(security_.block_pairing_until_, clock_.Now());
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700267 EXPECT_LE(security_.block_pairing_until_,
Vitaly Buka41aa8092015-12-09 20:04:34 -0800268 clock_.Now() + base::TimeDelta::FromMinutes(15));
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700269
270 // Wait timeout.
271 security_.block_pairing_until_ =
Vitaly Buka41aa8092015-12-09 20:04:34 -0800272 clock_.Now() - base::TimeDelta::FromMinutes(1);
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700273
274 // Allow exactly one attempt.
275 EXPECT_TRUE(pair());
276 EXPECT_FALSE(pair());
277
278 // Wait timeout.
279 security_.block_pairing_until_ =
Vitaly Buka41aa8092015-12-09 20:04:34 -0800280 clock_.Now() - base::TimeDelta::FromMinutes(1);
Vitaly Buka7ce499f2015-06-09 08:04:11 -0700281
282 // Completely unblock by successfully pairing.
283 std::string fingerprint;
284 std::string signature;
285 PairAndAuthenticate(&fingerprint, &signature);
286
287 // Now we have 3 attempts again.
288 EXPECT_TRUE(pair());
289 EXPECT_TRUE(pair());
290 EXPECT_TRUE(pair());
291 EXPECT_FALSE(pair());
292}
293
294TEST_F(SecurityManagerTest, DontBlockForCanceledSessions) {
295 for (int i = 0; i < 20; ++i) {
296 std::string session_id;
297 std::string device_commitment;
298 EXPECT_TRUE(security_.StartPairing(PairingType::kEmbeddedCode,
299 CryptoType::kSpake_p224, &session_id,
300 &device_commitment, nullptr));
301 EXPECT_TRUE(security_.CancelPairing(session_id, nullptr));
302 }
303}
304
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700305} // namespace privet
306} // namespace weave