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

Commit

Permalink
Merge pull request #1525 from advancedtelematic/fix/secondary-root-ve…
Browse files Browse the repository at this point in the history
…rsion-bump

Fix/secondary root version bump
  • Loading branch information
lbonn authored Jan 17, 2020
2 parents 14f7c6e + d62ecf4 commit 5d9ec24
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 20 deletions.
7 changes: 5 additions & 2 deletions src/aktualizr_secondary/aktualizr_secondary_metadata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,14 @@ bool Metadata::getRoleMetadata(std::string* result, const Uptane::RepositoryType
}

if (role == Uptane::Role::Root() && version != Uptane::Version()) {
if (repo == Uptane::RepositoryType::Director() && director_root_version != version) {
// If requesting a Root version beyond what we have available, fail as
// expected. If requesting a version before what is available, just use what
// is available, since root rotation isn't supported here.
if (repo == Uptane::RepositoryType::Director() && director_root_version < version) {
LOG_DEBUG << "Requested Director root version " << version << " but only version " << director_root_version
<< " is available.";
return false;
} else if (repo == Uptane::RepositoryType::Image() && image_root_version != version) {
} else if (repo == Uptane::RepositoryType::Image() && image_root_version < version) {
LOG_DEBUG << "Requested Image repo root version " << version << " but only version " << image_root_version
<< " is available.";
return false;
Expand Down
12 changes: 12 additions & 0 deletions src/aktualizr_secondary/aktualizr_secondary_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ class UptaneRepoWrapper {
return image_data;
}

void refreshRoot(Uptane::RepositoryType repo) { _uptane_repo.refresh(repo, Uptane::Role::Root()); }

private:
TemporaryDirectory _root_dir;
boost::filesystem::path _director_dir{_root_dir / "repo/director"};
Expand Down Expand Up @@ -190,6 +192,16 @@ TEST_F(SecondaryTest, IncorrectTargetQuantity) {
}
}

TEST_F(SecondaryTest, DirectorRootVersionIncremented) {
_uptane_repo.refreshRoot(Uptane::RepositoryType::Director());
EXPECT_TRUE(_secondary->putMetadata(_uptane_repo.getCurrentMetadata()));
}

TEST_F(SecondaryTest, ImageRootVersionIncremented) {
_uptane_repo.refreshRoot(Uptane::RepositoryType::Image());
EXPECT_TRUE(_secondary->putMetadata(_uptane_repo.getCurrentMetadata()));
}

TEST_F(SecondaryTest, InvalidImageFileSize) {
EXPECT_TRUE(_secondary->putMetadata(_uptane_repo.getCurrentMetadata()));
auto image_data = getImageData();
Expand Down
1 change: 1 addition & 0 deletions src/libaktualizr/uptane/tuf.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class Version {
int version() const { return version_; }
bool operator==(const Version &rhs) const { return version_ == rhs.version_; }
bool operator!=(const Version &rhs) const { return version_ != rhs.version_; }
bool operator<(const Version &rhs) const { return version_ < rhs.version_; }

private:
static const int ANY_VERSION = -1;
Expand Down
10 changes: 9 additions & 1 deletion src/uptane_generator/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ int main(int argc, char **argv) {
"emptytargets: \tclear the staged director targets metadata\n"
"oldtargets: \tfill the staged director targets metadata with what is currently signed\n"
"sign: \tsign arbitrary metadata with repo keys\n"
"addcampaigns: \tgenerate campaigns json")
"addcampaigns: \tgenerate campaigns json\n"
"refresh: \trefresh a metadata object (bump the version)")
("path", po::value<boost::filesystem::path>(), "path to the repository")
("filename", po::value<boost::filesystem::path>(), "path to the image")
("hwid", po::value<std::string>(), "target hardware identifier")
Expand Down Expand Up @@ -219,6 +220,13 @@ int main(int argc, char **argv) {
} else if (command == "addcampaigns") {
repo.generateCampaigns();
std::cout << "Generated campaigns" << std::endl;
} else if (command == "refresh") {
if (vm.count("repotype") == 0 || vm.count("keyname") == 0) {
std::cerr << "refresh command requires --repotype and --keyname\n";
exit(EXIT_FAILURE);
}
repo.refresh(Uptane::RepositoryType(vm["repotype"].as<std::string>()),
Uptane::Role(vm["keyname"].as<std::string>()));
} else {
std::cout << desc << std::endl;
exit(EXIT_FAILURE);
Expand Down
62 changes: 48 additions & 14 deletions src/uptane_generator/repo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ Repo::Repo(Uptane::RepositoryType repo_type, boost::filesystem::path path, const
}
}

if (repo_type == Uptane::RepositoryType("director")) {
if (repo_type_ == Uptane::RepositoryType::Director()) {
repo_dir_ = path_ / DirectorRepo::dir;
} else if (repo_type == Uptane::RepositoryType("image")) {
} else if (repo_type_ == Uptane::RepositoryType::Image()) {
repo_dir_ = path_ / ImageRepo::dir;
}
}
Expand All @@ -47,33 +47,29 @@ void Repo::addDelegationToSnapshot(Json::Value *snapshot, const Uptane::Role &ro
}

void Repo::updateRepo() {
boost::filesystem::path repo_dir = repo_dir_;
Json::Value old_snapshot = Utils::parseJSONFile(repo_dir / "snapshot.json")["signed"];

const Json::Value old_snapshot = Utils::parseJSONFile(repo_dir_ / "snapshot.json")["signed"];
Json::Value snapshot;
snapshot["_type"] = "Snapshot";
snapshot["expires"] = old_snapshot["expires"];
snapshot["version"] = (old_snapshot["version"].asUInt()) + 1;

Json::Value root = Utils::parseJSONFile(repo_dir / "root.json")["signed"];
std::string signed_root = Utils::readFile(repo_dir / "root.json");

const Json::Value root = Utils::parseJSONFile(repo_dir_ / "root.json")["signed"];
snapshot["meta"]["root.json"]["version"] = root["version"].asUInt();

addDelegationToSnapshot(&snapshot, Uptane::Role::Targets());

std::string signed_snapshot = Utils::jsonToCanonicalStr(signTuf(Uptane::Role::Snapshot(), snapshot));
Utils::writeFile(repo_dir / "snapshot.json", signed_snapshot);
const std::string signed_snapshot = Utils::jsonToCanonicalStr(signTuf(Uptane::Role::Snapshot(), snapshot));
Utils::writeFile(repo_dir_ / "snapshot.json", signed_snapshot);

Json::Value timestamp = Utils::parseJSONFile(repo_dir / "timestamp.json")["signed"];
Json::Value timestamp = Utils::parseJSONFile(repo_dir_ / "timestamp.json")["signed"];
timestamp["version"] = (timestamp["version"].asUInt()) + 1;
timestamp["meta"]["snapshot.json"]["hashes"]["sha256"] =
boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest(signed_snapshot)));
timestamp["meta"]["snapshot.json"]["hashes"]["sha512"] =
boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha512digest(signed_snapshot)));
timestamp["meta"]["snapshot.json"]["length"] = static_cast<Json::UInt>(signed_snapshot.length());
timestamp["meta"]["snapshot.json"]["version"] = snapshot["version"].asUInt();
Utils::writeFile(repo_dir / "timestamp.json",
Utils::writeFile(repo_dir_ / "timestamp.json",
Utils::jsonToCanonicalStr(signTuf(Uptane::Role::Timestamp(), timestamp)));
}

