Skip to content
This repository has been archived by the owner on May 21, 2024. It is now read-only.

Add image without actual file #1035

Merged
merged 5 commits into from
Jan 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/aktualizr_repo/director_repo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,23 @@ void DirectorRepo::signTargets() {
Utils::writeFile(path_ / "repo/director/targets.json",
Utils::jsonToCanonicalStr(signTuf(Uptane::Role::Targets(), targets_unsigned)));
boost::filesystem::remove(path_ / "repo/director/staging/targets.json");
}
void DirectorRepo::emptyTargets() {
const boost::filesystem::path staging = path_ / "repo/director/staging/targets.json";
Json::Value targets_unsigned;
targets_unsigned = Utils::parseJSONFile(staging);
targets_unsigned["targets"].clear();
Utils::writeFile(staging, Utils::jsonToCanonicalStr(targets_unsigned));
}

void DirectorRepo::oldTargets() {
const boost::filesystem::path current = path_ / "repo/director/targets.json";
const boost::filesystem::path staging = path_ / "repo/director/staging/targets.json";

if (!boost::filesystem::exists(current)) {
throw std::runtime_error(std::string("targets.json not found at ") + current.c_str() + "!");
}
Json::Value targets_current = Utils::parseJSONFile(current);
Json::Value targets_unsigned = targets_current["signed"];
Utils::writeFile(staging, Utils::jsonToCanonicalStr(targets_unsigned));
}
2 changes: 2 additions & 0 deletions src/aktualizr_repo/director_repo.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class DirectorRepo : public Repo {
void addTarget(const std::string &target_name, const Json::Value &target, const std::string &hardware_id,
const std::string &ecu_serial);
void signTargets();
void emptyTargets();
void oldTargets();
};

#endif // DIRECTOR_REPO_H_
29 changes: 23 additions & 6 deletions src/aktualizr_repo/image_repo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ void ImageRepo::addImage(const boost::filesystem::path &image_path) {
}
std::string image = Utils::readFile(image_path);

Json::Value targets = Utils::parseJSONFile(repo_dir / "targets.json")["signed"];
std::string target_name = image_path.filename().string();
targets["targets"][target_name]["length"] = Json::UInt64(image.size());
targets["targets"][target_name]["hashes"]["sha256"] =
boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest(image)));
targets["targets"][target_name]["hashes"]["sha512"] =
boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha512digest(image)));
Json::Value target;
target["length"] = Json::UInt64(image.size());
target["hashes"]["sha256"] = boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest(image)));
target["hashes"]["sha512"] = boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha512digest(image)));
addImage(target_name, target);
}

