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 <limits> 9 #include <memory> 10 #include <set> 11 12 #include <base/bind.h> 13 #include <base/guid.h> 14 #include <base/logging.h> 15 #include <base/rand_util.h> 16 #include <base/strings/string_number_conversions.h> 17 #include <base/strings/stringprintf.h> 18 #include <base/time/time.h> 19 #include <weave/provider/task_runner.h> 20 21 #include "src/data_encoding.h" 22 #include "src/privet/auth_manager.h" 23 #include "src/privet/constants.h" 24 #include "src/privet/openssl_utils.h" 25 #include "src/string_utils.h" 26 #include "third_party/chromium/crypto/p224_spake.h" 27 28 namespace weave { 29 namespace privet { 30 31 namespace { 32 33 const int kSessionExpirationTimeMinutes = 5; 34 const int kPairingExpirationTimeMinutes = 5; 35 const int kMaxAllowedPairingAttemts = 3; 36 const int kPairingBlockingTimeMinutes = 1; 37 38 const int kAccessTokenExpirationSeconds = 3600; 39 40 class Spakep224Exchanger : public SecurityManager::KeyExchanger { 41 public: 42 explicit Spakep224Exchanger(const std::string& password) 43 : spake_(crypto::P224EncryptedKeyExchange::kPeerTypeServer, password) {} 44 ~Spakep224Exchanger() override = default; 45 46 // SecurityManager::KeyExchanger methods. 47 const std::string& GetMessage() override { return spake_.GetNextMessage(); } 48 49 bool ProcessMessage(const std::string& message, ErrorPtr* error) override { 50 switch (spake_.ProcessMessage(message)) { 51 case crypto::P224EncryptedKeyExchange::kResultPending: 52 return true; 53 case crypto::P224EncryptedKeyExchange::kResultFailed: 54 return Error::AddTo(error, FROM_HERE, errors::kInvalidClientCommitment, 55 spake_.error()); 56 default: 57 LOG(FATAL) << "SecurityManager uses only one round trip"; 58 } 59 return false; 60 } 61 62 const std::string& GetKey() const override { 63 return spake_.GetUnverifiedKey(); 64 } 65 66 private: 67 crypto::P224EncryptedKeyExchange spake_; 68 }; 69 70 } // namespace 71 72 SecurityManager::SecurityManager(const Config* config, 73 AuthManager* auth_manager, 74 provider::TaskRunner* task_runner) 75 : config_{config}, auth_manager_{auth_manager}, task_runner_{task_runner} { 76 CHECK(auth_manager_); 77 CHECK_EQ(GetSettings().embedded_code.empty(), 78 std::find(GetSettings().pairing_modes.begin(), 79 GetSettings().pairing_modes.end(), 80 PairingType::kEmbeddedCode) == 81 GetSettings().pairing_modes.end()); 82 } 83 84 SecurityManager::~SecurityManager() { 85 while (!pending_sessions_.empty()) 86 ClosePendingSession(pending_sessions_.begin()->first); 87 } 88 89 bool SecurityManager::CreateAccessTokenImpl(AuthType auth_type, 90 AuthScope desired_scope, 91 std::vector<uint8_t>* access_token, 92 AuthScope* access_token_scope, 93 base::TimeDelta* access_token_ttl) { 94 auto user_id = std::to_string(++last_user_id_); 95 UserInfo user_info{ 96 desired_scope, 97 UserAppId{auth_type, {user_id.begin(), user_id.end()}, {}}}; 98 99 const base::TimeDelta kTtl = 100 base::TimeDelta::FromSeconds(kAccessTokenExpirationSeconds); 101 102 if (access_token) 103 *access_token = auth_manager_->CreateAccessToken(user_info, kTtl); 104 105 if (access_token_scope) 106 *access_token_scope = user_info.scope(); 107 108 if (access_token_ttl) 109 *access_token_ttl = kTtl; 110 111 return true; 112 } 113 114 bool SecurityManager::CreateAccessTokenImpl( 115 AuthType auth_type, 116 const std::vector<uint8_t>& auth_code, 117 AuthScope desired_scope, 118 std::vector<uint8_t>* access_token, 119 AuthScope* access_token_scope, 120 base::TimeDelta* access_token_ttl, 121 ErrorPtr* error) { 122 auto disabled_mode = [](ErrorPtr* error) { 123 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthMode, 124 "Mode is not available"); 125 }; 126 127 switch (auth_type) { 128 case AuthType::kAnonymous: 129 if (!IsAnonymousAuthSupported()) 130 return disabled_mode(error); 131 return CreateAccessTokenImpl(auth_type, desired_scope, access_token, 132 access_token_scope, access_token_ttl); 133 case AuthType::kPairing: 134 if (!IsPairingAuthSupported()) 135 return disabled_mode(error); 136 if (!IsValidPairingCode(auth_code)) { 137 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, 138 "Invalid authCode"); 139 } 140 return CreateAccessTokenImpl(auth_type, desired_scope, access_token, 141 access_token_scope, access_token_ttl); 142 case AuthType::kLocal: 143 if (!IsLocalAuthSupported()) 144 return disabled_mode(error); 145 const base::TimeDelta kTtl = 146 base::TimeDelta::FromSeconds(kAccessTokenExpirationSeconds); 147 return auth_manager_->CreateAccessTokenFromAuth( 148 auth_code, kTtl, access_token, access_token_scope, access_token_ttl, 149 error); 150 } 151 152 return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthMode, 153 "Unsupported auth mode"); 154 } 155 156 bool SecurityManager::CreateAccessToken(AuthType auth_type, 157 const std::string& auth_code, 158 AuthScope desired_scope, 159 std::string* access_token, 160 AuthScope* access_token_scope, 161 base::TimeDelta* access_token_ttl, 162 ErrorPtr* error) { 163 std::vector<uint8_t> auth_decoded; 164 if (auth_type != AuthType::kAnonymous && 165 !Base64Decode(auth_code, &auth_decoded)) { 166 Error::AddToPrintf(error, FROM_HERE, errors::kInvalidAuthorization, 167 "Invalid auth_code encoding: %s", auth_code.c_str()); 168 return false; 169 } 170 171 std::vector<uint8_t> access_token_decoded; 172 if (!CreateAccessTokenImpl(auth_type, auth_decoded, desired_scope, 173 &access_token_decoded, access_token_scope, 174 access_token_ttl, error)) { 175 return false; 176 } 177 178 if (access_token) 179 *access_token = Base64Encode(access_token_decoded); 180 181 return true; 182 } 183 184 bool SecurityManager::ParseAccessToken(const std::string& token, 185 UserInfo* user_info, 186 ErrorPtr* error) const { 187 std::vector<uint8_t> decoded; 188 if (!Base64Decode(token, &decoded)) { 189 Error::AddToPrintf(error, FROM_HERE, errors::kInvalidAuthorization, 190 "Invalid token encoding: %s", token.c_str()); 191 return false; 192 } 193 194 return auth_manager_->ParseAccessToken(decoded, user_info, error); 195 } 196 197 std::set<PairingType> SecurityManager::GetPairingTypes() const { 198 return GetSettings().pairing_modes; 199 } 200 201 std::set<CryptoType> SecurityManager::GetCryptoTypes() const { 202 std::set<CryptoType> result{CryptoType::kSpake_p224}; 203 return result; 204 } 205 206 std::set<AuthType> SecurityManager::GetAuthTypes() const { 207 std::set<AuthType> result; 208 if (IsAnonymousAuthSupported()) 209 result.insert(AuthType::kAnonymous); 210 211 if (IsPairingAuthSupported()) 212 result.insert(AuthType::kPairing); 213 214 if (IsLocalAuthSupported()) 215 result.insert(AuthType::kLocal); 216 217 return result; 218 } 219 220 std::string SecurityManager::ClaimRootClientAuthToken(ErrorPtr* error) { 221 return Base64Encode(auth_manager_->ClaimRootClientAuthToken( 222 RootClientTokenOwner::kClient, error)); 223 } 224 225 bool SecurityManager::ConfirmClientAuthToken(const std::string& token, 226 ErrorPtr* error) { 227 std::vector<uint8_t> token_decoded; 228 if (!Base64Decode(token, &token_decoded)) { 229 Error::AddToPrintf(error, FROM_HERE, errors::kInvalidFormat, 230 "Invalid auth token string: '%s'", token.c_str()); 231 return false; 232 } 233 return auth_manager_->ConfirmClientAuthToken(token_decoded, error); 234 } 235 236 const Config::Settings& SecurityManager::GetSettings() const { 237 return config_->GetSettings(); 238 } 239 240 bool SecurityManager::IsValidPairingCode( 241 const std::vector<uint8_t>& auth_code) const { 242 for (const auto& session : confirmed_sessions_) { 243 const std::string& key = session.second->GetKey(); 244 const std::string& id = session.first; 245 if (auth_code == HmacSha256(std::vector<uint8_t>(key.begin(), key.end()), 246 std::vector<uint8_t>(id.begin(), id.end()))) { 247 pairing_attemts_ = 0; 248 block_pairing_until_ = base::Time{}; 249 return true; 250 } 251 } 252 LOG(ERROR) << "Attempt to authenticate with invalide code."; 253 return false; 254 } 255 256 bool SecurityManager::StartPairing(PairingType mode, 257 CryptoType crypto, 258 std::string* session_id, 259 std::string* device_commitment, 260 ErrorPtr* error) { 261 if (!CheckIfPairingAllowed(error)) 262 return false; 263 264 const auto& pairing_modes = GetSettings().pairing_modes; 265 if (std::find(pairing_modes.begin(), pairing_modes.end(), mode) == 266 pairing_modes.end()) { 267 return Error::AddTo(error, FROM_HERE, errors::kInvalidParams, 268 "Pairing mode is not enabled"); 269 } 270 271 std::string code; 272 switch (mode) { 273 case PairingType::kEmbeddedCode: 274 CHECK(!GetSettings().embedded_code.empty()); 275 code = GetSettings().embedded_code; 276 break; 277 case PairingType::kPinCode: 278 code = base::StringPrintf("%04i", base::RandInt(0, 9999)); 279 break; 280 default: 281 return Error::AddTo(error, FROM_HERE, errors::kInvalidParams, 282 "Unsupported pairing mode"); 283 } 284 285 std::unique_ptr<KeyExchanger> spake; 286 switch (crypto) { 287 case CryptoType::kSpake_p224: 288 spake.reset(new Spakep224Exchanger(code)); 289 break; 290 // Fall through... 291 default: 292 return Error::AddTo(error, FROM_HERE, errors::kInvalidParams, 293 "Unsupported crypto"); 294 } 295 296 // Allow only a single session at a time for now. 297 while (!pending_sessions_.empty()) 298 ClosePendingSession(pending_sessions_.begin()->first); 299 300 std::string session; 301 do { 302 session = base::GenerateGUID(); 303 } while (confirmed_sessions_.find(session) != confirmed_sessions_.end() || 304 pending_sessions_.find(session) != pending_sessions_.end()); 305 std::string commitment = spake->GetMessage(); 306 pending_sessions_.insert(std::make_pair(session, std::move(spake))); 307 308 task_runner_->PostDelayedTask( 309 FROM_HERE, 310 base::Bind(base::IgnoreResult(&SecurityManager::ClosePendingSession), 311 weak_ptr_factory_.GetWeakPtr(), session), 312 base::TimeDelta::FromMinutes(kPairingExpirationTimeMinutes)); 313 314 *session_id = session; 315 *device_commitment = Base64Encode(commitment); 316 LOG(INFO) << "Pairing code for session " << *session_id << " is " << code; 317 // TODO(vitalybuka): Handle case when device can't start multiple pairing 318 // simultaneously and implement throttling to avoid brute force attack. 319 if (!on_start_.is_null()) { 320 on_start_.Run(session, mode, 321 std::vector<uint8_t>{code.begin(), code.end()}); 322 } 323 324 return true; 325 } 326 327 bool SecurityManager::ConfirmPairing(const std::string& session_id, 328 const std::string& client_commitment, 329 std::string* fingerprint, 330 std::string* signature, 331 ErrorPtr* error) { 332 auto session = pending_sessions_.find(session_id); 333 if (session == pending_sessions_.end()) { 334 Error::AddToPrintf(error, FROM_HERE, errors::kUnknownSession, 335 "Unknown session id: '%s'", session_id.c_str()); 336 return false; 337 } 338 339 std::vector<uint8_t> commitment; 340 if (!Base64Decode(client_commitment, &commitment)) { 341 ClosePendingSession(session_id); 342 Error::AddToPrintf(error, FROM_HERE, errors::kInvalidFormat, 343 "Invalid commitment string: '%s'", 344 client_commitment.c_str()); 345 return false; 346 } 347 348 if (!session->second->ProcessMessage( 349 std::string(commitment.begin(), commitment.end()), error)) { 350 ClosePendingSession(session_id); 351 return Error::AddTo(error, FROM_HERE, errors::kCommitmentMismatch, 352 "Pairing code or crypto implementation mismatch"); 353 } 354 355 const std::string& key = session->second->GetKey(); 356 VLOG(3) << "KEY " << base::HexEncode(key.data(), key.size()); 357 358 const auto& certificate_fingerprint = 359 auth_manager_->GetCertificateFingerprint(); 360 *fingerprint = Base64Encode(certificate_fingerprint); 361 std::vector<uint8_t> cert_hmac = HmacSha256( 362 std::vector<uint8_t>(key.begin(), key.end()), certificate_fingerprint); 363 *signature = Base64Encode(cert_hmac); 364 confirmed_sessions_.insert( 365 std::make_pair(session->first, std::move(session->second))); 366 task_runner_->PostDelayedTask( 367 FROM_HERE, 368 base::Bind(base::IgnoreResult(&SecurityManager::CloseConfirmedSession), 369 weak_ptr_factory_.GetWeakPtr(), session_id), 370 base::TimeDelta::FromMinutes(kSessionExpirationTimeMinutes)); 371 ClosePendingSession(session_id); 372 return true; 373 } 374 375 bool SecurityManager::CancelPairing(const std::string& session_id, 376 ErrorPtr* error) { 377 bool confirmed = CloseConfirmedSession(session_id); 378 bool pending = ClosePendingSession(session_id); 379 if (pending) { 380 CHECK_GE(pairing_attemts_, 1); 381 --pairing_attemts_; 382 } 383 CHECK(!confirmed || !pending); 384 if (confirmed || pending) 385 return true; 386 Error::AddToPrintf(error, FROM_HERE, errors::kUnknownSession, 387 "Unknown session id: '%s'", session_id.c_str()); 388 return false; 389 } 390 391 std::string SecurityManager::CreateSessionId() { 392 return auth_manager_->CreateSessionId(); 393 } 394 395 void SecurityManager::RegisterPairingListeners( 396 const PairingStartListener& on_start, 397 const PairingEndListener& on_end) { 398 CHECK(on_start_.is_null() && on_end_.is_null()); 399 on_start_ = on_start; 400 on_end_ = on_end; 401 } 402 403 bool SecurityManager::CheckIfPairingAllowed(ErrorPtr* error) { 404 if (block_pairing_until_ > auth_manager_->Now()) { 405 return Error::AddTo(error, FROM_HERE, errors::kDeviceBusy, 406 "Too many pairing attempts"); 407 } 408 409 if (++pairing_attemts_ >= kMaxAllowedPairingAttemts) { 410 LOG(INFO) << "Pairing blocked for" << kPairingBlockingTimeMinutes 411 << "minutes."; 412 block_pairing_until_ = auth_manager_->Now(); 413 block_pairing_until_ += 414 base::TimeDelta::FromMinutes(kPairingBlockingTimeMinutes); 415 } 416 417 return true; 418 } 419 420 bool SecurityManager::ClosePendingSession(const std::string& session_id) { 421 // The most common source of these session_id values is the map containing 422 // the sessions, which we're about to clear out. Make a local copy. 423 const std::string safe_session_id{session_id}; 424 const size_t num_erased = pending_sessions_.erase(safe_session_id); 425 if (num_erased > 0 && !on_end_.is_null()) 426 on_end_.Run(safe_session_id); 427 return num_erased != 0; 428 } 429 430 bool SecurityManager::CloseConfirmedSession(const std::string& session_id) { 431 return confirmed_sessions_.erase(session_id) != 0; 432 } 433 434 bool SecurityManager::IsAnonymousAuthSupported() const { 435 return GetSettings().local_anonymous_access_role != AuthScope::kNone; 436 } 437 438 bool SecurityManager::IsPairingAuthSupported() const { 439 return GetSettings().local_pairing_enabled; 440 } 441 442 bool SecurityManager::IsLocalAuthSupported() const { 443 return GetSettings().root_client_token_owner != RootClientTokenOwner::kNone; 444 } 445 446 } // namespace privet 447 } // namespace weave 448