Expand Down Expand Up @@ -180,7 +176,7 @@ void Repo::generateRepo(KeyType key_type) {
role["keyids"].append(keys_[Uptane::Role::Timestamp()].public_key.KeyId());
root["roles"]["timestamp"] = role;

std::string signed_root = Utils::jsonToCanonicalStr(signTuf(Uptane::Role::Root(), root));
const std::string signed_root = Utils::jsonToCanonicalStr(signTuf(Uptane::Role::Root(), root));
Utils::writeFile(repo_dir_ / "root.json", signed_root);
Utils::writeFile(repo_dir_ / "1.root.json", signed_root);

Expand All @@ -192,7 +188,7 @@ void Repo::generateRepo(KeyType key_type) {
if (repo_type_ == Uptane::RepositoryType::Director() && correlation_id_ != "") {
targets["custom"]["correlationId"] = correlation_id_;
}
std::string signed_targets = Utils::jsonToCanonicalStr(signTuf(Uptane::Role::Targets(), targets));
const std::string signed_targets = Utils::jsonToCanonicalStr(signTuf(Uptane::Role::Targets(), targets));
Utils::writeFile(repo_dir_ / "targets.json", signed_targets);

Json::Value snapshot;
Expand Down Expand Up @@ -280,6 +276,44 @@ void Repo::readKeys() {
}
}

void Repo::refresh(const Uptane::Role &role) {
boost::filesystem::path meta_path = repo_dir_;

if (repo_type_ == Uptane::RepositoryType::Director() &&
(role == Uptane::Role::Timestamp() || role == Uptane::Role::Snapshot())) {
throw std::runtime_error("The " + role.ToString() + " in the Director repo is not currently supported.");
}

if (role == Uptane::Role::Root()) {
meta_path /= "root.json";
} else if (role == Uptane::Role::Timestamp()) {
meta_path /= "timestamp.json";
} else if (role == Uptane::Role::Snapshot()) {
meta_path /= "snapshot.json";
} else if (role == Uptane::Role::Targets()) {
meta_path /= "targets.json";
} else {
throw std::runtime_error("Refreshing custom role " + role.ToString() + " is not currently supported.");
}

// The only interesting part here is to increment the version. It could be
// interesting to allow changing the expiry, too.
Json::Value meta_raw = Utils::parseJSONFile(meta_path)["signed"];
const unsigned version = meta_raw["version"].asUInt() + 1;
meta_raw["version"] = version;
const std::string signed_meta = Utils::jsonToCanonicalStr(signTuf(role, meta_raw));
Utils::writeFile(meta_path, signed_meta);

// Write a new numbered version of the Root if relevant.
if (role == Uptane::Role::Root()) {
std::stringstream root_name;
root_name << version << ".root.json";
Utils::writeFile(repo_dir_ / root_name.str(), signed_meta);
}

updateRepo();
}

Delegation::Delegation(const boost::filesystem::path &repo_path, std::string delegation_name)
: name(std::move(delegation_name)) {
if (Uptane::Role::IsReserved(name)) {
Expand Down
1 change: 1 addition & 0 deletions src/uptane_generator/repo.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Repo {
Json::Value getTarget(const std::string &target_name);
Json::Value signTuf(const Uptane::Role &role, const Json::Value &json);
void generateCampaigns() const;
void refresh(const Uptane::Role &role);

protected:
void generateRepoKeys(KeyType key_type);
Expand Down
120 changes: 120 additions & 0 deletions src/uptane_generator/repo_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,126 @@ TEST(uptane_generator, generateCampaigns) {
EXPECT_EQ(campaigns["campaigns"][0]["metadata"][2]["value"], "20");
}

/*
* Bump the version of the Director Root metadata.
*/
TEST(uptane_generator, refreshDirectorRoot) {
TemporaryDirectory temp_dir;
UptaneRepo repo(temp_dir.Path(), "", "");
repo.generateRepo(key_type);
repo.refresh(Uptane::RepositoryType::Director(), Uptane::Role::Root());

const Json::Value director_root = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "root.json");
EXPECT_EQ(director_root["signed"]["version"].asUInt(), 2);
const Json::Value director_timestamp = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "timestamp.json");
EXPECT_EQ(director_timestamp["signed"]["version"].asUInt(), 2);
const Json::Value director_snapshot = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "snapshot.json");
EXPECT_EQ(director_snapshot["signed"]["version"].asUInt(), 2);
const Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
EXPECT_EQ(director_targets["signed"]["version"].asUInt(), 1);

const Json::Value image_root = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "root.json");
EXPECT_EQ(image_root["signed"]["version"].asUInt(), 1);
const Json::Value image_timestamp = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "timestamp.json");
EXPECT_EQ(image_timestamp["signed"]["version"].asUInt(), 1);
const Json::Value image_snapshot = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "snapshot.json");
EXPECT_EQ(image_snapshot["signed"]["version"].asUInt(), 1);
const Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
EXPECT_EQ(image_targets["signed"]["version"].asUInt(), 1);

