1 #include <gmock/gmock.h>
2 #include <gtest/gtest.h>
4 #include <boost/process.hpp>
6 #include "aktualizr_secondary_file.h"
7 #include "crypto/keymanager.h"
8 #include "test_utils.h"
9 #include "update_agent.h"
10 #include "update_agent_file.h"
11 #include "uptane_repo.h"
12 #include "utilities/utils.h"
14 using ::testing::NiceMock;
18 UpdateAgentMock(boost::filesystem::path target_filepath, std::string target_name)
20 ON_CALL(*
this, receiveData).WillByDefault([
this](
const Uptane::Target& target,
const uint8_t*
data,
size_t size) {
21 return FileUpdateAgent::receiveData(target,
data, size);
23 ON_CALL(*
this, install).WillByDefault([
this](
const Uptane::Target& target) {
24 return FileUpdateAgent::install(target);
36 config.pacman.type = PACKAGE_MANAGER_NONE;
38 config.storage.path = storage_dir_.Path();
39 config.storage.type = StorageType::kSqlite;
41 storage_ = INvStorage::newStorage(config.storage);
43 update_agent_ = std::make_shared<NiceMock<UpdateAgentMock>>(config.storage.path /
"firmware.txt",
"");
45 secondary_ = std::make_shared<AktualizrSecondaryFile>(config, storage_, update_agent_);
46 secondary_->initialize();
49 std::shared_ptr<AktualizrSecondaryFile>& operator->() {
return secondary_; }
52 boost::optional<Uptane::Target> pending_target;
54 storage_->loadInstalledVersions(secondary_->serial().ToString(),
nullptr, &pending_target);
55 return *pending_target;
58 std::string hardwareID()
const {
return secondary_->hwID().ToString(); }
60 std::string serial()
const {
return secondary_->serial().ToString(); }
62 boost::filesystem::path targetFilepath()
const {
63 return storage_dir_.Path() / AktualizrSecondaryFile::FileUpdateDefaultFile;
66 std::shared_ptr<NiceMock<UpdateAgentMock>> update_agent_;
70 std::shared_ptr<AktualizrSecondaryFile> secondary_;
71 std::shared_ptr<INvStorage> storage_;
78 Metadata addImageFile(
const std::string& targetname,
const std::string& hardware_id,
const std::string& serial,
79 size_t size = 2049,
bool add_and_sign_target =
true,
bool add_invalid_images =
false,
81 const auto image_file_path = root_dir_ / targetname;
82 generateRandomFile(image_file_path, size);
84 uptane_repo_.addImage(image_file_path, targetname, hardware_id,
"",
Delegation());
85 if (add_and_sign_target) {
86 uptane_repo_.addTarget(targetname, hardware_id, serial,
"");
87 uptane_repo_.signTargets();
90 if (add_and_sign_target && add_invalid_images) {
91 const auto smaller_image_file_path = image_file_path.string() +
".smaller";
92 const auto bigger_image_file_path = image_file_path.string() +
".bigger";
93 const auto broken_image_file_path = image_file_path.string() +
".broken";
95 boost::filesystem::copy(image_file_path, smaller_image_file_path);
96 boost::filesystem::copy(image_file_path, bigger_image_file_path);
97 boost::filesystem::copy(image_file_path, broken_image_file_path);
99 if (!boost::filesystem::exists(smaller_image_file_path)) {
100 LOG_ERROR <<
"File does not exists: " << smaller_image_file_path;
103 boost::filesystem::resize_file(smaller_image_file_path, size - delta);
104 boost::filesystem::resize_file(bigger_image_file_path, size + delta);
106 std::ofstream broken_image{broken_image_file_path,
107 std::ios_base::in | std::ios_base::out | std::ios_base::ate | std::ios_base::binary};
108 unsigned char data_to_inject[]{0xFF};
109 broken_image.seekp(
static_cast<long>(-
sizeof(data_to_inject)), std::ios_base::end);
110 broken_image.write(
reinterpret_cast<const char*
>(data_to_inject),
sizeof(data_to_inject));
111 broken_image.close();
114 return getCurrentMetadata();
117 Uptane::MetaBundle getCurrentMetadata()
const {
118 Uptane::MetaBundle meta_bundle;
119 std::string metadata;
121 boost::filesystem::load_string_file(director_dir_ /
"root.json", metadata);
122 meta_bundle.emplace(std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Root()), std::move(metadata));
123 boost::filesystem::load_string_file(director_dir_ /
"targets.json", metadata);
124 meta_bundle.emplace(std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Targets()),
125 std::move(metadata));
127 boost::filesystem::load_string_file(imagerepo_dir_ /
"root.json", metadata);
128 meta_bundle.emplace(std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Root()), std::move(metadata));
129 boost::filesystem::load_string_file(imagerepo_dir_ /
"timestamp.json", metadata);
130 meta_bundle.emplace(std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Timestamp()),
131 std::move(metadata));
132 boost::filesystem::load_string_file(imagerepo_dir_ /
"snapshot.json", metadata);
133 meta_bundle.emplace(std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Snapshot()), std::move(metadata));
134 boost::filesystem::load_string_file(imagerepo_dir_ /
"targets.json", metadata);
135 meta_bundle.emplace(std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Targets()), std::move(metadata));
140 std::string getTargetImagePath(
const std::string& targetname)
const {
return (root_dir_ / targetname).string(); }
145 static void generateRandomFile(
const boost::filesystem::path& filepath,
size_t size) {
146 std::ofstream file{filepath.string(), std::ofstream::binary};
148 if (!file.is_open() || !file.good()) {
149 throw std::runtime_error(
"Failed to create a file: " + filepath.string());
152 const unsigned char symbols[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv";
153 unsigned char cur_symbol;
155 for (
unsigned int ii = 0; ii < size; ++ii) {
156 cur_symbol = symbols[
static_cast<unsigned int>(rand()) %
sizeof(symbols)];
157 file.put(
static_cast<char>(cur_symbol));
165 boost::filesystem::path director_dir_{root_dir_ /
"repo/director"};
166 boost::filesystem::path imagerepo_dir_{root_dir_ /
"repo/repo"};
167 UptaneRepo uptane_repo_{root_dir_.Path(),
"",
""};
173 SecondaryTest() : update_agent_(*(secondary_.update_agent_)) {
174 uptane_repo_.addImageFile(default_target_, secondary_->hwID().ToString(), secondary_->serial().ToString(),
175 target_size,
true,
true, inavlid_target_size_delta);
178 std::vector<Uptane::Target> getCurrentTargets() {
180 uptane_repo_.getCurrentMetadata(), Uptane::RepositoryType::Director(), Uptane::Role::Targets())));
181 return targets.getTargets(secondary_->serial(), secondary_->hwID());
185 auto targets = getCurrentTargets();
186 EXPECT_GT(targets.size(), 0);
190 Hash getDefaultTargetHash() {
return Hash(Hash::Type::kSha256, getDefaultTarget().sha256Hash()); }
193 auto image_path = uptane_repo_.getTargetImagePath(target_name);
194 size_t total_size = boost::filesystem::file_size(image_path);
196 std::ifstream file{image_path};
198 uint8_t buf[send_buffer_size];
199 size_t read_and_send_data_size = 0;
201 while (read_and_send_data_size < total_size) {
202 auto read_bytes = file.readsome(
reinterpret_cast<char*
>(buf),
sizeof(buf));
203 if (read_bytes < 0) {
208 auto result = secondary_->receiveData(buf,
static_cast<size_t>(read_bytes));
209 if (!
result.isSuccess()) {
211 return result.result_code.num_code;
213 read_and_send_data_size +=
static_cast<size_t>(read_bytes);
219 if (read_and_send_data_size == total_size) {
220 result = data::ResultCode::Numeric::kOk;
227 static constexpr
const char*
const default_target_{
"default-target"};
228 static constexpr
const char*
const bigger_target_{
"default-target.bigger"};
229 static constexpr
const char*
const smaller_target_{
"default-target.smaller"};
230 static constexpr
const char*
const broken_target_{
"default-target.broken"};
232 static const size_t target_size{2049};
233 static const size_t inavlid_target_size_delta{2};
234 static const size_t send_buffer_size{1024};
238 NiceMock<UpdateAgentMock>& update_agent_;
243 public ::testing::WithParamInterface<std::pair<Uptane::RepositoryType, Uptane::Role>> {
252 :
Metadata(valid_metadata), repo_type_(repo), role_(role) {}
256 Metadata::getRoleMetadata(
result, repo, role, version);
257 if (!(repo_type_ == repo && role_ == role)) {
269 return MetadataInvalidator(uptane_repo_.getCurrentMetadata(), GetParam().first, GetParam().second);
274 NiceMock<UpdateAgentMock>& update_agent_;
284 EXPECT_FALSE(secondary_->putMetadata(currentMetadata()).isSuccess());
286 EXPECT_CALL(update_agent_, receiveData).Times(0);
287 EXPECT_CALL(update_agent_, install).Times(0);
289 EXPECT_FALSE(secondary_->install().isSuccess());
297 ::testing::Values(std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Root()),
298 std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Targets()),
299 std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Root()),
300 std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Timestamp()),
301 std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Snapshot()),
302 std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Targets())));
305 EXPECT_CALL(update_agent_, receiveData)
306 .Times(target_size / send_buffer_size + (target_size % send_buffer_size ? 1 : 0));
307 EXPECT_CALL(update_agent_, install).Times(1);
309 ASSERT_TRUE(secondary_->putMetadata(uptane_repo_.getCurrentMetadata()).isSuccess());
310 ASSERT_EQ(sendImageFile(), data::ResultCode::Numeric::kOk);
311 ASSERT_TRUE(secondary_->install().isSuccess());
314 ASSERT_TRUE(boost::filesystem::exists(secondary_.targetFilepath()));
315 auto target = getDefaultTarget();
318 auto target_hash =
Hash(Hash::Type::kSha256, target.sha256Hash());
319 auto target_file_hash = Hash::generate(Hash::Type::kSha256, Utils::readFile(secondary_.targetFilepath()));
320 EXPECT_EQ(target_hash, target_file_hash);
323 auto manifest = secondary_->getManifest();
324 EXPECT_EQ(manifest.installedImageHash(), target_file_hash);
325 EXPECT_EQ(manifest.filepath(), target.filename());
331 uptane_repo_.addImageFile(
"second_image_00", secondary_->hwID().ToString(), secondary_->serial().ToString(),
332 target_size,
false,
false);
333 EXPECT_TRUE(secondary_->putMetadata(uptane_repo_.getCurrentMetadata()).isSuccess());
339 uptane_repo_.addImageFile(
"second_target", secondary_->hwID().ToString(), secondary_->serial().ToString());
341 EXPECT_FALSE(secondary_->putMetadata(uptane_repo_.getCurrentMetadata()).isSuccess());
346 auto metadata =
UptaneRepoWrapper().addImageFile(
"mytarget", secondary_->hwID().ToString(),
"non-existing-serial");
348 EXPECT_FALSE(secondary_->putMetadata(metadata).isSuccess());
353 auto metadata =
UptaneRepoWrapper().addImageFile(
"mytarget",
"non-existig-hwid", secondary_->serial().ToString());
355 EXPECT_FALSE(secondary_->putMetadata(metadata).isSuccess());
360 uptane_repo_.refreshRoot(Uptane::RepositoryType::Director());
361 EXPECT_TRUE(secondary_->putMetadata(uptane_repo_.getCurrentMetadata()).isSuccess());
365 uptane_repo_.refreshRoot(Uptane::RepositoryType::Image());
366 EXPECT_TRUE(secondary_->putMetadata(uptane_repo_.getCurrentMetadata()).isSuccess());
370 EXPECT_CALL(update_agent_, receiveData)
371 .Times((target_size - inavlid_target_size_delta) / send_buffer_size +
372 ((target_size - inavlid_target_size_delta) % send_buffer_size ? 1 : 0));
373 EXPECT_CALL(update_agent_, install).Times(1);
375 EXPECT_TRUE(secondary_->putMetadata(uptane_repo_.getCurrentMetadata()).isSuccess());
377 EXPECT_EQ(sendImageFile(smaller_target_), data::ResultCode::Numeric::kOk);
378 EXPECT_FALSE(secondary_->install().isSuccess());
382 EXPECT_CALL(update_agent_, receiveData)
383 .Times((target_size + inavlid_target_size_delta) / send_buffer_size +
384 ((target_size + inavlid_target_size_delta) % send_buffer_size ? 1 : 0));
385 EXPECT_CALL(update_agent_, install).Times(1);
387 EXPECT_TRUE(secondary_->putMetadata(uptane_repo_.getCurrentMetadata()).isSuccess());
389 EXPECT_EQ(sendImageFile(bigger_target_), data::ResultCode::Numeric::kOk);
390 EXPECT_FALSE(secondary_->install().isSuccess());
394 EXPECT_CALL(update_agent_, receiveData)
395 .Times(target_size / send_buffer_size + (target_size % send_buffer_size ? 1 : 0));
396 EXPECT_CALL(update_agent_, install).Times(1);
398 EXPECT_TRUE(secondary_->putMetadata(uptane_repo_.getCurrentMetadata()).isSuccess());
399 EXPECT_EQ(sendImageFile(broken_target_), data::ResultCode::Numeric::kOk);
400 EXPECT_FALSE(secondary_->install().isSuccess());
403 int main(
int argc,
char** argv) {
404 ::testing::InitGoogleTest(&argc, argv);
407 logger_set_threshold(boost::log::trivial::info);
409 return RUN_ALL_TESTS();