void ImageRepo::addImage(const std::string &name, const Json::Value &target) {
boost::filesystem::path repo_dir(path_ / "repo/image");

Json::Value targets = Utils::parseJSONFile(repo_dir / "targets.json")["signed"];
targets["targets"][name] = target;
targets["version"] = (targets["version"].asUInt()) + 1;

std::string signed_targets = Utils::jsonToCanonicalStr(signTuf(Uptane::Role::Targets(), targets));
Expand All @@ -44,4 +50,15 @@ void ImageRepo::addImage(const boost::filesystem::path &image_path) {
timestamp["meta"]["snapshot.json"]["version"] = snapshot["version"].asUInt();
Utils::writeFile(repo_dir / "timestamp.json",
Utils::jsonToCanonicalStr(signTuf(Uptane::Role::Timestamp(), timestamp)));
}

void ImageRepo::addCustomImage(const std::string &name, const Uptane::Hash &hash, const uint64_t length) {
Json::Value target;
target["length"] = Json::UInt(length);
if (hash.type() == Uptane::Hash::Type::kSha256) {
target["hashes"]["sha256"] = hash.HashString();
} else if (hash.type() == Uptane::Hash::Type::kSha512) {
target["hashes"]["sha512"] = hash.HashString();
}
addImage(name, target);
}
4 changes: 4 additions & 0 deletions src/aktualizr_repo/image_repo.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ class ImageRepo : public Repo {
ImageRepo(boost::filesystem::path path, const std::string &expires, std::string correlation_id)
: Repo(Uptane::RepositoryType::Image(), std::move(path), expires, std::move(correlation_id)) {}
void addImage(const boost::filesystem::path &image_path);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like too much overloading of this addImage.

Could be helpful to give them different names.

void addCustomImage(const std::string &name, const Uptane::Hash &hash, uint64_t length);

private:
void addImage(const std::string &name, const Json::Value &target);
};

#endif // IMAGE_REPO_H_
29 changes: 26 additions & 3 deletions src/aktualizr_repo/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ int main(int argc, char **argv) {
// clang-format off
desc.add_options()
("help,h", "print usage")
("command", po::value<std::string>(), "generate|sign|image|addtarget|signtargets")
("command", po::value<std::string>(), "generate|sign|image|addtarget|emptytargets|oldtargets|signtargets")
("path", po::value<boost::filesystem::path>(), "path to the repository")
("filename", po::value<std::string>(), "path to the image")
("hwid", po::value<std::string>(), "target hardware identifier")
Expand All @@ -24,7 +24,11 @@ int main(int argc, char **argv) {
("keyname", po::value<std::string>(), "name of key's role")
("repotype", po::value<std::string>(), "director|image")
("correlationid", po::value<std::string>()->default_value(""), "correlation id")
("keytype", po::value<std::string>()->default_value("RSA2048"), "UPTANE key type");
("keytype", po::value<std::string>()->default_value("RSA2048"), "UPTANE key type")
("targetname", po::value<std::string>(), "target's name (for adding metadata without an actual file)")
("targetsha256", po::value<std::string>(), "target's SHA256 hash (for adding metadata without an actual file)")
("targetsha512", po::value<std::string>(), "target's SHA512 hash (for adding metadata without an actual file)")
("targetlength", po::value<uint64_t>(), "target's length (for adding metadata without an actual file)");
// clang-format on

po::positional_options_description positionalOptions;
Expand Down Expand Up @@ -63,11 +67,30 @@ int main(int argc, char **argv) {
key_type_str >> key_type;
repo.generateRepo(key_type);
} else if (command == "image") {
repo.addImage(vm["filename"].as<std::string>());
if (vm.count("filename") > 0) {
repo.addImage(vm["filename"].as<std::string>());
} else if (vm.count("targetname") > 0 && (vm.count("targetsha256") > 0 || vm.count("targetsha512") > 0) &&
vm.count("targetlength") > 0) {
std::unique_ptr<Uptane::Hash> hash;
if (vm.count("targetsha256") > 0) {
hash = std_::make_unique<Uptane::Hash>(Uptane::Hash::Type::kSha256, vm["targetsha256"].as<std::string>());
} else {
hash = std_::make_unique<Uptane::Hash>(Uptane::Hash::Type::kSha512, vm["targetsha512"].as<std::string>());
}
repo.addCustomImage(vm["targetname"].as<std::string>(), *hash, vm["targetlength"].as<uint64_t>());
} else {
std::cerr
<< "You shoud provide --filename or --targetname, --targetsha256 or --targetsha512, and --targetlength\n";
exit(EXIT_FAILURE);
}
} else if (command == "addtarget") {
repo.addTarget(vm["filename"].as<std::string>(), vm["hwid"].as<std::string>(), vm["serial"].as<std::string>());
} else if (command == "signtargets") {
repo.signTargets();
} else if (command == "emptytargets") {
repo.emptyTargets();
} else if (command == "oldtargets") {
repo.oldTargets();
} else if (command == "sign") {
if (vm.count("repotype") == 0 || vm.count("keyname") == 0) {
std::cerr << "--repotype or --keyname is missing\n";
Expand Down
107 changes: 107 additions & 0 deletions src/aktualizr_repo/repo_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,113 @@ TEST(aktualizr_repo, sign) {
EXPECT_NO_THROW(root.UnpackSignedObject(Uptane::RepositoryType::Director(), json));
}

TEST(aktualizr_repo, image_custom) {
TemporaryDirectory temp_dir;
std::ostringstream keytype_stream;
keytype_stream << key_type;
std::string cmd = generate_repo_exec + " generate " + temp_dir.Path().string() + " --keytype " + keytype_stream.str();
std::string output;
int retval = Utils::shell(cmd, &output);
if (retval) {
FAIL() << "'" << cmd << "' exited with error code\n";
}
cmd = generate_repo_exec + " image " + temp_dir.Path().string();
cmd +=
" --targetname target1 --targetsha256 8ab755c16de6ee9b6224169b36cbf0f2a545f859be385501ad82cdccc240d0a6 "
"--targetlength 123";
retval = Utils::shell(cmd, &output);
if (retval) {
FAIL() << "'" << cmd << "' exited with error code\n";
}

Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / "repo/image/targets.json");
EXPECT_EQ(image_targets["signed"]["targets"].size(), 1);
EXPECT_EQ(image_targets["signed"]["targets"]["target1"]["length"].asUInt(), 123);
}

TEST(aktualizr_repo, emptytargets) {
TemporaryDirectory temp_dir;
std::ostringstream keytype_stream;
keytype_stream << key_type;
std::string cmd = generate_repo_exec + " generate " + temp_dir.Path().string() + " --keytype " + keytype_stream.str();
std::string output;
int retval = Utils::shell(cmd, &output);
if (retval) {
FAIL() << "'" << cmd << "' exited with error code\n";
}
cmd = generate_repo_exec + " image " + temp_dir.Path().string();
cmd +=
" --targetname target1 --targetsha256 8ab755c16de6ee9b6224169b36cbf0f2a545f859be385501ad82cdccc240d0a6 "
"--targetlength 123";
retval = Utils::shell(cmd, &output);
if (retval) {
FAIL() << "'" << cmd << "' exited with error code\n";
}

cmd = generate_repo_exec + " addtarget " + temp_dir.Path().string();
cmd += " --filename target1 --hwid hwid123 --serial serial123";
retval = Utils::shell(cmd, &output);
if (retval) {
FAIL() << "'" << cmd << "' exited with error code\n";
}

Json::Value targets = Utils::parseJSONFile(temp_dir.Path() / "repo/director/staging/targets.json");
EXPECT_EQ(targets["targets"].size(), 1);
EXPECT_EQ(targets["targets"]["target1"]["length"].asUInt(), 123);
EXPECT_EQ(targets["targets"]["target1"]["hashes"]["sha256"].asString(),
"8AB755C16DE6EE9B6224169B36CBF0F2A545F859BE385501AD82CDCCC240D0A6");

cmd = generate_repo_exec + " emptytargets " + temp_dir.Path().string();
retval = Utils::shell(cmd, &output);
if (retval) {
FAIL() << "'" << cmd << "' exited with error code\n"
<< "output: " << output;
}

Json::Value empty_targets = Utils::parseJSONFile(temp_dir.Path() / "repo/director/staging/targets.json");
EXPECT_EQ(empty_targets["targets"].size(), 0);
}

TEST(aktualizr_repo, oldtargets) {
TemporaryDirectory temp_dir;
UptaneRepo repo(temp_dir.Path(), "", "");
repo.generateRepo(key_type);
Uptane::Hash hash(Uptane::Hash::Type::kSha256, "8ab755c16de6ee9b6224169b36cbf0f2a545f859be385501ad82cdccc240d0a6");
repo.addCustomImage("target1", hash, 123);
repo.addCustomImage("target2", hash, 321);
repo.addTarget("target1", "test-hw", "test-serial");
repo.signTargets();
repo.addTarget("target2", "test-hw", "test-serial");

Json::Value targets = Utils::parseJSONFile(temp_dir.Path() / "repo/director/staging/targets.json");
EXPECT_EQ(targets["targets"].size(), 2);
EXPECT_EQ(targets["targets"]["target1"]["length"].asUInt(), 123);
EXPECT_EQ(targets["targets"]["target1"]["hashes"]["sha256"].asString(),
"8AB755C16DE6EE9B6224169B36CBF0F2A545F859BE385501AD82CDCCC240D0A6");
EXPECT_EQ(targets["targets"]["target2"]["length"].asUInt(), 321);
EXPECT_EQ(targets["targets"]["target2"]["hashes"]["sha256"].asString(),
"8AB755C16DE6EE9B6224169B36CBF0F2A545F859BE385501AD82CDCCC240D0A6");

Json::Value targets_current = Utils::parseJSONFile(temp_dir.Path() / "repo/director/targets.json");
EXPECT_EQ(targets_current["signed"]["targets"].size(), 1);
EXPECT_EQ(targets_current["signed"]["targets"]["target1"]["length"].asUInt(), 123);
EXPECT_EQ(targets_current["signed"]["targets"]["target1"]["hashes"]["sha256"].asString(),
"8AB755C16DE6EE9B6224169B36CBF0F2A545F859BE385501AD82CDCCC240D0A6");

std::string cmd = generate_repo_exec + " oldtargets " + temp_dir.Path().string();
std::string output;
int retval = Utils::shell(cmd, &output);
if (retval) {
FAIL() << "'" << cmd << "' exited with error code\n"
<< "output: " << output;
}
targets = Utils::parseJSONFile(temp_dir.Path() / "repo/director/staging/targets.json");
EXPECT_EQ(targets["targets"].size(), 1);
EXPECT_EQ(targets["targets"]["target1"]["length"].asUInt(), 123);
EXPECT_EQ(targets["targets"]["target1"]["hashes"]["sha256"].asString(),
"8AB755C16DE6EE9B6224169B36CBF0F2A545F859BE385501AD82CDCCC240D0A6");
}

#ifndef __NO_MAIN__
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
Expand Down
10 changes: 9 additions & 1 deletion src/aktualizr_repo/uptane_repo.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

#include "uptane_repo.h"

UptaneRepo::UptaneRepo(const boost::filesystem::path &path, const std::string &expires,
Expand All @@ -17,4 +18,11 @@ void UptaneRepo::addTarget(const std::string &target_name, const std::string &ha
director_repo_.addTarget(target_name, target, hardware_id, ecu_serial);
}
void UptaneRepo::addImage(const boost::filesystem::path &image_path) { image_repo_.addImage(image_path); }
void UptaneRepo::signTargets() { director_repo_.signTargets(); }
void UptaneRepo::addCustomImage(const std::string &name, const Uptane::Hash &hash, uint64_t length) {
image_repo_.addCustomImage(name, hash, length);
}

void UptaneRepo::signTargets() { director_repo_.signTargets(); }

void UptaneRepo::emptyTargets() { director_repo_.emptyTargets(); }
void UptaneRepo::oldTargets() { director_repo_.oldTargets(); }
3 changes: 3 additions & 0 deletions src/aktualizr_repo/uptane_repo.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ class UptaneRepo {
void generateRepo(KeyType key_type = KeyType::kRSA2048);
void addTarget(const std::string &target_name, const std::string &hardware_id, const std::string &ecu_serial);
void addImage(const boost::filesystem::path &image_path);
void addCustomImage(const std::string &name, const Uptane::Hash &hash, uint64_t length);
void signTargets();
void emptyTargets();
void oldTargets();

private:
DirectorRepo director_repo_;
Expand Down