Merge remote-tracking branch 'weave/master' into dev_dev * weave/master: Remove event_http_client example provider.
diff --git a/examples/daemon/common/daemon.h b/examples/daemon/common/daemon.h index 4cccff3..985c5e5 100644 --- a/examples/daemon/common/daemon.h +++ b/examples/daemon/common/daemon.h
@@ -20,7 +20,6 @@ public: struct Options { bool force_bootstrapping_{false}; - bool disable_security_{false}; bool disable_privet_{false}; std::string registration_ticket_; std::string model_id_{"AAAAA"}; @@ -31,7 +30,6 @@ << "\t-h,--help Show this help message\n" << "\t--v=LEVEL Logging level\n" << "\t-b,--bootstrapping Force WiFi bootstrapping\n" - << "\t-d,--disable_security Disable privet security\n" << "\t--registration_ticket=TICKET Register device with the " "given ticket\n" << "\t--disable_privet Disable local privet\n"; @@ -44,8 +42,6 @@ return false; } else if (arg == "-b" || arg == "--bootstrapping") { force_bootstrapping_ = true; - } else if (arg == "-d" || arg == "--disable_security") { - disable_security_ = true; } else if (arg == "--disable_privet") { disable_privet_ = true; } else if (arg.find("--registration_ticket") != std::string::npos) { @@ -69,10 +65,10 @@ }; Daemon(const Options& opts) - : config_store_{new weave::examples::FileConfigStore( - opts.disable_security_, - opts.model_id_)}, - task_runner_{new weave::examples::EventTaskRunner}, + : task_runner_{new weave::examples::EventTaskRunner}, + config_store_{ + new weave::examples::FileConfigStore(opts.model_id_, + task_runner_.get())}, http_client_{new weave::examples::CurlHttpClient(task_runner_.get())}, network_{new weave::examples::EventNetworkImpl(task_runner_.get())}, bluetooth_{new weave::examples::BluetoothImpl} { @@ -114,8 +110,8 @@ LOG(INFO) << "Device registered: " << device->GetSettings().cloud_id; } - std::unique_ptr<weave::examples::FileConfigStore> config_store_; std::unique_ptr<weave::examples::EventTaskRunner> task_runner_; + std::unique_ptr<weave::examples::FileConfigStore> config_store_; std::unique_ptr<weave::examples::CurlHttpClient> http_client_; std::unique_ptr<weave::examples::EventNetworkImpl> network_; std::unique_ptr<weave::examples::BluetoothImpl> bluetooth_;
diff --git a/examples/provider/file_config_store.cc b/examples/provider/file_config_store.cc index 6faa242..a6c2e60 100644 --- a/examples/provider/file_config_store.cc +++ b/examples/provider/file_config_store.cc
@@ -12,16 +12,25 @@ #include <string> #include <vector> +#include <base/bind.h> + namespace weave { namespace examples { const char kSettingsDir[] = "/var/lib/weave/"; -FileConfigStore::FileConfigStore(bool disable_security, - const std::string& model_id) - : disable_security_{disable_security}, - model_id_{model_id}, - settings_path_{"/var/lib/weave/weave_settings_" + model_id + ".json"} {} +FileConfigStore::FileConfigStore(const std::string& model_id, + provider::TaskRunner* task_runner) + : model_id_{model_id}, + task_runner_{task_runner} {} + +std::string FileConfigStore::GetPath(const std::string& name) const { + std::string path{kSettingsDir}; + path += path + "weave_settings_" + model_id_; + if (!name.empty()) + path += "_" + name; + return path + ".json"; +} bool FileConfigStore::LoadDefaults(Settings* settings) { char host_name[HOST_NAME_MAX] = {}; @@ -50,22 +59,29 @@ settings->client_secret = "LS_iPYo_WIOE0m2VnLdduhnx"; settings->api_key = "AIzaSyACK3oZtmIylUKXiTMqkZqfuRiCgQmQSAQ"; - settings->disable_security = disable_security_; return true; } std::string FileConfigStore::LoadSettings() { - LOG(INFO) << "Loading settings from " << settings_path_; - std::ifstream str(settings_path_); + return LoadSettings(""); +} + +std::string FileConfigStore::LoadSettings(const std::string& name) { + LOG(INFO) << "Loading settings from " << GetPath(name); + std::ifstream str(GetPath(name)); return std::string(std::istreambuf_iterator<char>(str), std::istreambuf_iterator<char>()); } -void FileConfigStore::SaveSettings(const std::string& settings) { +void FileConfigStore::SaveSettings(const std::string& name, + const std::string& settings, + const DoneCallback& callback) { CHECK(mkdir(kSettingsDir, S_IRWXU) == 0 || errno == EEXIST); - LOG(INFO) << "Saving settings to " << settings_path_; - std::ofstream str(settings_path_); + LOG(INFO) << "Saving settings to " << GetPath(name); + std::ofstream str(GetPath(name)); str << settings; + if (!callback.is_null()) + task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr), {}); } } // namespace examples
diff --git a/examples/provider/file_config_store.h b/examples/provider/file_config_store.h index 578f940..337e82a 100644 --- a/examples/provider/file_config_store.h +++ b/examples/provider/file_config_store.h
@@ -10,22 +10,28 @@ #include <vector> #include <weave/provider/config_store.h> +#include <weave/provider/task_runner.h> namespace weave { namespace examples { class FileConfigStore : public provider::ConfigStore { public: - FileConfigStore(bool disable_security, const std::string& model_id); + FileConfigStore(const std::string& model_id, + provider::TaskRunner* task_runner); bool LoadDefaults(Settings* settings) override; + std::string LoadSettings(const std::string& name) override; + void SaveSettings(const std::string& name, + const std::string& settings, + const DoneCallback& callback) override; + std::string LoadSettings() override; - void SaveSettings(const std::string& settings) override; private: - const bool disable_security_; + std::string GetPath(const std::string& name) const; const std::string model_id_; - const std::string settings_path_; + provider::TaskRunner* task_runner_{nullptr}; }; } // namespace examples
diff --git a/file_lists.mk b/file_lists.mk index b944c3a..9a015e2 100644 --- a/file_lists.mk +++ b/file_lists.mk
@@ -3,6 +3,8 @@ # found in the LICENSE file. WEAVE_SRC_FILES := \ + src/access_api_handler.cc \ + src/access_black_list_manager_impl.cc \ src/backoff_entry.cc \ src/base_api_handler.cc \ src/commands/cloud_command_proxy.cc \ @@ -48,6 +50,8 @@ src/test/unittest_utils.cc WEAVE_UNITTEST_SRC_FILES := \ + src/access_api_handler_unittest.cc \ + src/access_black_list_manager_impl_unittest.cc \ src/backoff_entry_unittest.cc \ src/base_api_handler_unittest.cc \ src/commands/cloud_command_proxy_unittest.cc \ @@ -161,4 +165,3 @@ third_party/libuweave/src/macaroon_caveat.c \ third_party/libuweave/src/macaroon_context.c \ third_party/libuweave/src/macaroon_encoding.c -
diff --git a/include/weave/provider/config_store.h b/include/weave/provider/config_store.h index 1b7988f..128eccc 100644 --- a/include/weave/provider/config_store.h +++ b/include/weave/provider/config_store.h
@@ -13,6 +13,7 @@ #include <base/callback.h> #include <base/time/time.h> #include <weave/enum_to_string.h> +#include <weave/error.h> #include <weave/settings.h> namespace weave { @@ -36,8 +37,8 @@ // Implementation of LoadSettings() method should load previously // stored settings from the persistent storage (file, flash, etc). // For example: -// std::string FileConfigStore::LoadSettings() { -// std::ifstream str("/var/lib/weave/weave_settings.json"); +// std::string FileConfigStore::LoadSettings(const std::string& name) { +// std::ifstream str("/var/lib/weave/weave_" + name + ".json"); // return std::string(std::istreambuf_iterator<char>(str), // std::istreambuf_iterator<char>()); // } @@ -47,9 +48,14 @@ // Implementation of SaveSettings(...) method should store data in the // persistent storage (file, flash, etc). // For example: -// void FileConfigStore::SaveSettings(const std::string& settings) { -// std::ofstream str(kSettingsPath); +// void FileConfigStore::SaveSettings(const std::string& name, +// const std::string& settings, +// const DoneCallback& callback) { +// std::ofstream str("/var/lib/weave/weave_" + name + ".json"); // str << settings; +// if (!callback.is_null()) +// task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr), +// {}); // } // It is highly recommended to protected data using encryption with // hardware backed key. @@ -67,12 +73,20 @@ // Returns settings saved by SaveSettings during last run of libweave. // Implementation should return data as-is without parsing or modifications. - virtual std::string LoadSettings() = 0; + // |name| is the name of settings blob. Could be used as filename. + virtual std::string LoadSettings(const std::string& name) = 0; // Saves settings. Implementation should save data as-is without parsing or // modifications. Data stored in settings can be sensitive, so it's highly // recommended to protect data, e.g. using encryption. - virtual void SaveSettings(const std::string& settings) = 0; + // |name| is the name of settings blob. Could be used as filename. + // Implementation must call or post callback + virtual void SaveSettings(const std::string& name, + const std::string& settings, + const DoneCallback& callback) = 0; + + // Deprecated: only for migration of old configs to version with |name|. + virtual std::string LoadSettings() = 0; protected: virtual ~ConfigStore() {}
diff --git a/include/weave/provider/test/mock_config_store.h b/include/weave/provider/test/mock_config_store.h index 3873251..a7eb374 100644 --- a/include/weave/provider/test/mock_config_store.h +++ b/include/weave/provider/test/mock_config_store.h
@@ -18,10 +18,13 @@ class MockConfigStore : public ConfigStore { public: - MockConfigStore() { + explicit MockConfigStore(bool set_expectations = true) { using testing::_; using testing::Return; + if (!set_expectations) + return; + EXPECT_CALL(*this, LoadDefaults(_)) .WillRepeatedly(testing::Invoke([](Settings* settings) { settings->firmware_version = "TEST_FIRMWARE"; @@ -39,11 +42,21 @@ "version": 1, "device_id": "TEST_DEVICE_ID" })")); - EXPECT_CALL(*this, SaveSettings(_)).WillRepeatedly(Return()); + EXPECT_CALL(*this, LoadSettings(_)).WillRepeatedly(Return("")); + EXPECT_CALL(*this, SaveSettings(_, _, _)) + .WillRepeatedly(testing::WithArgs<1, 2>(testing::Invoke( + [](const std::string& json, const DoneCallback& callback) { + if (!callback.is_null()) + callback.Run(nullptr); + }))); } MOCK_METHOD1(LoadDefaults, bool(Settings*)); + MOCK_METHOD1(LoadSettings, std::string(const std::string&)); + MOCK_METHOD3(SaveSettings, + void(const std::string&, + const std::string&, + const DoneCallback&)); MOCK_METHOD0(LoadSettings, std::string()); - MOCK_METHOD1(SaveSettings, void(const std::string&)); }; } // namespace test
diff --git a/include/weave/settings.h b/include/weave/settings.h index eeb3f93..741fff2 100644 --- a/include/weave/settings.h +++ b/include/weave/settings.h
@@ -71,7 +71,6 @@ // Internal options to tweak some library functionality. External code should // avoid using them. bool wifi_auto_setup_enabled{true}; - bool disable_security{false}; std::string test_privet_ssid; };
diff --git a/src/access_api_handler.cc b/src/access_api_handler.cc new file mode 100644 index 0000000..7c39b20 --- /dev/null +++ b/src/access_api_handler.cc
@@ -0,0 +1,227 @@ +// Copyright 2016 The Weave Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/access_api_handler.h" + +#include <base/bind.h> +#include <weave/device.h> + +#include "src/access_black_list_manager.h" +#include "src/commands/schema_constants.h" +#include "src/data_encoding.h" +#include "src/json_error_codes.h" + +namespace weave { + +namespace { + +const char kComponent[] = "accessControl"; +const char kTrait[] = "_accessControlBlackList"; +const char kStateSize[] = "_accessControlBlackList.size"; +const char kStateCapacity[] = "_accessControlBlackList.capacity"; +const char kUserId[] = "userId"; +const char kApplicationId[] = "applicationId"; +const char kExpirationTimeout[] = "expirationTimeoutSec"; +const char kBlackList[] = "blackList"; + +bool GetIds(const base::DictionaryValue& parameters, + std::vector<uint8_t>* user_id_decoded, + std::vector<uint8_t>* app_id_decoded, + ErrorPtr* error) { + std::string user_id; + parameters.GetString(kUserId, &user_id); + if (!Base64Decode(user_id, user_id_decoded)) { + Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidPropValue, + "Invalid user id '%s'", user_id.c_str()); + return false; + } + + std::string app_id; + parameters.GetString(kApplicationId, &app_id); + if (!Base64Decode(app_id, app_id_decoded)) { + Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidPropValue, + "Invalid app id '%s'", user_id.c_str()); + return false; + } + + return true; +} + +} // namespace + +AccessApiHandler::AccessApiHandler(Device* device, + AccessBlackListManager* manager) + : device_{device}, manager_{manager} { + device_->AddTraitDefinitionsFromJson(R"({ + "_accessControlBlackList": { + "commands": { + "block": { + "minimalRole": "owner", + "parameters": { + "userId": { + "type": "string" + }, + "applicationId": { + "type": "string" + }, + "expirationTimeoutSec": { + "type": "integer" + } + } + }, + "unblock": { + "minimalRole": "owner", + "parameters": { + "userId": { + "type": "string" + }, + "applicationId": { + "type": "string" + } + } + }, + "list": { + "minimalRole": "owner", + "parameters": {}, + "results": { + "blackList": { + "type": "array", + "items": { + "type": "object", + "properties": { + "userId": { + "type": "string" + }, + "applicationId": { + "type": "string" + } + }, + "additionalProperties": false + } + } + } + } + }, + "state": { + "size": { + "type": "integer", + "isRequired": true + }, + "capacity": { + "type": "integer", + "isRequired": true + } + } + } + })"); + CHECK(device_->AddComponent(kComponent, {kTrait}, nullptr)); + UpdateState(); + + device_->AddCommandHandler( + kComponent, "_accessControlBlackList.block", + base::Bind(&AccessApiHandler::Block, weak_ptr_factory_.GetWeakPtr())); + device_->AddCommandHandler( + kComponent, "_accessControlBlackList.unblock", + base::Bind(&AccessApiHandler::Unblock, weak_ptr_factory_.GetWeakPtr())); + device_->AddCommandHandler( + kComponent, "_accessControlBlackList.list", + base::Bind(&AccessApiHandler::List, weak_ptr_factory_.GetWeakPtr())); +} + +void AccessApiHandler::Block(const std::weak_ptr<Command>& cmd) { + auto command = cmd.lock(); + if (!command) + return; + + CHECK(command->GetState() == Command::State::kQueued) + << EnumToString(command->GetState()); + command->SetProgress(base::DictionaryValue{}, nullptr); + + const auto& parameters = command->GetParameters(); + std::vector<uint8_t> user_id; + std::vector<uint8_t> app_id; + ErrorPtr error; + if (!GetIds(parameters, &user_id, &app_id, &error)) { + command->Abort(error.get(), nullptr); + return; + } + + int timeout_sec = 0; + parameters.GetInteger(kExpirationTimeout, &timeout_sec); + + base::Time expiration = + base::Time::Now() + base::TimeDelta::FromSeconds(timeout_sec); + + manager_->Block(user_id, app_id, expiration, + base::Bind(&AccessApiHandler::OnCommandDone, + weak_ptr_factory_.GetWeakPtr(), cmd)); +} + +void AccessApiHandler::Unblock(const std::weak_ptr<Command>& cmd) { + auto command = cmd.lock(); + if (!command) + return; + + CHECK(command->GetState() == Command::State::kQueued) + << EnumToString(command->GetState()); + command->SetProgress(base::DictionaryValue{}, nullptr); + + const auto& parameters = command->GetParameters(); + std::vector<uint8_t> user_id; + std::vector<uint8_t> app_id; + ErrorPtr error; + if (!GetIds(parameters, &user_id, &app_id, &error)) { + command->Abort(error.get(), nullptr); + return; + } + + manager_->Unblock(user_id, app_id, + base::Bind(&AccessApiHandler::OnCommandDone, + weak_ptr_factory_.GetWeakPtr(), cmd)); +} + +void AccessApiHandler::List(const std::weak_ptr<Command>& cmd) { + auto command = cmd.lock(); + if (!command) + return; + + CHECK(command->GetState() == Command::State::kQueued) + << EnumToString(command->GetState()); + command->SetProgress(base::DictionaryValue{}, nullptr); + + std::unique_ptr<base::ListValue> entries{new base::ListValue}; + for (const auto& e : manager_->GetEntries()) { + std::unique_ptr<base::DictionaryValue> entry{new base::DictionaryValue}; + entry->SetString(kUserId, Base64Encode(e.user_id)); + entry->SetString(kApplicationId, Base64Encode(e.app_id)); + entries->Append(entry.release()); + } + + base::DictionaryValue result; + result.Set(kBlackList, entries.release()); + + command->Complete(result, nullptr); +} + +void AccessApiHandler::OnCommandDone(const std::weak_ptr<Command>& cmd, + ErrorPtr error) { + auto command = cmd.lock(); + if (!command) + return; + UpdateState(); + if (error) { + command->Abort(error.get(), nullptr); + return; + } + command->Complete({}, nullptr); +} + +void AccessApiHandler::UpdateState() { + base::DictionaryValue state; + state.SetInteger(kStateSize, manager_->GetSize()); + state.SetInteger(kStateCapacity, manager_->GetCapacity()); + device_->SetStateProperties(kComponent, state, nullptr); +} + +} // namespace weave
diff --git a/src/access_api_handler.h b/src/access_api_handler.h new file mode 100644 index 0000000..821ce02 --- /dev/null +++ b/src/access_api_handler.h
@@ -0,0 +1,47 @@ +// Copyright 2016 The Weave Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBWEAVE_SRC_ACCESS_API_HANDLER_H_ +#define LIBWEAVE_SRC_ACCESS_API_HANDLER_H_ + +#include <memory> + +#include <base/memory/weak_ptr.h> +#include <weave/error.h> + +namespace weave { + +class AccessBlackListManager; +class Command; +class Device; + +// Handles commands for 'accessControlBlackList' trait. +// Objects of the class subscribe for notification from CommandManager and +// execute incoming commands. +// Handled commands: +// accessControlBlackList.block +// accessControlBlackList.unblock +// accessControlBlackList.list +class AccessApiHandler final { + public: + AccessApiHandler(Device* device, AccessBlackListManager* manager); + + private: + void Block(const std::weak_ptr<Command>& command); + void Unblock(const std::weak_ptr<Command>& command); + void List(const std::weak_ptr<Command>& command); + void UpdateState(); + + void OnCommandDone(const std::weak_ptr<Command>& command, ErrorPtr error); + + Device* device_{nullptr}; + AccessBlackListManager* manager_{nullptr}; + + base::WeakPtrFactory<AccessApiHandler> weak_ptr_factory_{this}; + DISALLOW_COPY_AND_ASSIGN(AccessApiHandler); +}; + +} // namespace weave + +#endif // LIBWEAVE_SRC_ACCESS_API_HANDLER_H_
diff --git a/src/access_api_handler_unittest.cc b/src/access_api_handler_unittest.cc new file mode 100644 index 0000000..a142735 --- /dev/null +++ b/src/access_api_handler_unittest.cc
@@ -0,0 +1,257 @@ +// Copyright 2016 The Weave Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/access_api_handler.h" + +#include <gtest/gtest.h> +#include <weave/test/mock_device.h> +#include <weave/test/unittest_utils.h> + +#include "src/component_manager_impl.h" +#include "src/access_black_list_manager.h" +#include "src/data_encoding.h" + +using testing::_; +using testing::AnyOf; +using testing::Invoke; +using testing::Return; +using testing::StrictMock; +using testing::WithArgs; + +namespace weave { + +class MockAccessBlackListManager : public AccessBlackListManager { + public: + MOCK_METHOD4(Block, + void(const std::vector<uint8_t>&, + const std::vector<uint8_t>&, + const base::Time&, + const DoneCallback&)); + MOCK_METHOD3(Unblock, + void(const std::vector<uint8_t>&, + const std::vector<uint8_t>&, + const DoneCallback&)); + MOCK_CONST_METHOD2(IsBlocked, + bool(const std::vector<uint8_t>&, + const std::vector<uint8_t>&)); + MOCK_CONST_METHOD0(GetEntries, std::vector<Entry>()); + MOCK_CONST_METHOD0(GetSize, size_t()); + MOCK_CONST_METHOD0(GetCapacity, size_t()); +}; + +class AccessApiHandlerTest : public ::testing::Test { + protected: + void SetUp() override { + EXPECT_CALL(device_, AddTraitDefinitionsFromJson(_)) + .WillRepeatedly(Invoke([this](const std::string& json) { + EXPECT_TRUE(component_manager_.LoadTraits(json, nullptr)); + })); + EXPECT_CALL(device_, SetStateProperties(_, _, _)) + .WillRepeatedly( + Invoke(&component_manager_, &ComponentManager::SetStateProperties)); + EXPECT_CALL(device_, SetStateProperty(_, _, _, _)) + .WillRepeatedly( + Invoke(&component_manager_, &ComponentManager::SetStateProperty)); + EXPECT_CALL(device_, AddComponent(_, _, _)) + .WillRepeatedly(Invoke([this](const std::string& name, + const std::vector<std::string>& traits, + ErrorPtr* error) { + return component_manager_.AddComponent("", name, traits, error); + })); + + EXPECT_CALL(device_, + AddCommandHandler(_, AnyOf("_accessControlBlackList.block", + "_accessControlBlackList.unblock", + "_accessControlBlackList.list"), + _)) + .WillRepeatedly( + Invoke(&component_manager_, &ComponentManager::AddCommandHandler)); + + EXPECT_CALL(access_manager_, GetSize()).WillRepeatedly(Return(0)); + + EXPECT_CALL(access_manager_, GetCapacity()).WillRepeatedly(Return(10)); + + handler_.reset(new AccessApiHandler{&device_, &access_manager_}); + } + + const base::DictionaryValue& AddCommand(const std::string& command) { + std::string id; + auto command_instance = component_manager_.ParseCommandInstance( + *test::CreateDictionaryValue(command.c_str()), Command::Origin::kLocal, + UserRole::kOwner, &id, nullptr); + EXPECT_NE(nullptr, command_instance.get()); + component_manager_.AddCommand(std::move(command_instance)); + EXPECT_EQ(Command::State::kDone, + component_manager_.FindCommand(id)->GetState()); + return component_manager_.FindCommand(id)->GetResults(); + } + + std::unique_ptr<base::DictionaryValue> GetState() { + std::string path = + component_manager_.FindComponentWithTrait("_accessControlBlackList"); + EXPECT_FALSE(path.empty()); + const auto* component = component_manager_.FindComponent(path, nullptr); + EXPECT_TRUE(component); + const base::DictionaryValue* state = nullptr; + EXPECT_TRUE( + component->GetDictionary("state._accessControlBlackList", &state)); + return std::unique_ptr<base::DictionaryValue>{state->DeepCopy()}; + } + + ComponentManagerImpl component_manager_; + StrictMock<test::MockDevice> device_; + StrictMock<MockAccessBlackListManager> access_manager_; + std::unique_ptr<AccessApiHandler> handler_; +}; + +TEST_F(AccessApiHandlerTest, Initialization) { + const base::DictionaryValue* trait = nullptr; + ASSERT_TRUE(component_manager_.GetTraits().GetDictionary( + "_accessControlBlackList", &trait)); + + auto expected = R"({ + "commands": { + "block": { + "minimalRole": "owner", + "parameters": { + "userId": { + "type": "string" + }, + "applicationId": { + "type": "string" + }, + "expirationTimeoutSec": { + "type": "integer" + } + } + }, + "unblock": { + "minimalRole": "owner", + "parameters": { + "userId": { + "type": "string" + }, + "applicationId": { + "type": "string" + } + } + }, + "list": { + "minimalRole": "owner", + "parameters": {}, + "results": { + "blackList": { + "type": "array", + "items": { + "type": "object", + "properties": { + "userId": { + "type": "string" + }, + "applicationId": { + "type": "string" + } + }, + "additionalProperties": false + } + } + } + } + }, + "state": { + "size": { + "type": "integer", + "isRequired": true + }, + "capacity": { + "type": "integer", + "isRequired": true + } + } + })"; + EXPECT_JSON_EQ(expected, *trait); + + expected = R"({ + "capacity": 10, + "size": 0 + })"; + EXPECT_JSON_EQ(expected, *GetState()); +} + +TEST_F(AccessApiHandlerTest, Block) { + EXPECT_CALL(access_manager_, Block(std::vector<uint8_t>{1, 2, 3}, + std::vector<uint8_t>{3, 4, 5}, _, _)) + .WillOnce(WithArgs<3>( + Invoke([](const DoneCallback& callback) { callback.Run(nullptr); }))); + EXPECT_CALL(access_manager_, GetSize()).WillRepeatedly(Return(1)); + + AddCommand(R"({ + 'name' : '_accessControlBlackList.block', + 'component': 'accessControl', + 'parameters': { + 'userId': 'AQID', + 'applicationId': 'AwQF', + 'expirationTimeoutSec': 1234 + } + })"); + + auto expected = R"({ + "capacity": 10, + "size": 1 + })"; + EXPECT_JSON_EQ(expected, *GetState()); +} + +TEST_F(AccessApiHandlerTest, Unblock) { + EXPECT_CALL(access_manager_, Unblock(std::vector<uint8_t>{1, 2, 3}, + std::vector<uint8_t>{3, 4, 5}, _)) + .WillOnce(WithArgs<2>( + Invoke([](const DoneCallback& callback) { callback.Run(nullptr); }))); + EXPECT_CALL(access_manager_, GetSize()).WillRepeatedly(Return(4)); + + AddCommand(R"({ + 'name' : '_accessControlBlackList.unblock', + 'component': 'accessControl', + 'parameters': { + 'userId': 'AQID', + 'applicationId': 'AwQF', + 'expirationTimeoutSec': 1234 + } + })"); + + auto expected = R"({ + "capacity": 10, + "size": 4 + })"; + EXPECT_JSON_EQ(expected, *GetState()); +} + +TEST_F(AccessApiHandlerTest, List) { + std::vector<AccessBlackListManager::Entry> entries{ + {{11, 12, 13}, {21, 22, 23}, base::Time::FromTimeT(1410000000)}, + {{31, 32, 33}, {41, 42, 43}, base::Time::FromTimeT(1420000000)}, + }; + EXPECT_CALL(access_manager_, GetEntries()).WillOnce(Return(entries)); + EXPECT_CALL(access_manager_, GetSize()).WillRepeatedly(Return(4)); + + auto expected = R"({ + "blackList": [ { + "applicationId": "FRYX", + "userId": "CwwN" + }, { + "applicationId": "KSor", + "userId": "HyAh" + } ] + })"; + + const auto& results = AddCommand(R"({ + 'name' : '_accessControlBlackList.list', + 'component': 'accessControl', + 'parameters': { + } + })"); + + EXPECT_JSON_EQ(expected, results); +} +} // namespace weave
diff --git a/src/access_black_list_manager.h b/src/access_black_list_manager.h new file mode 100644 index 0000000..b56226a --- /dev/null +++ b/src/access_black_list_manager.h
@@ -0,0 +1,56 @@ +// Copyright 2016 The Weave Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBWEAVE_SRC_ACCESS_BLACK_LIST_H_ +#define LIBWEAVE_SRC_ACCESS_BLACK_LIST_H_ + +#include <vector> + +#include <base/time/time.h> + +namespace weave { + +class AccessBlackListManager { + public: + struct Entry { + // user_id is empty, app_id is empty: block everything. + // user_id is not empty, app_id is empty: block if user_id matches. + // user_id is empty, app_id is not empty: block if app_id matches. + // user_id is not empty, app_id is not empty: block if both match. + std::vector<uint8_t> user_id; + std::vector<uint8_t> app_id; + + // Time after which to discard the rule. + base::Time expiration; + }; + virtual ~AccessBlackListManager() = default; + + virtual void Block(const std::vector<uint8_t>& user_id, + const std::vector<uint8_t>& app_id, + const base::Time& expiration, + const DoneCallback& callback) = 0; + virtual void Unblock(const std::vector<uint8_t>& user_id, + const std::vector<uint8_t>& app_id, + const DoneCallback& callback) = 0; + virtual bool IsBlocked(const std::vector<uint8_t>& user_id, + const std::vector<uint8_t>& app_id) const = 0; + virtual std::vector<Entry> GetEntries() const = 0; + virtual size_t GetSize() const = 0; + virtual size_t GetCapacity() const = 0; +}; + +inline bool operator==(const AccessBlackListManager::Entry& l, + const AccessBlackListManager::Entry& r) { + return l.user_id == r.user_id && l.app_id == r.app_id && + l.expiration == r.expiration; +} + +inline bool operator!=(const AccessBlackListManager::Entry& l, + const AccessBlackListManager::Entry& r) { + return !(l == r); +} + +} // namespace weave + +#endif // LIBWEAVE_SRC_ACCESS_BLACK_LIST_H_
diff --git a/src/access_black_list_manager_impl.cc b/src/access_black_list_manager_impl.cc new file mode 100644 index 0000000..992a680 --- /dev/null +++ b/src/access_black_list_manager_impl.cc
@@ -0,0 +1,163 @@ +// Copyright 2016 The Weave Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/access_black_list_manager_impl.h" + +#include <base/json/json_reader.h> +#include <base/json/json_writer.h> +#include <base/values.h> + +#include "src/commands/schema_constants.h" +#include "src/data_encoding.h" + +namespace weave { + +namespace { +const char kConfigFileName[] = "black_list"; + +const char kUser[] = "user"; +const char kApp[] = "app"; +const char kExpiration[] = "expiration"; +} + +AccessBlackListManagerImpl::AccessBlackListManagerImpl( + provider::ConfigStore* store, + size_t capacity, + base::Clock* clock) + : capacity_{capacity}, clock_{clock}, store_{store} { + Load(); +} + +void AccessBlackListManagerImpl::Load() { + if (!store_) + return; + if (auto list = base::ListValue::From( + base::JSONReader::Read(store_->LoadSettings(kConfigFileName)))) { + for (const auto& e : *list) { + const base::DictionaryValue* entry{nullptr}; + std::string user; + std::string app; + decltype(entries_)::key_type key; + int expiration; + if (e->GetAsDictionary(&entry) && entry->GetString(kUser, &user) && + Base64Decode(user, &key.first) && entry->GetString(kApp, &app) && + Base64Decode(app, &key.second) && + entry->GetInteger(kExpiration, &expiration)) { + base::Time expiration_time = base::Time::FromTimeT(expiration); + if (expiration_time > clock_->Now()) + entries_[key] = expiration_time; + } + } + if (entries_.size() < list->GetSize()) { + // Save some storage space by saving without expired entries. + Save({}); + } + } +} + +void AccessBlackListManagerImpl::Save(const DoneCallback& callback) { + if (!store_) { + if (!callback.is_null()) + callback.Run(nullptr); + return; + } + + base::ListValue list; + for (const auto& e : entries_) { + scoped_ptr<base::DictionaryValue> entry{new base::DictionaryValue}; + entry->SetString(kUser, Base64Encode(e.first.first)); + entry->SetString(kApp, Base64Encode(e.first.second)); + entry->SetInteger(kExpiration, e.second.ToTimeT()); + list.Append(std::move(entry)); + } + + std::string json; + base::JSONWriter::Write(list, &json); + store_->SaveSettings(kConfigFileName, json, callback); +} + +void AccessBlackListManagerImpl::RemoveExpired() { + for (auto i = begin(entries_); i != end(entries_);) { + if (i->second <= clock_->Now()) + i = entries_.erase(i); + else + ++i; + } +} + +void AccessBlackListManagerImpl::Block(const std::vector<uint8_t>& user_id, + const std::vector<uint8_t>& app_id, + const base::Time& expiration, + const DoneCallback& callback) { + // Iterating is OK as Save below is more expensive. + RemoveExpired(); + if (expiration <= clock_->Now()) { + if (!callback.is_null()) { + ErrorPtr error; + Error::AddTo(&error, FROM_HERE, "aleady_expired", + "Entry already expired"); + callback.Run(std::move(error)); + } + return; + } + if (entries_.size() >= capacity_) { + if (!callback.is_null()) { + ErrorPtr error; + Error::AddTo(&error, FROM_HERE, "blacklist_is_full", + "Unable to store more entries"); + callback.Run(std::move(error)); + } + return; + } + auto& value = entries_[std::make_pair(user_id, app_id)]; + value = std::max(value, expiration); + Save(callback); +} + +void AccessBlackListManagerImpl::Unblock(const std::vector<uint8_t>& user_id, + const std::vector<uint8_t>& app_id, + const DoneCallback& callback) { + if (!entries_.erase(std::make_pair(user_id, app_id))) { + if (!callback.is_null()) { + ErrorPtr error; + Error::AddTo(&error, FROM_HERE, "entry_not_found", "Unknown entry"); + callback.Run(std::move(error)); + } + return; + } + // Iterating is OK as Save below is more expensive. + RemoveExpired(); + Save(callback); +} + +bool AccessBlackListManagerImpl::IsBlocked( + const std::vector<uint8_t>& user_id, + const std::vector<uint8_t>& app_id) const { + for (const auto& user : {{}, user_id}) { + for (const auto& app : {{}, app_id}) { + auto both = entries_.find(std::make_pair(user, app)); + if (both != end(entries_) && both->second > clock_->Now()) + return true; + } + } + return false; +} + +std::vector<AccessBlackListManager::Entry> +AccessBlackListManagerImpl::GetEntries() const { + std::vector<Entry> result; + for (const auto& e : entries_) + result.push_back({e.first.first, e.first.second, e.second}); + return result; +} + +size_t AccessBlackListManagerImpl::GetSize() const { + return entries_.size(); +} + +size_t AccessBlackListManagerImpl::GetCapacity() const { + return capacity_; +} + +} // namespace weave
diff --git a/src/access_black_list_manager_impl.h b/src/access_black_list_manager_impl.h new file mode 100644 index 0000000..1c175db --- /dev/null +++ b/src/access_black_list_manager_impl.h
@@ -0,0 +1,58 @@ +// Copyright 2016 The Weave Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBWEAVE_SRC_ACCESS_BLACK_LIST_IMPL_H_ +#define LIBWEAVE_SRC_ACCESS_BLACK_LIST_IMPL_H_ + +#include <map> +#include <utility> + +#include <base/time/default_clock.h> +#include <base/time/time.h> +#include <weave/error.h> +#include <weave/provider/config_store.h> + +#include "src/access_black_list_manager.h" + +namespace weave { + +class AccessBlackListManagerImpl : public AccessBlackListManager { + public: + explicit AccessBlackListManagerImpl(provider::ConfigStore* store, + size_t capacity = 1024, + base::Clock* clock = nullptr); + + // AccessBlackListManager implementation. + void Block(const std::vector<uint8_t>& user_id, + const std::vector<uint8_t>& app_id, + const base::Time& expiration, + const DoneCallback& callback) override; + void Unblock(const std::vector<uint8_t>& user_id, + const std::vector<uint8_t>& app_id, + const DoneCallback& callback) override; + bool IsBlocked(const std::vector<uint8_t>& user_id, + const std::vector<uint8_t>& app_id) const override; + std::vector<Entry> GetEntries() const override; + size_t GetSize() const override; + size_t GetCapacity() const override; + + private: + void Load(); + void Save(const DoneCallback& callback); + void RemoveExpired(); + + const size_t capacity_{0}; + base::DefaultClock default_clock_; + base::Clock* clock_{&default_clock_}; + + provider::ConfigStore* store_{nullptr}; + std::map<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>, base::Time> + entries_; + + DISALLOW_COPY_AND_ASSIGN(AccessBlackListManagerImpl); +}; + +} // namespace weave + +#endif // LIBWEAVE_SRC_ACCESS_BLACK_LIST_IMPL_H_
diff --git a/src/access_black_list_manager_impl_unittest.cc b/src/access_black_list_manager_impl_unittest.cc new file mode 100644 index 0000000..fd9f226 --- /dev/null +++ b/src/access_black_list_manager_impl_unittest.cc
@@ -0,0 +1,165 @@ +// Copyright 2016 The Weave Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/access_black_list_manager_impl.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <weave/provider/test/mock_config_store.h> +#include <weave/test/unittest_utils.h> + +#include "src/test/mock_clock.h" +#include "src/bind_lambda.h" + +using testing::_; +using testing::Return; +using testing::StrictMock; + +namespace weave { + +class AccessBlackListManagerImplTest : public testing::Test { + protected: + void SetUp() { + std::string to_load = R"([{ + "user": "BQID", + "app": "BwQF", + "expiration": 1410000000 + }, { + "user": "AQID", + "app": "AwQF", + "expiration": 1419999999 + }])"; + + EXPECT_CALL(config_store_, LoadSettings("black_list")) + .WillOnce(Return(to_load)); + + EXPECT_CALL(config_store_, SaveSettings("black_list", _, _)) + .WillOnce(testing::WithArgs<1, 2>(testing::Invoke( + [](const std::string& json, const DoneCallback& callback) { + std::string to_save = R"([{ + "user": "AQID", + "app": "AwQF", + "expiration": 1419999999 + }])"; + EXPECT_JSON_EQ(to_save, *test::CreateValue(json)); + if (!callback.is_null()) + callback.Run(nullptr); + }))); + + EXPECT_CALL(clock_, Now()) + .WillRepeatedly(Return(base::Time::FromTimeT(1412121212))); + manager_.reset(new AccessBlackListManagerImpl{&config_store_, 10, &clock_}); + } + StrictMock<test::MockClock> clock_; + StrictMock<provider::test::MockConfigStore> config_store_{false}; + std::unique_ptr<AccessBlackListManagerImpl> manager_; +}; + +TEST_F(AccessBlackListManagerImplTest, Init) { + EXPECT_EQ(1u, manager_->GetSize()); + EXPECT_EQ(10u, manager_->GetCapacity()); + EXPECT_EQ((std::vector<AccessBlackListManagerImpl::Entry>{{ + {1, 2, 3}, {3, 4, 5}, base::Time::FromTimeT(1419999999), + }}), + manager_->GetEntries()); +} + +TEST_F(AccessBlackListManagerImplTest, Block) { + EXPECT_CALL(config_store_, SaveSettings("black_list", _, _)) + .WillOnce(testing::WithArgs<1, 2>(testing::Invoke( + [](const std::string& json, const DoneCallback& callback) { + std::string to_save = R"([{ + "user": "AQID", + "app": "AwQF", + "expiration": 1419999999 + }, { + "app": "CAgI", + "user": "BwcH", + "expiration": 1419990000 + }])"; + EXPECT_JSON_EQ(to_save, *test::CreateValue(json)); + if (!callback.is_null()) + callback.Run(nullptr); + }))); + manager_->Block({7, 7, 7}, {8, 8, 8}, base::Time::FromTimeT(1419990000), {}); +} + +TEST_F(AccessBlackListManagerImplTest, BlockExpired) { + manager_->Block({}, {}, base::Time::FromTimeT(1400000000), + base::Bind([](ErrorPtr error) { + EXPECT_TRUE(error->HasError("aleady_expired")); + })); +} + +TEST_F(AccessBlackListManagerImplTest, BlockListIsFull) { + EXPECT_CALL(config_store_, SaveSettings("black_list", _, _)) + .WillRepeatedly(testing::WithArgs<1, 2>(testing::Invoke( + [](const std::string& json, const DoneCallback& callback) { + if (!callback.is_null()) + callback.Run(nullptr); + }))); + for (size_t i = manager_->GetSize(); i < manager_->GetCapacity(); ++i) { + manager_->Block( + {99, static_cast<uint8_t>(i / 256), static_cast<uint8_t>(i % 256)}, + {8, 8, 8}, base::Time::FromTimeT(1419990000), {}); + EXPECT_EQ(i + 1, manager_->GetSize()); + } + manager_->Block({99}, {8, 8, 8}, base::Time::FromTimeT(1419990000), + base::Bind([](ErrorPtr error) { + EXPECT_TRUE(error->HasError("blacklist_is_full")); + })); +} + +TEST_F(AccessBlackListManagerImplTest, Unblock) { + EXPECT_CALL(config_store_, SaveSettings("black_list", _, _)) + .WillOnce(testing::WithArgs<1, 2>(testing::Invoke( + [](const std::string& json, const DoneCallback& callback) { + EXPECT_JSON_EQ("[]", *test::CreateValue(json)); + if (!callback.is_null()) + callback.Run(nullptr); + }))); + manager_->Unblock({1, 2, 3}, {3, 4, 5}, {}); +} + +TEST_F(AccessBlackListManagerImplTest, UnblockNotFound) { + manager_->Unblock({5, 2, 3}, {5, 4, 5}, base::Bind([](ErrorPtr error) { + EXPECT_TRUE(error->HasError("entry_not_found")); + })); +} + +TEST_F(AccessBlackListManagerImplTest, IsBlockedFalse) { + EXPECT_FALSE(manager_->IsBlocked({7, 7, 7}, {8, 8, 8})); +} + +class AccessBlackListManagerImplIsBlockedTest + : public AccessBlackListManagerImplTest, + public testing::WithParamInterface< + std::tuple<std::vector<uint8_t>, std::vector<uint8_t>>> { + public: + void SetUp() override { + AccessBlackListManagerImplTest::SetUp(); + EXPECT_CALL(config_store_, SaveSettings("black_list", _, _)) + .WillOnce(testing::WithArgs<2>( + testing::Invoke([](const DoneCallback& callback) { + if (!callback.is_null()) + callback.Run(nullptr); + }))); + manager_->Block(std::get<0>(GetParam()), std::get<1>(GetParam()), + base::Time::FromTimeT(1419990000), {}); + } +}; + +TEST_P(AccessBlackListManagerImplIsBlockedTest, IsBlocked) { + EXPECT_TRUE(manager_->IsBlocked({7, 7, 7}, {8, 8, 8})); +} + +INSTANTIATE_TEST_CASE_P( + Filters, + AccessBlackListManagerImplIsBlockedTest, + testing::Combine(testing::Values(std::vector<uint8_t>{}, + std::vector<uint8_t>{7, 7, 7}), + testing::Values(std::vector<uint8_t>{}, + std::vector<uint8_t>{8, 8, 8}))); + +} // namespace weave
diff --git a/src/base_api_handler.h b/src/base_api_handler.h index 1dbbac8..6eebfca 100644 --- a/src/base_api_handler.h +++ b/src/base_api_handler.h
@@ -33,7 +33,7 @@ void OnConfigChanged(const Settings& settings); DeviceRegistrationInfo* device_info_; - Device* device_; + Device* device_{nullptr}; base::WeakPtrFactory<BaseApiHandler> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(BaseApiHandler);
diff --git a/src/config.cc b/src/config.cc index 76be205..44d20dd 100644 --- a/src/config.cc +++ b/src/config.cc
@@ -18,9 +18,12 @@ #include "src/data_encoding.h" #include "src/privet/privet_types.h" #include "src/string_utils.h" +#include "src/bind_lambda.h" namespace weave { +const char kConfigName[] = "config"; + namespace config_keys { const char kVersion[] = "version"; @@ -139,9 +142,12 @@ void Config::Transaction::LoadState() { if (!config_->config_store_) return; - std::string json_string = config_->config_store_->LoadSettings(); - if (json_string.empty()) - return; + std::string json_string = config_->config_store_->LoadSettings(kConfigName); + if (json_string.empty()) { + json_string = config_->config_store_->LoadSettings(); + if (json_string.empty()) + return; + } auto value = base::JSONReader::Read(json_string); base::DictionaryValue* dict = nullptr; @@ -266,7 +272,9 @@ base::JSONWriter::WriteWithOptions( dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_string); - config_store_->SaveSettings(json_string); + config_store_->SaveSettings( + kConfigName, json_string, + base::Bind([](ErrorPtr error) { CHECK(!error); })); } Config::Transaction::~Transaction() {
diff --git a/src/config_unittest.cc b/src/config_unittest.cc index 0367516..4b0e5b4 100644 --- a/src/config_unittest.cc +++ b/src/config_unittest.cc
@@ -17,18 +17,20 @@ using testing::_; using testing::Invoke; using testing::Return; +using testing::WithArgs; namespace weave { +const char kConfigName[] = "config"; + class ConfigTest : public ::testing::Test { protected: void SetUp() override { - EXPECT_CALL(*this, OnConfigChanged(_)) - .Times(1); // Called from AddOnChangedCallback Reload(); } void Reload() { + EXPECT_CALL(*this, OnConfigChanged(_)).Times(1); config_.reset(new Config{&config_store_}); config_->AddOnChangedCallback( base::Bind(&ConfigTest::OnConfigChanged, base::Unretained(this))); @@ -66,7 +68,6 @@ EXPECT_FALSE(GetSettings().device_id.empty()); EXPECT_EQ("", GetSettings().firmware_version); EXPECT_TRUE(GetSettings().wifi_auto_setup_enabled); - EXPECT_FALSE(GetSettings().disable_security); EXPECT_EQ("", GetSettings().test_privet_ssid); EXPECT_EQ(std::set<PairingType>{PairingType::kPinCode}, GetSettings().pairing_modes); @@ -86,31 +87,45 @@ } TEST_F(ConfigTest, LoadStateV0) { - EXPECT_CALL(config_store_, LoadSettings()) + EXPECT_CALL(config_store_, LoadSettings(kConfigName)) .WillOnce(Return(R"({ "device_id": "state_device_id" })")); - EXPECT_CALL(*this, OnConfigChanged(_)).Times(1); Reload(); EXPECT_EQ("state_device_id", GetSettings().cloud_id); EXPECT_FALSE(GetSettings().device_id.empty()); EXPECT_NE(GetSettings().cloud_id, GetSettings().device_id); - EXPECT_CALL(config_store_, LoadSettings()) + EXPECT_CALL(config_store_, LoadSettings(kConfigName)) .WillOnce(Return(R"({ "device_id": "state_device_id", "cloud_id": "state_cloud_id" })")); - EXPECT_CALL(*this, OnConfigChanged(_)).Times(1); Reload(); EXPECT_EQ("state_cloud_id", GetSettings().cloud_id); EXPECT_EQ("state_device_id", GetSettings().device_id); } +TEST_F(ConfigTest, LoadStateUnnamed) { + EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return("")); + + EXPECT_CALL(config_store_, LoadSettings()).Times(1); + + Reload(); +} + +TEST_F(ConfigTest, LoadStateNamed) { + EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return("{}")); + + EXPECT_CALL(config_store_, LoadSettings()).Times(0); + + Reload(); +} + TEST_F(ConfigTest, LoadState) { auto state = R"({ "version": 1, @@ -133,9 +148,8 @@ "secret": "c3RhdGVfc2VjcmV0", "service_url": "state_service_url" })"; - EXPECT_CALL(config_store_, LoadSettings()).WillOnce(Return(state)); + EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return(state)); - EXPECT_CALL(*this, OnConfigChanged(_)).Times(1); Reload(); EXPECT_EQ("state_client_id", GetSettings().client_id); @@ -149,8 +163,6 @@ EXPECT_EQ("state_device_id", GetSettings().device_id); EXPECT_EQ(GetDefaultSettings().wifi_auto_setup_enabled, GetSettings().wifi_auto_setup_enabled); - EXPECT_EQ(GetDefaultSettings().disable_security, - GetSettings().disable_security); EXPECT_EQ(GetDefaultSettings().test_privet_ssid, GetSettings().test_privet_ssid); EXPECT_EQ(GetDefaultSettings().pairing_modes, GetSettings().pairing_modes); @@ -243,9 +255,10 @@ EXPECT_CALL(*this, OnConfigChanged(_)).Times(1); - EXPECT_CALL(config_store_, SaveSettings(_)) - .WillOnce(Invoke([](const std::string& json) { - auto expected = R"({ + EXPECT_CALL(config_store_, SaveSettings(kConfigName, _, _)) + .WillOnce(WithArgs<1, 2>( + Invoke([](const std::string& json, const DoneCallback& callback) { + auto expected = R"({ 'version': 1, 'api_key': 'set_api_key', 'client_id': 'set_client_id', @@ -266,8 +279,9 @@ 'secret': 'AQIDBAU=', 'service_url': 'set_service_url' })"; - EXPECT_JSON_EQ(expected, *test::CreateValue(json)); - })); + EXPECT_JSON_EQ(expected, *test::CreateValue(json)); + callback.Run(nullptr); + }))); change.Commit(); }
diff --git a/src/device_manager.cc b/src/device_manager.cc index 04d7a6b..8eed558 100644 --- a/src/device_manager.cc +++ b/src/device_manager.cc
@@ -8,6 +8,8 @@ #include <base/bind.h> +#include "src/access_api_handler.h" +#include "src/access_black_list_manager_impl.h" #include "src/base_api_handler.h" #include "src/commands/schema_constants.h" #include "src/component_manager_impl.h" @@ -40,6 +42,10 @@ network, auth_manager_.get())); base_api_handler_.reset(new BaseApiHandler{device_info_.get(), this}); + black_list_manager_.reset(new AccessBlackListManagerImpl{config_store}); + access_api_handler_.reset( + new AccessApiHandler{this, black_list_manager_.get()}); + device_info_->Start(); if (http_server) {
diff --git a/src/device_manager.h b/src/device_manager.h index d40ba8e..d77bacc 100644 --- a/src/device_manager.h +++ b/src/device_manager.h
@@ -10,6 +10,8 @@ namespace weave { +class AccessApiHandler; +class AccessBlackListManager; class BaseApiHandler; class Config; class ComponentManager; @@ -107,6 +109,8 @@ std::unique_ptr<ComponentManager> component_manager_; std::unique_ptr<DeviceRegistrationInfo> device_info_; std::unique_ptr<BaseApiHandler> base_api_handler_; + std::unique_ptr<AccessBlackListManager> black_list_manager_; + std::unique_ptr<AccessApiHandler> access_api_handler_; std::unique_ptr<privet::Manager> privet_; base::WeakPtrFactory<DeviceManager> weak_ptr_factory_{this};
diff --git a/src/privet/auth_manager.cc b/src/privet/auth_manager.cc index 66d04c4..71a4f39 100644 --- a/src/privet/auth_manager.cc +++ b/src/privet/auth_manager.cc
@@ -18,6 +18,7 @@ extern "C" { #include "third_party/libuweave/src/macaroon.h" +#include "third_party/libuweave/src/macaroon_caveat_internal.h" } namespace weave { @@ -25,9 +26,19 @@ namespace { +const time_t kJ2000ToTimeT = 946684800; const size_t kMaxMacaroonSize = 1024; const size_t kMaxPendingClaims = 10; const char kInvalidTokenError[] = "invalid_token"; +const int kSessionIdTtlMinutes = 1; + +uint32_t ToJ2000Time(const base::Time& time) { + return std::max(time.ToTimeT(), kJ2000ToTimeT) - kJ2000ToTimeT; +} + +base::Time FromJ2000Time(uint32_t time) { + return base::Time::FromTimeT(time + kJ2000ToTimeT); +} template <class T> void AppendToArray(T value, std::vector<uint8_t>* array) { @@ -37,78 +48,98 @@ class Caveat { public: - // TODO(vitalybuka): Use _get_buffer_size_ when available. - Caveat(UwMacaroonCaveatType type, uint32_t value) : buffer(8) { - CHECK(uw_macaroon_caveat_create_with_uint_(type, value, buffer.data(), - buffer.size(), &caveat)); + Caveat(UwMacaroonCaveatType type, size_t str_len) + : buffer_(uw_macaroon_caveat_creation_get_buffsize_(type, str_len)) { + CHECK(!buffer_.empty()); } + const UwMacaroonCaveat& GetCaveat() const { return caveat_; } - // TODO(vitalybuka): Use _get_buffer_size_ when available. - Caveat(UwMacaroonCaveatType type, const std::string& value) - : buffer(std::max<size_t>(value.size(), 32u) * 2) { - CHECK(uw_macaroon_caveat_create_with_str_( - type, reinterpret_cast<const uint8_t*>(value.data()), value.size(), - buffer.data(), buffer.size(), &caveat)); - } - - const UwMacaroonCaveat& GetCaveat() const { return caveat; } - - private: - UwMacaroonCaveat caveat; - std::vector<uint8_t> buffer; + protected: + UwMacaroonCaveat caveat_{}; + std::vector<uint8_t> buffer_; DISALLOW_COPY_AND_ASSIGN(Caveat); }; -bool CheckCaveatType(const UwMacaroonCaveat& caveat, - UwMacaroonCaveatType type, - ErrorPtr* error) { - UwMacaroonCaveatType caveat_type{}; - if (!uw_macaroon_caveat_get_type_(&caveat, &caveat_type)) { - return Error::AddTo(error, FROM_HERE, kInvalidTokenError, - "Unable to get type"); +class ScopeCaveat : public Caveat { + public: + explicit ScopeCaveat(UwMacaroonCaveatScopeType scope) + : Caveat(kUwMacaroonCaveatTypeScope, 0) { + CHECK(uw_macaroon_caveat_create_scope_(scope, buffer_.data(), + buffer_.size(), &caveat_)); } - if (caveat_type != type) { - return Error::AddTo(error, FROM_HERE, kInvalidTokenError, - "Unexpected caveat type"); + DISALLOW_COPY_AND_ASSIGN(ScopeCaveat); +}; + +class TimestampCaveat : public Caveat { + public: + explicit TimestampCaveat(const base::Time& timestamp) + : Caveat(kUwMacaroonCaveatTypeDelegationTimestamp, 0) { + CHECK(uw_macaroon_caveat_create_delegation_timestamp_( + ToJ2000Time(timestamp), buffer_.data(), buffer_.size(), &caveat_)); } - return true; -} + DISALLOW_COPY_AND_ASSIGN(TimestampCaveat); +}; -bool ReadCaveat(const UwMacaroonCaveat& caveat, - UwMacaroonCaveatType type, - uint32_t* value, - ErrorPtr* error) { - if (!CheckCaveatType(caveat, type, error)) - return false; - - if (!uw_macaroon_caveat_get_value_uint_(&caveat, value)) { - return Error::AddTo(error, FROM_HERE, kInvalidTokenError, - "Unable to read caveat"); +class ExpirationCaveat : public Caveat { + public: + explicit ExpirationCaveat(const base::Time& timestamp) + : Caveat(kUwMacaroonCaveatTypeExpirationAbsolute, 0) { + CHECK(uw_macaroon_caveat_create_expiration_absolute_( + ToJ2000Time(timestamp), buffer_.data(), buffer_.size(), &caveat_)); } - return true; -} + DISALLOW_COPY_AND_ASSIGN(ExpirationCaveat); +}; -bool ReadCaveat(const UwMacaroonCaveat& caveat, - UwMacaroonCaveatType type, - std::string* value, - ErrorPtr* error) { - if (!CheckCaveatType(caveat, type, error)) - return false; - - const uint8_t* start{nullptr}; - size_t size{0}; - if (!uw_macaroon_caveat_get_value_str_(&caveat, &start, &size)) { - return Error::AddTo(error, FROM_HERE, kInvalidTokenError, - "Unable to read caveat"); +class UserIdCaveat : public Caveat { + public: + explicit UserIdCaveat(const std::string& id) + : Caveat(kUwMacaroonCaveatTypeDelegateeUser, id.size()) { + CHECK(uw_macaroon_caveat_create_delegatee_user_( + reinterpret_cast<const uint8_t*>(id.data()), id.size(), buffer_.data(), + buffer_.size(), &caveat_)); } - value->assign(reinterpret_cast<const char*>(start), size); - return true; -} + DISALLOW_COPY_AND_ASSIGN(UserIdCaveat); +}; + +class ServiceCaveat : public Caveat { + public: + explicit ServiceCaveat(const std::string& id) + : Caveat(kUwMacaroonCaveatTypeDelegateeService, id.size()) { + CHECK(uw_macaroon_caveat_create_delegatee_service_( + reinterpret_cast<const uint8_t*>(id.data()), id.size(), buffer_.data(), + buffer_.size(), &caveat_)); + } + + DISALLOW_COPY_AND_ASSIGN(ServiceCaveat); +}; + +class SessionIdCaveat : public Caveat { + public: + explicit SessionIdCaveat(const std::string& id) + : Caveat(kUwMacaroonCaveatTypeLanSessionID, id.size()) { + CHECK(uw_macaroon_caveat_create_lan_session_id_( + reinterpret_cast<const uint8_t*>(id.data()), id.size(), buffer_.data(), + buffer_.size(), &caveat_)); + } + + DISALLOW_COPY_AND_ASSIGN(SessionIdCaveat); +}; + +class ClientAuthTokenCaveat : public Caveat { + public: + ClientAuthTokenCaveat() + : Caveat(kUwMacaroonCaveatTypeClientAuthorizationTokenV1, 0) { + CHECK(uw_macaroon_caveat_create_client_authorization_token_( + nullptr, 0, buffer_.data(), buffer_.size(), &caveat_)); + } + + DISALLOW_COPY_AND_ASSIGN(ClientAuthTokenCaveat); +}; std::vector<uint8_t> CreateSecret() { std::vector<uint8_t> secret(kSha256OutputSize); @@ -122,18 +153,53 @@ std::vector<uint8_t> CreateMacaroonToken( const std::vector<uint8_t>& secret, - const std::vector<UwMacaroonCaveat>& caveats) { + const base::Time& time, + const std::vector<const UwMacaroonCaveat*>& caveats) { CHECK_EQ(kSha256OutputSize, secret.size()); + + UwMacaroonContext context{}; + CHECK(uw_macaroon_context_create_(ToJ2000Time(time), nullptr, 0, &context)); + UwMacaroon macaroon{}; - CHECK(uw_macaroon_new_from_root_key_(&macaroon, secret.data(), secret.size(), - caveats.data(), caveats.size())); + CHECK(uw_macaroon_create_from_root_key_(&macaroon, secret.data(), + secret.size(), &context, + caveats.data(), caveats.size())); - std::vector<uint8_t> token(kMaxMacaroonSize); + std::vector<uint8_t> serialized_token(kMaxMacaroonSize); size_t len = 0; - CHECK(uw_macaroon_dump_(&macaroon, token.data(), token.size(), &len)); - token.resize(len); + CHECK(uw_macaroon_serialize_(&macaroon, serialized_token.data(), + serialized_token.size(), &len)); + serialized_token.resize(len); - return token; + return serialized_token; +} + +std::vector<uint8_t> ExtendMacaroonToken( + const UwMacaroon& macaroon, + const base::Time& time, + const std::vector<const UwMacaroonCaveat*>& caveats) { + UwMacaroonContext context{}; + CHECK(uw_macaroon_context_create_(ToJ2000Time(time), nullptr, 0, &context)); + + UwMacaroon prev_macaroon = macaroon; + std::vector<uint8_t> prev_buffer(kMaxMacaroonSize); + std::vector<uint8_t> new_buffer(kMaxMacaroonSize); + + for (auto caveat : caveats) { + UwMacaroon new_macaroon{}; + CHECK(uw_macaroon_extend_(&prev_macaroon, &new_macaroon, &context, caveat, + new_buffer.data(), new_buffer.size())); + new_buffer.swap(prev_buffer); + prev_macaroon = new_macaroon; + } + + std::vector<uint8_t> serialized_token(kMaxMacaroonSize); + size_t len = 0; + CHECK(uw_macaroon_serialize_(&prev_macaroon, serialized_token.data(), + serialized_token.size(), &len)); + serialized_token.resize(len); + + return serialized_token; } bool LoadMacaroon(const std::vector<uint8_t>& token, @@ -141,8 +207,8 @@ UwMacaroon* macaroon, ErrorPtr* error) { buffer->resize(kMaxMacaroonSize); - if (!uw_macaroon_load_(token.data(), token.size(), buffer->data(), - buffer->size(), macaroon)) { + if (!uw_macaroon_deserialize_(token.data(), token.size(), buffer->data(), + buffer->size(), macaroon)) { return Error::AddTo(error, FROM_HERE, kInvalidTokenError, "Invalid token format"); } @@ -151,10 +217,16 @@ bool VerifyMacaroon(const std::vector<uint8_t>& secret, const UwMacaroon& macaroon, + const base::Time& time, + UwMacaroonValidationResult* result, ErrorPtr* error) { CHECK_EQ(kSha256OutputSize, secret.size()); - if (!uw_macaroon_verify_(&macaroon, secret.data(), secret.size())) { - return Error::AddTo(error, FROM_HERE, "invalid_signature", + UwMacaroonContext context = {}; + CHECK(uw_macaroon_context_create_(ToJ2000Time(time), nullptr, 0, &context)); + + if (!uw_macaroon_validate_(&macaroon, secret.data(), secret.size(), &context, + result)) { + return Error::AddTo(error, FROM_HERE, "invalid_token", "Invalid token signature"); } return true; @@ -239,15 +311,16 @@ std::vector<uint8_t> AuthManager::CreateAccessToken(const UserInfo& user_info, base::TimeDelta ttl) const { - Caveat scope{kUwMacaroonCaveatTypeScope, ToMacaroonScope(user_info.scope())}; - Caveat user{kUwMacaroonCaveatTypeIdentifier, user_info.user_id()}; - Caveat issued{kUwMacaroonCaveatTypeExpiration, - static_cast<uint32_t>((Now() + ttl).ToTimeT())}; + ScopeCaveat scope{ToMacaroonScope(user_info.scope())}; + UserIdCaveat user{user_info.user_id()}; + const base::Time now = Now(); + ExpirationCaveat expiration{now + ttl}; return CreateMacaroonToken( - access_secret_, + access_secret_, now, { - scope.GetCaveat(), user.GetCaveat(), issued.GetCaveat(), + &scope.GetCaveat(), &user.GetCaveat(), &expiration.GetCaveat(), }); + return {}; } bool AuthManager::ParseAccessToken(const std::vector<uint8_t>& token, @@ -256,35 +329,28 @@ std::vector<uint8_t> buffer; UwMacaroon macaroon{}; - uint32_t scope{0}; - std::string user_id; - uint32_t expiration{0}; - + UwMacaroonValidationResult result{}; + const base::Time now = Now(); if (!LoadMacaroon(token, &buffer, &macaroon, error) || - !VerifyMacaroon(access_secret_, macaroon, error) || macaroon.num_caveats != 3 || - !ReadCaveat(macaroon.caveats[0], kUwMacaroonCaveatTypeScope, &scope, - error) || - !ReadCaveat(macaroon.caveats[1], kUwMacaroonCaveatTypeIdentifier, - &user_id, error) || - !ReadCaveat(macaroon.caveats[2], kUwMacaroonCaveatTypeExpiration, - &expiration, error)) { + !VerifyMacaroon(access_secret_, macaroon, now, &result, error)) { return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization, "Invalid token"); } - AuthScope auth_scope{FromMacaroonScope(scope)}; + AuthScope auth_scope{FromMacaroonScope(result.granted_scope)}; if (auth_scope == AuthScope::kNone) { return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthorization, "Invalid token data"); } - base::Time time{base::Time::FromTimeT(expiration)}; - if (time < clock_->Now()) { - return Error::AddTo(error, FROM_HERE, errors::kAuthorizationExpired, - "Token is expired"); - } - + // If token is valid and token was not extended, it should has precisely this + // values. + CHECK_GE(FromJ2000Time(result.expiration_time), now); + CHECK_EQ(1u, result.num_delegatees); + CHECK_EQ(kUwMacaroonDelegateeTypeUser, result.delegatees[0].type); + std::string user_id{reinterpret_cast<const char*>(result.delegatees[0].id), + result.delegatees[0].id_len}; if (user_info) *user_info = UserInfo{auth_scope, user_id}; @@ -309,7 +375,7 @@ std::unique_ptr<AuthManager>{new AuthManager{nullptr, {}}}, owner)); if (pending_claims_.size() > kMaxPendingClaims) pending_claims_.pop_front(); - return pending_claims_.back().first->GetRootClientAuthToken(); + return pending_claims_.back().first->GetRootClientAuthToken(owner); } bool AuthManager::ConfirmClientAuthToken(const std::vector<uint8_t>& token, @@ -332,14 +398,20 @@ return true; } -std::vector<uint8_t> AuthManager::GetRootClientAuthToken() const { - Caveat scope{kUwMacaroonCaveatTypeScope, kUwMacaroonCaveatScopeTypeOwner}; - Caveat issued{kUwMacaroonCaveatTypeIssued, - static_cast<uint32_t>(Now().ToTimeT())}; - return CreateMacaroonToken(auth_secret_, - { - scope.GetCaveat(), issued.GetCaveat(), - }); +std::vector<uint8_t> AuthManager::GetRootClientAuthToken( + RootClientTokenOwner owner) const { + CHECK(RootClientTokenOwner::kNone != owner); + ClientAuthTokenCaveat auth_token; + const base::Time now = Now(); + TimestampCaveat issued{now}; + + ServiceCaveat client{owner == RootClientTokenOwner::kCloud ? "google.com" + : "privet"}; + return CreateMacaroonToken( + auth_secret_, now, + { + &auth_token.GetCaveat(), &issued.GetCaveat(), &client.GetCaveat(), + }); } base::Time AuthManager::Now() const { @@ -350,8 +422,9 @@ ErrorPtr* error) const { std::vector<uint8_t> buffer; UwMacaroon macaroon{}; + UwMacaroonValidationResult result{}; if (!LoadMacaroon(token, &buffer, &macaroon, error) || - !VerifyMacaroon(auth_secret_, macaroon, error)) { + !VerifyMacaroon(auth_secret_, macaroon, Now(), &result, error)) { return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, "Invalid token"); } @@ -365,19 +438,54 @@ AuthScope* access_token_scope, base::TimeDelta* access_token_ttl, ErrorPtr* error) const { - // TODO(vitalybuka): implement token validation. - if (!IsValidAuthToken(auth_token, error)) - return false; + std::vector<uint8_t> buffer; + UwMacaroon macaroon{}; + UwMacaroonValidationResult result{}; + const base::Time now = Now(); + if (!LoadMacaroon(auth_token, &buffer, &macaroon, error) || + !VerifyMacaroon(auth_secret_, macaroon, now, &result, error)) { + return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, + "Invalid token"); + } + + AuthScope auth_scope{FromMacaroonScope(result.granted_scope)}; + if (auth_scope == AuthScope::kNone) { + return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, + "Invalid token data"); + } + + // TODO: Integrate black list checks. + auto delegates_rbegin = std::reverse_iterator<const UwMacaroonDelegateeInfo*>( + result.delegatees + result.num_delegatees); + auto delegates_rend = + std::reverse_iterator<const UwMacaroonDelegateeInfo*>(result.delegatees); + auto last_user_id = + std::find_if(delegates_rbegin, delegates_rend, + [](const UwMacaroonDelegateeInfo& delegatee) { + return delegatee.type == kUwMacaroonDelegateeTypeUser; + }); + + if (last_user_id == delegates_rend || !last_user_id->id_len) { + return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, + "User ID is missing"); + } + + const char* session_id = reinterpret_cast<const char*>(result.lan_session_id); + if (!IsValidSessionId({session_id, session_id + result.lan_session_id_len})) { + return Error::AddTo(error, FROM_HERE, errors::kInvalidAuthCode, + "Invalid session id"); + } + + CHECK_GE(FromJ2000Time(result.expiration_time), now); if (!access_token) return true; - // TODO(vitalybuka): User and scope must be parsed from auth_token. - UserInfo info{config_ ? config_->GetSettings().local_anonymous_access_role - : AuthScope::kViewer, - base::GenerateGUID()}; + std::string user_id{reinterpret_cast<const char*>(last_user_id->id), + last_user_id->id_len}; + UserInfo info{auth_scope, user_id}; - // TODO(vitalybuka): TTL also should be reduced in accordance with auth_token. + ttl = std::min(ttl, FromJ2000Time(result.expiration_time) - now); *access_token = CreateAccessToken(info, ttl); if (access_token_scope) @@ -388,11 +496,39 @@ return true; } -std::vector<uint8_t> AuthManager::CreateSessionId() { - std::vector<uint8_t> result; - AppendToArray(Now().ToTimeT(), &result); - AppendToArray(++session_counter_, &result); - return result; +std::string AuthManager::CreateSessionId() const { + return std::to_string(ToJ2000Time(Now())) + ":" + + std::to_string(++session_counter_); +} + +bool AuthManager::IsValidSessionId(const std::string& session_id) const { + base::Time ssid_time = FromJ2000Time(std::atoi(session_id.c_str())); + return Now() - base::TimeDelta::FromMinutes(kSessionIdTtlMinutes) <= + ssid_time && + ssid_time <= Now(); +} + +std::vector<uint8_t> AuthManager::DelegateToUser( + const std::vector<uint8_t>& token, + base::TimeDelta ttl, + const UserInfo& user_info) const { + std::vector<uint8_t> buffer; + UwMacaroon macaroon{}; + CHECK(LoadMacaroon(token, &buffer, &macaroon, nullptr)); + + const base::Time now = Now(); + TimestampCaveat issued{now}; + ExpirationCaveat expiration{now + ttl}; + ScopeCaveat scope{ToMacaroonScope(user_info.scope())}; + UserIdCaveat user{user_info.user_id()}; + SessionIdCaveat session{CreateSessionId()}; + + return ExtendMacaroonToken( + macaroon, now, + { + &issued.GetCaveat(), &expiration.GetCaveat(), &scope.GetCaveat(), + &user.GetCaveat(), &session.GetCaveat(), + }); } } // namespace privet
diff --git a/src/privet/auth_manager.h b/src/privet/auth_manager.h index 309d80e..f0a5761 100644 --- a/src/privet/auth_manager.h +++ b/src/privet/auth_manager.h
@@ -9,6 +9,7 @@ #include <string> #include <vector> +#include <base/gtest_prod_util.h> #include <base/time/default_clock.h> #include <base/time/time.h> #include <weave/error.h> @@ -54,7 +55,7 @@ bool ConfirmClientAuthToken(const std::vector<uint8_t>& token, ErrorPtr* error); - std::vector<uint8_t> GetRootClientAuthToken() const; + std::vector<uint8_t> GetRootClientAuthToken(RootClientTokenOwner owner) const; bool IsValidAuthToken(const std::vector<uint8_t>& token, ErrorPtr* error) const; bool CreateAccessTokenFromAuth(const std::vector<uint8_t>& auth_token, @@ -67,13 +68,21 @@ void SetAuthSecret(const std::vector<uint8_t>& secret, RootClientTokenOwner owner); - std::vector<uint8_t> CreateSessionId(); + std::string CreateSessionId() const; + bool IsValidSessionId(const std::string& session_id) const; private: + friend class AuthManagerTest; + + // Test helpers. Device does not need to implement delegation. + std::vector<uint8_t> DelegateToUser(const std::vector<uint8_t>& token, + base::TimeDelta ttl, + const UserInfo& user_info) const; + Config* config_{nullptr}; // Can be nullptr for tests. base::DefaultClock default_clock_; base::Clock* clock_{&default_clock_}; - uint32_t session_counter_{0}; + mutable uint32_t session_counter_{0}; std::vector<uint8_t> auth_secret_; // Persistent. std::vector<uint8_t> certificate_fingerprint_;
diff --git a/src/privet/auth_manager_unittest.cc b/src/privet/auth_manager_unittest.cc index 70750ad..2adb1c2 100644 --- a/src/privet/auth_manager_unittest.cc +++ b/src/privet/auth_manager_unittest.cc
@@ -29,6 +29,11 @@ } protected: + std::vector<uint8_t> DelegateToUser(const std::vector<uint8_t>& token, + base::TimeDelta ttl, + const UserInfo& user_info) const { + return auth_.DelegateToUser(token, ttl, user_info); + } const std::vector<uint8_t> kSecret1{ 78, 40, 39, 68, 29, 19, 70, 86, 38, 61, 13, 55, 33, 32, 51, 52, 34, 43, 97, 48, 8, 56, 11, 99, 50, 59, 24, 26, 31, 71, 76, 28}; @@ -64,18 +69,18 @@ } TEST_F(AuthManagerTest, CreateAccessToken) { - EXPECT_EQ("UABRUHgcSZDry0bvIsoJv+WDQgEURQJjMjM0RgUaVArkgA==", + EXPECT_EQ("WCKDQgEURQlDMjM0RgUaG52hAFA3hFh7TexW1jC96sU4CxvN", Base64Encode(auth_.CreateAccessToken( UserInfo{AuthScope::kViewer, "234"}, {}))); - EXPECT_EQ("UL7YEruLg5QQRDIp2+u1cqCDQgEIRQJjMjU3RgUaVArkgA==", + EXPECT_EQ("WCKDQgEIRQlDMjU3RgUaG52hAFD3dEHl3Y9Y28uoUESiYuLq", Base64Encode(auth_.CreateAccessToken( UserInfo{AuthScope::kManager, "257"}, {}))); - EXPECT_EQ("UPFGeZRanR1wLGYLP5ZDkXiDQgECRQJjNDU2RgUaVArkgA==", + EXPECT_EQ("WCKDQgECRQlDNDU2RgUaG52hAFBy35bQdtvlqYf+Y/ANyxLU", Base64Encode(auth_.CreateAccessToken( UserInfo{AuthScope::kOwner, "456"}, {}))); auto new_time = clock_.Now() + base::TimeDelta::FromDays(11); EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); - EXPECT_EQ("UMm9KlF3OEtZFBmhScJpl4uDQgEORQJjMzQ1RgUaVBllAA==", + EXPECT_EQ("WCKDQgEORQlDMzQ1RgUaG6whgFD1HGVxL8+FPaf/U0bOkXr8", Base64Encode(auth_.CreateAccessToken( UserInfo{AuthScope::kUser, "345"}, {}))); } @@ -129,6 +134,10 @@ .WillRepeatedly(Return(kStartTime + base::TimeDelta::FromSeconds(i))); EXPECT_TRUE(auth.ParseAccessToken(token, &user_info, nullptr)); + auto extended = DelegateToUser(token, base::TimeDelta::FromSeconds(1000), + UserInfo{AuthScope::kUser, "234"}); + EXPECT_FALSE(auth.ParseAccessToken(extended, &user_info, nullptr)); + EXPECT_CALL(clock_, Now()) .WillRepeatedly( Return(kStartTime + base::TimeDelta::FromSeconds(i + 1))); @@ -137,35 +146,135 @@ } TEST_F(AuthManagerTest, GetRootClientAuthToken) { - EXPECT_EQ("UK1ACOc3cWGjGBoTIX2bd3qCQgECRgMaVArkgA==", - Base64Encode(auth_.GetRootClientAuthToken())); + EXPECT_EQ("WCaDQxkgAUYIGhudoQBIDEZwcml2ZXRQ5aV4jIdY2JGosyU0APnQpA==", + Base64Encode( + auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient))); +} + +TEST_F(AuthManagerTest, GetRootClientAuthTokenDifferentOwner) { + EXPECT_EQ( + "WCqDQxkgAUYIGhudoQBMDEpnb29nbGUuY29tUOoLAxSUAZAAv54drarqhag=", + Base64Encode(auth_.GetRootClientAuthToken(RootClientTokenOwner::kCloud))); } TEST_F(AuthManagerTest, GetRootClientAuthTokenDifferentTime) { auto new_time = clock_.Now() + base::TimeDelta::FromDays(15); EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); - EXPECT_EQ("UBpNF8g/GbNUmAyHg1qqJr+CQgECRgMaVB6rAA==", - Base64Encode(auth_.GetRootClientAuthToken())); + EXPECT_EQ("WCaDQxkgAUYIGhuxZ4BIDEZwcml2ZXRQsDNy7gcfJT/yvRs3/q40oA==", + Base64Encode( + auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient))); } TEST_F(AuthManagerTest, GetRootClientAuthTokenDifferentSecret) { AuthManager auth{kSecret2, {}, kSecret1, &clock_}; - EXPECT_EQ("UFTBUcgd9d0HnPRnLeroN2mCQgECRgMaVArkgA==", - Base64Encode(auth.GetRootClientAuthToken())); + EXPECT_EQ( + "WCaDQxkgAUYIGhudoQBIDEZwcml2ZXRQKw9xcidyzrelxUkgkLmv1g==", + Base64Encode(auth.GetRootClientAuthToken(RootClientTokenOwner::kClient))); } TEST_F(AuthManagerTest, IsValidAuthToken) { - EXPECT_TRUE(auth_.IsValidAuthToken(auth_.GetRootClientAuthToken(), nullptr)); + EXPECT_TRUE(auth_.IsValidAuthToken( + auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient), nullptr)); // Multiple attempts with random secrets. for (size_t i = 0; i < 1000; ++i) { AuthManager auth{{}, {}, {}, &clock_}; - auto token = auth.GetRootClientAuthToken(); + auto token = auth.GetRootClientAuthToken(RootClientTokenOwner::kClient); EXPECT_FALSE(auth_.IsValidAuthToken(token, nullptr)); EXPECT_TRUE(auth.IsValidAuthToken(token, nullptr)); } } +TEST_F(AuthManagerTest, CreateSessionId) { + EXPECT_EQ("463315200:1", auth_.CreateSessionId()); +} + +TEST_F(AuthManagerTest, IsValidSessionId) { + EXPECT_TRUE(auth_.IsValidSessionId("463315200:1")); + EXPECT_TRUE(auth_.IsValidSessionId("463315200:2")); + EXPECT_TRUE(auth_.IsValidSessionId("463315150")); + + // Future + EXPECT_FALSE(auth_.IsValidSessionId("463315230:1")); + + // Expired + EXPECT_FALSE(auth_.IsValidSessionId("463315100:1")); +} + +TEST_F(AuthManagerTest, CreateAccessTokenFromAuth) { + std::vector<uint8_t> access_token; + AuthScope scope; + base::TimeDelta ttl; + auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kCloud); + auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000), + UserInfo{AuthScope::kUser, "234"}); + EXPECT_EQ( + "WE+IQxkgAUYIGhudoQBMDEpnb29nbGUuY29tRggaG52hAEYFGhudpOhCAQ5FCUMyMzRNEUs0" + "NjMzMTUyMDA6MVCRVKU+0SpOoBppnwqdKMwP", + Base64Encode(extended)); + EXPECT_TRUE( + auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), + &access_token, &scope, &ttl, nullptr)); + UserInfo user_info; + EXPECT_TRUE(auth_.ParseAccessToken(access_token, &user_info, nullptr)); + EXPECT_EQ(scope, user_info.scope()); + EXPECT_EQ(AuthScope::kUser, user_info.scope()); + + EXPECT_EQ("234", user_info.user_id()); +} + +TEST_F(AuthManagerTest, CreateAccessTokenFromAuthNotMinted) { + std::vector<uint8_t> access_token; + auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); + ErrorPtr error; + EXPECT_FALSE(auth_.CreateAccessTokenFromAuth( + root, base::TimeDelta::FromDays(1), nullptr, nullptr, nullptr, &error)); + EXPECT_TRUE(error->HasError("invalidAuthCode")); +} + +TEST_F(AuthManagerTest, CreateAccessTokenFromAuthValidateAfterSomeTime) { + auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); + auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000), + UserInfo{AuthScope::kUser, "234"}); + + // new_time < session_id_expiration < token_expiration. + auto new_time = clock_.Now() + base::TimeDelta::FromSeconds(15); + EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); + EXPECT_TRUE( + auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), + nullptr, nullptr, nullptr, nullptr)); +} + +TEST_F(AuthManagerTest, CreateAccessTokenFromAuthExpired) { + auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); + auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(10), + UserInfo{AuthScope::kUser, "234"}); + ErrorPtr error; + + // token_expiration < new_time < session_id_expiration. + auto new_time = clock_.Now() + base::TimeDelta::FromSeconds(15); + EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); + EXPECT_FALSE( + auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), + nullptr, nullptr, nullptr, &error)); + EXPECT_TRUE(error->HasError("invalidAuthCode")); +} + +TEST_F(AuthManagerTest, CreateAccessTokenFromAuthExpiredSessionid) { + auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); + auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000), + UserInfo{AuthScope::kUser, "234"}); + ErrorPtr error; + + // session_id_expiration < new_time < token_expiration. + auto new_time = clock_.Now() + base::TimeDelta::FromSeconds(200); + EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); + EXPECT_FALSE( + auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), + nullptr, nullptr, nullptr, &error)); + EXPECT_TRUE(error->HasError("invalidAuthCode")); +} + class AuthManagerClaimTest : public testing::Test { public: void SetUp() override { EXPECT_EQ(auth_.GetAuthSecret().size(), 32u); } @@ -241,18 +350,5 @@ EXPECT_FALSE(auth_.ConfirmClientAuthToken(token, nullptr)); } -TEST_F(AuthManagerClaimTest, CreateAccessTokenFromAuth) { - std::vector<uint8_t> access_token; - AuthScope scope; - base::TimeDelta ttl; - EXPECT_TRUE(auth_.CreateAccessTokenFromAuth( - auth_.GetRootClientAuthToken(), base::TimeDelta::FromDays(1), - &access_token, &scope, &ttl, nullptr)); - UserInfo user_info; - EXPECT_TRUE(auth_.ParseAccessToken(access_token, &user_info, nullptr)); - EXPECT_EQ(scope, user_info.scope()); - EXPECT_FALSE(user_info.user_id().empty()); -} - } // namespace privet } // namespace weave
diff --git a/src/privet/openssl_utils.cc b/src/privet/openssl_utils.cc index f38fd1a..17ebf70 100644 --- a/src/privet/openssl_utils.cc +++ b/src/privet/openssl_utils.cc
@@ -18,13 +18,9 @@ std::vector<uint8_t> HmacSha256(const std::vector<uint8_t>& key, const std::vector<uint8_t>& data) { std::vector<uint8_t> mac(kSha256OutputSize); - uint8_t hmac_state[uw_crypto_hmac_required_buffer_size_()]; - CHECK(uw_crypto_hmac_init_(hmac_state, sizeof(hmac_state), key.data(), - key.size())); - CHECK(uw_crypto_hmac_update_(hmac_state, sizeof(hmac_state), data.data(), - data.size())); - CHECK(uw_crypto_hmac_final_(hmac_state, sizeof(hmac_state), mac.data(), - mac.size())); + const UwCryptoHmacMsg messages[] = {{data.data(), data.size()}}; + CHECK(uw_crypto_hmac_(key.data(), key.size(), messages, arraysize(messages), + mac.data(), mac.size())); return mac; }
diff --git a/src/privet/privet_manager.cc b/src/privet/privet_manager.cc index edc7907..9c717ce 100644 --- a/src/privet/privet_manager.cc +++ b/src/privet/privet_manager.cc
@@ -53,8 +53,6 @@ CHECK(auth_manager); CHECK(device); - disable_security_ = device->GetSettings().disable_security; - device_ = DeviceDelegate::CreateDefault( task_runner_, http_server->GetHttpPort(), http_server->GetHttpsPort(), http_server->GetRequestTimeout()); @@ -129,9 +127,6 @@ const std::shared_ptr<provider::HttpServer::Request>& request, const std::string& data) { std::string auth_header = request->GetFirstHeader(http::kAuthorization); - if (auth_header.empty() && disable_security_) - auth_header = "Privet anonymous"; - base::DictionaryValue empty; auto value = base::JSONReader::Read(data); const base::DictionaryValue* dictionary = ∅
diff --git a/src/privet/privet_manager.h b/src/privet/privet_manager.h index 371d843..06eb89a 100644 --- a/src/privet/privet_manager.h +++ b/src/privet/privet_manager.h
@@ -79,7 +79,6 @@ void OnChanged(); void OnConnectivityChanged(); - bool disable_security_{false}; provider::TaskRunner* task_runner_{nullptr}; std::unique_ptr<CloudDelegate> cloud_; std::unique_ptr<DeviceDelegate> device_;
diff --git a/src/privet/privet_types.cc b/src/privet/privet_types.cc index dd291b3..9e50f94 100644 --- a/src/privet/privet_types.cc +++ b/src/privet/privet_types.cc
@@ -52,7 +52,6 @@ }; const EnumToStringMap<CryptoType>::Map kCryptoTypeMap[] = { - {CryptoType::kNone, "none"}, {CryptoType::kSpake_p224, "p224_spake2"}, };
diff --git a/src/privet/privet_types.h b/src/privet/privet_types.h index c738865..49c4522 100644 --- a/src/privet/privet_types.h +++ b/src/privet/privet_types.h
@@ -15,7 +15,6 @@ namespace privet { enum class CryptoType { - kNone, kSpake_p224, };
diff --git a/src/privet/security_manager.cc b/src/privet/security_manager.cc index 358876d..04164b3 100644 --- a/src/privet/security_manager.cc +++ b/src/privet/security_manager.cc
@@ -67,25 +67,6 @@ crypto::P224EncryptedKeyExchange spake_; }; -class UnsecureKeyExchanger : public SecurityManager::KeyExchanger { - public: - explicit UnsecureKeyExchanger(const std::string& password) - : password_(password) {} - ~UnsecureKeyExchanger() override = default; - - // SecurityManager::KeyExchanger methods. - const std::string& GetMessage() override { return password_; } - - bool ProcessMessage(const std::string& message, ErrorPtr* error) override { - return true; - } - - const std::string& GetKey() const override { return password_; } - - private: - std::string password_; -}; - } // namespace SecurityManager::SecurityManager(const Config* config, @@ -218,8 +199,6 @@ std::set<CryptoType> SecurityManager::GetCryptoTypes() const { std::set<CryptoType> result{CryptoType::kSpake_p224}; - if (GetSettings().disable_security) - result.insert(CryptoType::kNone); return result; } @@ -259,8 +238,6 @@ bool SecurityManager::IsValidPairingCode( const std::vector<uint8_t>& auth_code) const { - if (GetSettings().disable_security) - return true; for (const auto& session : confirmed_sessions_) { const std::string& key = session.second->GetKey(); const std::string& id = session.first; @@ -309,11 +286,6 @@ case CryptoType::kSpake_p224: spake.reset(new Spakep224Exchanger(code)); break; - case CryptoType::kNone: - if (GetSettings().disable_security) { - spake.reset(new UnsecureKeyExchanger(code)); - break; - } // Fall through... default: return Error::AddTo(error, FROM_HERE, errors::kInvalidParams, @@ -416,7 +388,7 @@ } std::string SecurityManager::CreateSessionId() { - return Base64Encode(auth_manager_->CreateSessionId()); + return auth_manager_->CreateSessionId(); } void SecurityManager::RegisterPairingListeners( @@ -428,9 +400,6 @@ } bool SecurityManager::CheckIfPairingAllowed(ErrorPtr* error) { - if (GetSettings().disable_security) - return true; - if (block_pairing_until_ > auth_manager_->Now()) { return Error::AddTo(error, FROM_HERE, errors::kDeviceBusy, "Too many pairing attempts");
diff --git a/src/weave_unittest.cc b/src/weave_unittest.cc index 5bef931..ebc66cd 100644 --- a/src/weave_unittest.cc +++ b/src/weave_unittest.cc
@@ -204,10 +204,6 @@ }))); } - void InitConfigStore() { - EXPECT_CALL(config_store_, SaveSettings("")).WillRepeatedly(Return()); - } - void InitNetwork() { EXPECT_CALL(network_, AddConnectionChangedCallback(_)) .WillRepeatedly(Invoke( @@ -267,7 +263,6 @@ } void InitDefaultExpectations() { - InitConfigStore(); InitNetwork(); EXPECT_CALL(wifi_, StartAccessPoint(MatchesRegex("TEST_NAME.*prv"))) .WillOnce(Return()); @@ -360,13 +355,11 @@ } TEST_F(WeaveTest, StartMinimal) { - InitConfigStore(); device_ = weave::Device::Create(&config_store_, &task_runner_, &http_client_, &network_, nullptr, nullptr, &wifi_, nullptr); } TEST_F(WeaveTest, StartNoWifi) { - InitConfigStore(); InitNetwork(); InitHttpServer(); InitDnsSd(); @@ -450,7 +443,6 @@ void SetUp() override { WeaveTest::SetUp(); - InitConfigStore(); InitHttpServer(); InitNetwork(); InitDnsSd();
diff --git a/third_party/libuweave/src/crypto_hmac.c b/third_party/libuweave/src/crypto_hmac.c index 8b75133..d3dca65 100644 --- a/third_party/libuweave/src/crypto_hmac.c +++ b/third_party/libuweave/src/crypto_hmac.c
@@ -11,41 +11,24 @@ #include <openssl/evp.h> #include <openssl/hmac.h> -size_t uw_crypto_hmac_required_buffer_size_() { - return sizeof(HMAC_CTX); -} - -bool uw_crypto_hmac_init_(uint8_t* state_buffer, - size_t state_buffer_len, - const uint8_t* key, - size_t key_len) { - if (sizeof(HMAC_CTX) > state_buffer_len) { +bool uw_crypto_hmac_(const uint8_t* key, + size_t key_len, + const UwCryptoHmacMsg messages[], + size_t num_messages, + uint8_t* truncated_digest, + size_t truncated_digest_len) { + HMAC_CTX context = {0}; + HMAC_CTX_init(&context); + if (!HMAC_Init(&context, key, key_len, EVP_sha256())) return false; - } - HMAC_CTX* context = (HMAC_CTX*)state_buffer; - HMAC_CTX_init(context); - return HMAC_Init(context, key, key_len, EVP_sha256()); -} -bool uw_crypto_hmac_update_(uint8_t* state_buffer, - size_t state_buffer_len, - const uint8_t* data, - size_t data_len) { - if (sizeof(HMAC_CTX) > state_buffer_len) { - return false; + for (size_t i = 0; i < num_messages; ++i) { + if (messages[i].num_bytes && + (!messages[i].bytes || + !HMAC_Update(&context, messages[i].bytes, messages[i].num_bytes))) { + return false; + } } - HMAC_CTX* context = (HMAC_CTX*)state_buffer; - return HMAC_Update(context, data, data_len); -} - -bool uw_crypto_hmac_final_(uint8_t* state_buffer, - size_t state_buffer_len, - uint8_t* truncated_digest, - size_t truncated_digest_len) { - if (sizeof(HMAC_CTX) > state_buffer_len) { - return false; - } - HMAC_CTX* context = (HMAC_CTX*)state_buffer; const size_t kFullDigestLen = (size_t)EVP_MD_size(EVP_sha256()); if (truncated_digest_len > kFullDigestLen) { @@ -55,8 +38,8 @@ uint8_t digest[kFullDigestLen]; uint32_t len = kFullDigestLen; - bool result = HMAC_Final(context, digest, &len) && kFullDigestLen == len; - HMAC_CTX_cleanup(context); + bool result = HMAC_Final(&context, digest, &len) && kFullDigestLen == len; + HMAC_CTX_cleanup(&context); if (result) { memcpy(truncated_digest, digest, truncated_digest_len); }
diff --git a/third_party/libuweave/src/crypto_hmac.h b/third_party/libuweave/src/crypto_hmac.h index bac634a..6f76ed0 100644 --- a/third_party/libuweave/src/crypto_hmac.h +++ b/third_party/libuweave/src/crypto_hmac.h
@@ -9,21 +9,21 @@ #include <stddef.h> #include <stdint.h> -// Return the minimum required number of bytes for the state_buffer used in the -// init, update and final functions. -size_t uw_crypto_hmac_required_buffer_size_(); +typedef struct { + const uint8_t* bytes; + size_t num_bytes; +} UwCryptoHmacMsg; -bool uw_crypto_hmac_init_(uint8_t* state_buffer, - size_t state_buffer_len, - const uint8_t* key, - size_t key_len); -bool uw_crypto_hmac_update_(uint8_t* state_buffer, - size_t state_buffer_len, - const uint8_t* data, - size_t data_len); -bool uw_crypto_hmac_final_(uint8_t* state_buffer, - size_t state_buffer_len, - uint8_t* truncated_digest, - size_t truncated_digest_len); +/** + * Compute HMAC over a list of messages, which is equivalent to computing HMAC + * over the concatenation of all the messages. The HMAC output will be truncated + * to the desired length truncated_digest_len, and written into trucated_digest. + */ +bool uw_crypto_hmac_(const uint8_t* key, + size_t key_len, + const UwCryptoHmacMsg messages[], + size_t num_messages, + uint8_t* truncated_digest, + size_t truncated_digest_len); #endif // LIBUWEAVE_SRC_CRYPTO_HMAC_H_
diff --git a/third_party/libuweave/src/crypto_utils.c b/third_party/libuweave/src/crypto_utils.c index 76b8068..7a6e38f 100644 --- a/third_party/libuweave/src/crypto_utils.c +++ b/third_party/libuweave/src/crypto_utils.c
@@ -7,13 +7,6 @@ bool uw_crypto_utils_equal_(const uint8_t* arr1, const uint8_t* arr2, size_t len) { - if (arr1 == NULL || arr2 == NULL) { - if (arr1 == NULL && arr2 == NULL && len == 0) { - return true; - } - return false; - } - uint8_t diff = 0; for (size_t i = 0; i < len; i++) { diff |= arr1[i] ^ arr2[i];
diff --git a/third_party/libuweave/src/macaroon.c b/third_party/libuweave/src/macaroon.c index 70afda1..aa775c2 100644 --- a/third_party/libuweave/src/macaroon.c +++ b/third_party/libuweave/src/macaroon.c
@@ -8,13 +8,17 @@ #include "src/crypto_utils.h" #include "src/macaroon_caveat.h" +#include "src/macaroon_caveat_internal.h" #include "src/macaroon_encoding.h" -static bool create_mac_tag_(const uint8_t* key, size_t key_len, - const UwMacaroonCaveat* caveats, size_t num_caveats, +static bool create_mac_tag_(const uint8_t* key, + size_t key_len, + const UwMacaroonContext* context, + const UwMacaroonCaveat* const caveats[], + size_t num_caveats, uint8_t mac_tag[UW_MACAROON_MAC_LEN]) { - if (key == NULL || key_len == 0 || caveats == NULL || num_caveats == 0 || - mac_tag == NULL) { + if (key == NULL || key_len == 0 || context == NULL || caveats == NULL || + num_caveats == 0 || mac_tag == NULL) { return false; } @@ -26,15 +30,15 @@ uint8_t mac_tag_buff[UW_MACAROON_MAC_LEN]; // Compute the first tag by using the key - if (!uw_macaroon_caveat_sign_(key, key_len, &(caveats[0]), mac_tag_buff, + if (!uw_macaroon_caveat_sign_(key, key_len, context, caveats[0], mac_tag_buff, UW_MACAROON_MAC_LEN)) { return false; } // Compute the rest of the tags by using the tag as the key for (size_t i = 1; i < num_caveats; i++) { - if (!uw_macaroon_caveat_sign_(mac_tag_buff, UW_MACAROON_MAC_LEN, - &(caveats[i]), mac_tag_buff, + if (!uw_macaroon_caveat_sign_(mac_tag_buff, UW_MACAROON_MAC_LEN, context, + caveats[i], mac_tag_buff, UW_MACAROON_MAC_LEN)) { return false; } @@ -44,33 +48,38 @@ return true; } -bool uw_macaroon_new_from_mac_tag_(UwMacaroon* new_macaroon, - const uint8_t mac_tag[UW_MACAROON_MAC_LEN], - const UwMacaroonCaveat* caveats, - size_t num_caveats) { - if (new_macaroon == NULL || mac_tag == NULL || caveats == NULL || - num_caveats == 0) { +static bool verify_mac_tag_(const uint8_t* root_key, + size_t root_key_len, + const UwMacaroonContext* context, + const UwMacaroonCaveat* const caveats[], + size_t num_caveats, + const uint8_t mac_tag[UW_MACAROON_MAC_LEN]) { + if (root_key == NULL || root_key_len == 0 || context == NULL || + caveats == NULL || num_caveats == 0 || mac_tag == 0) { return false; } - memcpy(new_macaroon->mac_tag, mac_tag, UW_MACAROON_MAC_LEN); - new_macaroon->num_caveats = num_caveats; - new_macaroon->caveats = caveats; + uint8_t computed_mac_tag[UW_MACAROON_MAC_LEN] = {0}; + if (!create_mac_tag_(root_key, root_key_len, context, caveats, num_caveats, + computed_mac_tag)) { + return false; + } - return true; + return uw_crypto_utils_equal_(mac_tag, computed_mac_tag, UW_MACAROON_MAC_LEN); } -bool uw_macaroon_new_from_root_key_(UwMacaroon* new_macaroon, - const uint8_t* root_key, - size_t root_key_len, - const UwMacaroonCaveat* caveats, - size_t num_caveats) { - if (new_macaroon == NULL || root_key == NULL || root_key_len == 0 || - caveats == NULL || num_caveats == 0) { +bool uw_macaroon_create_from_root_key_(UwMacaroon* new_macaroon, + const uint8_t* root_key, + size_t root_key_len, + const UwMacaroonContext* context, + const UwMacaroonCaveat* const caveats[], + size_t num_caveats) { + if (new_macaroon == NULL || root_key == NULL || context == NULL || + root_key_len == 0 || caveats == NULL || num_caveats == 0) { return false; } - if (!create_mac_tag_(root_key, root_key_len, caveats, num_caveats, + if (!create_mac_tag_(root_key, root_key_len, context, caveats, num_caveats, new_macaroon->mac_tag)) { return false; } @@ -81,139 +90,228 @@ return true; } -bool uw_macaroon_verify_(const UwMacaroon* macaroon, - const uint8_t* root_key, - size_t root_key_len) { - if (macaroon == NULL || root_key == NULL) { - return false; - } - - uint8_t mac_tag[UW_MACAROON_MAC_LEN] = {0}; - if (!create_mac_tag_(root_key, root_key_len, macaroon->caveats, - macaroon->num_caveats, mac_tag)) { - return false; - } - - return uw_crypto_utils_equal_(mac_tag, macaroon->mac_tag, - UW_MACAROON_MAC_LEN); -} - bool uw_macaroon_extend_(const UwMacaroon* old_macaroon, UwMacaroon* new_macaroon, + const UwMacaroonContext* context, const UwMacaroonCaveat* additional_caveat, - uint8_t* buffer, size_t buffer_size) { - if (old_macaroon == NULL || new_macaroon == NULL || + uint8_t* buffer, + size_t buffer_size) { + if (old_macaroon == NULL || new_macaroon == NULL || context == NULL || additional_caveat == NULL || buffer == NULL || buffer_size == 0) { return false; } new_macaroon->num_caveats = old_macaroon->num_caveats + 1; - // Extend the caveat list - if ((new_macaroon->num_caveats) * sizeof(UwMacaroonCaveat) > buffer_size) { - // Not enough memory to store the extended caveat list + // Extend the caveat pointer list + if ((new_macaroon->num_caveats) * sizeof(UwMacaroonCaveat*) > buffer_size) { + // Not enough memory to store the extended caveat pointer list return false; } - UwMacaroonCaveat* extended_list = (UwMacaroonCaveat*)buffer; - if (old_macaroon->caveats != NULL && extended_list != old_macaroon->caveats) { + const UwMacaroonCaveat** extended_list = (const UwMacaroonCaveat**)buffer; + if (new_macaroon->caveats != old_macaroon->caveats) { memcpy(extended_list, old_macaroon->caveats, - (old_macaroon->num_caveats) * sizeof(UwMacaroonCaveat)); + old_macaroon->num_caveats * sizeof(old_macaroon->caveats[0])); } - extended_list[old_macaroon->num_caveats] = *additional_caveat; - new_macaroon->caveats = extended_list; + extended_list[old_macaroon->num_caveats] = additional_caveat; + new_macaroon->caveats = (const UwMacaroonCaveat* const*)extended_list; // Compute the new MAC tag - return create_mac_tag_(old_macaroon->mac_tag, UW_MACAROON_MAC_LEN, - additional_caveat, 1, new_macaroon->mac_tag); + return create_mac_tag_(old_macaroon->mac_tag, UW_MACAROON_MAC_LEN, context, + new_macaroon->caveats + old_macaroon->num_caveats, 1, + new_macaroon->mac_tag); +} + +static void init_validation_result(UwMacaroonValidationResult* result) { + // Start from the largest scope + result->granted_scope = kUwMacaroonCaveatScopeTypeOwner; + result->expiration_time = UINT32_MAX; + result->weave_app_restricted = false; + result->lan_session_id = NULL; + result->lan_session_id_len = 0; + result->num_delegatees = 0; +} + +/** Reset the result object to the lowest scope when encountering errors */ +static void reset_validation_result(UwMacaroonValidationResult* result) { + // Start from the largest scope or highest privilege + result->granted_scope = + (UwMacaroonCaveatScopeType)UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE; + result->expiration_time = 0; + result->weave_app_restricted = true; + result->lan_session_id = NULL; + result->lan_session_id_len = 0; + + result->num_delegatees = 0; + for (size_t i = 0; i < MAX_NUM_DELEGATEES; i++) { + result->delegatees[i].id = NULL; + result->delegatees[i].id_len = 0; + result->delegatees[i].type = kUwMacaroonDelegateeTypeNone; + } +} + +bool uw_macaroon_validate_(const UwMacaroon* macaroon, + const uint8_t* root_key, + size_t root_key_len, + const UwMacaroonContext* context, + UwMacaroonValidationResult* result) { + if (result == NULL) { + return false; + } + init_validation_result(result); + + if (root_key == NULL || root_key_len == 0 || macaroon == NULL || + context == NULL || result == NULL || + !verify_mac_tag_(root_key, root_key_len, context, macaroon->caveats, + macaroon->num_caveats, macaroon->mac_tag)) { + return false; + } + + UwMacaroonValidationState state; + if (!uw_macaroon_caveat_init_validation_state_(&state)) { + return false; + } + for (size_t i = 0; i < macaroon->num_caveats; i++) { + if (!uw_macaroon_caveat_validate_(macaroon->caveats[i], context, &state, + result)) { + reset_validation_result(result); // Reset the result object + return false; + } + } + + return true; } // Encode a Macaroon to a byte string -bool uw_macaroon_dump_(const UwMacaroon* macaroon, - uint8_t* out, - size_t out_len, - size_t* resulting_str_len) { - if (macaroon == NULL || out == NULL || out_len == 0 || +bool uw_macaroon_serialize_(const UwMacaroon* macaroon, + uint8_t* out, + size_t out_len, + size_t* resulting_str_len) { + if (macaroon == NULL || out == NULL || + out_len < UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN || resulting_str_len == NULL) { return false; } - size_t offset = 0, item_len; + // Need to encode the whole Macaroon again into a byte string. - if (!uw_macaroon_encoding_encode_byte_str_( - macaroon->mac_tag, UW_MACAROON_MAC_LEN, out, out_len, &item_len)) { - return false; - } - offset += item_len; - - if (!uw_macaroon_encoding_encode_array_len_( - (uint32_t)(macaroon->num_caveats), out + offset, out_len - offset, &item_len)) { + // First encode the part without the overall byte string header to the buffer + // to get the total length. + size_t item_len = 0; + // Start with an offset + size_t offset = UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN; + if (!uw_macaroon_encoding_encode_array_len_((uint32_t)(macaroon->num_caveats), + out + offset, out_len - offset, + &item_len)) { return false; } offset += item_len; for (size_t i = 0; i < macaroon->num_caveats; i++) { if (!uw_macaroon_encoding_encode_byte_str_( - macaroon->caveats[i].bytes, macaroon->caveats[i].num_bytes, + macaroon->caveats[i]->bytes, macaroon->caveats[i]->num_bytes, out + offset, out_len - offset, &item_len)) { return false; } offset += item_len; } - *resulting_str_len = offset; + if (!uw_macaroon_encoding_encode_byte_str_(macaroon->mac_tag, + UW_MACAROON_MAC_LEN, out + offset, + out_len - offset, &item_len)) { + return false; + } + offset += item_len; + + // Encode the length of the body at the beginning of the buffer + size_t bstr_len = offset - UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN; + if (!uw_macaroon_encoding_encode_byte_str_len_( + bstr_len, out, UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN, &item_len)) { + return false; + } + + // Move the body part to be adjacent to the byte string header part + memmove(out + item_len, out + UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN, + bstr_len); + + *resulting_str_len = item_len + bstr_len; return true; } // Decode a byte string to a Macaroon -bool uw_macaroon_load_(const uint8_t* in, - size_t in_len, - uint8_t* caveats_buffer, - size_t caveats_buffer_size, - UwMacaroon* macaroon) { - if (in == NULL || in_len == 0 || caveats_buffer == NULL || - caveats_buffer_size == 0 || macaroon == NULL) { +bool uw_macaroon_deserialize_(const uint8_t* in, + size_t in_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroon* macaroon) { + if (in == NULL || in_len == 0 || buffer == NULL || buffer_size == 0 || + macaroon == NULL) { return false; } - const uint8_t* tag; - size_t tag_len; - if (!uw_macaroon_encoding_decode_byte_str_(in, in_len, &tag, &tag_len) || - tag_len != UW_MACAROON_MAC_LEN) { + size_t offset = 0; + size_t item_len = 0; + + const uint8_t* bstr = NULL; + size_t bstr_len = 0; + if (!uw_macaroon_encoding_decode_byte_str_(in + offset, in_len - offset, + &bstr, &bstr_len)) { return false; } - memcpy(macaroon->mac_tag, tag, UW_MACAROON_MAC_LEN); + item_len = bstr - in; // The length of the first byte string header + offset += item_len; - size_t offset = 0, cbor_item_len; - if (!uw_macaroon_encoding_get_item_len_(in, in_len, &cbor_item_len)) { + if (item_len + bstr_len != in_len) { + // The string length doesn't match return false; } - offset += cbor_item_len; - uint32_t array_len; + uint32_t array_len = 0; if (!uw_macaroon_encoding_decode_array_len_(in + offset, in_len - offset, &array_len)) { return false; } macaroon->num_caveats = (size_t)array_len; - if (caveats_buffer_size < array_len * sizeof(UwMacaroonCaveat)) { + if (buffer_size < + (array_len * (sizeof(UwMacaroonCaveat) + sizeof(UwMacaroonCaveat*)))) { + // Need two levels of abstraction, one for structs and one for pointers return false; } - UwMacaroonCaveat* caveats = (UwMacaroonCaveat*)caveats_buffer; - for (size_t i = 0; i < array_len; i++) { - if (!uw_macaroon_encoding_get_item_len_(in + offset, in_len - offset, - &cbor_item_len)) { - return false; - } - offset += cbor_item_len; - - if (!uw_macaroon_encoding_decode_byte_str_(in + offset, in_len - offset, - &(caveats[i].bytes), - &(caveats[i].num_bytes))) { - return false; - } + if (!uw_macaroon_encoding_get_item_len_(in + offset, in_len - offset, + &item_len)) { + return false; } - macaroon->caveats = caveats; + offset += item_len; + + const UwMacaroonCaveat** caveat_pointers = (const UwMacaroonCaveat**)buffer; + buffer += array_len * sizeof(UwMacaroonCaveat*); + UwMacaroonCaveat* caveat_structs = (UwMacaroonCaveat*)buffer; + for (size_t i = 0; i < array_len; i++) { + caveat_pointers[i] = &(caveat_structs[i]); + + if (!uw_macaroon_encoding_decode_byte_str_( + in + offset, in_len - offset, &(caveat_structs[i].bytes), + &(caveat_structs[i].num_bytes))) { + return false; + } + + if (!uw_macaroon_encoding_get_item_len_(in + offset, in_len - offset, + &item_len)) { + return false; + } + offset += item_len; + } + macaroon->caveats = caveat_pointers; + + const uint8_t* tag; + size_t tag_len; + if (!uw_macaroon_encoding_decode_byte_str_(in + offset, in_len - offset, &tag, + &tag_len) || + tag_len != UW_MACAROON_MAC_LEN) { + return false; + } + memcpy(macaroon->mac_tag, tag, UW_MACAROON_MAC_LEN); return true; }
diff --git a/third_party/libuweave/src/macaroon.h b/third_party/libuweave/src/macaroon.h index 61242f7..c93bbb2 100644 --- a/third_party/libuweave/src/macaroon.h +++ b/third_party/libuweave/src/macaroon.h
@@ -9,7 +9,8 @@ #include <stddef.h> #include <stdint.h> -#include "macaroon_caveat.h" +#include "src/macaroon_caveat.h" +#include "src/macaroon_context.h" #define UW_MACAROON_MAC_LEN 16 @@ -20,45 +21,78 @@ typedef struct { uint8_t mac_tag[UW_MACAROON_MAC_LEN]; size_t num_caveats; - const UwMacaroonCaveat* caveats; + const UwMacaroonCaveat* const* caveats; } UwMacaroon; -bool uw_macaroon_new_from_mac_tag_(UwMacaroon* new_macaroon, - const uint8_t mac_tag[UW_MACAROON_MAC_LEN], - const UwMacaroonCaveat* caveats, - size_t num_caveats); +// For the delegatee list in the validation result object +typedef enum { + kUwMacaroonDelegateeTypeNone = 0, + kUwMacaroonDelegateeTypeUser = 1, + kUwMacaroonDelegateeTypeApp = 2, + kUwMacaroonDelegateeTypeService = 3, +} UwMacaroonDelegateeType; -bool uw_macaroon_new_from_root_key_(UwMacaroon* new_macaroon, - const uint8_t* root_key, - size_t root_key_len, - const UwMacaroonCaveat* caveats, - size_t num_caveats); +typedef struct { + const uint8_t* id; + size_t id_len; + UwMacaroonDelegateeType type; +} UwMacaroonDelegateeInfo; -bool uw_macaroon_verify_(const UwMacaroon* macaroon, - const uint8_t* root_key, - size_t root_key_len); +#define MAX_NUM_DELEGATEES 10 -// Create a new macaroon with a new caveat +typedef struct { + UwMacaroonCaveatScopeType granted_scope; + uint32_t expiration_time; + bool weave_app_restricted; + const uint8_t* lan_session_id; + size_t lan_session_id_len; + UwMacaroonDelegateeInfo delegatees[MAX_NUM_DELEGATEES]; + size_t num_delegatees; +} UwMacaroonValidationResult; + +bool uw_macaroon_create_from_root_key_(UwMacaroon* new_macaroon, + const uint8_t* root_key, + size_t root_key_len, + const UwMacaroonContext* context, + const UwMacaroonCaveat* const caveats[], + size_t num_caveats); + +/** Creates a new macaroon with a new caveat. */ bool uw_macaroon_extend_(const UwMacaroon* old_macaroon, UwMacaroon* new_macaroon, + const UwMacaroonContext* context, const UwMacaroonCaveat* additional_caveat, - uint8_t* buffer, size_t buffer_size); + uint8_t* buffer, + size_t buffer_size); -// Encode a Macaroon to a byte string -bool uw_macaroon_dump_(const UwMacaroon* macaroon, - uint8_t* out, - size_t out_len, - size_t* resulting_str_len); +/** + * Verify and validate the Macaroon, and put relevant information into the + * result object. + */ +bool uw_macaroon_validate_( + const UwMacaroon* macaroon, + const uint8_t* root_key, + size_t root_key_len, + const UwMacaroonContext* context, + UwMacaroonValidationResult* result); -// Decode a byte string to a Macaroon (the caveats_buffer here is used only for -// the caveat pointer list *caveats in the UwMacaroon *macaroon). One note is -// that the function doesn't copy string values to new buffers, so the caller -// may maintain the input string around to make caveats with string values to -// be usuable. -bool uw_macaroon_load_(const uint8_t* in, - size_t in_len, - uint8_t* caveats_buffer, - size_t caveats_buffer_size, - UwMacaroon* macaroon); +/** Encode a Macaroon to a byte string. */ +bool uw_macaroon_serialize_(const UwMacaroon* macaroon, + uint8_t* out, + size_t out_len, + size_t* resulting_str_len); + +/** + * Decodes a byte string to a Macaroon. + * + * One note is that the function doesn't copy string values to new buffers, so + * the caller must maintain the input string around to make caveats with string + * values to be usable. + */ +bool uw_macaroon_deserialize_(const uint8_t* in, + size_t in_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroon* new_macaroon); #endif // LIBUWEAVE_SRC_MACAROON_H_
diff --git a/third_party/libuweave/src/macaroon_caveat.c b/third_party/libuweave/src/macaroon_caveat.c index 594f9de..a2b26dc 100644 --- a/third_party/libuweave/src/macaroon_caveat.c +++ b/third_party/libuweave/src/macaroon_caveat.c
@@ -3,125 +3,298 @@ // found in the LICENSE file. #include "src/macaroon_caveat.h" +#include "src/macaroon_caveat_internal.h" #include <string.h> #include "src/crypto_hmac.h" +#include "src/macaroon.h" #include "src/macaroon_context.h" #include "src/macaroon_encoding.h" -// TODO(bozhu): Find a better way to pre-allocate memory for HMACc computations? -// Are C99 variable-length arrays allowed on embedded devices? -#define HMAC_STATE_BUFFER_SIZE 1024 +static bool is_valid_caveat_type_(UwMacaroonCaveatType type) { + switch (type) { + case kUwMacaroonCaveatTypeNonce: + case kUwMacaroonCaveatTypeScope: + case kUwMacaroonCaveatTypeExpirationAbsolute: + case kUwMacaroonCaveatTypeTTL1Hour: + case kUwMacaroonCaveatTypeTTL24Hour: + case kUwMacaroonCaveatTypeDelegationTimestamp: + case kUwMacaroonCaveatTypeDelegateeUser: + case kUwMacaroonCaveatTypeDelegateeApp: + case kUwMacaroonCaveatTypeAppCommandsOnly: + case kUwMacaroonCaveatTypeDelegateeService: + case kUwMacaroonCaveatTypeBleSessionID: + case kUwMacaroonCaveatTypeLanSessionID: + case kUwMacaroonCaveatTypeClientAuthorizationTokenV1: + case kUwMacaroonCaveatTypeServerAuthenticationTokenV1: + return true; + } + return false; +} -static bool create_caveat_(UwMacaroonCaveatType type, const void* value, - size_t value_len, uint8_t* buffer, - size_t buffer_size, UwMacaroonCaveat* caveat) { - if (buffer == NULL || buffer_size == 0 || caveat == NULL) { - // Here value can be NULL, and value_len can be 0 +static bool is_valid_scope_type_(UwMacaroonCaveatScopeType type) { + switch (type) { + case kUwMacaroonCaveatScopeTypeOwner: + case kUwMacaroonCaveatScopeTypeManager: + case kUwMacaroonCaveatScopeTypeUser: + case kUwMacaroonCaveatScopeTypeViewer: + return true; + } + return false; +} + +static bool create_caveat_no_value_(UwMacaroonCaveatType type, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + // (buffer_size == 0 || get_buffer_size_() > buffer_size) will conver the case + // that get_buffer_size_() returns 0 (for errors), so there is no need to + // check get_buffer_size_() == 0 again. + if (buffer == NULL || buffer_size == 0 || new_caveat == NULL || + uw_macaroon_caveat_creation_get_buffsize_(type, 0) > buffer_size) { return false; } - caveat->bytes = buffer; - size_t encoded_str_len, total_str_len; - - uint32_t unsigned_int = (uint32_t)type; - if (!uw_macaroon_encoding_encode_uint_(unsigned_int, buffer, buffer_size, + size_t encoded_str_len = 0, total_str_len = 0; + if (!uw_macaroon_encoding_encode_uint_((uint32_t)type, buffer, buffer_size, &encoded_str_len)) { return false; } - total_str_len = encoded_str_len; - buffer += encoded_str_len; - buffer_size -= encoded_str_len; - - switch (type) { - case kUwMacaroonCaveatTypeStop: - case kUwMacaroonCaveatTypeSessionIdentifier: - // No value - encoded_str_len = 0; - break; - - case kUwMacaroonCaveatTypeScope: - case kUwMacaroonCaveatTypeIssued: - case kUwMacaroonCaveatTypeTTL: - case kUwMacaroonCaveatTypeExpiration: - // Integer - if (value_len != sizeof(uint32_t)) { - // Wrong size for integers - return false; - } - unsigned_int = *((uint32_t*)value); - if (!uw_macaroon_encoding_encode_uint_(unsigned_int, buffer, buffer_size, - &encoded_str_len)) { - return false; - } - break; - - case kUwMacaroonCaveatTypeIdentifier: - // Text string - if (!uw_macaroon_encoding_encode_text_str_((uint8_t*)value, value_len, - buffer, buffer_size, - &encoded_str_len)) { - return false; - } - break; - - default: - // Should never reach here - return false; - } - total_str_len += encoded_str_len; - caveat->num_bytes = total_str_len; + + new_caveat->bytes = buffer; + new_caveat->num_bytes = total_str_len; return true; } -bool uw_macaroon_caveat_create_without_value_(UwMacaroonCaveatType type, +static bool create_caveat_uint_value_(UwMacaroonCaveatType type, + uint32_t unsigned_int, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + if (buffer == NULL || buffer_size == 0 || new_caveat == NULL || + uw_macaroon_caveat_creation_get_buffsize_(type, 0) > buffer_size) { + return false; + } + + size_t encoded_str_len = 0, total_str_len = 0; + if (!uw_macaroon_encoding_encode_uint_((uint32_t)type, buffer, buffer_size, + &encoded_str_len)) { + return false; + } + total_str_len += encoded_str_len; + if (!uw_macaroon_encoding_encode_uint_(unsigned_int, buffer + total_str_len, + buffer_size - total_str_len, + &encoded_str_len)) { + return false; + } + total_str_len += encoded_str_len; + + new_caveat->bytes = buffer; + new_caveat->num_bytes = total_str_len; + return true; +} + +static bool create_caveat_bstr_value_(UwMacaroonCaveatType type, + const uint8_t* str, + size_t str_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + if (str == NULL || str_len == 0 || buffer == NULL || buffer_size == 0 || + new_caveat == NULL || + uw_macaroon_caveat_creation_get_buffsize_(type, str_len) > buffer_size) { + return false; + } + + size_t encoded_str_len = 0, total_str_len = 0; + if (!uw_macaroon_encoding_encode_uint_((uint32_t)type, buffer, buffer_size, + &encoded_str_len)) { + return false; + } + total_str_len += encoded_str_len; + if (!uw_macaroon_encoding_encode_byte_str_( + str, str_len, buffer + total_str_len, buffer_size - total_str_len, + &encoded_str_len)) { + return false; + } + total_str_len += encoded_str_len; + + new_caveat->bytes = buffer; + new_caveat->num_bytes = total_str_len; + return true; +} + +size_t uw_macaroon_caveat_creation_get_buffsize_(UwMacaroonCaveatType type, + size_t str_len) { + switch (type) { + // No values + case kUwMacaroonCaveatTypeTTL1Hour: + case kUwMacaroonCaveatTypeTTL24Hour: + case kUwMacaroonCaveatTypeAppCommandsOnly: + case kUwMacaroonCaveatTypeBleSessionID: + return UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN; + + // Unsigned integers + case kUwMacaroonCaveatTypeScope: + case kUwMacaroonCaveatTypeExpirationAbsolute: + case kUwMacaroonCaveatTypeDelegationTimestamp: + return 2 * UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN; + + // Byte strings + case kUwMacaroonCaveatTypeNonce: + case kUwMacaroonCaveatTypeDelegateeUser: + case kUwMacaroonCaveatTypeDelegateeApp: + case kUwMacaroonCaveatTypeDelegateeService: + case kUwMacaroonCaveatTypeLanSessionID: + case kUwMacaroonCaveatTypeClientAuthorizationTokenV1: + case kUwMacaroonCaveatTypeServerAuthenticationTokenV1: + return str_len + UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN; + + default: + return 0; // For errors + } +} + +bool uw_macaroon_caveat_create_nonce_(const uint8_t* nonce, + size_t nonce_size, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + return create_caveat_bstr_value_(kUwMacaroonCaveatTypeNonce, nonce, + nonce_size, buffer, buffer_size, new_caveat); +} + +bool uw_macaroon_caveat_create_scope_(UwMacaroonCaveatScopeType scope, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + if (!is_valid_scope_type_(scope)) { + return false; + } + + return create_caveat_uint_value_(kUwMacaroonCaveatTypeScope, scope, buffer, + buffer_size, new_caveat); +} + +bool uw_macaroon_caveat_create_expiration_absolute_( + uint32_t expiration_time, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + return create_caveat_uint_value_(kUwMacaroonCaveatTypeExpirationAbsolute, + expiration_time, buffer, buffer_size, + new_caveat); +} + +bool uw_macaroon_caveat_create_ttl_1_hour_(uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + return create_caveat_no_value_(kUwMacaroonCaveatTypeTTL1Hour, buffer, + buffer_size, new_caveat); +} + +bool uw_macaroon_caveat_create_ttl_24_hour_(uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + return create_caveat_no_value_(kUwMacaroonCaveatTypeTTL24Hour, buffer, + buffer_size, new_caveat); +} + +bool uw_macaroon_caveat_create_delegation_timestamp_( + uint32_t timestamp, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + return create_caveat_uint_value_(kUwMacaroonCaveatTypeDelegationTimestamp, + timestamp, buffer, buffer_size, new_caveat); +} + +bool uw_macaroon_caveat_create_delegatee_user_(const uint8_t* id_str, + size_t id_str_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + return create_caveat_bstr_value_(kUwMacaroonCaveatTypeDelegateeUser, id_str, + id_str_len, buffer, buffer_size, new_caveat); +} + +bool uw_macaroon_caveat_create_delegatee_app_(const uint8_t* id_str, + size_t id_str_len, uint8_t* buffer, size_t buffer_size, - UwMacaroonCaveat* caveat) { - if (buffer == NULL || buffer_size == 0 || caveat == NULL) { - return false; - } - if (type != kUwMacaroonCaveatTypeStop && - type != kUwMacaroonCaveatTypeSessionIdentifier) { - return false; - } - - return create_caveat_(type, NULL, 0, buffer, buffer_size, caveat); + UwMacaroonCaveat* new_caveat) { + return create_caveat_bstr_value_(kUwMacaroonCaveatTypeDelegateeApp, id_str, + id_str_len, buffer, buffer_size, new_caveat); } -bool uw_macaroon_caveat_create_with_uint_(UwMacaroonCaveatType type, - uint32_t value, uint8_t* buffer, - size_t buffer_size, - UwMacaroonCaveat* caveat) { - if (buffer == NULL || buffer_size == 0 || caveat == NULL) { - return false; - } - if (type != kUwMacaroonCaveatTypeScope && - type != kUwMacaroonCaveatTypeIssued && - type != kUwMacaroonCaveatTypeTTL && - type != kUwMacaroonCaveatTypeExpiration) { - return false; - } - - return create_caveat_(type, &value, sizeof(uint32_t), buffer, buffer_size, - caveat); +bool uw_macaroon_caveat_create_app_commands_only_( + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + return create_caveat_no_value_(kUwMacaroonCaveatTypeAppCommandsOnly, buffer, + buffer_size, new_caveat); } -bool uw_macaroon_caveat_create_with_str_(UwMacaroonCaveatType type, - const uint8_t* str, size_t str_len, - uint8_t* buffer, size_t buffer_size, - UwMacaroonCaveat* caveat) { - if (buffer == NULL || buffer_size == 0 || caveat == NULL || - (str == NULL && str_len != 0)) { - return false; - } - if (type != kUwMacaroonCaveatTypeIdentifier) { - return false; - } +bool uw_macaroon_caveat_create_delegatee_service_( + const uint8_t* id_str, + size_t id_str_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + return create_caveat_bstr_value_(kUwMacaroonCaveatTypeDelegateeService, + id_str, id_str_len, buffer, buffer_size, + new_caveat); +} - return create_caveat_(type, str, str_len, buffer, buffer_size, caveat); +bool uw_macaroon_caveat_create_ble_session_id_(uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + return create_caveat_no_value_(kUwMacaroonCaveatTypeBleSessionID, buffer, + buffer_size, new_caveat); +} + +bool uw_macaroon_caveat_create_lan_session_id_(const uint8_t* session_id, + size_t session_id_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + return create_caveat_bstr_value_(kUwMacaroonCaveatTypeLanSessionID, + session_id, session_id_len, buffer, + buffer_size, new_caveat); +} + +bool uw_macaroon_caveat_create_client_authorization_token_( + const uint8_t* str, + size_t str_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + if (str_len == 0) { + return create_caveat_no_value_( + kUwMacaroonCaveatTypeClientAuthorizationTokenV1, buffer, buffer_size, + new_caveat); + } + return create_caveat_bstr_value_( + kUwMacaroonCaveatTypeClientAuthorizationTokenV1, str, str_len, buffer, + buffer_size, new_caveat); +} + +bool uw_macaroon_caveat_create_server_authentication_token_( + const uint8_t* str, + size_t str_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat) { + if (str_len == 0) { + return create_caveat_no_value_( + kUwMacaroonCaveatTypeServerAuthenticationTokenV1, buffer, buffer_size, + new_caveat); + } + return create_caveat_bstr_value_( + kUwMacaroonCaveatTypeServerAuthenticationTokenV1, str, str_len, buffer, + buffer_size, new_caveat); } bool uw_macaroon_caveat_get_type_(const UwMacaroonCaveat* caveat, @@ -137,20 +310,224 @@ } *type = (UwMacaroonCaveatType)unsigned_int; + return is_valid_caveat_type_(*type); +} - if (*type != kUwMacaroonCaveatTypeStop && - *type != kUwMacaroonCaveatTypeScope && - *type != kUwMacaroonCaveatTypeIdentifier && - *type != kUwMacaroonCaveatTypeIssued && - *type != kUwMacaroonCaveatTypeTTL && - *type != kUwMacaroonCaveatTypeExpiration && - *type != kUwMacaroonCaveatTypeSessionIdentifier) { +/* === Some internal functions defined in macaroon_caveat_internal.h === */ + +bool uw_macaroon_caveat_sign_(const uint8_t* key, + size_t key_len, + const UwMacaroonContext* context, + const UwMacaroonCaveat* caveat, + uint8_t* mac_tag, + size_t mac_tag_size) { + if (key == NULL || key_len == 0 || context == NULL || caveat == NULL || + mac_tag == NULL || mac_tag_size == 0) { return false; } + UwMacaroonCaveatType caveat_type; + if (!uw_macaroon_caveat_get_type_(caveat, &caveat_type) || + !is_valid_caveat_type_(caveat_type)) { + return false; + } + + // Need to encode the whole caveat as a byte string and then sign it + + // If there is no additional value from the context, just compute the HMAC on + // the current byte string. + uint8_t bstr_cbor_prefix[UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN] = {0}; + size_t bstr_cbor_prefix_len = 0; + if (caveat_type != kUwMacaroonCaveatTypeBleSessionID) { + if (!uw_macaroon_encoding_encode_byte_str_len_( + (uint32_t)(caveat->num_bytes), bstr_cbor_prefix, + sizeof(bstr_cbor_prefix), &bstr_cbor_prefix_len)) { + return false; + } + + UwCryptoHmacMsg messages[] = { + {bstr_cbor_prefix, bstr_cbor_prefix_len}, + {caveat->bytes, caveat->num_bytes}, + }; + + return uw_crypto_hmac_(key, key_len, messages, + sizeof(messages) / sizeof(messages[0]), mac_tag, + mac_tag_size); + } + + // If there is additional value from the context. + if (context->ble_session_id == NULL || context->ble_session_id_len == 0) { + return false; + } + + // The length here includes the length of the BLE session ID string. + if (!uw_macaroon_encoding_encode_byte_str_len_( + (uint32_t)(context->ble_session_id_len + caveat->num_bytes), + bstr_cbor_prefix, sizeof(bstr_cbor_prefix), &bstr_cbor_prefix_len)) { + return false; + } + + uint8_t value_cbor_prefix[UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN] = {0}; + size_t value_cbor_prefix_len = 0; + if (!uw_macaroon_encoding_encode_byte_str_len_( + (uint32_t)(context->ble_session_id_len), value_cbor_prefix, + sizeof(value_cbor_prefix), &value_cbor_prefix_len)) { + return false; + } + + UwCryptoHmacMsg messages[] = { + {bstr_cbor_prefix, bstr_cbor_prefix_len}, + {caveat->bytes, caveat->num_bytes}, + {value_cbor_prefix, value_cbor_prefix_len}, + {context->ble_session_id, context->ble_session_id_len}, + }; + + return uw_crypto_hmac_(key, key_len, messages, + sizeof(messages) / sizeof(messages[0]), mac_tag, + mac_tag_size); +} + +static bool update_and_check_expiration_time( + uint32_t current_time, + uint32_t new_expiration_time, + UwMacaroonValidationResult* result) { + if (result->expiration_time > new_expiration_time) { + result->expiration_time = new_expiration_time; + } + + return current_time <= result->expiration_time; +} + +static bool update_delegatee_list(UwMacaroonCaveatType caveat_type, + const UwMacaroonCaveat* caveat, + UwMacaroonValidationResult* result) { + if (result->num_delegatees >= MAX_NUM_DELEGATEES) { + return false; + } + + UwMacaroonDelegateeType delegatee_type = kUwMacaroonDelegateeTypeNone; + switch (caveat_type) { + case kUwMacaroonCaveatTypeDelegateeUser: + delegatee_type = kUwMacaroonDelegateeTypeUser; + break; + + case kUwMacaroonCaveatTypeDelegateeApp: + delegatee_type = kUwMacaroonDelegateeTypeApp; + break; + + case kUwMacaroonCaveatTypeDelegateeService: + delegatee_type = kUwMacaroonDelegateeTypeService; + break; + + default: + return false; + } + + if (caveat_type != kUwMacaroonCaveatTypeDelegateeUser) { + for (size_t i = 0; i < result->num_delegatees; i++) { + // There must have at most one DelegateeApp or DelegateeService + if (result->delegatees[i].type == delegatee_type) { + return false; + } + } + } + + if (!uw_macaroon_caveat_get_value_bstr_( + caveat, &(result->delegatees[result->num_delegatees].id), + &(result->delegatees[result->num_delegatees].id_len))) { + return false; + } + result->delegatees[result->num_delegatees].type = delegatee_type; + result->num_delegatees++; return true; } +bool uw_macaroon_caveat_validate_(const UwMacaroonCaveat* caveat, + const UwMacaroonContext* context, + UwMacaroonValidationState* state, + UwMacaroonValidationResult* result) { + if (caveat == NULL || context == NULL || state == NULL || result == NULL) { + return false; + } + + uint32_t expiration_time = 0; + uint32_t issued_time = 0; + uint32_t scope = UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE; + + UwMacaroonCaveatType caveat_type; + if (!uw_macaroon_caveat_get_type_(caveat, &caveat_type)) { + return false; + } + + switch (caveat_type) { + // The types that always validate + case kUwMacaroonCaveatTypeClientAuthorizationTokenV1: + case kUwMacaroonCaveatTypeServerAuthenticationTokenV1: + case kUwMacaroonCaveatTypeNonce: + case kUwMacaroonCaveatTypeBleSessionID: + return true; + + case kUwMacaroonCaveatTypeDelegationTimestamp: + state->has_issued_time = true; + if (!uw_macaroon_caveat_get_value_uint_(caveat, &issued_time)) { + return false; + } + state->issued_time = issued_time; + return true; + + case kUwMacaroonCaveatTypeTTL1Hour: + if (!(state->has_issued_time)) { + return false; + } + return update_and_check_expiration_time( + context->current_time, state->issued_time + 60 * 60, result); + + case kUwMacaroonCaveatTypeTTL24Hour: + if (!(state->has_issued_time)) { + return false; + } + return update_and_check_expiration_time( + context->current_time, state->issued_time + 24 * 60 * 60, result); + + // Need to create a list of delegatees + case kUwMacaroonCaveatTypeDelegateeUser: + case kUwMacaroonCaveatTypeDelegateeApp: + case kUwMacaroonCaveatTypeDelegateeService: + return update_delegatee_list(caveat_type, caveat, result); + + // Time related caveats + case kUwMacaroonCaveatTypeExpirationAbsolute: + if (!uw_macaroon_caveat_get_value_uint_(caveat, &expiration_time)) { + return false; + } + return update_and_check_expiration_time(context->current_time, + expiration_time, result); + + // The caveats that update the values of the result object + case kUwMacaroonCaveatTypeScope: + if (!uw_macaroon_caveat_get_value_uint_(caveat, &scope) || + // Larger value means less priviledge + scope < UW_MACAROON_CAVEAT_SCOPE_HIGHEST_POSSIBLE || + scope > UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE) { + return false; + } + if (scope > (uint32_t)(result->granted_scope)) { + result->granted_scope = (UwMacaroonCaveatScopeType)scope; + } + return true; + + case kUwMacaroonCaveatTypeAppCommandsOnly: + result->weave_app_restricted = true; + return true; + + case kUwMacaroonCaveatTypeLanSessionID: + return uw_macaroon_caveat_get_value_bstr_( + caveat, &(result->lan_session_id), &(result->lan_session_id_len)); + } + + return false; +} + bool uw_macaroon_caveat_get_value_uint_(const UwMacaroonCaveat* caveat, uint32_t* unsigned_int) { if (caveat == NULL || unsigned_int == NULL) { @@ -162,13 +539,13 @@ return false; } if (type != kUwMacaroonCaveatTypeScope && - type != kUwMacaroonCaveatTypeIssued && - type != kUwMacaroonCaveatTypeTTL && - type != kUwMacaroonCaveatTypeExpiration) { + type != kUwMacaroonCaveatTypeExpirationAbsolute && + type != kUwMacaroonCaveatTypeDelegationTimestamp) { // Wrong type return false; } + // Skip the portion for CBOR type size_t offset; if (!uw_macaroon_encoding_get_item_len_(caveat->bytes, caveat->num_bytes, &offset)) { @@ -179,8 +556,9 @@ caveat->bytes + offset, caveat->num_bytes - offset, unsigned_int); } -bool uw_macaroon_caveat_get_value_str_(const UwMacaroonCaveat* caveat, - const uint8_t** str, size_t* str_len) { +bool uw_macaroon_caveat_get_value_bstr_(const UwMacaroonCaveat* caveat, + const uint8_t** str, + size_t* str_len) { if (caveat == NULL || str == NULL || str_len == NULL) { return false; } @@ -189,7 +567,13 @@ if (!uw_macaroon_caveat_get_type_(caveat, &type)) { return false; } - if (type != kUwMacaroonCaveatTypeIdentifier) { + if (type != kUwMacaroonCaveatTypeNonce && + type != kUwMacaroonCaveatTypeDelegateeUser && + type != kUwMacaroonCaveatTypeDelegateeApp && + type != kUwMacaroonCaveatTypeDelegateeService && + type != kUwMacaroonCaveatTypeLanSessionID && + type != kUwMacaroonCaveatTypeClientAuthorizationTokenV1 && + type != kUwMacaroonCaveatTypeServerAuthenticationTokenV1) { // Wrong type return false; } @@ -200,48 +584,17 @@ return false; } - return uw_macaroon_encoding_decode_text_str_( + return uw_macaroon_encoding_decode_byte_str_( caveat->bytes + offset, caveat->num_bytes - offset, str, str_len); } -bool uw_macaroon_caveat_sign_(const uint8_t* key, size_t key_len, - const UwMacaroonCaveat* caveat, uint8_t* mac_tag, - size_t mac_tag_size) { - if (key == NULL || key_len == 0 || caveat == NULL || mac_tag == NULL || - mac_tag_size == 0) { +bool uw_macaroon_caveat_init_validation_state_( + UwMacaroonValidationState* state) { + if (state == NULL) { return false; } - uint8_t hmac_state_buffer[HMAC_STATE_BUFFER_SIZE]; - if (HMAC_STATE_BUFFER_SIZE < uw_crypto_hmac_required_buffer_size_()) { - return false; - } - - if (!uw_crypto_hmac_init_(hmac_state_buffer, HMAC_STATE_BUFFER_SIZE, key, - key_len)) { - return false; - } - - if (!uw_crypto_hmac_update_(hmac_state_buffer, HMAC_STATE_BUFFER_SIZE, - caveat->bytes, caveat->num_bytes)) { - return false; - } - - const uint8_t* context; - size_t context_len; - UwMacaroonCaveatType caveat_type; - - if ((!uw_macaroon_caveat_get_type_(caveat, &caveat_type)) || - (!uw_macaroon_context_get_(caveat_type, &context, &context_len))) { - return false; - } - if (context != NULL && context_len != 0) { - if (!uw_crypto_hmac_update_(hmac_state_buffer, HMAC_STATE_BUFFER_SIZE, - context, context_len)) { - return false; - } - } - - return uw_crypto_hmac_final_(hmac_state_buffer, HMAC_STATE_BUFFER_SIZE, - mac_tag, mac_tag_size); + state->has_issued_time = false; + state->issued_time = 0; + return true; }
diff --git a/third_party/libuweave/src/macaroon_caveat.h b/third_party/libuweave/src/macaroon_caveat.h index 2e01742..b6846e8 100644 --- a/third_party/libuweave/src/macaroon_caveat.h +++ b/third_party/libuweave/src/macaroon_caveat.h
@@ -15,15 +15,28 @@ } UwMacaroonCaveat; typedef enum { - kUwMacaroonCaveatTypeStop = 0, - kUwMacaroonCaveatTypeScope = 1, - kUwMacaroonCaveatTypeIdentifier = 2, - kUwMacaroonCaveatTypeIssued = 3, - kUwMacaroonCaveatTypeTTL = 4, - kUwMacaroonCaveatTypeExpiration = 5, - kUwMacaroonCaveatTypeSessionIdentifier = 16, + kUwMacaroonCaveatTypeNonce = 0, // bstr + kUwMacaroonCaveatTypeScope = 1, // uint + kUwMacaroonCaveatTypeExpirationAbsolute = 5, // uint + kUwMacaroonCaveatTypeTTL1Hour = 6, // no value + kUwMacaroonCaveatTypeTTL24Hour = 7, // no value + kUwMacaroonCaveatTypeDelegationTimestamp = 8, // uint + + kUwMacaroonCaveatTypeDelegateeUser = 9, // bstr + kUwMacaroonCaveatTypeDelegateeApp = 10, // bstr + kUwMacaroonCaveatTypeDelegateeService = 12, // bstr + + kUwMacaroonCaveatTypeAppCommandsOnly = 11, // no value + kUwMacaroonCaveatTypeBleSessionID = 16, // no value + kUwMacaroonCaveatTypeLanSessionID = 17, // bstr + kUwMacaroonCaveatTypeClientAuthorizationTokenV1 = 8193, // bstr (0x2001) + kUwMacaroonCaveatTypeServerAuthenticationTokenV1 = 12289, // bstr (0x3001) } UwMacaroonCaveatType; +// For security sanity checks +#define UW_MACAROON_CAVEAT_SCOPE_HIGHEST_POSSIBLE 0 +#define UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE 127 + typedef enum { kUwMacaroonCaveatScopeTypeOwner = 2, kUwMacaroonCaveatScopeTypeManager = 8, @@ -31,28 +44,80 @@ kUwMacaroonCaveatScopeTypeViewer = 20, } UwMacaroonCaveatScopeType; -bool uw_macaroon_caveat_create_without_value_(UwMacaroonCaveatType type, +/** Compute the buffer sizes that are enough for caveat creation functions. */ +size_t uw_macaroon_caveat_creation_get_buffsize_(UwMacaroonCaveatType type, + size_t str_len); + +// Caveat creation functions +bool uw_macaroon_caveat_create_nonce_(const uint8_t* nonce, + size_t nonce_size, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); +bool uw_macaroon_caveat_create_scope_(UwMacaroonCaveatScopeType scope, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); +bool uw_macaroon_caveat_create_expiration_absolute_( + uint32_t expiration_time, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); +bool uw_macaroon_caveat_create_ttl_1_hour_(uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); +bool uw_macaroon_caveat_create_ttl_24_hour_(uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); +bool uw_macaroon_caveat_create_delegation_timestamp_( + uint32_t timestamp, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); +bool uw_macaroon_caveat_create_delegatee_user_(const uint8_t* id_str, + size_t id_str_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); +bool uw_macaroon_caveat_create_delegatee_app_(const uint8_t* id_str, + size_t id_str_len, uint8_t* buffer, size_t buffer_size, UwMacaroonCaveat* new_caveat); -bool uw_macaroon_caveat_create_with_uint_(UwMacaroonCaveatType type, - uint32_t value, uint8_t* buffer, - size_t buffer_size, - UwMacaroonCaveat* new_caveat); -bool uw_macaroon_caveat_create_with_str_(UwMacaroonCaveatType type, - const uint8_t* str, size_t str_len, - uint8_t* buffer, size_t buffer_size, - UwMacaroonCaveat* new_caveat); +bool uw_macaroon_caveat_create_delegatee_service_(const uint8_t* id_str, + size_t id_str_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); +bool uw_macaroon_caveat_create_app_commands_only_(uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); +bool uw_macaroon_caveat_create_ble_session_id_(uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); +bool uw_macaroon_caveat_create_lan_session_id_(const uint8_t* session_id, + size_t session_id_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); +// The string values for these two token types are optional. +// Use str_len = 0 to indicate creating the caveats without string values. +bool uw_macaroon_caveat_create_client_authorization_token_( + const uint8_t* str, + size_t str_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); +bool uw_macaroon_caveat_create_server_authentication_token_( + const uint8_t* str, + size_t str_len, + uint8_t* buffer, + size_t buffer_size, + UwMacaroonCaveat* new_caveat); + +/** Get the type for the given caveat. */ bool uw_macaroon_caveat_get_type_(const UwMacaroonCaveat* caveat, UwMacaroonCaveatType* type); -bool uw_macaroon_caveat_get_value_uint_(const UwMacaroonCaveat* caveat, - uint32_t* unsigned_int); -bool uw_macaroon_caveat_get_value_str_(const UwMacaroonCaveat* caveat, - const uint8_t** str, size_t* str_len); - -bool uw_macaroon_caveat_sign_(const uint8_t* key, size_t key_len, - const UwMacaroonCaveat* caveat, uint8_t* mac_tag, - size_t mac_tag_size); #endif // LIBUWEAVE_SRC_MACAROON_CAVEAT_H_
diff --git a/third_party/libuweave/src/macaroon_caveat_internal.h b/third_party/libuweave/src/macaroon_caveat_internal.h new file mode 100644 index 0000000..46a72fb --- /dev/null +++ b/third_party/libuweave/src/macaroon_caveat_internal.h
@@ -0,0 +1,41 @@ +// Copyright 2015 The Weave Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBUWEAVE_SRC_MACAROON_CAVEAT_INTERNAL_H_ +#define LIBUWEAVE_SRC_MACAROON_CAVEAT_INTERNAL_H_ + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "src/macaroon.h" +#include "src/macaroon_caveat.h" + +bool uw_macaroon_caveat_sign_(const uint8_t* key, + size_t key_len, + const UwMacaroonContext* context, + const UwMacaroonCaveat* caveat, + uint8_t* mac_tag, + size_t mac_tag_size); + +typedef struct { + bool has_issued_time; + uint32_t issued_time; +} UwMacaroonValidationState; + +bool uw_macaroon_caveat_init_validation_state_( + UwMacaroonValidationState* state); + +bool uw_macaroon_caveat_validate_(const UwMacaroonCaveat* caveat, + const UwMacaroonContext* context, + UwMacaroonValidationState* state, + UwMacaroonValidationResult* result); + +bool uw_macaroon_caveat_get_value_uint_(const UwMacaroonCaveat* caveat, + uint32_t* unsigned_int); +bool uw_macaroon_caveat_get_value_bstr_(const UwMacaroonCaveat* caveat, + const uint8_t** str, + size_t* str_len); + +#endif // LIBUWEAVE_SRC_MACAROON_CAVEAT_INTERNAL_H_
diff --git a/third_party/libuweave/src/macaroon_context.c b/third_party/libuweave/src/macaroon_context.c index 7477784..2f1685d 100644 --- a/third_party/libuweave/src/macaroon_context.c +++ b/third_party/libuweave/src/macaroon_context.c
@@ -4,19 +4,19 @@ #include "src/macaroon_context.h" -#include "src/macaroon_caveat.h" - -bool uw_macaroon_context_get_(UwMacaroonCaveatType type, - const uint8_t** context, size_t* context_len) { - if (type != kUwMacaroonCaveatTypeSessionIdentifier) { - *context = NULL; - *context_len = 0; +bool uw_macaroon_context_create_(uint32_t current_time, + const uint8_t* ble_session_id, + size_t ble_session_id_len, + UwMacaroonContext* new_context) { + if (ble_session_id == NULL && ble_session_id_len != 0) { + return false; + } + if (new_context == NULL) { + return false; } - // TODO(bozhu): Waiting for a proper way to obtain the session identifier. - // Have we already implemented something related to session identifiers? - *context = NULL; - *context_len = 0; - + new_context->current_time = current_time; + new_context->ble_session_id = ble_session_id; + new_context->ble_session_id_len = ble_session_id_len; return true; }
diff --git a/third_party/libuweave/src/macaroon_context.h b/third_party/libuweave/src/macaroon_context.h index 8522b69..c230eb7 100644 --- a/third_party/libuweave/src/macaroon_context.h +++ b/third_party/libuweave/src/macaroon_context.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UW_LIBUWEAVE_SRC_MACAROON_CONTEXT_ -#define UW_LIBUWEAVE_SRC_MACAROON_CONTEXT_ +#ifndef LIBUWEAVE_SRC_MACAROON_CONTEXT_ +#define LIBUWEAVE_SRC_MACAROON_CONTEXT_ #include <stdbool.h> #include <stddef.h> @@ -11,7 +11,15 @@ #include "src/macaroon_caveat.h" -bool uw_macaroon_context_get_(UwMacaroonCaveatType type, - const uint8_t** context, size_t* context_len); +typedef struct { + uint32_t current_time; // In number of seconds since Jan 1st 2000 00:00:00 + const uint8_t* ble_session_id; // Only for BLE + size_t ble_session_id_len; +} UwMacaroonContext; -#endif // UW_LIBUWEAVE_SRC_MACAROON_CONTEXT_ +bool uw_macaroon_context_create_(uint32_t current_time, + const uint8_t* ble_session_id, + size_t ble_session_id_len, + UwMacaroonContext* new_context); + +#endif // LIBUWEAVE_SRC_MACAROON_CONTEXT_
diff --git a/third_party/libuweave/src/macaroon_encoding.c b/third_party/libuweave/src/macaroon_encoding.c index 3fb5323..29adc52 100644 --- a/third_party/libuweave/src/macaroon_encoding.c +++ b/third_party/libuweave/src/macaroon_encoding.c
@@ -21,28 +21,34 @@ kCborMajorTypeArray = 4 << 5, // type 4 -- arrays } CborMajorType; -// -- Prototypes begin -- static inline CborMajorType get_type_(const uint8_t* cbor); static inline uint8_t get_addtl_data_(const uint8_t* cbor); static inline void set_type_(CborMajorType type, uint8_t* cbor); static inline void set_addtl_data_(uint8_t addtl_data, uint8_t* cbor); -// Compute the minimum number of bytes to store the unsigned integer. +/** Computes the minimum number of bytes to store the unsigned integer. */ static inline size_t uint_min_len_(uint32_t unsigned_int); -// Encoding or decoding without checking types -static bool blindly_encode_uint_(uint32_t unsigned_int, uint8_t* buffer, - size_t buffer_size, size_t* result_len); -static bool blindly_encode_str_(const uint8_t* str, size_t str_len, - uint8_t* buffer, size_t buffer_size, +/** Encoding or decoding without checking types */ +static bool blindly_encode_uint_(uint32_t unsigned_int, + uint8_t* buffer, + size_t buffer_size, + size_t* result_len); +static bool blindly_encode_str_(const uint8_t* str, + size_t str_len, + uint8_t* buffer, + size_t buffer_size, size_t* result_len); -static bool blindly_decode_uint_(const uint8_t* cbor, size_t cbor_len, +static bool blindly_decode_uint_(const uint8_t* cbor, + size_t cbor_len, uint32_t* unsigned_int); -static bool blindly_decode_str_(const uint8_t* cbor, size_t cbor_len, - const uint8_t** out_str, size_t* out_str_len); -// -- Prototypes end -- +static bool blindly_decode_str_(const uint8_t* cbor, + size_t cbor_len, + const uint8_t** out_str, + size_t* out_str_len); -bool uw_macaroon_encoding_get_item_len_(const uint8_t* cbor, size_t cbor_len, +bool uw_macaroon_encoding_get_item_len_(const uint8_t* cbor, + size_t cbor_len, size_t* first_item_len) { if (cbor == NULL || cbor_len == 0 || first_item_len == NULL) { return false; @@ -76,7 +82,8 @@ } bool uw_macaroon_encoding_encode_uint_(const uint32_t unsigned_int, - uint8_t* buffer, size_t buffer_size, + uint8_t* buffer, + size_t buffer_size, size_t* resulting_cbor_len) { if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) { return false; @@ -88,7 +95,8 @@ } bool uw_macaroon_encoding_encode_array_len_(const uint32_t array_len, - uint8_t* buffer, size_t buffer_size, + uint8_t* buffer, + size_t buffer_size, size_t* resulting_cbor_len) { if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) { return false; @@ -99,8 +107,10 @@ resulting_cbor_len); } -bool uw_macaroon_encoding_encode_byte_str_(const uint8_t* str, size_t str_len, - uint8_t* buffer, size_t buffer_size, +bool uw_macaroon_encoding_encode_byte_str_(const uint8_t* str, + size_t str_len, + uint8_t* buffer, + size_t buffer_size, size_t* resulting_cbor_len) { if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) { return false; @@ -111,8 +121,10 @@ resulting_cbor_len); } -bool uw_macaroon_encoding_encode_text_str_(const uint8_t* str, size_t str_len, - uint8_t* buffer, size_t buffer_size, +bool uw_macaroon_encoding_encode_text_str_(const uint8_t* str, + size_t str_len, + uint8_t* buffer, + size_t buffer_size, size_t* resulting_cbor_len) { if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) { return false; @@ -123,7 +135,19 @@ resulting_cbor_len); } -bool uw_macaroon_encoding_decode_uint_(const uint8_t* cbor, size_t cbor_len, +bool uw_macaroon_encoding_encode_byte_str_len_(size_t str_len, + uint8_t* buffer, + size_t buffer_size, + size_t* resulting_cbor_len) { + if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) { + return false; + } + set_type_(kCborMajorTypeByteStr, buffer); + return blindly_encode_uint_(str_len, buffer, buffer_size, resulting_cbor_len); +} + +bool uw_macaroon_encoding_decode_uint_(const uint8_t* cbor, + size_t cbor_len, uint32_t* unsigned_int) { if (cbor == NULL || cbor_len == 0 || unsigned_int == NULL || get_type_(cbor) != kCborMajorTypeUint) { @@ -144,7 +168,8 @@ return blindly_decode_uint_(cbor, cbor_len, array_len); } -bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor, size_t cbor_len, +bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor, + size_t cbor_len, const uint8_t** out_str, size_t* out_str_len) { if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str_len == NULL || @@ -155,7 +180,8 @@ return blindly_decode_str_(cbor, cbor_len, out_str, out_str_len); } -bool uw_macaroon_encoding_decode_text_str_(const uint8_t* cbor, size_t cbor_len, +bool uw_macaroon_encoding_decode_text_str_(const uint8_t* cbor, + size_t cbor_len, const uint8_t** out_str, size_t* out_str_len) { if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str_len == NULL || @@ -193,9 +219,12 @@ return 4; } -// Write the unsigned int in the big-endian fashion by using the minimum number -// of bytes in CBOR -static inline bool write_uint_big_endian_(uint32_t unsigned_int, uint8_t* buff, +/** + * Writes the unsigned int in the big-endian fashion by using the minimum number + * of bytes in CBOR + */ +static inline bool write_uint_big_endian_(uint32_t unsigned_int, + uint8_t* buff, size_t buff_len) { if (buff == NULL || buff_len == 0) { return false; @@ -225,8 +254,9 @@ return true; } -// Read the unsigned int written in big-endian -static inline bool read_uint_big_endian_(const uint8_t* bytes, size_t num_bytes, +/** Reads the unsigned int written in big-endian. */ +static inline bool read_uint_big_endian_(const uint8_t* bytes, + size_t num_bytes, uint32_t* unsigned_int) { if (bytes == NULL || num_bytes == 0 || num_bytes > 4 || unsigned_int == NULL) { @@ -252,8 +282,10 @@ return true; } -static bool blindly_encode_uint_(uint32_t unsigned_int, uint8_t* buffer, - size_t buffer_size, size_t* result_len) { +static bool blindly_encode_uint_(uint32_t unsigned_int, + uint8_t* buffer, + size_t buffer_size, + size_t* result_len) { if (buffer == NULL || buffer_size == 0 || result_len == NULL) { return false; } @@ -288,8 +320,10 @@ return write_uint_big_endian_(unsigned_int, buffer + 1, buffer_size - 1); } -static bool blindly_encode_str_(const uint8_t* str, size_t str_len, - uint8_t* buffer, size_t buffer_size, +static bool blindly_encode_str_(const uint8_t* str, + size_t str_len, + uint8_t* buffer, + size_t buffer_size, size_t* result_len) { if (buffer == NULL || buffer_size == 0) { return false; @@ -320,7 +354,8 @@ return true; } -static bool blindly_decode_uint_(const uint8_t* cbor, size_t cbor_len, +static bool blindly_decode_uint_(const uint8_t* cbor, + size_t cbor_len, uint32_t* unsigned_int) { if (cbor == NULL || cbor_len == 0 || unsigned_int == NULL) { return false; @@ -344,8 +379,10 @@ return read_uint_big_endian_(cbor + 1, uint_num_bytes, unsigned_int); } -static bool blindly_decode_str_(const uint8_t* cbor, size_t cbor_len, - const uint8_t** out_str, size_t* out_str_len) { +static bool blindly_decode_str_(const uint8_t* cbor, + size_t cbor_len, + const uint8_t** out_str, + size_t* out_str_len) { if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str == NULL) { return false; }
diff --git a/third_party/libuweave/src/macaroon_encoding.h b/third_party/libuweave/src/macaroon_encoding.h index edddfc1..60f80a6 100644 --- a/third_party/libuweave/src/macaroon_encoding.h +++ b/third_party/libuweave/src/macaroon_encoding.h
@@ -17,32 +17,53 @@ #include <stddef.h> #include <stdint.h> -// Get the number of bytes that is occupied by the first data item in the give -// CBOR string. -bool uw_macaroon_encoding_get_item_len_(const uint8_t* cbor, size_t cbor_len, +#define UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN 5 + +/** + * Gets the number of bytes that is occupied by the first data item in the give + * CBOR string. + */ +bool uw_macaroon_encoding_get_item_len_(const uint8_t* cbor, + size_t cbor_len, size_t* first_item_len); bool uw_macaroon_encoding_encode_uint_(const uint32_t unsigned_int, - uint8_t* buffer, size_t buffer_size, + uint8_t* buffer, + size_t buffer_size, size_t* resulting_cbor_len); bool uw_macaroon_encoding_encode_array_len_(const uint32_t array_len, - uint8_t* buffer, size_t buffer_size, + uint8_t* buffer, + size_t buffer_size, size_t* resulting_cbor_len); -bool uw_macaroon_encoding_encode_byte_str_(const uint8_t* str, size_t str_len, - uint8_t* buffer, size_t buffer_size, +bool uw_macaroon_encoding_encode_byte_str_(const uint8_t* str, + size_t str_len, + uint8_t* buffer, + size_t buffer_size, size_t* resulting_cbor_len); -bool uw_macaroon_encoding_encode_text_str_(const uint8_t* str, size_t str_len, - uint8_t* buffer, size_t buffer_size, +bool uw_macaroon_encoding_encode_text_str_(const uint8_t* str, + size_t str_len, + uint8_t* buffer, + size_t buffer_size, size_t* resulting_cbor_len); -bool uw_macaroon_encoding_decode_uint_(const uint8_t* cbor, size_t cbor_len, +/** Only encode the header (major type and length) of the byte string */ +bool uw_macaroon_encoding_encode_byte_str_len_(size_t str_len, + uint8_t* buffer, + size_t buffer_size, + size_t* resulting_cbor_len); + +bool uw_macaroon_encoding_decode_uint_(const uint8_t* cbor, + size_t cbor_len, uint32_t* unsigned_int); bool uw_macaroon_encoding_decode_array_len_(const uint8_t* cbor, - size_t cbor_len, uint32_t* array_len); -bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor, size_t cbor_len, + size_t cbor_len, + uint32_t* array_len); +bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor, + size_t cbor_len, const uint8_t** str, size_t* str_len); -bool uw_macaroon_encoding_decode_text_str_(const uint8_t* cbor, size_t cbor_len, +bool uw_macaroon_encoding_decode_text_str_(const uint8_t* cbor, + size_t cbor_len, const uint8_t** str, size_t* str_len);