Aktualizr
C++ SOTA Client
types.h
Go to the documentation of this file.
1 #ifndef TYPES_H_
2 #define TYPES_H_
3 /** \file */
4 
5 #include <algorithm>
6 #include <stdexcept>
7 #include <unordered_map>
8 
9 #include <boost/filesystem.hpp>
10 
11 #include "json/json.h"
12 
13 // kSharedCredReuse is intended solely for testing. It should not be used in
14 // production.
15 enum class ProvisionMode { kSharedCred = 0, kDeviceCred, kSharedCredReuse, kDefault };
16 std::ostream &operator<<(std::ostream &os, ProvisionMode mode);
17 
18 enum class StorageType { kFileSystem = 0, kSqlite };
19 std::ostream &operator<<(std::ostream &os, StorageType stype);
20 
21 namespace utils {
22 /**
23  * @brief The BasedPath class
24  * Can represent an absolute or relative path, only readable through the BasePath::get() method.
25  *
26  * The intent is to avoid unintentional use of the "naked" relative path by
27  * mandating a base directory for each instantiation.
28  *
29  * TODO has to be moved into Utils namespace
30  */
31 class BasedPath {
32  public:
33  BasedPath(boost::filesystem::path p) : p_(std::move(p)) {}
34  boost::filesystem::path get(const boost::filesystem::path &base) const;
35  bool empty() const { return p_.empty(); }
36  bool operator==(const BasedPath &b) const { return p_ == b.p_; }
37  bool operator!=(const BasedPath &b) const { return !(*this == b); }
38 
39  private:
40  boost::filesystem::path p_;
41 };
42 
43 } // namespace utils
44 
45 // Keep these in sync with AKIpUptaneKeyType ASN.1 definitions.
46 enum class KeyType {
47  kED25519 = 0,
48  kFirstKnown = kED25519,
49  kRSA2048,
50  kRSA3072,
51  kRSA4096,
52  kLastKnown = kRSA4096,
53  kUnknown = 0xff
54 };
55 
56 inline std::ostream &operator<<(std::ostream &os, const KeyType kt) {
57  std::string kt_str;
58  switch (kt) {
59  case KeyType::kRSA2048:
60  kt_str = "RSA2048";
61  break;
62  case KeyType::kRSA3072:
63  kt_str = "RSA3072";
64  break;
65  case KeyType::kRSA4096:
66  kt_str = "RSA4096";
67  break;
68  case KeyType::kED25519:
69  kt_str = "ED25519";
70  break;
71  default:
72  kt_str = "unknown";
73  break;
74  }
75  os << '"' << kt_str << '"';
76  return os;
77 }
78 
79 inline std::istream &operator>>(std::istream &is, KeyType &kt) {
80  std::string kt_str;
81 
82  is >> kt_str;
83  std::transform(kt_str.begin(), kt_str.end(), kt_str.begin(), ::toupper);
84  kt_str.erase(std::remove(kt_str.begin(), kt_str.end(), '"'), kt_str.end());
85 
86  if (kt_str == "RSA2048") {
87  kt = KeyType::kRSA2048;
88  } else if (kt_str == "RSA3072") {
89  kt = KeyType::kRSA3072;
90  } else if (kt_str == "RSA4096") {
91  kt = KeyType::kRSA4096;
92  } else if (kt_str == "ED25519") {
93  kt = KeyType::kED25519;
94  } else {
95  kt = KeyType::kUnknown;
96  }
97  return is;
98 }
99 
100 enum class CryptoSource { kFile = 0, kPkcs11 };
101 
102 inline std::ostream &operator<<(std::ostream &os, CryptoSource cs) {
103  std::string cs_str;
104  switch (cs) {
105  case CryptoSource::kFile:
106  cs_str = "file";
107  break;
108  case CryptoSource::kPkcs11:
109  cs_str = "pkcs11";
110  break;
111  default:
112  cs_str = "unknown";
113  break;
114  }
115  os << '"' << cs_str << '"';
116  return os;
117 }
118 
119 class PublicKey {
120  public:
121  PublicKey() = default;
122  explicit PublicKey(const boost::filesystem::path &path);
123 
124  explicit PublicKey(const Json::Value &uptane_json);
125 
126  PublicKey(const std::string &value, KeyType type);
127 
128  std::string Value() const { return value_; }
129 
130  KeyType Type() const { return type_; }
131  /**
132  * Verify a signature using this public key
133  */
134  bool VerifySignature(const std::string &signature, const std::string &message) const;
135  /**
136  * Uptane Json representation of this public key. Used in root.json
137  * and during provisioning.
138  */
139  Json::Value ToUptane() const;
140 
141  std::string KeyId() const;
142  bool operator==(const PublicKey &rhs) const;
143 
144  bool operator!=(const PublicKey &rhs) const { return !(*this == rhs); }
145 
146  private:
147  // std::string can be implicitly converted to a Json::Value. Make sure that
148  // the Json::Value constructor is not called accidentally.
149  PublicKey(std::string);
150  std::string value_;
151  KeyType type_{KeyType::kUnknown};
152 };
153 
154 /**
155  * @brief The Hash class The hash of a file or Uptane metadata.
156  * File hashes/checksums in Uptane include the length of the object,
157  * in order to defeat infinite download attacks.
158  */
159 class Hash {
160  public:
161  // order corresponds algorithm priority
162  enum class Type { kSha256, kSha512, kUnknownAlgorithm };
163 
164  static Hash generate(Type type, const std::string &data);
165  Hash(const std::string &type, const std::string &hash);
166  Hash(Type type, const std::string &hash);
167 
168  bool HaveAlgorithm() const { return type_ != Type::kUnknownAlgorithm; }
169  bool operator==(const Hash &other) const;
170  bool operator!=(const Hash &other) const { return !operator==(other); }
171  static std::string TypeString(Type type);
172  std::string TypeString() const;
173  Type type() const;
174  std::string HashString() const { return hash_; }
175  friend std::ostream &operator<<(std::ostream &os, const Hash &h);
176 
177  static std::string encodeVector(const std::vector<Hash> &hashes);
178  static std::vector<Hash> decodeVector(std::string hashes_str);
179 
180  private:
181  Type type_;
182  std::string hash_;
183 };
184 
185 std::ostream &operator<<(std::ostream &os, const Hash &h);
186 
187 // timestamp, compatible with tuf
188 class TimeStamp {
189  public:
190  static TimeStamp Now();
191  static struct tm CurrentTime();
192  /** An invalid TimeStamp */
194  explicit TimeStamp(std::string rfc3339);
195  explicit TimeStamp(struct tm time);
196  bool IsExpiredAt(const TimeStamp &now) const;
197  bool IsValid() const;
198  std::string ToString() const { return time_; }
199  bool operator<(const TimeStamp &other) const;
200  bool operator>(const TimeStamp &other) const;
201  friend std::ostream &operator<<(std::ostream &os, const TimeStamp &t);
202  bool operator==(const TimeStamp &rhs) const { return time_ == rhs.time_; }
203 
204  class InvalidTimeStamp : public std::domain_error {
205  public:
206  InvalidTimeStamp() : std::domain_error("invalid timestamp") {}
207  ~InvalidTimeStamp() noexcept override = default;
208  };
209 
210  private:
211  std::string time_;
212 };
213 
214 std::ostream &operator<<(std::ostream &os, const TimeStamp &t);
215 
216 /// General data structures.
217 namespace data {
218 
219 struct ResultCode {
220  // Keep these in sync with AKInstallationResultCode ASN.1 definitions.
221  enum class Numeric {
222  kOk = 0,
223  /// Operation has already been processed
224  kAlreadyProcessed = 1,
225  /// Metadata verification failed
227  /// Package installation failed
228  kInstallFailed = 4,
229  /// Package download failed
230  kDownloadFailed = 5,
231  /// SWM Internal integrity error
232  kInternalError = 18,
233  /// Other error
234  kGeneralError = 19,
235  // Install needs to be finalized (e.g: reboot)
236  kNeedCompletion = 21,
237  // Customer specific
238  kCustomError = 22,
239  // Unknown
240  kUnknown = -1,
241  };
242 
243  // note: intentionally *not* explicit, to make the common case easier
244  ResultCode(ResultCode::Numeric in_num_code) : num_code(in_num_code) {}
245  ResultCode(ResultCode::Numeric in_num_code, std::string text_code_in)
246  : num_code(in_num_code), text_code(std::move(text_code_in)) {}
247 
248  bool operator==(const ResultCode &rhs) const { return num_code == rhs.num_code && toString() == rhs.toString(); }
249  bool operator!=(const ResultCode &rhs) const { return !(*this == rhs); }
250  friend std::ostream &operator<<(std::ostream &os, const ResultCode &result_code);
251 
252  Numeric num_code;
253  std::string text_code;
254 
255  // Allows to have a numeric code with a default representation, but also with
256  // any string representation. This is specifically useful for campaign success
257  // analysis, because the device installation report concatenates the
258  // individual ECU ResultCodes.
259  std::string toString() const {
260  if (text_code != "") {
261  return text_code;
262  }
263 
264  return std::string(string_repr.at(num_code));
265  }
266 
267  // non-lossy reprensation for serialization
268  std::string toRepr() const;
269  static ResultCode fromRepr(const std::string &repr);
270 
271  private:
272  static const std::map<Numeric, const char *> string_repr;
273 };
274 
275 std::ostream &operator<<(std::ostream &os, const ResultCode &result_code);
276 
278  InstallationResult() = default;
279  InstallationResult(ResultCode result_code_in, std::string description_in)
280  : success(result_code_in.num_code == ResultCode::Numeric::kOk ||
281  result_code_in.num_code == ResultCode::Numeric::kAlreadyProcessed),
282  result_code(std::move(result_code_in)),
283  description(std::move(description_in)) {}
284  InstallationResult(bool success_in, ResultCode result_code_in, std::string description_in)
285  : success(success_in), result_code(std::move(result_code_in)), description(std::move(description_in)) {}
286 
287  Json::Value toJson() const;
288  bool isSuccess() const { return success; };
289  bool needCompletion() const { return result_code == ResultCode::Numeric::kNeedCompletion; }
290 
291  bool success{true};
292  ResultCode result_code{ResultCode::Numeric::kOk};
293  std::string description;
294 };
295 
296 } // namespace data
297 
298 namespace Uptane {
299 
300 class RepositoryType;
301 class Role;
302 struct MetaPairHash;
303 
304 using MetaBundle = std::unordered_map<std::pair<RepositoryType, Role>, std::string, MetaPairHash>;
305 
307  InstalledImageInfo() : name{""} {}
308  InstalledImageInfo(std::string name_in, uint64_t len_in, std::string hash_in)
309  : name(std::move(name_in)), len(len_in), hash(std::move(hash_in)) {}
310  std::string name;
311  uint64_t len{0};
312  std::string hash;
313 };
314 
316  public:
317  // https://github.com/advancedtelematic/ota-tuf/blob/master/libtuf/src/main/scala/com/advancedtelematic/libtuf/data/TufDataType.scala
318  static const int kMinLength = 0;
319  static const int kMaxLength = 200;
320 
321  static HardwareIdentifier Unknown() { return HardwareIdentifier("Unknown"); }
322  explicit HardwareIdentifier(const std::string &hwid) : hwid_(hwid) {
323  /* if (hwid.length() < kMinLength) {
324  throw std::out_of_range("Hardware Identifier too short");
325  } */
326  if (kMaxLength < hwid.length()) {
327  throw std::out_of_range("Hardware Identifier too long");
328  }
329  }
330 
331  std::string ToString() const { return hwid_; }
332 
333  bool operator==(const HardwareIdentifier &rhs) const { return hwid_ == rhs.hwid_; }
334  bool operator!=(const HardwareIdentifier &rhs) const { return !(*this == rhs); }
335 
336  bool operator<(const HardwareIdentifier &rhs) const { return hwid_ < rhs.hwid_; }
337  friend std::ostream &operator<<(std::ostream &os, const HardwareIdentifier &hwid);
338  friend struct std::hash<Uptane::HardwareIdentifier>;
339 
340  private:
341  std::string hwid_;
342 };
343 
344 std::ostream &operator<<(std::ostream &os, const HardwareIdentifier &hwid);
345 
346 class EcuSerial {
347  public:
348  // https://github.com/advancedtelematic/ota-tuf/blob/master/libtuf/src/main/scala/com/advancedtelematic/libtuf/data/TufDataType.scala
349  static const int kMinLength = 1;
350  static const int kMaxLength = 64;
351 
352  static EcuSerial Unknown() { return EcuSerial("Unknown"); }
353  explicit EcuSerial(const std::string &ecu_serial) : ecu_serial_(ecu_serial) {
354  if (ecu_serial.length() < kMinLength) {
355  throw std::out_of_range("ECU serial identifier is too short");
356  }
357  if (kMaxLength < ecu_serial.length()) {
358  throw std::out_of_range("ECU serial identifier is too long");
359  }
360  }
361 
362  std::string ToString() const { return ecu_serial_; }
363 
364  bool operator==(const EcuSerial &rhs) const { return ecu_serial_ == rhs.ecu_serial_; }
365  bool operator!=(const EcuSerial &rhs) const { return !(*this == rhs); }
366 
367  bool operator<(const EcuSerial &rhs) const { return ecu_serial_ < rhs.ecu_serial_; }
368  friend std::ostream &operator<<(std::ostream &os, const EcuSerial &ecu_serial);
369  friend struct std::hash<Uptane::EcuSerial>;
370 
371  private:
372  std::string ecu_serial_;
373 };
374 
375 std::ostream &operator<<(std::ostream &os, const EcuSerial &ecu_serial);
376 
377 using EcuMap = std::map<EcuSerial, HardwareIdentifier>;
378 
379 class Target {
380  public:
381  // From Uptane metadata
382  Target(std::string filename, const Json::Value &content);
383  // Internal use only. Only used for reading installed_versions list and by
384  // various tests.
385  Target(std::string filename, EcuMap ecus, std::vector<Hash> hashes, uint64_t length, std::string correlation_id = "");
386 
387  static Target Unknown();
388 
389  const EcuMap &ecus() const { return ecus_; }
390  std::string filename() const { return filename_; }
391  std::string sha256Hash() const;
392  std::string sha512Hash() const;
393  const std::vector<Hash> &hashes() const { return hashes_; }
394  const std::vector<HardwareIdentifier> &hardwareIds() const { return hwids_; }
395  std::string custom_version() const;
396  Json::Value custom_data() const { return custom_; }
397  void updateCustom(Json::Value &custom) { custom_ = custom; }
398  std::string correlation_id() const { return correlation_id_; }
399  void setCorrelationId(std::string correlation_id) { correlation_id_ = std::move(correlation_id); }
400  uint64_t length() const { return length_; }
401  bool IsValid() const { return valid; }
402  std::string uri() const { return uri_; }
403  void setUri(std::string uri) { uri_ = std::move(uri); }
404  bool MatchHash(const Hash &hash) const;
405 
406  void InsertEcu(const std::pair<EcuSerial, HardwareIdentifier> &pair) { ecus_.insert(pair); }
407 
408  bool IsForEcu(const EcuSerial &ecuIdentifier) const {
409  return (std::find_if(ecus_.cbegin(), ecus_.cend(),
410  [&ecuIdentifier](const std::pair<EcuSerial, HardwareIdentifier> &pair) {
411  return pair.first == ecuIdentifier;
412  }) != ecus_.cend());
413  }
414 
415  /**
416  * Is this an OSTree target?
417  * OSTree targets need special treatment because the hash doesn't represent
418  * the contents of the update itself, instead it is the hash (name) of the
419  * root commit object.
420  */
421  bool IsOstree() const;
422  std::string type() const { return type_; }
423 
424  // Comparison is usually not meaningful. Use MatchTarget instead.
425  bool operator==(const Target &t2) = delete;
426  bool MatchTarget(const Target &t2) const;
427  Json::Value toDebugJson() const;
428  friend std::ostream &operator<<(std::ostream &os, const Target &t);
429  InstalledImageInfo getTargetImageInfo() const { return {filename(), length(), sha256Hash()}; }
430 
431  private:
432  bool valid{true};
433  std::string filename_;
434  std::string type_;
435  EcuMap ecus_; // Director only
436  std::vector<Hash> hashes_;
437  std::vector<HardwareIdentifier> hwids_; // Image repo only
438  Json::Value custom_;
439  uint64_t length_{0};
440  std::string correlation_id_;
441  std::string uri_;
442 
443  std::string hashString(Hash::Type type) const;
444 };
445 
446 std::ostream &operator<<(std::ostream &os, const Target &t);
447 
448 class Manifest : public Json::Value {
449  public:
450  Manifest(const Json::Value &value = Json::Value()) : Json::Value(value) {}
451 
452  public:
453  std::string filepath() const;
454  Hash installedImageHash() const;
455  std::string signature() const;
456  std::string signedBody() const;
457  bool verifySignature(const PublicKey &pub_key) const;
458 };
459 
460 } // namespace Uptane
461 
462 struct SecondaryInfo {
463  SecondaryInfo() : serial(Uptane::EcuSerial::Unknown()), hw_id(Uptane::HardwareIdentifier::Unknown()) {}
464  SecondaryInfo(Uptane::EcuSerial serial_in, Uptane::HardwareIdentifier hw_id_in, std::string type_in,
465  PublicKey pub_key_in, std::string extra_in)
466  : serial(std::move(serial_in)),
467  hw_id(std::move(hw_id_in)),
468  type(std::move(type_in)),
469  pub_key(std::move(pub_key_in)),
470  extra(std::move(extra_in)) {}
471 
472  Uptane::EcuSerial serial;
473  Uptane::HardwareIdentifier hw_id;
474  std::string type;
475  PublicKey pub_key;
476 
477  std::string extra;
478 };
479 
480 #endif
Hash
The Hash class The hash of a file or Uptane metadata.
Definition: types.h:159
data::ResultCode
Definition: types.h:219
data::ResultCode::Numeric::kVerificationFailed
@ kVerificationFailed
Metadata verification failed.
data::ResultCode::Numeric::kGeneralError
@ kGeneralError
Other error.
data::ResultCode::Numeric::kAlreadyProcessed
@ kAlreadyProcessed
Operation has already been processed.
data::InstallationResult
Definition: types.h:277
PublicKey::ToUptane
Json::Value ToUptane() const
Uptane Json representation of this public key.
Definition: crypto.cc:86
TimeStamp::InvalidTimeStamp
Definition: types.h:204
TimeStamp::TimeStamp
TimeStamp()
An invalid TimeStamp.
Definition: types.h:193
data
General data structures.
Definition: types.h:217
Uptane::HardwareIdentifier
Definition: types.h:315
utils::BasedPath
The BasedPath class Can represent an absolute or relative path, only readable through the BasePath::g...
Definition: types.h:31
TimeStamp
Definition: types.h:188
Uptane::InstalledImageInfo
Definition: types.h:306
PublicKey
Definition: types.h:119
data::ResultCode::Numeric::kInternalError
@ kInternalError
SWM Internal integrity error.
data::ResultCode::Numeric
Numeric
Definition: types.h:221
Uptane
Base data types that are used in The Update Framework (TUF), part of Uptane.
Definition: packagemanagerinterface.h:18
data::ResultCode::Numeric::kDownloadFailed
@ kDownloadFailed
Package download failed.
PublicKey::VerifySignature
bool VerifySignature(const std::string &signature, const std::string &message) const
Verify a signature using this public key.
Definition: crypto.cc:72
data::ResultCode::Numeric::kInstallFailed
@ kInstallFailed
Package installation failed.