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