Loading test/ecc.cpp 0 → 100644 +344 −0 Original line number Diff line number Diff line #include <iostream> #include <iomanip> #include <cstring> #include <cstdint> #include "crypto/ecc_p256.h" #include "crypto/ecc_u256.h" using namespace netplus; // ------------------------------------------------------------ // Dump helpers // ------------------------------------------------------------ static void dump_hex(const char* name, const uint8_t* p, size_t n) { std::cout << name << " (" << std::dec << n << " bytes): "; for (size_t i = 0; i < n; i++) std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)p[i]; std::cout << std::dec << "\n"; } static void dump_u256_be(const char* name, const u256& a) { uint8_t b[32]; u256_to_be(b, a); dump_hex(name, b, 32); } static void dump_u256_words(const char* name, const u256& a) { std::cout << name << " (LE words w0..w7): "; for(int i=0;i<8;i++){ std::cout << std::hex << std::setw(8) << std::setfill('0') << a.w[i]; if(i!=7) std::cout << " "; } std::cout << std::dec << "\n"; } static void dump_u512_words(const char* name, const u512& x) { std::cout << name << " (w15..w0): "; for(int i=15;i>=0;--i){ std::cout << std::hex << std::setw(8) << std::setfill('0') << x.w[i]; if(i!=0) std::cout << " "; } std::cout << std::dec << "\n"; } // ------------------------------------------------------------ // Correct: 64 bytes Big Endian -> u512 with w[0]=LSW // ------------------------------------------------------------ static u512 u512_from_be64(const uint8_t be[64]) { u512 r; for(int i=0;i<16;i++){ int o = 4*i; uint32_t w = ((uint32_t)be[o+0] << 24) | ((uint32_t)be[o+1] << 16) | ((uint32_t)be[o+2] << 8) | ((uint32_t)be[o+3] << 0); r.w[15 - i] = w; // store LE } return r; } // ------------------------------------------------------------ // Pattern sanity: 00..3F must map to w15..w0 = 00010203 ... 3C3D3E3F // ------------------------------------------------------------ static bool test_u512_loader() { std::cout << "\n=== u512_from_be64() sanity test (00..3F pattern) ===\n"; uint8_t pat[64]; for(int i=0;i<64;i++) pat[i]=(uint8_t)i; u512 x = u512_from_be64(pat); dump_u512_words("PATTERN", x); uint32_t exp_w15_to_w0[16] = { 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f, 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f }; bool ok=true; for(int i=0;i<16;i++){ uint32_t want = exp_w15_to_w0[i]; uint32_t got = x.w[15 - i]; if(got != want) ok=false; } std::cout << (ok ? "✅ PASS\n" : "❌ FAIL\n"); return ok; } // ------------------------------------------------------------ // Prime check // ------------------------------------------------------------ static void verify_prime_constant() { std::cout << "\n--- Prime Constant Check ---\n"; dump_u256_be("P256_P", P256_P); } // ------------------------------------------------------------ // Montgomery field ops test // ------------------------------------------------------------ static bool test_field_ops_mont() { std::cout << "\n=== Field Operations Test (Montgomery) ===\n"; bool ok = true; u256 two_std = {{2,0,0,0,0,0,0,0}}; u256 three_std = {{3,0,0,0,0,0,0,0}}; u256 one_std = {{1,0,0,0,0,0,0,0}}; u256 prod_std = from_mont(fp_mul(to_mont(two_std), to_mont(three_std))); if(prod_std.w[0] != 6) { ok=false; std::cout << "FAIL: 2*3 != 6\n"; dump_u256_be("Got", prod_std); } else std::cout << "PASS: 2*3 == 6\n"; uint32_t br=0; u256 pm1_std = u256_sub_raw(P256_P, one_std, br); u256 sum_std = from_mont(fp_add(to_mont(pm1_std), to_mont(one_std))); if(!u256_is_zero(sum_std)) { ok=false; std::cout << "FAIL: (P-1)+1 != 0\n"; dump_u256_be("Got", sum_std); } else std::cout << "PASS: (P-1)+1 == 0\n"; u256 sq_std = from_mont(fp_mul(to_mont(pm1_std), to_mont(pm1_std))); if(u256_cmp(sq_std, one_std)!=0) { ok=false; std::cout << "FAIL: (P-1)^2 != 1\n"; dump_u256_be("sq", sq_std); dump_u256_words("sq", sq_std); } else std::cout << "PASS: (P-1)^2 == 1\n"; return ok; } // ------------------------------------------------------------ // FIPS-ish test vector: input = P||payload => result = payload // ------------------------------------------------------------ static bool test_fips_vector() { std::cout << "\n=== NIST P-256 Reduction Test (P||payload) ===\n"; uint8_t be[64] = { // MS 256 bits = P 0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x01, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, // LS 256 bits = payload 0x11,0x22,0x33,0x44, 0x55,0x66,0x77,0x88, 0x99,0xaa,0xbb,0xcc, 0xdd,0xee,0xff,0x00, 0x01,0x02,0x03,0x04, 0x05,0x06,0x07,0x08, 0x09,0x0a,0x0b,0x0c, 0x0d,0x0e,0x0f,0x10 }; u512 input = u512_from_be64(be); dump_u512_words("Input", input); // since input = P*2^256 + payload, input mod P = payload u256 expected = {{ 0x0d0e0f10, 0x090a0b0c, 0x05060708, 0x01020304, 0xddeeff00, 0x99aabbcc, 0x55667788, 0x11223344 }}; u256 got = mod_p256(input); if(u256_cmp(got, expected) != 0) { std::cout << "❌ FAIL\n"; dump_u256_be("Got", got); dump_u256_be("Expected", expected); dump_u256_words("Got", got); dump_u256_words("Expected", expected); return false; } std::cout << "✅ PASS\n"; return true; } // ------------------------------------------------------------ // NIST vector: x = 2^512-1 // ------------------------------------------------------------ // ------------------------------------------------------------ // NIST vector: x = 2^512-1 // ------------------------------------------------------------ static bool test_nist_vector() { std::cout << "\n=== NIST P-256 Reduction Vector Test (All F's) ===\n"; u512 input; for(int i = 0; i < 16; i++) input.w[i] = 0xFFFFFFFFu; // ✅ Correct expected: // (2^512 - 1) mod p = // 00000004fffffffdfffffffffffffffefffffffbffffffff0000000000000002 u256 expected = {{ 0x00000002, // ✅ FIXED (LSW) 0x00000000, 0xFFFFFFFF, 0xFFFFFFFB, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFD, 0x00000004 // MSW }}; u256 got = mod_p256(input); if(u256_cmp(got, expected) != 0) { std::cout << "❌ FAIL\n"; dump_u256_be("Got", got); dump_u256_be("Expected", expected); dump_u256_words("Got", got); dump_u256_words("Expected", expected); return false; } std::cout << "✅ PASS\n"; return true; } static const uint8_t VEC_A_IN[64] = { 0x11,0x22,0x33,0x44,0x38,0x47,0x56,0x21, 0x19,0x28,0x37,0x46,0x48,0x29,0x30,0x21, 0x39,0x53,0x79,0x01,0x18,0x73,0x13,0x2e, 0x45,0x25,0x38,0x11,0x1f,0x49,0x67,0x3b, 0x93,0x30,0x10,0xad,0x48,0x11,0xa2,0xf9, 0xad,0x52,0x07,0x7e,0x7c,0x26,0x37,0x99, 0x22,0x80,0x2d,0x3f,0x11,0x1c,0x0a,0xd1, 0x36,0x7f,0x8a,0x7a,0x4b,0xc4,0x21,0xc4 }; static const uint8_t VEC_A_OUT[32] = { 0x32,0xc8,0x1e,0x1e,0xc7,0xe5,0xa3,0xdf, 0x0f,0x8c,0xc9,0x62,0x19,0x78,0x11,0x49, 0xc9,0x10,0xe4,0x39,0x00,0x50,0xd6,0x54, 0xe9,0x5c,0xe4,0xec,0xdd,0x46,0x8a,0x87 }; static const uint8_t VEC_B_IN[64] = { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }; static const uint8_t VEC_B_OUT[32] = { 0x00,0x00,0x00,0x04,0xff,0xff,0xff,0xfd, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe, 0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02 }; static const uint8_t VEC_C_OUT[32] = { 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88, 0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x00, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, 0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10 }; static bool run_vec(const char* name, const uint8_t in64[64], const uint8_t out32[32]) { u512 x = u512_from_be64(in64); u256 got = mod_p256(x); uint8_t got_be[32]; u256_to_be(got_be, got); if (std::memcmp(got_be, out32, 32) != 0) { std::cout << "❌ FAIL " << name << "\n"; dump_hex("Got", got_be, 32); dump_hex("Expected", out32, 32); return false; } std::cout << "✅ PASS " << name << "\n"; return true; } static bool selftest_modp() { int fails = 0; fails += !run_vec("VEC_A_FIPS", VEC_A_IN, VEC_A_OUT); fails += !run_vec("VEC_B_ALLF", VEC_B_IN, VEC_B_OUT); if (fails != 0){ std::cout << "❌ MODP FAILS: " << fails << "\n"; return false; } std::cout << "✅ ALL MODP TESTS PASSED\n"; return true; } static bool test_kp_plus_x() { u256 x = {{0xdeadbeef,0x11111111,0x22222222,0x33333333, 0x44444444,0x55555555,0x66666666,0x77777777}}; // build u512 input = p*2^256 + x (k=2^256) u512 in; for(int i=0;i<8;i++) in.w[i] = x.w[i]; for(int i=0;i<8;i++) in.w[i+8] = P256_P.w[i]; u256 got = mod_p256(in); bool test = u256_cmp(got, x) == 0 ? true : false; if(!test) std::cout << "❌ test_kp_plus_x FAILED"<< "\n"; return test; } int main() { int fails = 0; if(!test_kp_plus_x()) fails++; if(!selftest_modp()) fails++; if(!test_u512_loader()) fails++; verify_prime_constant(); if(!test_field_ops_mont()) fails++; if(!test_fips_vector()) fails++; if(!test_nist_vector()) fails++; std::cout << "\n==============================\n"; std::cout << "TEST SUMMARY\n"; std::cout << "==============================\n"; if(fails==0) { std::cout << "✅ ALL TESTS PASSED\n"; return 0; } std::cout << "❌ FAILURES: " << fails << "\n"; return 1; } Loading
test/ecc.cpp 0 → 100644 +344 −0 Original line number Diff line number Diff line #include <iostream> #include <iomanip> #include <cstring> #include <cstdint> #include "crypto/ecc_p256.h" #include "crypto/ecc_u256.h" using namespace netplus; // ------------------------------------------------------------ // Dump helpers // ------------------------------------------------------------ static void dump_hex(const char* name, const uint8_t* p, size_t n) { std::cout << name << " (" << std::dec << n << " bytes): "; for (size_t i = 0; i < n; i++) std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)p[i]; std::cout << std::dec << "\n"; } static void dump_u256_be(const char* name, const u256& a) { uint8_t b[32]; u256_to_be(b, a); dump_hex(name, b, 32); } static void dump_u256_words(const char* name, const u256& a) { std::cout << name << " (LE words w0..w7): "; for(int i=0;i<8;i++){ std::cout << std::hex << std::setw(8) << std::setfill('0') << a.w[i]; if(i!=7) std::cout << " "; } std::cout << std::dec << "\n"; } static void dump_u512_words(const char* name, const u512& x) { std::cout << name << " (w15..w0): "; for(int i=15;i>=0;--i){ std::cout << std::hex << std::setw(8) << std::setfill('0') << x.w[i]; if(i!=0) std::cout << " "; } std::cout << std::dec << "\n"; } // ------------------------------------------------------------ // Correct: 64 bytes Big Endian -> u512 with w[0]=LSW // ------------------------------------------------------------ static u512 u512_from_be64(const uint8_t be[64]) { u512 r; for(int i=0;i<16;i++){ int o = 4*i; uint32_t w = ((uint32_t)be[o+0] << 24) | ((uint32_t)be[o+1] << 16) | ((uint32_t)be[o+2] << 8) | ((uint32_t)be[o+3] << 0); r.w[15 - i] = w; // store LE } return r; } // ------------------------------------------------------------ // Pattern sanity: 00..3F must map to w15..w0 = 00010203 ... 3C3D3E3F // ------------------------------------------------------------ static bool test_u512_loader() { std::cout << "\n=== u512_from_be64() sanity test (00..3F pattern) ===\n"; uint8_t pat[64]; for(int i=0;i<64;i++) pat[i]=(uint8_t)i; u512 x = u512_from_be64(pat); dump_u512_words("PATTERN", x); uint32_t exp_w15_to_w0[16] = { 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f, 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f, 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f }; bool ok=true; for(int i=0;i<16;i++){ uint32_t want = exp_w15_to_w0[i]; uint32_t got = x.w[15 - i]; if(got != want) ok=false; } std::cout << (ok ? "✅ PASS\n" : "❌ FAIL\n"); return ok; } // ------------------------------------------------------------ // Prime check // ------------------------------------------------------------ static void verify_prime_constant() { std::cout << "\n--- Prime Constant Check ---\n"; dump_u256_be("P256_P", P256_P); } // ------------------------------------------------------------ // Montgomery field ops test // ------------------------------------------------------------ static bool test_field_ops_mont() { std::cout << "\n=== Field Operations Test (Montgomery) ===\n"; bool ok = true; u256 two_std = {{2,0,0,0,0,0,0,0}}; u256 three_std = {{3,0,0,0,0,0,0,0}}; u256 one_std = {{1,0,0,0,0,0,0,0}}; u256 prod_std = from_mont(fp_mul(to_mont(two_std), to_mont(three_std))); if(prod_std.w[0] != 6) { ok=false; std::cout << "FAIL: 2*3 != 6\n"; dump_u256_be("Got", prod_std); } else std::cout << "PASS: 2*3 == 6\n"; uint32_t br=0; u256 pm1_std = u256_sub_raw(P256_P, one_std, br); u256 sum_std = from_mont(fp_add(to_mont(pm1_std), to_mont(one_std))); if(!u256_is_zero(sum_std)) { ok=false; std::cout << "FAIL: (P-1)+1 != 0\n"; dump_u256_be("Got", sum_std); } else std::cout << "PASS: (P-1)+1 == 0\n"; u256 sq_std = from_mont(fp_mul(to_mont(pm1_std), to_mont(pm1_std))); if(u256_cmp(sq_std, one_std)!=0) { ok=false; std::cout << "FAIL: (P-1)^2 != 1\n"; dump_u256_be("sq", sq_std); dump_u256_words("sq", sq_std); } else std::cout << "PASS: (P-1)^2 == 1\n"; return ok; } // ------------------------------------------------------------ // FIPS-ish test vector: input = P||payload => result = payload // ------------------------------------------------------------ static bool test_fips_vector() { std::cout << "\n=== NIST P-256 Reduction Test (P||payload) ===\n"; uint8_t be[64] = { // MS 256 bits = P 0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x01, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, // LS 256 bits = payload 0x11,0x22,0x33,0x44, 0x55,0x66,0x77,0x88, 0x99,0xaa,0xbb,0xcc, 0xdd,0xee,0xff,0x00, 0x01,0x02,0x03,0x04, 0x05,0x06,0x07,0x08, 0x09,0x0a,0x0b,0x0c, 0x0d,0x0e,0x0f,0x10 }; u512 input = u512_from_be64(be); dump_u512_words("Input", input); // since input = P*2^256 + payload, input mod P = payload u256 expected = {{ 0x0d0e0f10, 0x090a0b0c, 0x05060708, 0x01020304, 0xddeeff00, 0x99aabbcc, 0x55667788, 0x11223344 }}; u256 got = mod_p256(input); if(u256_cmp(got, expected) != 0) { std::cout << "❌ FAIL\n"; dump_u256_be("Got", got); dump_u256_be("Expected", expected); dump_u256_words("Got", got); dump_u256_words("Expected", expected); return false; } std::cout << "✅ PASS\n"; return true; } // ------------------------------------------------------------ // NIST vector: x = 2^512-1 // ------------------------------------------------------------ // ------------------------------------------------------------ // NIST vector: x = 2^512-1 // ------------------------------------------------------------ static bool test_nist_vector() { std::cout << "\n=== NIST P-256 Reduction Vector Test (All F's) ===\n"; u512 input; for(int i = 0; i < 16; i++) input.w[i] = 0xFFFFFFFFu; // ✅ Correct expected: // (2^512 - 1) mod p = // 00000004fffffffdfffffffffffffffefffffffbffffffff0000000000000002 u256 expected = {{ 0x00000002, // ✅ FIXED (LSW) 0x00000000, 0xFFFFFFFF, 0xFFFFFFFB, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFD, 0x00000004 // MSW }}; u256 got = mod_p256(input); if(u256_cmp(got, expected) != 0) { std::cout << "❌ FAIL\n"; dump_u256_be("Got", got); dump_u256_be("Expected", expected); dump_u256_words("Got", got); dump_u256_words("Expected", expected); return false; } std::cout << "✅ PASS\n"; return true; } static const uint8_t VEC_A_IN[64] = { 0x11,0x22,0x33,0x44,0x38,0x47,0x56,0x21, 0x19,0x28,0x37,0x46,0x48,0x29,0x30,0x21, 0x39,0x53,0x79,0x01,0x18,0x73,0x13,0x2e, 0x45,0x25,0x38,0x11,0x1f,0x49,0x67,0x3b, 0x93,0x30,0x10,0xad,0x48,0x11,0xa2,0xf9, 0xad,0x52,0x07,0x7e,0x7c,0x26,0x37,0x99, 0x22,0x80,0x2d,0x3f,0x11,0x1c,0x0a,0xd1, 0x36,0x7f,0x8a,0x7a,0x4b,0xc4,0x21,0xc4 }; static const uint8_t VEC_A_OUT[32] = { 0x32,0xc8,0x1e,0x1e,0xc7,0xe5,0xa3,0xdf, 0x0f,0x8c,0xc9,0x62,0x19,0x78,0x11,0x49, 0xc9,0x10,0xe4,0x39,0x00,0x50,0xd6,0x54, 0xe9,0x5c,0xe4,0xec,0xdd,0x46,0x8a,0x87 }; static const uint8_t VEC_B_IN[64] = { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }; static const uint8_t VEC_B_OUT[32] = { 0x00,0x00,0x00,0x04,0xff,0xff,0xff,0xfd, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe, 0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02 }; static const uint8_t VEC_C_OUT[32] = { 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88, 0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x00, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, 0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10 }; static bool run_vec(const char* name, const uint8_t in64[64], const uint8_t out32[32]) { u512 x = u512_from_be64(in64); u256 got = mod_p256(x); uint8_t got_be[32]; u256_to_be(got_be, got); if (std::memcmp(got_be, out32, 32) != 0) { std::cout << "❌ FAIL " << name << "\n"; dump_hex("Got", got_be, 32); dump_hex("Expected", out32, 32); return false; } std::cout << "✅ PASS " << name << "\n"; return true; } static bool selftest_modp() { int fails = 0; fails += !run_vec("VEC_A_FIPS", VEC_A_IN, VEC_A_OUT); fails += !run_vec("VEC_B_ALLF", VEC_B_IN, VEC_B_OUT); if (fails != 0){ std::cout << "❌ MODP FAILS: " << fails << "\n"; return false; } std::cout << "✅ ALL MODP TESTS PASSED\n"; return true; } static bool test_kp_plus_x() { u256 x = {{0xdeadbeef,0x11111111,0x22222222,0x33333333, 0x44444444,0x55555555,0x66666666,0x77777777}}; // build u512 input = p*2^256 + x (k=2^256) u512 in; for(int i=0;i<8;i++) in.w[i] = x.w[i]; for(int i=0;i<8;i++) in.w[i+8] = P256_P.w[i]; u256 got = mod_p256(in); bool test = u256_cmp(got, x) == 0 ? true : false; if(!test) std::cout << "❌ test_kp_plus_x FAILED"<< "\n"; return test; } int main() { int fails = 0; if(!test_kp_plus_x()) fails++; if(!selftest_modp()) fails++; if(!test_u512_loader()) fails++; verify_prime_constant(); if(!test_field_ops_mont()) fails++; if(!test_fips_vector()) fails++; if(!test_nist_vector()) fails++; std::cout << "\n==============================\n"; std::cout << "TEST SUMMARY\n"; std::cout << "==============================\n"; if(fails==0) { std::cout << "✅ ALL TESTS PASSED\n"; return 0; } std::cout << "❌ FAILURES: " << fails << "\n"; return 1; }