Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
uptane_test.cc
1 #include <gmock/gmock.h>
2 #include <gtest/gtest.h>
3 
4 #include <unistd.h>
5 
6 #include <algorithm>
7 #include <fstream>
8 #include <iostream>
9 #include <memory>
10 #include <string>
11 #include <vector>
12 
13 #include <boost/filesystem.hpp>
14 #include "json/json.h"
15 
16 #include "crypto/p11engine.h"
17 #include "httpfake.h"
18 #include "primary/initializer.h"
19 #include "primary/sotauptaneclient.h"
20 #include "storage/fsstorage_read.h"
21 #include "storage/invstorage.h"
22 #include "test_utils.h"
23 #include "uptane/secondaryinterface.h"
24 #include "uptane/tuf.h"
25 #include "uptane/uptanerepository.h"
26 #include "uptane_test_common.h"
27 #include "utilities/utils.h"
28 
29 #ifdef BUILD_P11
30 #ifndef TEST_PKCS11_MODULE_PATH
31 #define TEST_PKCS11_MODULE_PATH "/usr/local/softhsm/libsofthsm2.so"
32 #endif
33 #endif
34 
35 static Config config_common() {
36  Config config;
37  config.uptane.key_type = KeyType::kED25519;
38  return config;
39 }
40 
41 TEST(Uptane, Verify) {
42  TemporaryDirectory temp_dir;
43  auto http = std::make_shared<HttpFake>(temp_dir.Path());
44  Config config = config_common();
45  config.uptane.director_server = http->tls_server + "/director";
46  config.uptane.repo_server = http->tls_server + "/repo";
47 
48  config.storage.path = temp_dir.Path();
49  auto storage = INvStorage::newStorage(config.storage);
50  HttpResponse response = http->get(http->tls_server + "/director/root.json", HttpInterface::kNoLimit);
51  Uptane::Root root(Uptane::Root::Policy::kAcceptAll);
52  Uptane::Root(Uptane::RepositoryType::Director(), response.getJson(), root);
53 }
54 
55 /* Throw an exception if Root metadata is unsigned. */
56 TEST(Uptane, VerifyDataBad) {
57  TemporaryDirectory temp_dir;
58  auto http = std::make_shared<HttpFake>(temp_dir.Path());
59  Config config = config_common();
60  config.uptane.director_server = http->tls_server + "/director";
61  config.uptane.repo_server = http->tls_server + "/repo";
62 
63  config.storage.path = temp_dir.Path();
64  auto storage = INvStorage::newStorage(config.storage);
65  Json::Value data_json = http->get(http->tls_server + "/director/root.json", HttpInterface::kNoLimit).getJson();
66  data_json.removeMember("signatures");
67 
68  Uptane::Root root(Uptane::Root::Policy::kAcceptAll);
69  EXPECT_THROW(Uptane::Root(Uptane::RepositoryType::Director(), data_json, root), Uptane::UnmetThreshold);
70 }
71 
72 /* Throw an exception if Root metadata has unknown signature types. */
73 TEST(Uptane, VerifyDataUnknownType) {
74  TemporaryDirectory temp_dir;
75  auto http = std::make_shared<HttpFake>(temp_dir.Path());
76  Config config = config_common();
77  config.uptane.director_server = http->tls_server + "/director";
78  config.uptane.repo_server = http->tls_server + "/repo";
79 
80  config.storage.path = temp_dir.Path();
81  auto storage = INvStorage::newStorage(config.storage);
82  Json::Value data_json = http->get(http->tls_server + "/director/root.json", HttpInterface::kNoLimit).getJson();
83  data_json["signatures"][0]["method"] = "badsignature";
84  data_json["signatures"][1]["method"] = "badsignature";
85 
86  Uptane::Root root(Uptane::Root::Policy::kAcceptAll);
87  EXPECT_THROW(Uptane::Root(Uptane::RepositoryType::Director(), data_json, root), Uptane::SecurityException);
88 }
89 
90 /* Throw an exception if Root metadata has invalid key IDs. */
91 TEST(Uptane, VerifyDataBadKeyId) {
92  TemporaryDirectory temp_dir;
93  auto http = std::make_shared<HttpFake>(temp_dir.Path());
94  Config config = config_common();
95  config.uptane.director_server = http->tls_server + "/director";
96  config.uptane.repo_server = http->tls_server + "/repo";
97 
98  config.storage.path = temp_dir.Path();
99  auto storage = INvStorage::newStorage(config.storage);
100  Json::Value data_json = http->get(http->tls_server + "/director/root.json", HttpInterface::kNoLimit).getJson();
101 
102  data_json["signatures"][0]["keyid"] = "badkeyid";
103 
104  Uptane::Root root(Uptane::Root::Policy::kAcceptAll);
105  EXPECT_THROW(Uptane::Root(Uptane::RepositoryType::Director(), data_json, root), Uptane::BadKeyId);
106 }
107 
108 /* Throw an exception if Root metadata signature threshold is invalid. */
109 TEST(Uptane, VerifyDataBadThreshold) {
110  TemporaryDirectory temp_dir;
111  auto http = std::make_shared<HttpFake>(temp_dir.Path());
112  Config config = config_common();
113  config.uptane.director_server = http->tls_server + "/director";
114  config.uptane.repo_server = http->tls_server + "/repo";
115 
116  config.storage.path = temp_dir.Path();
117  auto storage = INvStorage::newStorage(config.storage);
118  Json::Value data_json = http->get(http->tls_server + "/director/root.json", HttpInterface::kNoLimit).getJson();
119  data_json["signed"]["roles"]["root"]["threshold"] = -1;
120  try {
121  Uptane::Root root(Uptane::Root::Policy::kAcceptAll);
122  Uptane::Root(Uptane::RepositoryType::Director(), data_json, root);
123  FAIL() << "Illegal threshold should have thrown an error.";
124  } catch (const Uptane::IllegalThreshold &ex) {
125  } catch (const Uptane::UnmetThreshold &ex) {
126  }
127 }
128 
129 /* Get manifest from Primary.
130  * Get manifest from Secondaries. */
131 TEST(Uptane, AssembleManifestGood) {
132  TemporaryDirectory temp_dir;
133  auto http = std::make_shared<HttpFake>(temp_dir.Path());
134  Config config = config_common();
135  config.storage.path = temp_dir.Path();
136  boost::filesystem::copy_file("tests/test_data/cred.zip", (temp_dir / "cred.zip").string());
137  boost::filesystem::copy_file("tests/test_data/firmware.txt", (temp_dir / "firmware.txt").string());
138  boost::filesystem::copy_file("tests/test_data/firmware_name.txt", (temp_dir / "firmware_name.txt").string());
139  config.provision.provision_path = temp_dir / "cred.zip";
140  config.provision.mode = ProvisionMode::kSharedCred;
141  config.uptane.director_server = http->tls_server + "/director";
142  config.uptane.repo_server = http->tls_server + "/repo";
143  config.provision.primary_ecu_serial = "testecuserial";
144  config.pacman.type = PACKAGE_MANAGER_NONE;
145  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hardware");
146 
147  auto storage = INvStorage::newStorage(config.storage);
148  auto sota_client = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http);
149  EXPECT_NO_THROW(sota_client->initialize());
150 
151  Json::Value manifest = sota_client->AssembleManifest()["ecu_version_manifests"];
152  EXPECT_EQ(manifest.size(), 2);
153  EXPECT_EQ(manifest["testecuserial"]["signed"]["ecu_serial"].asString(), config.provision.primary_ecu_serial);
154  EXPECT_EQ(manifest["secondary_ecu_serial"]["signed"]["ecu_serial"].asString(), "secondary_ecu_serial");
155  // Manifest should not have an installation result yet.
156  EXPECT_FALSE(manifest["testecuserial"]["signed"].isMember("custom"));
157  EXPECT_FALSE(manifest["secondary_ecu_serial"]["signed"].isMember("custom"));
158 
159  std::string counter_str = manifest["testecuserial"]["signed"]["report_counter"].asString();
160  int64_t primary_ecu_report_counter = std::stoll(counter_str);
161  Json::Value manifest2 = sota_client->AssembleManifest()["ecu_version_manifests"];
162  std::string counter_str2 = manifest2["testecuserial"]["signed"]["report_counter"].asString();
163  int64_t primary_ecu_report_counter2 = std::stoll(counter_str2);
164  EXPECT_EQ(primary_ecu_report_counter2, primary_ecu_report_counter + 1);
165 }
166 
167 /* Bad signatures are ignored when assembling the manifest. */
168 TEST(Uptane, AssembleManifestBad) {
169  TemporaryDirectory temp_dir;
170  auto http = std::make_shared<HttpFake>(temp_dir.Path());
171  Config config = config_common();
172  config.storage.path = temp_dir.Path();
173  boost::filesystem::copy_file("tests/test_data/cred.zip", (temp_dir / "cred.zip").string());
174  boost::filesystem::copy_file("tests/test_data/firmware.txt", (temp_dir / "firmware.txt").string());
175  boost::filesystem::copy_file("tests/test_data/firmware_name.txt", (temp_dir / "firmware_name.txt").string());
176  config.provision.provision_path = temp_dir / "cred.zip";
177  config.provision.mode = ProvisionMode::kSharedCred;
178  config.uptane.director_server = http->tls_server + "/director";
179  config.uptane.repo_server = http->tls_server + "/repo";
180  config.provision.primary_ecu_serial = "testecuserial";
181  config.pacman.type = PACKAGE_MANAGER_NONE;
183  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hardware");
184 
185  /* Overwrite the Secondary's keys on disk. */
186  std::string private_key, public_key;
187  ASSERT_TRUE(Crypto::generateKeyPair(ecu_config.key_type, &public_key, &private_key));
188  Utils::writeFile(ecu_config.full_client_dir / ecu_config.ecu_private_key, private_key);
189  public_key = Utils::readFile("tests/test_data/public.key");
190  Utils::writeFile(ecu_config.full_client_dir / ecu_config.ecu_public_key, public_key);
191 
192  auto storage = INvStorage::newStorage(config.storage);
193  auto sota_client = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http);
194  EXPECT_NO_THROW(sota_client->initialize());
195 
196  Json::Value manifest = sota_client->AssembleManifest()["ecu_version_manifests"];
197  EXPECT_EQ(manifest.size(), 1);
198  EXPECT_EQ(manifest["testecuserial"]["signed"]["ecu_serial"].asString(), config.provision.primary_ecu_serial);
199  // Manifest should not have an installation result yet.
200  EXPECT_FALSE(manifest["testecuserial"]["signed"].isMember("custom"));
201  EXPECT_FALSE(manifest["secondary_ecu_serial"]["signed"].isMember("custom"));
202 }
203 
204 /* Get manifest from Primary.
205  * Get manifest from Secondaries.
206  * Send manifest to the server. */
207 TEST(Uptane, PutManifest) {
208  TemporaryDirectory temp_dir;
209  auto http = std::make_shared<HttpFake>(temp_dir.Path());
210  Config config = config_common();
211  config.storage.path = temp_dir.Path();
212  boost::filesystem::copy_file("tests/test_data/cred.zip", (temp_dir / "cred.zip").string());
213  boost::filesystem::copy_file("tests/test_data/firmware.txt", (temp_dir / "firmware.txt").string());
214  boost::filesystem::copy_file("tests/test_data/firmware_name.txt", (temp_dir / "firmware_name.txt").string());
215  config.provision.provision_path = temp_dir / "cred.zip";
216  config.provision.mode = ProvisionMode::kSharedCred;
217  config.uptane.director_server = http->tls_server + "/director";
218  config.uptane.repo_server = http->tls_server + "/repo";
219  config.provision.primary_ecu_serial = "testecuserial";
220  config.pacman.type = PACKAGE_MANAGER_NONE;
221  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hardware");
222 
223  auto storage = INvStorage::newStorage(config.storage);
224 
225  auto sota_client = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http);
226  EXPECT_NO_THROW(sota_client->initialize());
227  EXPECT_TRUE(sota_client->putManifestSimple());
228 
229  Json::Value json = http->last_manifest;
230 
231  EXPECT_EQ(json["signatures"].size(), 1u);
232  EXPECT_EQ(json["signed"]["primary_ecu_serial"].asString(), "testecuserial");
233  EXPECT_EQ(
234  json["signed"]["ecu_version_manifests"]["testecuserial"]["signed"]["installed_image"]["filepath"].asString(),
235  "unknown");
236  EXPECT_EQ(json["signed"]["ecu_version_manifests"].size(), 2u);
237  EXPECT_EQ(json["signed"]["ecu_version_manifests"]["secondary_ecu_serial"]["signed"]["ecu_serial"].asString(),
238  "secondary_ecu_serial");
239  EXPECT_EQ(json["signed"]["ecu_version_manifests"]["secondary_ecu_serial"]["signed"]["installed_image"]["filepath"]
240  .asString(),
241  "test-package");
242 }
243 
244 class HttpPutManifestFail : public HttpFake {
245  public:
246  HttpPutManifestFail(const boost::filesystem::path &test_dir_in, std::string flavor = "")
247  : HttpFake(test_dir_in, flavor) {}
248  HttpResponse put(const std::string &url, const Json::Value &data) override {
249  (void)data;
250  return HttpResponse(url, 504, CURLE_OK, "");
251  }
252 };
253 
254 int num_events_PutManifestError = 0;
255 void process_events_PutManifestError(const std::shared_ptr<event::BaseEvent> &event) {
256  std::cout << event->variant << "\n";
257  if (event->variant == "PutManifestComplete") {
258  EXPECT_FALSE(std::static_pointer_cast<event::PutManifestComplete>(event)->success);
259  num_events_PutManifestError++;
260  }
261 }
262 
263 /*
264  * Send PutManifestComplete event if send is unsuccessful
265  */
266 TEST(Uptane, PutManifestError) {
267  TemporaryDirectory temp_dir;
268  auto http = std::make_shared<HttpPutManifestFail>(temp_dir.Path());
269 
270  Config conf("tests/config/basic.toml");
271  conf.storage.path = temp_dir.Path();
272 
273  auto storage = INvStorage::newStorage(conf.storage);
274  auto events_channel = std::make_shared<event::Channel>();
275  std::function<void(std::shared_ptr<event::BaseEvent> event)> f_cb = process_events_PutManifestError;
276  events_channel->connect(f_cb);
277  num_events_PutManifestError = 0;
278  auto sota_client = std_::make_unique<UptaneTestCommon::TestUptaneClient>(conf, storage, http, events_channel);
279  EXPECT_NO_THROW(sota_client->initialize());
280  auto result = sota_client->putManifest();
281  EXPECT_FALSE(result);
282  EXPECT_EQ(num_events_PutManifestError, 1);
283 }
284 
285 /*
286  * Verify that failing to send the manifest will not prevent checking for
287  * updates (which is the only way to recover if something goes wrong with
288  * sending the manifest).
289  */
290 TEST(Uptane, FetchMetaFail) {
291  TemporaryDirectory temp_dir;
292  auto http = std::make_shared<HttpPutManifestFail>(temp_dir.Path(), "noupdates");
293 
294  Config conf("tests/config/basic.toml");
295  conf.provision.primary_ecu_serial = "CA:FE:A6:D2:84:9D";
296  conf.provision.primary_ecu_hardware_id = "primary_hw";
297  conf.uptane.director_server = http->tls_server + "/director";
298  conf.uptane.repo_server = http->tls_server + "/repo";
299  conf.storage.path = temp_dir.Path();
300  conf.tls.server = http->tls_server;
301 
302  auto storage = INvStorage::newStorage(conf.storage);
303  auto up = std_::make_unique<UptaneTestCommon::TestUptaneClient>(conf, storage, http);
304 
305  EXPECT_NO_THROW(up->initialize());
306  result::UpdateCheck result = up->fetchMeta();
307  EXPECT_EQ(result.status, result::UpdateStatus::kNoUpdatesAvailable);
308 }
309 
310 unsigned int num_events_InstallTarget = 0;
311 unsigned int num_events_AllInstalls = 0;
312 void process_events_Install(const std::shared_ptr<event::BaseEvent> &event) {
313  if (event->variant == "InstallTargetComplete") {
314  auto concrete_event = std::static_pointer_cast<event::InstallTargetComplete>(event);
315  if (num_events_InstallTarget <= 1) {
316  EXPECT_TRUE(concrete_event->success);
317  } else {
318  EXPECT_FALSE(concrete_event->success);
319  }
320  num_events_InstallTarget++;
321  }
322  if (event->variant == "AllInstallsComplete") {
323  auto concrete_event = std::static_pointer_cast<event::AllInstallsComplete>(event);
324  if (num_events_AllInstalls == 0) {
325  EXPECT_TRUE(concrete_event->result.dev_report.isSuccess());
326  } else if (num_events_AllInstalls == 1) {
327  EXPECT_FALSE(concrete_event->result.dev_report.isSuccess());
328  EXPECT_EQ(concrete_event->result.dev_report.result_code, data::ResultCode::Numeric::kAlreadyProcessed);
329  } else {
330  EXPECT_FALSE(concrete_event->result.dev_report.isSuccess());
331  EXPECT_EQ(concrete_event->result.dev_report.result_code, data::ResultCode::Numeric::kInternalError);
332  }
333  num_events_AllInstalls++;
334  }
335 }
336 
337 /*
338  * Verify successful installation of a package.
339  *
340  * Identify ECU for each target.
341  * Check if there are updates to install for the Primary.
342  * Install a binary update on the Primary.
343  * Store installation result for Primary.
344  * Store installation result for device.
345  * Check if an update is already installed.
346  * Reject an update that matches the currently installed version's filename but
347  * not the length and/or hashes.
348  */
349 TEST(Uptane, InstallFakeGood) {
350  Config conf("tests/config/basic.toml");
351  TemporaryDirectory temp_dir;
352  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates");
353  conf.uptane.director_server = http->tls_server + "director";
354  conf.uptane.repo_server = http->tls_server + "repo";
355  conf.pacman.type = PACKAGE_MANAGER_NONE;
356  conf.provision.primary_ecu_serial = "CA:FE:A6:D2:84:9D";
357  conf.provision.primary_ecu_hardware_id = "primary_hw";
358  conf.storage.path = temp_dir.Path();
359  conf.tls.server = http->tls_server;
360  UptaneTestCommon::addDefaultSecondary(conf, temp_dir, "secondary_ecu_serial", "secondary_hw");
361  conf.postUpdateValues();
362 
363  auto storage = INvStorage::newStorage(conf.storage);
364  auto events_channel = std::make_shared<event::Channel>();
365  std::function<void(std::shared_ptr<event::BaseEvent> event)> f_cb = process_events_Install;
366  events_channel->connect(f_cb);
367  auto up = std_::make_unique<UptaneTestCommon::TestUptaneClient>(conf, storage, http, events_channel);
368  EXPECT_NO_THROW(up->initialize());
369 
370  result::UpdateCheck update_result = up->fetchMeta();
371  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
372  result::Download download_result = up->downloadImages(update_result.updates);
373  EXPECT_EQ(download_result.status, result::DownloadStatus::kSuccess);
374  result::Install install_result1 = up->uptaneInstall(download_result.updates);
375  EXPECT_TRUE(install_result1.dev_report.isSuccess());
376  EXPECT_EQ(install_result1.dev_report.result_code, data::ResultCode::Numeric::kOk);
377 
378  // Make sure operation_result and filepath were correctly written and formatted.
379  Json::Value manifest = up->AssembleManifest();
380  EXPECT_EQ(manifest["ecu_version_manifests"]["CA:FE:A6:D2:84:9D"]["signed"]["installed_image"]["filepath"].asString(),
381  "primary_firmware.txt");
382  EXPECT_EQ(
383  manifest["ecu_version_manifests"]["secondary_ecu_serial"]["signed"]["installed_image"]["filepath"].asString(),
384  "secondary_firmware.txt");
385 
386  EXPECT_EQ(num_events_InstallTarget, 2);
387  EXPECT_EQ(num_events_AllInstalls, 1);
388  Json::Value installation_report = manifest["installation_report"]["report"];
389  EXPECT_EQ(installation_report["result"]["success"].asBool(), true);
390  EXPECT_EQ(installation_report["result"]["code"].asString(), "OK");
391  EXPECT_EQ(installation_report["items"][0]["ecu"].asString(), "CA:FE:A6:D2:84:9D");
392  EXPECT_EQ(installation_report["items"][0]["result"]["success"].asBool(), true);
393  EXPECT_EQ(installation_report["items"][0]["result"]["code"].asString(), "OK");
394  EXPECT_EQ(installation_report["items"][1]["ecu"].asString(), "secondary_ecu_serial");
395  EXPECT_EQ(installation_report["items"][1]["result"]["success"].asBool(), true);
396  EXPECT_EQ(installation_report["items"][1]["result"]["code"].asString(), "OK");
397 
398  // Second install to verify that we detect already installed updates
399  // correctly.
400  result::Install install_result2 = up->uptaneInstall(download_result.updates);
401  EXPECT_FALSE(install_result2.dev_report.isSuccess());
402  EXPECT_EQ(install_result2.dev_report.result_code, data::ResultCode::Numeric::kAlreadyProcessed);
403  EXPECT_EQ(num_events_InstallTarget, 2);
404  EXPECT_EQ(num_events_AllInstalls, 2);
405  manifest = up->AssembleManifest();
406  installation_report = manifest["installation_report"]["report"];
407  EXPECT_EQ(installation_report["result"]["success"].asBool(), false);
408 
409  // Recheck updates in order to repopulate Director Targets metadata in the
410  // database (it gets dropped after failure).
411  result::UpdateCheck update_result2 = up->fetchMeta();
412  EXPECT_EQ(update_result2.status, result::UpdateStatus::kNoUpdatesAvailable);
413 
414  // Remove the hashes from the current Target version stored in the database
415  // for the Primary.
416  boost::optional<Uptane::Target> current_version;
417  EXPECT_TRUE(storage->loadInstalledVersions("CA:FE:A6:D2:84:9D", &current_version, nullptr));
418  const auto bad_target = Uptane::Target(current_version->filename(), current_version->ecus(), std::vector<Hash>{},
419  current_version->length());
420  storage->saveInstalledVersion("CA:FE:A6:D2:84:9D", bad_target, InstalledVersionUpdateMode::kCurrent);
421 
422  // Third install to verify that we reject updates with the same filename but
423  // different contents.
424  result::Install install_result3 = up->uptaneInstall(download_result.updates);
425  EXPECT_FALSE(install_result3.dev_report.isSuccess());
426  EXPECT_EQ(install_result3.dev_report.result_code, data::ResultCode::Numeric::kInternalError);
427  EXPECT_THROW(std::rethrow_exception(up->getLastException()), Uptane::TargetContentMismatch);
428  EXPECT_EQ(num_events_InstallTarget, 2);
429  EXPECT_EQ(num_events_AllInstalls, 3);
430 }
431 
432 /*
433  * Verify that installation will fail if the underlying data does not match the
434  * target.
435  */
436 TEST(Uptane, InstallFakeBad) {
437  Config conf("tests/config/basic.toml");
438  TemporaryDirectory temp_dir;
439  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates");
440  conf.uptane.director_server = http->tls_server + "director";
441  conf.uptane.repo_server = http->tls_server + "repo";
442  conf.pacman.type = PACKAGE_MANAGER_NONE;
443  conf.provision.primary_ecu_serial = "CA:FE:A6:D2:84:9D";
444  conf.provision.primary_ecu_hardware_id = "primary_hw";
445  conf.storage.path = temp_dir.Path();
446  conf.tls.server = http->tls_server;
447  UptaneTestCommon::addDefaultSecondary(conf, temp_dir, "secondary_ecu_serial", "secondary_hw");
448  conf.postUpdateValues();
449 
450  auto storage = INvStorage::newStorage(conf.storage);
451  std::function<void(std::shared_ptr<event::BaseEvent> event)> f_cb = process_events_Install;
452  auto up = std_::make_unique<UptaneTestCommon::TestUptaneClient>(conf, storage, http);
453  EXPECT_NO_THROW(up->initialize());
454 
455  result::UpdateCheck update_result = up->fetchMeta();
456  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
457  result::Download download_result = up->downloadImages(update_result.updates);
458  EXPECT_EQ(download_result.status, result::DownloadStatus::kSuccess);
459 
460  std::string hash = download_result.updates[0].sha256Hash();
461  std::transform(hash.begin(), hash.end(), hash.begin(), ::toupper);
462  boost::filesystem::path image = temp_dir / "images" / hash;
463 
464  // Overwrite the file on disk with garbage so that the target verification
465  // fails. First read the existing data so we can re-write it later.
466  auto rhandle = storage->openTargetFile(download_result.updates[0]);
467  const uint64_t length = download_result.updates[0].length();
468  uint8_t content[length];
469  EXPECT_EQ(rhandle->rread(content, length), length);
470  rhandle->rclose();
471  auto whandle = storage->allocateTargetFile(download_result.updates[0]);
472  uint8_t content_bad[length + 1];
473  memset(content_bad, 0, length + 1);
474  EXPECT_EQ(whandle->wfeed(content_bad, 3), 3);
475  whandle->wcommit();
476 
477  result::Install install_result = up->uptaneInstall(download_result.updates);
478  EXPECT_FALSE(install_result.dev_report.isSuccess());
479  EXPECT_EQ(install_result.dev_report.result_code, data::ResultCode::Numeric::kInternalError);
480 
481  // Try again with oversized data.
482  whandle = storage->allocateTargetFile(download_result.updates[0]);
483  EXPECT_EQ(whandle->wfeed(content_bad, length + 1), length + 1);
484  whandle->wcommit();
485 
486  install_result = up->uptaneInstall(download_result.updates);
487  EXPECT_FALSE(install_result.dev_report.isSuccess());
488  EXPECT_EQ(install_result.dev_report.result_code, data::ResultCode::Numeric::kInternalError);
489 
490  // Try again with equally long data to make sure the hash check actually gets
491  // triggered.
492  whandle = storage->allocateTargetFile(download_result.updates[0]);
493  EXPECT_EQ(whandle->wfeed(content_bad, length), length);
494  whandle->wcommit();
495 
496  install_result = up->uptaneInstall(download_result.updates);
497  EXPECT_FALSE(install_result.dev_report.isSuccess());
498  EXPECT_EQ(install_result.dev_report.result_code, data::ResultCode::Numeric::kInternalError);
499 
500  // Try with the real data, but incomplete.
501  whandle = storage->allocateTargetFile(download_result.updates[0]);
502  EXPECT_EQ(whandle->wfeed(reinterpret_cast<uint8_t *>(content), length - 1), length - 1);
503  whandle->wcommit();
504 
505  install_result = up->uptaneInstall(download_result.updates);
506  EXPECT_FALSE(install_result.dev_report.isSuccess());
507  EXPECT_EQ(install_result.dev_report.result_code, data::ResultCode::Numeric::kInternalError);
508 
509  // Restore the original data to the file so that verification succeeds.
510  whandle = storage->allocateTargetFile(download_result.updates[0]);
511  EXPECT_EQ(whandle->wfeed(reinterpret_cast<uint8_t *>(content), length), length);
512  whandle->wcommit();
513 
514  install_result = up->uptaneInstall(download_result.updates);
515  EXPECT_TRUE(install_result.dev_report.isSuccess());
516  EXPECT_EQ(install_result.dev_report.result_code, data::ResultCode::Numeric::kOk);
517 }
518 
519 bool EcuInstallationStartedReportGot = false;
520 class HttpFakeEvents : public HttpFake {
521  public:
522  HttpFakeEvents(const boost::filesystem::path &test_dir_in, std::string flavor = "")
523  : HttpFake(test_dir_in, std::move(flavor)) {}
524 
525  virtual HttpResponse handle_event(const std::string &url, const Json::Value &data) override {
526  for (const auto &event : data) {
527  if (event["eventType"]["id"].asString() == "EcuInstallationStarted") {
528  if (event["event"]["ecu"].asString() == "secondary_ecu_serial") {
529  EcuInstallationStartedReportGot = true;
530  }
531  }
532  }
533  return HttpResponse(url, 200, CURLE_OK, "");
534  }
535 };
536 
538  public:
539  explicit SecondaryInterfaceMock(Primary::VirtualSecondaryConfig &sconfig_in) : sconfig(std::move(sconfig_in)) {
540  std::string private_key, public_key;
541  if (!Crypto::generateKeyPair(sconfig.key_type, &public_key, &private_key)) {
542  throw std::runtime_error("Key generation failure");
543  }
544  public_key_ = PublicKey(public_key, sconfig.key_type);
545  Json::Value manifest_unsigned;
546  manifest_unsigned["key"] = "value";
547 
548  std::string b64sig = Utils::toBase64(
549  Crypto::Sign(sconfig.key_type, nullptr, private_key, Utils::jsonToCanonicalStr(manifest_unsigned)));
550  Json::Value signature;
551  signature["method"] = "rsassa-pss";
552  signature["sig"] = b64sig;
553  signature["keyid"] = public_key_.KeyId();
554  manifest_["signed"] = manifest_unsigned;
555  manifest_["signatures"].append(signature);
556  }
557  std::string Type() const override { return "mock"; }
558  PublicKey getPublicKey() const override { return public_key_; }
559 
560  Uptane::HardwareIdentifier getHwId() const override { return Uptane::HardwareIdentifier(sconfig.ecu_hardware_id); }
561  Uptane::EcuSerial getSerial() const override {
562  if (!sconfig.ecu_serial.empty()) {
563  return Uptane::EcuSerial(sconfig.ecu_serial);
564  }
565  return Uptane::EcuSerial(public_key_.KeyId());
566  }
567  Uptane::Manifest getManifest() const override { return manifest_; }
568  bool ping() const override { return true; }
569  MOCK_METHOD(bool, putMetadataMock, (const Uptane::RawMetaPack &));
570  MOCK_METHOD(int32_t, getRootVersionMock, (bool), (const));
571 
572  bool putMetadata(const Uptane::RawMetaPack &meta_pack) override {
573  putMetadataMock(meta_pack);
574  return true;
575  }
576  int32_t getRootVersion(bool director) const override { return getRootVersionMock(director); }
577 
578  bool putRoot(const std::string &, bool) override { return true; }
579  bool sendFirmware(const std::string &) override { return true; }
580  virtual data::ResultCode::Numeric install(const std::string &) override { return data::ResultCode::Numeric::kOk; }
581 
582  PublicKey public_key_;
583  Json::Value manifest_;
584 
586 };
587 
588 MATCHER_P(matchMeta, meta, "") {
589  return (arg.director_root == meta.director_root) && (arg.image_root == meta.image_root) &&
590  (arg.director_targets == meta.director_targets) && (arg.image_timestamp == meta.image_timestamp) &&
591  (arg.image_snapshot == meta.image_snapshot) && (arg.image_targets == meta.image_targets);
592 }
593 
594 /*
595  * Send metadata to Secondary ECUs
596  * Send EcuInstallationStartedReport to server for Secondaries
597  */
598 TEST(Uptane, SendMetadataToSecondary) {
599  Config conf("tests/config/basic.toml");
600  TemporaryDirectory temp_dir;
601  auto http = std::make_shared<HttpFakeEvents>(temp_dir.Path(), "hasupdates");
602  conf.provision.primary_ecu_serial = "CA:FE:A6:D2:84:9D";
603  conf.provision.primary_ecu_hardware_id = "primary_hw";
604  conf.uptane.director_server = http->tls_server + "/director";
605  conf.uptane.repo_server = http->tls_server + "/repo";
606  conf.storage.path = temp_dir.Path();
607  conf.tls.server = http->tls_server;
608 
610  ecu_config.partial_verifying = false;
611  ecu_config.full_client_dir = temp_dir.Path();
612  ecu_config.ecu_serial = "secondary_ecu_serial";
613  ecu_config.ecu_hardware_id = "secondary_hw";
614  ecu_config.ecu_private_key = "sec.priv";
615  ecu_config.ecu_public_key = "sec.pub";
616  ecu_config.firmware_path = temp_dir / "firmware.txt";
617  ecu_config.target_name_path = temp_dir / "firmware_name.txt";
618  ecu_config.metadata_path = temp_dir / "secondary_metadata";
619 
620  auto sec = std::make_shared<SecondaryInterfaceMock>(ecu_config);
621  auto storage = INvStorage::newStorage(conf.storage);
622  auto up = std_::make_unique<UptaneTestCommon::TestUptaneClient>(conf, storage, http);
623  up->addSecondary(sec);
624  EXPECT_NO_THROW(up->initialize());
625  result::UpdateCheck update_result = up->fetchMeta();
626  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
627 
628  Uptane::RawMetaPack meta;
629  storage->loadLatestRoot(&meta.director_root, Uptane::RepositoryType::Director());
630  storage->loadNonRoot(&meta.director_targets, Uptane::RepositoryType::Director(), Uptane::Role::Targets());
631  storage->loadLatestRoot(&meta.image_root, Uptane::RepositoryType::Image());
632  storage->loadNonRoot(&meta.image_timestamp, Uptane::RepositoryType::Image(), Uptane::Role::Timestamp());
633  storage->loadNonRoot(&meta.image_snapshot, Uptane::RepositoryType::Image(), Uptane::Role::Snapshot());
634  storage->loadNonRoot(&meta.image_targets, Uptane::RepositoryType::Image(), Uptane::Role::Targets());
635 
636  EXPECT_CALL(*sec, putMetadataMock(matchMeta(meta)));
637  result::Download download_result = up->downloadImages(update_result.updates);
638  EXPECT_EQ(download_result.status, result::DownloadStatus::kSuccess);
639  result::Install install_result = up->uptaneInstall(download_result.updates);
640  EXPECT_TRUE(install_result.dev_report.isSuccess());
641  EXPECT_EQ(install_result.dev_report.result_code, data::ResultCode::Numeric::kOk);
642  EXPECT_TRUE(EcuInstallationStartedReportGot);
643 }
644 
645 /* Register Secondary ECUs with Director. */
646 TEST(Uptane, UptaneSecondaryAdd) {
647  TemporaryDirectory temp_dir;
648  auto http = std::make_shared<HttpFake>(temp_dir.Path());
649  Config config = config_common();
650  boost::filesystem::copy_file("tests/test_data/cred.zip", temp_dir / "cred.zip");
651  config.provision.provision_path = temp_dir / "cred.zip";
652  config.provision.mode = ProvisionMode::kSharedCred;
653  config.uptane.director_server = http->tls_server + "/director";
654  config.uptane.repo_server = http->tls_server + "/repo";
655  config.tls.server = http->tls_server;
656  config.provision.primary_ecu_serial = "testecuserial";
657  config.storage.path = temp_dir.Path();
658  config.pacman.type = PACKAGE_MANAGER_NONE;
659  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hardware");
660 
661  auto storage = INvStorage::newStorage(config.storage);
662  auto sota_client = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http);
663  EXPECT_NO_THROW(sota_client->initialize());
664 
665  /* Verify the correctness of the metadata sent to the server about the
666  * Secondary. */
667  Json::Value ecu_data = Utils::parseJSONFile(temp_dir / "post.json");
668  EXPECT_EQ(ecu_data["ecus"].size(), 2);
669  EXPECT_EQ(ecu_data["primary_ecu_serial"].asString(), config.provision.primary_ecu_serial);
670  EXPECT_EQ(ecu_data["ecus"][1]["ecu_serial"].asString(), "secondary_ecu_serial");
671  EXPECT_EQ(ecu_data["ecus"][1]["hardware_identifier"].asString(), "secondary_hardware");
672  EXPECT_EQ(ecu_data["ecus"][1]["clientKey"]["keytype"].asString(), "RSA");
673  EXPECT_TRUE(ecu_data["ecus"][1]["clientKey"]["keyval"]["public"].asString().size() > 0);
674 }
675 
676 /* Adding multiple Secondaries with the same serial throws an error */
677 TEST(Uptane, UptaneSecondaryAddSameSerial) {
678  TemporaryDirectory temp_dir;
679  auto http = std::make_shared<HttpFake>(temp_dir.Path());
680  boost::filesystem::copy_file("tests/test_data/cred.zip", temp_dir / "cred.zip");
681  Config config = config_common();
682  config.provision.provision_path = temp_dir / "cred.zip";
683  config.provision.mode = ProvisionMode::kSharedCred;
684  config.pacman.type = PACKAGE_MANAGER_NONE;
685  config.storage.path = temp_dir.Path();
686 
687  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hardware");
688 
689  auto storage = INvStorage::newStorage(config.storage);
690  auto sota_client = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http);
691  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hardware_new");
692  EXPECT_THROW(sota_client->addSecondary(std::make_shared<Primary::VirtualSecondary>(
693  Primary::VirtualSecondaryConfig::create_from_file(config.uptane.secondary_config_file)[0])),
694  std::runtime_error);
695 }
696 
697 #if 0
698 // TODO: this test needs some refresh, it relies on the behaviour of
699 // UptaneTestCommon::TestUptaneClient which differs from actual SotaUptaneClient
700 /*
701  * Identify previously unknown Secondaries
702  * Identify currently unavailable Secondaries
703  */
704 TEST(Uptane, UptaneSecondaryMisconfigured) {
705  TemporaryDirectory temp_dir;
706  boost::filesystem::copy_file("tests/test_data/cred.zip", temp_dir / "cred.zip");
707  auto http = std::make_shared<HttpFake>(temp_dir.Path());
708  {
709  Config config = config_common();
710  config.provision.provision_path = temp_dir / "cred.zip";
711  config.provision.mode = ProvisionMode::kSharedCred;
712  config.pacman.type = PACKAGE_MANAGER_NONE;
713  config.storage.path = temp_dir.Path();
714  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hardware");
715 
716  auto storage = INvStorage::newStorage(config.storage);
717  auto sota_client = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http);
718  EXPECT_NO_THROW(sota_client->initialize());
719 
720  std::vector<MisconfiguredEcu> ecus;
721  storage->loadMisconfiguredEcus(&ecus);
722  EXPECT_EQ(ecus.size(), 0);
723  }
724  {
725  Config config = config_common();
726  config.provision.provision_path = temp_dir / "cred.zip";
727  config.provision.mode = ProvisionMode::kSharedCred;
728  config.pacman.type = PACKAGE_MANAGER_NONE;
729  config.storage.path = temp_dir.Path();
730  auto storage = INvStorage::newStorage(config.storage);
731  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "new_secondary_ecu_serial", "new_secondary_hardware");
732  auto sota_client = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http);
733  EXPECT_NO_THROW(sota_client->initialize());
734 
735  std::vector<MisconfiguredEcu> ecus;
736  storage->loadMisconfiguredEcus(&ecus);
737  EXPECT_EQ(ecus.size(), 2);
738  if (ecus[0].serial.ToString() == "new_secondary_ecu_serial") {
739  EXPECT_EQ(ecus[0].state, EcuState::kNotRegistered);
740  EXPECT_EQ(ecus[1].serial.ToString(), "secondary_ecu_serial");
741  EXPECT_EQ(ecus[1].state, EcuState::kOld);
742  } else if (ecus[0].serial.ToString() == "secondary_ecu_serial") {
743  EXPECT_EQ(ecus[0].state, EcuState::kOld);
744  EXPECT_EQ(ecus[1].serial.ToString(), "new_secondary_ecu_serial");
745  EXPECT_EQ(ecus[1].state, EcuState::kNotRegistered);
746  } else {
747  FAIL() << "Unexpected Secondary serial in storage: " << ecus[0].serial.ToString();
748  }
749  }
750  {
751  Config config = config_common();
752  config.provision.provision_path = temp_dir / "cred.zip";
753  config.provision.mode = ProvisionMode::kSharedCred;
754  config.pacman.type = PACKAGE_MANAGER_NONE;
755  config.storage.path = temp_dir.Path();
756  auto storage = INvStorage::newStorage(config.storage);
757  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hardware");
758  auto sota_client = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http);
759  EXPECT_NO_THROW(sota_client->initialize());
760 
761  std::vector<MisconfiguredEcu> ecus;
762  storage->loadMisconfiguredEcus(&ecus);
763  EXPECT_EQ(ecus.size(), 0);
764  }
765 }
766 #endif
767 
768 /**
769  * Check that basic device info sent by aktualizr during provisioning matches
770  * our expectations.
771  *
772  * Ideally, we would compare what we have with what the server reports, but in
773  * lieu of that, we can check what we send via http.
774  */
775 class HttpFakeProv : public HttpFake {
776  public:
777  HttpFakeProv(const boost::filesystem::path &test_dir_in, std::string flavor, Config &config_in)
778  : HttpFake(test_dir_in, std::move(flavor)), config(config_in) {}
779 
780  HttpResponse post(const std::string &url, const std::string &content_type, const std::string &data) override {
781  std::cout << "post " << url << "\n";
782 
783  if (url.find("/system_info/config") != std::string::npos) {
784  /* Send libaktualizr configuration to the server. */
785  config_count++;
786  std::stringstream conf_ss;
787  config.writeToStream(conf_ss);
788  EXPECT_EQ(data, conf_ss.str());
789  EXPECT_EQ(content_type, "application/toml");
790  } else {
791  EXPECT_EQ(0, 1) << "Unexpected post to URL: " << url;
792  }
793  return HttpFake::post(url, content_type, data);
794  }
795 
796  HttpResponse post(const std::string &url, const Json::Value &data) override {
797  std::cout << "post " << url << "\n";
798 
799  if (url.find("/devices") != std::string::npos) {
800  devices_count++;
801  EXPECT_EQ(data["deviceId"].asString(), "tst149_device_id");
802  return HttpResponse(Utils::readFile("tests/test_data/cred.p12"), 200, CURLE_OK, "");
803  } else if (url.find("/director/ecus") != std::string::npos) {
804  /* Register Primary ECU with Director. */
805  ecus_count++;
806  EXPECT_EQ(data["primary_ecu_serial"].asString(), "CA:FE:A6:D2:84:9D");
807  EXPECT_EQ(data["ecus"][0]["hardware_identifier"].asString(), "primary_hw");
808  EXPECT_EQ(data["ecus"][0]["ecu_serial"].asString(), "CA:FE:A6:D2:84:9D");
809  if (ecus_count == 1) {
810  return HttpResponse("{}", 200, CURLE_OK, "");
811  } else {
812  return HttpResponse(R"({"code":"ecu_already_registered"})", 409, CURLE_OK, "");
813  }
814  } else if (url.find("/events") != std::string::npos) {
815  return handle_event(url, data);
816  }
817  EXPECT_EQ(0, 1) << "Unexpected post to URL: " << url;
818  return HttpResponse("", 400, CURLE_OK, "");
819  }
820 
821  HttpResponse handle_event_single(const Json::Value &event) {
822  if (event["eventType"]["id"] == "DownloadProgressReport") {
823  return HttpResponse("", 200, CURLE_OK, "");
824  }
825  const std::string event_type = event["eventType"]["id"].asString();
826  const std::string serial = event["event"]["ecu"].asString();
827  std::cout << "Got " << event_type << " event\n";
828  ++events_seen;
829  switch (events_seen) {
830  case 0:
831  EXPECT_EQ(event_type, "SendDeviceDataComplete");
832  break;
833  case 1:
834  case 2:
835  case 3:
836  case 4:
837  if (event_type == "EcuDownloadStarted") {
838  if (serial == "CA:FE:A6:D2:84:9D") {
839  ++primary_download_start;
840  } else if (serial == "secondary_ecu_serial") {
841  ++secondary_download_start;
842  }
843  } else if (event_type == "EcuDownloadCompleted") {
844  if (serial == "CA:FE:A6:D2:84:9D") {
845  ++primary_download_complete;
846  } else if (serial == "secondary_ecu_serial") {
847  ++secondary_download_complete;
848  }
849  }
850  if (events_seen == 4) {
851  EXPECT_EQ(primary_download_start, 1);
852  EXPECT_EQ(primary_download_complete, 1);
853  EXPECT_EQ(secondary_download_start, 1);
854  EXPECT_EQ(secondary_download_complete, 1);
855  }
856  break;
857  case 5:
858  /* Send EcuInstallationStartedReport to server for Primary. */
859  EXPECT_EQ(event_type, "EcuInstallationStarted");
860  EXPECT_EQ(serial, "CA:FE:A6:D2:84:9D");
861  break;
862  case 6:
863  /* Send EcuInstallationCompletedReport to server for Primary. */
864  EXPECT_EQ(event_type, "EcuInstallationCompleted");
865  EXPECT_EQ(serial, "CA:FE:A6:D2:84:9D");
866  break;
867  case 7:
868  /* Send EcuInstallationStartedReport to server for secondaries. */
869  EXPECT_EQ(event_type, "EcuInstallationStarted");
870  EXPECT_EQ(serial, "secondary_ecu_serial");
871  break;
872  case 8:
873  /* Send EcuInstallationCompletedReport to server for secondaries. */
874  EXPECT_EQ(event_type, "EcuInstallationCompleted");
875  EXPECT_EQ(serial, "secondary_ecu_serial");
876  break;
877  default:
878  std::cout << "Unexpected event: " << event_type;
879  EXPECT_EQ(0, 1);
880  }
881  return HttpResponse("", 200, CURLE_OK, "");
882  }
883 
884  HttpResponse handle_event(const std::string &url, const Json::Value &data) override {
885  (void)url;
886  for (const Json::Value &ev : data) {
887  handle_event_single(ev);
888  }
889  return HttpResponse("", 200, CURLE_OK, "");
890  }
891 
892  HttpResponse put(const std::string &url, const Json::Value &data) override {
893  std::cout << "put " << url << "\n";
894  if (url.find("core/installed") != std::string::npos) {
895  /* Send a list of installed packages to the server. */
896  installed_count++;
897  EXPECT_EQ(data.size(), 1);
898  EXPECT_EQ(data[0]["name"].asString(), "fake-package");
899  EXPECT_EQ(data[0]["version"].asString(), "1.0");
900  } else if (url.find("/director/manifest") != std::string::npos) {
901  /* Get manifest from Primary.
902  * Get Primary installation result.
903  * Send manifest to the server. */
904  manifest_count++;
905  std::string file_primary;
906  std::string file_secondary;
907  std::string hash_primary;
908  std::string hash_secondary;
909  if (manifest_count <= 1) {
910  file_primary = "unknown";
911  file_secondary = "noimage";
912  // Check for default initial value of packagemanagerfake.
913  hash_primary = boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest("")));
914  hash_secondary = boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest("")));
915  } else {
916  file_primary = "primary_firmware.txt";
917  file_secondary = "secondary_firmware.txt";
918  const Json::Value json = Utils::parseJSON(Utils::readFile(meta_dir / "director/targets_hasupdates.json"));
919  const Json::Value targets_list = json["signed"]["targets"];
920  hash_primary = targets_list["primary_firmware.txt"]["hashes"]["sha256"].asString();
921  hash_secondary = targets_list["secondary_firmware.txt"]["hashes"]["sha256"].asString();
922  }
923  const Json::Value manifest = data["signed"]["ecu_version_manifests"];
924  const Json::Value manifest_primary = manifest["CA:FE:A6:D2:84:9D"]["signed"]["installed_image"];
925  const Json::Value manifest_secondary = manifest["secondary_ecu_serial"]["signed"]["installed_image"];
926  EXPECT_EQ(file_primary, manifest_primary["filepath"].asString());
927  EXPECT_EQ(file_secondary, manifest_secondary["filepath"].asString());
928  EXPECT_EQ(manifest_primary["fileinfo"]["hashes"]["sha256"].asString(), hash_primary);
929  EXPECT_EQ(manifest_secondary["fileinfo"]["hashes"]["sha256"].asString(), hash_secondary);
930  } else if (url.find("/system_info/network") != std::string::npos) {
931  /* Send networking info to the server. */
932  network_count++;
933  Json::Value nwinfo = Utils::getNetworkInfo();
934  EXPECT_EQ(nwinfo["local_ipv4"].asString(), data["local_ipv4"].asString());
935  EXPECT_EQ(nwinfo["mac"].asString(), data["mac"].asString());
936  EXPECT_EQ(nwinfo["hostname"].asString(), data["hostname"].asString());
937  } else if (url.find("/system_info") != std::string::npos) {
938  /* Send hardware info to the server. */
939  system_info_count++;
940  if (system_info_count <= 2) {
941  Json::Value hwinfo = Utils::getHardwareInfo();
942  EXPECT_EQ(hwinfo["id"].asString(), data["id"].asString());
943  EXPECT_EQ(hwinfo["description"].asString(), data["description"].asString());
944  EXPECT_EQ(hwinfo["class"].asString(), data["class"].asString());
945  EXPECT_EQ(hwinfo["product"].asString(), data["product"].asString());
946  } else {
947  EXPECT_EQ(custom_hw_info, data);
948  }
949  } else {
950  EXPECT_EQ(0, 1) << "Unexpected put to URL: " << url;
951  }
952 
953  return HttpFake::put(url, data);
954  }
955 
956  size_t events_seen{0};
957  int devices_count{0};
958  int ecus_count{0};
959  int manifest_count{0};
960  int installed_count{0};
961  int system_info_count{0};
962  int network_count{0};
963  int config_count{0};
964  Json::Value custom_hw_info;
965 
966  private:
967  Config &config;
968  int primary_download_start{0};
969  int primary_download_complete{0};
970  int secondary_download_start{0};
971  int secondary_download_complete{0};
972 };
973 
974 /* Provision with a fake server and check for the exact number of expected
975  * calls to each endpoint.
976  * Use a provided hardware ID
977  * Send SendDeviceDataComplete event
978  */
979 TEST(Uptane, ProvisionOnServer) {
980  RecordProperty("zephyr_key", "OTA-984,TST-149");
981  TemporaryDirectory temp_dir;
982  Config config("tests/config/basic.toml");
983  auto http = std::make_shared<HttpFakeProv>(temp_dir.Path(), "hasupdates", config);
984  const std::string &server = http->tls_server;
985  config.provision.server = server;
986  config.tls.server = server;
987  config.uptane.director_server = server + "/director";
988  config.uptane.repo_server = server + "/repo";
989  config.provision.ecu_registration_endpoint = server + "/director/ecus";
990  config.provision.device_id = "tst149_device_id";
991  config.provision.primary_ecu_serial = "CA:FE:A6:D2:84:9D";
992  config.provision.primary_ecu_hardware_id = "primary_hw";
993  config.storage.path = temp_dir.Path();
994  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hw");
995  logger_set_threshold(boost::log::trivial::trace);
996 
997  auto storage = INvStorage::newStorage(config.storage);
998  auto events_channel = std::make_shared<event::Channel>();
999  auto up = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http, events_channel);
1000 
1001  EXPECT_EQ(http->devices_count, 0);
1002  EXPECT_EQ(http->ecus_count, 0);
1003  EXPECT_EQ(http->manifest_count, 0);
1004  EXPECT_EQ(http->installed_count, 0);
1005  EXPECT_EQ(http->system_info_count, 0);
1006  EXPECT_EQ(http->network_count, 0);
1007  EXPECT_EQ(http->config_count, 0);
1008 
1009  EXPECT_NO_THROW(up->initialize());
1010  EcuSerials serials;
1011  storage->loadEcuSerials(&serials);
1012  EXPECT_EQ(serials[0].second.ToString(), "primary_hw");
1013 
1014  EXPECT_EQ(http->devices_count, 1);
1015  EXPECT_EQ(http->ecus_count, 1);
1016 
1017  EXPECT_NO_THROW(up->sendDeviceData());
1018  EXPECT_EQ(http->installed_count, 1);
1019  EXPECT_EQ(http->system_info_count, 1);
1020  EXPECT_EQ(http->network_count, 1);
1021  EXPECT_EQ(http->config_count, 1);
1022 
1023  result::UpdateCheck update_result = up->fetchMeta();
1024  EXPECT_EQ(update_result.status, result::UpdateStatus::kUpdatesAvailable);
1025  EXPECT_EQ(http->manifest_count, 1);
1026 
1027  // Test installation to make sure the metadata put to the server is correct.
1028  result::Download download_result = up->downloadImages(update_result.updates);
1029  EXPECT_EQ(download_result.status, result::DownloadStatus::kSuccess);
1030  result::Install install_result = up->uptaneInstall(download_result.updates);
1031  EXPECT_TRUE(install_result.dev_report.isSuccess());
1032  EXPECT_EQ(install_result.dev_report.result_code, data::ResultCode::Numeric::kOk);
1033  up->putManifest();
1034 
1035  EXPECT_EQ(http->devices_count, 1);
1036  EXPECT_EQ(http->ecus_count, 1);
1037  EXPECT_EQ(http->manifest_count, 2);
1038  EXPECT_EQ(http->installed_count, 1);
1039  EXPECT_EQ(http->system_info_count, 1);
1040  EXPECT_EQ(http->network_count, 1);
1041  EXPECT_EQ(http->config_count, 1);
1042  EXPECT_EQ(http->events_seen, 8);
1043 
1044  // Try sending device data again to confirm that it isn't resent if it hasn't
1045  // changed (and hardware info is only sent once).
1046  EXPECT_NO_THROW(up->sendDeviceData(http->custom_hw_info));
1047  EXPECT_EQ(http->installed_count, 1);
1048  EXPECT_EQ(http->system_info_count, 1);
1049  EXPECT_EQ(http->network_count, 1);
1050  EXPECT_EQ(http->config_count, 1);
1051 
1052  // Clear the stored values and resend to verify the data is resent.
1053  storage->clearDeviceData();
1054  EXPECT_NO_THROW(up->sendDeviceData(http->custom_hw_info));
1055  EXPECT_EQ(http->installed_count, 2);
1056  EXPECT_EQ(http->system_info_count, 2);
1057  EXPECT_EQ(http->network_count, 2);
1058  EXPECT_EQ(http->config_count, 2);
1059 
1060  // Set hardware info to a custom value and send device data again.
1061  http->custom_hw_info["hardware"] = "test-hw";
1062  EXPECT_NO_THROW(up->sendDeviceData(http->custom_hw_info));
1063  EXPECT_EQ(http->installed_count, 2);
1064  EXPECT_EQ(http->system_info_count, 3);
1065  EXPECT_EQ(http->network_count, 2);
1066  EXPECT_EQ(http->config_count, 2);
1067 
1068  // Try once again; nothing should be resent.
1069  http->custom_hw_info["hardware"] = "test-hw";
1070  EXPECT_NO_THROW(up->sendDeviceData(http->custom_hw_info));
1071  EXPECT_EQ(http->installed_count, 2);
1072  EXPECT_EQ(http->system_info_count, 3);
1073  EXPECT_EQ(http->network_count, 2);
1074  EXPECT_EQ(http->config_count, 2);
1075 }
1076 
1077 /* Migrate from the legacy filesystem storage. */
1078 TEST(Uptane, FsToSqlFull) {
1079  TemporaryDirectory temp_dir;
1080  Utils::copyDir("tests/test_data/prov", temp_dir.Path());
1081  ASSERT_GE(chmod(temp_dir.Path().c_str(), S_IRWXU), 0);
1082  StorageConfig config;
1083  config.type = StorageType::kSqlite;
1084  config.path = temp_dir.Path();
1085 
1086  FSStorageRead fs_storage(config);
1087 
1088  std::string public_key;
1089  std::string private_key;
1090  fs_storage.loadPrimaryKeys(&public_key, &private_key);
1091 
1092  std::string ca;
1093  std::string cert;
1094  std::string pkey;
1095  fs_storage.loadTlsCreds(&ca, &cert, &pkey);
1096 
1097  std::string device_id;
1098  fs_storage.loadDeviceId(&device_id);
1099 
1100  EcuSerials serials;
1101  fs_storage.loadEcuSerials(&serials);
1102 
1103  bool ecu_registered = fs_storage.loadEcuRegistered();
1104 
1105  std::vector<Uptane::Target> fs_installed_versions;
1106  std::vector<Uptane::Target> fixed_installed_versions;
1107  fs_storage.loadInstalledVersions(&fs_installed_versions, nullptr);
1108  // Add the serial/hwid mapping to match what the SQL storage will do when
1109  // reading it back after it has been copied from FS storage.
1110  for (auto &target : fs_installed_versions) {
1111  Json::Value dump = target.toDebugJson();
1112  dump["custom"]["ecuIdentifiers"][serials[0].first.ToString()]["hardwareId"] = serials[0].second.ToString();
1113  fixed_installed_versions.emplace_back(Uptane::Target(target.filename(), dump));
1114  }
1115 
1116  std::string director_root;
1117  std::string director_targets;
1118  std::string image_root;
1119  std::string image_targets;
1120  std::string image_timestamp;
1121  std::string image_snapshot;
1122 
1123  EXPECT_TRUE(fs_storage.loadLatestRoot(&director_root, Uptane::RepositoryType::Director()));
1124  EXPECT_TRUE(fs_storage.loadNonRoot(&director_targets, Uptane::RepositoryType::Director(), Uptane::Role::Targets()));
1125  EXPECT_TRUE(fs_storage.loadLatestRoot(&image_root, Uptane::RepositoryType::Image()));
1126  EXPECT_TRUE(fs_storage.loadNonRoot(&image_targets, Uptane::RepositoryType::Image(), Uptane::Role::Targets()));
1127  EXPECT_TRUE(fs_storage.loadNonRoot(&image_timestamp, Uptane::RepositoryType::Image(), Uptane::Role::Timestamp()));
1128  EXPECT_TRUE(fs_storage.loadNonRoot(&image_snapshot, Uptane::RepositoryType::Image(), Uptane::Role::Snapshot()));
1129 
1130  EXPECT_TRUE(boost::filesystem::exists(config.uptane_public_key_path.get(config.path)));
1131  EXPECT_TRUE(boost::filesystem::exists(config.uptane_private_key_path.get(config.path)));
1132  EXPECT_TRUE(boost::filesystem::exists(config.tls_cacert_path.get(config.path)));
1133  EXPECT_TRUE(boost::filesystem::exists(config.tls_clientcert_path.get(config.path)));
1134  EXPECT_TRUE(boost::filesystem::exists(config.tls_pkey_path.get(config.path)));
1135 
1136  boost::filesystem::path image_path = config.uptane_metadata_path.get(config.path) / "repo";
1137  boost::filesystem::path director_path = config.uptane_metadata_path.get(config.path) / "director";
1138  EXPECT_TRUE(boost::filesystem::exists(director_path / "1.root.json"));
1139  EXPECT_TRUE(boost::filesystem::exists(director_path / "targets.json"));
1140  EXPECT_TRUE(boost::filesystem::exists(image_path / "1.root.json"));
1141  EXPECT_TRUE(boost::filesystem::exists(image_path / "targets.json"));
1142  EXPECT_TRUE(boost::filesystem::exists(image_path / "timestamp.json"));
1143  EXPECT_TRUE(boost::filesystem::exists(image_path / "snapshot.json"));
1144  EXPECT_TRUE(boost::filesystem::exists(Utils::absolutePath(config.path, "device_id")));
1145  EXPECT_TRUE(boost::filesystem::exists(Utils::absolutePath(config.path, "is_registered")));
1146  EXPECT_TRUE(boost::filesystem::exists(Utils::absolutePath(config.path, "primary_ecu_serial")));
1147  EXPECT_TRUE(boost::filesystem::exists(Utils::absolutePath(config.path, "primary_ecu_hardware_id")));
1148  EXPECT_TRUE(boost::filesystem::exists(Utils::absolutePath(config.path, "secondaries_list")));
1149  auto sql_storage = INvStorage::newStorage(config);
1150 
1151  EXPECT_FALSE(boost::filesystem::exists(config.uptane_public_key_path.get(config.path)));
1152  EXPECT_FALSE(boost::filesystem::exists(config.uptane_private_key_path.get(config.path)));
1153  EXPECT_FALSE(boost::filesystem::exists(config.tls_cacert_path.get(config.path)));
1154  EXPECT_FALSE(boost::filesystem::exists(config.tls_clientcert_path.get(config.path)));
1155  EXPECT_FALSE(boost::filesystem::exists(config.tls_pkey_path.get(config.path)));
1156 
1157  EXPECT_FALSE(boost::filesystem::exists(director_path / "root.json"));
1158  EXPECT_FALSE(boost::filesystem::exists(director_path / "targets.json"));
1159  EXPECT_FALSE(boost::filesystem::exists(director_path / "root.json"));
1160  EXPECT_FALSE(boost::filesystem::exists(director_path / "targets.json"));
1161  EXPECT_FALSE(boost::filesystem::exists(image_path / "timestamp.json"));
1162  EXPECT_FALSE(boost::filesystem::exists(image_path / "snapshot.json"));
1163  EXPECT_FALSE(boost::filesystem::exists(Utils::absolutePath(config.path, "device_id")));
1164  EXPECT_FALSE(boost::filesystem::exists(Utils::absolutePath(config.path, "is_registered")));
1165  EXPECT_FALSE(boost::filesystem::exists(Utils::absolutePath(config.path, "primary_ecu_serial")));
1166  EXPECT_FALSE(boost::filesystem::exists(Utils::absolutePath(config.path, "primary_ecu_hardware_id")));
1167  EXPECT_FALSE(boost::filesystem::exists(Utils::absolutePath(config.path, "secondaries_list")));
1168 
1169  std::string sql_public_key;
1170  std::string sql_private_key;
1171  sql_storage->loadPrimaryKeys(&sql_public_key, &sql_private_key);
1172 
1173  std::string sql_ca;
1174  std::string sql_cert;
1175  std::string sql_pkey;
1176  sql_storage->loadTlsCreds(&sql_ca, &sql_cert, &sql_pkey);
1177 
1178  std::string sql_device_id;
1179  sql_storage->loadDeviceId(&sql_device_id);
1180 
1181  EcuSerials sql_serials;
1182  sql_storage->loadEcuSerials(&sql_serials);
1183 
1184  bool sql_ecu_registered = sql_storage->loadEcuRegistered();
1185 
1186  std::vector<Uptane::Target> sql_installed_versions;
1187  sql_storage->loadPrimaryInstallationLog(&sql_installed_versions, true);
1188 
1189  std::string sql_director_root;
1190  std::string sql_director_targets;
1191  std::string sql_image_root;
1192  std::string sql_image_targets;
1193  std::string sql_image_timestamp;
1194  std::string sql_image_snapshot;
1195 
1196  sql_storage->loadLatestRoot(&sql_director_root, Uptane::RepositoryType::Director());
1197  sql_storage->loadNonRoot(&sql_director_targets, Uptane::RepositoryType::Director(), Uptane::Role::Targets());
1198  sql_storage->loadLatestRoot(&sql_image_root, Uptane::RepositoryType::Image());
1199  sql_storage->loadNonRoot(&sql_image_targets, Uptane::RepositoryType::Image(), Uptane::Role::Targets());
1200  sql_storage->loadNonRoot(&sql_image_timestamp, Uptane::RepositoryType::Image(), Uptane::Role::Timestamp());
1201  sql_storage->loadNonRoot(&sql_image_snapshot, Uptane::RepositoryType::Image(), Uptane::Role::Snapshot());
1202 
1203  EXPECT_EQ(sql_public_key, public_key);
1204  EXPECT_EQ(sql_private_key, private_key);
1205  EXPECT_EQ(sql_ca, ca);
1206  EXPECT_EQ(sql_cert, cert);
1207  EXPECT_EQ(sql_pkey, pkey);
1208  EXPECT_EQ(sql_device_id, device_id);
1209  EXPECT_EQ(sql_serials, serials);
1210  EXPECT_EQ(sql_ecu_registered, ecu_registered);
1211  EXPECT_TRUE(Uptane::MatchTargetVector(sql_installed_versions, fixed_installed_versions));
1212 
1213  EXPECT_EQ(sql_director_root, director_root);
1214  EXPECT_EQ(sql_director_targets, director_targets);
1215  EXPECT_EQ(sql_image_root, image_root);
1216  EXPECT_EQ(sql_image_targets, image_targets);
1217  EXPECT_EQ(sql_image_timestamp, image_timestamp);
1218  EXPECT_EQ(sql_image_snapshot, image_snapshot);
1219 }
1220 
1221 /* Import a list of installed packages into the storage. */
1222 TEST(Uptane, InstalledVersionImport) {
1223  Config config = config_common();
1224 
1225  TemporaryDirectory temp_dir;
1226  Utils::createDirectories(temp_dir / "import", S_IRWXU);
1227  config.storage.path = temp_dir.Path();
1228  config.import.base_path = temp_dir / "import";
1229  config.postUpdateValues();
1230 
1231  boost::filesystem::copy_file("tests/test_data/prov/installed_versions",
1232  temp_dir.Path() / "import/installed_versions");
1233 
1234  // test basic import
1235  auto storage = INvStorage::newStorage(config.storage);
1236  storage->importData(config.import);
1237 
1238  boost::optional<Uptane::Target> current_version;
1239  storage->loadPrimaryInstalledVersions(&current_version, nullptr);
1240  EXPECT_TRUE(!!current_version);
1241  EXPECT_EQ(current_version->filename(), "master-863de625f305413dc3be306afab7c3f39d8713045cfff812b3af83f9722851f0");
1242 
1243  // check that data is not re-imported later: store new data, reload a new
1244  // storage with import and see that the new data is still there
1245  Json::Value target_json;
1246  target_json["hashes"]["sha256"] = "a0fb2e119cf812f1aa9e993d01f5f07cb41679096cb4492f1265bff5ac901d0d";
1247  target_json["length"] = 123;
1248  Uptane::Target new_installed_version{"filename", target_json};
1249  storage->savePrimaryInstalledVersion(new_installed_version, InstalledVersionUpdateMode::kCurrent);
1250 
1251  auto new_storage = INvStorage::newStorage(config.storage);
1252  new_storage->importData(config.import);
1253 
1254  current_version = boost::none;
1255  new_storage->loadPrimaryInstalledVersions(&current_version, nullptr);
1256  EXPECT_TRUE(!!current_version);
1257  EXPECT_EQ(current_version->filename(), "filename");
1258 }
1259 
1260 /* Store a list of installed package versions. */
1261 TEST(Uptane, SaveAndLoadVersion) {
1262  TemporaryDirectory temp_dir;
1263  Config config = config_common();
1264  config.storage.path = temp_dir.Path();
1265  config.provision.device_id = "device_id";
1266  config.postUpdateValues();
1267  auto storage = INvStorage::newStorage(config.storage);
1268 
1269  Json::Value target_json;
1270  target_json["hashes"]["sha256"] = "a0fb2e119cf812f1aa9e993d01f5f07cb41679096cb4492f1265bff5ac901d0d";
1271  target_json["length"] = 123;
1272 
1273  Uptane::Target t("target_name", target_json);
1274  storage->savePrimaryInstalledVersion(t, InstalledVersionUpdateMode::kCurrent);
1275 
1276  boost::optional<Uptane::Target> current_version;
1277  storage->loadPrimaryInstalledVersions(&current_version, nullptr);
1278 
1279  EXPECT_TRUE(!!current_version);
1280  EXPECT_EQ(current_version->sha256Hash(), "a0fb2e119cf812f1aa9e993d01f5f07cb41679096cb4492f1265bff5ac901d0d");
1281  EXPECT_EQ(current_version->length(), 123);
1282  EXPECT_TRUE(current_version->MatchTarget(t));
1283 }
1284 
1285 class HttpFakeUnstable : public HttpFake {
1286  public:
1287  HttpFakeUnstable(const boost::filesystem::path &test_dir_in) : HttpFake(test_dir_in, "hasupdates") {}
1288  HttpResponse get(const std::string &url, int64_t maxsize) override {
1289  if (unstable_valid_count >= unstable_valid_num) {
1290  return HttpResponse({}, 503, CURLE_OK, "");
1291  } else {
1292  ++unstable_valid_count;
1293  return HttpFake::get(url, maxsize);
1294  }
1295  }
1296 
1297  void setUnstableValidNum(int num) {
1298  unstable_valid_num = num;
1299  unstable_valid_count = 0;
1300  }
1301 
1302  int unstable_valid_num{0};
1303  int unstable_valid_count{0};
1304 };
1305 
1306 /* Recover from an interrupted Uptane iteration.
1307  * Fetch metadata from the Director.
1308  * Check metadata from the Director.
1309  * Identify targets for known ECUs.
1310  * Fetch metadata from the Image repo.
1311  * Check metadata from the Image repo.
1312  *
1313  * This is a bit fragile because it depends upon a precise number of HTTP get
1314  * requests being made. If that changes, this test will need to be adjusted. */
1315 TEST(Uptane, restoreVerify) {
1316  TemporaryDirectory temp_dir;
1317  auto http = std::make_shared<HttpFakeUnstable>(temp_dir.Path());
1318  Config config("tests/config/basic.toml");
1319  config.storage.path = temp_dir.Path();
1320  config.pacman.type = PACKAGE_MANAGER_NONE;
1321  config.uptane.director_server = http->tls_server + "director";
1322  config.uptane.repo_server = http->tls_server + "repo";
1323  config.provision.primary_ecu_serial = "CA:FE:A6:D2:84:9D";
1324  config.provision.primary_ecu_hardware_id = "primary_hw";
1325  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hw");
1326  config.postUpdateValues();
1327 
1328  auto storage = INvStorage::newStorage(config.storage);
1329  auto sota_client = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http);
1330 
1331  EXPECT_NO_THROW(sota_client->initialize());
1332  sota_client->AssembleManifest();
1333  // 1st attempt, don't get anything
1334  EXPECT_THROW(sota_client->uptaneIteration(nullptr, nullptr), Uptane::MetadataFetchFailure);
1335  EXPECT_FALSE(storage->loadLatestRoot(nullptr, Uptane::RepositoryType::Director()));
1336 
1337  // 2nd attempt, get Director root.json
1338  http->setUnstableValidNum(1);
1339  EXPECT_THROW(sota_client->uptaneIteration(nullptr, nullptr), Uptane::MetadataFetchFailure);
1340  EXPECT_TRUE(storage->loadLatestRoot(nullptr, Uptane::RepositoryType::Director()));
1341  EXPECT_FALSE(storage->loadNonRoot(nullptr, Uptane::RepositoryType::Director(), Uptane::Role::Targets()));
1342 
1343  // 3rd attempt, get Director targets.json
1344  http->setUnstableValidNum(2);
1345  EXPECT_THROW(sota_client->uptaneIteration(nullptr, nullptr), Uptane::MetadataFetchFailure);
1346  EXPECT_TRUE(storage->loadLatestRoot(nullptr, Uptane::RepositoryType::Director()));
1347  EXPECT_TRUE(storage->loadNonRoot(nullptr, Uptane::RepositoryType::Director(), Uptane::Role::Targets()));
1348  EXPECT_FALSE(storage->loadLatestRoot(nullptr, Uptane::RepositoryType::Image()));
1349 
1350  // 4th attempt, get Image repo root.json
1351  http->setUnstableValidNum(3);
1352  EXPECT_THROW(sota_client->uptaneIteration(nullptr, nullptr), Uptane::MetadataFetchFailure);
1353  EXPECT_TRUE(storage->loadLatestRoot(nullptr, Uptane::RepositoryType::Image()));
1354  EXPECT_FALSE(storage->loadNonRoot(nullptr, Uptane::RepositoryType::Image(), Uptane::Role::Timestamp()));
1355 
1356  // 5th attempt, get Image repo timestamp.json
1357  http->setUnstableValidNum(4);
1358  EXPECT_THROW(sota_client->uptaneIteration(nullptr, nullptr), Uptane::MetadataFetchFailure);
1359  EXPECT_TRUE(storage->loadNonRoot(nullptr, Uptane::RepositoryType::Image(), Uptane::Role::Timestamp()));
1360  EXPECT_FALSE(storage->loadNonRoot(nullptr, Uptane::RepositoryType::Image(), Uptane::Role::Snapshot()));
1361 
1362  // 6th attempt, get Image repo snapshot.json
1363  http->setUnstableValidNum(5);
1364  EXPECT_THROW(sota_client->uptaneIteration(nullptr, nullptr), Uptane::MetadataFetchFailure);
1365  EXPECT_TRUE(storage->loadNonRoot(nullptr, Uptane::RepositoryType::Image(), Uptane::Role::Snapshot()));
1366  EXPECT_FALSE(storage->loadNonRoot(nullptr, Uptane::RepositoryType::Image(), Uptane::Role::Targets()));
1367 
1368  // 7th attempt, get Image repo targets.json, successful iteration
1369  http->setUnstableValidNum(6);
1370  EXPECT_NO_THROW(sota_client->uptaneIteration(nullptr, nullptr));
1371  EXPECT_TRUE(storage->loadNonRoot(nullptr, Uptane::RepositoryType::Image(), Uptane::Role::Targets()));
1372 }
1373 
1374 /* Fetch metadata from the Director.
1375  * Check metadata from the Director.
1376  * Identify targets for known ECUs.
1377  * Fetch metadata from the Image repo.
1378  * Check metadata from the Image repo. */
1379 TEST(Uptane, offlineIteration) {
1380  TemporaryDirectory temp_dir;
1381  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates");
1382  Config config("tests/config/basic.toml");
1383  config.storage.path = temp_dir.Path();
1384  config.uptane.director_server = http->tls_server + "director";
1385  config.uptane.repo_server = http->tls_server + "repo";
1386  config.pacman.type = PACKAGE_MANAGER_NONE;
1387  config.provision.primary_ecu_serial = "CA:FE:A6:D2:84:9D";
1388  config.provision.primary_ecu_hardware_id = "primary_hw";
1389  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hw");
1390  config.postUpdateValues();
1391 
1392  auto storage = INvStorage::newStorage(config.storage);
1393  auto sota_client = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http);
1394  EXPECT_NO_THROW(sota_client->initialize());
1395 
1396  std::vector<Uptane::Target> targets_online;
1397  EXPECT_NO_THROW(sota_client->uptaneIteration(&targets_online, nullptr));
1398 
1399  std::vector<Uptane::Target> targets_offline;
1400  EXPECT_NO_THROW(sota_client->uptaneOfflineIteration(&targets_offline, nullptr));
1401  EXPECT_TRUE(Uptane::MatchTargetVector(targets_online, targets_offline));
1402 }
1403 
1404 /*
1405  * Ignore updates for unrecognized ECUs.
1406  * Reject targets which do not match a known ECU.
1407  */
1408 TEST(Uptane, IgnoreUnknownUpdate) {
1409  TemporaryDirectory temp_dir;
1410  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates");
1411  Config config("tests/config/basic.toml");
1412  config.storage.path = temp_dir.Path();
1413  config.uptane.director_server = http->tls_server + "director";
1414  config.uptane.repo_server = http->tls_server + "repo";
1415  config.pacman.type = PACKAGE_MANAGER_NONE;
1416  config.provision.primary_ecu_serial = "primary_ecu";
1417  config.provision.primary_ecu_hardware_id = "primary_hw";
1418  UptaneTestCommon::addDefaultSecondary(config, temp_dir, "secondary_ecu_serial", "secondary_hw");
1419  config.postUpdateValues();
1420 
1421  auto storage = INvStorage::newStorage(config.storage);
1422  auto sota_client = std_::make_unique<UptaneTestCommon::TestUptaneClient>(config, storage, http);
1423 
1424  EXPECT_NO_THROW(sota_client->initialize());
1425 
1426  auto result = sota_client->fetchMeta();
1427  EXPECT_EQ(result.status, result::UpdateStatus::kError);
1428  std::vector<Uptane::Target> packages_to_install = UptaneTestCommon::makePackage("testecuserial", "testecuhwid");
1429  auto report = sota_client->uptaneInstall(packages_to_install);
1430  EXPECT_EQ(report.ecu_reports.size(), 0);
1431 }
1432 
1433 #ifdef BUILD_P11
1434 TEST(Uptane, Pkcs11Provision) {
1435  Config config;
1436  TemporaryDirectory temp_dir;
1437  Utils::createDirectories(temp_dir / "import", S_IRWXU);
1438  boost::filesystem::copy_file("tests/test_data/device_cred_prov/ca.pem", temp_dir / "import/root.crt");
1439  config.tls.cert_source = CryptoSource::kPkcs11;
1440  config.tls.pkey_source = CryptoSource::kPkcs11;
1441  config.p11.module = TEST_PKCS11_MODULE_PATH;
1442  config.p11.pass = "1234";
1443  config.p11.tls_clientcert_id = "01";
1444  config.p11.tls_pkey_id = "02";
1445  config.import.base_path = (temp_dir / "import").string();
1446  config.import.tls_cacert_path = BasedPath("root.crt");
1447 
1448  config.storage.path = temp_dir.Path();
1449  config.postUpdateValues();
1450 
1451  auto storage = INvStorage::newStorage(config.storage);
1452  storage->importData(config.import);
1453  auto http = std::make_shared<HttpFake>(temp_dir.Path(), "hasupdates");
1454  KeyManager keys(storage, config.keymanagerConfig());
1455 
1456  EXPECT_NO_THROW(Initializer(config.provision, storage, http, keys, {}));
1457 }
1458 #endif
1459 
1460 #ifndef __NO_MAIN__
1461 int main(int argc, char **argv) {
1462  ::testing::InitGoogleTest(&argc, argv);
1463 
1464  logger_init();
1465  logger_set_threshold(boost::log::trivial::trace);
1466 
1467  return RUN_ALL_TESTS();
1468 }
1469 #endif
1470 
1471 // vim: set tabstop=2 shiftwidth=2 expandtab:
All ECU installation attempts for an update have completed.
Definition: events.h:151
Check that basic device info sent by aktualizr during provisioning matches our expectations.
Definition: uptane_test.cc:775
General data structures.
Definition: types.cc:55
Container for information about downloading an update.
Definition: results.h:117
Configuration object for an aktualizr instance running on a Primary ECU.
Definition: config.h:74
Operation has already been processed.
An installation attempt on an ECU has completed.
Definition: events.h:135
Container for information about installing an update.
Definition: results.h:130
SWM Internal integrity error.
Container for information about available updates.
Definition: results.h:38
Results of libaktualizr API calls.
Definition: results.h:13
Base data types that are used in The Update Framework (TUF), part of Uptane.
Aktualizr status events.
Definition: events.h:18