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 42 bool IsBase64Char(char c) { 43 return isalnum(c) || (c == '+') || (c == '/') || (c == '='); 44 } 45 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: 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: 84 void SetUp() override { 85 EXPECT_CALL(clock_, Now()) 86 .WillRepeatedly(Return(base::Time::FromTimeT(1410000000))); 87 } 88 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 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 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 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 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 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 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 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