diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index 88d2a418dc..a8702292c1 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -795,7 +795,6 @@ INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../CONTRIBUTING.md \ @CMAKE_CURRENT_SOURCE_DIR@/../src/aktualizr_repo \ @CMAKE_CURRENT_SOURCE_DIR@/../src/aktualizr_secondary \ @CMAKE_CURRENT_SOURCE_DIR@/../src/cert_provider \ - @CMAKE_CURRENT_SOURCE_DIR@/../src/external_secondaries \ @CMAKE_CURRENT_SOURCE_DIR@/../src/hmi_stub \ @CMAKE_CURRENT_SOURCE_DIR@/../src/implicit_writer \ @CMAKE_CURRENT_SOURCE_DIR@/../src/libaktualizr \ diff --git a/docs/README.adoc b/docs/README.adoc index 7368a841c3..188da0bdf2 100644 --- a/docs/README.adoc +++ b/docs/README.adoc @@ -16,8 +16,6 @@ link:./hsm-provisioning.adoc[hsm-provisioning.adoc] - An explanation of aktualiz link:./implicit-provisioning.adoc[implicit-provisioning.adoc] - Implicit provisioning an alternative to automatic provisioning. It's distinct from automatic provisioning in that it requires each image to have some unique credentials side-loaded. -link:./legacysecondary.adoc[legacysecondary.adoc] - Aktualizr is designed for automotive use cases, including sending updates to secondary ECUs that don't have their own internet connection. - link:./linux-secondaries.adoc[linux-secondaries.adoc] - A quick how-to demonstrating aktualizr on a secondary ECU, using two QEMU devices. link:./opcua-bridge.adoc[opcua-bridge.adoc] - Some basic documentation on getting OPC-UA working. OPC-UA is a protocol for in-vehicle inter-ECU communication. diff --git a/docs/configuration.adoc b/docs/configuration.adoc index 22276a0233..1c935511d8 100644 --- a/docs/configuration.adoc +++ b/docs/configuration.adoc @@ -101,7 +101,6 @@ Options for Uptane. | `repo_server` | | Image repository server URL. If empty, set to `tls.server` with `/repo` appended. | `key_source` | `"file"` | Where to read the device's private key from. Options: `"file"`, `"pkcs11"`. | `key_type` | `"RSA2048"` | Type of cryptographic keys to use. Options: `"ED25519"`, `"RSA2048"`, `"RSA3072"` or `"RSA4096"`. -| `legacy_interface` | | Path to an executable interface for communicating with legacy secondary ECUs. See link:{aktualizr-github-url}/docs/legacysecondary.adoc[] for more information. |========================================================================================== == `discovery` diff --git a/docs/legacysecondary.adoc b/docs/legacysecondary.adoc deleted file mode 100644 index 112e7aa085..0000000000 --- a/docs/legacysecondary.adoc +++ /dev/null @@ -1,88 +0,0 @@ -= Legacy secondary support - -The Uptane specification indicates that secondary ECUs, i.e. ECUs that don't have direct access to the global network and can only get updates from the primary ECUs, should also be able to verify Uptane metadata and sign version manifests. In this way, secondaries are protected not only from compromise of the remote server, but also from compromise of the primary ECU or in-vehicle network. - -However, in some cases it might be necessary to be able to update legacy secondaries via the Uptane primary without modifying the secondaries' flashing procedure. Such secondaries still take full advantage of Uptane's protection against remote server/global network compromise and can also implement some in-ECU protection with proprietary mechanisms. - -In the latter case, the primary (i.e. aktualizr) will take care of verifying metadata and signing version manifests on behalf of secondaries by using an external utility for ECU discovery and flashing. - -== Flasher command line interface - -The command line interface shall support three commands. - -=== api-version - - $ example-flasher api-version - -Returns a single line with the version number of the API that the tool supports. At present, there is only one version, so this should always return `1`. This command should always succeed. - -=== list-ecus - - $ example-flasher list-ecus - -Returns a list of the ECUs that are attached to the system. Each line starts with the hardware identifier, and may optionally include the serial number as the second column. For example: - - - - - ... - -Hardware IDs are used by Uptane to check if a firmware image is suitable for the particular device, so the hardware ID specified for your image in https://docs.atsgarage.com/[OTA Connect] should exactly match what is returned by the flasher. - -The ECU serial uniquely identifies an ECU in the Uptane repository, so it should be unique globally, not just within the vehicle scope. If a serial is not provided for an entry, a unique serial is generated randomly by the primary for that entry. - -Hardware IDs for ECUs without provided serials should be unique within the scope of the list. Each entry is treated as a single ECU by Uptane. If you want to flash multiple ECUs of the same kind and maintain atomicity of the update, it is the responsibility of the flasher to verify that all the ECUs either get the new image or continue to run the old image if the update fails. - -The command shall fail if the ECUs could not be found due to communication error or if the discovered number of ECUs does not match the expected value. On subsequent runs, the command should output the same number of ECUs. If some ECUs stop responding, these can be skipped. ECU hotplugging is not supported. - -=== install-software - - $ example-flasher install-software --hardware-identifier [--ecu-identifier ecu-serial] --firmware /path/to/firmware.img - -Delivers an update to a particular ECU. `--ecu-identifier` should be provided for any ECU that was listed with a serial by the `list-ecus` command. If `list-ecus` did not provide a serial for a given ECU, `--ecu-identifier` may be skipped for that ECU, and the flasher should ignore it if it is provided. - -The command's return value should indicate the result of the installation: - -[options=header] -|=================== -| Return value | Meaning -| 0 | Installation completed successfully. -| 1 | The firmware image is invalid. -| 2 | Installation failure. The previous firmware was not modified. -| 3 | Installation failure. The previous firmware was partially overwritten or erased. -|=================== - -=== Additional options - -All three commands should support one additional option to control the log level: - - $ example-flasher --log-level N - -Valid values for `N` are `0..4`: - -[options=header] -|=================== -| Log level | Meaning -| 0 | Trace -| 1 | Debug -| 2 | Informational -| 3 | Warning -| 4 | Error -|=================== - -The flasher may ignore this option. - -== Integration with aktualizr - -Aktualizr has a `--legacy-interface /path/to/example-flasher` command line option which will make all the ECUs reported by the `list-ecus` command available to the aktualizr and https://docs.atsgarage.com/[OTA Connect]. - -In most cases you will want to build your Uptane-enabled system using the https://github.com/advancedtelematic/meta-updater[meta-updater] Yocto layer. Please read the documentation listed in the README for general information about building with meta-updater. Adding support for legacy secondaries requires two more things to do: - - . Create a recipe that will install your flasher at some location in the device's file system. For more information on how to write a recipe, please consult the http://www.yoctoproject.org/docs/current/dev-manual/dev-manual.html#new-recipe-writing-a-new-recipe[Yocto documentation]. - . In your local.conf set `SOTA_LEGACY_SECONDARY_INTERFACE = ""`, where the path is the absolute path to the flasher as seen from the booted device. - -Make sure that you're using a version of meta-updater that is new enough to support these features. The head of the `pyro` branch supports it at the time of writing. - -== Additional requirements for secondaries - -As a result of an update, a secondary should run either the new version of firmware (success case) or the one it was running before the update (failure case). The traditional way to implement this requirement is by using a dual-bank memory layout on the secondary. diff --git a/docs/secondaries.svg b/docs/secondaries.svg index effd43f775..d12dbe9748 100644 --- a/docs/secondaries.svg +++ b/docs/secondaries.svg @@ -14,7 +14,7 @@ viewBox="0 0 297 210" version="1.1" id="svg8" - inkscape:version="0.92.1 r15371" + inkscape:version="0.92.3 (2405546, 2018-03-11)" sodipodi:docname="secondaries.svg"> @@ -186,21 +186,6 @@ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1" id="path7736" /> - - - - - - VirtualSecondary LegacySecondary + id="tspan3867" + x="96.308334" + y="151.47913">Customer Secondary UptaneSecondary + id="g6390" + transform="translate(-17.991667)"> Legacy ECU Uptane ECU - example_interface - - (), "serial number of primary ecu") ("primary-ecu-hardware-id", bpo::value(), "hardware ID of primary ecu") ("secondary-config", bpo::value >()->composing(), "secondary ECU json configuration file") - ("legacy-interface", bpo::value(), "path to legacy secondary ECU interface program") - ("campaign-id", bpo::value(), "id of the campaign to act on") - ("disable-keyid-validation", "deprecated") - ("gateway-socket", bpo::value(), "deprecated"); + ("campaign-id", bpo::value(), "id of the campaign to act on"); // clang-format on // consider the first positional argument as the aktualizr running mode diff --git a/src/external_secondaries/CMakeLists.txt b/src/external_secondaries/CMakeLists.txt deleted file mode 100644 index 05c3760eeb..0000000000 --- a/src/external_secondaries/CMakeLists.txt +++ /dev/null @@ -1,52 +0,0 @@ -# set headers used for clang format -set(ECU_INTERFACE_HEADERS ecuinterface.h make_ecu.h) - -set(MAIN_ECU_INTERFACE_SRC interface_main.cc make_ecu.cc) - -add_executable(example-interface ${MAIN_ECU_INTERFACE_SRC} example_flasher.cc) -target_link_libraries(example-interface - aktualizr_static_lib - ${AKTUALIZR_EXTERNAL_LIBS}) -target_compile_options(example-interface PUBLIC -DECUFLASHER_EXAMPLE) - -add_executable(aktualizr-validate-secondary-interface validate_secondary_interface_test.cc) -target_link_libraries(aktualizr-validate-secondary-interface ${Boost_LIBRARIES} gtest) - -aktualizr_source_file_checks(${MAIN_ECU_INTERFACE_SRC} ${ECU_INTERFACE_HEADERS} - example_flasher.cc example_flasher.h validate_secondary_interface_test.cc) - -install(TARGETS example-interface RUNTIME DESTINATION bin) - - -set(ISOTP_PATH_PREFIX ../../partial/extern/isotp-c/src) -set(BITFIELD_PATH_PREFIX ../../partial/extern/isotp-c/deps/bitfield-c/src) -set(ISOTP_SRC ${ISOTP_PATH_PREFIX}/isotp/isotp.c - ${ISOTP_PATH_PREFIX}/isotp/send.c - ${ISOTP_PATH_PREFIX}/isotp/receive.c - ${BITFIELD_PATH_PREFIX}/bitfield/8byte.c - ${BITFIELD_PATH_PREFIX}/bitfield/bitarray.c - ${BITFIELD_PATH_PREFIX}/bitfield/bitfield.c) -set(ISOTP_TEST_SRC test_isotp_interface.cc isotp_allocate.cc isotp_protocol.cc ) -set(ISOTP_TEST_HEADERS test_isotp_interface.h) -set_source_files_properties(${ISOTP_SRC} PROPERTIES COMPILE_FLAGS "-Wno-sign-conversion -Wno-conversion") -aktualizr_source_file_checks(${ISOTP_TEST_SRC} ${ISOTP_TEST_HEADERS}) - -if(BUILD_ISOTP) - add_executable(isotp-test-interface ${MAIN_ECU_INTERFACE_SRC} ${ISOTP_TEST_SRC} ${ISOTP_SRC}) - target_link_libraries(isotp-test-interface - aktualizr_static_lib - ${AKTUALIZR_EXTERNAL_LIBS}) - target_compile_options(isotp-test-interface PUBLIC -DECUFLASHER_TEST_ISOTP) - target_include_directories(isotp-test-interface PUBLIC ${ISOTP_PATH_PREFIX} ${BITFIELD_PATH_PREFIX}) - - install(TARGETS isotp-test-interface RUNTIME DESTINATION bin) -endif(BUILD_ISOTP) - -add_dependencies(build_tests aktualizr-validate-secondary-interface example-interface) -add_test(NAME validate-secondary-interface - COMMAND aktualizr-validate-secondary-interface - --target ${PROJECT_BINARY_DIR}/src/external_secondaries/example-interface - --firmware ${PROJECT_SOURCE_DIR}/tests/test_data/firmware.txt) - - -# vim: set tabstop=4 shiftwidth=4 expandtab: diff --git a/src/external_secondaries/ecuinterface.h b/src/external_secondaries/ecuinterface.h deleted file mode 100644 index 4ce44bc013..0000000000 --- a/src/external_secondaries/ecuinterface.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef ECUINTERFACE_H_ -#define ECUINTERFACE_H_ - -#include - -class ECUInterface { - public: - enum InstallStatus { - InstallationSuccessful = 0, - FirmawareIsInvalid, - InstallFailureNotModified, - InstallFailureModified - }; - virtual ~ECUInterface() = default; - virtual std::string apiVersion() = 0; - virtual std::string listEcus() = 0; - virtual InstallStatus installSoftware(const std::string &hardware_id, const std::string &ecu_id, - const std::string &firmware) = 0; -}; - -#endif // ECUINTERFACE_H_ diff --git a/src/external_secondaries/example_flasher.cc b/src/external_secondaries/example_flasher.cc deleted file mode 100644 index ffae83126a..0000000000 --- a/src/external_secondaries/example_flasher.cc +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include - -#include - -#include "example_flasher.h" -#include "utilities/utils.h" - -std::string filename; -std::string serial; - -ExampleFlasher::ExampleFlasher(unsigned int loglevel) : loglevel_(loglevel) { - // Use /var/sota if it is available and accessible to the current user. - // Generally this is true for devices but not hosts. - if (boost::filesystem::exists("/var/sota")) { - if (access("/var/sota", R_OK | W_OK | X_OK) != 0) { - filename = "/tmp/example_serial"; - } else { - filename = "/var/sota/example_serial"; - } - } else { - filename = "/tmp/example_serial"; - } - - if (boost::filesystem::exists(filename)) { - serial = Utils::readFile(filename); - } else { - serial = Utils::randomUuid(); - Utils::writeFile(filename, serial); - } -} - -std::string ExampleFlasher::apiVersion() { - if (loglevel_ == 0) { - std::cerr << "Displaying api version:\n"; - } - return "1"; -} - -std::string ExampleFlasher::listEcus() { - if (loglevel_ == 0) { - std::cerr << "Displaying list of ecus:\n"; - } - return std::string("example1 ") + serial + "\n" + "example2\n"; -} - -ExampleFlasher::InstallStatus ExampleFlasher::installSoftware(const std::string &hardware_id, const std::string &ecu_id, - const std::string &firmware) { - if (loglevel_ == 0) { - std::cerr << "Installing hardware_id: " << hardware_id << ", ecu_id: " << ecu_id << " firmware_path: " << firmware - << "\n"; - } - if (hardware_id == "example1" && ecu_id == serial) { - return InstallationSuccessful; - } - if (hardware_id == "example2") { - return InstallationSuccessful; - } - return InstallFailureNotModified; -} diff --git a/src/external_secondaries/example_flasher.h b/src/external_secondaries/example_flasher.h deleted file mode 100644 index ccfbed14fb..0000000000 --- a/src/external_secondaries/example_flasher.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef EXAMPLE_FLASHER_H_ -#define EXAMPLE_FLASHER_H_ - -#include "ecuinterface.h" - -class ExampleFlasher : public ECUInterface { - public: - ~ExampleFlasher() override = default; - explicit ExampleFlasher(unsigned int loglevel); - std::string apiVersion() override; - std::string listEcus() override; - InstallStatus installSoftware(const std::string &hardware_id, const std::string &ecu_id, - const std::string &firmware) override; - - private: - unsigned int loglevel_; -}; - -#endif // EXAMPLE_FLASHER_H_ diff --git a/src/external_secondaries/interface_main.cc b/src/external_secondaries/interface_main.cc deleted file mode 100644 index b26e62b4f9..0000000000 --- a/src/external_secondaries/interface_main.cc +++ /dev/null @@ -1,83 +0,0 @@ -#include -#include -#include -#include - -#include "make_ecu.h" - -namespace po = boost::program_options; -using std::string; - -int main(int argc, char **argv) { - po::options_description desc( - "Usage: ecuinterface api-version | list-ecus | install-software [options]\nAllowed options"); - - string command; - string hardware_identifier; - string ecu_identifier; - boost::filesystem::path firmware_path; - - // clang-format off - desc.add_options() - ("help,h", "print usage") - ("loglevel,l", po::value()->default_value(2), - "set log level 0-5 (trace, debug, info, warning, error, fatal)") - ("hardware-identifier", po::value(&hardware_identifier), - "hardware identifier of the ECU where the update should be installed (e.g. rh850)") - ("ecu-identifier", po::value(&ecu_identifier), - "unique serial number of the ECU where the update should be installed (e.g. ‘abcdef12345’)") - ("firmware", po::value(&firmware_path), - "absolute path to the firmware image to be installed.") - ("command", po::value(&command), - "command to run: api-version | list-ecus | install-software"); - // clang-format on - - po::positional_options_description positional_options; - positional_options.add("command", 1); - - try { - po::variables_map vm; - po::store(po::command_line_parser(argc, argv).options(desc).positional(positional_options).run(), vm); - po::notify(vm); - if (vm.count("help") != 0) { - std::cout << desc << '\n'; - exit(EXIT_SUCCESS); - } - - if (vm.count("command") != 0u) { - ECUInterface *ecu = make_ecu(vm["loglevel"].as()); - if (command == "api-version") { - std::cout << ecu->apiVersion(); - return EXIT_SUCCESS; - } - if (command == "list-ecus") { - std::cout << ecu->listEcus(); - return EXIT_SUCCESS; - } - if (command == "install-software") { - if ((vm.count("hardware-identifier") == 0u) || (vm.count("firmware") == 0u)) { - std::cerr << "install-software command requires --hardware-identifier, --firmware, and possibly " - "--ecu-identifier.\n"; - return EXIT_FAILURE; - } - ECUInterface::InstallStatus result = - ecu->installSoftware(hardware_identifier, ecu_identifier, firmware_path.string()); - std::cout << "Installation result: " << result << "\n"; - return result; - } - std::cout << "unknown command: " << command[0] << "\n"; - std::cout << desc; - - return EXIT_SUCCESS; - } - std::cout << "You must provide a command.\n"; - std::cout << desc; - return EXIT_FAILURE; - - } catch (const po::error &o) { - std::cout << o.what(); - std::cout << desc; - return EXIT_FAILURE; - } -} -// vim: set tabstop=2 shiftwidth=2 expandtab: diff --git a/src/external_secondaries/isotp_allocate.cc b/src/external_secondaries/isotp_allocate.cc deleted file mode 100644 index 79fe5275d6..0000000000 --- a/src/external_secondaries/isotp_allocate.cc +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include -extern "C" { -uint8_t* allocate(size_t size) { return static_cast(malloc((sizeof(uint8_t)) * size)); } - -void free_allocated(uint8_t* data) { free(data); } -} diff --git a/src/external_secondaries/isotp_protocol.cc b/src/external_secondaries/isotp_protocol.cc deleted file mode 100644 index fa212e0417..0000000000 --- a/src/external_secondaries/isotp_protocol.cc +++ /dev/null @@ -1,12 +0,0 @@ -#include - -extern "C" { - -uint32_t protocol_swap_sa_ta(uint32_t af) { - uint32_t res = (af >> 5) & 0x1F; - - res |= (af & 0x1F) << 5; - - return res; -} -} diff --git a/src/external_secondaries/make_ecu.cc b/src/external_secondaries/make_ecu.cc deleted file mode 100644 index 522168efcc..0000000000 --- a/src/external_secondaries/make_ecu.cc +++ /dev/null @@ -1,17 +0,0 @@ -#ifdef ECUFLASHER_EXAMPLE -#include "example_flasher.h" -#endif - -#ifdef ECUFLASHER_TEST_ISOTP -#include "test_isotp_interface.h" -#endif - -ECUInterface* make_ecu(unsigned int loglevel) { -#if defined(ECUFLASHER_EXAMPLE) - return (new ExampleFlasher(loglevel)); -#elif defined(ECUFLASHER_TEST_ISOTP) - return (new TestIsotpInterface(loglevel)); -#else - return NULL; -#endif -} diff --git a/src/external_secondaries/make_ecu.h b/src/external_secondaries/make_ecu.h deleted file mode 100644 index 05a72cafda..0000000000 --- a/src/external_secondaries/make_ecu.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef MAKE_ECU_H_ -#define MAKE_ECU_H_ - -#include "ecuinterface.h" - -ECUInterface* make_ecu(unsigned int loglevel); - -#endif // MAKE_ECU_H_ diff --git a/src/external_secondaries/test_isotp_interface.cc b/src/external_secondaries/test_isotp_interface.cc deleted file mode 100644 index 11d1d8ce1b..0000000000 --- a/src/external_secondaries/test_isotp_interface.cc +++ /dev/null @@ -1,420 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "test_isotp_interface.h" -#include "utilities/utils.h" - -#define HW_ID_DID 0x0001 -#define ECU_SERIAL_DID 0x0002 - -TestIsotpInterface::TestIsotpInterface(const unsigned int loglevel, uint32_t canid, std::string canif) - : loglevel_(loglevel), canId(canid), canIface(std::move(canif)) { - can_socket = socket(PF_CAN, SOCK_RAW, CAN_RAW); - - if (can_socket < -1) { - throw std::runtime_error("Unable to open socket"); - } - - struct can_filter filter {}; - filter.can_id = canId & 0x1F; - filter.can_mask = 0x1F; - setsockopt(can_socket, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(filter)); - - struct ifreq ifr {}; - memcpy(ifr.ifr_name, canIface.c_str(), IFNAMSIZ); - - if (ioctl(can_socket, SIOCGIFINDEX, &ifr) != 0) { - throw std::runtime_error("Unable to get interface index"); - } - - struct sockaddr_can addr {}; - addr.can_family = AF_CAN; - addr.can_ifindex = ifr.ifr_ifindex; // NOLINT - - if (bind(can_socket, reinterpret_cast(&addr), sizeof(addr)) < 0) { - throw std::runtime_error("Unable to bind socket"); - } - - isotp_shims = isotp_init_shims(nullptr, isoTpSend, nullptr, this); - - populateEcus(); -} - -std::string TestIsotpInterface::apiVersion() { - if (loglevel_ == 0) { - std::cerr << "Displaying api version" << std::endl; - } - return "1"; -} - -std::string TestIsotpInterface::listEcus() { - if (loglevel_ == 0) { - std::cerr << "Displaying list of ecus:" << std::endl; - } - - std::string res; - std::map, uint32_t>::iterator it; - for (it = ecus.begin(); it != ecus.end(); ++it) { - const std::string& hw_id = it->first.first; - const std::string& ecu_serial = it->first.second; - res += ((hw_id + "\t") += ecu_serial) += "\n"; - } - - return res; -} - -TestIsotpInterface::InstallStatus TestIsotpInterface::installSoftware(const std::string& hardware_id, - const std::string& ecu_id, - const std::string& firmware) { - if (loglevel_ == 0) { - std::cerr << "Installing hardware_id: " << hardware_id << ", ecu_id: " << ecu_id << " firmware_path: " << firmware - << std::endl; - } - - std::map, uint32_t>::const_iterator it = - ecus.find(std::make_pair(hardware_id, ecu_id)); - if (it == ecus.end()) { - std::cerr << "ECU with hardware_id " << hardware_id << " and serial number " << ecu_id << " was not found." - << std::endl; - return InstallFailureNotModified; - } - uint32_t id = it->second; - - if (!boost::filesystem::exists(firmware)) { - std::cerr << "Firmware not found on path " << firmware << std::endl; - return InstallFailureNotModified; - } - std::string firmware_content = Utils::readFile(firmware); - - std::string payload; - std::string resp; - - payload.push_back(0x10); // DiagnosticSessionControl - payload.push_back(0x02); // ProgrammingSession - - struct timeval timeout = {0, 100000}; // 100ms - if (!sendRecvUds(payload, &resp, canId, id, &timeout) || resp.empty()) { - std::cerr << "Error entering programming mode" << std::endl; - return InstallFailureNotModified; - } - - uint32_t start_addr = 0x00008000; // TODO: get from ECU - uint32_t size = firmware_content.size(); - payload.clear(); - payload.push_back(0x31); // RoutineControl - payload.push_back(0x01); // Start routine - payload.push_back(0xFF); // Erase - payload.push_back(0x00); - payload.push_back(start_addr >> 24); - payload.push_back(start_addr >> 16); - payload.push_back(start_addr >> 8); - payload.push_back(start_addr); - payload.push_back(size >> 24); - payload.push_back(size >> 16); - payload.push_back(size >> 8); - payload.push_back(size); - - if (!sendRecvUds(payload, &resp, canId, id, &timeout) || resp.empty()) { - std::cerr << "Error erasing flash" << std::endl; - return InstallFailureNotModified; - } - - payload.clear(); - payload.push_back(0x34); // RequestDownload - payload.push_back(0x00); // Raw data - payload.push_back(0x44); // 4 bytes long address and size - payload.push_back(start_addr >> 24); - payload.push_back(start_addr >> 16); - payload.push_back(start_addr >> 8); - payload.push_back(start_addr); - payload.push_back(size >> 24); - payload.push_back(size >> 16); - payload.push_back(size >> 8); - payload.push_back(size); - - if (!sendRecvUds(payload, &resp, canId, id, &timeout) || resp.empty()) { - std::cerr << "RequestDownload failed" << std::endl; - return InstallFailureModified; - } - if (resp[0] != (0x34 | 0x40)) { - std::cerr << "Unexpected response on RequestDownload" << std::endl; - return InstallFailureModified; - } - if (((resp[1] >> 4) & 0x0f) > 4) { - std::cerr << "Block size doesn't fit in 4 bytes" << std::endl; - return InstallFailureModified; - } - uint32_t block_size = 0; - for (uint8_t i = 0; i < ((resp[1] >> 4) & 0x0f); i++) { - block_size |= resp[2 + i]; - block_size <<= 8; - } - - if (block_size > kMaxBlockSize) { - block_size = kMaxBlockSize; - } - - uint8_t seqn = 1; - - for (size_t i = 0; i < size; i += block_size) { - payload.clear(); - payload.push_back(0x36); // TransferData - payload.push_back(seqn++); - - int len = (size - i >= block_size) ? block_size : size - i; - - for (int j = 0; j < len; j++) { - payload.push_back(firmware_content[i + j]); - } - - if (!sendRecvUds(payload, &resp, canId, id, &timeout) || resp.empty()) { - std::cerr << "TransferData failed" << std::endl; - return InstallFailureModified; - } - } - payload.clear(); - payload.push_back(0x37); // RequestTransferExit - if (!sendRecvUds(payload, &resp, canId, id, &timeout) || resp.empty()) { - std::cerr << "RequestTransferExit failed" << std::endl; - return InstallFailureModified; - } - - payload.clear(); - payload.push_back(0x11); // ECUReset - payload.push_back(0x01); // Hard reset - if (!sendRecvUds(payload, &resp, canId, id, &timeout) || resp.empty()) { - std::cerr << "RequestTransferExit failed" << std::endl; - return InstallFailureModified; - } - - return InstallationSuccessful; -} - -uint16_t TestIsotpInterface::makeCanAf(uint16_t sa, uint16_t ta) { return ((sa << 5) & 0x3E0) | (ta & 0x1F); } - -bool TestIsotpInterface::isoTpSend(const uint32_t arbitration_id, const uint8_t* data, const uint8_t size, - void* private_data) { - auto* instance = static_cast(private_data); - - if ((instance == nullptr) || size > 8) { - return false; - } - - if (instance->loglevel_ == 0) { - std::cerr << "Sending CAN message AF: 0x" << std::hex << arbitration_id << "; Data:"; - for (int i = 0; i < size; i++) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - std::cerr << " " << std::hex << static_cast(data[i]); - } - std::cerr << std::endl; - } - - int can_socket = instance->can_socket; - - struct can_frame frame {}; - - frame.can_id = arbitration_id; - frame.can_dlc = size; - memcpy(frame.data, data, size); - - ssize_t res = write(can_socket, &frame, sizeof(frame)); - if (res < 0) { - std::cerr << "CAN write error: " << strerror(errno) << std::endl; - return false; - } - if (res != sizeof(frame)) { - std::cerr << "CAN write error: " << res << " bytes of " << sizeof(frame) << " were sent" << std::endl; - return false; - } - return true; -} - -bool TestIsotpInterface::sendRecvUds(const std::string& out, std::string* in, uint16_t sa, uint16_t ta, - struct timeval* to) { - if (out.empty()) { - return false; - } - - IsoTpMessage message_tx = - isotp_new_send_message(makeCanAf(sa, ta), reinterpret_cast(out.c_str()), out.length()); - IsoTpSendHandle send_handle = isotp_send(&isotp_shims, &message_tx, nullptr); - if (send_handle.completed) { - if (!send_handle.success) { - std::cerr << "Message send failed" << std::endl; - return false; - } - } else { - while (true) { - fd_set read_set; - FD_ZERO(&read_set); - FD_SET(can_socket, &read_set); - - struct timeval timeout = *to; - if (select((can_socket + 1), &read_set, nullptr, nullptr, &timeout) >= 0) { - if (FD_ISSET(can_socket, &read_set)) { - struct can_frame f {}; - int ret = read(can_socket, &f, sizeof(f)); - if (ret < 0) { - std::cerr << "Error receiving CAN frame" << std::endl; - return false; - } - - if (!isotp_receive_flowcontrol(&isotp_shims, &send_handle, f.can_id, f.data, f.can_dlc)) { - std::cerr << "IsoTp receiving error" << std::endl; - return false; - } - - while (send_handle.to_send != 0) { - // TODO: fix firmware - usleep(100000); - if (!isotp_continue_send(&isotp_shims, &send_handle)) { - std::cerr << "IsoTp sending error" << std::endl; - return false; - } - if (send_handle.completed) { - if (send_handle.success) { - break; // proceed to waiting for response - } - std::cerr << "IsoTp send failed" << std::endl; - return false; - } - } - - } else { - if (loglevel_ == 0) { - std::cerr << "Timeout on CAN socket" << std::endl; - } - *in = ""; - return true; - } - if (send_handle.completed) { - break; - } - } else { - std::cerr << "Select failed" << std::endl; - return false; - } - } - } - - IsoTpReceiveHandle recv_handle = isotp_receive(&isotp_shims, makeCanAf(ta, sa), nullptr); - - while (true) { - fd_set read_set; - FD_ZERO(&read_set); - FD_SET(can_socket, &read_set); - - struct timeval timeout = *to; - if (select((can_socket + 1), &read_set, nullptr, nullptr, &timeout) >= 0) { - if (FD_ISSET(can_socket, &read_set)) { - struct can_frame f {}; - int ret = read(can_socket, &f, sizeof(f)); - if (ret < 0) { - std::cerr << "Error receiving CAN frame" << std::endl; - return false; - } - - IsoTpMessage message_rx = isotp_continue_receive(&isotp_shims, &recv_handle, f.can_id, f.data, f.can_dlc); - if (message_rx.completed && recv_handle.completed) { - if (!recv_handle.success) { - std::cerr << "IsoTp receiving error" << std::endl; - return false; - } - *in = std::string(reinterpret_cast(message_rx.payload), static_cast(message_rx.size)); - return true; - } - } else { - if (loglevel_ == 0) { - std::cerr << "Timeout on CAN socket" << std::endl; - } - *in = ""; - return true; - } - } else { - std::cerr << "Select failed" << std::endl; - return false; - } - } -} - -void TestIsotpInterface::populateEcus() { - uint8_t payload_hwid[3] = {0x22, (HW_ID_DID >> 8), (HW_ID_DID & 0xFF)}; - uint8_t payload_serial[3] = {0x22, (ECU_SERIAL_DID >> 8), (ECU_SERIAL_DID & 0xFF)}; - - struct timeval timeout = {0, 20000}; // 20ms - - for (uint8_t id = 0x01; id <= 0x1f; id++) { - std::string resp; - std::string hwid; - if (!sendRecvUds(std::string(reinterpret_cast(payload_hwid), static_cast(3)), &resp, canId, id, - &timeout)) { - std::cerr << "Error sending request for HW ID for id " << static_cast(id) << std::endl; - continue; - } - if (resp.empty()) { // timeout - if (loglevel_ == 0) { - std::cerr << "Request for HW ID for id " << static_cast(id) << " timed out" << std::endl; - } - continue; - } - - if (resp.length() < 3) { - std::cerr << "Invalid response to request for HW ID" << std::endl; - continue; - } - - if (resp[0] != 0x62) { - std::cerr << "Invalid response id " << static_cast(resp[0]) << " when " << 0x62 << " was expected" - << std::endl; - continue; - } - - if (((resp[1] << 8) | resp[2]) != HW_ID_DID) { - std::cerr << "Invalid DID " << ((resp[1] << 8) | resp[2]) << " when " << HW_ID_DID << " was expected" - << std::endl; - continue; - } - - hwid = resp.substr(3); - if (!sendRecvUds(std::string(reinterpret_cast(payload_serial), static_cast(3)), &resp, canId, - id, &timeout)) { - std::cerr << "Error sending request for ECU serial for id " << static_cast(id) << std::endl; - continue; - } - - if (resp.empty()) { // timeout - if (loglevel_ == 0) { - std::cerr << "Request for HW ID for id " << static_cast(id) << " timed out" << std::endl; - } - continue; - } - - if (resp.length() < 3) { - std::cerr << "Invalid response to request for HW ID" << std::endl; - continue; - } - - if (resp[0] != 0x62) { - std::cerr << "Invalid response id " << static_cast(resp[0]) << " when " << 0x62 << " was expected" - << std::endl; - continue; - } - - if (((resp[1] << 8) | resp[2]) != ECU_SERIAL_DID) { - std::cerr << "Invalid DID " << ((resp[1] << 8) | resp[2]) << " when " << ECU_SERIAL_DID << " was expected" - << std::endl; - continue; - } - ecus[std::make_pair(hwid, resp.substr(3))] = id; - } -} diff --git a/src/external_secondaries/test_isotp_interface.h b/src/external_secondaries/test_isotp_interface.h deleted file mode 100644 index 402049bdd4..0000000000 --- a/src/external_secondaries/test_isotp_interface.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef TEST_ISOTP_INTERFACE_H_ -#define TEST_ISOTP_INTERFACE_H_ - -#include -#include -#include - -#include "isotp/isotp.h" - -#include "ecuinterface.h" - -const uint32_t kDefaultCanId = 0x03; -const uint32_t kMaxBlockSize = 1024; // 1K -const std::string kDefaultCanIf = "can0"; -class TestIsotpInterface : public ECUInterface { - public: - TestIsotpInterface(unsigned int loglevel, uint32_t canid = kDefaultCanId, std::string canif = kDefaultCanIf); - std::string apiVersion() override; - std::string listEcus() override; - InstallStatus installSoftware(const std::string& hardware_id, const std::string& ecu_id, - const std::string& firmware) override; - - private: - // (hardware_id, ecu_serial) -> CAN address - std::map, uint32_t> ecus; - - void populateEcus(); - uint16_t makeCanAf(uint16_t sa, uint16_t ta); - bool sendRecvUds(const std::string& out, std::string* in, uint16_t sa, uint16_t ta, struct timeval* to); - - static bool isoTpSend(uint32_t arbitration_id, const uint8_t* data, uint8_t size, void* private_data); - - int can_socket; - - int loglevel_; - uint32_t canId; - std::string canIface; - IsoTpShims isotp_shims{}; -}; - -#endif // TEST_ISOTP_INTERFACE_H_ diff --git a/src/external_secondaries/validate_secondary_interface_test.cc b/src/external_secondaries/validate_secondary_interface_test.cc deleted file mode 100644 index f053fc98a0..0000000000 --- a/src/external_secondaries/validate_secondary_interface_test.cc +++ /dev/null @@ -1,131 +0,0 @@ -#include - -#include -#include - -#include -#include -#include - -namespace bpo = boost::program_options; - -std::string firmware; -std::string target; - -std::string exec(const std::string &cmd) { - std::array buffer; - std::string result; - std::shared_ptr pipe(popen(cmd.c_str(), "r"), pclose); - if (!pipe) throw std::runtime_error("popen() failed!"); - while (!feof(pipe.get())) { - if (fgets(buffer.data(), 128, pipe.get()) != nullptr) result += buffer.data(); - } - return result; -} - -bool isVersionGood(const std::string &output) { - std::string version = "1"; - return (output == version || output == version + "\n"); -} - -TEST(ECUInterface, check_version) { - std::string cmd = target + " api-version"; - EXPECT_TRUE(isVersionGood(exec(cmd))); -} - -TEST(ECUInterface, check_loglevel) { - std::string cmd = target + " api-version --loglevel 4"; - EXPECT_TRUE(isVersionGood(exec(cmd))); -} - -bool isECUListValid(const std::string &output) { - std::smatch ecu_match; - std::regex ecu_regex(R"([\w-]+(?:\s*[\w-]+)?\s*)"); - - size_t matched_symbols = 0; - std::string::const_iterator search_start(output.cbegin()); - while (std::regex_search(search_start, output.cend(), ecu_match, ecu_regex)) { - matched_symbols += static_cast(ecu_match.length()); - search_start += ecu_match.position() + ecu_match.length(); - } - return matched_symbols == output.size(); -} - -TEST(ECUInterface, check_list_ecus) { - std::string cmd = target + " list-ecus"; - EXPECT_TRUE(isECUListValid(exec(cmd))); -} - -TEST(ECUInterface, check_list_ecus_loglevel) { - std::string cmd = target + " list-ecus --loglevel 4"; - EXPECT_TRUE(isECUListValid(exec(cmd))); -} - -TEST(ECUInterface, install_good) { - std::string output = exec(target + " list-ecus"); - std::stringstream ss(output); - std::string buffer; - int line = 0; - while (std::getline(ss, buffer, '\n')) { - ++line; - std::vector ecu_info; - boost::split(ecu_info, buffer, boost::is_any_of(" \n\r\t"), boost::token_compress_on); - std::string cmd; - - if (ecu_info.size() == 0) { - std::cout << "Line " << line << ": empty line ignored.\n"; - continue; - } - if (ecu_info.size() >= 1) { - cmd = target + " install-software --firmware " + firmware + " --hardware-identifier " + ecu_info[0]; - } - if (ecu_info.size() >= 2) { - cmd += std::string(" --ecu-identifier ") + ecu_info[1]; - } - if (ecu_info.size() >= 3) { - std::cout << "Line " << line << ": further content after second token ignored.\n"; - } - - EXPECT_EQ(system(cmd.c_str()), 0); - } -} - -TEST(ECUInterface, install_bad) { - std::string cmd = target + - " install-software --firmware somepath --hardware-identifier unexistent" - " --ecu-identifier unexistentserial"; - EXPECT_NE(system(cmd.c_str()), 0); -} - -TEST(ECUInterface, install_bad_good_firmware) { - std::string cmd = target + " install-software --firmware " + firmware + - " --hardware-identifier unexistent" - " --ecu-identifier unexistentserial"; - EXPECT_NE(system(cmd.c_str()), 0); -} - -#ifndef __NO_MAIN__ -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - bpo::options_description description("aktualizr-validate-secondary-interface command line options"); - bpo::variables_map vm; - // clang-format off - description.add_options() - ("target", bpo::value(&target)->required(), "target executable") - ("firmware", bpo::value(&firmware)->required(), "firmware to install"); - // clang-format on - bpo::basic_parsed_options parsed_options = bpo::command_line_parser(argc, argv).options(description).run(); - try { - bpo::store(parsed_options, vm); - bpo::notify(vm); - } catch (const bpo::required_option &ex) { - std::cout << ex.what() << std::endl << description; - return EXIT_FAILURE; - } catch (const bpo::error &ex) { - std::cout << ex.what() << std::endl << description; - return EXIT_FAILURE; - } - - return RUN_ALL_TESTS(); -} -#endif diff --git a/src/libaktualizr/config/config.cc b/src/libaktualizr/config/config.cc index f28b2e4ccc..6f774c153b 100644 --- a/src/libaktualizr/config/config.cc +++ b/src/libaktualizr/config/config.cc @@ -76,9 +76,8 @@ void UptaneConfig::updateFromPropertyTree(const boost::property_tree::ptree& pt) CopyFromConfig(repo_server, "repo_server", pt); CopyFromConfig(key_source, "key_source", pt); CopyFromConfig(key_type, "key_type", pt); - CopyFromConfig(legacy_interface, "legacy_interface", pt); // uptane.secondary_configs is populated by processing secondary configs from - // the commandline and uptane.legacy_interface. + // the commandline. } void UptaneConfig::writeToStream(std::ostream& out_stream) const { @@ -88,7 +87,6 @@ void UptaneConfig::writeToStream(std::ostream& out_stream) const { writeOption(out_stream, repo_server, "repo_server"); writeOption(out_stream, key_source, "key_source"); writeOption(out_stream, key_type, "key_type"); - writeOption(out_stream, legacy_interface, "legacy_interface"); } void DiscoveryConfig::updateFromPropertyTree(const boost::property_tree::ptree& pt) { @@ -192,8 +190,6 @@ void Config::postUpdateValues() { } } - checkLegacyVersion(); - initLegacySecondaries(); LOG_TRACE << "Final configuration that will be used: \n" << (*this); } @@ -256,9 +252,6 @@ void Config::updateFromCommandLine(const boost::program_options::variables_map& std::vector sconfigs = cmd["secondary-config"].as>(); readSecondaryConfigs(sconfigs); } - if (cmd.count("legacy-interface") != 0) { - uptane.legacy_interface = cmd["legacy-interface"].as(); - } } void Config::readSecondaryConfigs(const std::vector& sconfigs) { @@ -267,78 +260,6 @@ void Config::readSecondaryConfigs(const std::vector& sc } } -void Config::checkLegacyVersion() { - if (uptane.legacy_interface.empty()) { - return; - } - if (!boost::filesystem::exists(uptane.legacy_interface)) { - throw FatalException(std::string("Legacy external flasher not found: ") + uptane.legacy_interface.string()); - } - std::stringstream command; - std::string output; - command << uptane.legacy_interface << " api-version --loglevel " << loggerGetSeverity(); - int rs = Utils::shell(command.str(), &output); - if (rs != 0) { - throw FatalException(std::string("Legacy external flasher api-version command failed: ") + output); - } - boost::trim_if(output, boost::is_any_of(" \n\r\t")); - if (output != "1") { - throw FatalException(std::string("Unexpected legacy external flasher API version: ") + output); - } -} - -void Config::initLegacySecondaries() { - if (uptane.legacy_interface.empty()) { - return; - } - std::stringstream command; - std::string output; - command << uptane.legacy_interface << " list-ecus --loglevel " << loggerGetSeverity(); - int rs = Utils::shell(command.str(), &output); - if (rs != 0) { - LOG_ERROR << "Legacy external flasher list-ecus command failed: " << output; - return; - } - - std::stringstream ss(output); - std::string buffer; - while (std::getline(ss, buffer, '\n')) { - Uptane::SecondaryConfig sconfig; - sconfig.secondary_type = Uptane::SecondaryType::kLegacy; - std::vector ecu_info; - boost::split(ecu_info, buffer, boost::is_any_of(" \n\r\t"), boost::token_compress_on); - if (ecu_info.size() == 0) { - // Could print a warning but why bother. - continue; - } - if (ecu_info.size() == 1) { - sconfig.ecu_hardware_id = ecu_info[0]; - // Use getSerial, which will get the public_key_id, initialized in ManagedSecondary constructor. - sconfig.ecu_serial = ""; - sconfig.full_client_dir = storage.path / sconfig.ecu_hardware_id; - LOG_INFO << "Legacy ECU configured with hardware ID " << sconfig.ecu_hardware_id; - } else if (ecu_info.size() >= 2) { - // For now, silently ignore anything after the second token. - sconfig.ecu_hardware_id = ecu_info[0]; - sconfig.ecu_serial = ecu_info[1]; - sconfig.full_client_dir = storage.path / (sconfig.ecu_hardware_id + "-" + sconfig.ecu_serial); - LOG_INFO << "Legacy ECU configured with hardware ID " << sconfig.ecu_hardware_id << " and serial " - << sconfig.ecu_serial; - } - - sconfig.partial_verifying = false; - sconfig.ecu_private_key = "sec.private"; - sconfig.ecu_public_key = "sec.public"; - - sconfig.firmware_path = sconfig.full_client_dir / "firmware.bin"; - sconfig.metadata_path = sconfig.full_client_dir / "metadata"; - sconfig.target_name_path = sconfig.full_client_dir / "target_name"; - sconfig.flasher = uptane.legacy_interface; - - uptane.secondary_configs.push_back(sconfig); - } -} - void Config::writeToStream(std::ostream& sink) const { // Keep this order the same as in config.h and // Config::updateFromPropertyTree(). diff --git a/src/libaktualizr/config/config.h b/src/libaktualizr/config/config.h index 333cf383fd..1f0819465b 100644 --- a/src/libaktualizr/config/config.h +++ b/src/libaktualizr/config/config.h @@ -76,7 +76,6 @@ struct UptaneConfig { std::string repo_server; CryptoSource key_source{CryptoSource::kFile}; KeyType key_type{KeyType::kRSA2048}; - boost::filesystem::path legacy_interface{}; std::vector secondary_configs{}; void updateFromPropertyTree(const boost::property_tree::ptree& pt); @@ -129,8 +128,6 @@ class Config : public BaseConfig { void updateFromPropertyTree(const boost::property_tree::ptree& pt) override; void updateFromCommandLine(const boost::program_options::variables_map& cmd); void readSecondaryConfigs(const std::vector& sconfigs); - void checkLegacyVersion(); - void initLegacySecondaries(); std::vector config_dirs_ = {"/usr/lib/sota/conf.d", "/etc/sota/conf.d/"}; bool loglevel_from_cmdline{false}; diff --git a/src/libaktualizr/config/config_test.cc b/src/libaktualizr/config/config_test.cc index a56c3917d5..b7392cdea1 100644 --- a/src/libaktualizr/config/config_test.cc +++ b/src/libaktualizr/config/config_test.cc @@ -123,45 +123,6 @@ TEST(config, SecondaryConfig) { EXPECT_TRUE(conf.uptane.secondary_configs[0].ecu_serial.empty()); } -void checkSecondaryConfig(const Config &conf) { - EXPECT_EQ(conf.uptane.secondary_configs.size(), 2); - EXPECT_EQ(conf.uptane.secondary_configs[0].secondary_type, Uptane::SecondaryType::kLegacy); - EXPECT_EQ(conf.uptane.secondary_configs[0].ecu_hardware_id, "example1"); - EXPECT_FALSE(conf.uptane.secondary_configs[0].ecu_serial.empty()); - EXPECT_EQ(conf.uptane.secondary_configs[1].secondary_type, Uptane::SecondaryType::kLegacy); - EXPECT_EQ(conf.uptane.secondary_configs[1].ecu_hardware_id, "example2"); - // If not provided, serial is not generated until SotaUptaneClient is initialized. - EXPECT_TRUE(conf.uptane.secondary_configs[1].ecu_serial.empty()); -} - -TEST(config, CmdlLegacyInterface) { - TemporaryDirectory temp_dir; - const std::string conf_path_str = (temp_dir.Path() / "config.toml").string(); - TestUtils::writePathToConfig("tests/config/minimal.toml", conf_path_str, temp_dir.Path()); - - bpo::variables_map cmd; - bpo::options_description description("some text"); - // clang-format off - description.add_options() - ("legacy-interface", bpo::value()->composing(), "path to legacy secondary ECU interface program") - ("config,c", bpo::value >()->composing(), "configuration directory"); - - // clang-format on - std::string path = (build_dir / "src/external_secondaries/example-interface").string(); - const char *argv[] = {"aktualizr", "--legacy-interface", path.c_str(), "-c", conf_path_str.c_str()}; - bpo::store(bpo::parse_command_line(5, argv, description), cmd); - - Config conf(cmd); - checkSecondaryConfig(conf); -} - -TEST(config, TomlLegacyInterface) { - Config conf("tests/config/minimal.toml"); - conf.uptane.legacy_interface = build_dir / "src/external_secondaries/example-interface"; - conf.postUpdateValues(); - checkSecondaryConfig(conf); -} - /** * Verify that aktualizr can start in implicit provisioning mode. */ diff --git a/src/libaktualizr/primary/sotauptaneclient.cc b/src/libaktualizr/primary/sotauptaneclient.cc index 84ce085450..2c61ab6a89 100644 --- a/src/libaktualizr/primary/sotauptaneclient.cc +++ b/src/libaktualizr/primary/sotauptaneclient.cc @@ -897,8 +897,7 @@ void SotaUptaneClient::putManifest() { } } -// Check stored secondaries list against secondaries known to aktualizr via -// commandline input and legacy interface. +// Check stored secondaries list against secondaries known to aktualizr. void SotaUptaneClient::verifySecondaries() { EcuSerials serials; if (!storage->loadEcuSerials(&serials) || serials.empty()) { diff --git a/src/libaktualizr/uptane/CMakeLists.txt b/src/libaktualizr/uptane/CMakeLists.txt index e08673c5a6..f145cd4f62 100644 --- a/src/libaktualizr/uptane/CMakeLists.txt +++ b/src/libaktualizr/uptane/CMakeLists.txt @@ -1,7 +1,6 @@ set(SOURCES fetcher.cc ipsecondarydiscovery.cc ipuptanesecondary.cc - legacysecondary.cc managedsecondary.cc partialverificationsecondary.cc role.cc @@ -16,7 +15,6 @@ set(HEADERS exceptions.h fetcher.h ipsecondarydiscovery.h ipuptanesecondary.h - legacysecondary.h managedsecondary.h partialverificationsecondary.h secondaryconfig.h diff --git a/src/libaktualizr/uptane/legacysecondary.cc b/src/libaktualizr/uptane/legacysecondary.cc deleted file mode 100644 index cc0aba1f9b..0000000000 --- a/src/libaktualizr/uptane/legacysecondary.cc +++ /dev/null @@ -1,54 +0,0 @@ -#include "uptane/legacysecondary.h" - -#include - -#include -#include - -#include "crypto/crypto.h" -#include "logging/logging.h" -#include "utilities/utils.h" - -namespace Uptane { -LegacySecondary::LegacySecondary(const SecondaryConfig& sconfig_in) : ManagedSecondary(sconfig_in) {} - -bool LegacySecondary::storeFirmware(const std::string& target_name, const std::string& content) { - // reading target hash back is not currently supported, so primary needs to save the firmware file locally - Utils::writeFile(sconfig.target_name_path, target_name); - Utils::writeFile(sconfig.firmware_path, content); - sync(); - - std::stringstream command; - std::string output; - command << sconfig.flasher.string() << " install-software --hardware-identifier " << sconfig.ecu_hardware_id - << " --ecu-identifier " << getSerial() << " --firmware " << sconfig.firmware_path.string() << " --loglevel " - << loggerGetSeverity(); - int rs = Utils::shell(command.str(), &output); - - if (rs != 0) { - // TODO: Should we do anything with the return value? - // 1 - The firmware image is invalid. - // 2 - Installation failure. The previous firmware was not modified. - // 3 - Installation failure. The previous firmware was partially overwritten or erased. - LOG_ERROR << "Legacy external flasher install-software command failed: " << output; - } - return (rs == 0); -} - -bool LegacySecondary::getFirmwareInfo(std::string* target_name, size_t& target_len, std::string* sha256hash) { - std::string content; - - // reading target hash back is not currently supported, just use the saved file - if (!boost::filesystem::exists(sconfig.target_name_path) || !boost::filesystem::exists(sconfig.firmware_path)) { - *target_name = std::string("noimage"); - content = ""; - } else { - *target_name = Utils::readFile(sconfig.target_name_path.string()); - content = Utils::readFile(sconfig.firmware_path.string()); - } - *sha256hash = boost::algorithm::to_lower_copy(boost::algorithm::hex(Crypto::sha256digest(content))); - target_len = content.size(); - - return true; -} -} // namespace Uptane diff --git a/src/libaktualizr/uptane/legacysecondary.h b/src/libaktualizr/uptane/legacysecondary.h deleted file mode 100644 index 1032ba17cb..0000000000 --- a/src/libaktualizr/uptane/legacysecondary.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef UPTANE_LEGACYSECONDARY_H_ -#define UPTANE_LEGACYSECONDARY_H_ - -#include - -#include "uptane/managedsecondary.h" -#include "utilities/types.h" - -namespace Uptane { -class LegacySecondary : public ManagedSecondary { - public: - explicit LegacySecondary(const SecondaryConfig& sconfig_in); - - private: - bool storeFirmware(const std::string& target_name, const std::string& content) override; - bool getFirmwareInfo(std::string* target_name, size_t& target_len, std::string* sha256hash) override; -}; -} // namespace Uptane - -#endif // UPTANE_LEGACYSECONDARY_H_ diff --git a/src/libaktualizr/uptane/managedsecondary.h b/src/libaktualizr/uptane/managedsecondary.h index b8c97643c7..d6a40ad999 100644 --- a/src/libaktualizr/uptane/managedsecondary.h +++ b/src/libaktualizr/uptane/managedsecondary.h @@ -15,8 +15,9 @@ namespace Uptane { -// Managed secondary is an abstraction over virtual and legacy secondaries. Both of them have all the UPTANE-related -// functionality implemented in aktualizr itself, so there's some shared code. +// Managed secondary is an abstraction over virtual and other types of legacy +// (non-UPTANE) secondaries. They require all the UPTANE-related functionality +// to be implemented in aktualizr itself, so there's some shared code. class ManagedSecondary : public SecondaryInterface { public: diff --git a/src/libaktualizr/uptane/secondaryconfig.h b/src/libaktualizr/uptane/secondaryconfig.h index 3c7d485c25..479ff6af80 100644 --- a/src/libaktualizr/uptane/secondaryconfig.h +++ b/src/libaktualizr/uptane/secondaryconfig.h @@ -15,8 +15,7 @@ enum class SecondaryType { kVirtual, // Virtual secondary (in-process fake implementation). - kLegacy, // legacy non-UPTANE secondary. All the UPTANE metadata is managed locally. All commands are sent to an - // external firmware loader via shell. + kLegacy, // Deprecated. Do not use. kOpcuaUptane, // Uptane protocol over OPC-UA @@ -38,7 +37,7 @@ class SecondaryConfig { if (stype == "virtual") { secondary_type = Uptane::SecondaryType::kVirtual; } else if (stype == "legacy") { - throw FatalException("Legacy secondaries should be initialized with --legacy-interface."); + throw FatalException("Legacy secondaries are deprecated."); } else if (stype == "ip_uptane") { secondary_type = Uptane::SecondaryType::kIpUptane; } else if (stype == "opcua_uptane") { @@ -56,7 +55,6 @@ class SecondaryConfig { firmware_path = boost::filesystem::path(config_json["firmware_path"].asString()); metadata_path = boost::filesystem::path(config_json["metadata_path"].asString()); target_name_path = boost::filesystem::path(config_json["target_name_path"].asString()); - flasher = ""; std::string key_type_str = config_json["key_type"].asString(); if (key_type_str.size() != 0u) { @@ -81,12 +79,10 @@ class SecondaryConfig { std::string opcua_lds_url; - boost::filesystem::path full_client_dir; // SecondaryType::kVirtual, SecondaryType::kLegacy - boost::filesystem::path firmware_path; // SecondaryType::kVirtual, SecondaryType::kLegacy - boost::filesystem::path metadata_path; // SecondaryType::kVirtual, SecondaryType::kLegacy - boost::filesystem::path target_name_path; // SecondaryType::kVirtual, SecondaryType::kLegacy - - boost::filesystem::path flasher; // SecondaryType::kLegacy + boost::filesystem::path full_client_dir; // SecondaryType::kVirtual + boost::filesystem::path firmware_path; // SecondaryType::kVirtual + boost::filesystem::path metadata_path; // SecondaryType::kVirtual + boost::filesystem::path target_name_path; // SecondaryType::kVirtual sockaddr_storage ip_addr{}; // SecondaryType::kIpUptane }; diff --git a/src/libaktualizr/uptane/secondaryfactory.h b/src/libaktualizr/uptane/secondaryfactory.h index 0651ac68bd..268c80fb6b 100644 --- a/src/libaktualizr/uptane/secondaryfactory.h +++ b/src/libaktualizr/uptane/secondaryfactory.h @@ -3,7 +3,6 @@ #include "logging/logging.h" #include "uptane/ipuptanesecondary.h" -#include "uptane/legacysecondary.h" #include "uptane/opcuasecondary.h" #include "uptane/secondaryconfig.h" #include "uptane/secondaryinterface.h" @@ -18,17 +17,16 @@ class SecondaryFactory { switch (sconfig.secondary_type) { case SecondaryType::kVirtual: return std::make_shared(sconfig); - break; case SecondaryType::kLegacy: - return std::make_shared(sconfig); - break; + LOG_ERROR << "Legacy secondary support is deprecated."; + return std::shared_ptr(); // NULL-equivalent case SecondaryType::kIpUptane: return std::make_shared(sconfig); case SecondaryType::kOpcuaUptane: #ifdef OPCUA_SECONDARY_ENABLED return std::make_shared(sconfig); #else - LOG_ERROR << "Built with no OPC-UA secondary support"; + LOG_ERROR << "libaktualizr was built without OPC-UA secondary support."; return std::shared_ptr(); // NULL-equivalent #endif default: diff --git a/src/libaktualizr/uptane/uptane_ci_test.cc b/src/libaktualizr/uptane/uptane_ci_test.cc index a7b5bceb4f..e261108d87 100644 --- a/src/libaktualizr/uptane/uptane_ci_test.cc +++ b/src/libaktualizr/uptane/uptane_ci_test.cc @@ -87,10 +87,7 @@ TEST(UptaneCI, CheckKeys) { std::map >::iterator it; for (it = sota_client->secondaries.begin(); it != sota_client->secondaries.end(); it++) { - if (it->second->sconfig.secondary_type != Uptane::SecondaryType::kVirtual && - it->second->sconfig.secondary_type != Uptane::SecondaryType::kLegacy) { - continue; - } + EXPECT_EQ(it->second->sconfig.secondary_type, Uptane::SecondaryType::kVirtual); std::shared_ptr managed = boost::polymorphic_pointer_downcast(it->second); std::string public_key; diff --git a/src/libaktualizr/uptane/uptane_secondary_test.cc b/src/libaktualizr/uptane/uptane_secondary_test.cc index 767cab0376..2133d74985 100644 --- a/src/libaktualizr/uptane/uptane_secondary_test.cc +++ b/src/libaktualizr/uptane/uptane_secondary_test.cc @@ -22,21 +22,13 @@ TEST(SecondaryFactory, Virtual) { EXPECT_TRUE(sec); } +/* Legacy secondaries are deprecated and are no longer supported. */ TEST(SecondaryFactory, Legacy) { TemporaryDirectory temp_dir; Uptane::SecondaryConfig sconfig; sconfig.secondary_type = Uptane::SecondaryType::kLegacy; - sconfig.partial_verifying = false; - sconfig.full_client_dir = temp_dir.Path(); - sconfig.ecu_serial = ""; - sconfig.ecu_hardware_id = "secondary_hardware"; - sconfig.ecu_private_key = "sec.priv"; - sconfig.ecu_public_key = "sec.pub"; - sconfig.firmware_path = temp_dir.Path() / "firmware.txt"; - sconfig.target_name_path = temp_dir.Path() / "firmware_name.txt"; - sconfig.metadata_path = temp_dir.Path() / "metadata"; std::shared_ptr sec = Uptane::SecondaryFactory::makeSecondary(sconfig); - EXPECT_TRUE(sec); + EXPECT_FALSE(sec); } TEST(SecondaryFactory, Uptane_get_key) { diff --git a/src/libaktualizr/uptane/uptane_serial_test.cc b/src/libaktualizr/uptane/uptane_serial_test.cc index c61acb97e3..1047aa4d4f 100644 --- a/src/libaktualizr/uptane/uptane_serial_test.cc +++ b/src/libaktualizr/uptane/uptane_serial_test.cc @@ -152,79 +152,6 @@ TEST(Uptane, ReloadSerial) { EXPECT_NE(ecu_serials_2[0].first, ecu_serials_2[1].first); } -/** - * Check that aktualizr saves random ecu_serial for primary and all secondaries. - * - * Test with a legacy secondary interface with two secondaries. - */ -TEST(Uptane, LegacySerial) { - RecordProperty("zephyr_key", "OTA-989,TST-156"); - TemporaryDirectory temp_dir; - const std::string conf_path_str = (temp_dir.Path() / "config.toml").string(); - TestUtils::writePathToConfig("tests/config/basic.toml", conf_path_str, temp_dir.Path()); - - bpo::variables_map cmd; - bpo::options_description description("some text"); - // clang-format off - description.add_options() - ("legacy-interface", bpo::value()->composing(), "path to legacy secondary ECU interface program") - ("config,c", bpo::value >()->composing(), "configuration directory"); - - // clang-format on - std::string path = (build_dir / "src/external_secondaries/example-interface").string(); - const char* argv[] = {"aktualizr", "--legacy-interface", path.c_str(), "-c", conf_path_str.c_str()}; - bpo::store(bpo::parse_command_line(5, argv, description), cmd); - - EcuSerials ecu_serials_1; - EcuSerials ecu_serials_2; - - // Initialize. Should store new serials. - { - Config conf(cmd); - conf.provision.primary_ecu_serial = ""; - - auto storage = INvStorage::newStorage(conf.storage); - auto http = std::make_shared(temp_dir.Path()); - auto uptane_client = SotaUptaneClient::newTestClient(conf, storage, http); - EXPECT_NO_THROW(uptane_client->initialize()); - EXPECT_TRUE(storage->loadEcuSerials(&ecu_serials_1)); - EXPECT_EQ(ecu_serials_1.size(), 3); - EXPECT_FALSE(ecu_serials_1[0].first.ToString().empty()); - EXPECT_FALSE(ecu_serials_1[1].first.ToString().empty()); - EXPECT_FALSE(ecu_serials_1[2].first.ToString().empty()); - } - - // Keep storage directory, but initialize new objects. Should load existing - // serials. - { - Config conf(cmd); - conf.provision.primary_ecu_serial = ""; - - auto storage = INvStorage::newStorage(conf.storage); - auto http = std::make_shared(temp_dir.Path()); - auto uptane_client = SotaUptaneClient::newTestClient(conf, storage, http); - EXPECT_NO_THROW(uptane_client->initialize()); - EXPECT_TRUE(storage->loadEcuSerials(&ecu_serials_2)); - EXPECT_EQ(ecu_serials_2.size(), 3); - EXPECT_FALSE(ecu_serials_2[0].first.ToString().empty()); - EXPECT_FALSE(ecu_serials_2[1].first.ToString().empty()); - EXPECT_FALSE(ecu_serials_2[2].first.ToString().empty()); - } - - // Verify that serials match across initializations. - EXPECT_EQ(ecu_serials_1[0].first, ecu_serials_2[0].first); - EXPECT_EQ(ecu_serials_1[1].first, ecu_serials_2[1].first); - EXPECT_EQ(ecu_serials_1[2].first, ecu_serials_2[2].first); - - // Sanity check that primary and secondary serials do not match. - EXPECT_NE(ecu_serials_1[0].first, ecu_serials_1[1].first); - EXPECT_NE(ecu_serials_1[0].first, ecu_serials_1[2].first); - EXPECT_NE(ecu_serials_1[1].first, ecu_serials_1[2].first); - EXPECT_NE(ecu_serials_2[0].first, ecu_serials_2[1].first); - EXPECT_NE(ecu_serials_2[0].first, ecu_serials_2[2].first); - EXPECT_NE(ecu_serials_2[1].first, ecu_serials_2[2].first); -} - #ifndef __NO_MAIN__ int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 58d8ff7de0..382000d2ff 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -152,15 +152,6 @@ add_test(NAME test-help-with-nonexistent-options set_tests_properties(test-help-with-nonexistent-options PROPERTIES PASS_REGULAR_EXPRESSION "aktualizr command line options") -# --tls-server is provided only to prevent the credentials file from getting -# read before the desired error message is printed. This was a problem if -# /var/sota existed but was owned by root, because aktualizr would crash. -add_test(NAME test-legacy-interface-with-nonexistent-file - COMMAND aktualizr -c ${PROJECT_SOURCE_DIR}/config/sota_autoprov.toml - --tls-server fake --legacy-interface nonexistentfile) -set_tests_properties(test-legacy-interface-with-nonexistent-file - PROPERTIES PASS_REGULAR_EXPRESSION "Legacy external flasher not found: nonexistentfile") - add_test(NAME test-secondary-config-with-nonexistent-file COMMAND aktualizr -c ${PROJECT_SOURCE_DIR}/config/sota_autoprov.toml --tls-server fake --secondary-config nonexistentfile)