Files
SimpleRemoter/test/unit/file/SHA256VerifyTest.cpp
2026-04-19 22:55:21 +02:00

648 lines
19 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file SHA256VerifyTest.cpp
* @brief SHA-256 文件校验测试
*
* 测试覆盖:
* - SHA-256 哈希计算
* - FileCompletePacketV2 构建与解析
* - 文件完整性校验逻辑
* - 校验失败处理
*/
#include <gtest/gtest.h>
#include <cstring>
#include <vector>
#include <array>
#include <string>
#include <iomanip>
#include <sstream>
// ============================================
// 简化版 SHA-256 实现(测试专用)
// 生产环境使用 OpenSSL 或 Windows CNG
// ============================================
class SHA256 {
public:
SHA256() { Reset(); }
void Reset() {
m_state[0] = 0x6a09e667;
m_state[1] = 0xbb67ae85;
m_state[2] = 0x3c6ef372;
m_state[3] = 0xa54ff53a;
m_state[4] = 0x510e527f;
m_state[5] = 0x9b05688c;
m_state[6] = 0x1f83d9ab;
m_state[7] = 0x5be0cd19;
m_bitLen = 0;
m_bufferLen = 0;
}
void Update(const uint8_t* data, size_t len) {
for (size_t i = 0; i < len; ++i) {
m_buffer[m_bufferLen++] = data[i];
if (m_bufferLen == 64) {
Transform();
m_bitLen += 512;
m_bufferLen = 0;
}
}
}
void Update(const std::vector<uint8_t>& data) {
Update(data.data(), data.size());
}
std::array<uint8_t, 32> Finalize() {
size_t i = m_bufferLen;
// Pad
if (m_bufferLen < 56) {
m_buffer[i++] = 0x80;
while (i < 56) m_buffer[i++] = 0x00;
} else {
m_buffer[i++] = 0x80;
while (i < 64) m_buffer[i++] = 0x00;
Transform();
memset(m_buffer, 0, 56);
}
// Append length
m_bitLen += m_bufferLen * 8;
m_buffer[63] = static_cast<uint8_t>(m_bitLen);
m_buffer[62] = static_cast<uint8_t>(m_bitLen >> 8);
m_buffer[61] = static_cast<uint8_t>(m_bitLen >> 16);
m_buffer[60] = static_cast<uint8_t>(m_bitLen >> 24);
m_buffer[59] = static_cast<uint8_t>(m_bitLen >> 32);
m_buffer[58] = static_cast<uint8_t>(m_bitLen >> 40);
m_buffer[57] = static_cast<uint8_t>(m_bitLen >> 48);
m_buffer[56] = static_cast<uint8_t>(m_bitLen >> 56);
Transform();
// Output
std::array<uint8_t, 32> hash;
for (int j = 0; j < 8; ++j) {
hash[j * 4 + 0] = (m_state[j] >> 24) & 0xff;
hash[j * 4 + 1] = (m_state[j] >> 16) & 0xff;
hash[j * 4 + 2] = (m_state[j] >> 8) & 0xff;
hash[j * 4 + 3] = m_state[j] & 0xff;
}
return hash;
}
static std::string HashToHex(const std::array<uint8_t, 32>& hash) {
std::ostringstream ss;
for (uint8_t b : hash) {
ss << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(b);
}
return ss.str();
}
private:
static uint32_t RotR(uint32_t x, uint32_t n) { return (x >> n) | (x << (32 - n)); }
static uint32_t Ch(uint32_t x, uint32_t y, uint32_t z) { return (x & y) ^ (~x & z); }
static uint32_t Maj(uint32_t x, uint32_t y, uint32_t z) { return (x & y) ^ (x & z) ^ (y & z); }
static uint32_t Sig0(uint32_t x) { return RotR(x, 2) ^ RotR(x, 13) ^ RotR(x, 22); }
static uint32_t Sig1(uint32_t x) { return RotR(x, 6) ^ RotR(x, 11) ^ RotR(x, 25); }
static uint32_t sig0(uint32_t x) { return RotR(x, 7) ^ RotR(x, 18) ^ (x >> 3); }
static uint32_t sig1(uint32_t x) { return RotR(x, 17) ^ RotR(x, 19) ^ (x >> 10); }
void Transform() {
static const uint32_t K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
uint32_t W[64];
for (int i = 0; i < 16; ++i) {
W[i] = (m_buffer[i * 4] << 24) | (m_buffer[i * 4 + 1] << 16) |
(m_buffer[i * 4 + 2] << 8) | m_buffer[i * 4 + 3];
}
for (int i = 16; i < 64; ++i) {
W[i] = sig1(W[i - 2]) + W[i - 7] + sig0(W[i - 15]) + W[i - 16];
}
uint32_t a = m_state[0], b = m_state[1], c = m_state[2], d = m_state[3];
uint32_t e = m_state[4], f = m_state[5], g = m_state[6], h = m_state[7];
for (int i = 0; i < 64; ++i) {
uint32_t t1 = h + Sig1(e) + Ch(e, f, g) + K[i] + W[i];
uint32_t t2 = Sig0(a) + Maj(a, b, c);
h = g; g = f; f = e; e = d + t1;
d = c; c = b; b = a; a = t1 + t2;
}
m_state[0] += a; m_state[1] += b; m_state[2] += c; m_state[3] += d;
m_state[4] += e; m_state[5] += f; m_state[6] += g; m_state[7] += h;
}
uint32_t m_state[8];
uint8_t m_buffer[64];
uint64_t m_bitLen;
size_t m_bufferLen;
};
// ============================================
// 协议结构(测试专用)
// ============================================
#pragma pack(push, 1)
struct FileCompletePacketV2 {
uint8_t cmd;
uint64_t transferID;
uint64_t srcClientID;
uint64_t dstClientID;
uint32_t fileIndex;
uint64_t fileSize;
uint8_t sha256[32];
};
enum FileErrorV2 : uint8_t {
FEV2_OK = 0,
FEV2_TARGET_OFFLINE = 1,
FEV2_VERSION_MISMATCH = 2,
FEV2_FILE_NOT_FOUND = 3,
FEV2_ACCESS_DENIED = 4,
FEV2_DISK_FULL = 5,
FEV2_TRANSFER_CANCEL = 6,
FEV2_CHECKSUM_ERROR = 7,
FEV2_HASH_MISMATCH = 8,
};
#pragma pack(pop)
// ============================================
// 辅助函数
// ============================================
std::vector<uint8_t> BuildFileCompletePacket(
uint64_t transferID,
uint64_t srcClientID,
uint64_t dstClientID,
uint32_t fileIndex,
uint64_t fileSize,
const std::array<uint8_t, 32>& sha256)
{
std::vector<uint8_t> buffer(sizeof(FileCompletePacketV2));
FileCompletePacketV2* pkt = reinterpret_cast<FileCompletePacketV2*>(buffer.data());
pkt->cmd = 91; // COMMAND_FILE_COMPLETE_V2
pkt->transferID = transferID;
pkt->srcClientID = srcClientID;
pkt->dstClientID = dstClientID;
pkt->fileIndex = fileIndex;
pkt->fileSize = fileSize;
memcpy(pkt->sha256, sha256.data(), 32);
return buffer;
}
bool ParseFileCompletePacket(
const uint8_t* buffer, size_t len,
FileCompletePacketV2& pkt)
{
if (len < sizeof(FileCompletePacketV2)) {
return false;
}
memcpy(&pkt, buffer, sizeof(FileCompletePacketV2));
return true;
}
// 校验文件完整性
FileErrorV2 VerifyFileIntegrity(
const std::array<uint8_t, 32>& expectedHash,
const std::vector<uint8_t>& fileData)
{
SHA256 sha;
sha.Update(fileData);
auto actualHash = sha.Finalize();
if (actualHash == expectedHash) {
return FEV2_OK;
}
return FEV2_HASH_MISMATCH;
}
// ============================================
// SHA-256 基础测试
// ============================================
class SHA256Test : public ::testing::Test {};
TEST_F(SHA256Test, EmptyString) {
SHA256 sha;
auto hash = sha.Finalize();
// SHA-256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
std::string hex = SHA256::HashToHex(hash);
EXPECT_EQ(hex, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
}
TEST_F(SHA256Test, HelloWorld) {
SHA256 sha;
const char* msg = "Hello, World!";
sha.Update(reinterpret_cast<const uint8_t*>(msg), strlen(msg));
auto hash = sha.Finalize();
// SHA-256("Hello, World!") = dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
std::string hex = SHA256::HashToHex(hash);
EXPECT_EQ(hex, "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f");
}
TEST_F(SHA256Test, ABC) {
SHA256 sha;
const char* msg = "abc";
sha.Update(reinterpret_cast<const uint8_t*>(msg), strlen(msg));
auto hash = sha.Finalize();
// SHA-256("abc") = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
std::string hex = SHA256::HashToHex(hash);
EXPECT_EQ(hex, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
}
TEST_F(SHA256Test, IncrementalUpdate) {
SHA256 sha1, sha2;
const char* part1 = "Hello, ";
const char* part2 = "World!";
const char* full = "Hello, World!";
sha1.Update(reinterpret_cast<const uint8_t*>(part1), strlen(part1));
sha1.Update(reinterpret_cast<const uint8_t*>(part2), strlen(part2));
sha2.Update(reinterpret_cast<const uint8_t*>(full), strlen(full));
auto hash1 = sha1.Finalize();
auto hash2 = sha2.Finalize();
EXPECT_EQ(hash1, hash2);
}
TEST_F(SHA256Test, LargeData) {
SHA256 sha;
// 1 MB of zeros
std::vector<uint8_t> data(1024 * 1024, 0);
sha.Update(data);
auto hash = sha.Finalize();
// 验证计算成功(不崩溃)
EXPECT_EQ(hash.size(), 32u);
}
TEST_F(SHA256Test, BinaryData) {
SHA256 sha;
std::vector<uint8_t> data = {0x00, 0x01, 0x02, 0x03, 0xff, 0xfe, 0xfd, 0xfc};
sha.Update(data);
auto hash = sha.Finalize();
// 验证计算成功
EXPECT_EQ(hash.size(), 32u);
}
TEST_F(SHA256Test, Reset) {
SHA256 sha;
sha.Update(reinterpret_cast<const uint8_t*>("test"), 4);
sha.Reset();
auto hash = sha.Finalize();
// 重置后应该等于空字符串的哈希
std::string hex = SHA256::HashToHex(hash);
EXPECT_EQ(hex, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
}
// ============================================
// FileCompletePacketV2 测试
// ============================================
class FileCompletePacketTest : public ::testing::Test {};
TEST_F(FileCompletePacketTest, BuildAndParse) {
std::array<uint8_t, 32> sha256 = {};
sha256[0] = 0xAA;
sha256[31] = 0xBB;
auto buffer = BuildFileCompletePacket(12345, 100, 200, 5, 1024 * 1024, sha256);
FileCompletePacketV2 pkt;
ASSERT_TRUE(ParseFileCompletePacket(buffer.data(), buffer.size(), pkt));
EXPECT_EQ(pkt.cmd, 91);
EXPECT_EQ(pkt.transferID, 12345u);
EXPECT_EQ(pkt.srcClientID, 100u);
EXPECT_EQ(pkt.dstClientID, 200u);
EXPECT_EQ(pkt.fileIndex, 5u);
EXPECT_EQ(pkt.fileSize, 1024u * 1024u);
EXPECT_EQ(pkt.sha256[0], 0xAA);
EXPECT_EQ(pkt.sha256[31], 0xBB);
}
TEST_F(FileCompletePacketTest, TruncatedPacket) {
std::array<uint8_t, 32> sha256 = {};
auto buffer = BuildFileCompletePacket(1, 0, 0, 0, 100, sha256);
// 截断
FileCompletePacketV2 pkt;
EXPECT_FALSE(ParseFileCompletePacket(buffer.data(), sizeof(FileCompletePacketV2) - 10, pkt));
}
TEST_F(FileCompletePacketTest, LargeFileSize) {
std::array<uint8_t, 32> sha256 = {};
uint64_t largeSize = 100ULL * 1024 * 1024 * 1024; // 100 GB
auto buffer = BuildFileCompletePacket(1, 0, 0, 0, largeSize, sha256);
FileCompletePacketV2 pkt;
ASSERT_TRUE(ParseFileCompletePacket(buffer.data(), buffer.size(), pkt));
EXPECT_EQ(pkt.fileSize, largeSize);
}
// ============================================
// 文件完整性校验测试
// ============================================
class FileIntegrityTest : public ::testing::Test {};
TEST_F(FileIntegrityTest, CorrectHash) {
std::vector<uint8_t> fileData = {'H', 'e', 'l', 'l', 'o'};
SHA256 sha;
sha.Update(fileData);
auto expectedHash = sha.Finalize();
auto result = VerifyFileIntegrity(expectedHash, fileData);
EXPECT_EQ(result, FEV2_OK);
}
TEST_F(FileIntegrityTest, IncorrectHash) {
std::vector<uint8_t> fileData = {'H', 'e', 'l', 'l', 'o'};
std::array<uint8_t, 32> wrongHash = {}; // 全零的错误哈希
auto result = VerifyFileIntegrity(wrongHash, fileData);
EXPECT_EQ(result, FEV2_HASH_MISMATCH);
}
TEST_F(FileIntegrityTest, CorruptedData) {
std::vector<uint8_t> originalData = {'H', 'e', 'l', 'l', 'o'};
SHA256 sha;
sha.Update(originalData);
auto originalHash = sha.Finalize();
// 修改一个字节
std::vector<uint8_t> corruptedData = originalData;
corruptedData[2] = 'X';
auto result = VerifyFileIntegrity(originalHash, corruptedData);
EXPECT_EQ(result, FEV2_HASH_MISMATCH);
}
TEST_F(FileIntegrityTest, EmptyFile) {
std::vector<uint8_t> emptyData;
SHA256 sha;
auto emptyHash = sha.Finalize();
auto result = VerifyFileIntegrity(emptyHash, emptyData);
EXPECT_EQ(result, FEV2_OK);
}
TEST_F(FileIntegrityTest, SingleBitDifference) {
std::vector<uint8_t> data1 = {0x00};
std::vector<uint8_t> data2 = {0x01}; // 单比特差异
SHA256 sha1, sha2;
sha1.Update(data1);
sha2.Update(data2);
auto hash1 = sha1.Finalize();
auto hash2 = sha2.Finalize();
// 哈希应该完全不同
EXPECT_NE(hash1, hash2);
// 验证错误检测
auto result = VerifyFileIntegrity(hash1, data2);
EXPECT_EQ(result, FEV2_HASH_MISMATCH);
}
// ============================================
// 流式哈希计算测试
// ============================================
class StreamingHashTest : public ::testing::Test {};
TEST_F(StreamingHashTest, ChunkedUpdate) {
std::vector<uint8_t> fullData(10000);
for (size_t i = 0; i < fullData.size(); ++i) {
fullData[i] = static_cast<uint8_t>(i & 0xFF);
}
// 一次性计算
SHA256 sha1;
sha1.Update(fullData);
auto hash1 = sha1.Finalize();
// 分块计算
SHA256 sha2;
size_t chunkSize = 64; // SHA-256 块大小
for (size_t i = 0; i < fullData.size(); i += chunkSize) {
size_t len = std::min(chunkSize, fullData.size() - i);
sha2.Update(fullData.data() + i, len);
}
auto hash2 = sha2.Finalize();
EXPECT_EQ(hash1, hash2);
}
TEST_F(StreamingHashTest, VariableChunkSizes) {
std::vector<uint8_t> data(1000);
for (size_t i = 0; i < data.size(); ++i) {
data[i] = static_cast<uint8_t>(i);
}
SHA256 sha1;
sha1.Update(data);
auto expected = sha1.Finalize();
// 不同大小的块
std::vector<size_t> chunkSizes = {1, 7, 63, 64, 65, 128, 1000};
for (size_t chunkSize : chunkSizes) {
SHA256 sha2;
for (size_t i = 0; i < data.size(); i += chunkSize) {
size_t len = std::min(chunkSize, data.size() - i);
sha2.Update(data.data() + i, len);
}
auto actual = sha2.Finalize();
EXPECT_EQ(expected, actual) << "Failed for chunk size: " << chunkSize;
}
}
// ============================================
// 文件传输完整性验证场景
// ============================================
class TransferVerificationTest : public ::testing::Test {};
TEST_F(TransferVerificationTest, SimulateSuccessfulTransfer) {
// 模拟文件数据
std::vector<uint8_t> fileData(1024 * 64); // 64 KB
for (size_t i = 0; i < fileData.size(); ++i) {
fileData[i] = static_cast<uint8_t>(i * 17); // 伪随机数据
}
// 发送方计算哈希
SHA256 senderSha;
senderSha.Update(fileData);
auto senderHash = senderSha.Finalize();
// 构建校验包
auto packet = BuildFileCompletePacket(12345, 100, 0, 0, fileData.size(), senderHash);
// 接收方解析并验证
FileCompletePacketV2 pkt;
ASSERT_TRUE(ParseFileCompletePacket(packet.data(), packet.size(), pkt));
EXPECT_EQ(pkt.fileSize, fileData.size());
// 接收方计算哈希并比较
std::array<uint8_t, 32> expectedHash;
memcpy(expectedHash.data(), pkt.sha256, 32);
auto result = VerifyFileIntegrity(expectedHash, fileData);
EXPECT_EQ(result, FEV2_OK);
}
TEST_F(TransferVerificationTest, SimulateCorruptedTransfer) {
// 原始文件
std::vector<uint8_t> originalData(1024);
for (size_t i = 0; i < originalData.size(); ++i) {
originalData[i] = static_cast<uint8_t>(i);
}
// 发送方计算哈希
SHA256 senderSha;
senderSha.Update(originalData);
auto senderHash = senderSha.Finalize();
// 模拟传输中数据损坏
std::vector<uint8_t> receivedData = originalData;
receivedData[512] ^= 0xFF; // 翻转一个字节
// 校验失败
auto result = VerifyFileIntegrity(senderHash, receivedData);
EXPECT_EQ(result, FEV2_HASH_MISMATCH);
}
TEST_F(TransferVerificationTest, MultipleFilesInTransfer) {
struct FileInfo {
std::vector<uint8_t> data;
std::array<uint8_t, 32> hash;
};
std::vector<FileInfo> files(5);
// 生成测试文件
for (int i = 0; i < 5; ++i) {
files[i].data.resize(1000 * (i + 1));
for (size_t j = 0; j < files[i].data.size(); ++j) {
files[i].data[j] = static_cast<uint8_t>(j + i * 7);
}
SHA256 sha;
sha.Update(files[i].data);
files[i].hash = sha.Finalize();
}
// 验证每个文件
for (int i = 0; i < 5; ++i) {
auto result = VerifyFileIntegrity(files[i].hash, files[i].data);
EXPECT_EQ(result, FEV2_OK) << "File " << i << " verification failed";
}
// 交叉验证应该失败
auto crossResult = VerifyFileIntegrity(files[0].hash, files[1].data);
EXPECT_EQ(crossResult, FEV2_HASH_MISMATCH);
}
// ============================================
// 边界条件测试
// ============================================
class HashBoundaryTest : public ::testing::Test {};
TEST_F(HashBoundaryTest, ExactBlockSize) {
// SHA-256 块大小是 64 字节
std::vector<uint8_t> data(64, 'A');
SHA256 sha;
sha.Update(data);
auto hash = sha.Finalize();
EXPECT_EQ(hash.size(), 32u);
}
TEST_F(HashBoundaryTest, BlockSizePlusOne) {
std::vector<uint8_t> data(65, 'B');
SHA256 sha;
sha.Update(data);
auto hash = sha.Finalize();
EXPECT_EQ(hash.size(), 32u);
}
TEST_F(HashBoundaryTest, BlockSizeMinusOne) {
std::vector<uint8_t> data(63, 'C');
SHA256 sha;
sha.Update(data);
auto hash = sha.Finalize();
EXPECT_EQ(hash.size(), 32u);
}
TEST_F(HashBoundaryTest, MultipleBlocks) {
std::vector<uint8_t> data(64 * 10, 'D');
SHA256 sha;
sha.Update(data);
auto hash = sha.Finalize();
EXPECT_EQ(hash.size(), 32u);
}
TEST_F(HashBoundaryTest, PaddingBoundary55) {
// 55 字节需要特殊处理padding + length 刚好 64
std::vector<uint8_t> data(55, 'E');
SHA256 sha;
sha.Update(data);
auto hash = sha.Finalize();
EXPECT_EQ(hash.size(), 32u);
}
TEST_F(HashBoundaryTest, PaddingBoundary56) {
// 56 字节需要额外的块
std::vector<uint8_t> data(56, 'F');
SHA256 sha;
sha.Update(data);
auto hash = sha.Finalize();
EXPECT_EQ(hash.size(), 32u);
}