UltrafastSecp256k1/cpu/tests/test_sha.cpp
shrec d64cd2b8e2
Horizontal audit: benchmarks + tests for Adaptor/FROST/MuSig2/ECIES/SHA/FFI
bench_unified: add sections 8.6-8.10
  - Adaptor Signatures (Schnorr + ECDSA)
  - FROST 2-of-3 threshold (keygen/sign/verify/agg)
  - MuSig2 2-of-2 (key_agg/nonce/partial_sign/sig_agg)
  - ECIES encrypt/decrypt (256B payload)
  - Message Signing, SHA-256/512, Multi-scalar mul (4pt/64pt)
  - Throughput summary entries for all new sections

test_ecies.cpp: 7 tests (roundtrip, variable lengths, wrong key, tamper, nondeterminism)
test_sha.cpp: 9 tests with NIST FIPS 180-4 vectors (SHA-256/512)
test_ffi_coverage.cpp: 34 C ABI tests (ctx_size, Pedersen, ZK range, AEAD, ElligatorSwift, BIP-324)

Bug fixes:
  - ufsecp.h: UFSECP_ZK_RANGE_PROOF_MAX_LEN 675 -> 688 (was too small for actual proof)
  - ufsecp_impl.cpp: fix undefined UFSECP_ERROR -> UFSECP_ERR_INTERNAL,
    UFSECP_ERR_BUFFER_TOO_SMALL -> UFSECP_ERR_BUF_TOO_SMALL in BIP-324 functions

All 28/28 selftest modules pass. All benchmarks produce valid output.
2026-03-22 11:53:04 +00:00

180 lines
6.5 KiB
C++

// ============================================================================
// Test: SHA-256 and SHA-512 Standalone
// ============================================================================
// NIST / RFC test vectors for SHA-256 and SHA-512.
// Covers empty input, short input, two-block input, and hash256 (double-SHA).
// ============================================================================
#include "secp256k1/sha256.hpp"
#include "secp256k1/sha512.hpp"
#include <cstdio>
#include <cstring>
#include <array>
using namespace secp256k1;
static int tests_run = 0;
static int tests_passed = 0;
#define CHECK(cond, msg) do { \
++tests_run; \
if (cond) { ++tests_passed; } \
else { std::printf(" [FAIL] %s\n", msg); } \
} while(0)
// Hex-decode helper (compile-time safe for known test vectors)
static std::array<std::uint8_t, 32> hex32(const char* hex) {
std::array<std::uint8_t, 32> out{};
for (int i = 0; i < 32; ++i) {
auto nibble = [](char c) -> std::uint8_t {
if (c >= '0' && c <= '9') return static_cast<std::uint8_t>(c - '0');
if (c >= 'a' && c <= 'f') return static_cast<std::uint8_t>(c - 'a' + 10);
if (c >= 'A' && c <= 'F') return static_cast<std::uint8_t>(c - 'A' + 10);
return 0;
};
out[static_cast<std::size_t>(i)] = static_cast<std::uint8_t>(
(nibble(hex[2 * i]) << 4) | nibble(hex[2 * i + 1]));
}
return out;
}
static std::array<std::uint8_t, 64> hex64(const char* hex) {
std::array<std::uint8_t, 64> out{};
for (int i = 0; i < 64; ++i) {
auto nibble = [](char c) -> std::uint8_t {
if (c >= '0' && c <= '9') return static_cast<std::uint8_t>(c - '0');
if (c >= 'a' && c <= 'f') return static_cast<std::uint8_t>(c - 'a' + 10);
if (c >= 'A' && c <= 'F') return static_cast<std::uint8_t>(c - 'A' + 10);
return 0;
};
out[static_cast<std::size_t>(i)] = static_cast<std::uint8_t>(
(nibble(hex[2 * i]) << 4) | nibble(hex[2 * i + 1]));
}
return out;
}
// ============================================================================
// SHA-256 Test Vectors (NIST FIPS 180-4)
// ============================================================================
static void test_sha256_empty() {
std::printf("\n=== SHA-256: Empty input ===\n");
auto h = SHA256::hash(nullptr, 0);
auto exp = hex32("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
CHECK(h == exp, "SHA-256(\"\") matches NIST");
}
static void test_sha256_abc() {
std::printf("\n=== SHA-256: \"abc\" ===\n");
const char* msg = "abc";
auto h = SHA256::hash(msg, 3);
auto exp = hex32("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
CHECK(h == exp, "SHA-256(\"abc\") matches NIST");
}
static void test_sha256_two_block() {
std::printf("\n=== SHA-256: Two-block message ===\n");
// "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
const char* msg = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
auto h = SHA256::hash(msg, 56);
auto exp = hex32("248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
CHECK(h == exp, "SHA-256(two-block) matches NIST");
}
static void test_sha256_hash256() {
std::printf("\n=== SHA-256: Double-hash (hash256) ===\n");
// Bitcoin's hash256 = SHA256(SHA256(x))
const char* msg = "abc";
auto h = SHA256::hash256(msg, 3);
// SHA256(SHA256("abc")) = known value
auto inner = SHA256::hash(msg, 3);
auto expected = SHA256::hash(inner.data(), inner.size());
CHECK(h == expected, "hash256 == SHA256(SHA256(data))");
}
static void test_sha256_incremental() {
std::printf("\n=== SHA-256: Incremental update ===\n");
// Hash "abc" as 'a' + 'bc'
SHA256 ctx;
ctx.update("a", 1);
ctx.update("bc", 2);
auto h = ctx.finalize();
auto exp = hex32("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
CHECK(h == exp, "incremental SHA-256(\"abc\") matches one-shot");
}
// ============================================================================
// SHA-512 Test Vectors (NIST FIPS 180-4)
// ============================================================================
static void test_sha512_empty() {
std::printf("\n=== SHA-512: Empty input ===\n");
auto h = SHA512::hash(nullptr, 0);
auto exp = hex64(
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce"
"47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");
CHECK(h == exp, "SHA-512(\"\") matches NIST");
}
static void test_sha512_abc() {
std::printf("\n=== SHA-512: \"abc\" ===\n");
const char* msg = "abc";
auto h = SHA512::hash(msg, 3);
auto exp = hex64(
"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a"
"2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f");
CHECK(h == exp, "SHA-512(\"abc\") matches NIST");
}
static void test_sha512_two_block() {
std::printf("\n=== SHA-512: Two-block message ===\n");
const char* msg = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
"hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
auto h = SHA512::hash(msg, 112);
auto exp = hex64(
"8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018"
"501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909");
CHECK(h == exp, "SHA-512(two-block) matches NIST");
}
static void test_sha512_incremental() {
std::printf("\n=== SHA-512: Incremental update ===\n");
SHA512 ctx;
ctx.update("a", 1);
ctx.update("bc", 2);
auto h = ctx.finalize();
auto exp = hex64(
"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a"
"2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f");
CHECK(h == exp, "incremental SHA-512(\"abc\") matches one-shot");
}
// ============================================================================
// Entry point
// ============================================================================
int test_sha_run() {
std::printf("\n========== SHA-256 / SHA-512 Test Vectors ==========\n");
test_sha256_empty();
test_sha256_abc();
test_sha256_two_block();
test_sha256_hash256();
test_sha256_incremental();
test_sha512_empty();
test_sha512_abc();
test_sha512_two_block();
test_sha512_incremental();
std::printf("\n SHA: %d/%d passed\n", tests_passed, tests_run);
return tests_run - tests_passed;
}
#ifdef STANDALONE_TEST
int main() {
return test_sha_run();
}
#endif