diff --git a/doc/ot_api_doc.h b/doc/ot_api_doc.h index ac6ec5a83..834280707 100644 --- a/doc/ot_api_doc.h +++ b/doc/ot_api_doc.h @@ -154,6 +154,7 @@ * @} * * @defgroup api-sntp SNTP + * @defgroup api-verhoeff-checksum Verhoeff Checksum * * @} * diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index da77ef01f..bc18aea32 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -249,6 +249,7 @@ ot_option(OT_TX_BEACON_PAYLOAD OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENA ot_option(OT_TX_QUEUE_STATS OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE "tx queue statistics") ot_option(OT_UDP_FORWARD OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE "UDP forward") ot_option(OT_UPTIME OPENTHREAD_CONFIG_UPTIME_ENABLE "uptime") +ot_option(OT_VERHOEFF_CHECKSUM OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE "verhoeff checksum") option(OT_DOC "build OpenThread documentation") message(STATUS "- - - - - - - - - - - - - - - - ") diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn index 5948fb73a..453639710 100644 --- a/include/openthread/BUILD.gn +++ b/include/openthread/BUILD.gn @@ -125,6 +125,7 @@ source_set("openthread") { "thread_ftd.h", "trel.h", "udp.h", + "verhoeff_checksum.h", ] public_deps = [ ":openthread_config" ] diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 1ce766c02..470ce012c 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (402) +#define OPENTHREAD_API_VERSION (403) /** * @addtogroup api-instance diff --git a/include/openthread/verhoeff_checksum.h b/include/openthread/verhoeff_checksum.h new file mode 100644 index 000000000..bbd53ab3b --- /dev/null +++ b/include/openthread/verhoeff_checksum.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief + * This file defines APIs for Verhoeff checksum calculation and validation. + */ + +#ifndef OPENTHREAD_VERHOEFF_CHECKSUM_H_ +#define OPENTHREAD_VERHOEFF_CHECKSUM_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup api-verhoeff-checksum + * + * @brief + * This module includes functions for Verhoeff checksum calculation and validation. + * + * The functions in this module are available when `OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE` is enabled. + * + * @{ + * + */ + +/** + * Specifies the maximum length of decimal string input in `otVerhoeffChecksum` functions. + * + */ +#define OT_VERHOEFF_CHECKSUM_MAX_STRING_LENGTH 128 + +/** + * Calculates the Verhoeff checksum for a given decimal string. + * + * Requires `OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE`. + * + * @param[in] aDecimalString The string containing decimal digits. + * @param[out] aChecksum Pointer to a `char` to return the calculated checksum. + * + * @retval OT_ERROR_NONE Successfully calculated the checksum, @p aChecksum is updated. + * @retval OT_ERROR_INVALID_ARGS The @p aDecimalString is not valid, i.e. it either contains chars other than + * ['0'-'9'], or is longer than `OT_VERHOEFF_CHECKSUM_MAX_STRING_LENGTH`. + * + */ +otError otVerhoeffChecksumCalculate(const char *aDecimalString, char *aChecksum); + +/** + * Validates the Verhoeff checksum for a given decimal string. + * + * Requires `OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE`. + * + * @param[in] aDecimalString The string containing decimal digits (last char is treated as checksum). + * + * @retval OT_ERROR_NONE Successfully validated the checksum in @p aDecimalString. + * @retval OT_ERROR_FAILED Checksum validation failed. + * @retval OT_ERROR_INVALID_ARGS The @p aDecimalString is not valid, i.e. it either contains chars other than + * ['0'-'9'], or is longer than `OT_VERHOEFF_CHECKSUM_MAX_STRING_LENGTH`. + * + */ +otError otVerhoeffChecksumValidate(const char *aDecimalString); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_VERHOEFF_CHECKSUM_H_ diff --git a/src/cli/README.md b/src/cli/README.md index c8dfe2744..2921b19a6 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -130,6 +130,7 @@ Done - [unsecureport](#unsecureport-add-port) - [uptime](#uptime) - [vendor](#vendor-name) +- [verhoeff](#verhoeff-calculate) - [version](#version) ## OpenThread Command Details @@ -3964,6 +3965,35 @@ Set the vendor SW version (requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_ Done ``` +### verhoeff calculate + +Calculates the Verhoeff checksum for a given decimal string. + +Requires `OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE`. + +The input string MUST consist of characters in `['0'-'9']`. + +```bash +> verhoeff calculate 30731842 +1 +Done +``` + +### verhoeff validate + +Validates the Verhoeff checksum for a given decimal string. + +Requires `OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE`. + +The input string MUST consist of characters in `['0'-'9']`. The last digit is treated as checksum. + +```bash +> verhoeff validate 307318421 +Done +> verhoeff validate 307318425 +Error 1: Failed +``` + ### version Print the build version information. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 33d1123bc..e6132df8f 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include "common/num_utils.hpp" #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE #include @@ -8355,6 +8356,57 @@ void Interpreter::HandleIp6Receive(otMessage *aMessage, void *aContext) } #endif +#if OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE + +template <> otError Interpreter::Process(Arg aArgs[]) +{ + otError error; + + /** + * @cli verhoeff calculate + * @code + * verhoeff calculate 30731842 + * 1 + * Done + * @endcode + * @cparam verhoeff calculate @ca{decimalstring} + * @par api_copy + * #otVerhoeffChecksumCalculate + */ + if (aArgs[0] == "calculate") + { + char checksum; + + VerifyOrExit(!aArgs[1].IsEmpty() && aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + SuccessOrExit(error = otVerhoeffChecksumCalculate(aArgs[1].GetCString(), &checksum)); + OutputLine("%c", checksum); + } + /** + * @cli verhoeff validate + * @code + * verhoeff validate 307318421 + * Done + * @endcode + * @cparam verhoeff validate @ca{decimalstring} + * @par api_copy + * #otVerhoeffChecksumValidate + */ + else if (aArgs[0] == "validate") + { + VerifyOrExit(!aArgs[1].IsEmpty() && aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + error = otVerhoeffChecksumValidate(aArgs[1].GetCString()); + } + else + { + error = OT_ERROR_INVALID_COMMAND; + } + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE + #endif // OPENTHREAD_FTD || OPENTHREAD_MTD void Interpreter::Initialize(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext) @@ -8653,6 +8705,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) CmdEntry("uptime"), #endif CmdEntry("vendor"), +#if OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE + CmdEntry("verhoeff"), +#endif #endif // OPENTHREAD_FTD || OPENTHREAD_MTD CmdEntry("version"), }; diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 715741d58..08fa6db98 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -360,6 +360,7 @@ openthread_core_files = [ "api/thread_ftd_api.cpp", "api/trel_api.cpp", "api/udp_api.cpp", + "api/verhoeff_checksum_api.cpp", "backbone_router/backbone_tmf.cpp", "backbone_router/backbone_tmf.hpp", "backbone_router/bbr_leader.cpp", @@ -736,6 +737,8 @@ openthread_core_files = [ "utils/slaac_address.hpp", "utils/srp_client_buffers.cpp", "utils/srp_client_buffers.hpp", + "utils/verhoeff_checksum.cpp", + "utils/verhoeff_checksum.hpp", ] openthread_radio_sources = [ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5e6cee135..937de5c5a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -86,6 +86,7 @@ set(COMMON_SOURCES api/thread_ftd_api.cpp api/trel_api.cpp api/udp_api.cpp + api/verhoeff_checksum_api.cpp backbone_router/backbone_tmf.cpp backbone_router/bbr_leader.cpp backbone_router/bbr_local.cpp @@ -257,6 +258,7 @@ set(COMMON_SOURCES utils/power_calibration.cpp utils/slaac_address.cpp utils/srp_client_buffers.cpp + utils/verhoeff_checksum.cpp ) set(RADIO_COMMON_SOURCES diff --git a/src/core/api/verhoeff_checksum_api.cpp b/src/core/api/verhoeff_checksum_api.cpp new file mode 100644 index 000000000..ac658f941 --- /dev/null +++ b/src/core/api/verhoeff_checksum_api.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements the Verhoeff Checksum public APIs. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE + +#include + +#include "common/debug.hpp" +#include "utils/verhoeff_checksum.hpp" + +using namespace ot::Utils; + +otError otVerhoeffChecksumCalculate(const char *aDecimalString, char *aChecksum) +{ + AssertPointerIsNotNull(aDecimalString); + AssertPointerIsNotNull(aChecksum); + + return VerhoeffChecksum::Calculate(aDecimalString, *aChecksum); +} + +otError otVerhoeffChecksumValidate(const char *aDecimalString) +{ + AssertPointerIsNotNull(aDecimalString); + + return VerhoeffChecksum::Validate(aDecimalString); +} + +#endif // OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE diff --git a/src/core/config/misc.h b/src/core/config/misc.h index 70e0c12e0..e764b8872 100644 --- a/src/core/config/misc.h +++ b/src/core/config/misc.h @@ -141,6 +141,16 @@ #define OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE + * + * Define to 1 to enable Verhoeff checksum utility module. + * + */ +#ifndef OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE +#define OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE +#endif + /** * @def OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE * diff --git a/src/core/utils/verhoeff_checksum.cpp b/src/core/utils/verhoeff_checksum.cpp new file mode 100644 index 000000000..bf41709a4 --- /dev/null +++ b/src/core/utils/verhoeff_checksum.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements Verhoeff checksum calculation and validation. + */ + +#include "verhoeff_checksum.hpp" + +#if OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE + +#include "common/code_utils.hpp" +#include "common/string.hpp" + +namespace ot { +namespace Utils { + +uint8_t VerhoeffChecksum::Lookup(uint8_t aIndex, const uint8_t aCompressedArray[]) +{ + // The values in the array are [0-9]. To save space, two + // entries are saved as a single byte in @p aCompressedArray, + // such that higher 4-bit corresponds to one entry, and the + // lower 4-bit to the next entry. + + uint8_t result = aCompressedArray[aIndex / 2]; + + if ((aIndex & 1) == 0) + { + result >>= 4; + } + else + { + result &= 0x0f; + } + + return result; +} + +uint8_t VerhoeffChecksum::Multiply(uint8_t aFirst, uint8_t aSecond) +{ + static uint8_t kMultiplication[][5] = {{0x01, 0x23, 0x45, 0x67, 0x89}, {0x12, 0x34, 0x06, 0x78, 0x95}, + {0x23, 0x40, 0x17, 0x89, 0x56}, {0x34, 0x01, 0x28, 0x95, 0x67}, + {0x40, 0x12, 0x39, 0x56, 0x78}, {0x59, 0x87, 0x60, 0x43, 0x21}, + {0x65, 0x98, 0x71, 0x04, 0x32}, {0x76, 0x59, 0x82, 0x10, 0x43}, + {0x87, 0x65, 0x93, 0x21, 0x04}, {0x98, 0x76, 0x54, 0x32, 0x10}}; + + return Lookup(aSecond, kMultiplication[aFirst]); +} + +uint8_t VerhoeffChecksum::Permute(uint8_t aPosition, uint8_t aValue) +{ + static uint8_t kPermutation[][5] = {{0x01, 0x23, 0x45, 0x67, 0x89}, {0x15, 0x76, 0x28, 0x30, 0x94}, + {0x58, 0x03, 0x79, 0x61, 0x42}, {0x89, 0x16, 0x04, 0x35, 0x27}, + {0x94, 0x53, 0x12, 0x68, 0x70}, {0x42, 0x86, 0x57, 0x39, 0x01}, + {0x27, 0x93, 0x80, 0x64, 0x15}, {0x70, 0x46, 0x91, 0x32, 0x58}}; + + return Lookup(aValue, kPermutation[aPosition]); +} + +uint8_t VerhoeffChecksum::InverseOf(uint8_t aValue) +{ + static uint8_t kInverse[] = {0x04, 0x32, 0x15, 0x67, 0x89}; + + return Lookup(aValue, kInverse); +} + +Error VerhoeffChecksum::Calculate(const char *aDecimalString, char &aChecksum) +{ + Error error; + uint8_t code; + + SuccessOrExit(error = ComputeCode(aDecimalString, code, /* aValidate */ false)); + aChecksum = static_cast('0' + InverseOf(code)); + +exit: + return error; +} + +Error VerhoeffChecksum::Validate(const char *aDecimalString) +{ + Error error; + uint8_t code; + + SuccessOrExit(error = ComputeCode(aDecimalString, code, /* aValidate */ true)); + VerifyOrExit(code == 0, error = kErrorFailed); + +exit: + return error; +} + +Error VerhoeffChecksum::ComputeCode(const char *aDecimalString, uint8_t &aCode, bool aValidate) +{ + Error error = kErrorNone; + uint8_t code = 0; + uint16_t index = 0; + uint16_t length = StringLength(aDecimalString, kMaxStringLength + 1); + + VerifyOrExit(length <= kMaxStringLength, error = kErrorInvalidArgs); + + if (!aValidate) + { + length++; + index++; + } + + for (; index < length; ++index) + { + char digit = aDecimalString[length - index - 1]; + + VerifyOrExit(digit >= '0' && digit <= '9', error = kErrorInvalidArgs); + code = Multiply(code, Permute(index % 8, static_cast(digit - '0'))); + } + + aCode = code; + +exit: + return error; +} + +} // namespace Utils +} // namespace ot + +#endif // OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE diff --git a/src/core/utils/verhoeff_checksum.hpp b/src/core/utils/verhoeff_checksum.hpp new file mode 100644 index 000000000..9249e23ad --- /dev/null +++ b/src/core/utils/verhoeff_checksum.hpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for Verhoeff checksum calculation and validation. + */ + +#ifndef VERHOEFF_CHECKSUM_HPP_ +#define VERHOEFF_CHECKSUM_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE + +#include + +#include "common/error.hpp" + +namespace ot { +namespace Utils { + +class VerhoeffChecksum +{ +public: + /** + * Specifies the maximum length of decimal string input. + * + */ + static constexpr uint16_t kMaxStringLength = OT_VERHOEFF_CHECKSUM_MAX_STRING_LENGTH; + + /** + * Calculates the Verhoeff checksum for a given decimal string. + * + * + * @param[in] a DecimalString The string containing decimal digits. + * @param[out] aChecksum Reference to a `char` to return the calculated checksum. + * + * @retval kErrorNone Successfully calculated the checksum, @p aChecksum is updated. + * @retval kErrorInvalidArgs The @p aDecimalString is not valid, i.e. it either contains chars other than + * ['0'-'9'], or is longer than `kMaxStringLength`. + * + */ + static Error Calculate(const char *aDecimalString, char &aChecksum); + + /** + * Validates the Verhoeff checksum for a given decimal string. + * + * @param[in] aDecimalString The string containing decimal digits (last char is treated as checksum). + * + * @retval kErrorNone Successfully validated the checksum in @p aDecimalString. + * @retval kErrorFailed Checksum is not valid. + * @retval kErrorInvalidArgs The @p aDecimalString is not valid, i.e. it either contains chars other than + * ['0'-'9'], or is longer than `kMaxStringLength`. + * + */ + static Error Validate(const char *aDecimalString); + + VerhoeffChecksum(void) = delete; + +private: + static Error ComputeCode(const char *aDecimalString, uint8_t &aCode, bool aValidate); + static uint8_t Lookup(uint8_t aIndex, const uint8_t aCompressedArray[]); + static uint8_t Multiply(uint8_t aFirst, uint8_t aSecond); + static uint8_t Permute(uint8_t aPosition, uint8_t aValue); + static uint8_t InverseOf(uint8_t aValue); +}; + +} // namespace Utils +} // namespace ot + +#endif // OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE + +#endif // VERHOEFF_CHECKSUM_HPP_ diff --git a/tests/unit/test_checksum.cpp b/tests/unit/test_checksum.cpp index dafd2dbbb..0c16f31c3 100644 --- a/tests/unit/test_checksum.cpp +++ b/tests/unit/test_checksum.cpp @@ -30,11 +30,13 @@ #include "common/message.hpp" #include "common/numeric_limits.hpp" #include "common/random.hpp" +#include "common/string.hpp" #include "instance/instance.hpp" #include "net/checksum.hpp" #include "net/icmp6.hpp" #include "net/ip4_types.hpp" #include "net/udp6.hpp" +#include "utils/verhoeff_checksum.hpp" #include "test_platform.h" #include "test_util.hpp" @@ -467,6 +469,62 @@ public: } }; +#if OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE + +void TestVerhoeffChecksum(void) +{ + static constexpr uint16_t kMaxStringSize = 50; + + const char *kExamples[] = {"307318421", "487300178", "123455672", "0", "15", + "999999994", "000000001", "100000000", "2363"}; + + const char *kInvalidFormats[] = { + "307 318421", + "307318421 ", + " 307318421", + "ABCDE", + }; + + char string[kMaxStringSize]; + char checksum; + char expectedChecksum; + + printf("\nVerhoeffChecksum\n"); + + for (const char *example : kExamples) + { + uint16_t length = StringLength(example, kMaxStringSize - 1); + + memcpy(string, example, length + 1); + + printf("- \"%s\"\n", string); + + SuccessOrQuit(Utils::VerhoeffChecksum::Validate(string)); + + expectedChecksum = string[length - 1]; + + string[length - 1] = (expectedChecksum == '0') ? '9' : (expectedChecksum - 1); + VerifyOrQuit(Utils::VerhoeffChecksum::Validate(string) == kErrorFailed); + + string[length - 1] = '\0'; + SuccessOrQuit(Utils::VerhoeffChecksum::Calculate(string, checksum)); + VerifyOrQuit(checksum == expectedChecksum); + + string[length - 1] = expectedChecksum == '0' ? '9' : (expectedChecksum - 1); + } + + printf("\nInvalid format:\n"); + + for (const char *example : kInvalidFormats) + { + printf("- \"%s\"\n", example); + VerifyOrQuit(Utils::VerhoeffChecksum::Validate(example) == kErrorInvalidArgs); + VerifyOrQuit(Utils::VerhoeffChecksum::Calculate(example, checksum) == kErrorInvalidArgs); + } +} + +#endif // OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE + } // namespace ot int main(void) @@ -477,6 +535,10 @@ int main(void) ot::TestTcp4MessageChecksum(); ot::TestUdp4MessageChecksum(); ot::TestIcmp4MessageChecksum(); +#if OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE + ot::TestVerhoeffChecksum(); +#endif + printf("All tests passed\n"); return 0; }