From 7a7d0c6cbd9dd6410e660a155a991f656eb733a4 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 27 Jan 2025 22:29:26 -0800 Subject: [PATCH] [common] update and enhance CRC calculation (#11182) This commit updates and enhances the CRC module. It updates the implementation to allow both CRC16 and CRC32 calculation with different polynomials. It adds new `Feed()` methods to add bytes from a buffer or from a message into CRC computation. It also adds a unit test to cover CRC calculation (both CRC16 and CRC32). --- src/core/BUILD.gn | 4 +- src/core/CMakeLists.txt | 2 +- src/core/common/{crc16.cpp => crc.cpp} | 66 ++++++++---- src/core/common/crc.hpp | 136 +++++++++++++++++++++++++ src/core/common/crc16.hpp | 88 ---------------- src/core/common/message.hpp | 4 + src/core/meshcop/meshcop.cpp | 15 +-- tests/unit/CMakeLists.txt | 1 + tests/unit/test_crc.cpp | 116 +++++++++++++++++++++ 9 files changed, 310 insertions(+), 122 deletions(-) rename src/core/common/{crc16.cpp => crc.cpp} (57%) create mode 100644 src/core/common/crc.hpp delete mode 100644 src/core/common/crc16.hpp create mode 100644 tests/unit/test_crc.cpp diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index b36998234..0913a1e70 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -394,8 +394,8 @@ openthread_core_files = [ "common/callback.hpp", "common/clearable.hpp", "common/const_cast.hpp", - "common/crc16.cpp", - "common/crc16.hpp", + "common/crc.cpp", + "common/crc.hpp", "common/data.cpp", "common/data.hpp", "common/debug.hpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index af0046b64..6550ae80d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -101,7 +101,7 @@ set(COMMON_SOURCES coap/coap_secure.cpp common/appender.cpp common/binary_search.cpp - common/crc16.cpp + common/crc.cpp common/data.cpp common/error.cpp common/frame_builder.cpp diff --git a/src/core/common/crc16.cpp b/src/core/common/crc.cpp similarity index 57% rename from src/core/common/crc16.cpp rename to src/core/common/crc.cpp index f3f60ef1d..8f06bf534 100644 --- a/src/core/common/crc16.cpp +++ b/src/core/common/crc.cpp @@ -31,34 +31,62 @@ * This file implements CRC16 computations. */ -#include "crc16.hpp" +#include "crc.hpp" namespace ot { -Crc16::Crc16(Polynomial aPolynomial) +template UintType CrcCalculator::FeedByte(uint8_t aByte) { - mPolynomial = static_cast(aPolynomial); - Init(); -} + static constexpr UintType kMsb = kIsUint16 ? (1u << 15) : (1u << 31); + static constexpr uint8_t kBitShift = kIsUint16 ? 8 : 24; -void Crc16::Update(uint8_t aByte) -{ - uint8_t i; + mCrc ^= (static_cast(aByte) << kBitShift); - mCrc = mCrc ^ static_cast(aByte << 8); - i = 8; - - do + for (uint8_t i = 8; i > 0; i--) { - if (mCrc & 0x8000) + bool msbIsSet = (mCrc & kMsb); + + mCrc <<= 1; + + if (msbIsSet) { - mCrc = static_cast(mCrc << 1) ^ mPolynomial; + mCrc ^= mPolynomial; } - else - { - mCrc = static_cast(mCrc << 1); - } - } while (--i); + } + + return mCrc; } +template UintType CrcCalculator::FeedBytes(const void *aBytes, uint16_t aLength) +{ + const uint8_t *bytes = reinterpret_cast(aBytes); + + while (aLength-- > 0) + { + FeedByte(*bytes++); + } + + return mCrc; +} + +template +UintType CrcCalculator::Feed(const Message &aMessage, const OffsetRange &aOffsetRange) +{ + uint16_t length = aOffsetRange.GetLength(); + Message::Chunk chunk; + + aMessage.GetFirstChunk(aOffsetRange.GetOffset(), length, chunk); + + while (chunk.GetLength() > 0) + { + FeedBytes(chunk.GetBytes(), chunk.GetLength()); + aMessage.GetNextChunk(length, chunk); + } + + return mCrc; +} + +template class CrcCalculator; +template class CrcCalculator; + } // namespace ot diff --git a/src/core/common/crc.hpp b/src/core/common/crc.hpp new file mode 100644 index 000000000..af3cf6412 --- /dev/null +++ b/src/core/common/crc.hpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2016, 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 CRC computations. + */ + +#ifndef CRC_HPP_ +#define CRC_HPP_ + +#include "openthread-core-config.h" + +#include "common/message.hpp" +#include "common/type_traits.hpp" + +namespace ot { + +constexpr uint16_t kCrc16CcittPolynomial = 0x1021; ///< CRC16-CCITT Polynomial (x^16 + x^12 + x^5 + 1) +constexpr uint16_t kCrc16AnsiPolynomial = 0x8005; ///< CRC16-ANSI Polynomial (x^16 + x^15 + x^2 + 1) + +/** + * CRC32-ANSI Polynomial + * + * (x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1) + */ +constexpr uint32_t kCrc32AnsiPolynomial = 0x04c11db7; + +/** + * Implements CRC computations. + * + * @tparam UintType The unsigned int type indicating CRC bit width. MUST be either `uint16_t` or `uint32_t`. + */ +template class CrcCalculator +{ + constexpr static bool kIsUint16 = TypeTraits::IsSame::kValue; + constexpr static bool kIsUint32 = TypeTraits::IsSame::kValue; + + static_assert(kIsUint16 || kIsUint32, "UintType MUST be either `uint16_t` or `uint32_t`"); + +public: + /** + * Initializes the `CrcCalculator` object. + * + * @param[in] aPolynomial The polynomial to use for CRC calculation. + */ + explicit CrcCalculator(UintType aPolynomial) + : mPolynomial(aPolynomial) + , mCrc(0) + { + } + + /** + * Gets the current CRC value. + * + * @returns The current CRC value. + */ + UintType GetCrc(void) const { return mCrc; } + + /** + * Feeds a byte value into the CRC computation. + * + * @param[in] aByte The byte value. + * + * @returns The current CRC value. + */ + UintType FeedByte(uint8_t aByte); + + /** + * Feeds a sequence of bytes into the CRC computation. + * + * @param[in] aBytes A pointer to buffer containing the bytes. + * @param[in] aLength Number of bytes in @p aBytes. + * + * @returns The current CRC value. + */ + UintType FeedBytes(const void *aBytes, uint16_t aLength); + + /** + * Feed an object (all its bytes) into the CRC computation. + * + * @tparam ObjectType The object type. + * + * @param[in] aObject A reference to the object. + * + * @returns The current CRC value. + */ + template UintType Feed(const ObjectType &aObject) + { + static_assert(!TypeTraits::IsPointer::kValue, "ObjectType must not be a pointer"); + return FeedBytes(&aObject, sizeof(ObjectType)); + } + + /** + * Feed bytes read from a given message within a given offset range into the CRC computation. + * + * @param[in] aMessage The message to read from. + * @param[in] aOffsetRaneg The offset range in @p aMessage to read bytes from. + * + * @returns The current CRC value. + */ + UintType Feed(const Message &aMessage, const OffsetRange &aOffsetRange); + +private: + UintType mPolynomial; + UintType mCrc; +}; + +} // namespace ot + +#endif // CRC_HPP_ diff --git a/src/core/common/crc16.hpp b/src/core/common/crc16.hpp deleted file mode 100644 index add84a6d1..000000000 --- a/src/core/common/crc16.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2016, 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 CRC16 computations. - */ - -#ifndef CRC16_HPP_ -#define CRC16_HPP_ - -#include "openthread-core-config.h" - -#include - -namespace ot { - -/** - * Implements CRC16 computations. - */ -class Crc16 -{ -public: - enum Polynomial : uint16_t - { - kCcitt = 0x1021, ///< CRC16_CCITT - kAnsi = 0x8005, ///< CRC16-ANSI - }; - - /** - * Initializes the object. - * - * @param[in] aPolynomial The polynomial value. - */ - explicit Crc16(Polynomial aPolynomial); - - /** - * Initializes the CRC16 computation. - */ - void Init(void) { mCrc = 0; } - - /*c* - * Feeds a byte value into the CRC16 computation. - * - * @param[in] aByte The byte value. - */ - void Update(uint8_t aByte); - - /** - * Gets the current CRC16 value. - * - * @returns The current CRC16 value. - */ - uint16_t Get(void) const { return mCrc; } - -private: - uint16_t mPolynomial; - uint16_t mCrc; -}; - -} // namespace ot - -#endif // CRC16_HPP_ diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 4441af580..008b1f77c 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -69,6 +69,8 @@ struct otMessage namespace ot { +template class CrcCalculator; + namespace Crypto { class AesCcm; @@ -272,6 +274,8 @@ static_assert(sizeof(Buffer) >= kBufferSize, class Message : public otMessage, public Buffer, public GetProvider { friend class Checksum; + friend class CrcCalculator; + friend class CrcCalculator; friend class Crypto::HmacSha256; friend class Crypto::Sha256; friend class Crypto::AesCcm; diff --git a/src/core/meshcop/meshcop.cpp b/src/core/meshcop/meshcop.cpp index 922cc64b4..dd38a5895 100644 --- a/src/core/meshcop/meshcop.cpp +++ b/src/core/meshcop/meshcop.cpp @@ -34,7 +34,7 @@ #include "meshcop.hpp" -#include "common/crc16.hpp" +#include "common/crc.hpp" #include "instance/instance.hpp" namespace ot { @@ -244,17 +244,8 @@ bool SteeringData::Contains(const HashBitIndexes &aIndexes) const void SteeringData::CalculateHashBitIndexes(const Mac::ExtAddress &aJoinerId, HashBitIndexes &aIndexes) { - Crc16 ccitt(Crc16::kCcitt); - Crc16 ansi(Crc16::kAnsi); - - for (uint8_t b : aJoinerId.m8) - { - ccitt.Update(b); - ansi.Update(b); - } - - aIndexes.mIndex[0] = ccitt.Get(); - aIndexes.mIndex[1] = ansi.Get(); + aIndexes.mIndex[0] = CrcCalculator(kCrc16CcittPolynomial).Feed(aJoinerId); + aIndexes.mIndex[1] = CrcCalculator(kCrc16AnsiPolynomial).Feed(aJoinerId); } void SteeringData::CalculateHashBitIndexes(const JoinerDiscerner &aDiscerner, HashBitIndexes &aIndexes) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 40847ed5c..af89d17d7 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -205,6 +205,7 @@ ot_unit_test(checksum) ot_unit_test(child) ot_unit_test(child_table) ot_unit_test(cmd_line_parser) +ot_unit_test(crc) ot_unit_test(data) ot_unit_test(dataset) ot_unit_test(dns) diff --git a/tests/unit/test_crc.cpp b/tests/unit/test_crc.cpp new file mode 100644 index 000000000..295560963 --- /dev/null +++ b/tests/unit/test_crc.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2025, 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. + */ + +#include "common/crc.hpp" + +#include "test_platform.h" +#include "test_util.hpp" + +namespace ot { + +void TestCrc16(void) +{ + struct TestCase + { + const uint8_t *mData; + uint16_t mDataLength; + uint16_t mExpectedCcittCrc16; + uint16_t mExpectedAnsiCrc16; + }; + + static const uint8_t kTestData1[] = {0xff}; + static const uint8_t kTestData2[] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39}; // "123456789" + static const uint8_t kTestData3[] = {0x10, 0x20, 0x03, 0x15, 0xbe, 0xef, 0xca, 0xfe}; + + static const TestCase kTestCases[] = { + {kTestData1, sizeof(kTestData1), 0x1ef0, 0x0202}, + {kTestData2, sizeof(kTestData2), 0x31c3, 0xfee8}, + {kTestData3, sizeof(kTestData3), 0x926a, 0x070c}, + }; + + printf("\nTestCrc16"); + + for (const TestCase &testCase : kTestCases) + { + CrcCalculator ccitt(kCrc16CcittPolynomial); + CrcCalculator ansi(kCrc16AnsiPolynomial); + + ccitt.FeedBytes(testCase.mData, testCase.mDataLength); + ansi.FeedBytes(testCase.mData, testCase.mDataLength); + + DumpBuffer("CRC16", testCase.mData, testCase.mDataLength); + printf("-> CCIT: 0x%04x, ANSI: 0x%04x\n", ccitt.GetCrc(), ansi.GetCrc()); + + VerifyOrQuit(ccitt.GetCrc() == testCase.mExpectedCcittCrc16); + VerifyOrQuit(ansi.GetCrc() == testCase.mExpectedAnsiCrc16); + } +} + +void TestCrc32(void) +{ + struct TestCase + { + const uint8_t *mData; + uint16_t mDataLength; + uint32_t mExpectedAnsiCrc32; + }; + + static const uint8_t kTestData1[] = {0xff}; + static const uint8_t kTestData2[] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39}; // "123456789" + static const uint8_t kTestData3[] = {0x10, 0x20, 0x03, 0x15, 0xbe, 0xef, 0xca, 0xfe}; + + static const TestCase kTestCases[] = { + {kTestData1, sizeof(kTestData1), 0xb1f740b4}, + {kTestData2, sizeof(kTestData2), 0x89a1897f}, + {kTestData3, sizeof(kTestData3), 0xd651e770}, + }; + + printf("\nTestCrc32\n"); + + for (const TestCase &testCase : kTestCases) + { + CrcCalculator crc32(kCrc32AnsiPolynomial); + + crc32.FeedBytes(testCase.mData, testCase.mDataLength); + + DumpBuffer("CRC32", testCase.mData, testCase.mDataLength); + printf("-> 0x%08lx\n", ToUlong(crc32.GetCrc())); + + VerifyOrQuit(crc32.GetCrc() == testCase.mExpectedAnsiCrc32); + } +} + +} // namespace ot + +int main(void) +{ + ot::TestCrc16(); + ot::TestCrc32(); + printf("All tests passed\n"); + return 0; +}