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" 22 std::ostream &Uptane::operator<<(std::ostream &os,
const Version &v) {
23 if (v.version_ == Version::ANY_VERSION) {
26 os <<
"v" << v.version_;
33 struct tm time_struct {};
35 gmtime_r(&raw_time, &time_struct);
37 strftime(formatted, 22,
"%Y-%m-%dT%H:%M:%SZ", &time_struct);
42 if (rfc3339.length() != 20 || rfc3339[19] !=
'Z') {
48 bool TimeStamp::IsValid()
const {
return time_.length() != 0; }
50 bool TimeStamp::IsExpiredAt(
const TimeStamp &now)
const {
60 bool TimeStamp::operator<(
const TimeStamp &other)
const {
return IsValid() && other.IsValid() && time_ < other.time_; }
62 bool TimeStamp::operator>(
const TimeStamp &other)
const {
return (other < *
this); }
64 std::ostream &Uptane::operator<<(std::ostream &os,
const TimeStamp &t) {
69 std::ostream &Uptane::operator<<(std::ostream &os,
const HardwareIdentifier &hwid) {
74 std::ostream &Uptane::operator<<(std::ostream &os,
const EcuSerial &ecu_serial) {
75 os << ecu_serial.ecu_serial_;
79 Hash::Hash(
const std::string &type,
const std::string &hash) : hash_(
boost::algorithm::to_upper_copy(hash)) {
80 if (type ==
"sha512") {
81 type_ = Hash::Type::kSha512;
82 }
else if (type ==
"sha256") {
83 type_ = Hash::Type::kSha256;
85 type_ = Hash::Type::kUnknownAlgorithm;
89 Hash::Hash(Type type,
const std::string &hash) : type_(type), hash_(
boost::algorithm::to_upper_copy(hash)) {}
91 bool Hash::operator==(
const Hash &other)
const {
return type_ == other.type_ && hash_ == other.hash_; }
93 std::string Hash::TypeString()
const {
104 Hash::Type Hash::type()
const {
return type_; }
106 std::ostream &Uptane::operator<<(std::ostream &os,
const Hash &h) {
107 os <<
"Hash: " << h.hash_;
111 Target::Target(std::string filename,
const Json::Value &content) : filename_(
std::move(filename)) {
112 if (content.isMember(
"custom")) {
113 Json::Value custom = content[
"custom"];
115 Json::Value ecus = custom[
"ecuIdentifiers"];
116 for (Json::ValueIterator i = ecus.begin(); i != ecus.end(); ++i) {
117 ecus_.insert({EcuSerial(i.key().asString()), HardwareIdentifier((*i)[
"hardwareId"].asString())});
120 if (custom.isMember(
"targetFormat")) {
121 type_ = custom[
"targetFormat"].asString();
125 length_ = content[
"length"].asInt64();
127 Json::Value hashes = content[
"hashes"];
128 for (Json::ValueIterator i = hashes.begin(); i != hashes.end(); ++i) {
129 Hash h(i.key().asString(), (*i).asString());
130 if (h.HaveAlgorithm()) {
131 hashes_.push_back(h);
135 std::sort(hashes_.begin(), hashes_.end(), [](
const Hash &l,
const Hash &r) {
return l.type() < r.type(); });
138 bool Target::MatchWith(
const Hash &hash)
const {
139 return (std::find(hashes_.begin(), hashes_.end(), hash) != hashes_.end());
142 std::string Target::sha256Hash()
const {
143 std::vector<Uptane::Hash>::const_iterator it;
144 for (it = hashes_.begin(); it != hashes_.end(); it++) {
145 if (it->type() == Hash::Type::kSha256) {
146 return boost::algorithm::to_lower_copy(it->HashString());
149 return std::string(
"");
153 if (type_ ==
"OSTREE") {
156 }
else if (type_.empty() && length() == 0) {
167 Json::Value Target::toDebugJson()
const {
169 for (
auto it = ecus_.begin(); it != ecus_.cend(); ++it) {
170 res[
"custom"][
"ecuIdentifiers"][it->first.ToString()][
"hardwareId"] = it->second.ToString();
172 res[
"custom"][
"targetFormat"] = type_;
174 for (
auto it = hashes_.cbegin(); it != hashes_.cend(); ++it) {
175 res[
"hashes"][it->TypeString()] = it->HashString();
177 res[
"length"] = Json::Value(static_cast<Json::Value::Int64>(length_));
181 std::ostream &Uptane::operator<<(std::ostream &os,
const Target &t) {
182 os <<
"Target(" << t.filename_;
183 os <<
" ecu_identifiers: (";
185 for (
auto it = t.ecus_.begin(); it != t.ecus_.end(); ++it) {
189 <<
" length:" << t.length();
191 for (
auto it = t.hashes_.begin(); it != t.hashes_.end(); ++it) {
199 void Uptane::BaseMeta::init(
const Json::Value &json) {
200 if (!json.isObject() || !json.isMember(
"signed")) {
201 LOG_ERROR <<
"BM FAILURE";
205 version_ = json[
"signed"][
"version"].asInt();
207 original_object_ = json;
209 Uptane::BaseMeta::BaseMeta(
const Json::Value &json) { init(json); }
211 Uptane::BaseMeta::BaseMeta(
RepositoryType repo,
const Json::Value &json,
Root &root) {
212 if (!json.isObject() || !json.isMember(
"signed")) {
221 void Uptane::Targets::init(
const Json::Value &json) {
222 if (!json.isObject() || json[
"signed"][
"_type"] !=
"Targets") {
226 Json::Value target_list = json[
"signed"][
"targets"];
227 for (Json::ValueIterator t_it = target_list.begin(); t_it != target_list.end(); t_it++) {
228 Target t(t_it.key().asString(), *t_it);
229 targets.push_back(t);
233 Uptane::Targets::Targets(
const Json::Value &json) : BaseMeta(json) { init(json); }
235 Uptane::Targets::Targets(
RepositoryType repo,
const Json::Value &json,
Root &root) : BaseMeta(repo, json, root) {
239 void Uptane::TimestampMeta::init(
const Json::Value &json) {
240 Json::Value hashes_list = json[
"signed"][
"meta"][
"snapshot.json"][
"hashes"];
241 Json::Value meta_size = json[
"signed"][
"meta"][
"snapshot.json"][
"length"];
242 Json::Value meta_version = json[
"signed"][
"meta"][
"snapshot.json"][
"version"];
243 if (!json.isObject() || json[
"signed"][
"_type"] !=
"Timestamp" || !hashes_list.isObject() ||
244 !meta_size.isIntegral() || !meta_version.isIntegral()) {
248 for (Json::ValueIterator it = hashes_list.begin(); it != hashes_list.end(); ++it) {
249 Hash h(it.key().asString(), (*it).asString());
250 snapshot_hashes_.push_back(h);
252 snapshot_size_ = meta_size.asInt();
253 snapshot_version_ = meta_version.asInt();
256 Uptane::TimestampMeta::TimestampMeta(
const Json::Value &json) : BaseMeta(json) { init(json); }
258 Uptane::TimestampMeta::TimestampMeta(
RepositoryType repo,
const Json::Value &json,
Root &root)
259 : BaseMeta(repo, json, root) {
263 void Uptane::Snapshot::init(
const Json::Value &json) {
264 Json::Value hashes_list = json[
"signed"][
"meta"][
"targets.json"][
"hashes"];
265 Json::Value meta_size = json[
"signed"][
"meta"][
"targets.json"][
"length"];
266 Json::Value meta_version = json[
"signed"][
"meta"][
"targets.json"][
"version"];
268 if (!json.isObject() || json[
"signed"][
"_type"] !=
"Snapshot" || !meta_version.isIntegral()) {
272 if (hashes_list.isObject()) {
273 for (Json::ValueIterator it = hashes_list.begin(); it != hashes_list.end(); ++it) {
274 Hash h(it.key().asString(), (*it).asString());
275 targets_hashes_.push_back(h);
279 if (meta_size.isIntegral()) {
280 targets_size_ = meta_size.asInt();
284 targets_version_ = meta_version.asInt();
287 Uptane::Snapshot::Snapshot(
const Json::Value &json) : BaseMeta(json) { init(json); }
289 Uptane::Snapshot::Snapshot(
RepositoryType repo,
const Json::Value &json,
Root &root) : BaseMeta(repo, json, root) {
293 bool MetaPack::isConsistent()
const {
296 if (director_root.original() != Json::nullValue) {
298 Uptane::Root new_root(RepositoryType::Director, director_root.original(), new_root);
299 if (director_targets.original() != Json::nullValue) {
300 Uptane::Targets(RepositoryType::Director, director_targets.original(), original_root);
303 }
catch (
const std::logic_error &exc) {
304 LOG_WARNING <<
"Inconsistent metadata: " << exc.what();
312 case RepositoryType::Director:
314 case RepositoryType::Images:
321 int Uptane::extractVersionUntrusted(
const std::string &meta) {
322 auto version_json = Utils::parseJSON(meta)[
"signed"][
"version"];
323 if (!version_json.isIntegral()) {
326 return version_json.asInt();
Metadata version numbers.
TimeStamp()
An invalid TimeStamp.
bool IsOstree() const
Is this an OSTree target? OSTree targets need special treatment because the hash doesn't represent th...
RepositoryType
This must match the repo_type table in sqlstorage.
void UnpackSignedObject(RepositoryType repo, const Json::Value &signed_object)
Take a JSON blob that contains a signatures/signed component that is supposedly for a given role...
The hash of a file or TUF metadata.