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