Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
secondary_rpc_test.cc
1 #include <gtest/gtest.h>
2 
3 #include <thread>
4 
5 #include <netinet/tcp.h>
6 
7 #include "ipuptanesecondary.h"
8 #include "logging/logging.h"
9 #include "msg_handler.h"
10 #include "package_manager/packagemanagerfactory.h"
11 #include "package_manager/packagemanagerinterface.h"
12 #include "secondary_tcp_server.h"
13 #include "storage/invstorage.h"
14 #include "test_utils.h"
15 
16 enum class HandlerVersion { kV1, kV2, kV2Failure };
17 
18 /* This class allows us to divert messages from the regular handlers in
19  * AktualizrSecondary to our own test functions. This lets us test only what was
20  * received by the Secondary but not how it was processed.
21  *
22  * It also has handlers for both the old/v1 and new/v2 versions of the RPC
23  * protocol, so this is how we prove that the Primary is still
24  * backwards-compatible with older/v1 Secondaries. */
25 class SecondaryMock : public MsgDispatcher {
26  public:
27  SecondaryMock(const Uptane::EcuSerial& serial, const Uptane::HardwareIdentifier& hdw_id, const PublicKey& pub_key,
28  const Uptane::Manifest& manifest, HandlerVersion handler_version = HandlerVersion::kV2)
29  : serial_(serial),
30  hdw_id_(hdw_id),
31  pub_key_(pub_key),
32  manifest_(manifest),
33  image_filepath_{image_dir_ / "image.bin"},
34  hasher_{MultiPartHasher::create(Hash::Type::kSha256)},
35  handler_version_(handler_version) {
36  registerHandlers();
37  }
38 
39  public:
40  const std::string verification_failure = "Expected verification test failure";
41  const std::string upload_data_failure = "Expected data upload test failure";
42  const std::string ostree_failure = "Expected OSTree download test failure";
43  const std::string installation_failure = "Expected installation test failure";
44 
45  const Uptane::EcuSerial& serial() const { return serial_; }
46  const Uptane::HardwareIdentifier& hwID() const { return hdw_id_; }
47  const PublicKey& publicKey() const { return pub_key_; }
48  const Uptane::Manifest& manifest() const { return manifest_; }
49  const Uptane::MetaBundle& metadata() const { return meta_bundle_; }
50  HandlerVersion handlerVersion() const { return handler_version_; }
51  void setHandlerVersion(HandlerVersion handler_version_in) { handler_version_ = handler_version_in; }
52  void registerHandlers() {
53  registerBaseHandlers();
54  if (handler_version_ == HandlerVersion::kV1) {
55  registerV1Handlers();
56  } else if (handler_version_ == HandlerVersion::kV2) {
57  registerV2Handlers();
58  } else {
59  registerV2FailureHandlers();
60  }
61  }
62 
63  void resetImageHash() const { hasher_->reset(); }
64  Hash getReceivedImageHash() const { return hasher_->getHash(); }
65  size_t getReceivedImageSize() const { return boost::filesystem::file_size(image_filepath_); }
66 
67  const std::string& getReceivedTlsCreds() const { return tls_creds_; }
68 
69  // Used by both protocol versions:
70  void registerBaseHandlers() {
71  registerHandler(AKIpUptaneMes_PR_getInfoReq,
72  std::bind(&SecondaryMock::getInfoHdlr, this, std::placeholders::_1, std::placeholders::_2));
73 
74  registerHandler(AKIpUptaneMes_PR_versionReq,
75  std::bind(&SecondaryMock::versionHdlr, this, std::placeholders::_1, std::placeholders::_2));
76 
77  registerHandler(AKIpUptaneMes_PR_manifestReq,
78  std::bind(&SecondaryMock::getManifestHdlr, this, std::placeholders::_1, std::placeholders::_2));
79  }
80 
81  // Used by protocol v1 (deprecated, no longer implemented in production code) only:
82  void registerV1Handlers() {
83  registerHandler(AKIpUptaneMes_PR_putMetaReq,
84  std::bind(&SecondaryMock::putMetaHdlr, this, std::placeholders::_1, std::placeholders::_2));
85 
86  registerHandler(AKIpUptaneMes_PR_sendFirmwareReq,
87  std::bind(&SecondaryMock::sendFirmwareHdlr, this, std::placeholders::_1, std::placeholders::_2));
88 
89  registerHandler(AKIpUptaneMes_PR_installReq,
90  std::bind(&SecondaryMock::installHdlr, this, std::placeholders::_1, std::placeholders::_2));
91  }
92 
93  // Used by protocol v2 (based on current aktualizr-secondary implementation) only:
94  void registerV2Handlers() {
95  registerHandler(AKIpUptaneMes_PR_putMetaReq2,
96  std::bind(&SecondaryMock::putMeta2Hdlr, this, std::placeholders::_1, std::placeholders::_2));
97 
98  registerHandler(AKIpUptaneMes_PR_uploadDataReq,
99  std::bind(&SecondaryMock::uploadDataHdlr, this, std::placeholders::_1, std::placeholders::_2));
100 
101  registerHandler(AKIpUptaneMes_PR_downloadOstreeRevReq,
102  std::bind(&SecondaryMock::downloadOstreeRev, this, std::placeholders::_1, std::placeholders::_2));
103 
104  registerHandler(AKIpUptaneMes_PR_installReq,
105  std::bind(&SecondaryMock::install2Hdlr, this, std::placeholders::_1, std::placeholders::_2));
106  }
107 
108  // Procotol v2 handlers that fail in predictable ways.
109  void registerV2FailureHandlers() {
110  registerHandler(AKIpUptaneMes_PR_putMetaReq2,
111  std::bind(&SecondaryMock::putMeta2FailureHdlr, this, std::placeholders::_1, std::placeholders::_2));
112 
113  registerHandler(AKIpUptaneMes_PR_uploadDataReq, std::bind(&SecondaryMock::uploadDataFailureHdlr, this,
114  std::placeholders::_1, std::placeholders::_2));
115 
116  registerHandler(AKIpUptaneMes_PR_downloadOstreeRevReq, std::bind(&SecondaryMock::downloadOstreeRevFailure, this,
117  std::placeholders::_1, std::placeholders::_2));
118 
119  registerHandler(AKIpUptaneMes_PR_installReq,
120  std::bind(&SecondaryMock::install2FailureHdlr, this, std::placeholders::_1, std::placeholders::_2));
121  }
122 
123  private:
124  MsgHandler::ReturnCode getInfoHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
125  (void)in_msg;
126 
127  out_msg.present(AKIpUptaneMes_PR_getInfoResp);
128  auto info_resp = out_msg.getInfoResp();
129 
130  SetString(&info_resp->ecuSerial, serial_.ToString());
131  SetString(&info_resp->hwId, hdw_id_.ToString());
132  info_resp->keyType = static_cast<AKIpUptaneKeyType_t>(pub_key_.Type());
133  SetString(&info_resp->key, pub_key_.Value());
134 
135  return ReturnCode::kOk;
136  }
137 
138  MsgHandler::ReturnCode versionHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
139  (void)in_msg;
140 
141  out_msg.present(AKIpUptaneMes_PR_versionResp);
142  auto version_resp = out_msg.versionResp();
143 
144  if (handler_version_ == HandlerVersion::kV1) {
145  version_resp->version = 1;
146  } else {
147  version_resp->version = 2;
148  }
149 
150  return ReturnCode::kOk;
151  }
152 
153  MsgHandler::ReturnCode getManifestHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
154  (void)in_msg;
155 
156  out_msg.present(AKIpUptaneMes_PR_manifestResp);
157  auto manifest_resp = out_msg.manifestResp();
158  manifest_resp->manifest.present = manifest_PR_json;
159  SetString(&manifest_resp->manifest.choice.json, Utils::jsonToStr(manifest()));
160 
161  return ReturnCode::kOk;
162  }
163 
164  // This is basically the old implemention from AktualizrSecondary.
165  MsgHandler::ReturnCode putMetaHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
166  auto md = in_msg.putMetaReq();
167 
168  meta_bundle_.emplace(std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Root()),
169  ToString(md->director.choice.json.root));
170  meta_bundle_.emplace(std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Targets()),
171  ToString(md->director.choice.json.targets));
172 
173  meta_bundle_.emplace(std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Root()),
174  ToString(md->image.choice.json.root));
175  meta_bundle_.emplace(std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Timestamp()),
176  ToString(md->image.choice.json.timestamp));
177  meta_bundle_.emplace(std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Snapshot()),
178  ToString(md->image.choice.json.snapshot));
179  meta_bundle_.emplace(std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Targets()),
180  ToString(md->image.choice.json.targets));
181 
182  out_msg.present(AKIpUptaneMes_PR_putMetaResp).putMetaResp()->result = AKInstallationResult_success;
183 
184  return ReturnCode::kOk;
185  }
186 
187  // This is annoyingly similar to AktualizrSecondary::putMetaHdlr().
188  MsgHandler::ReturnCode putMeta2Hdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
189  auto md = in_msg.putMetaReq2();
190  Uptane::MetaBundle meta_bundle;
191 
192  EXPECT_EQ(md->directorRepo.present, directorRepo_PR_collection);
193  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
194  const int director_meta_count = md->directorRepo.choice.collection.list.count;
195  EXPECT_EQ(director_meta_count, 2);
196  for (int i = 0; i < director_meta_count; i++) {
197  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-union-access)
198  const AKMetaJson_t object = *md->directorRepo.choice.collection.list.array[i];
199  const std::string role = ToString(object.role);
200  std::string json = ToString(object.json);
201  if (role == Uptane::Role::ROOT) {
202  meta_bundle.emplace(std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Root()), std::move(json));
203  } else if (role == Uptane::Role::TARGETS) {
204  meta_bundle.emplace(std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Targets()),
205  std::move(json));
206  }
207  }
208 
209  EXPECT_EQ(md->imageRepo.present, imageRepo_PR_collection);
210  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
211  const int image_meta_count = md->imageRepo.choice.collection.list.count;
212  EXPECT_EQ(image_meta_count, 4);
213  for (int i = 0; i < image_meta_count; i++) {
214  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-union-access)
215  const AKMetaJson_t object = *md->imageRepo.choice.collection.list.array[i];
216  const std::string role = ToString(object.role);
217  std::string json = ToString(object.json);
218  if (role == Uptane::Role::ROOT) {
219  meta_bundle.emplace(std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Root()), std::move(json));
220  } else if (role == Uptane::Role::TIMESTAMP) {
221  meta_bundle.emplace(std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Timestamp()),
222  std::move(json));
223  } else if (role == Uptane::Role::SNAPSHOT) {
224  meta_bundle.emplace(std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Snapshot()), std::move(json));
225  } else if (role == Uptane::Role::TARGETS) {
226  meta_bundle.emplace(std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Targets()), std::move(json));
227  }
228  }
229 
230  data::InstallationResult result = putMetadata2(meta_bundle);
231 
232  auto m = out_msg.present(AKIpUptaneMes_PR_putMetaResp2).putMetaResp2();
233  m->result = static_cast<AKInstallationResultCode_t>(result.result_code.num_code);
234  SetString(&m->description, result.description);
235 
236  return ReturnCode::kOk;
237  }
238 
239  MsgHandler::ReturnCode putMeta2FailureHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
240  (void)in_msg;
241 
242  auto m = out_msg.present(AKIpUptaneMes_PR_putMetaResp2).putMetaResp2();
243  m->result = static_cast<AKInstallationResultCode_t>(data::ResultCode::Numeric::kVerificationFailed);
244  SetString(&m->description, verification_failure);
245 
246  return ReturnCode::kOk;
247  }
248 
249  MsgHandler::ReturnCode installHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
250  auto result = install(ToString(in_msg.installReq()->hash)).result_code.num_code;
251 
252  out_msg.present(AKIpUptaneMes_PR_installResp).installResp()->result =
253  static_cast<AKInstallationResultCode_t>(result);
254 
255  if (data::ResultCode::Numeric::kNeedCompletion == result) {
256  return ReturnCode::kRebootRequired;
257  }
258 
259  return ReturnCode::kOk;
260  }
261 
262  MsgHandler::ReturnCode install2Hdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
263  auto result = install(ToString(in_msg.installReq()->hash));
264 
265  auto m = out_msg.present(AKIpUptaneMes_PR_installResp2).installResp2();
266  m->result = static_cast<AKInstallationResultCode_t>(result.result_code.num_code);
267  SetString(&m->description, result.description);
268 
269  if (data::ResultCode::Numeric::kNeedCompletion == result.result_code.num_code) {
270  return ReturnCode::kRebootRequired;
271  }
272 
273  return ReturnCode::kOk;
274  }
275 
276  MsgHandler::ReturnCode install2FailureHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
277  (void)in_msg;
278 
279  auto m = out_msg.present(AKIpUptaneMes_PR_installResp2).installResp2();
280  m->result = static_cast<AKInstallationResultCode_t>(data::ResultCode::Numeric::kInstallFailed);
281  SetString(&m->description, installation_failure);
282 
283  return ReturnCode::kOk;
284  }
285 
286  MsgHandler::ReturnCode uploadDataHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
287  if (in_msg.uploadDataReq()->data.size < 0) {
288  out_msg.present(AKIpUptaneMes_PR_uploadDataResp).uploadDataResp()->result = AKInstallationResult_failure;
289  return ReturnCode::kOk;
290  }
291 
292  size_t data_size = static_cast<size_t>(in_msg.uploadDataReq()->data.size);
293  auto result = receiveImageData(in_msg.uploadDataReq()->data.buf, data_size);
294 
295  auto m = out_msg.present(AKIpUptaneMes_PR_uploadDataResp).uploadDataResp();
296  m->result = static_cast<AKInstallationResultCode_t>(result.result_code.num_code);
297  SetString(&m->description, result.description);
298 
299  return ReturnCode::kOk;
300  }
301 
302  MsgHandler::ReturnCode uploadDataFailureHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
303  (void)in_msg;
304 
305  auto m = out_msg.present(AKIpUptaneMes_PR_uploadDataResp).uploadDataResp();
306  m->result = static_cast<AKInstallationResultCode_t>(data::ResultCode::Numeric::kDownloadFailed);
307  SetString(&m->description, upload_data_failure);
308 
309  return ReturnCode::kOk;
310  }
311 
312  MsgHandler::ReturnCode sendFirmwareHdlr(Asn1Message& in_msg, Asn1Message& out_msg) {
313  received_firmware_data_ = ToString(in_msg.sendFirmwareReq()->firmware);
314  out_msg.present(AKIpUptaneMes_PR_sendFirmwareResp).sendFirmwareResp()->result = AKInstallationResult_success;
315 
316  return ReturnCode::kOk;
317  }
318 
319  MsgHandler::ReturnCode downloadOstreeRev(Asn1Message& in_msg, Asn1Message& out_msg) {
320  tls_creds_ = ToString(in_msg.downloadOstreeRevReq()->tlsCred);
321  auto m = out_msg.present(AKIpUptaneMes_PR_downloadOstreeRevResp).downloadOstreeRevResp();
322  m->result = static_cast<AKInstallationResultCode_t>(data::ResultCode::Numeric::kOk);
323  SetString(&m->description, "");
324 
325  return ReturnCode::kOk;
326  }
327 
328  MsgHandler::ReturnCode downloadOstreeRevFailure(Asn1Message& in_msg, Asn1Message& out_msg) {
329  (void)in_msg;
330 
331  auto m = out_msg.present(AKIpUptaneMes_PR_downloadOstreeRevResp).downloadOstreeRevResp();
332  m->result = static_cast<AKInstallationResultCode_t>(data::ResultCode::Numeric::kDownloadFailed);
333  SetString(&m->description, ostree_failure);
334 
335  return ReturnCode::kOk;
336  }
337 
338  data::InstallationResult putMetadata2(const Uptane::MetaBundle& meta_bundle) {
339  meta_bundle_ = meta_bundle;
340  return data::InstallationResult(data::ResultCode::Numeric::kOk, "");
341  }
342 
343  data::InstallationResult receiveImageData(const uint8_t* data, size_t size) {
344  std::ofstream target_file(image_filepath_.c_str(), std::ofstream::out | std::ofstream::binary | std::ofstream::app);
345 
346  target_file.write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(size));
347  hasher_->update(data, size);
348 
349  target_file.close();
350  return data::InstallationResult(data::ResultCode::Numeric::kOk, "");
351  }
352 
353  data::InstallationResult install(const std::string& target_name) {
354  if (!received_firmware_data_.empty()) {
355  // data was received via the old request (sendFirmware)
356  if (target_name == "OSTREE") {
357  tls_creds_ = received_firmware_data_;
358  } else if (handler_version_ == HandlerVersion::kV1) {
359  // v2 calls this directly in uploadDataHdlr.
360  receiveImageData(reinterpret_cast<const uint8_t*>(received_firmware_data_.c_str()),
361  received_firmware_data_.size());
362  }
363  }
364  return data::InstallationResult(data::ResultCode::Numeric::kOk, "");
365  }
366 
367  private:
368  const Uptane::EcuSerial serial_;
369  const Uptane::HardwareIdentifier hdw_id_;
370  const PublicKey pub_key_;
371  const Uptane::Manifest manifest_;
372 
373  Uptane::MetaBundle meta_bundle_;
374 
375  TemporaryDirectory image_dir_;
376  boost::filesystem::path image_filepath_;
377  std::shared_ptr<MultiPartHasher> hasher_;
378  std::unordered_map<unsigned int, Handler> handler_map_;
379  std::string tls_creds_;
380  std::string received_firmware_data_;
381  HandlerVersion handler_version_;
382 };
383 
384 class TargetFile {
385  public:
386  TargetFile(const std::string filename, size_t size = 1024, Hash::Type hash_type = Hash::Type::kSha256)
387  : image_size_{size},
388  image_filepath_{target_dir_ / filename},
389  image_hash_{generateRandomFile(image_filepath_, size, hash_type)} {}
390 
391  std::string path() const { return image_filepath_.string(); }
392  const Hash& hash() const { return image_hash_; }
393  const size_t& size() const { return image_size_; }
394 
395  static Hash generateRandomFile(const boost::filesystem::path& filepath, size_t size, Hash::Type hash_type) {
396  auto hasher = MultiPartHasher::create(hash_type);
397  std::ofstream file{filepath.string(), std::ofstream::binary};
398 
399  if (!file.is_open() || !file.good()) {
400  throw std::runtime_error("Failed to create a file: " + filepath.string());
401  }
402 
403  const unsigned char symbols[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv";
404  unsigned char cur_symbol;
405 
406  for (unsigned int ii = 0; ii < size; ++ii) {
407  cur_symbol = symbols[static_cast<unsigned int>(rand()) % sizeof(symbols)];
408  file.put(static_cast<char>(cur_symbol));
409  hasher->update(&cur_symbol, sizeof(cur_symbol));
410  }
411 
412  file.close();
413  return hasher->getHash();
414  }
415 
416  Uptane::Target createTarget(std::shared_ptr<PackageManagerInterface>& package_manager_) {
417  Json::Value target_json;
418  target_json["custom"]["targetFormat"] = "BINARY";
419  target_json["hashes"]["sha256"] = hash().HashString();
420  target_json["length"] = size();
421  Uptane::Target target = Uptane::Target(path(), target_json);
422 
423  auto fhandle = package_manager_->createTargetFile(target);
424  const std::string content = Utils::readFile(path());
425  fhandle.write(const_cast<char*>(content.c_str()), static_cast<std::streamsize>(size()));
426  fhandle.close();
427 
428  return target;
429  }
430 
431  private:
432  TemporaryDirectory target_dir_;
433  const size_t image_size_;
434  boost::filesystem::path image_filepath_;
435  Hash image_hash_;
436 };
437 
438 class SecondaryRpcCommon : public ::testing::Test {
439  public:
440  const std::string ca_ = "ca";
441  const std::string cert_ = "cert";
442  const std::string pkey_ = "pkey";
443  const std::string server_ = "ostree-server";
444  const std::string director_root_ = "director-root";
445  const std::string director_targets_ = "director-targets";
446  const std::string image_root_ = "image-root";
447  const std::string image_timestamp_ = "image-timestamp";
448  const std::string image_snapshot_ = "image-snapshot";
449  const std::string image_targets_ = "image-targets";
450 
451  protected:
452  SecondaryRpcCommon(size_t image_size, HandlerVersion handler_version)
453  : secondary_{Uptane::EcuSerial("serial"), Uptane::HardwareIdentifier("hardware-id"),
454  PublicKey("pub-key", KeyType::kED25519), Uptane::Manifest(), handler_version},
455  secondary_server_{secondary_, "", 0},
456  secondary_server_thread_{std::bind(&SecondaryRpcCommon::runSecondaryServer, this)},
457  image_file_{"mytarget_image.img", image_size} {
458  secondary_server_.wait_until_running();
459  ip_secondary_ = Uptane::IpUptaneSecondary::connectAndCreate("localhost", secondary_server_.port());
460 
461  config_.pacman.ostree_server = server_;
462  config_.pacman.type = PACKAGE_MANAGER_NONE;
463  config_.pacman.images_path = temp_dir_.Path() / "images";
464  config_.storage.path = temp_dir_.Path();
465 
466  storage_ = INvStorage::newStorage(config_.storage);
467  storage_->storeTlsCreds(ca_, cert_, pkey_);
468  storage_->storeRoot(director_root_, Uptane::RepositoryType::Director(), Uptane::Version(1));
469  storage_->storeNonRoot(director_targets_, Uptane::RepositoryType::Director(), Uptane::Role::Targets());
470  storage_->storeRoot(image_root_, Uptane::RepositoryType::Image(), Uptane::Version(1));
471  storage_->storeNonRoot(image_timestamp_, Uptane::RepositoryType::Image(), Uptane::Role::Timestamp());
472  storage_->storeNonRoot(image_snapshot_, Uptane::RepositoryType::Image(), Uptane::Role::Snapshot());
473  storage_->storeNonRoot(image_targets_, Uptane::RepositoryType::Image(), Uptane::Role::Targets());
474 
475  package_manager_ = PackageManagerFactory::makePackageManager(config_.pacman, config_.bootloader, storage_, nullptr);
476  secondary_provider_ = std::make_shared<SecondaryProvider>(config_, storage_, package_manager_);
477  ip_secondary_->init(secondary_provider_);
478  }
479 
480  ~SecondaryRpcCommon() {
481  secondary_server_.stop();
482  secondary_server_thread_.join();
483  }
484 
485  void runSecondaryServer() { secondary_server_.run(); }
486 
487  void resetHandlers(HandlerVersion handler_version) {
488  secondary_.setHandlerVersion(handler_version);
489  secondary_.registerHandlers();
490  }
491 
492  void verifyMetadata(const Uptane::MetaBundle& meta_bundle) {
493  EXPECT_EQ(Uptane::getMetaFromBundle(meta_bundle, Uptane::RepositoryType::Director(), Uptane::Role::Root()),
494  director_root_);
495  EXPECT_EQ(Uptane::getMetaFromBundle(meta_bundle, Uptane::RepositoryType::Director(), Uptane::Role::Targets()),
496  director_targets_);
497  EXPECT_EQ(Uptane::getMetaFromBundle(meta_bundle, Uptane::RepositoryType::Image(), Uptane::Role::Root()),
498  image_root_);
499  EXPECT_EQ(Uptane::getMetaFromBundle(meta_bundle, Uptane::RepositoryType::Image(), Uptane::Role::Timestamp()),
500  image_timestamp_);
501  EXPECT_EQ(Uptane::getMetaFromBundle(meta_bundle, Uptane::RepositoryType::Image(), Uptane::Role::Snapshot()),
502  image_snapshot_);
503  EXPECT_EQ(Uptane::getMetaFromBundle(meta_bundle, Uptane::RepositoryType::Image(), Uptane::Role::Targets()),
504  image_targets_);
505  }
506 
507  void sendAndInstallBinaryImage() {
508  Uptane::Target target = image_file_.createTarget(package_manager_);
509  const HandlerVersion handler_version = secondary_.handlerVersion();
510 
511  data::InstallationResult result = ip_secondary_->putMetadata(target);
512  if (handler_version == HandlerVersion::kV2Failure) {
513  EXPECT_EQ(result.result_code, data::ResultCode::Numeric::kVerificationFailed);
514  EXPECT_EQ(result.description, secondary_.verification_failure);
515  } else {
516  EXPECT_TRUE(result.isSuccess());
517  verifyMetadata(secondary_.metadata());
518  }
519 
520  result = ip_secondary_->sendFirmware(target);
521  if (handler_version == HandlerVersion::kV2Failure) {
522  EXPECT_EQ(result.result_code, data::ResultCode::Numeric::kDownloadFailed);
523  EXPECT_EQ(result.description, secondary_.upload_data_failure);
524  } else {
525  EXPECT_TRUE(result.isSuccess());
526  }
527 
528  result = ip_secondary_->install(target);
529  if (handler_version == HandlerVersion::kV2Failure) {
530  EXPECT_EQ(result.result_code, data::ResultCode::Numeric::kInstallFailed);
531  EXPECT_EQ(result.description, secondary_.installation_failure);
532  } else {
533  EXPECT_TRUE(result.isSuccess());
534  EXPECT_EQ(image_file_.hash(), secondary_.getReceivedImageHash());
535  }
536  }
537 
538  void installOstreeRev() {
539  Json::Value target_json;
540  target_json["custom"]["targetFormat"] = "OSTREE";
541  Uptane::Target target = Uptane::Target("OSTREE", target_json);
542 
543  const HandlerVersion handler_version = secondary_.handlerVersion();
544 
545  data::InstallationResult result = ip_secondary_->putMetadata(target);
546  if (handler_version == HandlerVersion::kV2Failure) {
547  EXPECT_EQ(result.result_code, data::ResultCode::Numeric::kVerificationFailed);
548  EXPECT_EQ(result.description, secondary_.verification_failure);
549  } else {
550  EXPECT_TRUE(result.isSuccess());
551  verifyMetadata(secondary_.metadata());
552  }
553 
554  result = ip_secondary_->sendFirmware(target);
555  if (handler_version == HandlerVersion::kV2Failure) {
556  EXPECT_EQ(result.result_code, data::ResultCode::Numeric::kDownloadFailed);
557  EXPECT_EQ(result.description, secondary_.ostree_failure);
558  } else {
559  EXPECT_TRUE(result.isSuccess());
560  }
561 
562  result = ip_secondary_->install(target);
563  if (handler_version == HandlerVersion::kV2Failure) {
564  EXPECT_EQ(result.result_code, data::ResultCode::Numeric::kInstallFailed);
565  EXPECT_EQ(result.description, secondary_.installation_failure);
566  } else {
567  EXPECT_TRUE(result.isSuccess());
568  std::string archive = secondary_.getReceivedTlsCreds();
569  {
570  std::stringstream as(archive);
571  EXPECT_EQ(ca_, Utils::readFileFromArchive(as, "ca.pem"));
572  }
573  {
574  std::stringstream as(archive);
575  EXPECT_EQ(cert_, Utils::readFileFromArchive(as, "client.pem"));
576  }
577  {
578  std::stringstream as(archive);
579  EXPECT_EQ(pkey_, Utils::readFileFromArchive(as, "pkey.pem"));
580  }
581  {
582  std::stringstream as(archive);
583  EXPECT_EQ(server_, Utils::readFileFromArchive(as, "server.url", true));
584  }
585  }
586  }
587 
588  SecondaryMock secondary_;
589  std::shared_ptr<SecondaryProvider> secondary_provider_;
590  SecondaryTcpServer secondary_server_;
591  std::thread secondary_server_thread_;
592  TargetFile image_file_;
593  SecondaryInterface::Ptr ip_secondary_;
594  TemporaryDirectory temp_dir_;
595  std::shared_ptr<INvStorage> storage_;
596  Config config_;
597  std::shared_ptr<PackageManagerInterface> package_manager_;
598 };
599 
601  public ::testing::WithParamInterface<std::pair<size_t, HandlerVersion>> {
602  protected:
603  SecondaryRpcTest() : SecondaryRpcCommon(GetParam().first, GetParam().second) {}
604 };
605 
606 // Test the serialization/deserialization and the TCP/IP communication implementation
607 // that occurs during communication between Primary and IP Secondary
608 TEST_P(SecondaryRpcTest, AllRpcCallsTest) {
609  ASSERT_TRUE(ip_secondary_ != nullptr) << "Failed to create IP Secondary";
610  EXPECT_EQ(ip_secondary_->getSerial(), secondary_.serial());
611  EXPECT_EQ(ip_secondary_->getHwId(), secondary_.hwID());
612  EXPECT_EQ(ip_secondary_->getPublicKey(), secondary_.publicKey());
613  EXPECT_EQ(ip_secondary_->getManifest(), secondary_.manifest());
614 
615  sendAndInstallBinaryImage();
616 
617  installOstreeRev();
618 }
619 
620 /* These tests use a mock of most of the Secondary internals in order to test
621  * the RPC mechanism between the Primary and IP Secondary. The tests cover the
622  * old/v1/fallback handlers as well as the new/v2 versions. */
623 INSTANTIATE_TEST_SUITE_P(
624  SecondaryRpcTestCases, SecondaryRpcTest,
625  ::testing::Values(std::make_pair(1, HandlerVersion::kV2), std::make_pair(1024, HandlerVersion::kV2),
626  std::make_pair(1024 - 1, HandlerVersion::kV2), std::make_pair(1024 + 1, HandlerVersion::kV2),
627  std::make_pair(1024 * 10 + 1, HandlerVersion::kV2), std::make_pair(1, HandlerVersion::kV1),
628  std::make_pair(1024, HandlerVersion::kV1), std::make_pair(1024 - 1, HandlerVersion::kV1),
629  std::make_pair(1024 + 1, HandlerVersion::kV1), std::make_pair(1024 * 10 + 1, HandlerVersion::kV1),
630  std::make_pair(1024, HandlerVersion::kV2Failure)));
631 
633  protected:
634  SecondaryRpcUpgrade() : SecondaryRpcCommon(1024, HandlerVersion::kV1) {}
635 };
636 
637 /* Test upgrade and downgrade of protocol versions after installation, both for
638  * binary and OSTree updates. */
639 TEST_F(SecondaryRpcUpgrade, VersionUpgrade) {
640  ASSERT_TRUE(ip_secondary_ != nullptr) << "Failed to create IP Secondary";
641 
642  sendAndInstallBinaryImage();
643  resetHandlers(HandlerVersion::kV2);
644  secondary_.resetImageHash();
645  sendAndInstallBinaryImage();
646  resetHandlers(HandlerVersion::kV1);
647  secondary_.resetImageHash();
648  sendAndInstallBinaryImage();
649 
650  resetHandlers(HandlerVersion::kV1);
651  installOstreeRev();
652  resetHandlers(HandlerVersion::kV2);
653  installOstreeRev();
654  resetHandlers(HandlerVersion::kV1);
655  installOstreeRev();
656 }
657 
658 TEST(SecondaryTcpServer, TestIpSecondaryIfSecondaryIsNotRunning) {
659  in_port_t secondary_port = TestUtils::getFreePortAsInt();
660  SecondaryInterface::Ptr ip_secondary;
661 
662  // Try to connect to a non-running Secondary and create a corresponding instance on Primary.
663  ip_secondary = Uptane::IpUptaneSecondary::connectAndCreate("localhost", secondary_port);
664  EXPECT_EQ(ip_secondary, nullptr);
665 
666  // Create Secondary on Primary without actually connecting to Secondary.
667  ip_secondary = std::make_shared<Uptane::IpUptaneSecondary>("localhost", secondary_port, Uptane::EcuSerial("serial"),
669  PublicKey("key", KeyType::kED25519));
670 
671  TemporaryDirectory temp_dir;
672  Config config;
673  config.storage.path = temp_dir.Path();
674  config.pacman.type = PACKAGE_MANAGER_NONE;
675  config.pacman.images_path = temp_dir.Path() / "images";
676  std::shared_ptr<INvStorage> storage = INvStorage::newStorage(config.storage);
677  std::shared_ptr<PackageManagerInterface> package_manager =
678  PackageManagerFactory::makePackageManager(config.pacman, config.bootloader, storage, nullptr);
679  std::shared_ptr<SecondaryProvider> secondary_provider =
680  std::make_shared<SecondaryProvider>(config, storage, package_manager);
681  ip_secondary->init(secondary_provider);
682 
683  // Expect nothing since the Secondary is not running.
684  EXPECT_EQ(ip_secondary->getManifest(), Json::Value());
685 
686  TargetFile target_file("mytarget_image.img");
687  Uptane::Target target = target_file.createTarget(package_manager);
688 
689  // Expect failures since the Secondary is not running.
690  EXPECT_FALSE(ip_secondary->putMetadata(target).isSuccess());
691  EXPECT_FALSE(ip_secondary->sendFirmware(target).isSuccess());
692  EXPECT_FALSE(ip_secondary->install(target).isSuccess());
693 }
694 
695 /* This class returns a positive result for every message. The test cases verify
696  * that the implementation can recover from situations where something goes wrong. */
697 class SecondaryRpcTestPositive : public ::testing::Test, public MsgHandler {
698  protected:
700  : secondary_server_{*this, "", 0}, secondary_server_thread_{[&]() { secondary_server_.run(); }} {
701  secondary_server_.wait_until_running();
702  }
703 
705  secondary_server_.stop();
706  secondary_server_thread_.join();
707  }
708 
709  // Override default implementation with a stub that always returns success.
710  ReturnCode handleMsg(const Asn1Message::Ptr& in_msg, Asn1Message::Ptr& out_msg) override {
711  (void)in_msg;
712  out_msg->present(AKIpUptaneMes_PR_installResp).installResp()->result = AKInstallationResultCode_ok;
713  return ReturnCode::kOk;
714  }
715 
716  AKIpUptaneMes_PR sendInstallMsg() {
717  // compose and send a valid message
718  Asn1Message::Ptr req(Asn1Message::Empty());
719  req->present(AKIpUptaneMes_PR_installReq);
720 
721  // prepare request message
722  auto req_mes = req->installReq();
723  SetString(&req_mes->hash, "target_name");
724  // send request and receive response, a request-response type of RPC
725  std::pair<std::string, uint16_t> secondary_server_addr{"127.0.0.1", secondary_server_.port()};
726  auto resp = Asn1Rpc(req, secondary_server_addr);
727 
728  return resp->present();
729  }
730 
731  protected:
732  SecondaryTcpServer secondary_server_;
733  std::thread secondary_server_thread_;
734 };
735 
736 /* This test fails because the Secondary TCP server implementation is a
737  * single-threaded, synchronous and blocking hence it cannot accept any new
738  * connections until the current one is closed. Therefore, if a client/Primary
739  * does not close its socket for some reason then Secondary becomes
740  * "unavailable" */
741 // TEST_F(SecondaryRpcTestPositive, primaryNotClosingSocket) {
742 // ConnectionSocket con_sock{"127.0.0.1", secondary_server_.port()};
743 // con_sock.connect();
744 // ASSERT_EQ(sendInstallMsg(), AKIpUptaneMes_PR_installResp);
745 //}
746 
747 TEST_F(SecondaryRpcTestPositive, primaryConnectAndDisconnect) {
748  ConnectionSocket{"127.0.0.1", secondary_server_.port()}.connect();
749  // do a valid request/response exchange to verify if Secondary works as expected
750  // after accepting and closing a new connection
751  ASSERT_EQ(sendInstallMsg(), AKIpUptaneMes_PR_installResp);
752 }
753 
754 TEST_F(SecondaryRpcTestPositive, primaryConnectAndSendValidButNotSupportedMsg) {
755  {
756  ConnectionSocket con_sock{"127.0.0.1", secondary_server_.port()};
757  con_sock.connect();
758  uint8_t garbage[] = {0x30, 0x13, 0x02, 0x01, 0x05, 0x16, 0x0e, 0x41, 0x6e, 0x79, 0x62,
759  0x6f, 0x64, 0x79, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x3f};
760  send(*con_sock, garbage, sizeof(garbage), 0);
761  }
762  // do a valid request/response exchange to verify if Secondary works as expected
763  // after receiving not-supported message
764  ASSERT_EQ(sendInstallMsg(), AKIpUptaneMes_PR_installResp);
765 }
766 
767 TEST_F(SecondaryRpcTestPositive, primaryConnectAndSendBrokenAsn1Msg) {
768  {
769  ConnectionSocket con_sock{"127.0.0.1", secondary_server_.port()};
770  con_sock.connect();
771  uint8_t garbage[] = {0x30, 0x99, 0x02, 0x01, 0x05, 0x16, 0x0e, 0x41, 0x6e, 0x79,
772  0x62, 0x6f, 0x64, 0x79, 0x20, 0x74, 0x68, 0x72, 0x65, 0x3f};
773  send(*con_sock, garbage, sizeof(garbage), 0);
774  }
775  // do a valid request/response exchange to verify if Secondary works as expected
776  // after receiving broken ASN1 message
777  ASSERT_EQ(sendInstallMsg(), AKIpUptaneMes_PR_installResp);
778 }
779 
780 TEST_F(SecondaryRpcTestPositive, primaryConnectAndSendGarbage) {
781  {
782  ConnectionSocket con_sock{"127.0.0.1", secondary_server_.port()};
783  con_sock.connect();
784  uint8_t garbage[] = "some garbage message";
785  send(*con_sock, garbage, sizeof(garbage), 0);
786  }
787  // do a valid request/response exchange to verify if Secondary works as expected
788  // after receiving some garbage
789  ASSERT_EQ(sendInstallMsg(), AKIpUptaneMes_PR_installResp);
790 }
791 
792 int main(int argc, char** argv) {
793  ::testing::InitGoogleTest(&argc, argv);
794 
795  logger_init();
796  logger_set_threshold(boost::log::trivial::debug);
797 
798  return RUN_ALL_TESTS();
799 }
General data structures.
Definition: types.h:215
Metadata version numbers.
Definition: tuf.h:120
static Asn1Message::Ptr Empty()
Create a new Asn1Message, in order to fill it with data and send it.
Definition: asn1_message.h:46
Configuration object for an aktualizr instance running on a Primary ECU.
Definition: config.h:210
Listens on a socket, decodes calls (ASN.1) and forwards them to an Uptane Secondary implementation...
Package installation failed.
Reference counted holder for the top-level ASN1 message structure.
Definition: asn1_message.h:34
The Hash class The hash of a file or Uptane metadata.
Definition: types.h:157
Results of libaktualizr API calls.
Definition: results.h:12