[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).
This commit is contained in:
Abtin Keshavarzian
2025-01-27 22:29:26 -08:00
committed by GitHub
parent 27782ad164
commit 7a7d0c6cbd
9 changed files with 310 additions and 122 deletions
+2 -2
View File
@@ -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",
+1 -1
View File
@@ -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
@@ -31,34 +31,62 @@
* This file implements CRC16 computations.
*/
#include "crc16.hpp"
#include "crc.hpp"
namespace ot {
Crc16::Crc16(Polynomial aPolynomial)
template <typename UintType> UintType CrcCalculator<UintType>::FeedByte(uint8_t aByte)
{
mPolynomial = static_cast<uint16_t>(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<UintType>(aByte) << kBitShift);
mCrc = mCrc ^ static_cast<uint16_t>(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<uint16_t>(mCrc << 1) ^ mPolynomial;
mCrc ^= mPolynomial;
}
else
{
mCrc = static_cast<uint16_t>(mCrc << 1);
}
} while (--i);
}
return mCrc;
}
template <typename UintType> UintType CrcCalculator<UintType>::FeedBytes(const void *aBytes, uint16_t aLength)
{
const uint8_t *bytes = reinterpret_cast<const uint8_t *>(aBytes);
while (aLength-- > 0)
{
FeedByte(*bytes++);
}
return mCrc;
}
template <typename UintType>
UintType CrcCalculator<UintType>::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<uint16_t>;
template class CrcCalculator<uint32_t>;
} // namespace ot
+136
View File
@@ -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 <typename UintType> class CrcCalculator
{
constexpr static bool kIsUint16 = TypeTraits::IsSame<UintType, uint16_t>::kValue;
constexpr static bool kIsUint32 = TypeTraits::IsSame<UintType, uint32_t>::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 <typename ObjectType> UintType Feed(const ObjectType &aObject)
{
static_assert(!TypeTraits::IsPointer<ObjectType>::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_
-88
View File
@@ -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 <stdint.h>
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_
+4
View File
@@ -69,6 +69,8 @@ struct otMessage
namespace ot {
template <typename UintType> class CrcCalculator;
namespace Crypto {
class AesCcm;
@@ -272,6 +274,8 @@ static_assert(sizeof(Buffer) >= kBufferSize,
class Message : public otMessage, public Buffer, public GetProvider<Message>
{
friend class Checksum;
friend class CrcCalculator<uint16_t>;
friend class CrcCalculator<uint32_t>;
friend class Crypto::HmacSha256;
friend class Crypto::Sha256;
friend class Crypto::AesCcm;
+3 -12
View File
@@ -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<uint16_t>(kCrc16CcittPolynomial).Feed(aJoinerId);
aIndexes.mIndex[1] = CrcCalculator<uint16_t>(kCrc16AnsiPolynomial).Feed(aJoinerId);
}
void SteeringData::CalculateHashBitIndexes(const JoinerDiscerner &aDiscerner, HashBitIndexes &aIndexes)
+1
View File
@@ -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)
+116
View File
@@ -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<uint16_t> ccitt(kCrc16CcittPolynomial);
CrcCalculator<uint16_t> 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<uint32_t> 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;
}