diff --git a/CMakeLists.txt b/CMakeLists.txt index c4b2238303e7025235ad7c4c8dfbb0d1fa621407..7983b271566373a46549e3c21f4bd245c895febc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ add_custom_target(check add_library(common OBJECT src/Message.cpp src/MessageParser.cpp + src/Helpers/CRCHelper.cpp src/Services/EventReportService.cpp src/Services/MemoryManagementService.cpp src/Services/ParameterService.cpp diff --git a/inc/Helpers/CRCHelper.hpp b/inc/Helpers/CRCHelper.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d70ca5782b7a33add0110990bd23c0c2253dd1e7 --- /dev/null +++ b/inc/Helpers/CRCHelper.hpp @@ -0,0 +1,43 @@ +#ifndef ECSS_SERVICES_CRCHELPER_HPP +#define ECSS_SERVICES_CRCHELPER_HPP + +#include <cstdint> + +class CRCHelper { + + /** + * CRC32 calculation helper class + * This class declares a function which calculates the CRC16 checksum of the given data. + * + * For now the actual implementation is the CRC16/CCITT variant (ECSS-E-ST-70-41C, pg.615) + * (polynomial 0x1021, normal input), but this can change at any time + * (even to a hardware CRC implementation, if available) + * + * Please report all found bugs. + * + * @author (CRC explanation) http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html + * @author (class code & dox) Grigoris Pavlakis <grigpavl@ece.auth.gr> + */ + +// TODO: Change this to hardware implementation or a trusted software one +// TODO: Use CRC with received TC and transmitted TM packets +public: + /** + * Actual CRC calculation function. + * @param message (pointer to the data to be checksummed) + * @param length (size in bytes) + * @return the CRC32 checksum of the input data + */ + static uint16_t calculateCRC(const uint8_t* message, uint32_t length); + + /** + * CRC validation function. Make sure the passed message actually contains a CRC checksum + * appended at the very end! + * @param message (pointer to the data to be validated) + * @param length (in bytes, plus 2 bytes for the CRC checksum) + * @return 0 when the data is valid, a nonzero uint16 when the data is corrupted + */ + static uint16_t validateCRC(const uint8_t* message, uint32_t length); +}; + +#endif //ECSS_SERVICES_CRCHELPER_HPP diff --git a/src/Helpers/CRCHelper.cpp b/src/Helpers/CRCHelper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e0cd4ebf914ff885c7f50a52aeec4cd3e42c8da8 --- /dev/null +++ b/src/Helpers/CRCHelper.cpp @@ -0,0 +1,34 @@ +#include "Helpers/CRCHelper.hpp" + +// TODO: THIS IS TEMPORARY CODE, WILL SURELY BE REPLACED + +uint16_t CRCHelper::calculateCRC(const uint8_t* message, uint32_t length) { + // shift register contains all 1's initially (ECSS-E-ST-70-41C, Annex B - CRC and ISO checksum) + uint16_t shiftReg = 0xFFFFu; + + // CRC16-CCITT generator polynomial (as specified in standard) + uint16_t polynomial = 0x1021u; + + for (int i = 0; i < length; i++) { + // "copy" (XOR w/ existing contents) the current msg bits into the MSB of the shift register + shiftReg ^= (message[i] << 8u); + + for (int j = 0; j < 8; j++) { + // if the MSB is set, the bitwise AND gives 1 + if ((shiftReg & 0x8000u) != 0) { + // toss out of the register the MSB and divide (XOR) its content with the generator + shiftReg = ((shiftReg << 1u) ^ polynomial); + } + else { + // just toss out the MSB and make room for a new bit + shiftReg <<= 1u; + } + } + } + return shiftReg; +} + +uint16_t CRCHelper::validateCRC(const uint8_t *message, uint32_t length) { + return calculateCRC(message, length); + // CRC result of a correct msg w/checksum appended is 0 +} diff --git a/src/main.cpp b/src/main.cpp index 277cb16107311a6a318abbc86079f8aa6022e69c..8e2d90d253fad7a1425dca04d07f3766eed5dd89 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ #include <iostream> +#include "Helpers/CRCHelper.hpp" #include "Services/TestService.hpp" #include "Services/ParameterService.hpp" #include "Services/RequestVerificationService.hpp" diff --git a/test/Helpers/CRCHelper.cpp b/test/Helpers/CRCHelper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73909a920026b63e62ba0364f0db51777ea665c9 --- /dev/null +++ b/test/Helpers/CRCHelper.cpp @@ -0,0 +1,51 @@ +#include "catch2/catch.hpp" +#include "Helpers/CRCHelper.hpp" + +TEST_CASE("CRC calculation - Basic string tests") { + CHECK(CRCHelper::calculateCRC((uint8_t*) "Raccoon Squad!", 14) == 0x08FC); + CHECK(CRCHelper::calculateCRC((uint8_t*) "ASAT", 4) == 0xBFFA); + CHECK(CRCHelper::calculateCRC((uint8_t*) "All your space are belong to us", 31) == 0x545F); + CHECK(CRCHelper::calculateCRC((uint8_t*) "SPAAAAAAAAACE!", 14) == 0xB441); +} + +TEST_CASE("CRC calculation - Basic byte tests") { + SECTION("ECSS compliance verification tests (p.617)") { + uint8_t data1[2] = {0x00, 0x00}; + uint8_t data2[3] = {0x00, 0x00, 0x00}; + uint8_t data3[4] = {0xAB, 0xCD, 0xEF, 0x01}; + uint8_t data4[6] = {0x14, 0x56, 0xF8, 0x9A, 0x00, 0x01}; + + CHECK(CRCHelper::calculateCRC(data1, 2) == 0x1D0F); + CHECK(CRCHelper::calculateCRC(data2, 3) == 0xCC9C); + CHECK(CRCHelper::calculateCRC(data3, 4) == 0x04A2); + CHECK(CRCHelper::calculateCRC(data4, 6) == 0x7FD5); + } + + SECTION("Null (0x00) before ending") { + uint8_t data1[5] = {0x45, 0xF2, 0x00, 0xA2, 0x01}; + uint8_t data2[8] = {0x21, 0x65, 0xDF, 0x00, 0xC4, 0x00, 0x00, 0xBF}; + uint8_t data3[4] = {0x07, 0x00, 0x05, 0xFF}; + + CHECK(CRCHelper::calculateCRC(data1, 5) == 0x3A2B); + CHECK(CRCHelper::calculateCRC(data2, 8) == 0x89EE); + CHECK(CRCHelper::calculateCRC(data3, 4) == 0x34E8); + } +} + +TEST_CASE("CRC validation - Basic tests") { + uint8_t data1[7] = {'H', 'e', 'l', 'l', 'o', 0xDA, 0xDA}; + uint8_t data2[6] = {'A', 'S', 'A', 'T', 0xBF, 0xFA}; + uint8_t data3[10] = {'S', 'p', 'A', '@', 'A', 'c', '3', '!', 0xB4, 0x41}; + // checksum from original SPAAAAAAAAACE! string + uint8_t data4[6] = {'A', 0x43, 0x52, 0xDF, 0xBF, 0xFA}; + // ASAT, but "corrupted" with the last 2 bytes the original checksum of the 'ASAT' string + + uint8_t data5[9] = {'C', 'U', 'B', 'E', 'S', 'A', 'T', 0x53, 0x15}; //corrupted CRC checksum + + + CHECK(CRCHelper::validateCRC(data1, 7) == 0x0); + CHECK(CRCHelper::validateCRC(data2, 6) == 0x0); + CHECK(CRCHelper::validateCRC(data3, 10) != 0x0); + CHECK(CRCHelper::validateCRC(data4, 6) != 0x0); + CHECK(CRCHelper::validateCRC(data5, 9) != 0x0); +}