Aktualizr
C++ SOTA Client
managedsecondary.cc
1 #include "uptane/managedsecondary.h"
2 
3 #include <boost/algorithm/hex.hpp>
4 #include <boost/filesystem.hpp>
5 
6 #include "crypto/crypto.h"
7 #include "logging/logging.h"
8 #include "utilities/events.h"
9 
10 namespace Uptane {
11 ManagedSecondary::ManagedSecondary(const SecondaryConfig &sconfig_in) : SecondaryInterface(sconfig_in) {
12  boost::filesystem::create_directories(sconfig.metadata_path);
13 
14  // TODO: FIX
15  // loadMetadata(meta_pack);
16  std::string public_key_string;
17 
18  if (!loadKeys(&public_key_string, &private_key)) {
19  if (!Crypto::generateKeyPair(sconfig.key_type, &public_key_string, &private_key)) {
20  LOG_ERROR << "Could not generate rsa keys for secondary " << ManagedSecondary::getSerial() << "@"
21  << sconfig.ecu_hardware_id;
22  throw std::runtime_error("Unable to generate secondary rsa keys");
23  }
24  storeKeys(public_key_string, private_key);
25  }
26  public_key_ = PublicKey(public_key_string, sconfig.key_type);
27 }
28 
29 void ManagedSecondary::rawToMeta() {
30  // raw meta is trusted
31  current_meta.director_root = Uptane::Root(RepositoryType::Director, Utils::parseJSON(current_raw_meta.director_root));
32  current_meta.director_targets = Uptane::Targets(Utils::parseJSON(current_raw_meta.director_targets));
33  current_meta.image_root = Uptane::Root(RepositoryType::Images, Utils::parseJSON(current_raw_meta.image_root));
34  current_meta.image_targets = Uptane::Targets(Utils::parseJSON(current_raw_meta.image_targets));
35  current_meta.image_timestamp = Uptane::TimestampMeta(Utils::parseJSON(current_raw_meta.image_timestamp));
36  current_meta.image_snapshot = Uptane::Snapshot(Utils::parseJSON(current_raw_meta.image_snapshot));
37 }
38 
39 bool ManagedSecondary::putMetadata(const RawMetaPack &meta_pack) {
40  // No verification is currently performed, we can add verification in future for testing purposes
41  detected_attack = "";
42 
43  current_raw_meta = meta_pack;
44  rawToMeta(); // current_raw_meta -> current_meta
45  if (!current_meta.isConsistent()) {
46  return false;
47  }
48  storeMetadata(current_raw_meta);
49 
50  expected_target_name = "";
51  expected_target_hashes.clear();
52  expected_target_length = 0;
53 
54  bool target_found = false;
55 
56  std::vector<Uptane::Target>::const_iterator it;
57  for (it = current_meta.director_targets.targets.begin(); it != current_meta.director_targets.targets.end(); ++it) {
58  // TODO: what about hardware ID? Also missing in Uptane::Target
59  if (it->ecus().find(getSerial()) != it->ecus().end()) {
60  if (target_found) {
61  detected_attack = "Duplicate entry for this ECU";
62  break;
63  }
64  expected_target_name = it->filename();
65  expected_target_hashes = it->hashes();
66  expected_target_length = it->length();
67  target_found = true;
68  }
69  }
70 
71  if (!target_found) {
72  detected_attack = "No update for this ECU";
73  }
74 
75  return true;
76 }
77 
78 int ManagedSecondary::getRootVersion(const bool director) {
79  if (director) {
80  return current_meta.director_root.version();
81  }
82  return current_meta.image_root.version();
83 }
84 
85 bool ManagedSecondary::putRoot(const std::string &root, const bool director) {
86  Uptane::Root &prev_root = (director) ? current_meta.director_root : current_meta.image_root;
87  std::string &prev_raw_root = (director) ? current_raw_meta.director_root : current_raw_meta.image_root;
88  Uptane::Root new_root =
89  Uptane::Root((director) ? RepositoryType::Director : RepositoryType::Images, Utils::parseJSON(root));
90 
91  // No verification is currently performed, we can add verification in future for testing purposes
92  if (new_root.version() == prev_root.version() + 1) {
93  prev_root = new_root;
94  prev_raw_root = root;
95  } else {
96  detected_attack = "Tried to update root version " + std::to_string(prev_root.version()) + " with version " +
97  std::to_string(new_root.version());
98  }
99 
100  if (!current_meta.isConsistent()) {
101  return false;
102  }
103  storeMetadata(current_raw_meta);
104  return true;
105 }
106 
107 bool ManagedSecondary::sendFirmwareAsync(const std::shared_ptr<std::string> &data) {
108  if (!install_future.valid() || install_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
109  install_future = std::async(std::launch::async, &ManagedSecondary::sendFirmware, this, data);
110  return true;
111  }
112  return false;
113 }
114 
115 bool ManagedSecondary::sendFirmware(const std::shared_ptr<std::string> &data) {
116  sendEvent(std::make_shared<event::InstallStarted>(getSerial()));
117 
118  if (expected_target_name.empty()) {
119  sendEvent(std::make_shared<event::InstallComplete>(getSerial()));
120  return true;
121  }
122  if (!detected_attack.empty()) {
123  sendEvent(std::make_shared<event::InstallComplete>(getSerial()));
124  return true;
125  }
126 
127  if (data->size() > static_cast<size_t>(expected_target_length)) {
128  detected_attack = "overflow";
129  sendEvent(std::make_shared<event::InstallComplete>(getSerial()));
130  return true;
131  }
132 
133  std::vector<Hash>::const_iterator it;
134  for (it = expected_target_hashes.begin(); it != expected_target_hashes.end(); it++) {
135  if (it->TypeString() == "sha256") {
136  if (boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest(*data))) !=
137  boost::algorithm::to_lower_copy(it->HashString())) {
138  detected_attack = "wrong_hash";
139  sendEvent(std::make_shared<event::InstallComplete>(getSerial()));
140  return true;
141  }
142  } else if (it->TypeString() == "sha512") {
143  if (boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha512digest(*data))) !=
144  boost::algorithm::to_lower_copy(it->HashString())) {
145  detected_attack = "wrong_hash";
146  sendEvent(std::make_shared<event::InstallComplete>(getSerial()));
147  return true;
148  }
149  }
150  }
151  detected_attack = "";
152  bool result = storeFirmware(expected_target_name, *data);
153  sendEvent(std::make_shared<event::InstallComplete>(getSerial()));
154  return result;
155 }
156 
157 Json::Value ManagedSecondary::getManifest() {
158  std::string hash;
159  std::string targetname;
160  size_t target_len;
161  if (!getFirmwareInfo(&targetname, target_len, &hash)) {
162  return Json::nullValue;
163  }
164 
165  Json::Value manifest;
166 
167  // package manager will generate this part in future
168  Json::Value installed_image;
169  installed_image["filepath"] = targetname;
170 
171  installed_image["fileinfo"]["hashes"]["sha256"] = hash;
172  installed_image["fileinfo"]["length"] = static_cast<Json::Int64>(target_len);
173 
174  manifest["attacks_detected"] = detected_attack;
175  manifest["installed_image"] = installed_image;
176  manifest["ecu_serial"] = getSerial().ToString();
177  manifest["previous_timeserver_time"] = "1970-01-01T00:00:00Z";
178  manifest["timeserver_time"] = "1970-01-01T00:00:00Z";
179 
180  Json::Value signed_ecu_version;
181 
182  std::string b64sig = Utils::toBase64(Crypto::RSAPSSSign(nullptr, private_key, Json::FastWriter().write(manifest)));
183  Json::Value signature;
184  signature["method"] = "rsassa-pss";
185  signature["sig"] = b64sig;
186 
187  signature["keyid"] = public_key_.KeyId();
188  signed_ecu_version["signed"] = manifest;
189  signed_ecu_version["signatures"] = Json::Value(Json::arrayValue);
190  signed_ecu_version["signatures"].append(signature);
191 
192  return signed_ecu_version;
193 }
194 
195 void ManagedSecondary::storeKeys(const std::string &pub_key, const std::string &priv_key) {
196  Utils::writeFile((sconfig.full_client_dir / sconfig.ecu_private_key), priv_key);
197  Utils::writeFile((sconfig.full_client_dir / sconfig.ecu_public_key), pub_key);
198 }
199 
200 bool ManagedSecondary::loadKeys(std::string *pub_key, std::string *priv_key) {
201  boost::filesystem::path public_key_path = sconfig.full_client_dir / sconfig.ecu_public_key;
202  boost::filesystem::path private_key_path = sconfig.full_client_dir / sconfig.ecu_private_key;
203 
204  if (!boost::filesystem::exists(public_key_path) || !boost::filesystem::exists(private_key_path)) {
205  return false;
206  }
207 
208  *priv_key = Utils::readFile(private_key_path.string());
209  *pub_key = Utils::readFile(public_key_path.string());
210  return true;
211 }
212 } // namespace Uptane
General data structures.
Definition: types.cc:6
Base data types that are used in The Update Framework (TUF), part of UPTANE.