1 #include "uptane/tuf.h"
7 #include <boost/algorithm/hex.hpp>
8 #include <boost/algorithm/string/case_conv.hpp>
11 #include "crypto/crypto.h"
13 #include "logging/logging.h"
14 #include "utilities/exceptions.h"
19 std::ostream &Uptane::operator<<(std::ostream &os,
const Version &v) {
20 if (v.version_ == Version::ANY_VERSION) {
23 os <<
"v" << v.version_;
28 std::ostream &Uptane::operator<<(std::ostream &os,
const HardwareIdentifier &hwid) {
33 std::ostream &Uptane::operator<<(std::ostream &os,
const EcuSerial &ecu_serial) {
34 os << ecu_serial.ecu_serial_;
38 std::string Hash::encodeVector(
const std::vector<Hash> &hashes) {
41 for (
auto it = hashes.cbegin(); it != hashes.cend(); it++) {
42 hs << it->TypeString() <<
":" << it->HashString();
43 if (std::next(it) != hashes.cend()) {
51 std::vector<Hash> Hash::decodeVector(std::string hashes_str) {
52 std::vector<Hash> hash_v;
54 std::string cs = std::move(hashes_str);
56 size_t scp = cs.find(
';');
57 std::string hash_token = cs.substr(0, scp);
58 if (scp == std::string::npos) {
61 cs = cs.substr(scp + 1);
63 if (hash_token.empty()) {
67 size_t cp = hash_token.find(
':');
68 std::string hash_type_str = hash_token.substr(0, cp);
69 if (cp == std::string::npos) {
72 std::string hash_value_str = hash_token.substr(cp + 1);
74 if (!hash_value_str.empty()) {
75 Hash h{hash_type_str, hash_value_str};
76 if (h.type() != Hash::Type::kUnknownAlgorithm) {
77 hash_v.push_back(std::move(h));
85 Target::Target(std::string filename,
const Json::Value &content) : filename_(std::move(filename)) {
86 if (content.isMember(
"custom")) {
87 custom_ = content[
"custom"];
90 if (custom_.isMember(
"hardwareIds")) {
91 Json::Value hwids = custom_[
"hardwareIds"];
92 for (
auto i = hwids.begin(); i != hwids.end(); ++i) {
93 hwids_.emplace_back(HardwareIdentifier((*i).asString()));
98 Json::Value ecus = custom_[
"ecuIdentifiers"];
99 for (
auto i = ecus.begin(); i != ecus.end(); ++i) {
100 ecus_.insert({EcuSerial(i.key().asString()), HardwareIdentifier((*i)[
"hardwareId"].asString())});
103 if (custom_.isMember(
"targetFormat")) {
104 type_ = custom_[
"targetFormat"].asString();
107 if (custom_.isMember(
"uri")) {
108 std::string custom_uri = custom_[
"uri"].asString();
110 if (custom_uri !=
"https://example.com/") {
111 uri_ = std::move(custom_uri);
116 length_ = content[
"length"].asUInt64();
118 const Json::Value hashes = content[
"hashes"];
119 for (
auto i = hashes.begin(); i != hashes.end(); ++i) {
120 Hash h(i.key().asString(), (*i).asString());
121 if (h.HaveAlgorithm()) {
122 hashes_.push_back(h);
126 std::sort(hashes_.begin(), hashes_.end(), [](
const Hash &l,
const Hash &r) { return l.type() < r.type(); });
130 Target::Target(std::string filename, EcuMap ecus, std::vector<Hash> hashes, uint64_t length, std::string correlation_id)
131 : filename_(std::move(filename)),
132 ecus_(std::move(ecus)),
133 hashes_(std::move(hashes)),
135 correlation_id_(std::move(correlation_id)) {
137 std::sort(hashes_.begin(), hashes_.end(), [](
const Hash &l,
const Hash &r) { return l.type() < r.type(); });
141 Target Target::Unknown() {
143 t_json[
"hashes"][
"sha256"] = boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest(
"")));
144 t_json[
"length"] = 0;
147 target.valid =
false;
152 bool Target::MatchHash(
const Hash &hash)
const {
153 return (std::find(hashes_.begin(), hashes_.end(), hash) != hashes_.end());
156 std::string Target::hashString(Hash::Type type)
const {
157 std::vector<Hash>::const_iterator it;
158 for (it = hashes_.begin(); it != hashes_.end(); it++) {
159 if (it->type() == type) {
160 return boost::algorithm::to_lower_copy(it->HashString());
163 return std::string(
"");
166 std::string Target::sha256Hash()
const {
return hashString(Hash::Type::kSha256); }
168 std::string Target::sha512Hash()
const {
return hashString(Hash::Type::kSha512); }
170 std::string Target::custom_version()
const {
172 return custom_[
"version"].asString();
173 }
catch (
const std::exception &ex) {
174 LOG_ERROR <<
"Unable to parse custom version: " << ex.what();
181 if (type_ ==
"OSTREE") {
184 }
else if (type_.empty() && length() == 0) {
195 bool Target::MatchTarget(
const Target &t2)
const {
202 if (filename_ != t2.filename_) {
205 if (length_ != t2.length_) {
214 if (hwids_ != t2.hwids_ || ecus_ != t2.ecus_) {
215 std::shared_ptr<EcuMap> ecu_map;
216 std::shared_ptr<std::vector<HardwareIdentifier>> hwid_vector;
217 if (!hwids_.empty() && ecus_.empty() && t2.hwids_.empty() && !t2.ecus_.empty()) {
218 ecu_map = std::make_shared<EcuMap>(t2.ecus_);
219 hwid_vector = std::make_shared<std::vector<HardwareIdentifier>>(hwids_);
220 }
else if (!t2.hwids_.empty() && t2.ecus_.empty() && hwids_.empty() && !ecus_.empty()) {
221 ecu_map = std::make_shared<EcuMap>(ecus_);
222 hwid_vector = std::make_shared<std::vector<HardwareIdentifier>>(t2.hwids_);
226 for (
auto map_it = ecu_map->cbegin(); map_it != ecu_map->cend(); ++map_it) {
227 auto vec_it = find(hwid_vector->cbegin(), hwid_vector->cend(), map_it->second);
228 if (vec_it == hwid_vector->end()) {
237 bool oneMatchingHash =
false;
238 for (
const Hash &hash : hashes_) {
239 for (
const Hash &hash2 : t2.hashes_) {
240 if (hash.type() == hash2.type() && !(hash == hash2)) {
244 oneMatchingHash =
true;
248 return oneMatchingHash;
251 Json::Value Target::toDebugJson()
const {
253 for (
const auto &ecu : ecus_) {
254 res[
"custom"][
"ecuIdentifiers"][ecu.first.ToString()][
"hardwareId"] = ecu.second.ToString();
256 if (!hwids_.empty()) {
258 for (Json::Value::ArrayIndex i = 0; i < static_cast<Json::Value::ArrayIndex>(hwids_.size()); ++i) {
259 hwids[i] = hwids_[i].ToString();
261 res[
"custom"][
"hardwareIds"] = hwids;
263 res[
"custom"][
"targetFormat"] = type_;
265 for (
const auto &hash : hashes_) {
266 res[
"hashes"][hash.TypeString()] = hash.HashString();
268 res[
"length"] = Json::Value(
static_cast<Json::Value::Int64
>(length_));
272 std::ostream &Uptane::operator<<(std::ostream &os,
const Target &t) {
273 os <<
"Target(" << t.filename_;
274 os <<
" ecu_identifiers: (";
275 for (
const auto &ecu : t.ecus_) {
276 os << ecu.first <<
" (hw_id: " << ecu.second <<
"), ";
280 for (
const auto &hwid : t.hwids_) {
284 <<
" length:" << t.length();
286 for (
const auto &hash : t.hashes_) {
294 void Uptane::BaseMeta::init(
const Json::Value &json) {
295 if (!json.isObject() || !json.isMember(
"signed")) {
296 LOG_ERROR <<
"Failure during base metadata initialization from json";
300 version_ = json[
"signed"][
"version"].asInt();
302 expiry_ =
TimeStamp(json[
"signed"][
"expires"].asString());
306 original_object_ = json;
308 Uptane::BaseMeta::BaseMeta(
const Json::Value &json) { init(json); }
310 Uptane::BaseMeta::BaseMeta(RepositoryType repo,
const Role &role,
const Json::Value &json,
311 const std::shared_ptr<MetaWithKeys> &signer) {
312 if (!json.isObject() || !json.isMember(
"signed")) {
321 void Uptane::Targets::init(
const Json::Value &json) {
322 if (!json.isObject() || json[
"signed"][
"_type"] !=
"Targets") {
326 const Json::Value target_list = json[
"signed"][
"targets"];
327 for (
auto t_it = target_list.begin(); t_it != target_list.end(); t_it++) {
328 Target t(t_it.key().asString(), *t_it);
329 targets.push_back(t);
332 if (json[
"signed"][
"delegations"].isObject()) {
333 const Json::Value key_list = json[
"signed"][
"delegations"][
"keys"];
334 ParseKeys(Uptane::RepositoryType::Image(), key_list);
336 const Json::Value role_list = json[
"signed"][
"delegations"][
"roles"];
337 for (
auto it = role_list.begin(); it != role_list.end(); it++) {
338 const std::string role_name = (*it)[
"name"].asString();
339 const Role role = Role::Delegation(role_name);
340 delegated_role_names_.push_back(role_name);
341 ParseRole(Uptane::RepositoryType::Image(), it, role, name_);
343 const Json::Value paths_list = (*it)[
"paths"];
344 std::vector<std::string> paths;
345 for (
auto p_it = paths_list.begin(); p_it != paths_list.end(); p_it++) {
346 paths.emplace_back((*p_it).asString());
348 paths_for_role_[role] = paths;
350 terminating_role_[role] = (*it)[
"terminating"].asBool();
354 if (json[
"signed"][
"custom"].isObject()) {
355 correlation_id_ = json[
"signed"][
"custom"][
"correlationId"].asString();
357 correlation_id_ =
"";
361 Uptane::Targets::Targets(
const Json::Value &json) : MetaWithKeys(json) { init(json); }
363 Uptane::Targets::Targets(RepositoryType repo,
const Role &role,
const Json::Value &json,
364 const std::shared_ptr<MetaWithKeys> &signer)
365 : MetaWithKeys(repo, role, json, signer), name_(role.ToString()) {
369 void Uptane::TimestampMeta::init(
const Json::Value &json) {
370 Json::Value hashes_list = json[
"signed"][
"meta"][
"snapshot.json"][
"hashes"];
371 Json::Value meta_size = json[
"signed"][
"meta"][
"snapshot.json"][
"length"];
372 Json::Value meta_version = json[
"signed"][
"meta"][
"snapshot.json"][
"version"];
373 if (!json.isObject() || json[
"signed"][
"_type"] !=
"Timestamp" || !hashes_list.isObject() ||
374 !meta_size.isIntegral() || !meta_version.isIntegral()) {
378 for (
auto it = hashes_list.begin(); it != hashes_list.end(); ++it) {
379 Hash h(it.key().asString(), (*it).asString());
380 snapshot_hashes_.push_back(h);
382 snapshot_size_ = meta_size.asInt();
383 snapshot_version_ = meta_version.asInt();
386 Uptane::TimestampMeta::TimestampMeta(
const Json::Value &json) : BaseMeta(json) { init(json); }
388 Uptane::TimestampMeta::TimestampMeta(RepositoryType repo,
const Json::Value &json,
389 const std::shared_ptr<MetaWithKeys> &signer)
390 : BaseMeta(repo, Role::Timestamp(), json, signer) {
394 void Uptane::Snapshot::init(
const Json::Value &json) {
395 Json::Value meta_list = json[
"signed"][
"meta"];
396 if (!json.isObject() || json[
"signed"][
"_type"] !=
"Snapshot" || !meta_list.isObject()) {
400 for (
auto it = meta_list.begin(); it != meta_list.end(); ++it) {
401 Json::Value hashes_list = (*it)[
"hashes"];
402 Json::Value meta_size = (*it)[
"length"];
403 Json::Value meta_version = (*it)[
"version"];
405 if (!meta_version.isIntegral()) {
410 it.key().asString().substr(0, it.key().asString().rfind(
'.'));
411 auto role_object = Role(role_name, !Role::IsReserved(role_name));
413 if (meta_version.isIntegral()) {
414 role_version_[role_object] = meta_version.asInt();
416 role_version_[role_object] = -1;
421 if (meta_size.isObject()) {
422 role_size_[role_object] = meta_size.asInt64();
424 role_size_[role_object] = -1;
426 if (hashes_list.isObject()) {
427 for (
auto h_it = hashes_list.begin(); h_it != hashes_list.end(); ++h_it) {
428 Hash h(h_it.key().asString(), (*h_it).asString());
429 role_hashes_[role_object].push_back(h);
435 Uptane::Snapshot::Snapshot(
const Json::Value &json) : BaseMeta(json) { init(json); }
437 Uptane::Snapshot::Snapshot(RepositoryType repo,
const Json::Value &json,
const std::shared_ptr<MetaWithKeys> &signer)
438 : BaseMeta(repo, Role::Snapshot(), json, signer) {
442 std::vector<Hash> Uptane::Snapshot::role_hashes(
const Uptane::Role &role)
const {
443 auto hashes = role_hashes_.find(role);
444 if (hashes == role_hashes_.end()) {
445 return std::vector<Hash>();
447 return hashes->second;
451 int64_t Uptane::Snapshot::role_size(
const Uptane::Role &role)
const {
452 auto size = role_size_.find(role);
453 if (size == role_size_.end()) {
460 int Uptane::Snapshot::role_version(
const Uptane::Role &role)
const {
461 auto version = role_version_.find(role);
462 if (version == role_version_.end()) {
465 return version->second;
469 int Uptane::extractVersionUntrusted(
const std::string &meta) {
470 auto version_json = Utils::parseJSON(meta)[
"signed"][
"version"];
471 if (!version_json.isIntegral()) {
474 return version_json.asInt();
478 std::string Uptane::getMetaFromBundle(
const MetaBundle &bundle,
const RepositoryType repo,
const Role &role) {
479 auto it = bundle.find(std::make_pair(repo, role));
480 if (it == bundle.end()) {
481 throw std::runtime_error(
"Metadata not found for " + role.ToString() +
" role from the " + repo.toString() +