Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
imagesrepository.cc
1 #include "imagesrepository.h"
2 
3 namespace Uptane {
4 
5 void ImagesRepository::resetMeta() {
6  resetRoot();
7  targets.reset();
8  snapshot = Snapshot();
9  timestamp = TimestampMeta();
10 }
11 
12 bool ImagesRepository::verifyTimestamp(const std::string& timestamp_raw) {
13  try {
14  // Verify the signature:
15  timestamp =
16  TimestampMeta(RepositoryType::Image(), Utils::parseJSON(timestamp_raw), std::make_shared<MetaWithKeys>(root));
17  } catch (const Exception& e) {
18  LOG_ERROR << "Signature verification for timestamp metadata failed";
19  last_exception = e;
20  return false;
21  }
22  return true;
23 }
24 
25 bool ImagesRepository::fetchSnapshot(INvStorage& storage, const IMetadataFetcher& fetcher, const int local_version) {
26  std::string images_snapshot;
27  const int64_t snapshot_size = (snapshotSize() > 0) ? snapshotSize() : kMaxSnapshotSize;
28  if (!fetcher.fetchLatestRole(&images_snapshot, snapshot_size, RepositoryType::Image(), Role::Snapshot())) {
29  return false;
30  }
31  const int remote_version = extractVersionUntrusted(images_snapshot);
32 
33  // 6. Check that each Targets metadata filename listed in the previous Snapshot metadata file is also listed in this
34  // Snapshot metadata file. If this condition is not met, discard the new Snapshot metadata file, abort the update
35  // cycle, and report the failure. (Checks for a rollback attack.)
36  // Let's wait for this ticket resolution https://github.com/uptane/uptane-standard/issues/149
37  // https://saeljira.it.here.com/browse/OTA-4121
38  if (!verifySnapshot(images_snapshot)) {
39  return false;
40  }
41 
42  if (local_version > remote_version) {
43  return false;
44  } else if (local_version < remote_version) {
45  storage.storeNonRoot(images_snapshot, RepositoryType::Image(), Role::Snapshot());
46  }
47 
48  return true;
49 }
50 
51 bool ImagesRepository::verifySnapshot(const std::string& snapshot_raw) {
52  try {
53  const std::string canonical = Utils::jsonToCanonicalStr(Utils::parseJSON(snapshot_raw));
54  bool hash_exists = false;
55  for (const auto& it : timestamp.snapshot_hashes()) {
56  switch (it.type()) {
57  case Hash::Type::kSha256:
58  if (Hash(Hash::Type::kSha256, boost::algorithm::hex(Crypto::sha256digest(canonical))) != it) {
59  LOG_ERROR << "Hash verification for snapshot metadata failed";
60  return false;
61  }
62  hash_exists = true;
63  break;
64  case Hash::Type::kSha512:
65  if (Hash(Hash::Type::kSha512, boost::algorithm::hex(Crypto::sha512digest(canonical))) != it) {
66  LOG_ERROR << "Hash verification for snapshot metadata failed";
67  return false;
68  }
69  hash_exists = true;
70  break;
71  default:
72  break;
73  }
74  }
75  if (!hash_exists) {
76  LOG_ERROR << "No hash found for shapshot.json";
77  return false;
78  }
79  // Verify the signature:
80  snapshot = Snapshot(RepositoryType::Image(), Utils::parseJSON(snapshot_raw), std::make_shared<MetaWithKeys>(root));
81  if (snapshot.version() != timestamp.snapshot_version()) {
82  return false;
83  }
84  } catch (const Exception& e) {
85  LOG_ERROR << "Signature verification for snapshot metadata failed";
86  last_exception = e;
87  return false;
88  }
89  return true;
90 }
91 
92 bool ImagesRepository::fetchTargets(INvStorage& storage, const IMetadataFetcher& fetcher, const int local_version) {
93  std::string images_targets;
94  const Role targets_role = Role::Targets();
95 
96  auto targets_size = getRoleSize(Role::Targets());
97  if (targets_size <= 0) {
98  targets_size = kMaxImagesTargetsSize;
99  }
100  if (!fetcher.fetchLatestRole(&images_targets, targets_size, RepositoryType::Image(), targets_role)) {
101  return false;
102  }
103  const int remote_version = extractVersionUntrusted(images_targets);
104 
105  if (!verifyTargets(images_targets)) {
106  return false;
107  }
108 
109  if (local_version > remote_version) {
110  return false;
111  } else if (local_version < remote_version) {
112  storage.storeNonRoot(images_targets, RepositoryType::Image(), targets_role);
113  }
114 
115  return true;
116 }
117 
118 bool ImagesRepository::verifyRoleHashes(const std::string& role_data, const Uptane::Role& role) const {
119  const std::string canonical = Utils::jsonToCanonicalStr(Utils::parseJSON(role_data));
120  // Hashes are not required. If present, however, we may as well check them.
121  // This provides no security benefit, but may help with fault detection.
122  for (const auto& it : snapshot.role_hashes(role)) {
123  switch (it.type()) {
124  case Hash::Type::kSha256:
125  if (Hash(Hash::Type::kSha256, boost::algorithm::hex(Crypto::sha256digest(canonical))) != it) {
126  LOG_ERROR << "Hash verification for " << role.ToString() << " metadata failed";
127  return false;
128  }
129  break;
130  case Hash::Type::kSha512:
131  if (Hash(Hash::Type::kSha512, boost::algorithm::hex(Crypto::sha512digest(canonical))) != it) {
132  LOG_ERROR << "Hash verification for " << role.ToString() << " metadata failed";
133  return false;
134  }
135  break;
136  default:
137  break;
138  }
139  }
140 
141  return true;
142 }
143 
144 int ImagesRepository::getRoleVersion(const Uptane::Role& role) const { return snapshot.role_version(role); }
145 
146 int64_t ImagesRepository::getRoleSize(const Uptane::Role& role) const { return snapshot.role_size(role); }
147 
148 bool ImagesRepository::verifyTargets(const std::string& targets_raw) {
149  try {
150  if (!verifyRoleHashes(targets_raw, Uptane::Role::Targets())) {
151  return false;
152  }
153 
154  auto targets_json = Utils::parseJSON(targets_raw);
155 
156  // Verify the signature:
157  auto signer = std::make_shared<MetaWithKeys>(root);
158  targets = std::make_shared<Uptane::Targets>(
159  Targets(RepositoryType::Image(), Uptane::Role::Targets(), targets_json, signer));
160 
161  if (targets->version() != snapshot.role_version(Uptane::Role::Targets())) {
162  return false;
163  }
164  } catch (const Exception& e) {
165  LOG_ERROR << "Signature verification for images targets metadata failed";
166  last_exception = e;
167  return false;
168  }
169  return true;
170 }
171 
172 std::shared_ptr<Uptane::Targets> ImagesRepository::verifyDelegation(const std::string& delegation_raw,
173  const Uptane::Role& role,
174  const Targets& parent_target) {
175  try {
176  const Json::Value delegation_json = Utils::parseJSON(delegation_raw);
177  const std::string canonical = Utils::jsonToCanonicalStr(delegation_json);
178 
179  // Verify the signature:
180  auto signer = std::make_shared<MetaWithKeys>(parent_target);
181  return std::make_shared<Uptane::Targets>(Targets(RepositoryType::Image(), role, delegation_json, signer));
182  } catch (const Exception& e) {
183  LOG_ERROR << "Signature verification for images delegated targets metadata failed";
184  throw e;
185  }
186 
187  return std::shared_ptr<Uptane::Targets>(nullptr);
188 }
189 
190 bool ImagesRepository::updateMeta(INvStorage& storage, const IMetadataFetcher& fetcher) {
191  resetMeta();
192 
193  if (!updateRoot(storage, fetcher, RepositoryType::Image())) {
194  return false;
195  }
196 
197  // Update Images Timestamp Metadata
198  {
199  std::string images_timestamp;
200 
201  if (!fetcher.fetchLatestRole(&images_timestamp, kMaxTimestampSize, RepositoryType::Image(), Role::Timestamp())) {
202  return false;
203  }
204  int remote_version = extractVersionUntrusted(images_timestamp);
205 
206  int local_version;
207  std::string images_timestamp_stored;
208  if (storage.loadNonRoot(&images_timestamp_stored, RepositoryType::Image(), Role::Timestamp())) {
209  local_version = extractVersionUntrusted(images_timestamp_stored);
210  } else {
211  local_version = -1;
212  }
213 
214  if (!verifyTimestamp(images_timestamp)) {
215  return false;
216  }
217 
218  if (local_version > remote_version) {
219  return false;
220  } else if (local_version < remote_version) {
221  storage.storeNonRoot(images_timestamp, RepositoryType::Image(), Role::Timestamp());
222  }
223 
224  if (timestampExpired()) {
225  return false;
226  }
227  }
228 
229  // Update Images Snapshot Metadata
230  {
231  // First check if we already have the latest version according to the
232  // Timestamp metadata.
233  bool fetch_snapshot = true;
234  int local_version;
235  std::string images_snapshot_stored;
236  if (storage.loadNonRoot(&images_snapshot_stored, RepositoryType::Image(), Role::Snapshot())) {
237  if (verifySnapshot(images_snapshot_stored)) {
238  fetch_snapshot = false;
239  LOG_DEBUG << "Skipping Image repo Snapshot download; stored version is still current.";
240  }
241  local_version = snapshot.version();
242  } else {
243  local_version = -1;
244  }
245 
246  // If we don't, attempt to fetch the latest.
247  if (fetch_snapshot) {
248  if (!fetchSnapshot(storage, fetcher, local_version)) {
249  return false;
250  }
251  }
252 
253  if (snapshotExpired()) {
254  return false;
255  }
256  }
257 
258  // Update Images Targets Metadata
259  {
260  // First check if we already have the latest version according to the
261  // Snapshot metadata.
262  bool fetch_targets = true;
263  int local_version = -1;
264  std::string images_targets_stored;
265  if (storage.loadNonRoot(&images_targets_stored, RepositoryType::Image(), Role::Targets())) {
266  if (verifyTargets(images_targets_stored)) {
267  fetch_targets = false;
268  LOG_DEBUG << "Skipping Image repo Targets download; stored version is still current.";
269  }
270  if (targets) {
271  local_version = targets->version();
272  }
273  }
274 
275  // If we don't, attempt to fetch the latest.
276  if (fetch_targets) {
277  if (!fetchTargets(storage, fetcher, local_version)) {
278  return false;
279  }
280  }
281 
282  if (targetsExpired()) {
283  return false;
284  }
285  }
286 
287  return true;
288 }
289 
290 bool ImagesRepository::checkMetaOffline(INvStorage& storage) {
291  resetMeta();
292  // Load Images Root Metadata
293  {
294  std::string images_root;
295  if (!storage.loadLatestRoot(&images_root, RepositoryType::Image())) {
296  return false;
297  }
298 
299  if (!initRoot(images_root)) {
300  return false;
301  }
302 
303  if (rootExpired()) {
304  return false;
305  }
306  }
307 
308  // Load Images Timestamp Metadata
309  {
310  std::string images_timestamp;
311  if (!storage.loadNonRoot(&images_timestamp, RepositoryType::Image(), Role::Timestamp())) {
312  return false;
313  }
314 
315  if (!verifyTimestamp(images_timestamp)) {
316  return false;
317  }
318 
319  if (timestampExpired()) {
320  return false;
321  }
322  }
323 
324  // Load Images Snapshot Metadata
325  {
326  std::string images_snapshot;
327 
328  if (!storage.loadNonRoot(&images_snapshot, RepositoryType::Image(), Role::Snapshot())) {
329  return false;
330  }
331 
332  if (!verifySnapshot(images_snapshot)) {
333  return false;
334  }
335 
336  if (snapshotExpired()) {
337  return false;
338  }
339  }
340 
341  // Load Images Targets Metadata
342  {
343  std::string images_targets;
344  Role targets_role = Uptane::Role::Targets();
345  if (!storage.loadNonRoot(&images_targets, RepositoryType::Image(), targets_role)) {
346  return false;
347  }
348 
349  if (!verifyTargets(images_targets)) {
350  return false;
351  }
352 
353  if (targetsExpired()) {
354  return false;
355  }
356  }
357 
358  return true;
359 }
360 
361 } // namespace Uptane
Uptane::Role
TUF Roles.
Definition: tuf.h:57
Uptane
Base data types that are used in The Update Framework (TUF), part of UPTANE.
Definition: secondary_tcp_server.h:8
INvStorage
Definition: invstorage.h:109