Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
managedsecondary.cc
1 #include "managedsecondary.h"
2 
3 #include <sys/stat.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 
7 #include <boost/algorithm/hex.hpp>
8 #include <boost/filesystem.hpp>
9 
10 #include "crypto/crypto.h"
11 #include "logging/logging.h"
12 #include "uptane/manifest.h"
13 #include "uptane/uptanerepository.h"
14 #include "utilities/exceptions.h"
15 
16 namespace Primary {
17 
18 ManagedSecondary::ManagedSecondary(Primary::ManagedSecondaryConfig sconfig_in) : sconfig(std::move(sconfig_in)) {
19  // TODO: FIX
20  // loadMetadata(meta_pack);
21  std::string public_key_string;
22 
23  if (!loadKeys(&public_key_string, &private_key)) {
24  if (!Crypto::generateKeyPair(sconfig.key_type, &public_key_string, &private_key)) {
25  LOG_ERROR << "Could not generate rsa keys for secondary " << ManagedSecondary::getSerial() << "@"
26  << sconfig.ecu_hardware_id;
27  throw std::runtime_error("Unable to generate secondary rsa keys");
28  }
29 
30  // do not store keys yet, wait until SotaUptaneClient performed device initialization
31  }
32  public_key_ = PublicKey(public_key_string, sconfig.key_type);
33  Initialize();
34 }
35 
36 void ManagedSecondary::Initialize() {
37  struct stat st {};
38 
39  if (!boost::filesystem::is_directory(sconfig.metadata_path)) {
40  Utils::createDirectories(sconfig.metadata_path, S_IRWXU);
41  }
42  if (stat(sconfig.metadata_path.c_str(), &st) < 0) {
43  throw std::runtime_error(std::string("Could not check metadata directory permissions: ") + std::strerror(errno));
44  }
45  if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
46  throw std::runtime_error("Secondary metadata directory has unsafe permissions");
47  }
48 
49  if (!boost::filesystem::is_directory(sconfig.full_client_dir)) {
50  Utils::createDirectories(sconfig.full_client_dir, S_IRWXU);
51  }
52  if (stat(sconfig.full_client_dir.c_str(), &st) < 0) {
53  throw std::runtime_error(std::string("Could not check client directory permissions: ") + std::strerror(errno));
54  }
55  if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
56  throw std::runtime_error("Secondary client directory has unsafe permissions");
57  }
58 
59  storeKeys(public_key_.Value(), private_key);
60 }
61 
62 void ManagedSecondary::rawToMeta() {
63  // raw meta is trusted
64  current_meta.director_root =
65  Uptane::Root(Uptane::RepositoryType::Director(), Utils::parseJSON(current_raw_meta.director_root));
66  current_meta.director_targets = Uptane::Targets(Utils::parseJSON(current_raw_meta.director_targets));
67  current_meta.image_root =
68  Uptane::Root(Uptane::RepositoryType::Image(), Utils::parseJSON(current_raw_meta.image_root));
69  current_meta.image_targets = Uptane::Targets(Utils::parseJSON(current_raw_meta.image_targets));
70  current_meta.image_timestamp = Uptane::TimestampMeta(Utils::parseJSON(current_raw_meta.image_timestamp));
71  current_meta.image_snapshot = Uptane::Snapshot(Utils::parseJSON(current_raw_meta.image_snapshot));
72 }
73 
74 bool ManagedSecondary::putMetadata(const Uptane::RawMetaPack &meta_pack) {
75  // No verification is currently performed, we can add verification in future for testing purposes
76  detected_attack = "";
77 
78  current_raw_meta = meta_pack;
79  rawToMeta(); // current_raw_meta -> current_meta
80  if (!current_meta.isConsistent()) {
81  return false;
82  }
83  storeMetadata(current_raw_meta);
84 
85  expected_target_name = "";
86  expected_target_hashes.clear();
87  expected_target_length = 0;
88 
89  bool target_found = false;
90 
91  std::vector<Uptane::Target>::const_iterator it;
92  for (it = current_meta.director_targets.targets.begin(); it != current_meta.director_targets.targets.end(); ++it) {
93  // TODO: what about hardware ID? Also missing in Uptane::Target
94  if (it->ecus().find(getSerial()) != it->ecus().end()) {
95  if (target_found) {
96  detected_attack = "Duplicate entry for this ECU";
97  break;
98  }
99  expected_target_name = it->filename();
100  expected_target_hashes = it->hashes();
101  expected_target_length = it->length();
102  target_found = true;
103  }
104  }
105 
106  if (!target_found) {
107  detected_attack = "No update for this ECU";
108  }
109 
110  return true;
111 }
112 
113 int ManagedSecondary::getRootVersion(const bool director) const {
114  if (director) {
115  return current_meta.director_root.version();
116  }
117  return current_meta.image_root.version();
118 }
119 
120 bool ManagedSecondary::putRoot(const std::string &root, const bool director) {
121  Uptane::Root &prev_root = (director) ? current_meta.director_root : current_meta.image_root;
122  std::string &prev_raw_root = (director) ? current_raw_meta.director_root : current_raw_meta.image_root;
123  Uptane::Root new_root = Uptane::Root(
124  (director) ? Uptane::RepositoryType::Director() : Uptane::RepositoryType::Image(), Utils::parseJSON(root));
125 
126  // No verification is currently performed, we can add verification in future for testing purposes
127  if (new_root.version() == prev_root.version() + 1) {
128  prev_root = new_root;
129  prev_raw_root = root;
130  } else {
131  detected_attack = "Tried to update root version " + std::to_string(prev_root.version()) + " with version " +
132  std::to_string(new_root.version());
133  }
134 
135  if (!current_meta.isConsistent()) {
136  return false;
137  }
138  storeMetadata(current_raw_meta);
139  return true;
140 }
141 
142 bool ManagedSecondary::sendFirmware(const std::string &data) {
143  std::lock_guard<std::mutex> l(install_mutex);
144 
145  if (expected_target_name.empty()) {
146  return false;
147  }
148  if (!detected_attack.empty()) {
149  return false;
150  }
151 
152  if (data.size() > static_cast<size_t>(expected_target_length)) {
153  detected_attack = "overflow";
154  return false;
155  }
156 
157  std::vector<Uptane::Hash>::const_iterator it;
158  for (it = expected_target_hashes.begin(); it != expected_target_hashes.end(); it++) {
159  if (it->TypeString() == "sha256") {
160  if (boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest(data))) !=
161  boost::algorithm::to_lower_copy(it->HashString())) {
162  detected_attack = "wrong_hash";
163  return false;
164  }
165  } else if (it->TypeString() == "sha512") {
166  if (boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha512digest(data))) !=
167  boost::algorithm::to_lower_copy(it->HashString())) {
168  detected_attack = "wrong_hash";
169  return false;
170  }
171  }
172  }
173  detected_attack = "";
174  const bool result = storeFirmware(expected_target_name, data);
175  return result;
176 }
177 
178 data::ResultCode::Numeric ManagedSecondary::install(const std::string &target_name) {
179  (void)target_name;
180  return data::ResultCode::Numeric::kOk;
181 }
182 
183 Uptane::Manifest ManagedSecondary::getManifest() const {
184  Uptane::InstalledImageInfo firmware_info;
185  if (!getFirmwareInfo(firmware_info)) {
186  return Json::Value(Json::nullValue);
187  }
188 
189  Json::Value manifest = Uptane::ManifestIssuer::assembleManifest(firmware_info, getSerial());
190  // consider updating Uptane::ManifestIssuer functionality to fulfill the given use-case
191  // and removing the following code from here so we encapsulate manifest generation
192  // and signing functionality in one place
193  manifest["attacks_detected"] = detected_attack;
194 
195  Json::Value signed_ecu_version;
196 
197  std::string b64sig = Utils::toBase64(Crypto::RSAPSSSign(nullptr, private_key, Utils::jsonToCanonicalStr(manifest)));
198  Json::Value signature;
199  signature["method"] = "rsassa-pss";
200  signature["sig"] = b64sig;
201 
202  signature["keyid"] = public_key_.KeyId();
203  signed_ecu_version["signed"] = manifest;
204  signed_ecu_version["signatures"] = Json::Value(Json::arrayValue);
205  signed_ecu_version["signatures"].append(signature);
206 
207  return signed_ecu_version;
208 }
209 
210 void ManagedSecondary::storeKeys(const std::string &pub_key, const std::string &priv_key) {
211  Utils::writeFile((sconfig.full_client_dir / sconfig.ecu_private_key), priv_key);
212  Utils::writeFile((sconfig.full_client_dir / sconfig.ecu_public_key), pub_key);
213 }
214 
215 bool ManagedSecondary::loadKeys(std::string *pub_key, std::string *priv_key) {
216  boost::filesystem::path public_key_path = sconfig.full_client_dir / sconfig.ecu_public_key;
217  boost::filesystem::path private_key_path = sconfig.full_client_dir / sconfig.ecu_private_key;
218 
219  if (!boost::filesystem::exists(public_key_path) || !boost::filesystem::exists(private_key_path)) {
220  return false;
221  }
222 
223  *priv_key = Utils::readFile(private_key_path.string());
224  *pub_key = Utils::readFile(public_key_path.string());
225  return true;
226 }
227 
228 } // namespace Primary
data
General data structures.
Definition: types.cc:44
Uptane::Snapshot
Definition: tuf.h:502
Uptane::RawMetaPack
Definition: tuf.h:532
Uptane::InstalledImageInfo
Definition: tuf.h:132
Uptane::Targets
Definition: tuf.h:437
Primary::ManagedSecondaryConfig
Definition: managedsecondary.h:17
PublicKey
Definition: crypto.h:26
result
Results of libaktualizr API calls.
Definition: results.h:13
data::ResultCode::Numeric
Numeric
Definition: types.h:125
Uptane::Root
Definition: tuf.h:382
Uptane::TimestampMeta
Definition: tuf.h:485
Uptane::Manifest
Definition: manifest.h:13