check_repo(temp_dir);
}

/*
* Bump the version of the Director Targets metadata.
*/
TEST(uptane_generator, refreshDirectorTargets) {
TemporaryDirectory temp_dir;
UptaneRepo repo(temp_dir.Path(), "", "");
repo.generateRepo(key_type);
repo.refresh(Uptane::RepositoryType::Director(), Uptane::Role::Targets());

const Json::Value director_root = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "root.json");
EXPECT_EQ(director_root["signed"]["version"].asUInt(), 1);
const Json::Value director_timestamp = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "timestamp.json");
EXPECT_EQ(director_timestamp["signed"]["version"].asUInt(), 2);
const Json::Value director_snapshot = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "snapshot.json");
EXPECT_EQ(director_snapshot["signed"]["version"].asUInt(), 2);
const Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
EXPECT_EQ(director_targets["signed"]["version"].asUInt(), 2);

const Json::Value image_root = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "root.json");
EXPECT_EQ(image_root["signed"]["version"].asUInt(), 1);
const Json::Value image_timestamp = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "timestamp.json");
EXPECT_EQ(image_timestamp["signed"]["version"].asUInt(), 1);
const Json::Value image_snapshot = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "snapshot.json");
EXPECT_EQ(image_snapshot["signed"]["version"].asUInt(), 1);
const Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
EXPECT_EQ(image_targets["signed"]["version"].asUInt(), 1);

