UltrafastSecp256k1/audit/test_infinity_edge_cases.cpp

525 lines
21 KiB
C++
Raw 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.

// ============================================================================
// test_infinity_edge_cases.cpp -- Point-at-Infinity Edge Case Audit
// ============================================================================
//
// Verifies correct handling of the additive identity (point at infinity / O)
// across all C ABI entry points that perform point arithmetic.
//
// The point at infinity arises when:
// - k·G where k = 0 mod n (scalar zero)
// - P + (P) (additive inverse)
// - seckey_tweak_add(k, t) where k + t ≡ 0 mod n
// - pubkey_tweak_add(P, t) where P + t·G = O
// - pubkey_combine with exactly cancelling keys
//
// Correct behaviour: all such operations must return an error (never silently
// yield O as a valid public key, since O is not a valid secp256k1 point for
// any protocol purpose).
//
// INF-1 … INF-4 : seckey_tweak_add cancellation (k + tweak ≡ 0 mod n)
// INF-5 … INF-8 : pubkey_add(P, P) = infinity → error
// INF-9 … INF-12 : pubkey_tweak_add(P, tweak) where P + tweak·G = O
// INF-13 … INF-16 : pubkey_combine with cancelling key set
// INF-17 … INF-20 : ECDH / scalar-mul with zero scalar or zero result
// INF-21 … INF-24 : Taproot and BIP-32 cancellation edge cases
// INF-25 … INF-28 : Serialization: infinity must never serialize as valid key
// ============================================================================
#include <cstdio>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#ifndef UFSECP_BUILDING
#define UFSECP_BUILDING
#endif
#include "ufsecp/ufsecp.h"
static int g_pass = 0, g_fail = 0;
#include "audit_check.hpp"
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
// Group order n (secp256k1)
static constexpr uint8_t N[32] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41
};
// n - 1 (negation of 1 mod n)
static constexpr uint8_t N_MINUS_1[32] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40
};
// n - 2 (negation of 2 mod n)
static constexpr uint8_t N_MINUS_2[32] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x3F
};
// n - 3
static constexpr uint8_t N_MINUS_3[32] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x3E
};
// Privkeys 1, 2, 3
static constexpr uint8_t KEY1[32] = {
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1
};
static constexpr uint8_t KEY2[32] = {
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,2
};
static constexpr uint8_t KEY3[32] = {
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,3
};
// ---------------------------------------------------------------------------
// INF-1 … INF-4 : seckey_tweak_add cancellation
// seckey_tweak_add(k, nk) must fail: result ≡ 0 mod n → invalid key
// ---------------------------------------------------------------------------
static void run_inf1_seckey_cancellation(ufsecp_ctx* ctx) {
AUDIT_LOG("\n [INF-1..4] seckey_tweak_add: k + tweak ≡ 0 mod n must fail\n");
// INF-1: key=1, tweak=n1 → 1 + (n1) = n ≡ 0
{
uint8_t key[32];
std::memcpy(key, KEY1, 32);
ufsecp_error_t rc = ufsecp_seckey_tweak_add(ctx, key, N_MINUS_1);
CHECK(rc != UFSECP_OK,
"INF-1: seckey_tweak_add(1, n-1) → k+t≡0 mod n must fail");
}
// INF-2: key=2, tweak=n2 → 2 + (n2) = n ≡ 0
{
uint8_t key[32];
std::memcpy(key, KEY2, 32);
ufsecp_error_t rc = ufsecp_seckey_tweak_add(ctx, key, N_MINUS_2);
CHECK(rc != UFSECP_OK,
"INF-2: seckey_tweak_add(2, n-2) → k+t≡0 mod n must fail");
}
// INF-3: key=3, tweak=n3 → 3 + (n3) = n ≡ 0
{
uint8_t key[32];
std::memcpy(key, KEY3, 32);
ufsecp_error_t rc = ufsecp_seckey_tweak_add(ctx, key, N_MINUS_3);
CHECK(rc != UFSECP_OK,
"INF-3: seckey_tweak_add(3, n-3) → k+t≡0 mod n must fail");
}
// INF-4: Normal tweak (non-cancelling) should still succeed
{
uint8_t key[32];
std::memcpy(key, KEY1, 32);
ufsecp_error_t rc = ufsecp_seckey_tweak_add(ctx, key, KEY2);
CHECK(rc == UFSECP_OK,
"INF-4: seckey_tweak_add(1, 2) → 3 is valid, must succeed");
}
}
// ---------------------------------------------------------------------------
// INF-5 … INF-8 : pubkey_add(P, P) = O → must fail
// ---------------------------------------------------------------------------
static void run_inf5_pubkey_add_cancel(ufsecp_ctx* ctx) {
AUDIT_LOG("\n [INF-5..8] pubkey_add(P, -P) = infinity must fail\n");
uint8_t result[33] = {};
// INF-5: pub(1) + pub(n-1) = 1·G + (n-1)·G = n·G = O
{
uint8_t pub1[33] = {}, pub_neg1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK,
"INF-5-setup: create pub(1)");
l = 33;
CHECK(ufsecp_pubkey_create(ctx, N_MINUS_1, 1, pub_neg1, &l) == UFSECP_OK,
"INF-5-setup: create pub(n-1)");
ufsecp_error_t rc = ufsecp_pubkey_add(ctx, pub1, pub_neg1, result);
CHECK(rc != UFSECP_OK,
"INF-5: pubkey_add(1·G, (n-1)·G) = n·G = O must fail");
}
// INF-6: pub(2) + pub(n-2) = O
{
uint8_t pub2[33] = {}, pub_neg2[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY2, 1, pub2, &l) == UFSECP_OK,
"INF-6-setup: create pub(2)");
l = 33;
CHECK(ufsecp_pubkey_create(ctx, N_MINUS_2, 1, pub_neg2, &l) == UFSECP_OK,
"INF-6-setup: create pub(n-2)");
ufsecp_error_t rc = ufsecp_pubkey_add(ctx, pub2, pub_neg2, result);
CHECK(rc != UFSECP_OK,
"INF-6: pubkey_add(2·G, (n-2)·G) = O must fail");
}
// INF-7: pub(1) negated via negate() then add back should give O
{
uint8_t pub1[33] = {}, neg_pub1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK,
"INF-7-setup: create pub(1)");
CHECK(ufsecp_pubkey_negate(ctx, pub1, neg_pub1) == UFSECP_OK,
"INF-7-setup: negate pub(1)");
ufsecp_error_t rc = ufsecp_pubkey_add(ctx, pub1, neg_pub1, result);
CHECK(rc != UFSECP_OK,
"INF-7: pubkey_add(P, -P) via negate = O must fail");
}
// INF-8: Normal non-cancelling add must still work
{
uint8_t pub1[33] = {}, pub2[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK,
"INF-8-setup: create pub(1)");
l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY2, 1, pub2, &l) == UFSECP_OK,
"INF-8-setup: create pub(2)");
ufsecp_error_t rc = ufsecp_pubkey_add(ctx, pub1, pub2, result);
CHECK(rc == UFSECP_OK,
"INF-8: pubkey_add(1·G, 2·G) = 3·G must succeed");
}
}
// ---------------------------------------------------------------------------
// INF-9 … INF-12 : pubkey_tweak_add(P, t) where P + t·G = O
// pubkey_tweak_add(pub(k), nk) = k·G + (nk)·G = O → must fail
// ---------------------------------------------------------------------------
static void run_inf9_pubkey_tweak_cancel(ufsecp_ctx* ctx) {
AUDIT_LOG("\n [INF-9..12] pubkey_tweak_add: P + t·G = O must fail\n");
uint8_t result[33] = {};
// INF-9: tweak_add(pub(1), n-1) = 1·G + (n-1)·G = O
{
uint8_t pub1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK,
"INF-9-setup: create pub(1)");
ufsecp_error_t rc = ufsecp_pubkey_tweak_add(ctx, pub1, N_MINUS_1, result);
CHECK(rc != UFSECP_OK,
"INF-9: pubkey_tweak_add(1·G, n-1) = O must fail");
}
// INF-10: tweak_add(pub(2), n-2) = O
{
uint8_t pub2[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY2, 1, pub2, &l) == UFSECP_OK,
"INF-10-setup: create pub(2)");
ufsecp_error_t rc = ufsecp_pubkey_tweak_add(ctx, pub2, N_MINUS_2, result);
CHECK(rc != UFSECP_OK,
"INF-10: pubkey_tweak_add(2·G, n-2) = O must fail");
}
// INF-11: tweak_add(pub(3), n-3) = O
{
uint8_t pub3[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY3, 1, pub3, &l) == UFSECP_OK,
"INF-11-setup: create pub(3)");
ufsecp_error_t rc = ufsecp_pubkey_tweak_add(ctx, pub3, N_MINUS_3, result);
CHECK(rc != UFSECP_OK,
"INF-11: pubkey_tweak_add(3·G, n-3) = O must fail");
}
// INF-12: Normal non-cancelling tweak_add must still work
{
uint8_t pub1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK,
"INF-12-setup: create pub(1)");
ufsecp_error_t rc = ufsecp_pubkey_tweak_add(ctx, pub1, KEY2, result);
CHECK(rc == UFSECP_OK,
"INF-12: pubkey_tweak_add(1·G, 2) = 3·G must succeed");
}
}
// ---------------------------------------------------------------------------
// INF-13 … INF-16 : pubkey_combine with cancelling key set
// ---------------------------------------------------------------------------
static void run_inf13_combine_cancel(ufsecp_ctx* ctx) {
AUDIT_LOG("\n [INF-13..16] pubkey_combine: cancelling key set must fail\n");
uint8_t result[33] = {};
// INF-13: combine([pub(1), pub(n-1)]) = O
{
uint8_t pub1[33] = {}, pub_neg1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK,
"INF-13-setup: create pub(1)");
l = 33;
CHECK(ufsecp_pubkey_create(ctx, N_MINUS_1, 1, pub_neg1, &l) == UFSECP_OK,
"INF-13-setup: create pub(n-1)");
uint8_t buf[66];
std::memcpy(buf, pub1, 33);
std::memcpy(buf + 33, pub_neg1, 33);
ufsecp_error_t rc = ufsecp_pubkey_combine(ctx, buf, 2, result);
CHECK(rc != UFSECP_OK,
"INF-13: pubkey_combine([1·G, (n-1)·G]) = O must fail");
}
// INF-14: combine([pub(1), pub(2), pub(n-3)]) = (1+2+(n-3))·G = O
{
uint8_t pub1[33] = {}, pub2[33] = {}, pub_neg3[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK, "INF-14-setup-1");
l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY2, 1, pub2, &l) == UFSECP_OK, "INF-14-setup-2");
l = 33;
CHECK(ufsecp_pubkey_create(ctx, N_MINUS_3, 1, pub_neg3, &l) == UFSECP_OK, "INF-14-setup-3");
uint8_t buf[99];
std::memcpy(buf, pub1, 33);
std::memcpy(buf + 33, pub2, 33);
std::memcpy(buf + 66, pub_neg3, 33);
ufsecp_error_t rc = ufsecp_pubkey_combine(ctx, buf, 3, result);
CHECK(rc != UFSECP_OK,
"INF-14: pubkey_combine([1G, 2G, (n-3)G]) = O must fail");
}
// INF-15: combine([pub(1), pub(2)]) = 3·G must succeed (normal case)
{
uint8_t pub1[33] = {}, pub2[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK, "INF-15-setup-1");
l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY2, 1, pub2, &l) == UFSECP_OK, "INF-15-setup-2");
uint8_t buf[66];
std::memcpy(buf, pub1, 33);
std::memcpy(buf + 33, pub2, 33);
ufsecp_error_t rc = ufsecp_pubkey_combine(ctx, buf, 2, result);
CHECK(rc == UFSECP_OK,
"INF-15: pubkey_combine([1G, 2G]) = 3G must succeed");
}
// INF-16: combine([pub(1)]) = pub(1) must succeed (trivial 1-element case)
{
uint8_t pub1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK, "INF-16-setup");
ufsecp_error_t rc = ufsecp_pubkey_combine(ctx, pub1, 1, result);
CHECK(rc == UFSECP_OK,
"INF-16: pubkey_combine([1·G]) = 1·G must succeed");
}
}
// ---------------------------------------------------------------------------
// INF-17 … INF-20 : ECDH / scalar-mul with zero scalar or degenerate input
// ---------------------------------------------------------------------------
static void run_inf17_ecdh_degenerate(ufsecp_ctx* ctx) {
AUDIT_LOG("\n [INF-17..20] ECDH / scalar-mul: degenerate inputs\n");
uint8_t shared[32] = {};
// INF-17: ecdh(scalar=0, pub) — 0·P = O → must fail
{
uint8_t zero_key[32] = {};
uint8_t pub1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK, "INF-17-setup");
ufsecp_error_t rc = ufsecp_ecdh(ctx, zero_key, pub1, shared);
CHECK(rc != UFSECP_OK,
"INF-17: ecdh(0, P) = 0·P = O must fail");
}
// INF-18: ecdh(scalar=n, pub) — n·P = O → must fail
{
uint8_t pub1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK, "INF-18-setup");
ufsecp_error_t rc = ufsecp_ecdh(ctx, N, pub1, shared);
CHECK(rc != UFSECP_OK,
"INF-18: ecdh(n, P) = n·P = O must fail (seckey=n is invalid)");
}
// INF-19: ecdh(key=1, pub=1·G) must succeed (normal case)
{
uint8_t pub1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK, "INF-19-setup");
ufsecp_error_t rc = ufsecp_ecdh(ctx, KEY1, pub1, shared);
CHECK(rc == UFSECP_OK,
"INF-19: ecdh(1, 1·G) = 1·G (normal case) must succeed");
}
// INF-20: ecdh_xonly with zero privkey → must fail
{
uint8_t zero_key[32] = {};
uint8_t pub1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY2, 1, pub1, &l) == UFSECP_OK, "INF-20-setup");
ufsecp_error_t rc = ufsecp_ecdh_xonly(ctx, zero_key, pub1, shared);
CHECK(rc != UFSECP_OK,
"INF-20: ecdh_xonly(0, P) must fail");
}
}
// ---------------------------------------------------------------------------
// INF-21 … INF-24 : Taproot and BIP-32 derived key cancellation
// ---------------------------------------------------------------------------
static void run_inf21_taproot_bip32(ufsecp_ctx* ctx) {
AUDIT_LOG("\n [INF-21..24] Taproot tweak + BIP-32 derived key edge cases\n");
// INF-21: taproot_tweak_pubkey(pub(1), tweak=n-1) → output key = O → must fail
{
uint8_t pub1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK, "INF-21-setup");
uint8_t out_xonly[32] = {};
// taproot_tweak_pubkey: internal_key + tweak·G; if = O must fail
ufsecp_error_t rc = ufsecp_taproot_tweak_pubkey(ctx, pub1, N_MINUS_1,
nullptr, out_xonly);
CHECK(rc != UFSECP_OK,
"INF-21: taproot_tweak_pubkey(1·G, n-1) = O must fail");
}
// INF-22: taproot_tweak_seckey(1, n-1) → 0 mod n → must fail
{
uint8_t key_copy[32];
std::memcpy(key_copy, KEY1, 32);
ufsecp_error_t rc = ufsecp_taproot_tweak_seckey(ctx, key_copy, N_MINUS_1);
CHECK(rc != UFSECP_OK,
"INF-22: taproot_tweak_seckey(1, n-1) → 0 mod n must fail");
}
// INF-23: taproot_tweak_pubkey normal case must succeed
{
uint8_t pub1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK, "INF-23-setup");
uint8_t out_xonly[32] = {};
uint8_t tweak[32] = {
0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,
0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20
};
ufsecp_error_t rc = ufsecp_taproot_tweak_pubkey(ctx, pub1, tweak,
nullptr, out_xonly);
CHECK(rc == UFSECP_OK,
"INF-23: taproot_tweak_pubkey normal case must succeed");
}
// INF-24: BIP-32 master from all-zero entropy must generate valid key
// (HMAC-SHA512 output over zero seed should be non-zero key)
{
uint8_t seed[32] = {}; // all-zero seed (degenerate but valid length)
ufsecp_bip32_key master = {};
ufsecp_error_t rc = ufsecp_bip32_master(ctx, seed, 32, &master);
// All-zero seed is valid input; the HMAC-SHA512 output is non-zero
// If it happens to produce an all-zero key (astronomically unlikely),
// the implementation must still reject it
if (rc == UFSECP_OK) {
uint8_t privkey[32] = {};
ufsecp_error_t prc = ufsecp_bip32_privkey(ctx, &master, privkey);
if (prc == UFSECP_OK) {
CHECK(ufsecp_seckey_verify(ctx, privkey) == UFSECP_OK,
"INF-24: BIP-32 master from zero seed produces valid key");
} else {
// If privkey extraction failed, the implementation handled it
CHECK(true, "INF-24: BIP-32 master from zero seed handled safely");
}
} else {
// Some implementations reject all-zero seed at the master level
CHECK(true, "INF-24: BIP-32 rejected all-zero seed (safe)");
}
}
}
// ---------------------------------------------------------------------------
// INF-25 … INF-28 : Serialization — negated key round-trip integrity
// (Verify the negated key serializes/parses to the correct different point)
// ---------------------------------------------------------------------------
static void run_inf25_negate_round_trip(ufsecp_ctx* ctx) {
AUDIT_LOG("\n [INF-25..28] Negated pubkey round-trip integrity\n");
// INF-25: negate(pub(1)) + pub(1) via seckey path
// negate(1) = n-1; seckey_verify(n-1) must be OK; pub(n-1) != pub(1)
{
uint8_t pub1[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY1, 1, pub1, &l) == UFSECP_OK, "INF-25-setup");
uint8_t neg_pub1[33] = {};
CHECK(ufsecp_pubkey_negate(ctx, pub1, neg_pub1) == UFSECP_OK,
"INF-25: pubkey_negate must succeed");
// neg_pub1 must be different from pub1
CHECK(std::memcmp(neg_pub1, pub1, 33) != 0,
"INF-25: negated pub(1) must differ from pub(1)");
}
// INF-26: round-trip: negate(negate(P)) == P
{
uint8_t pub2[33] = {};
size_t l = 33;
CHECK(ufsecp_pubkey_create(ctx, KEY2, 1, pub2, &l) == UFSECP_OK, "INF-26-setup");
uint8_t neg[33] = {};
CHECK(ufsecp_pubkey_negate(ctx, pub2, neg) == UFSECP_OK, "INF-26-negate");
uint8_t neg_neg[33] = {};
CHECK(ufsecp_pubkey_negate(ctx, neg, neg_neg) == UFSECP_OK, "INF-26-neg-neg");
CHECK(std::memcmp(neg_neg, pub2, 33) == 0,
"INF-26: negate(negate(P)) == P");
}
// INF-27: seckey negate: negate(1) = n-1; seckey_verify(n-1) OK; pub(n-1) = negate(pub(1))
{
uint8_t key[32];
std::memcpy(key, KEY1, 32);
CHECK(ufsecp_seckey_negate(ctx, key) == UFSECP_OK,
"INF-27: seckey_negate(1) must succeed");
CHECK(std::memcmp(key, N_MINUS_1, 32) == 0,
"INF-27: seckey_negate(1) == n-1");
CHECK(ufsecp_seckey_verify(ctx, key) == UFSECP_OK,
"INF-27: n-1 is a valid seckey");
}
// INF-28: seckey negate twice round-trips to original
{
uint8_t key[32];
std::memcpy(key, KEY3, 32);
CHECK(ufsecp_seckey_negate(ctx, key) == UFSECP_OK, "INF-28-negate");
CHECK(ufsecp_seckey_negate(ctx, key) == UFSECP_OK, "INF-28-neg-negate");
CHECK(std::memcmp(key, KEY3, 32) == 0,
"INF-28: seckey negate twice round-trips to original");
}
}
// ---------------------------------------------------------------------------
// Entry point
// ---------------------------------------------------------------------------
int test_infinity_edge_cases_run() {
g_pass = 0; g_fail = 0;
AUDIT_LOG("============================================================\n");
AUDIT_LOG(" Point-at-Infinity Edge Case Audit\n");
AUDIT_LOG(" P+(-P), k+(n-k), combine cancel, ECDH degenerate\n");
AUDIT_LOG("============================================================\n");
ufsecp_ctx* ctx = nullptr;
if (ufsecp_ctx_create(&ctx) != UFSECP_OK || ctx == nullptr) {
CHECK(false, "INF-ctx: failed to create context");
printf("[test_infinity_edge_cases] %d/%d checks passed (context failed)\n",
g_pass, g_pass + g_fail);
return 1;
}
run_inf1_seckey_cancellation(ctx);
run_inf5_pubkey_add_cancel(ctx);
run_inf9_pubkey_tweak_cancel(ctx);
run_inf13_combine_cancel(ctx);
run_inf17_ecdh_degenerate(ctx);
run_inf21_taproot_bip32(ctx);
run_inf25_negate_round_trip(ctx);
ufsecp_ctx_destroy(ctx);
printf("[test_infinity_edge_cases] %d/%d checks passed\n",
g_pass, g_pass + g_fail);
return (g_fail > 0) ? 1 : 0;
}
#ifndef UNIFIED_AUDIT_RUNNER
int main() {
return test_infinity_edge_cases_run();
}
#endif