Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
aktualizr_secondary_test.cc
1 #include <gtest/gtest.h>
2 
3 #include <boost/process.hpp>
4 
5 #include "aktualizr_secondary.h"
6 #include "aktualizr_secondary_factory.h"
7 #include "test_utils.h"
8 #include "uptane_repo.h"
9 
11  public:
14  config.pacman.type = PACKAGE_MANAGER_NONE;
15 
16  config.storage.path = _storage_dir.Path();
17  config.storage.type = StorageType::kSqlite;
18 
19  _storage = INvStorage::newStorage(config.storage);
20  _secondary = AktualizrSecondaryFactory::create(config, _storage);
21  }
22 
23  public:
24  std::shared_ptr<AktualizrSecondary>& operator->() { return _secondary; }
25 
26  Uptane::Target getPendingVersion() const {
27  boost::optional<Uptane::Target> pending_target;
28 
29  _storage->loadInstalledVersions(_secondary->getSerial().ToString(), nullptr, &pending_target);
30  return *pending_target;
31  }
32 
33  std::string hardwareID() const { return _secondary->getHwId().ToString(); }
34 
35  std::string serial() const { return _secondary->getSerial().ToString(); }
36 
37  boost::filesystem::path targetFilepath() const {
38  return _storage_dir.Path() / AktualizrSecondaryFactory::BinaryUpdateDefaultFile;
39  }
40 
41  private:
42  TemporaryDirectory _storage_dir;
43  AktualizrSecondary::Ptr _secondary;
44  std::shared_ptr<INvStorage> _storage;
45 };
46 
47 class UptaneRepoWrapper {
48  public:
49  UptaneRepoWrapper() { _uptane_repo.generateRepo(KeyType::kED25519); }
50 
51  Metadata addImageFile(const std::string& targetname, const std::string& hardware_id, const std::string& serial,
52  bool add_and_sign_target = true) {
53  const auto image_file_path = _root_dir / targetname;
54  boost::filesystem::ofstream(image_file_path) << "some data";
55 
56  _uptane_repo.addImage(image_file_path, targetname, hardware_id, "", Delegation());
57  if (add_and_sign_target) {
58  _uptane_repo.addTarget(targetname, hardware_id, serial, "");
59  _uptane_repo.signTargets();
60  }
61 
62  return getCurrentMetadata();
63  }
64 
65  Uptane::RawMetaPack getCurrentMetadata() const {
66  Uptane::RawMetaPack metadata;
67 
68  boost::filesystem::load_string_file(_director_dir / "root.json", metadata.director_root);
69  boost::filesystem::load_string_file(_director_dir / "targets.json", metadata.director_targets);
70 
71  boost::filesystem::load_string_file(_imagerepo_dir / "root.json", metadata.image_root);
72  boost::filesystem::load_string_file(_imagerepo_dir / "timestamp.json", metadata.image_timestamp);
73  boost::filesystem::load_string_file(_imagerepo_dir / "snapshot.json", metadata.image_snapshot);
74  boost::filesystem::load_string_file(_imagerepo_dir / "targets.json", metadata.image_targets);
75 
76  return metadata;
77  }
78 
79  std::string getImageData(const std::string& targetname) const {
80  std::string image_data;
81  boost::filesystem::load_string_file(_root_dir / targetname, image_data);
82  return image_data;
83  }
84 
85  void refreshRoot(Uptane::RepositoryType repo) { _uptane_repo.refresh(repo, Uptane::Role::Root()); }
86 
87  private:
88  TemporaryDirectory _root_dir;
89  boost::filesystem::path _director_dir{_root_dir / "repo/director"};
90  boost::filesystem::path _imagerepo_dir{_root_dir / "repo/repo"};
91  UptaneRepo _uptane_repo{_root_dir.Path(), "", ""};
92  Uptane::DirectorRepository _director_repo;
93 };
94 
95 class SecondaryTest : public ::testing::Test {
96  protected:
97  SecondaryTest() {
98  _uptane_repo.addImageFile(_default_target, _secondary->getHwId().ToString(), _secondary->getSerial().ToString());
99  }
100 
101  std::string getImageData(const std::string& targetname = _default_target) const {
102  return _uptane_repo.getImageData(targetname);
103  }
104 
105  std::vector<Uptane::Target> getCurrentTargets() {
106  auto targets = Uptane::Targets(Utils::parseJSON(_uptane_repo.getCurrentMetadata().director_targets));
107  return targets.getTargets(_secondary->getSerial(), _secondary->getHwId());
108  }
109 
110  Uptane::Target getDefaultTarget() {
111  auto targets = getCurrentTargets();
112  EXPECT_GT(targets.size(), 0);
113  return targets[0];
114  }
115 
116  Uptane::Hash getDefaultTargetHash() {
117  return Uptane::Hash(Uptane::Hash::Type::kSha256, getDefaultTarget().sha256Hash());
118  }
119 
120  protected:
121  static constexpr const char* const _default_target{"default-target"};
122  AktualizrSecondaryWrapper _secondary;
123  UptaneRepoWrapper _uptane_repo;
124 };
125 
126 class SecondaryTestNegative : public ::testing::Test,
127  public ::testing::WithParamInterface<std::pair<Uptane::RepositoryType, Uptane::Role>> {
128  protected:
129  class MetadataInvalidator : public Metadata {
130  public:
131  MetadataInvalidator(const Uptane::RawMetaPack& valid_metadata, const Uptane::RepositoryType& repo,
132  const Uptane::Role& role)
133  : Metadata(valid_metadata), _repo_type(repo), _role(role) {}
134 
135  bool getRoleMetadata(std::string* result, const Uptane::RepositoryType& repo, const Uptane::Role& role,
136  Uptane::Version version) const override {
137  auto return_val = Metadata::getRoleMetadata(result, repo, role, version);
138  if (!(_repo_type == repo && _role == role)) {
139  return return_val;
140  }
141  (*result)[10] = 'f';
142  return true;
143  }
144 
145  private:
146  Uptane::RepositoryType _repo_type;
147  Uptane::Role _role;
148  };
149 
150  protected:
151  MetadataInvalidator currentMetadata() const {
152  return MetadataInvalidator(_uptane_repo.getCurrentMetadata(), GetParam().first, GetParam().second);
153  }
154 
155  protected:
156  static AktualizrSecondaryWrapper _secondary;
157  static UptaneRepoWrapper _uptane_repo;
158 };
159 
160 AktualizrSecondaryWrapper SecondaryTestNegative::_secondary;
161 UptaneRepoWrapper SecondaryTestNegative::_uptane_repo;
162 
163 /**
164  * Parameterized test,
165  * The parameter is std::pair<Uptane::RepositoryType, Uptane::Role> to indicate which metadata to malform
166  *
167  * see INSTANTIATE_TEST_SUITE_P for the test instantiations with concrete parameter values
168  */
169 TEST_P(SecondaryTestNegative, MalformedMetadaJson) { EXPECT_FALSE(_secondary->putMetadata(currentMetadata())); }
170 
171 /**
172  * Instantiates the parameterized test for each specified value of std::pair<Uptane::RepositoryType, Uptane::Role>
173  * the parameter value indicates which metadata to malform
174  */
175 INSTANTIATE_TEST_SUITE_P(SecondaryTestMalformedMetadata, SecondaryTestNegative,
176  ::testing::Values(std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Root()),
177  std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Targets()),
178  std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Root()),
179  std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Timestamp()),
180  std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Snapshot()),
181  std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Targets())));
182 
183 TEST_F(SecondaryTest, fullUptaneVerificationPositive) {
184  ASSERT_TRUE(_secondary->putMetadata(_uptane_repo.getCurrentMetadata()));
185  ASSERT_TRUE(_secondary->sendFirmware(getImageData()));
186  ASSERT_EQ(_secondary->install(_default_target), data::ResultCode::Numeric::kOk);
187 
188  // check if a file was actually updated
189  ASSERT_TRUE(boost::filesystem::exists(_secondary.targetFilepath()));
190  auto target = getDefaultTarget();
191 
192  // check the updated file hash
193  auto target_hash = Uptane::Hash(Uptane::Hash::Type::kSha256, target.sha256Hash());
194  auto target_file_hash =
195  Uptane::Hash::generate(Uptane::Hash::Type::kSha256, Utils::readFile(_secondary.targetFilepath()));
196  EXPECT_EQ(target_hash, target_file_hash);
197 
198  // check the secondary manifest
199  auto manifest = _secondary->getManifest();
200  EXPECT_EQ(manifest.installedImageHash(), target_file_hash);
201  EXPECT_EQ(manifest.filepath(), target.filename());
202 }
203 
204 TEST_F(SecondaryTest, TwoImagesAndOneTarget) {
205  // two images for the same ECU, just one of them is added as a target and signed
206  // default image and corresponding target has been already added, just add another image
207  _uptane_repo.addImageFile("second_image_00", _secondary->getHwId().ToString(), _secondary->getSerial().ToString(),
208  false);
209  EXPECT_TRUE(_secondary->putMetadata(_uptane_repo.getCurrentMetadata()));
210 }
211 
212 TEST_F(SecondaryTest, IncorrectTargetQuantity) {
213  {
214  // two targets for the same ECU
215  _uptane_repo.addImageFile("second_target", _secondary->getHwId().ToString(), _secondary->getSerial().ToString());
216 
217  EXPECT_FALSE(_secondary->putMetadata(_uptane_repo.getCurrentMetadata()));
218  }
219 
220  {
221  // zero targets for the ECU being tested
222  auto metadata =
223  UptaneRepoWrapper().addImageFile("mytarget", _secondary->getHwId().ToString(), "non-existing-serial");
224 
225  EXPECT_FALSE(_secondary->putMetadata(metadata));
226  }
227 
228  {
229  // zero targets for the ECU being tested
230  auto metadata =
231  UptaneRepoWrapper().addImageFile("mytarget", "non-existig-hwid", _secondary->getSerial().ToString());
232 
233  EXPECT_FALSE(_secondary->putMetadata(metadata));
234  }
235 }
236 
237 TEST_F(SecondaryTest, DirectorRootVersionIncremented) {
238  _uptane_repo.refreshRoot(Uptane::RepositoryType::Director());
239  EXPECT_TRUE(_secondary->putMetadata(_uptane_repo.getCurrentMetadata()));
240 }
241 
242 TEST_F(SecondaryTest, ImageRootVersionIncremented) {
243  _uptane_repo.refreshRoot(Uptane::RepositoryType::Image());
244  EXPECT_TRUE(_secondary->putMetadata(_uptane_repo.getCurrentMetadata()));
245 }
246 
247 TEST_F(SecondaryTest, InvalidImageFileSize) {
248  EXPECT_TRUE(_secondary->putMetadata(_uptane_repo.getCurrentMetadata()));
249  auto image_data = getImageData();
250  image_data.append("\n");
251  EXPECT_FALSE(_secondary->sendFirmware(image_data));
252 }
253 
254 TEST_F(SecondaryTest, InvalidImageData) {
255  EXPECT_TRUE(_secondary->putMetadata(_uptane_repo.getCurrentMetadata()));
256  auto image_data = getImageData();
257  image_data.operator[](3) = '0';
258  EXPECT_FALSE(_secondary->sendFirmware(image_data));
259 }
260 
261 int main(int argc, char** argv) {
262  ::testing::InitGoogleTest(&argc, argv);
263 
264  logger_init();
265  logger_set_threshold(boost::log::trivial::info);
266 
267  return RUN_ALL_TESTS();
268 }
UptaneRepoWrapper
Definition: aktualizr_secondary_ostree_test.cc:145
Uptane::DirectorRepository
Definition: directorrepository.h:13
Uptane::Version
Metadata version numbers.
Definition: tuf.h:116
AktualizrSecondaryConfig
Definition: aktualizr_secondary_config.h:40
Uptane::Hash
The hash of a file or TUF metadata.
Definition: tuf.h:209
Uptane::RawMetaPack
Definition: tuf.h:535
Uptane::RepositoryType
Definition: tuf.h:20
SecondaryTest
Definition: aktualizr_secondary_test.cc:95
Uptane::Targets
Definition: tuf.h:440
SecondaryTestNegative::MetadataInvalidator
Definition: aktualizr_secondary_test.cc:129
SecondaryTestNegative
Definition: aktualizr_secondary_test.cc:126
TemporaryDirectory
Definition: utils.h:82
result
Results of libaktualizr API calls.
Definition: results.h:13
Uptane::Role
TUF Roles.
Definition: tuf.h:57
Uptane::Target
Definition: tuf.h:238
Metadata
Definition: aktualizr_secondary_metadata.h:8
UptaneRepo
Definition: uptane_repo.h:7
AktualizrSecondaryWrapper
Definition: aktualizr_secondary_ostree_test.cc:92
Delegation
Definition: repo.h:19