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