check_repo(temp_dir);
}

/*
* Bump the version of the Image repo Root metadata.
*/
TEST(uptane_generator, refreshImageRoot) {
TemporaryDirectory temp_dir;
UptaneRepo repo(temp_dir.Path(), "", "");
repo.generateRepo(key_type);
repo.refresh(Uptane::RepositoryType::Image(), Uptane::Role::Root());

const Json::Value director_root = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "root.json");
EXPECT_EQ(director_root["signed"]["version"].asUInt(), 1);
const Json::Value director_timestamp = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "timestamp.json");
EXPECT_EQ(director_timestamp["signed"]["version"].asUInt(), 1);
const Json::Value director_snapshot = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "snapshot.json");
EXPECT_EQ(director_snapshot["signed"]["version"].asUInt(), 1);
const Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
EXPECT_EQ(director_targets["signed"]["version"].asUInt(), 1);

const Json::Value image_root = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "root.json");
EXPECT_EQ(image_root["signed"]["version"].asUInt(), 2);
const Json::Value image_timestamp = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "timestamp.json");
EXPECT_EQ(image_timestamp["signed"]["version"].asUInt(), 2);
const Json::Value image_snapshot = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "snapshot.json");
EXPECT_EQ(image_snapshot["signed"]["version"].asUInt(), 2);
const Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
EXPECT_EQ(image_targets["signed"]["version"].asUInt(), 1);

check_repo(temp_dir);
}

/*
* Bump the version of the Image repo Targets metadata.
*/
TEST(uptane_generator, refreshImageTargets) {
TemporaryDirectory temp_dir;
UptaneRepo repo(temp_dir.Path(), "", "");
repo.generateRepo(key_type);
repo.refresh(Uptane::RepositoryType::Image(), Uptane::Role::Targets());

const Json::Value director_root = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "root.json");
EXPECT_EQ(director_root["signed"]["version"].asUInt(), 1);
const Json::Value director_timestamp = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "timestamp.json");
EXPECT_EQ(director_timestamp["signed"]["version"].asUInt(), 1);
const Json::Value director_snapshot = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "snapshot.json");
EXPECT_EQ(director_snapshot["signed"]["version"].asUInt(), 1);
const Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
EXPECT_EQ(director_targets["signed"]["version"].asUInt(), 1);

const Json::Value image_root = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "root.json");
EXPECT_EQ(image_root["signed"]["version"].asUInt(), 1);
const Json::Value image_timestamp = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "timestamp.json");
EXPECT_EQ(image_timestamp["signed"]["version"].asUInt(), 2);
const Json::Value image_snapshot = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "snapshot.json");
EXPECT_EQ(image_snapshot["signed"]["version"].asUInt(), 2);
const Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
EXPECT_EQ(image_targets["signed"]["version"].asUInt(), 2);

check_repo(temp_dir);
}

#ifndef __NO_MAIN__
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
Expand Down
12 changes: 10 additions & 2 deletions src/uptane_generator/uptane_repo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ void UptaneRepo::generateRepo(KeyType key_type) {
image_repo_.generateRepo(key_type);
}

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

void UptaneRepo::addTarget(const std::string &target_name, const std::string &hardware_id,
const std::string &ecu_serial, const std::string &url) {
auto target = image_repo_.getTarget(target_name);
Expand Down Expand Up @@ -45,3 +43,13 @@ void UptaneRepo::signTargets() { director_repo_.signTargets(); }

void UptaneRepo::emptyTargets() { director_repo_.emptyTargets(); }
void UptaneRepo::oldTargets() { director_repo_.oldTargets(); }

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

void UptaneRepo::refresh(Uptane::RepositoryType repo_type, const Uptane::Role &role) {
if (repo_type == Uptane::RepositoryType(Uptane::RepositoryType::Director())) {
director_repo_.refresh(role);
} else if (repo_type == Uptane::RepositoryType(Uptane::RepositoryType::Image())) {
image_repo_.refresh(role);
}
}
2 changes: 1 addition & 1 deletion src/uptane_generator/uptane_repo.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class UptaneRepo {
void signTargets();
void emptyTargets();
void oldTargets();

void generateCampaigns();
void refresh(Uptane::RepositoryType repo_type, const Uptane::Role &role);

private:
DirectorRepo director_repo_;
Expand Down

0 comments on commit 5d9ec24

Please sign in to comment.