1 #include "managedsecondary.h"
7 #include <boost/algorithm/hex.hpp>
8 #include <boost/filesystem.hpp>
10 #include "crypto/crypto.h"
11 #include "logging/logging.h"
12 #include "uptane/manifest.h"
13 #include "uptane/uptanerepository.h"
14 #include "utilities/exceptions.h"
21 std::string public_key_string;
23 if (!loadKeys(&public_key_string, &private_key)) {
24 if (!Crypto::generateKeyPair(sconfig.key_type, &public_key_string, &private_key)) {
25 LOG_ERROR <<
"Could not generate rsa keys for secondary " << ManagedSecondary::getSerial() <<
"@"
26 << sconfig.ecu_hardware_id;
27 throw std::runtime_error(
"Unable to generate secondary rsa keys");
32 public_key_ =
PublicKey(public_key_string, sconfig.key_type);
36 void ManagedSecondary::Initialize() {
39 if (!boost::filesystem::is_directory(sconfig.metadata_path)) {
40 Utils::createDirectories(sconfig.metadata_path, S_IRWXU);
42 if (stat(sconfig.metadata_path.c_str(), &st) < 0) {
43 throw std::runtime_error(std::string(
"Could not check metadata directory permissions: ") + std::strerror(errno));
45 if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
46 throw std::runtime_error(
"Secondary metadata directory has unsafe permissions");
49 if (!boost::filesystem::is_directory(sconfig.full_client_dir)) {
50 Utils::createDirectories(sconfig.full_client_dir, S_IRWXU);
52 if (stat(sconfig.full_client_dir.c_str(), &st) < 0) {
53 throw std::runtime_error(std::string(
"Could not check client directory permissions: ") + std::strerror(errno));
55 if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
56 throw std::runtime_error(
"Secondary client directory has unsafe permissions");
59 storeKeys(public_key_.Value(), private_key);
62 void ManagedSecondary::rawToMeta() {
64 current_meta.director_root =
65 Uptane::Root(Uptane::RepositoryType::Director(), Utils::parseJSON(current_raw_meta.director_root));
66 current_meta.director_targets =
Uptane::Targets(Utils::parseJSON(current_raw_meta.director_targets));
67 current_meta.image_root =
68 Uptane::Root(Uptane::RepositoryType::Image(), Utils::parseJSON(current_raw_meta.image_root));
69 current_meta.image_targets =
Uptane::Targets(Utils::parseJSON(current_raw_meta.image_targets));
70 current_meta.image_timestamp =
Uptane::TimestampMeta(Utils::parseJSON(current_raw_meta.image_timestamp));
71 current_meta.image_snapshot =
Uptane::Snapshot(Utils::parseJSON(current_raw_meta.image_snapshot));
78 current_raw_meta = meta_pack;
80 if (!current_meta.isConsistent()) {
83 storeMetadata(current_raw_meta);
85 expected_target_name =
"";
86 expected_target_hashes.clear();
87 expected_target_length = 0;
89 bool target_found =
false;
91 std::vector<Uptane::Target>::const_iterator it;
92 for (it = current_meta.director_targets.targets.begin(); it != current_meta.director_targets.targets.end(); ++it) {
94 if (it->ecus().find(getSerial()) != it->ecus().end()) {
96 detected_attack =
"Duplicate entry for this ECU";
99 expected_target_name = it->filename();
100 expected_target_hashes = it->hashes();
101 expected_target_length = it->length();
107 detected_attack =
"No update for this ECU";
113 int ManagedSecondary::getRootVersion(
const bool director)
const {
115 return current_meta.director_root.version();
117 return current_meta.image_root.version();
120 bool ManagedSecondary::putRoot(
const std::string &root,
const bool director) {
121 Uptane::Root &prev_root = (director) ? current_meta.director_root : current_meta.image_root;
122 std::string &prev_raw_root = (director) ? current_raw_meta.director_root : current_raw_meta.image_root;
124 (director) ? Uptane::RepositoryType::Director() : Uptane::RepositoryType::Image(), Utils::parseJSON(root));
127 if (new_root.version() == prev_root.version() + 1) {
128 prev_root = new_root;
129 prev_raw_root = root;
131 detected_attack =
"Tried to update root version " + std::to_string(prev_root.version()) +
" with version " +
132 std::to_string(new_root.version());
135 if (!current_meta.isConsistent()) {
138 storeMetadata(current_raw_meta);
142 bool ManagedSecondary::sendFirmware(
const std::string &
data) {
143 std::lock_guard<std::mutex> l(install_mutex);
145 if (expected_target_name.empty()) {
148 if (!detected_attack.empty()) {
152 if (
data.size() > static_cast<size_t>(expected_target_length)) {
153 detected_attack =
"overflow";
157 std::vector<Uptane::Hash>::const_iterator it;
158 for (it = expected_target_hashes.begin(); it != expected_target_hashes.end(); it++) {
159 if (it->TypeString() ==
"sha256") {
160 if (boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest(
data))) !=
161 boost::algorithm::to_lower_copy(it->HashString())) {
162 detected_attack =
"wrong_hash";
165 }
else if (it->TypeString() ==
"sha512") {
166 if (boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha512digest(
data))) !=
167 boost::algorithm::to_lower_copy(it->HashString())) {
168 detected_attack =
"wrong_hash";
173 detected_attack =
"";
174 const bool result = storeFirmware(expected_target_name,
data);
180 return data::ResultCode::Numeric::kOk;
185 if (!getFirmwareInfo(firmware_info)) {
186 return Json::Value(Json::nullValue);
189 Json::Value manifest = Uptane::ManifestIssuer::assembleManifest(firmware_info, getSerial());
193 manifest[
"attacks_detected"] = detected_attack;
195 Json::Value signed_ecu_version;
197 std::string b64sig = Utils::toBase64(Crypto::RSAPSSSign(
nullptr, private_key, Utils::jsonToCanonicalStr(manifest)));
198 Json::Value signature;
199 signature[
"method"] =
"rsassa-pss";
200 signature[
"sig"] = b64sig;
202 signature[
"keyid"] = public_key_.KeyId();
203 signed_ecu_version[
"signed"] = manifest;
204 signed_ecu_version[
"signatures"] = Json::Value(Json::arrayValue);
205 signed_ecu_version[
"signatures"].append(signature);
207 return signed_ecu_version;
210 void ManagedSecondary::storeKeys(
const std::string &pub_key,
const std::string &priv_key) {
211 Utils::writeFile((sconfig.full_client_dir / sconfig.ecu_private_key), priv_key);
212 Utils::writeFile((sconfig.full_client_dir / sconfig.ecu_public_key), pub_key);
215 bool ManagedSecondary::loadKeys(std::string *pub_key, std::string *priv_key) {
216 boost::filesystem::path public_key_path = sconfig.full_client_dir / sconfig.ecu_public_key;
217 boost::filesystem::path private_key_path = sconfig.full_client_dir / sconfig.ecu_private_key;
219 if (!boost::filesystem::exists(public_key_path) || !boost::filesystem::exists(private_key_path)) {
223 *priv_key = Utils::readFile(private_key_path.string());
224 *pub_key = Utils::readFile(public_key_path.string());