Aktualizr
C++ SOTA Client
image_repo.cc
1 #include "image_repo.h"
2 
3 void ImageRepo::addImage(const std::string &name, Json::Value &target, const std::string &hardware_id,
4  const Delegation &delegation) {
5  boost::filesystem::path repo_dir(path_ / ImageRepo::dir);
6 
7  boost::filesystem::path targets_path =
8  delegation ? ((repo_dir / "delegations") / delegation.name).string() + ".json" : repo_dir / "targets.json";
9  Json::Value targets = Utils::parseJSONFile(targets_path)["signed"];
10  // TODO: support multiple hardware IDs.
11  target["custom"]["hardwareIds"][0] = hardware_id;
12  targets["targets"][name] = target;
13  targets["version"] = (targets["version"].asUInt()) + 1;
14 
15  auto role = delegation ? Uptane::Role(delegation.name, true) : Uptane::Role::Targets();
16  std::string signed_targets = Utils::jsonToCanonicalStr(signTuf(role, targets));
17  Utils::writeFile(targets_path, signed_targets);
18  updateRepo();
19 }
20 
21 void ImageRepo::addBinaryImage(const boost::filesystem::path &image_path, const boost::filesystem::path &targetname,
22  const std::string &hardware_id, const std::string &url, const Delegation &delegation) {
23  boost::filesystem::path repo_dir(path_ / ImageRepo::dir);
24  boost::filesystem::path targets_path = repo_dir / "targets";
25 
26  auto targetname_dir = targetname.parent_path();
27  boost::filesystem::create_directories(targets_path / targetname_dir);
28  boost::filesystem::copy_file(image_path, targets_path / targetname_dir / image_path.filename(),
29  boost::filesystem::copy_option::overwrite_if_exists);
30 
31  std::string image = Utils::readFile(image_path);
32 
33  Json::Value target;
34  target["length"] = Json::UInt64(image.size());
35  target["hashes"]["sha256"] = boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest(image)));
36  target["hashes"]["sha512"] = boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha512digest(image)));
37  target["custom"]["targetFormat"] = "BINARY";
38  if (!url.empty()) {
39  target["custom"]["uri"] = url;
40  }
41  addImage(targetname.string(), target, hardware_id, delegation);
42 }
43 
44 void ImageRepo::addCustomImage(const std::string &name, const Hash &hash, const uint64_t length,
45  const std::string &hardware_id, const std::string &url, const Delegation &delegation,
46  const Json::Value &custom) {
47  Json::Value target;
48  target["length"] = Json::UInt(length);
49  if (hash.type() == Hash::Type::kSha256) {
50  target["hashes"]["sha256"] = hash.HashString();
51  } else if (hash.type() == Hash::Type::kSha512) {
52  target["hashes"]["sha512"] = hash.HashString();
53  }
54  target["custom"] = custom;
55  if (!url.empty()) {
56  target["custom"]["uri"] = url;
57  }
58  addImage(name, target, hardware_id, delegation);
59 }
60 
61 void ImageRepo::addDelegation(const Uptane::Role &name, const Uptane::Role &parent_role, const std::string &path,
62  bool terminating, KeyType key_type) {
63  if (keys_.count(name) != 0) {
64  throw std::runtime_error("Delegation with the same name already exist.");
65  }
66  if (Uptane::Role::IsReserved(name.ToString())) {
67  throw std::runtime_error("Delegation name " + name.ToString() + " is reserved.");
68  }
69 
70  boost::filesystem::path repo_dir(path_ / ImageRepo::dir);
71 
72  boost::filesystem::path parent_path(repo_dir);
73  if (parent_role.IsDelegation()) {
74  parent_path = parent_path /= "delegations";
75  }
76  parent_path = parent_path /= (parent_role.ToString() + ".json");
77 
78  if (!boost::filesystem::exists(parent_path)) {
79  throw std::runtime_error("Delegation role " + parent_role.ToString() + " does not exist.");
80  }
81 
82  generateKeyPair(key_type, name);
83  Json::Value delegate;
84  delegate["_type"] = "Targets";
85  delegate["expires"] = expiration_time_;
86  delegate["version"] = 1;
87  delegate["targets"] = Json::objectValue;
88 
89  std::string delegate_signed = Utils::jsonToCanonicalStr(signTuf(name, delegate));
90  Utils::writeFile((repo_dir / "delegations" / name.ToString()).string() + ".json", delegate_signed);
91 
92  Json::Value parent_notsigned = Utils::parseJSONFile(parent_path)["signed"];
93 
94  auto keypair = keys_[name];
95  parent_notsigned["delegations"]["keys"][keypair.public_key.KeyId()] = keypair.public_key.ToUptane();
96  Json::Value role;
97  role["name"] = name.ToString();
98  role["keyids"].append(keypair.public_key.KeyId());
99  role["paths"].append(path);
100  role["threshold"] = 1;
101  role["terminating"] = terminating;
102  parent_notsigned["delegations"]["roles"].append(role);
103  parent_notsigned["version"] = (parent_notsigned["version"].asUInt()) + 1;
104 
105  std::string signed_parent = Utils::jsonToCanonicalStr(signTuf(parent_role, parent_notsigned));
106  Utils::writeFile(parent_path, signed_parent);
107  updateRepo();
108 }
109 
110 void ImageRepo::removeDelegationRecursive(const Uptane::Role &name, const Uptane::Role &parent_name) {
111  boost::filesystem::path repo_dir(path_ / ImageRepo::dir);
112  if (parent_name.IsDelegation()) {
113  repo_dir = repo_dir / "delegations";
114  }
115  Json::Value targets = Utils::parseJSONFile(repo_dir / (parent_name.ToString() + ".json"))["signed"];
116 
117  auto keypair = keys_[name];
118  targets["delegations"]["keys"].removeMember(
119  keypair.public_key.KeyId()); // doesn't do anything if the key doesn't exist
120  Json::Value new_roles(Json::arrayValue);
121  for (const auto &role : targets["delegations"]["roles"]) {
122  auto delegated_name = role["name"].asString();
123  if (delegated_name != name.ToString()) {
124  new_roles.append(role);
125  removeDelegationRecursive(name, Uptane::Role(delegated_name, true));
126  }
127  }
128  targets["delegations"]["roles"] = new_roles;
129  targets["version"] = (targets["version"].asUInt()) + 1;
130  Utils::writeFile(repo_dir / (parent_name.ToString() + ".json"),
131  Utils::jsonToCanonicalStr(signTuf(parent_name, targets)));
132 }
133 
134 void ImageRepo::revokeDelegation(const Uptane::Role &name) {
135  if (keys_.count(name) == 0) {
136  throw std::runtime_error("Delegation does not exist.");
137  }
138  if (Uptane::Role::IsReserved(name.ToString())) {
139  throw std::runtime_error("Delegation name " + name.ToString() + " is reserved.");
140  }
141 
142  boost::filesystem::path keys_dir = path_ / ("keys/image/" + name.ToString());
143  boost::filesystem::remove_all(keys_dir);
144 
145  boost::filesystem::path repo_dir(path_ / ImageRepo::dir);
146 
147  boost::filesystem::remove(repo_dir / "delegations" / (name.ToString() + ".json"));
148 
149  removeDelegationRecursive(name, Uptane::Role::Targets());
150  updateRepo();
151 }
152 
153 std::vector<std::string> ImageRepo::getDelegationTargets(const Uptane::Role &name) {
154  std::vector<std::string> result;
155  boost::filesystem::path repo_dir(path_ / ImageRepo::dir);
156  auto targets = Utils::parseJSONFile((repo_dir / "delegations") / (name.ToString() + ".json"))["signed"]["targets"];
157  for (auto it = targets.begin(); it != targets.end(); ++it) {
158  result.push_back(it.key().asString());
159  }
160  return result;
161 }
Hash
The Hash class The hash of a file or Uptane metadata.
Definition: types.h:159
result
Results of libaktualizr API calls.
Definition: results.h:12
Uptane::Role
TUF Roles.
Definition: tuf.h:61
Uptane
Base data types that are used in The Update Framework (TUF), part of Uptane.
Definition: packagemanagerinterface.h:18
Delegation
Definition: repo.h:19