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