diff --git a/src/aktualizr_secondary/CMakeLists.txt b/src/aktualizr_secondary/CMakeLists.txt index 5a93f169fe..708ce67c54 100644 --- a/src/aktualizr_secondary/CMakeLists.txt +++ b/src/aktualizr_secondary/CMakeLists.txt @@ -55,9 +55,12 @@ add_aktualizr_test(NAME aktualizr_secondary_config add_aktualizr_test(NAME aktualizr_secondary_uptane_verification SOURCES uptane_verification_test.cc - ARGS "$" + ARGS ${PROJECT_BINARY_DIR}/ostree_repo PROJECT_WORKING_DIRECTORY) +set_target_properties(t_aktualizr_secondary_uptane_verification PROPERTIES LINK_FLAGS -Wl,--export-dynamic) +target_link_libraries(t_aktualizr_secondary_uptane_verification aktualizr_secondary_static_lib uptane_generator_lib) + add_aktualizr_test(NAME aktualizr_secondary_update SOURCES update_test.cc ARGS ${PROJECT_BINARY_DIR}/ostree_repo PROJECT_WORKING_DIRECTORY) diff --git a/src/aktualizr_secondary/aktualizr_secondary.cc b/src/aktualizr_secondary/aktualizr_secondary.cc index f9a4e18a45..fc9f718aa5 100644 --- a/src/aktualizr_secondary/aktualizr_secondary.cc +++ b/src/aktualizr_secondary/aktualizr_secondary.cc @@ -86,24 +86,26 @@ bool AktualizrSecondary::putRootResp(const std::string& root, bool director) { } bool AktualizrSecondary::sendFirmwareResp(const std::shared_ptr& firmware) { - auto targetsForThisEcu = director_repo_.getTargets(getSerial()); + auto targetsForThisEcu = director_repo_.getTargets(getSerial(), getHardwareID()); if (targetsForThisEcu.size() != 1) { LOG_ERROR << "Invalid number of targets (should be one): " << targetsForThisEcu.size(); return false; } - auto targetToApply = targetsForThisEcu[0]; + auto target_to_apply = targetsForThisEcu[0]; std::string treehub_server; + std::size_t firmware_size = firmware->length(); - if (targetToApply.IsOstree()) { + if (target_to_apply.IsOstree()) { // this is the ostree specific case try { std::string ca, cert, pkey, server_url; - extractCredentialsArchive(*firmware, &ca, &cert, &pkey, &treehub_server); + extractCredentialsArchive(*firmware, &ca, &cert, &pkey, &server_url); keys_.loadKeys(&ca, &cert, &pkey); boost::trim(server_url); treehub_server = server_url; + firmware_size = 0; } catch (std::runtime_error& exc) { LOG_ERROR << exc.what(); @@ -113,9 +115,9 @@ bool AktualizrSecondary::sendFirmwareResp(const std::shared_ptr& fi data::InstallationResult install_res; - if (targetToApply.IsOstree()) { + if (target_to_apply.IsOstree()) { #ifdef BUILD_OSTREE - install_res = OstreeManager::pull(config_.pacman.sysroot, treehub_server, keys_, targetToApply); + install_res = OstreeManager::pull(config_.pacman.sysroot, treehub_server, keys_, target_to_apply); if (install_res.result_code.num_code != data::ResultCode::Numeric::kOk) { LOG_ERROR << "Could not pull from OSTree (" << install_res.result_code.toString() @@ -128,23 +130,57 @@ bool AktualizrSecondary::sendFirmwareResp(const std::shared_ptr& fi #endif } else if (pacman->name() == "debian") { // TODO save debian package here. - LOG_ERROR << "Installation of non ostree images is not suppotrted yet."; + LOG_ERROR << "Installation of debian images is not suppotrted yet."; return false; } - // This check is not applicable for the ostree case, just for the binary file case(s) - if (targetToApply.length() != firmware->length()) { - LOG_ERROR << "The target image size specified in metadata " << targetToApply.length() + if (target_to_apply.length() != firmware_size) { + LOG_ERROR << "The target image size specified in metadata " << target_to_apply.length() << " does not match actual size " << firmware->length(); return false; } - install_res = pacman->install(targetToApply); - if (install_res.result_code.num_code != data::ResultCode::Numeric::kOk) { + if (!target_to_apply.IsOstree()) { + auto target_hashes = target_to_apply.hashes(); + if (target_hashes.size() == 0) { + LOG_ERROR << "No hash found in the target metadata: " << target_to_apply.filename(); + return false; + } + + try { + auto received_image_data_hash = Uptane::Hash::generate(target_hashes[0].type(), *firmware); + + if (!target_to_apply.MatchHash(received_image_data_hash)) { + LOG_ERROR << "The received image data hash doesn't match the hash specified in the target metadata," + " hash type: " + << target_hashes[0].TypeString(); + return false; + } + + } catch (const std::exception& exc) { + LOG_ERROR << "Failed to generate a hash of the received image data: " << exc.what(); + return false; + } + } + + install_res = pacman->install(target_to_apply); + if (install_res.result_code.num_code != data::ResultCode::Numeric::kOk && + install_res.result_code.num_code != data::ResultCode::Numeric::kNeedCompletion) { LOG_ERROR << "Could not install target (" << install_res.result_code.toString() << "): " << install_res.description; return false; } - storage_->saveInstalledVersion(getSerialResp().ToString(), targetToApply, InstalledVersionUpdateMode::kCurrent); + + if (install_res.result_code.num_code == data::ResultCode::Numeric::kNeedCompletion) { + storage_->saveInstalledVersion(getSerialResp().ToString(), target_to_apply, InstalledVersionUpdateMode::kPending); + } else { + storage_->saveInstalledVersion(getSerialResp().ToString(), target_to_apply, InstalledVersionUpdateMode::kCurrent); + } + + // TODO: https://saeljira.it.here.com/browse/OTA-4174 + // - return data::InstallationResult to Primary + // - add finaliztion support, pacman->finalizeInstall() must be called at secondary startup if there is pending + // version + return true; } @@ -213,10 +249,10 @@ bool AktualizrSecondary::doFullVerification(const Metadata& metadata) { return false; } - auto targetsForThisEcu = director_repo_.getTargets(getSerial()); + auto targetsForThisEcu = director_repo_.getTargets(getSerial(), getHardwareID()); if (targetsForThisEcu.size() != 1) { - LOG_ERROR << "Invalid number of targets (should be one): " << targetsForThisEcu.size(); + LOG_ERROR << "Invalid number of targets (should be 1): " << targetsForThisEcu.size(); return false; } diff --git a/src/aktualizr_secondary/aktualizr_secondary_common.h b/src/aktualizr_secondary/aktualizr_secondary_common.h index 97fb4db246..79d82200c2 100644 --- a/src/aktualizr_secondary/aktualizr_secondary_common.h +++ b/src/aktualizr_secondary/aktualizr_secondary_common.h @@ -17,6 +17,7 @@ class AktualizrSecondaryCommon { bool uptaneInitialize(); const Uptane::EcuSerial& getSerial() const { return ecu_serial_; } + const Uptane::HardwareIdentifier& getHardwareID() const { return hardware_id_; } protected: AktualizrSecondaryConfig config_; diff --git a/src/aktualizr_secondary/aktualizr_secondary_metadata.h b/src/aktualizr_secondary/aktualizr_secondary_metadata.h index d3cdb2fa20..a199b965b4 100644 --- a/src/aktualizr_secondary/aktualizr_secondary_metadata.h +++ b/src/aktualizr_secondary/aktualizr_secondary_metadata.h @@ -8,14 +8,15 @@ class Metadata : public Uptane::IMetadataFetcher { public: Metadata(const Uptane::RawMetaPack& meta_pack); + Metadata(Metadata&&) = default; bool fetchRole(std::string* result, int64_t maxsize, Uptane::RepositoryType repo, const Uptane::Role& role, Uptane::Version version) const override; bool fetchLatestRole(std::string* result, int64_t maxsize, Uptane::RepositoryType repo, const Uptane::Role& role) const override; - private: - bool getRoleMetadata(std::string* result, const Uptane::RepositoryType& repo, const Uptane::Role& role) const; + protected: + virtual bool getRoleMetadata(std::string* result, const Uptane::RepositoryType& repo, const Uptane::Role& role) const; private: const std::unordered_map _director_metadata; diff --git a/src/aktualizr_secondary/uptane_verification_test.cc b/src/aktualizr_secondary/uptane_verification_test.cc index b5dff9f619..eaa23c77f3 100644 --- a/src/aktualizr_secondary/uptane_verification_test.cc +++ b/src/aktualizr_secondary/uptane_verification_test.cc @@ -1,16 +1,105 @@ #include +#include +#include #include "aktualizr_secondary.h" +#include "package_manager/ostreemanager.h" #include "test_utils.h" +#include "uptane_repo.h" -static boost::filesystem::path UPTANE_METADATA_GENERATOR; +class Treehub { + public: + Treehub(const std::string& server_path) + : _port(TestUtils::getFreePort()), + _url("http://127.0.0.1:" + _port), + _process(server_path, "-p", _port, "-d", _root_dir.PathString(), "-s0.5", "--create") { + TestUtils::waitForServer(url() + "/"); + auto rev_process = Process("ostree").run({"rev-parse", "--repo", _root_dir.PathString(), "master"}); + EXPECT_EQ(std::get<0>(rev_process), 0) << std::get<2>(rev_process); + _cur_rev = std::get<1>(rev_process); + boost::trim_right_if(_cur_rev, boost::is_any_of(" \t\r\n")); + + LOG_INFO << "Treehub is running on: " << _port << " current revision: " << _cur_rev; + } + + ~Treehub() { + _process.terminate(); + _process.wait_for(std::chrono::seconds(10)); + if (_process.running()) { + LOG_ERROR << "Failed to stop Treehub server"; + } else { + LOG_INFO << "Treehub server has been stopped"; + } + } + + public: + const std::string& url() const { return _url; } + const std::string curRev() const { return _cur_rev; } + + private: + TemporaryDirectory _root_dir; + const std::string _port; + const std::string _url; + boost::process::child _process; + std::string _cur_rev; +}; + +class OstreeRootfs { + public: + OstreeRootfs(const std::string& rootfs_template) { + auto sysroot_copy = Process("cp").run({"-r", rootfs_template, getPath()}); + EXPECT_EQ(std::get<0>(sysroot_copy), 0) << std::get<1>(sysroot_copy); + + auto deployment_rev = Process("ostree").run( + {"rev-parse", std::string("--repo"), getPath() + "/ostree/repo", "generate-remote/generated"}); + + EXPECT_EQ(std::get<0>(deployment_rev), 0) << std::get<2>(deployment_rev); + + _rev = std::get<1>(deployment_rev); + boost::trim_right_if(_rev, boost::is_any_of(" \t\r\n")); + + _deployment.reset(ostree_deployment_new(0, getOSName(), getDeploymentRev(), getDeploymentSerial(), + getDeploymentRev(), getDeploymentSerial())); + } + + const std::string getPath() const { return _sysroot_dir; } + const char* getDeploymentRev() const { return _rev.c_str(); } + int getDeploymentSerial() const { return 0; } + const char* getOSName() const { return _os_name.c_str(); } + + OstreeDeployment* getDeployment() const { return _deployment.get(); } + + private: + const std::string _os_name{"dummy-os"}; + TemporaryDirectory _tmp_dir; + std::string _sysroot_dir{(_tmp_dir / "ostree-rootfs").c_str()}; + std::string _rev; + GObjectUniquePtr _deployment; +}; -class AktualizrSecondaryPtr { +class AktualizrSecondaryWrapper { public: - AktualizrSecondaryPtr() { + AktualizrSecondaryWrapper(const OstreeRootfs& sysroot, const Treehub& treehub) { + // ostree update AktualizrSecondaryConfig config; + config.pacman.type = PackageManager::kOstree; + config.pacman.os = sysroot.getOSName(); + config.pacman.sysroot = sysroot.getPath(); + config.pacman.ostree_server = treehub.url(); + + config.storage.path = _storage_dir.Path(); + config.storage.type = StorageType::kSqlite; + + _storage = INvStorage::newStorage(config.storage); + _secondary = std::make_shared(config, _storage); + } + + AktualizrSecondaryWrapper() { + // binary update + AktualizrSecondaryConfig config; config.pacman.type = PackageManager::kNone; + config.storage.path = _storage_dir.Path(); config.storage.type = StorageType::kSqlite; @@ -21,35 +110,47 @@ class AktualizrSecondaryPtr { public: std::shared_ptr& operator->() { return _secondary; } + Uptane::Target getPendingVersion() const { + boost::optional pending_target; + + _storage->loadInstalledVersions(_secondary->getSerialResp().ToString(), nullptr, &pending_target); + return *pending_target; + } + + std::string hardwareID() const { return _secondary->getHwIdResp().ToString(); } + + std::string serial() const { return _secondary->getSerialResp().ToString(); } + private: TemporaryDirectory _storage_dir; std::shared_ptr _secondary; + std::shared_ptr _storage; }; -class UptaneRepo { +class UptaneRepoWrapper { public: - UptaneRepo() { - _uptane_gen.run({"generate", "--path", _root_dir.PathString()}); - EXPECT_EQ(_uptane_gen.lastExitCode(), 0) << _uptane_gen.lastStdOut(); - } + UptaneRepoWrapper() { _uptane_repo.generateRepo(); } - Uptane::RawMetaPack addImageFile(const std::string& targetname, const std::string& hardware_id, - const std::string& serial, const std::string& expires = "") { + Metadata addImageFile(const std::string& targetname, const std::string& hardware_id, const std::string& serial) { const auto image_file_path = _root_dir / targetname; boost::filesystem::ofstream(image_file_path) << "some data"; - _uptane_gen.run({"image", "--path", _root_dir.PathString(), "--targetname", targetname, "--filename", - image_file_path.c_str(), "--hwid", hardware_id, "--expires", expires}); + _uptane_repo.addImage(image_file_path, targetname, hardware_id, "", Delegation()); + _uptane_repo.addTarget(targetname, hardware_id, serial, ""); + _uptane_repo.signTargets(); - EXPECT_EQ(_uptane_gen.lastExitCode(), 0) - << _uptane_gen.lastStdOut(); /* uptane_gen output message into stdout in case of an error */ + return getCurrentMetadata(); + } - _uptane_gen.run({"addtarget", "--path", _root_dir.PathString(), "--targetname", targetname, "--hwid", hardware_id, - "--serial", serial, "--expires", expires}); - EXPECT_EQ(_uptane_gen.lastExitCode(), 0) << _uptane_gen.lastStdOut(); + Metadata addOstreeRev(const std::string& rev, const std::string& hardware_id, const std::string& serial) { + // it makes sense to add 'addOstreeImage' to UptaneRepo interface/class uptane_repo.h + auto custom = Json::Value(); + custom["targetFormat"] = "OSTREE"; + _uptane_repo.addCustomImage(rev, Uptane::Hash(Uptane::Hash::Type::kSha256, rev), 0, hardware_id, "", Delegation(), + custom); - _uptane_gen.run({"signtargets", "--path", _root_dir.PathString()}); - EXPECT_EQ(_uptane_gen.lastExitCode(), 0) << _uptane_gen.lastStdOut(); + _uptane_repo.addTarget(rev, hardware_id, serial, ""); + _uptane_repo.signTargets(); return getCurrentMetadata(); } @@ -76,10 +177,9 @@ class UptaneRepo { private: TemporaryDirectory _root_dir; - TemporaryFile _default_image_file; boost::filesystem::path _director_dir{_root_dir / "repo/director"}; boost::filesystem::path _imagerepo_dir{_root_dir / "repo/repo"}; - Process _uptane_gen{UPTANE_METADATA_GENERATOR.string()}; + UptaneRepo _uptane_repo{_root_dir.Path(), "", ""}; }; class SecondaryUptaneVerificationTest : public ::testing::Test { @@ -95,57 +195,155 @@ class SecondaryUptaneVerificationTest : public ::testing::Test { protected: static constexpr const char* const _default_target{"defaulttarget"}; - AktualizrSecondaryPtr _secondary; - UptaneRepo _uptane_repo; + AktualizrSecondaryWrapper _secondary; + UptaneRepoWrapper _uptane_repo; }; -TEST_F(SecondaryUptaneVerificationTest, fullUptaneVerificationPositive) { - EXPECT_TRUE(_secondary->putMetadataResp(Metadata(_uptane_repo.getCurrentMetadata()))); - EXPECT_TRUE(_secondary->sendFirmwareResp(getImageData())); -} +class OstreeSecondaryUptaneVerificationTest : public ::testing::Test { + public: + static const char* curOstreeRootfsRev(OstreeDeployment* ostree_depl) { + (void)ostree_depl; + return _sysroot->getDeploymentRev(); + } -TEST_F(SecondaryUptaneVerificationTest, MalformedMetadaJson) { - { - auto malformed_metadata = _uptane_repo.getCurrentMetadata(); - malformed_metadata.director_root[4] = 'f'; + static OstreeDeployment* curOstreeDeployment(OstreeSysroot* ostree_sysroot) { + (void)ostree_sysroot; + return _sysroot->getDeployment(); + } - EXPECT_FALSE(_secondary->putMetadataResp(Metadata(malformed_metadata))); + static void setOstreeRootfsTemplate(const std::string& ostree_rootfs_template) { + _ostree_rootfs_template = ostree_rootfs_template; } - { - auto malformed_metadata = _uptane_repo.getCurrentMetadata(); - malformed_metadata.director_targets[4] = 'f'; + protected: + static void SetUpTestSuite() { + _treehub = std::make_shared("tests/sota_tools/treehub_server.py"); + _sysroot = std::make_shared(_ostree_rootfs_template); + } - EXPECT_FALSE(_secondary->putMetadataResp(Metadata(malformed_metadata))); + static void TearDownTestSuite() { + _treehub.reset(); + _sysroot.reset(); } - { - auto malformed_metadata = _uptane_repo.getCurrentMetadata(); - malformed_metadata.image_root[4] = 'f'; + protected: + OstreeSecondaryUptaneVerificationTest() {} - EXPECT_FALSE(_secondary->putMetadataResp(Metadata(malformed_metadata))); - } + Metadata addDefaultTarget() { return addTarget(_treehub->curRev()); } - { - auto malformed_metadata = _uptane_repo.getCurrentMetadata(); - malformed_metadata.image_timestamp[4] = 'f'; + Metadata addTarget(const std::string& rev = "", const std::string& hardware_id = "", const std::string& serial = "") { + auto rev_to_apply = rev.empty() ? _treehub->curRev() : rev; + auto hw_id = hardware_id.empty() ? _secondary.hardwareID() : hardware_id; + auto serial_id = serial.empty() ? _secondary.serial() : serial; + + _uptane_repo.addOstreeRev(rev, hw_id, serial_id); - EXPECT_FALSE(_secondary->putMetadataResp(Metadata(malformed_metadata))); + return currentMetadata(); } - { - auto malformed_metadata = _uptane_repo.getCurrentMetadata(); - malformed_metadata.image_snapshot[4] = 'f'; + Metadata currentMetadata() const { return _uptane_repo.getCurrentMetadata(); } + + std::shared_ptr getCredsToSend() const { + std::map creds_map = { + {"ca.pem", ""}, {"client.pem", ""}, {"pkey.pem", ""}, {"server.url", _treehub->url()}}; - EXPECT_FALSE(_secondary->putMetadataResp(Metadata(malformed_metadata))); + std::stringstream creads_strstream; + Utils::writeArchive(creds_map, creads_strstream); + + return std::make_shared(creads_strstream.str()); } - { - auto malformed_metadata = _uptane_repo.getCurrentMetadata(); - malformed_metadata.image_targets[4] = 'f'; + Uptane::Hash treehubCurRev() const { return Uptane::Hash(Uptane::Hash::Type::kSha256, _treehub->curRev()); } + + protected: + static std::shared_ptr _treehub; + static std::string _ostree_rootfs_template; + static std::shared_ptr _sysroot; + + AktualizrSecondaryWrapper _secondary{*_sysroot, *_treehub}; + UptaneRepoWrapper _uptane_repo; +}; - EXPECT_FALSE(_secondary->putMetadataResp(Metadata(malformed_metadata))); +std::shared_ptr OstreeSecondaryUptaneVerificationTest::_treehub{nullptr}; +std::string OstreeSecondaryUptaneVerificationTest::_ostree_rootfs_template{"./build/ostree_repo"}; +std::shared_ptr OstreeSecondaryUptaneVerificationTest::_sysroot{nullptr}; + +class SecondaryUptaneVerificationTestNegative + : public SecondaryUptaneVerificationTest, + public ::testing::WithParamInterface> { + protected: + class MetadataInvalidator : public Metadata { + public: + MetadataInvalidator(const Uptane::RawMetaPack& valid_metadata, const Uptane::RepositoryType& repo, + const Uptane::Role& role) + : Metadata(valid_metadata), _repo_type(repo), _role(role) {} + + bool getRoleMetadata(std::string* result, const Uptane::RepositoryType& repo, + const Uptane::Role& role) const override { + auto return_val = Metadata::getRoleMetadata(result, repo, role); + if (!(_repo_type == repo && _role == role)) { + return return_val; + } + (*result)[10] = 'f'; + return true; + } + + private: + Uptane::RepositoryType _repo_type; + Uptane::Role _role; + }; + + protected: + MetadataInvalidator currentMetadata() const { + return MetadataInvalidator(_uptane_repo.getCurrentMetadata(), GetParam().first, GetParam().second); } +}; + +/** + * Parameterized test, + * The parameter is std::pair to indicate which metadata to malform + * + * see INSTANTIATE_TEST_SUITE_P for the test instantiations with concrete parameter values + */ +TEST_P(SecondaryUptaneVerificationTestNegative, MalformedMetadaJson) { + EXPECT_FALSE(_secondary->putMetadataResp(currentMetadata())); +} + +/** + * Instantiates the parameterized test for each specified value of std::pair + * the parameter value indicates which metadata to malform + */ +INSTANTIATE_TEST_SUITE_P(SecondaryUptaneVerificationMalformedMetadata, SecondaryUptaneVerificationTestNegative, + ::testing::Values(std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Root()), + std::make_pair(Uptane::RepositoryType::Director(), Uptane::Role::Targets()), + std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Root()), + std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Timestamp()), + std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Snapshot()), + std::make_pair(Uptane::RepositoryType::Image(), Uptane::Role::Targets()))); + +TEST_F(OstreeSecondaryUptaneVerificationTest, fullUptaneVerificationPositive) { + EXPECT_TRUE(_secondary->putMetadataResp(addDefaultTarget())); + EXPECT_TRUE(_secondary->sendFirmwareResp(getCredsToSend())); + EXPECT_TRUE(_secondary.getPendingVersion().MatchHash(treehubCurRev())); + // TODO: emulate reboot and check installed version once ostree update finalization is supported by secondary +} + +TEST_F(OstreeSecondaryUptaneVerificationTest, fullUptaneVerificationInvalidRevision) { + EXPECT_TRUE(_secondary->putMetadataResp(addTarget("invalid-revision"))); + EXPECT_FALSE(_secondary->sendFirmwareResp(getCredsToSend())); +} + +TEST_F(OstreeSecondaryUptaneVerificationTest, fullUptaneVerificationInvalidHwID) { + EXPECT_FALSE(_secondary->putMetadataResp(addTarget("", "invalid-hardware-id", ""))); +} + +TEST_F(OstreeSecondaryUptaneVerificationTest, fullUptaneVerificationInvalidSerial) { + EXPECT_FALSE(_secondary->putMetadataResp(addTarget("", "", "invalid-serial-id"))); +} + +TEST_F(SecondaryUptaneVerificationTest, fullUptaneVerificationPositive) { + EXPECT_TRUE(_secondary->putMetadataResp(_uptane_repo.getCurrentMetadata())); + EXPECT_TRUE(_secondary->sendFirmwareResp(getImageData())); } TEST_F(SecondaryUptaneVerificationTest, IncorrectTargetQuantity) { @@ -154,45 +352,62 @@ TEST_F(SecondaryUptaneVerificationTest, IncorrectTargetQuantity) { _uptane_repo.addImageFile("second_target", _secondary->getHwIdResp().ToString(), _secondary->getSerialResp().ToString()); - EXPECT_FALSE(_secondary->putMetadataResp(Metadata(_uptane_repo.getCurrentMetadata()))); + EXPECT_FALSE(_secondary->putMetadataResp(_uptane_repo.getCurrentMetadata())); } { // zero targets for the ECU being tested - auto metadata = UptaneRepo().addImageFile("mytarget", _secondary->getHwIdResp().ToString(), "non-existing-serial"); + auto metadata = + UptaneRepoWrapper().addImageFile("mytarget", _secondary->getHwIdResp().ToString(), "non-existing-serial"); - EXPECT_FALSE(_secondary->putMetadataResp(Metadata(metadata))); + EXPECT_FALSE(_secondary->putMetadataResp(metadata)); } { // zero targets for the ECU being tested - auto metadata = UptaneRepo().addImageFile("mytarget", "non-existig-hwid", _secondary->getSerialResp().ToString()); + auto metadata = + UptaneRepoWrapper().addImageFile("mytarget", "non-existig-hwid", _secondary->getSerialResp().ToString()); - EXPECT_FALSE(_secondary->putMetadataResp(Metadata(metadata))); + EXPECT_FALSE(_secondary->putMetadataResp(metadata)); } } TEST_F(SecondaryUptaneVerificationTest, InvalidImageFileSize) { - EXPECT_TRUE(_secondary->putMetadataResp(Metadata(_uptane_repo.getCurrentMetadata()))); + EXPECT_TRUE(_secondary->putMetadataResp(_uptane_repo.getCurrentMetadata())); auto image_data = getImageData(); image_data->append("\n"); EXPECT_FALSE(_secondary->sendFirmwareResp(image_data)); } +TEST_F(SecondaryUptaneVerificationTest, InvalidImageData) { + EXPECT_TRUE(_secondary->putMetadataResp(_uptane_repo.getCurrentMetadata())); + auto image_data = getImageData(); + image_data->operator[](3) = '0'; + EXPECT_FALSE(_secondary->sendFirmwareResp(image_data)); +} + #ifndef __NO_MAIN__ int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); if (argc != 2) { - std::cerr << "Error: " << argv[0] << " requires the path to the uptane-generator utility\n"; + std::cerr << "Error: " << argv[0] << " \n"; return EXIT_FAILURE; } - UPTANE_METADATA_GENERATOR = argv[1]; + OstreeSecondaryUptaneVerificationTest::setOstreeRootfsTemplate(argv[1]); logger_init(); - logger_set_threshold(boost::log::trivial::error); + logger_set_threshold(boost::log::trivial::info); return RUN_ALL_TESTS(); } #endif + +extern "C" OstreeDeployment* ostree_sysroot_get_booted_deployment(OstreeSysroot* ostree_sysroot) { + return OstreeSecondaryUptaneVerificationTest::curOstreeDeployment(ostree_sysroot); +} + +extern "C" const char* ostree_deployment_get_csum(OstreeDeployment* ostree_deployment) { + return OstreeSecondaryUptaneVerificationTest::curOstreeRootfsRev(ostree_deployment); +} diff --git a/src/libaktualizr/uptane/directorrepository.h b/src/libaktualizr/uptane/directorrepository.h index d67b75110f..8b51c4e5dd 100644 --- a/src/libaktualizr/uptane/directorrepository.h +++ b/src/libaktualizr/uptane/directorrepository.h @@ -16,7 +16,10 @@ class DirectorRepository : public RepositoryCommon { bool verifyTargets(const std::string& targets_raw); const Targets& getTargets() const { return targets; } - std::vector getTargets(const Uptane::EcuSerial& ecu_id) const { return targets.getTargets(ecu_id); } + std::vector getTargets(const Uptane::EcuSerial& ecu_id, + const Uptane::HardwareIdentifier& hw_id) const { + return targets.getTargets(ecu_id, hw_id); + } const std::string& getCorrelationId() const { return targets.correlation_id(); } bool targetsExpired() const; bool checkMetaOffline(INvStorage& storage); diff --git a/src/libaktualizr/uptane/fetcher.h b/src/libaktualizr/uptane/fetcher.h index 8b6c46469b..a2cedd50eb 100644 --- a/src/libaktualizr/uptane/fetcher.h +++ b/src/libaktualizr/uptane/fetcher.h @@ -27,6 +27,7 @@ class IMetadataFetcher { protected: IMetadataFetcher() = default; + IMetadataFetcher(IMetadataFetcher&&) = default; }; class Fetcher : public IMetadataFetcher { diff --git a/src/libaktualizr/uptane/tuf.cc b/src/libaktualizr/uptane/tuf.cc index 48f4507713..87e026f7fc 100644 --- a/src/libaktualizr/uptane/tuf.cc +++ b/src/libaktualizr/uptane/tuf.cc @@ -37,6 +37,24 @@ std::ostream &Uptane::operator<<(std::ostream &os, const EcuSerial &ecu_serial) return os; } +Hash Hash::generate(Type type, const std::string &data) { + std::string hash; + + switch (type) { + case Type::kSha256: { + hash = boost::algorithm::hex(Crypto::sha256digest(data)); + break; + } + case Type::kSha512: { + hash = boost::algorithm::hex(Crypto::sha512digest(data)); + break; + } + default: { throw std::invalid_argument("Unsupported hash type"); } + } + + return Hash(type, hash); +} + Hash::Hash(const std::string &type, const std::string &hash) : hash_(boost::algorithm::to_upper_copy(hash)) { if (type == "sha512") { type_ = Hash::Type::kSha512; diff --git a/src/libaktualizr/uptane/tuf.h b/src/libaktualizr/uptane/tuf.h index a8e8971f64..c12567f0fa 100644 --- a/src/libaktualizr/uptane/tuf.h +++ b/src/libaktualizr/uptane/tuf.h @@ -199,6 +199,7 @@ class Hash { // order corresponds algorithm priority enum class Type { kSha256, kSha512, kUnknownAlgorithm }; + static Hash generate(Type type, const std::string &data); Hash(const std::string &type, const std::string &hash); Hash(Type type, const std::string &hash); @@ -236,8 +237,8 @@ class Target { std::string filename() const { return filename_; } std::string sha256Hash() const; std::string sha512Hash() const; - std::vector hashes() const { return hashes_; }; - std::vector hardwareIds() const { return hwids_; }; + const std::vector &hashes() const { return hashes_; }; + const std::vector &hardwareIds() const { return hwids_; }; std::string custom_version() const { return custom_["version"].asString(); } Json::Value custom_data() const { return custom_; } void updateCustom(Json::Value &custom) { custom_ = custom; }; @@ -439,10 +440,16 @@ class Targets : public MetaWithKeys { terminating_role_.clear(); } - std::vector getTargets(const Uptane::EcuSerial &ecu_id) const { + std::vector getTargets(const Uptane::EcuSerial &ecu_id, + const Uptane::HardwareIdentifier &hw_id) const { std::vector result; for (auto it = targets.begin(); it != targets.end(); ++it) { - if (it->ecus().find(ecu_id) != it->ecus().end()) { + auto found_loc = std::find_if(it->ecus().begin(), it->ecus().end(), + [ecu_id, hw_id](const std::pair &val) { + return ((ecu_id == val.first) && (hw_id == val.second)); + }); + + if (found_loc != it->ecus().end()) { result.push_back(*it); } }