Home | History | Annotate | Download | only in privet
      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