• 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 <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:
Spakep224Exchanger(const std::string & password)42   explicit Spakep224Exchanger(const std::string& password)
43       : spake_(crypto::P224EncryptedKeyExchange::kPeerTypeServer, password) {}
44   ~Spakep224Exchanger() override = default;
45 
46   // SecurityManager::KeyExchanger methods.
GetMessage()47   const std::string& GetMessage() override { return spake_.GetNextMessage(); }
48 
ProcessMessage(const std::string & message,ErrorPtr * error)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 
GetKey() const62   const std::string& GetKey() const override {
63     return spake_.GetUnverifiedKey();
64   }
65 
66  private:
67   crypto::P224EncryptedKeyExchange spake_;
68 };
69 
70 }  // namespace
71 
SecurityManager(const Config * config,AuthManager * auth_manager,provider::TaskRunner * task_runner)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 
~SecurityManager()84 SecurityManager::~SecurityManager() {
85   while (!pending_sessions_.empty())
86     ClosePendingSession(pending_sessions_.begin()->first);
87 }
88 
CreateAccessTokenImpl(AuthType auth_type,AuthScope desired_scope,std::vector<uint8_t> * access_token,AuthScope * access_token_scope,base::TimeDelta * access_token_ttl)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 
CreateAccessTokenImpl(AuthType auth_type,const std::vector<uint8_t> & auth_code,AuthScope desired_scope,std::vector<uint8_t> * access_token,AuthScope * access_token_scope,base::TimeDelta * access_token_ttl,ErrorPtr * error)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 
CreateAccessToken(AuthType auth_type,const std::string & auth_code,AuthScope desired_scope,std::string * access_token,AuthScope * access_token_scope,base::TimeDelta * access_token_ttl,ErrorPtr * error)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 
ParseAccessToken(const std::string & token,UserInfo * user_info,ErrorPtr * error) const184 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 
GetPairingTypes() const197 std::set<PairingType> SecurityManager::GetPairingTypes() const {
198   return GetSettings().pairing_modes;
199 }
200 
GetCryptoTypes() const201 std::set<CryptoType> SecurityManager::GetCryptoTypes() const {
202   std::set<CryptoType> result{CryptoType::kSpake_p224};
203   return result;
204 }
205 
GetAuthTypes() const206 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 
ClaimRootClientAuthToken(ErrorPtr * error)220 std::string SecurityManager::ClaimRootClientAuthToken(ErrorPtr* error) {
221   return Base64Encode(auth_manager_->ClaimRootClientAuthToken(
222       RootClientTokenOwner::kClient, error));
223 }
224 
ConfirmClientAuthToken(const std::string & token,ErrorPtr * error)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 
GetSettings() const236 const Config::Settings& SecurityManager::GetSettings() const {
237   return config_->GetSettings();
238 }
239 
IsValidPairingCode(const std::vector<uint8_t> & auth_code) const240 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 
StartPairing(PairingType mode,CryptoType crypto,std::string * session_id,std::string * device_commitment,ErrorPtr * error)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 
ConfirmPairing(const std::string & session_id,const std::string & client_commitment,std::string * fingerprint,std::string * signature,ErrorPtr * error)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 
CancelPairing(const std::string & session_id,ErrorPtr * error)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 
CreateSessionId()391 std::string SecurityManager::CreateSessionId() {
392   return auth_manager_->CreateSessionId();
393 }
394 
RegisterPairingListeners(const PairingStartListener & on_start,const PairingEndListener & on_end)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 
CheckIfPairingAllowed(ErrorPtr * error)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 
ClosePendingSession(const std::string & session_id)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 
CloseConfirmedSession(const std::string & session_id)430 bool SecurityManager::CloseConfirmedSession(const std::string& session_id) {
431   return confirmed_sessions_.erase(session_id) != 0;
432 }
433 
IsAnonymousAuthSupported() const434 bool SecurityManager::IsAnonymousAuthSupported() const {
435   return GetSettings().local_anonymous_access_role != AuthScope::kNone;
436 }
437 
IsPairingAuthSupported() const438 bool SecurityManager::IsPairingAuthSupported() const {
439   return GetSettings().local_pairing_enabled;
440 }
441 
IsLocalAuthSupported() const442 bool SecurityManager::IsLocalAuthSupported() const {
443   return GetSettings().root_client_token_owner != RootClientTokenOwner::kNone;
444 }
445 
446 }  // namespace privet
447 }  // namespace weave
448