Save device secret
Device secret is needed for verification of auth tokens after device
restart.
BUG: 24110921
Change-Id: If956886fcd6325dd4b419b7ca39ccb6bc4708230
Reviewed-on: https://weave-review.googlesource.com/1180
Reviewed-by: Alex Vakulenko <avakulenko@google.com>
diff --git a/libweave/include/weave/settings.h b/libweave/include/weave/settings.h
index 01f4c0d..264c6fa 100644
--- a/libweave/include/weave/settings.h
+++ b/libweave/include/weave/settings.h
@@ -41,6 +41,7 @@
std::string refresh_token;
std::string robot_account;
std::string last_configured_ssid;
+ std::string secret;
};
} // namespace weave
diff --git a/libweave/src/config.cc b/libweave/src/config.cc
index 3aa57d5..0f01e2c 100644
--- a/libweave/src/config.cc
+++ b/libweave/src/config.cc
@@ -36,6 +36,7 @@
const char kCloudId[] = "cloud_id";
const char kRobotAccount[] = "robot_account";
const char kLastConfiguredSsid[] = "last_configured_ssid";
+const char kSecret[] = "secret";
} // namespace config_keys
@@ -109,6 +110,7 @@
CHECK(!settings_.model_name.empty());
CHECK(!settings_.model_id.empty());
CHECK(!settings_.name.empty());
+
CHECK(IsValidAccessRole(settings_.local_anonymous_access_role))
<< "Invalid role: " << settings_.local_anonymous_access_role;
CHECK_EQ(
@@ -116,6 +118,13 @@
std::find(settings_.pairing_modes.begin(), settings_.pairing_modes.end(),
PairingType::kEmbeddedCode) == settings_.pairing_modes.end());
+ // Values below will be generated at runtime.
+ CHECK(settings_.cloud_id.empty());
+ CHECK(settings_.refresh_token.empty());
+ CHECK(settings_.robot_account.empty());
+ CHECK(settings_.last_configured_ssid.empty());
+ CHECK(settings_.secret.empty());
+
change.LoadState();
}
@@ -169,6 +178,9 @@
if (dict->GetBoolean(config_keys::kLocalPairingEnabled, &tmp_bool))
set_local_pairing_enabled(tmp_bool);
+ if (dict->GetString(config_keys::kCloudId, &tmp))
+ set_cloud_id(tmp);
+
if (dict->GetString(config_keys::kRefreshToken, &tmp))
set_refresh_token(tmp);
@@ -178,8 +190,8 @@
if (dict->GetString(config_keys::kLastConfiguredSsid, &tmp))
set_last_configured_ssid(tmp);
- if (dict->GetString(config_keys::kCloudId, &tmp))
- set_cloud_id(tmp);
+ if (dict->GetString(config_keys::kSecret, &tmp))
+ set_secret(tmp);
}
void Config::Save() {
@@ -197,6 +209,7 @@
dict.SetString(config_keys::kRobotAccount, settings_.robot_account);
dict.SetString(config_keys::kLastConfiguredSsid,
settings_.last_configured_ssid);
+ dict.SetString(config_keys::kSecret, settings_.secret);
dict.SetString(config_keys::kName, settings_.name);
dict.SetString(config_keys::kDescription, settings_.description);
dict.SetString(config_keys::kLocation, settings_.location);
diff --git a/libweave/src/config.h b/libweave/src/config.h
index 37b7cb6..c59cf86 100644
--- a/libweave/src/config.h
+++ b/libweave/src/config.h
@@ -78,6 +78,7 @@
void set_last_configured_ssid(const std::string& ssid) {
settings_->last_configured_ssid = ssid;
}
+ void set_secret(const std::string& secret) { settings_->secret = secret; }
void Commit();
diff --git a/libweave/src/config_unittest.cc b/libweave/src/config_unittest.cc
index 2a81769..ada7fde 100644
--- a/libweave/src/config_unittest.cc
+++ b/libweave/src/config_unittest.cc
@@ -75,6 +75,7 @@
EXPECT_EQ("", GetSettings().refresh_token);
EXPECT_EQ("", GetSettings().robot_account);
EXPECT_EQ("", GetSettings().last_configured_ssid);
+ EXPECT_EQ("", GetSettings().secret);
}
TEST_F(ConfigTest, LoadState) {
@@ -82,8 +83,9 @@
"api_key": "state_api_key",
"client_id": "state_client_id",
"client_secret": "state_client_secret",
- "description": "state_description",
"cloud_id": "state_cloud_id",
+ "description": "state_description",
+ "last_configured_ssid": "state_last_configured_ssid",
"local_anonymous_access_role": "user",
"local_discovery_enabled": false,
"local_pairing_enabled": false,
@@ -92,7 +94,7 @@
"oauth_url": "state_oauth_url",
"refresh_token": "state_refresh_token",
"robot_account": "state_robot_account",
- "last_configured_ssid": "state_last_configured_ssid",
+ "secret": "state_secret",
"service_url": "state_service_url"
})";
EXPECT_CALL(config_store_, LoadSettings()).WillOnce(Return(state));
@@ -127,6 +129,7 @@
EXPECT_EQ("state_refresh_token", GetSettings().refresh_token);
EXPECT_EQ("state_robot_account", GetSettings().robot_account);
EXPECT_EQ("state_last_configured_ssid", GetSettings().last_configured_ssid);
+ EXPECT_EQ("state_secret", GetSettings().secret);
}
TEST_F(ConfigTest, Setters) {
@@ -189,6 +192,9 @@
change.set_last_configured_ssid("set_last_configured_ssid");
EXPECT_EQ("set_last_configured_ssid", GetSettings().last_configured_ssid);
+ change.set_secret("set_secret");
+ EXPECT_EQ("set_secret", GetSettings().secret);
+
EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
EXPECT_CALL(config_store_, SaveSettings(_))
@@ -197,8 +203,9 @@
'api_key': 'set_api_key',
'client_id': 'set_client_id',
'client_secret': 'set_client_secret',
- 'description': 'set_description',
'cloud_id': 'set_cloud_id',
+ 'description': 'set_description',
+ 'last_configured_ssid': 'set_last_configured_ssid',
'local_anonymous_access_role': 'user',
'local_discovery_enabled': true,
'local_pairing_enabled': true,
@@ -207,7 +214,7 @@
'oauth_url': 'set_oauth_url',
'refresh_token': 'set_token',
'robot_account': 'set_account',
- 'last_configured_ssid': 'set_last_configured_ssid',
+ 'secret': 'set_secret',
'service_url': 'set_service_url'
})";
EXPECT_JSON_EQ(expected, *test::CreateValue(json));
diff --git a/libweave/src/privet/privet_manager.cc b/libweave/src/privet/privet_manager.cc
index 5b72a2a..e84fdf1 100644
--- a/libweave/src/privet/privet_manager.cc
+++ b/libweave/src/privet/privet_manager.cc
@@ -54,9 +54,17 @@
cloud_ = CloudDelegate::CreateDefault(task_runner, device, command_manager,
state_manager);
cloud_observer_.Add(cloud_.get());
- security_.reset(new SecurityManager(device->GetSettings().pairing_modes,
- device->GetSettings().embedded_code,
- disable_security_, task_runner));
+ security_.reset(new SecurityManager(
+ device->GetSettings().secret, device->GetSettings().pairing_modes,
+ device->GetSettings().embedded_code, disable_security_, task_runner));
+ if (device->GetSettings().secret.empty()) {
+ // TODO(vitalybuka): Post all Config::Transaction to avoid following.
+ task_runner->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&Manager::SaveDeviceSecret, weak_ptr_factory_.GetWeakPtr(),
+ base::Unretained(device->GetMutableConfig())),
+ {});
+ }
network->AddConnectionChangedCallback(
base::Bind(&Manager::OnConnectivityChanged, base::Unretained(this)));
@@ -164,5 +172,10 @@
security_->SetCertificateFingerprint(server.GetHttpsCertificateFingerprint());
}
+void Manager::SaveDeviceSecret(Config* config) {
+ Config::Transaction transaction(config);
+ transaction.set_secret(security_->GetSecret());
+}
+
} // namespace privet
} // namespace weave
diff --git a/libweave/src/privet/privet_manager.h b/libweave/src/privet/privet_manager.h
index 5e3df3f..a388967 100644
--- a/libweave/src/privet/privet_manager.h
+++ b/libweave/src/privet/privet_manager.h
@@ -83,6 +83,7 @@
void OnConnectivityChanged();
void OnHttpServerStatusChanged(const provider::HttpServer& server);
+ void SaveDeviceSecret(Config* config);
bool disable_security_{false};
std::unique_ptr<CloudDelegate> cloud_;
diff --git a/libweave/src/privet/publisher.cc b/libweave/src/privet/publisher.cc
index 5d6567b..cd9cd66 100644
--- a/libweave/src/privet/publisher.cc
+++ b/libweave/src/privet/publisher.cc
@@ -78,14 +78,17 @@
if (!cloud_->GetDescription().empty())
txt_record.emplace_back("note=" + cloud_->GetDescription());
- is_publishing_ = true;
+ auto new_data = std::make_pair(port, txt_record);
+ if (published_ == new_data)
+ return;
+ published_ = new_data;
dns_sd_->PublishService(kPrivetServiceType, port, txt_record);
}
void Publisher::RemoveService() {
- if (!is_publishing_)
+ if (!published_.first)
return;
- is_publishing_ = false;
+ published_ = {};
VLOG(1) << "Stopping service publishing.";
dns_sd_->StopPublishing(kPrivetServiceType);
}
diff --git a/libweave/src/privet/publisher.h b/libweave/src/privet/publisher.h
index d330992..c1a9325 100644
--- a/libweave/src/privet/publisher.h
+++ b/libweave/src/privet/publisher.h
@@ -7,6 +7,7 @@
#include <memory>
#include <string>
+#include <vector>
#include <base/macros.h>
@@ -43,13 +44,14 @@
void ExposeService();
void RemoveService();
- bool is_publishing_{false};
provider::DnsServiceDiscovery* dns_sd_{nullptr};
const DeviceDelegate* device_{nullptr};
const CloudDelegate* cloud_{nullptr};
const WifiDelegate* wifi_{nullptr};
+ std::pair<uint16_t, std::vector<std::string>> published_;
+
DISALLOW_COPY_AND_ASSIGN(Publisher);
};
diff --git a/libweave/src/privet/security_manager.cc b/libweave/src/privet/security_manager.cc
index 1dfbab5..96b114f 100644
--- a/libweave/src/privet/security_manager.cc
+++ b/libweave/src/privet/security_manager.cc
@@ -118,17 +118,19 @@
} // namespace
-SecurityManager::SecurityManager(const std::set<PairingType>& pairing_modes,
+SecurityManager::SecurityManager(const std::string& secret,
+ const std::set<PairingType>& pairing_modes,
const std::string& embedded_code,
bool disable_security,
provider::TaskRunner* task_runner)
: is_security_disabled_(disable_security),
pairing_modes_(pairing_modes),
embedded_code_(embedded_code),
- task_runner_{task_runner},
- secret_(kSha256OutputSize) {
- base::RandBytes(secret_.data(), kSha256OutputSize);
-
+ task_runner_{task_runner} {
+ if (!Base64Decode(secret, &secret_) || secret_.size() != kSha256OutputSize) {
+ secret_.resize(kSha256OutputSize);
+ base::RandBytes(secret_.data(), secret_.size());
+ }
CHECK_EQ(embedded_code_.empty(),
std::find(pairing_modes_.begin(), pairing_modes_.end(),
PairingType::kEmbeddedCode) == pairing_modes_.end());
@@ -350,6 +352,10 @@
on_end_ = on_end;
}
+std::string SecurityManager::GetSecret() const {
+ return Base64Encode(secret_);
+}
+
bool SecurityManager::CheckIfPairingAllowed(ErrorPtr* error) {
if (is_security_disabled_)
return true;
diff --git a/libweave/src/privet/security_manager.h b/libweave/src/privet/security_manager.h
index 8acbd1d..4855778 100644
--- a/libweave/src/privet/security_manager.h
+++ b/libweave/src/privet/security_manager.h
@@ -49,9 +49,11 @@
virtual const std::string& GetKey() const = 0;
};
- SecurityManager(const std::set<PairingType>& pairing_modes,
+ SecurityManager(const std::string& secret,
+ const std::set<PairingType>& pairing_modes,
const std::string& embedded_code,
bool disable_security,
+ // TODO(vitalybuka): Remove task_runner.
provider::TaskRunner* task_runner);
~SecurityManager() override;
@@ -84,6 +86,8 @@
certificate_fingerprint_ = fingerprint;
}
+ std::string GetSecret() const;
+
private:
FRIEND_TEST_ALL_PREFIXES(SecurityManagerTest, ThrottlePairing);
// Allows limited number of new sessions without successful authorization.
diff --git a/libweave/src/privet/security_manager_unittest.cc b/libweave/src/privet/security_manager_unittest.cc
index c85bd5f..ed225ae 100644
--- a/libweave/src/privet/security_manager_unittest.cc
+++ b/libweave/src/privet/security_manager_unittest.cc
@@ -58,7 +58,7 @@
public:
void SetUp() override {
std::vector<uint8_t> fingerprint;
- fingerprint.resize(256 / 8);
+ fingerprint.resize(32);
base::RandBytes(fingerprint.data(), fingerprint.size());
security_.SetCertificateFingerprint(fingerprint);
}
@@ -101,12 +101,29 @@
const base::Time time_ = base::Time::FromTimeT(1410000000);
provider::test::FakeTaskRunner task_runner_;
- SecurityManager security_{{PairingType::kEmbeddedCode},
+ SecurityManager security_{{},
+ {PairingType::kEmbeddedCode},
"1234",
false,
&task_runner_};
};
+TEST_F(SecurityManagerTest, RandomSecret) {
+ EXPECT_GE(security_.GetSecret().size(), 32);
+ EXPECT_TRUE(IsBase64(security_.GetSecret()));
+}
+
+TEST_F(SecurityManagerTest, DifferentSecret) {
+ SecurityManager security{{}, {}, "", false, &task_runner_};
+ EXPECT_NE(security_.GetSecret(), security.GetSecret());
+}
+
+TEST_F(SecurityManagerTest, ExternalSecret) {
+ const std::string kSecret = "T1SDv9CVGNO82zHKeRrUSzpAzjb1hmRyzXGotsn1gcU=";
+ SecurityManager security{kSecret, {}, "", false, &task_runner_};
+ EXPECT_EQ(kSecret, security.GetSecret());
+}
+
TEST_F(SecurityManagerTest, IsBase64) {
EXPECT_TRUE(IsBase64(
security_.CreateAccessToken(UserInfo{AuthScope::kUser, 7}, time_)));
@@ -139,14 +156,14 @@
TEST_F(SecurityManagerTest, CreateTokenDifferentInstance) {
EXPECT_NE(security_.CreateAccessToken(UserInfo{AuthScope::kUser, 123}, time_),
- SecurityManager({}, "", false, &task_runner_)
+ SecurityManager({}, {}, "", false, &task_runner_)
.CreateAccessToken(UserInfo{AuthScope::kUser, 123}, time_));
}
TEST_F(SecurityManagerTest, ParseAccessToken) {
// Multiple attempts with random secrets.
for (size_t i = 0; i < 1000; ++i) {
- SecurityManager security{{}, "", false, &task_runner_};
+ SecurityManager security{{}, {}, "", false, &task_runner_};
std::string token =
security.CreateAccessToken(UserInfo{AuthScope::kUser, 5}, time_);