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);