Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
repo_test.cc
1 #include <gtest/gtest.h>
2 
3 #include <iostream>
4 #include <string>
5 
6 #include <boost/filesystem.hpp>
7 
8 #include "libaktualizr/config.h"
9 #include "logging/logging.h"
10 #include "test_utils.h"
11 #include "uptane_repo.h"
12 
13 KeyType key_type = KeyType::kED25519;
14 std::string generate_repo_exec;
15 
16 void check_repo(boost::filesystem::path repo_dir) {
17  Json::Value targets = Utils::parseJSONFile(repo_dir / "targets.json")["signed"];
18  std::string signed_targets = Utils::readFile(repo_dir / "targets.json");
19 
20  Json::Value snapshot = Utils::parseJSONFile(repo_dir / "snapshot.json")["signed"];
21  EXPECT_EQ(snapshot["meta"]["targets.json"]["version"].asUInt(), targets["version"].asUInt());
22 
23  auto signed_snapshot = Utils::readFile(repo_dir / "snapshot.json");
24  Json::Value timestamp = Utils::parseJSONFile(repo_dir / "timestamp.json")["signed"];
25  EXPECT_EQ(timestamp["meta"]["snapshot.json"]["hashes"]["sha256"].asString(),
26  boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest(signed_snapshot))));
27  EXPECT_EQ(timestamp["meta"]["snapshot.json"]["hashes"]["sha512"].asString(),
28  boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha512digest(signed_snapshot))));
29  EXPECT_EQ(timestamp["meta"]["snapshot.json"]["length"].asUInt(), static_cast<Json::UInt>(signed_snapshot.length()));
30  EXPECT_EQ(timestamp["meta"]["snapshot.json"]["version"].asUInt(), snapshot["version"].asUInt());
31 }
32 
33 void check_repo(const TemporaryDirectory &temp_dir) {
34  check_repo(temp_dir.Path() / ImageRepo::dir);
35  check_repo(temp_dir.Path() / DirectorRepo::dir);
36 }
37 /*
38  * Generate Image and Director repos.
39  */
40 TEST(uptane_generator, generate_repo) {
41  TemporaryDirectory temp_dir;
42  UptaneRepo repo(temp_dir.Path(), "", "correlation");
43  repo.generateRepo(key_type);
44  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / DirectorRepo::dir / "root.json"));
45  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / DirectorRepo::dir / "1.root.json"));
46  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / DirectorRepo::dir / "targets.json"));
47  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / DirectorRepo::dir / "timestamp.json"));
48  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / ImageRepo::dir / "root.json"));
49  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / ImageRepo::dir / "1.root.json"));
50  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / ImageRepo::dir / "targets.json"));
51  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / ImageRepo::dir / "timestamp.json"));
52  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / DirectorRepo::dir / "manifest"));
53 
54  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/image/root/private.key"));
55  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/image/root/public.key"));
56  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/image/snapshot/private.key"));
57  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/image/snapshot/public.key"));
58  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/image/targets/private.key"));
59  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/image/targets/public.key"));
60  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/image/timestamp/private.key"));
61  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/image/timestamp/public.key"));
62 
63  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/director/root/private.key"));
64  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/director/root/public.key"));
65  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/director/snapshot/private.key"));
66  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/director/snapshot/public.key"));
67  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/director/targets/private.key"));
68  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/director/targets/public.key"));
69  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/director/timestamp/private.key"));
70  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / "keys/director/timestamp/public.key"));
71 
72  std::vector<std::string> keys;
73  std::function<void(const boost::filesystem::path &path)> recursive_check;
74  recursive_check = [&keys, &recursive_check](const boost::filesystem::path &path) {
75  for (auto &p : boost::filesystem::directory_iterator(path)) {
76  if (p.status().type() == boost::filesystem::file_type::directory_file) {
77  recursive_check(p.path());
78  } else {
79  if (p.path().filename().string() == "key_type") {
80  continue;
81  }
82  std::string hash = Crypto::sha512digest(Utils::readFile(p.path()));
83  if (std::find(keys.begin(), keys.end(), hash) == keys.end()) {
84  keys.push_back(hash);
85  } else {
86  FAIL() << p.path().string() << " is not unique";
87  }
88  }
89  }
90  };
91  recursive_check(temp_dir.Path() / "keys");
92 
93  Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
94  EXPECT_EQ(image_targets["signed"]["targets"].size(), 0);
95  Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
96  EXPECT_EQ(director_targets["signed"]["targets"].size(), 0);
97  EXPECT_EQ(director_targets["signed"]["custom"]["correlationId"], "correlation");
98  check_repo(temp_dir);
99 }
100 
101 /*
102  * Add an image to the Image repo.
103  */
104 TEST(uptane_generator, add_image) {
105  TemporaryDirectory temp_dir;
106  UptaneRepo repo(temp_dir.Path(), "", "");
107  repo.generateRepo(key_type);
108  repo.addImage(temp_dir.Path() / DirectorRepo::dir / "manifest", std::string(DirectorRepo::dir) + "/manifest",
109  "test-hw", "", {});
110  Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
111  EXPECT_EQ(image_targets["signed"]["targets"].size(), 1);
112  EXPECT_FALSE(
113  image_targets["signed"]["targets"][std::string(DirectorRepo::dir) + "/manifest"]["custom"].isMember("uri"));
114  Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
115  EXPECT_EQ(director_targets["signed"]["targets"].size(), 0);
116  check_repo(temp_dir);
117 }
118 
119 /*
120  * Copy an image to the Director repo.
121  */
122 TEST(uptane_generator, copy_image) {
123  TemporaryDirectory temp_dir;
124  UptaneRepo repo(temp_dir.Path(), "", "");
125  repo.generateRepo(key_type);
126  repo.addImage(temp_dir.Path() / DirectorRepo::dir / "manifest", "manifest", "test-hw", "", {});
127  repo.addTarget("manifest", "test-hw", "test-serial", "");
128  repo.signTargets();
129  Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
130  EXPECT_EQ(image_targets["signed"]["targets"].size(), 1);
131  EXPECT_FALSE(image_targets["signed"]["targets"]["manifest"]["custom"].isMember("uri"));
132  Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
133  EXPECT_EQ(director_targets["signed"]["targets"].size(), 1);
134  EXPECT_FALSE(director_targets["signed"]["targets"]["manifest"]["custom"].isMember("uri"));
135  check_repo(temp_dir);
136 }
137 
138 /*
139  * Add an image to the Image repo with a custom URL.
140  */
141 TEST(uptane_generator, image_custom_url) {
142  TemporaryDirectory temp_dir;
143  UptaneRepo repo(temp_dir.Path(), "", "");
144  repo.generateRepo(key_type);
145  repo.addImage(temp_dir.Path() / DirectorRepo::dir / "manifest", "manifest", "test-hw", "test-url", {});
146  repo.addTarget("manifest", "test-hw", "test-serial", "");
147  repo.signTargets();
148  Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
149  EXPECT_EQ(image_targets["signed"]["targets"].size(), 1);
150  EXPECT_EQ(image_targets["signed"]["targets"]["manifest"]["custom"]["uri"], "test-url");
151  Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
152  EXPECT_EQ(director_targets["signed"]["targets"].size(), 1);
153  EXPECT_FALSE(director_targets["signed"]["targets"]["manifest"]["custom"].isMember("uri"));
154  check_repo(temp_dir);
155 }
156 
157 /*
158  * Add an image to the Image repo with a custom URL.
159  * Copy an image to the Director repo with a custom URL.
160  */
161 TEST(uptane_generator, both_custom_url) {
162  TemporaryDirectory temp_dir;
163  UptaneRepo repo(temp_dir.Path(), "", "");
164  repo.generateRepo(key_type);
165  repo.addImage(temp_dir.Path() / DirectorRepo::dir / "manifest", "manifest", "test-hw", "test-url", {});
166  repo.addTarget("manifest", "test-hw", "test-serial", "test-url2");
167  repo.signTargets();
168  Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
169  EXPECT_EQ(image_targets["signed"]["targets"].size(), 1);
170  EXPECT_EQ(image_targets["signed"]["targets"]["manifest"]["custom"]["uri"], "test-url");
171  Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
172  EXPECT_EQ(director_targets["signed"]["targets"].size(), 1);
173  EXPECT_EQ(director_targets["signed"]["targets"]["manifest"]["custom"]["uri"], "test-url2");
174  check_repo(temp_dir);
175 }
176 
177 /*
178  * Add simple delegation.
179  * Add image with delegation.
180  */
181 TEST(uptane_generator, delegation) {
182  TemporaryDirectory temp_dir;
183  std::ostringstream keytype_stream;
184  keytype_stream << key_type;
185  std::string cmd = generate_repo_exec + " generate " + temp_dir.Path().string() + " --keytype " + keytype_stream.str();
186  std::string output;
187  int retval = Utils::shell(cmd, &output);
188  if (retval) {
189  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
190  }
191  cmd = generate_repo_exec + " adddelegation " + temp_dir.Path().string() + " --keytype " + keytype_stream.str() +
192  " --dname test_delegate --dpattern 'tests/test_data/*.txt' --hwid primary_hw";
193  retval = Utils::shell(cmd, &output);
194  if (retval) {
195  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
196  }
197 
198  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / ImageRepo::dir / "delegations/test_delegate.json"));
199  auto targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
200  EXPECT_EQ(targets["signed"]["delegations"]["roles"][0]["name"].asString(), "test_delegate");
201  EXPECT_EQ(targets["signed"]["delegations"]["roles"][0]["paths"][0].asString(), "tests/test_data/*.txt");
202 
203  cmd = generate_repo_exec + " image " + temp_dir.Path().string() + " --keytype " + keytype_stream.str() +
204  " --dname test_delegate --filename tests/test_data/firmware.txt --hwid primary_hw";
205  retval = Utils::shell(cmd, &output);
206  if (retval) {
207  FAIL() << "'" << output << "' exited with error code " << retval << "\n";
208  }
209  {
210  auto test_delegate = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "delegations/test_delegate.json");
211  Uptane::Targets delegate_targets(test_delegate);
212  EXPECT_EQ(delegate_targets.targets.size(), 1);
213  EXPECT_EQ(delegate_targets.targets[0].filename(), "tests/test_data/firmware.txt");
214  EXPECT_EQ(delegate_targets.targets[0].length(), 17);
215  EXPECT_EQ(delegate_targets.targets[0].sha256Hash(),
216  "d8e9caba8c1697fcbade1057f9c2488044192ff76bb64d4aba2c20e53dc33033");
217  }
218  cmd = generate_repo_exec + " image " + temp_dir.Path().string() + " --keytype " + keytype_stream.str() +
219  " --dname test_delegate --targetname tests/test_data/firmware2.txt --targetsha256 "
220  "d8e9caba8c1697fcbade1057f9c2488044192ff76bb64d4aba2c20e53dc33033 --targetlength 17 --hwid primary_hw";
221  retval = Utils::shell(cmd, &output);
222  if (retval) {
223  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
224  }
225  {
226  auto test_delegate = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "delegations/test_delegate.json");
227  Uptane::Targets delegate_targets(test_delegate);
228  EXPECT_EQ(delegate_targets.targets.size(), 2);
229  EXPECT_EQ(delegate_targets.targets[1].filename(), "tests/test_data/firmware2.txt");
230  EXPECT_EQ(delegate_targets.targets[1].length(), 17);
231  EXPECT_EQ(delegate_targets.targets[1].sha256Hash(),
232  "d8e9caba8c1697fcbade1057f9c2488044192ff76bb64d4aba2c20e53dc33033");
233  }
234  check_repo(temp_dir);
235 }
236 
237 TEST(uptane_generator, delegation_revoke) {
238  TemporaryDirectory temp_dir;
239  std::ostringstream keytype_stream;
240  keytype_stream << key_type;
241  std::string cmd = generate_repo_exec + " generate " + temp_dir.Path().string() + " --keytype " + keytype_stream.str();
242  std::string output;
243  int retval = Utils::shell(cmd, &output);
244  if (retval) {
245  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
246  }
247  cmd = generate_repo_exec + " adddelegation " + temp_dir.Path().string() + " --keytype " + keytype_stream.str();
248  cmd += " --dname test_delegate --dpattern 'tests/test_data/*.txt' ";
249  retval = Utils::shell(cmd, &output);
250  if (retval) {
251  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
252  }
253 
254  EXPECT_TRUE(boost::filesystem::exists(temp_dir.Path() / ImageRepo::dir / "delegations/test_delegate.json"));
255  auto targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
256  EXPECT_EQ(targets["signed"]["delegations"]["roles"][0]["name"].asString(), "test_delegate");
257  EXPECT_EQ(targets["signed"]["delegations"]["roles"][0]["paths"][0].asString(), "tests/test_data/*.txt");
258 
259  cmd = generate_repo_exec + " image " + temp_dir.Path().string() + " --keytype " + keytype_stream.str() +
260  " --dname test_delegate --filename tests/test_data/firmware.txt --hwid primary_hw";
261  retval = Utils::shell(cmd, &output);
262  if (retval) {
263  FAIL() << "'" << output << "' exited with error code " << retval << "\n";
264  }
265  {
266  auto test_delegate = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "delegations/test_delegate.json");
267  Uptane::Targets delegate_targets(test_delegate);
268  EXPECT_EQ(delegate_targets.targets.size(), 1);
269  EXPECT_EQ(delegate_targets.targets[0].filename(), "tests/test_data/firmware.txt");
270  EXPECT_EQ(delegate_targets.targets[0].length(), 17);
271  EXPECT_EQ(delegate_targets.targets[0].sha256Hash(),
272  "d8e9caba8c1697fcbade1057f9c2488044192ff76bb64d4aba2c20e53dc33033");
273  }
274  cmd = generate_repo_exec + " image " + temp_dir.Path().string() + " --keytype " + keytype_stream.str() +
275  " --dname test_delegate --targetname tests/test_data/firmware2.txt --targetsha256 "
276  "d8e9caba8c1697fcbade1057f9c2488044192ff76bb64d4aba2c20e53dc33033 --targetlength 17 --hwid primary_hw";
277  retval = Utils::shell(cmd, &output);
278  if (retval) {
279  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
280  }
281 
282  cmd = generate_repo_exec + " addtarget " + temp_dir.Path().string() + " --keytype " + keytype_stream.str() +
283  " --hwid primary_hw --serial CA:FE:A6:D2:84:9D --targetname tests/test_data/firmware.txt";
284  retval = Utils::shell(cmd, &output);
285  if (retval) {
286  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
287  }
288 
289  cmd = generate_repo_exec + " signtargets " + temp_dir.Path().string() + " --keytype " + keytype_stream.str();
290  retval = Utils::shell(cmd, &output);
291  if (retval) {
292  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
293  }
294  {
295  auto test_delegate = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "delegations/test_delegate.json");
296  Uptane::Targets delegate_targets(test_delegate);
297  EXPECT_EQ(delegate_targets.targets.size(), 2);
298  EXPECT_EQ(delegate_targets.targets[1].filename(), "tests/test_data/firmware2.txt");
299  EXPECT_EQ(delegate_targets.targets[1].length(), 17);
300  EXPECT_EQ(delegate_targets.targets[1].sha256Hash(),
301  "d8e9caba8c1697fcbade1057f9c2488044192ff76bb64d4aba2c20e53dc33033");
302  }
303  {
304  auto signed_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
305  Uptane::Targets director_targets(signed_targets);
306  EXPECT_EQ(director_targets.targets.size(), 1);
307  EXPECT_EQ(director_targets.targets[0].filename(), "tests/test_data/firmware.txt");
308  EXPECT_EQ(director_targets.targets[0].length(), 17);
309  EXPECT_EQ(director_targets.targets[0].sha256Hash(),
310  "d8e9caba8c1697fcbade1057f9c2488044192ff76bb64d4aba2c20e53dc33033");
311  }
312  check_repo(temp_dir);
313 
314  cmd = generate_repo_exec + " revokedelegation " + temp_dir.Path().string() + " --keytype " + keytype_stream.str() +
315  " --dname test_delegate";
316  retval = Utils::shell(cmd, &output);
317  if (retval) {
318  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
319  }
320  EXPECT_FALSE(boost::filesystem::exists(temp_dir.Path() / ImageRepo::dir / "delegations/test_delegate.json"));
321  auto new_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
322  EXPECT_EQ(new_targets["signed"]["delegations"]["keys"].size(), 0);
323  EXPECT_EQ(new_targets["signed"]["delegations"]["roles"].size(), 0);
324  EXPECT_EQ(new_targets["signed"]["version"].asUInt(), 3);
325  auto signed_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
326  Uptane::Targets director_targets(signed_targets);
327  EXPECT_EQ(director_targets.targets.size(), 0);
328 }
329 
330 /*
331  * Sign arbitrary metadata.
332  */
333 TEST(uptane_generator, sign) {
334  TemporaryDirectory temp_dir;
335  std::ostringstream keytype_stream;
336  keytype_stream << key_type;
337  std::string cmd = generate_repo_exec + " generate " + temp_dir.Path().string() + " --keytype " + keytype_stream.str();
338  std::string output;
339  int retval = Utils::shell(cmd, &output);
340  if (retval) {
341  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
342  }
343  cmd = generate_repo_exec + " sign " + temp_dir.Path().string();
344  cmd += " --repotype director --keyname snapshot";
345  std::string sign_cmd =
346  "echo \"{\\\"_type\\\":\\\"Snapshot\\\",\\\"expires\\\":\\\"2021-07-04T16:33:27Z\\\"}\" | " + cmd;
347  output.clear();
348  retval = Utils::shell(sign_cmd, &output);
349  if (retval) {
350  FAIL() << "'" << sign_cmd << "' exited with error code " << retval << "\n";
351  }
352  auto json = Utils::parseJSON(output);
353  Uptane::Root root(Uptane::RepositoryType::Director(),
354  Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "root.json"));
355  root.UnpackSignedObject(Uptane::RepositoryType::Director(), Uptane::Role::Snapshot(), json);
356  EXPECT_NO_THROW(root.UnpackSignedObject(Uptane::RepositoryType::Director(), Uptane::Role::Snapshot(), json));
357  check_repo(temp_dir);
358 }
359 
360 /*
361  * Add custom image metadata without an actual file.
362  */
363 TEST(uptane_generator, image_custom) {
364  TemporaryDirectory temp_dir;
365  std::ostringstream keytype_stream;
366  keytype_stream << key_type;
367  std::string cmd = generate_repo_exec + " generate " + temp_dir.Path().string() + " --keytype " + keytype_stream.str();
368  std::string output;
369  int retval = Utils::shell(cmd, &output);
370  if (retval) {
371  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
372  }
373  cmd = generate_repo_exec + " image " + temp_dir.Path().string() +
374  " --targetname target1 --targetsha256 8ab755c16de6ee9b6224169b36cbf0f2a545f859be385501ad82cdccc240d0a6 "
375  "--targetlength 123 --hwid primary_hw";
376  retval = Utils::shell(cmd, &output);
377  if (retval) {
378  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
379  }
380 
381  Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
382  EXPECT_EQ(image_targets["signed"]["targets"].size(), 1);
383  EXPECT_EQ(image_targets["signed"]["targets"]["target1"]["length"].asUInt(), 123);
384  EXPECT_FALSE(image_targets["signed"]["targets"]["target1"]["custom"].isMember("uri"));
385  check_repo(temp_dir);
386 }
387 
388 /*
389  * Clear the staged Director Targets metadata.
390  */
391 TEST(uptane_generator, emptytargets) {
392  TemporaryDirectory temp_dir;
393  std::ostringstream keytype_stream;
394  keytype_stream << key_type;
395  std::string cmd = generate_repo_exec + " generate " + temp_dir.Path().string() + " --keytype " + keytype_stream.str();
396  std::string output;
397  int retval = Utils::shell(cmd, &output);
398  if (retval) {
399  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
400  }
401  cmd = generate_repo_exec + " image " + temp_dir.Path().string() +
402  " --targetname target1 --targetsha256 8ab755c16de6ee9b6224169b36cbf0f2a545f859be385501ad82cdccc240d0a6 "
403  "--targetlength 123 --hwid primary_hw";
404  retval = Utils::shell(cmd, &output);
405  if (retval) {
406  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
407  }
408 
409  cmd = generate_repo_exec + " addtarget " + temp_dir.Path().string() +
410  " --targetname target1 --hwid primary_hw --serial serial123";
411  retval = Utils::shell(cmd, &output);
412  if (retval) {
413  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n";
414  }
415 
416  Json::Value targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "staging/targets.json");
417  EXPECT_EQ(targets["targets"].size(), 1);
418  EXPECT_EQ(targets["targets"]["target1"]["length"].asUInt(), 123);
419  EXPECT_EQ(targets["targets"]["target1"]["hashes"]["sha256"].asString(),
420  "8AB755C16DE6EE9B6224169B36CBF0F2A545F859BE385501AD82CDCCC240D0A6");
421 
422  cmd = generate_repo_exec + " emptytargets " + temp_dir.Path().string();
423  retval = Utils::shell(cmd, &output);
424  if (retval) {
425  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n"
426  << "output: " << output;
427  }
428 
429  Json::Value empty_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "staging/targets.json");
430  EXPECT_EQ(empty_targets["targets"].size(), 0);
431  check_repo(temp_dir);
432 }
433 
434 /*
435  * Populate the Director Targets metadata with the currently signed metadata.
436  */
437 TEST(uptane_generator, oldtargets) {
438  TemporaryDirectory temp_dir;
439  UptaneRepo repo(temp_dir.Path(), "", "");
440  repo.generateRepo(key_type);
441  Hash hash(Hash::Type::kSha256, "8ab755c16de6ee9b6224169b36cbf0f2a545f859be385501ad82cdccc240d0a6");
442  repo.addCustomImage("target1", hash, 123, "test-hw", "");
443  repo.addCustomImage("target2", hash, 321, "test-hw", "");
444  repo.addTarget("target1", "test-hw", "test-serial", "");
445  repo.signTargets();
446  repo.addTarget("target2", "test-hw", "test-serial", "");
447 
448  Json::Value targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "staging/targets.json");
449  EXPECT_EQ(targets["targets"].size(), 2);
450  EXPECT_EQ(targets["targets"]["target1"]["length"].asUInt(), 123);
451  EXPECT_EQ(targets["targets"]["target1"]["hashes"]["sha256"].asString(),
452  "8AB755C16DE6EE9B6224169B36CBF0F2A545F859BE385501AD82CDCCC240D0A6");
453  EXPECT_EQ(targets["targets"]["target2"]["length"].asUInt(), 321);
454  EXPECT_EQ(targets["targets"]["target2"]["hashes"]["sha256"].asString(),
455  "8AB755C16DE6EE9B6224169B36CBF0F2A545F859BE385501AD82CDCCC240D0A6");
456 
457  Json::Value targets_current = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
458  EXPECT_EQ(targets_current["signed"]["targets"].size(), 1);
459  EXPECT_EQ(targets_current["signed"]["targets"]["target1"]["length"].asUInt(), 123);
460  EXPECT_EQ(targets_current["signed"]["targets"]["target1"]["hashes"]["sha256"].asString(),
461  "8AB755C16DE6EE9B6224169B36CBF0F2A545F859BE385501AD82CDCCC240D0A6");
462 
463  std::string cmd = generate_repo_exec + " oldtargets " + temp_dir.Path().string();
464  std::string output;
465  int retval = Utils::shell(cmd, &output);
466  if (retval) {
467  FAIL() << "'" << cmd << "' exited with error code " << retval << "\n"
468  << "output: " << output;
469  }
470  targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "staging/targets.json");
471  EXPECT_EQ(targets["targets"].size(), 1);
472  EXPECT_EQ(targets["targets"]["target1"]["length"].asUInt(), 123);
473  EXPECT_EQ(targets["targets"]["target1"]["hashes"]["sha256"].asString(),
474  "8AB755C16DE6EE9B6224169B36CBF0F2A545F859BE385501AD82CDCCC240D0A6");
475  check_repo(temp_dir);
476 }
477 
478 /*
479  * Generate campaigns json in metadata dir.
480  */
481 TEST(uptane_generator, generateCampaigns) {
482  TemporaryDirectory temp_dir;
483  UptaneRepo repo(temp_dir.Path(), "", "");
484  repo.generateRepo(key_type);
485  repo.generateCampaigns();
486  Json::Value campaigns = Utils::parseJSONFile(temp_dir.Path() / "campaigns.json");
487 
488  EXPECT_EQ(campaigns["campaigns"][0]["name"], "campaign1");
489  EXPECT_EQ(campaigns["campaigns"][0]["id"], "c2eb7e8d-8aa0-429d-883f-5ed8fdb2a493");
490  EXPECT_EQ((campaigns["campaigns"][0]["size"]).asInt64(), 62470);
491  EXPECT_EQ(campaigns["campaigns"][0]["autoAccept"], true);
492  EXPECT_EQ(campaigns["campaigns"][0]["metadata"][0]["type"], "DESCRIPTION");
493  EXPECT_EQ(campaigns["campaigns"][0]["metadata"][0]["value"], "this is my message to show on the device");
494  EXPECT_EQ(campaigns["campaigns"][0]["metadata"][1]["type"], "ESTIMATED_INSTALLATION_DURATION");
495  EXPECT_EQ(campaigns["campaigns"][0]["metadata"][1]["value"], "10");
496  EXPECT_EQ(campaigns["campaigns"][0]["metadata"][2]["type"], "ESTIMATED_PREPARATION_DURATION");
497  EXPECT_EQ(campaigns["campaigns"][0]["metadata"][2]["value"], "20");
498 }
499 
500 /*
501  * Bump the version of the Director Root metadata.
502  */
503 TEST(uptane_generator, refreshDirectorRoot) {
504  TemporaryDirectory temp_dir;
505  UptaneRepo repo(temp_dir.Path(), "", "");
506  repo.generateRepo(key_type);
507  repo.refresh(Uptane::RepositoryType::Director(), Uptane::Role::Root());
508 
509  const Json::Value director_root = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "root.json");
510  EXPECT_EQ(director_root["signed"]["version"].asUInt(), 2);
511  const Json::Value director_timestamp = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "timestamp.json");
512  EXPECT_EQ(director_timestamp["signed"]["version"].asUInt(), 2);
513  const Json::Value director_snapshot = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "snapshot.json");
514  EXPECT_EQ(director_snapshot["signed"]["version"].asUInt(), 2);
515  const Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
516  EXPECT_EQ(director_targets["signed"]["version"].asUInt(), 1);
517 
518  const Json::Value image_root = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "root.json");
519  EXPECT_EQ(image_root["signed"]["version"].asUInt(), 1);
520  const Json::Value image_timestamp = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "timestamp.json");
521  EXPECT_EQ(image_timestamp["signed"]["version"].asUInt(), 1);
522  const Json::Value image_snapshot = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "snapshot.json");
523  EXPECT_EQ(image_snapshot["signed"]["version"].asUInt(), 1);
524  const Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
525  EXPECT_EQ(image_targets["signed"]["version"].asUInt(), 1);
526 
527  check_repo(temp_dir);
528 }
529 
530 /*
531  * Bump the version of the Director Targets metadata.
532  */
533 TEST(uptane_generator, refreshDirectorTargets) {
534  TemporaryDirectory temp_dir;
535  UptaneRepo repo(temp_dir.Path(), "", "");
536  repo.generateRepo(key_type);
537  repo.refresh(Uptane::RepositoryType::Director(), Uptane::Role::Targets());
538 
539  const Json::Value director_root = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "root.json");
540  EXPECT_EQ(director_root["signed"]["version"].asUInt(), 1);
541  const Json::Value director_timestamp = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "timestamp.json");
542  EXPECT_EQ(director_timestamp["signed"]["version"].asUInt(), 2);
543  const Json::Value director_snapshot = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "snapshot.json");
544  EXPECT_EQ(director_snapshot["signed"]["version"].asUInt(), 2);
545  const Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
546  EXPECT_EQ(director_targets["signed"]["version"].asUInt(), 2);
547 
548  const Json::Value image_root = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "root.json");
549  EXPECT_EQ(image_root["signed"]["version"].asUInt(), 1);
550  const Json::Value image_timestamp = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "timestamp.json");
551  EXPECT_EQ(image_timestamp["signed"]["version"].asUInt(), 1);
552  const Json::Value image_snapshot = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "snapshot.json");
553  EXPECT_EQ(image_snapshot["signed"]["version"].asUInt(), 1);
554  const Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
555  EXPECT_EQ(image_targets["signed"]["version"].asUInt(), 1);
556 
557  check_repo(temp_dir);
558 }
559 
560 /*
561  * Bump the version of the Image repo Root metadata.
562  */
563 TEST(uptane_generator, refreshImageRoot) {
564  TemporaryDirectory temp_dir;
565  UptaneRepo repo(temp_dir.Path(), "", "");
566  repo.generateRepo(key_type);
567  repo.refresh(Uptane::RepositoryType::Image(), Uptane::Role::Root());
568 
569  const Json::Value director_root = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "root.json");
570  EXPECT_EQ(director_root["signed"]["version"].asUInt(), 1);
571  const Json::Value director_timestamp = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "timestamp.json");
572  EXPECT_EQ(director_timestamp["signed"]["version"].asUInt(), 1);
573  const Json::Value director_snapshot = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "snapshot.json");
574  EXPECT_EQ(director_snapshot["signed"]["version"].asUInt(), 1);
575  const Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
576  EXPECT_EQ(director_targets["signed"]["version"].asUInt(), 1);
577 
578  const Json::Value image_root = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "root.json");
579  EXPECT_EQ(image_root["signed"]["version"].asUInt(), 2);
580  const Json::Value image_timestamp = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "timestamp.json");
581  EXPECT_EQ(image_timestamp["signed"]["version"].asUInt(), 2);
582  const Json::Value image_snapshot = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "snapshot.json");
583  EXPECT_EQ(image_snapshot["signed"]["version"].asUInt(), 2);
584  const Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
585  EXPECT_EQ(image_targets["signed"]["version"].asUInt(), 1);
586 
587  check_repo(temp_dir);
588 }
589 
590 /*
591  * Bump the version of the Image repo Targets metadata.
592  */
593 TEST(uptane_generator, refreshImageTargets) {
594  TemporaryDirectory temp_dir;
595  UptaneRepo repo(temp_dir.Path(), "", "");
596  repo.generateRepo(key_type);
597  repo.refresh(Uptane::RepositoryType::Image(), Uptane::Role::Targets());
598 
599  const Json::Value director_root = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "root.json");
600  EXPECT_EQ(director_root["signed"]["version"].asUInt(), 1);
601  const Json::Value director_timestamp = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "timestamp.json");
602  EXPECT_EQ(director_timestamp["signed"]["version"].asUInt(), 1);
603  const Json::Value director_snapshot = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "snapshot.json");
604  EXPECT_EQ(director_snapshot["signed"]["version"].asUInt(), 1);
605  const Json::Value director_targets = Utils::parseJSONFile(temp_dir.Path() / DirectorRepo::dir / "targets.json");
606  EXPECT_EQ(director_targets["signed"]["version"].asUInt(), 1);
607 
608  const Json::Value image_root = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "root.json");
609  EXPECT_EQ(image_root["signed"]["version"].asUInt(), 1);
610  const Json::Value image_timestamp = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "timestamp.json");
611  EXPECT_EQ(image_timestamp["signed"]["version"].asUInt(), 2);
612  const Json::Value image_snapshot = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "snapshot.json");
613  EXPECT_EQ(image_snapshot["signed"]["version"].asUInt(), 2);
614  const Json::Value image_targets = Utils::parseJSONFile(temp_dir.Path() / ImageRepo::dir / "targets.json");
615  EXPECT_EQ(image_targets["signed"]["version"].asUInt(), 2);
616 
617  check_repo(temp_dir);
618 }
619 
620 #ifndef __NO_MAIN__
621 int main(int argc, char **argv) {
622  ::testing::InitGoogleTest(&argc, argv);
623  logger_init();
624  logger_set_threshold(boost::log::trivial::trace);
625  if (argc >= 2) {
626  generate_repo_exec = argv[1];
627  } else {
628  std::cerr << "No generate-repo executable specified\n";
629  return EXIT_FAILURE;
630  }
631 
632  // note: we used to run tests for all key types, which was pretty slow.
633  // Now, they only run with ed25519
634  return RUN_ALL_TESTS();
635 }
636 #endif
Hash
The Hash class The hash of a file or Uptane metadata.
Definition: types.h:159
Uptane::Targets
Definition: tuf.h:271
TemporaryDirectory
Definition: utils.h:82
Uptane::Root
Definition: tuf.h:216
UptaneRepo
Definition: uptane_repo.h:7