1 #include "uptane/tuf.h"
7 #include <boost/algorithm/hex.hpp>
8 #include <boost/algorithm/string/case_conv.hpp>
11 #include "crypto/crypto.h"
12 #include "logging/logging.h"
13 #include "utilities/exceptions.h"
21 std::ostream &Uptane::operator<<(std::ostream &os,
const Version &v) {
22 if (v.version_ == Version::ANY_VERSION) {
25 os <<
"v" << v.version_;
30 std::ostream &Uptane::operator<<(std::ostream &os,
const HardwareIdentifier &hwid) {
35 std::ostream &Uptane::operator<<(std::ostream &os,
const EcuSerial &ecu_serial) {
36 os << ecu_serial.ecu_serial_;
40 Hash Hash::generate(Type type,
const std::string &
data) {
45 hash = boost::algorithm::hex(Crypto::sha256digest(
data));
49 hash = boost::algorithm::hex(Crypto::sha512digest(
data));
52 default: {
throw std::invalid_argument(
"Unsupported hash type"); }
55 return Hash(type, hash);
58 Hash::Hash(
const std::string &type,
const std::string &hash) : hash_(boost::algorithm::to_upper_copy(hash)) {
59 if (type ==
"sha512") {
60 type_ = Hash::Type::kSha512;
61 }
else if (type ==
"sha256") {
62 type_ = Hash::Type::kSha256;
64 type_ = Hash::Type::kUnknownAlgorithm;
68 Hash::Hash(Type type,
const std::string &hash) : type_(type), hash_(boost::algorithm::to_upper_copy(hash)) {}
70 bool Hash::operator==(
const Hash &other)
const {
return type_ == other.type_ && hash_ == other.hash_; }
72 std::string Hash::TypeString()
const {
83 Hash::Type Hash::type()
const {
return type_; }
85 std::ostream &Uptane::operator<<(std::ostream &os,
const Hash &h) {
86 os <<
"Hash: " << h.hash_;
90 std::string Hash::encodeVector(
const std::vector<Uptane::Hash> &hashes) {
93 for (
auto it = hashes.cbegin(); it != hashes.cend(); it++) {
94 hs << it->TypeString() <<
":" << it->HashString();
95 if (std::next(it) != hashes.cend()) {
103 std::vector<Uptane::Hash> Hash::decodeVector(std::string hashes_str) {
104 std::vector<Uptane::Hash> hash_v;
106 std::string cs = std::move(hashes_str);
107 while (!cs.empty()) {
108 size_t scp = cs.find(
';');
109 std::string hash_token = cs.substr(0, scp);
110 if (scp == std::string::npos) {
113 cs = cs.substr(scp + 1);
115 if (hash_token.empty()) {
119 size_t cp = hash_token.find(
':');
120 std::string hash_type_str = hash_token.substr(0, cp);
121 if (cp == std::string::npos) {
124 std::string hash_value_str = hash_token.substr(cp + 1);
126 if (!hash_value_str.empty()) {
128 if (h.type() != Uptane::Hash::Type::kUnknownAlgorithm) {
129 hash_v.push_back(std::move(h));
137 Target::Target(std::string filename,
const Json::Value &content) : filename_(std::move(filename)) {
138 if (content.isMember(
"custom")) {
139 custom_ = content[
"custom"];
142 if (custom_.isMember(
"hardwareIds")) {
143 Json::Value hwids = custom_[
"hardwareIds"];
144 for (
auto i = hwids.begin(); i != hwids.end(); ++i) {
145 hwids_.emplace_back(HardwareIdentifier((*i).asString()));
150 Json::Value ecus = custom_[
"ecuIdentifiers"];
151 for (
auto i = ecus.begin(); i != ecus.end(); ++i) {
152 ecus_.insert({EcuSerial(i.key().asString()), HardwareIdentifier((*i)[
"hardwareId"].asString())});
155 if (custom_.isMember(
"targetFormat")) {
156 type_ = custom_[
"targetFormat"].asString();
159 if (custom_.isMember(
"uri")) {
160 std::string custom_uri = custom_[
"uri"].asString();
162 if (custom_uri !=
"https://example.com/") {
163 uri_ = std::move(custom_uri);
168 length_ = content[
"length"].asUInt64();
170 Json::Value hashes = content[
"hashes"];
171 for (
auto i = hashes.begin(); i != hashes.end(); ++i) {
172 Hash h(i.key().asString(), (*i).asString());
173 if (h.HaveAlgorithm()) {
174 hashes_.push_back(h);
178 std::sort(hashes_.begin(), hashes_.end(), [](
const Hash &l,
const Hash &r) {
return l.type() < r.type(); });
181 Target::Target(std::string filename, EcuMap ecus, std::vector<Hash> hashes, uint64_t length, std::string correlation_id)
182 : filename_(std::move(filename)),
183 ecus_(std::move(ecus)),
184 hashes_(std::move(hashes)),
186 correlation_id_(std::move(correlation_id)) {
188 std::sort(hashes_.begin(), hashes_.end(), [](
const Hash &l,
const Hash &r) {
return l.type() < r.type(); });
191 Target Target::Unknown() {
193 t_json[
"hashes"][
"sha256"] = boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest(
"")));
194 t_json[
"length"] = 0;
197 target.valid =
false;
202 bool Target::MatchHash(
const Hash &hash)
const {
203 return (std::find(hashes_.begin(), hashes_.end(), hash) != hashes_.end());
206 std::string Target::hashString(Hash::Type type)
const {
207 std::vector<Uptane::Hash>::const_iterator it;
208 for (it = hashes_.begin(); it != hashes_.end(); it++) {
209 if (it->type() == type) {
210 return boost::algorithm::to_lower_copy(it->HashString());
213 return std::string(
"");
216 std::string Target::sha256Hash()
const {
return hashString(Hash::Type::kSha256); }
218 std::string Target::sha512Hash()
const {
return hashString(Hash::Type::kSha512); }
221 if (type_ ==
"OSTREE") {
224 }
else if (type_.empty() && length() == 0) {
235 bool Target::MatchTarget(
const Target &t2)
const {
242 if (filename_ != t2.filename_) {
245 if (length_ != t2.length_) {
254 if (hwids_ != t2.hwids_ || ecus_ != t2.ecus_) {
255 std::shared_ptr<EcuMap> ecu_map;
256 std::shared_ptr<std::vector<HardwareIdentifier>> hwid_vector;
257 if (!hwids_.empty() && ecus_.empty() && t2.hwids_.empty() && !t2.ecus_.empty()) {
258 ecu_map = std::make_shared<EcuMap>(t2.ecus_);
259 hwid_vector = std::make_shared<std::vector<HardwareIdentifier>>(hwids_);
260 }
else if (!t2.hwids_.empty() && t2.ecus_.empty() && hwids_.empty() && !ecus_.empty()) {
261 ecu_map = std::make_shared<EcuMap>(ecus_);
262 hwid_vector = std::make_shared<std::vector<HardwareIdentifier>>(t2.hwids_);
266 for (
auto map_it = ecu_map->cbegin(); map_it != ecu_map->cend(); ++map_it) {
267 auto vec_it = find(hwid_vector->cbegin(), hwid_vector->cend(), map_it->second);
268 if (vec_it == hwid_vector->end()) {
277 bool oneMatchingHash =
false;
278 for (
const Hash &hash : hashes_) {
279 for (
const Hash &hash2 : t2.hashes_) {
280 if (hash.type() == hash2.type() && !(hash == hash2)) {
284 oneMatchingHash =
true;
288 return oneMatchingHash;
291 Json::Value Target::toDebugJson()
const {
293 for (
const auto &ecu : ecus_) {
294 res[
"custom"][
"ecuIdentifiers"][ecu.first.ToString()][
"hardwareId"] = ecu.second.ToString();
296 if (!hwids_.empty()) {
298 for (Json::Value::ArrayIndex i = 0; i < hwids_.size(); ++i) {
299 hwids[i] = hwids_[i].ToString();
301 res[
"custom"][
"hardwareIds"] = hwids;
303 res[
"custom"][
"targetFormat"] = type_;
305 for (
const auto &hash : hashes_) {
306 res[
"hashes"][hash.TypeString()] = hash.HashString();
308 res[
"length"] = Json::Value(static_cast<Json::Value::Int64>(length_));
312 std::ostream &Uptane::operator<<(std::ostream &os,
const Target &t) {
313 os <<
"Target(" << t.filename_;
314 os <<
" ecu_identifiers: (";
315 for (
const auto &ecu : t.ecus_) {
316 os << ecu.first <<
" (hw_id: " << ecu.second <<
"), ";
320 for (
const auto &hwid : t.hwids_) {
324 <<
" length:" << t.length();
326 for (
const auto &hash : t.hashes_) {
334 void Uptane::BaseMeta::init(
const Json::Value &json) {
335 if (!json.isObject() || !json.isMember(
"signed")) {
336 LOG_ERROR <<
"Failure during base metadata initialization from json";
340 version_ = json[
"signed"][
"version"].asInt();
342 expiry_ =
TimeStamp(json[
"signed"][
"expires"].asString());
346 original_object_ = json;
348 Uptane::BaseMeta::BaseMeta(
const Json::Value &json) { init(json); }
350 Uptane::BaseMeta::BaseMeta(RepositoryType repo,
const Role &role,
const Json::Value &json,
351 const std::shared_ptr<MetaWithKeys> &signer) {
352 if (!json.isObject() || !json.isMember(
"signed")) {
361 void Uptane::Targets::init(
const Json::Value &json) {
362 if (!json.isObject() || json[
"signed"][
"_type"] !=
"Targets") {
366 const Json::Value target_list = json[
"signed"][
"targets"];
367 for (
auto t_it = target_list.begin(); t_it != target_list.end(); t_it++) {
368 Target t(t_it.key().asString(), *t_it);
369 targets.push_back(t);
372 if (json[
"signed"][
"delegations"].isObject()) {
373 const Json::Value key_list = json[
"signed"][
"delegations"][
"keys"];
374 ParseKeys(Uptane::RepositoryType::Image(), key_list);
376 const Json::Value role_list = json[
"signed"][
"delegations"][
"roles"];
377 for (
auto it = role_list.begin(); it != role_list.end(); it++) {
378 const std::string role_name = (*it)[
"name"].asString();
379 const Role role = Role::Delegation(role_name);
380 delegated_role_names_.push_back(role_name);
381 ParseRole(Uptane::RepositoryType::Image(), it, role, name_);
383 const Json::Value paths_list = (*it)[
"paths"];
384 std::vector<std::string> paths;
385 for (
auto p_it = paths_list.begin(); p_it != paths_list.end(); p_it++) {
386 paths.emplace_back((*p_it).asString());
388 paths_for_role_[role] = paths;
390 terminating_role_[role] = (*it)[
"terminating"].asBool();
394 if (json[
"signed"][
"custom"].isObject()) {
395 correlation_id_ = json[
"signed"][
"custom"][
"correlationId"].asString();
397 correlation_id_ =
"";
401 Uptane::Targets::Targets(
const Json::Value &json) : MetaWithKeys(json) { init(json); }
403 Uptane::Targets::Targets(RepositoryType repo,
const Role &role,
const Json::Value &json,
404 const std::shared_ptr<MetaWithKeys> &signer)
405 : MetaWithKeys(repo, role, json, signer), name_(role.ToString()) {
409 void Uptane::TimestampMeta::init(
const Json::Value &json) {
410 Json::Value hashes_list = json[
"signed"][
"meta"][
"snapshot.json"][
"hashes"];
411 Json::Value meta_size = json[
"signed"][
"meta"][
"snapshot.json"][
"length"];
412 Json::Value meta_version = json[
"signed"][
"meta"][
"snapshot.json"][
"version"];
413 if (!json.isObject() || json[
"signed"][
"_type"] !=
"Timestamp" || !hashes_list.isObject() ||
414 !meta_size.isIntegral() || !meta_version.isIntegral()) {
418 for (
auto it = hashes_list.begin(); it != hashes_list.end(); ++it) {
419 Hash h(it.key().asString(), (*it).asString());
420 snapshot_hashes_.push_back(h);
422 snapshot_size_ = meta_size.asInt();
423 snapshot_version_ = meta_version.asInt();
426 Uptane::TimestampMeta::TimestampMeta(
const Json::Value &json) : BaseMeta(json) { init(json); }
428 Uptane::TimestampMeta::TimestampMeta(RepositoryType repo,
const Json::Value &json,
429 const std::shared_ptr<MetaWithKeys> &signer)
430 : BaseMeta(repo, Role::Timestamp(), json, signer) {
434 void Uptane::Snapshot::init(
const Json::Value &json) {
435 Json::Value meta_list = json[
"signed"][
"meta"];
436 if (!json.isObject() || json[
"signed"][
"_type"] !=
"Snapshot" || !meta_list.isObject()) {
440 for (
auto it = meta_list.begin(); it != meta_list.end(); ++it) {
441 Json::Value hashes_list = (*it)[
"hashes"];
442 Json::Value meta_size = (*it)[
"length"];
443 Json::Value meta_version = (*it)[
"version"];
445 if (!meta_version.isIntegral()) {
450 it.key().asString().substr(0, it.key().asString().rfind(
'.'));
451 auto role_object = Role(role_name, !Role::IsReserved(role_name));
453 if (meta_version.isIntegral()) {
454 role_version_[role_object] = meta_version.asInt();
456 role_version_[role_object] = -1;
461 if (meta_size.isObject()) {
462 role_size_[role_object] = meta_size.asInt64();
464 role_size_[role_object] = -1;
466 if (hashes_list.isObject()) {
467 for (
auto h_it = hashes_list.begin(); h_it != hashes_list.end(); ++h_it) {
468 Hash h(h_it.key().asString(), (*h_it).asString());
469 role_hashes_[role_object].push_back(h);
475 Uptane::Snapshot::Snapshot(
const Json::Value &json) : BaseMeta(json) { init(json); }
477 Uptane::Snapshot::Snapshot(RepositoryType repo,
const Json::Value &json,
const std::shared_ptr<MetaWithKeys> &signer)
478 : BaseMeta(repo, Role::Snapshot(), json, signer) {
482 std::vector<Hash> Uptane::Snapshot::role_hashes(
const Uptane::Role &role)
const {
483 auto hashes = role_hashes_.find(role);
484 if (hashes == role_hashes_.end()) {
485 return std::vector<Hash>();
487 return hashes->second;
491 int64_t Uptane::Snapshot::role_size(
const Uptane::Role &role)
const {
492 auto size = role_size_.find(role);
493 if (size == role_size_.end()) {
500 int Uptane::Snapshot::role_version(
const Uptane::Role &role)
const {
501 auto version = role_version_.find(role);
502 if (version == role_version_.end()) {
505 return version->second;
509 bool MetaPack::isConsistent()
const {
512 if (director_root.original() != Json::nullValue) {
514 Uptane::Root new_root(RepositoryType::Director(), director_root.original(), new_root);
515 if (director_targets.original() != Json::nullValue) {
516 Uptane::Targets(RepositoryType::Director(), Role::Targets(), director_targets.original(),
517 std::make_shared<MetaWithKeys>(original_root));
520 }
catch (
const std::logic_error &exc) {
521 LOG_WARNING <<
"Inconsistent metadata: " << exc.what();
527 int Uptane::extractVersionUntrusted(
const std::string &meta) {
528 auto version_json = Utils::parseJSON(meta)[
"signed"][
"version"];
529 if (!version_json.isIntegral()) {
532 return version_json.asInt();