Aktualizr
C++ SOTA Client
directorrepository.cc
1 #include "directorrepository.h"
2 
3 namespace Uptane {
4 
5 void DirectorRepository::resetMeta() {
6  resetRoot();
7  targets = Targets();
8  latest_targets = Targets();
9 }
10 
11 void DirectorRepository::checkTargetsExpired() {
12  if (latest_targets.isExpired(TimeStamp::Now())) {
13  throw Uptane::ExpiredMetadata(type.toString(), Role::TARGETS);
14  }
15 }
16 
17 void DirectorRepository::targetsSanityCheck() {
18  // 5.4.4.6.6. If checking Targets metadata from the Director repository,
19  // verify that there are no delegations.
20  if (!latest_targets.delegated_role_names_.empty()) {
21  throw Uptane::InvalidMetadata(type.toString(), Role::TARGETS, "Found unexpected delegation.");
22  }
23  // 5.4.4.6.7. If checking Targets metadata from the Director repository,
24  // check that no ECU identifier is represented more than once.
25  std::set<Uptane::EcuSerial> ecu_ids;
26  for (const auto& target : targets.targets) {
27  for (const auto& ecu : target.ecus()) {
28  if (ecu_ids.find(ecu.first) == ecu_ids.end()) {
29  ecu_ids.insert(ecu.first);
30  } else {
31  LOG_ERROR << "ECU " << ecu.first << " appears twice in Director's Targets";
32  throw Uptane::InvalidMetadata(type.toString(), Role::TARGETS, "Found repeated ECU ID.");
33  }
34  }
35  }
36 }
37 
38 bool DirectorRepository::usePreviousTargets() const {
39  // Don't store the new targets if they are empty and we've previously received
40  // a non-empty list.
41  return !targets.targets.empty() && latest_targets.targets.empty();
42 }
43 
44 void DirectorRepository::verifyTargets(const std::string& targets_raw) {
45  try {
46  // Verify the signature:
47  latest_targets = Targets(RepositoryType::Director(), Role::Targets(), Utils::parseJSON(targets_raw),
48  std::make_shared<MetaWithKeys>(root));
49  if (!usePreviousTargets()) {
50  targets = latest_targets;
51  }
52  } catch (const Uptane::Exception& e) {
53  LOG_ERROR << "Signature verification for Director Targets metadata failed";
54  throw;
55  }
56 }
57 
58 void DirectorRepository::checkMetaOffline(INvStorage& storage) {
59  resetMeta();
60  // Load Director Root Metadata
61  {
62  std::string director_root;
63  if (!storage.loadLatestRoot(&director_root, RepositoryType::Director())) {
64  throw Uptane::SecurityException(RepositoryType::DIRECTOR, "Could not load latest root");
65  }
66 
67  initRoot(RepositoryType(RepositoryType::DIRECTOR), director_root);
68 
69  if (rootExpired()) {
70  throw Uptane::ExpiredMetadata(RepositoryType::DIRECTOR, Role::ROOT);
71  }
72  }
73 
74  // Load Director Targets Metadata
75  {
76  std::string director_targets;
77 
78  if (!storage.loadNonRoot(&director_targets, RepositoryType::Director(), Role::Targets())) {
79  throw Uptane::SecurityException(RepositoryType::DIRECTOR, "Could not load Targets role");
80  }
81 
82  verifyTargets(director_targets);
83 
84  checkTargetsExpired();
85 
86  targetsSanityCheck();
87  }
88 }
89 
90 void DirectorRepository::updateMeta(INvStorage& storage, const IMetadataFetcher& fetcher) {
91  // Uptane step 2 (download time) is not implemented yet.
92  // Uptane step 3 (download metadata)
93 
94  // reset Director repo to initial state before starting Uptane iteration
95  resetMeta();
96 
97  updateRoot(storage, fetcher, RepositoryType::Director());
98 
99  // Not supported: 3. Download and check the Timestamp metadata file from the Director repository, following the
100  // procedure in Section 5.4.4.4. Not supported: 4. Download and check the Snapshot metadata file from the Director
101  // repository, following the procedure in Section 5.4.4.5.
102 
103  // Update Director Targets Metadata
104  {
105  std::string director_targets;
106 
107  fetcher.fetchLatestRole(&director_targets, kMaxDirectorTargetsSize, RepositoryType::Director(), Role::Targets());
108  int remote_version = extractVersionUntrusted(director_targets);
109 
110  int local_version;
111  std::string director_targets_stored;
112  if (storage.loadNonRoot(&director_targets_stored, RepositoryType::Director(), Role::Targets())) {
113  local_version = extractVersionUntrusted(director_targets_stored);
114  try {
115  verifyTargets(director_targets_stored);
116  } catch (const std::exception& e) {
117  LOG_WARNING << "Unable to verify stored Director Targets metadata.";
118  }
119  } else {
120  local_version = -1;
121  }
122 
123  verifyTargets(director_targets);
124 
125  // TODO(OTA-4940): check if versions are equal but content is different. In
126  // that case, the member variable targets is updated, but it isn't stored in
127  // the database, which can cause some minor confusion.
128  if (local_version > remote_version) {
129  throw Uptane::SecurityException(RepositoryType::DIRECTOR, "Rollback attempt");
130  } else if (local_version < remote_version && !usePreviousTargets()) {
131  storage.storeNonRoot(director_targets, RepositoryType::Director(), Role::Targets());
132  }
133 
134  checkTargetsExpired();
135 
136  targetsSanityCheck();
137  }
138 }
139 
140 void DirectorRepository::dropTargets(INvStorage& storage) {
141  try {
142  storage.clearNonRootMeta(RepositoryType::Director());
143  resetMeta();
144  } catch (const Uptane::Exception& ex) {
145  LOG_ERROR << "Failed to reset Director Targets metadata: " << ex.what();
146  }
147 }
148 
149 bool DirectorRepository::matchTargetsWithImageTargets(const Uptane::Targets& image_targets) const {
150  // step 10 of https://uptane.github.io/papers/ieee-isto-6100.1.0.0.uptane-standard.html#rfc.section.5.4.4.2
151  // TODO(OTA-4800): support delegations. Consider reusing findTargetInDelegationTree(),
152  // but it would need to be moved into a common place to be resued by Primary and Secondary.
153  // Currently this is only used by aktualizr-secondary, but according to the
154  // Standard, "A Secondary ECU MAY elect to perform this check only on the
155  // metadata for the image it will install".
156  const auto& image_target_array = image_targets.targets;
157  const auto& director_target_array = targets.targets;
158 
159  for (const auto& director_target : director_target_array) {
160  auto found_it = std::find_if(
161  image_target_array.begin(), image_target_array.end(),
162  [&director_target](const Target& image_target) { return director_target.MatchTarget(image_target); });
163 
164  if (found_it == image_target_array.end()) {
165  return false;
166  }
167  }
168 
169  return true;
170 }
171 
172 } // namespace Uptane
Uptane::InvalidMetadata
Definition: exceptions.h:81
Uptane::ExpiredMetadata
Definition: exceptions.h:74
Uptane::Targets
Definition: tuf.h:271
Uptane::Exception
Definition: exceptions.h:10
Uptane
Base data types that are used in The Update Framework (TUF), part of Uptane.
Definition: packagemanagerinterface.h:18
INvStorage
Definition: invstorage.h:43
Uptane::SecurityException
Definition: exceptions.h:28