Loading src/crypto/ecc_p256.cpp +205 −149 Original line number Diff line number Diff line Loading @@ -11,42 +11,45 @@ namespace netplus { // b coefficient (NORMAL domain literal, then converted once into Montgomery) static const u256 P256_B_STD = { 0x27D2604B, 0x3BCE3C3E, 0xCC53B0F6, 0x651D06B0, 0x769886BC, 0xB3EBBD55, 0xAA3A93E7, 0x5AC635D8 static const uint8_t P256_B_BE[32] = { 0x5a,0xc6,0x35,0xd8,0xaa,0x3a,0x93,0xe7, 0xb3,0xeb,0xbd,0x55,0x76,0x98,0x86,0xbc, 0x65,0x1d,0x06,0xb0,0xcc,0x53,0xb0,0xf6, 0x3b,0xce,0x3c,0x3e,0x27,0xd2,0x60,0x4b }; const u256 P256_B = to_mont(P256_B_STD); const u256 P256_B = bytes_be_to_u256(P256_B_BE); // ------------------------------------------------------------ // Base point G (Generator) in Montgomery domain // ------------------------------------------------------------ // NIST P-256 / secp256r1 base point G (uncompressed) // Gx = 6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296 // Gy = 4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 // Base point in standard (normal) representation // Standard basepoint (normal domain, LE u256) static const P256Point P256_G_STD = []{ P256Point g; g.inf = false; g.mont = false; g.x = bytes_be_to_u256(P256_GX_BE); g.y = bytes_be_to_u256(P256_GY_BE); return g; }(); // Gx (std domain) g.x = {0xd898c296, 0xf4a13945, 0x2deb33a0, 0x77037d81, 0x63a440f2, 0xf8bce6e5, 0xe12c4247, 0x6b17d1f2}; const P256Point& P256_G() { return P256_G_STD; } // Gy (std domain) g.y = {0x37bf51f5, 0xcbb64068, 0x6b315ece, 0x2bce3357, 0x7c0f9e16, 0x8ee7eb4a, 0xfe1a7f9b, 0x4fe342e2}; return g; }(); // Montgomery basepoint (for internal jacobian arithmetic) static const P256Point P256_G_JAC_MONT = []{ P256Point j; j.mont=true; j.inf = false; j.x = to_mont(P256_G_STD.x); j.y = to_mont(P256_G_STD.y); static const P256Point P256_G_MONT = []{ P256Point g = P256_G_STD; g.x = to_mont(g.x); g.y = to_mont(g.y); g.inf = false; return g; }(); u256 one; u256_zero(one); one.w[0]=1; j.z = to_mont(one); // Z=1 in mont const P256Point& P256_G() { return P256_G_MONT; } return j; }(); // ------------------------------------------------------------ // Point Transformations Loading @@ -56,16 +59,14 @@ const P256Point& P256_G() { return P256_G_MONT; } * Converts Affine (Montgomery) to Jacobian (Montgomery). * Z starts as 1 in Montgomery domain: Z = to_mont(1). */ P256PointJ toJac(const P256Point& a) { P256PointJ r; r.inf = a.inf; r.X = a.x; r.Y = a.y; // Z = 1 (Montgomery) u256 one = {{1,0,0,0,0,0,0,0}}; r.Z = to_mont(one); static inline P256Point jac_inf() { P256Point r; r.inf = true; r.mont = true; u256 zero; u256_zero(zero); r.x = to_mont(zero); r.y = to_mont(zero); r.z = to_mont(zero); // Z=0 => infinity return r; } Loading @@ -73,24 +74,40 @@ P256PointJ toJac(const P256Point& a) { * Converts Jacobian (Montgomery) to Affine (Montgomery). * x = X / Z^2, y = Y / Z^3 (all Montgomery domain) */ P256Point toAff(const P256PointJ& a) { P256Point r; if (a.inf || u256_is_zero(a.Z)) { r.inf = true; return r; P256Point toAff(const P256Point& Jm) { P256Point P; P.mont = false; // ✅ because we return STD if (Jm.inf) { P.inf = true; return P; } // zi = 1/Z (Montgomery) u256 zi = fp_inv(a.Z); u256 zi2 = fp_sqr(zi); u256 zi3 = fp_mul(zi2, zi); // Z == 0 ? u256 z_std = from_mont(Jm.z); if (u256_is_zero(z_std)) { P.inf = true; return P; } r.x = fp_mul(a.X, zi2); r.y = fp_mul(a.Y, zi3); r.inf = false; return r; // zinv = 1/Z in MONT u256 zinv = fp_inv(Jm.z); u256 zinv2 = fp_mul(zinv, zinv); u256 zinv3 = fp_mul(zinv2, zinv); // affine in MONT u256 Xm = fp_mul(Jm.x, zinv2); u256 Ym = fp_mul(Jm.y, zinv3); // convert to STD for output P.inf = false; P.x = from_mont(Xm); P.y = from_mont(Ym); return P; } // ------------------------------------------------------------ // Point Arithmetic // ------------------------------------------------------------ Loading @@ -99,18 +116,18 @@ P256Point toAff(const P256PointJ& a) { * Jacobian point doubling: R = 2P (all Montgomery domain) * Optimized for a=-3. */ P256PointJ point_double(const P256PointJ& a) { if (a.inf || u256_is_zero(a.Z)) return a; P256Point point_double(const P256Point& a) { if (a.inf || u256_is_zero(a.z)) return a; u256 t1 = fp_sqr(a.Z); // Z^2 u256 t2 = fp_sub(a.X, t1); // X - Z^2 u256 t3 = fp_add(a.X, t1); // X + Z^2 u256 t1 = fp_sqr(a.z); // Z^2 u256 t2 = fp_sub(a.x, t1); // X - Z^2 u256 t3 = fp_add(a.x, t1); // X + Z^2 t2 = fp_mul(t2, t3); // (X-Z^2)(X+Z^2) u256 t4 = fp_add(t2, t2); // 2*t2 u256 M = fp_add(t4, t2); // 3*(X^2 - Z^4) u256 Y2 = fp_sqr(a.Y); // Y^2 u256 S = fp_mul(a.X, Y2); // X*Y^2 u256 Y2 = fp_sqr(a.y); // Y^2 u256 S = fp_mul(a.x, Y2); // X*Y^2 S = fp_add(S, S); S = fp_add(S, S); // S = 4*X*Y^2 Loading @@ -126,36 +143,36 @@ P256PointJ point_double(const P256PointJ& a) { u256 S_minus_X3 = fp_sub(S, X3); u256 Y3 = fp_sub(fp_mul(M, S_minus_X3), Y8); u256 YZ = fp_mul(a.Y, a.Z); u256 YZ = fp_mul(a.y, a.z); u256 Z3 = fp_add(YZ, YZ); P256PointJ r; P256Point r; r.inf = false; r.X = X3; r.Y = Y3; r.Z = Z3; r.x = X3; r.y = Y3; r.z = Z3; return r; } /** * Full Jacobian point addition: R = a + b (Montgomery) */ P256PointJ point_add_jac(const P256PointJ& a, const P256PointJ& b) { P256Point point_add_jac(const P256Point& a, const P256Point& b) { if (a.inf) return b; if (b.inf) return a; u256 z1z1 = fp_sqr(a.Z); u256 z2z2 = fp_sqr(b.Z); u256 z1z1 = fp_sqr(a.z); u256 z2z2 = fp_sqr(b.z); u256 u1 = fp_mul(a.X, z2z2); u256 u2 = fp_mul(b.X, z1z1); u256 u1 = fp_mul(a.x, z2z2); u256 u2 = fp_mul(b.x, z1z1); u256 s1 = fp_mul(fp_mul(a.Y, b.Z), z2z2); u256 s2 = fp_mul(fp_mul(b.Y, a.Z), z1z1); u256 s1 = fp_mul(fp_mul(a.y, b.z), z2z2); u256 s2 = fp_mul(fp_mul(b.y, a.z), z1z1); if (u256_cmp(u1, u2) == 0) { if (u256_cmp(s1, s2) == 0) return point_double(a); P256PointJ r; r.inf = true; return r; P256Point r; r.inf = true; return r; } u256 h = fp_sub(u2, u1); Loading @@ -166,13 +183,13 @@ P256PointJ point_add_jac(const P256PointJ& a, const P256PointJ& b) { u256 x3 = fp_sub(fp_sub(fp_sqr(r_val), h3), fp_add(v, v)); u256 y3 = fp_sub(fp_mul(r_val, fp_sub(v, x3)), fp_mul(s1, h3)); u256 z3 = fp_mul(fp_mul(a.Z, b.Z), h); u256 z3 = fp_mul(fp_mul(a.z, b.z), h); P256PointJ res; P256Point res; res.inf = false; res.X = x3; res.Y = y3; res.Z = z3; res.x = x3; res.y = y3; res.z = z3; return res; } Loading @@ -180,50 +197,111 @@ P256PointJ point_add_jac(const P256PointJ& a, const P256PointJ& b) { // Scalar Multiplication (Montgomery Ladder) // ------------------------------------------------------------ static inline void cswap_jac(P256PointJ& a, P256PointJ& b, uint32_t swap) { swap = 0 - swap; // 0x00000000 or 0xFFFFFFFF static inline void cswap_jac(P256Point& A, P256Point& B, uint32_t swap) { // swap is 0 or 1 cswap_u256(A.x, B.x, swap); cswap_u256(A.y, B.y, swap); cswap_u256(A.z, B.z, swap); // IMPORTANT: swap the infinity flag too uint32_t t = (uint32_t)A.inf ^ (uint32_t)B.inf; t &= (0u - swap); A.inf = (bool)((uint32_t)A.inf ^ t); B.inf = (bool)((uint32_t)B.inf ^ t); } P256Point toJac(const P256Point& Pstd) { P256Point J; J.mont = true; if (Pstd.inf) { J.inf = true; u256 z; u256_zero(z); J.x = to_mont(z); J.y = to_mont(z); J.z = to_mont(z); // Z=0 => INF return J; } for (int i = 0; i < 8; i++) { uint32_t t; J.inf = false; J.x = to_mont(Pstd.x); J.y = to_mont(Pstd.y); t = swap & (a.X.w[i] ^ b.X.w[i]); a.X.w[i] ^= t; b.X.w[i] ^= t; t = swap & (a.Y.w[i] ^ b.Y.w[i]); a.Y.w[i] ^= t; b.Y.w[i] ^= t; t = swap & (a.Z.w[i] ^ b.Z.w[i]); a.Z.w[i] ^= t; b.Z.w[i] ^= t; u256 one; u256_zero(one); one.w[0]=1; J.z = to_mont(one); // Z=1 return J; } uint32_t tinf = swap & (uint32_t)(a.inf ^ b.inf); a.inf ^= tinf; b.inf ^= tinf; static bool scalar_is_zero(const uint8_t k[32]) { for (int i=0;i<32;i++) if (k[i]) return false; return true; } P256Point scalar_mul(const uint8_t priv[32], const P256Point& P) { P256PointJ R0; R0.inf = true; P256Point scalar_mul(const uint8_t k[32], const P256Point& Pstd) { // R = INF (Jacobian, MONT) P256Point R; R.inf = true; R.mont = true; u256 z; u256_zero(z); R.x = to_mont(z); R.y = to_mont(z); R.z = to_mont(z); // Q = P as Jacobian (MONT) P256Point Q = toJac(Pstd); // classic double-and-add MSB->LSB (matches get_bit_be) for (int i=0;i<256;i++){ uint32_t bit = get_bit_be(k,i); // For infinity point, set dummy values in Montgomery (so ops are defined) u256 zero; u256_zero(zero); R0.X = to_mont(zero); R0.Y = to_mont(zero); R0.Z = to_mont(zero); R = point_double(R); if(bit) R = point_add_jac(R,Q); } P256PointJ R1 = toJac(P); // ✅ Return affine STD return toAff(R); } uint32_t prev = 0; for (int i = 0; i < 256; i++) { uint32_t b = get_bit_be(priv, i); uint32_t swap = b ^ prev; prev = b; cswap_jac(R0, R1, swap); R1 = point_add_jac(R0, R1); R0 = point_double(R0); P256Point scalar_mul_G(const uint8_t priv[32]) { if (scalar_is_zero(priv)) { P256Point R; R.inf = true; R.mont = false; return R; } cswap_jac(R0, R1, prev); return toAff(R0); return scalar_mul(priv, P256_G()); } P256Point scalar_mul_G(const uint8_t priv[32]) { return scalar_mul(priv, P256_G()); bool is_on_curve(const P256Point& P) { if (P.inf) return true; // reject x>=p or y>=p if (u256_cmp(P.x, P256_P) >= 0) return false; if (u256_cmp(P.y, P256_P) >= 0) return false; u256 x = P.mont ? P.x : to_mont(P.x); u256 y = P.mont ? P.y : to_mont(P.y); u256 b = to_mont(P256_B); u256 y2 = fp_mul(y,y); u256 x2 = fp_mul(x,x); u256 x3 = fp_mul(x2,x); // rhs = x^3 - 3x + b u256 three_std = {{3,0,0,0,0,0,0,0}}; u256 three = to_mont(three_std); u256 rhs = fp_sub(x3, fp_mul(three,x)); rhs = fp_add(rhs,b); return u256_cmp(y2,rhs)==0; } // ------------------------------------------------------------ Loading @@ -235,66 +313,44 @@ P256Point scalar_mul_G(const uint8_t priv[32]) { * Internally stores coordinates in Montgomery domain. * Also validates curve equation in Montgomery domain. */ bool decode_tls_point(P256Point& out, const uint8_t* p, size_t n) { if (!p || n < 65 || p[0] != 0x04) return false; // Read coordinates from network Big Endian -> normal u256 u256 x_std = u256_from_be(p + 1); u256 y_std = u256_from_be(p + 33); bool decode_tls_point(P256Point& out, const uint8_t* in, size_t len) { if (len != 65 || in[0] != 0x04) return false; // Convert to Montgomery internal representation out.x = to_mont(x_std); out.y = to_mont(y_std); out.inf = false; // Validate y^2 == x^3 - 3x + b (all Montgomery) u256 y2 = fp_sqr(out.y); u256 x2 = fp_sqr(out.x); u256 x3 = fp_mul(x2, out.x); u256 three_x = fp_add(out.x, out.x); three_x = fp_add(three_x, out.x); u256 rhs = fp_sub(x3, three_x); rhs = fp_add(rhs, P256_B); if (u256_cmp(y2, rhs) != 0) { out.inf = true; return false; } out.mont = false; out.x = bytes_be_to_u256(in + 1); out.y = bytes_be_to_u256(in + 33); return true; } /** * Encodes affine point (Montgomery) to TLS (std big endian). */ std::vector<uint8_t> encode_tls_point(const P256Point& P) { if (P.inf) return {}; std::vector<uint8_t> res(65); res[0] = 0x04; // Convert from Montgomery -> standard for output u256 x_std = from_mont(P.x); u256 y_std = from_mont(P.y); std::vector<uint8_t> encode_tls_point(const P256Point& P) { std::vector<uint8_t> out(65); out[0]=0x04; u256_to_be(&out[1], P.x); u256_to_be(&out[33],P.y); return out; } u256_to_be(res.data() + 1, x_std); u256_to_be(res.data() + 33, y_std); return res; } /** * ECDH: returns Big-Endian X-coordinate of shared point. */ bool ecdh_shared_secret(uint8_t out32[32], const uint8_t priv[32], const P256Point& peer) { bool ecdh_shared_secret(uint8_t out32[32], const uint8_t priv[32], const P256Point& peer) { if (!out32 || peer.inf) return false; P256Point shared_point = scalar_mul(priv, peer); if (shared_point.inf) return false; // scalar_mul returns affine STD P256Point shared = scalar_mul(priv, peer); if (shared.inf) return false; u256 x_std = from_mont(shared_point.x); u256_to_be(out32, x_std); u256_to_be(out32, shared.x); // ✅ already STD return true; } Loading src/crypto/ecc_p256.h +34 −17 Original line number Diff line number Diff line Loading @@ -6,6 +6,31 @@ namespace netplus { static const u256 P256_N = {{ 0xFC632551, // w[0] LSW 0xF3B9CAC2, 0xA7179E84, 0xBCE6FAAD, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF // w[7] MSW }}; static const uint8_t P256_GX_BE[32] = { 0x6b,0x17,0xd1,0xf2,0xe1,0x2c,0x42,0x47, 0xf8,0xbc,0xe6,0xe5,0x63,0xa4,0x40,0xf2, 0x77,0x03,0x7d,0x81,0x2d,0xeb,0x33,0xa0, 0xf4,0xa1,0x39,0x45,0xd8,0x98,0xc2,0x96 }; static const uint8_t P256_GY_BE[32] = { 0x4f,0xe3,0x42,0xe2,0xfe,0x1a,0x7f,0x9b, 0x8e,0xe7,0xeb,0x4a,0x7c,0x0f,0x9e,0x16, 0x2b,0xce,0x33,0x57,0x6b,0x31,0x5e,0xce, 0xcb,0xb6,0x40,0x68,0x37,0xbf,0x51,0xf5 }; /** * @brief Affine point on the NIST P-256 curve. * Equation: y^2 = x^3 - 3x + b (mod p) Loading @@ -14,19 +39,9 @@ namespace netplus { struct P256Point { u256 x; u256 y; bool inf = true; // Flag for Point at Infinity (neutral element) }; /** * @brief Jacobian point representation (X:Y:Z). * Conversion to affine: x = X/Z^2, y = Y/Z^3. * Used for efficient point arithmetic to avoid frequent modular inversions. */ struct P256PointJ { u256 X; u256 Y; u256 Z; u256 z; // only valid if mont=true (Jacobian) bool inf = true; bool mont = false; }; // --- NIST P-256 Curve Constants --- Loading @@ -46,26 +61,26 @@ extern const u256 P256_B; /** * @brief Converts affine coordinates to Jacobian coordinates. * Sets Z = 1. */ P256PointJ toJac(const P256Point& a); P256Point toJac(const P256Point& P); /** * @brief Converts Jacobian coordinates back to Affine. * Requires one modular inversion. */ P256Point toAff(const P256PointJ& a); P256Point toAff(const P256Point& a); /** * @brief Point doubling: R = 2 * P. * Optimized for the NIST case where a = -3. */ P256PointJ point_double(const P256PointJ& a); P256Point point_double(const P256Point& a); /** * @brief Full Jacobian point addition: R = P1 + P2. */ P256PointJ point_add_jac(const P256PointJ& a, const P256PointJ& b); P256Point point_add_jac(const P256Point& a, const P256Point& b); /** * @brief Mixed addition: R = P1 (Jacobian) + P2 (Affine). * Faster than full Jacobian addition when one point is already affine. */ P256PointJ point_add_mixed(const P256PointJ& a, const P256Point& b); P256Point point_add_mixed(const P256Point& a, const P256Point& b); // --- Scalar Multiplication --- Loading @@ -81,6 +96,8 @@ P256Point scalar_mul(const uint8_t priv[32], const P256Point& P); */ P256Point scalar_mul_G(const uint8_t priv[32]); bool is_on_curve(const P256Point& P); // --- TLS / Interoperability --- /** Loading src/crypto/ecc_u256.cpp +21 −7 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ const u256 P256_P = {{ 0x00000000u, 0x00000000u, 0x00000001u, 0xFFFFFFFFu }}; static const u256 P256_R = {{ 0x00000001u, 0x00000000u, Loading Loading @@ -115,6 +116,20 @@ void u256_to_be(uint8_t out32[32], const u256& a) { } } u256 bytes_be_to_u256(const uint8_t in[32]) { u256 r; for (int i = 0; i < 8; i++) { int o = 28 - 4*i; // ✅ same mapping as u256_to_be r.w[i] = ((uint32_t)in[o+0] << 24) | ((uint32_t)in[o+1] << 16) | ((uint32_t)in[o+2] << 8) | ((uint32_t)in[o+3] << 0); } return r; } // ============================================================================ // 256x256 -> 512 multiply (schoolbook, correct carry), NO __int128 // ============================================================================ Loading Loading @@ -167,7 +182,6 @@ static inline u512 u512_from_be64(const uint8_t be64[64]) { // ============================================================================ // Montgomery REDC for P-256 (8 limbs, 32-bit, no __int128) // This version is stable and matches classic HAC REDC. // ============================================================================ u256 mont_mul(const u256& a, const u256& b) { uint32_t t[16] = {0}; uint32_t extra = 0; // overflow beyond t[15] Loading Loading @@ -246,7 +260,6 @@ u256 mont_mul(const u256& a, const u256& b) { return r; } // ---------------------------------------------------------------------------- // to/from Montgomery // ---------------------------------------------------------------------------- Loading @@ -254,9 +267,9 @@ u256 to_mont(const u256& a) { return mont_mul(a, P256_R2); } u256 from_mont(const u256& a) { u256 from_mont(const u256& aR) { u256 one; u256_zero(one); one.w[0]=1; return mont_mul(a, one); return mont_mul(aR, one); // NUR wenn mont_mul als REDC(t) implementiert ist } // ---------------------------------------------------------------------------- Loading Loading @@ -294,8 +307,9 @@ u256 fp_sqr(const u256& a) { // bit access // ---------------------------------------------------------------------------- uint32_t get_bit_be(const uint8_t priv[32], int bit_index) { int byte = bit_index / 8; int bit = 7 - (bit_index % 8); // i=0 -> MSB of priv[0] int byte = bit_index >> 3; int bit = 7 - (bit_index & 7); return (priv[byte] >> bit) & 1; } Loading src/crypto/ecc_u256.h +20 −1 Original line number Diff line number Diff line Loading @@ -13,6 +13,25 @@ extern const u256 P256_R2; // -------- low-level helpers -------- void u256_zero(u256& a); bool u256_is_zero(const u256& a); static inline void u256_one(u256& a) { u256_zero(a); a.w[0] = 1; } static inline void cswap_u256(u256& A, u256& B, uint32_t swap) { // swap is 0 or 1 uint32_t mask = 0u - swap; // 0x00000000 or 0xFFFFFFFF for (int i = 0; i < 8; i++) { uint32_t t = (A.w[i] ^ B.w[i]) & mask; A.w[i] ^= t; B.w[i] ^= t; } } int u256_cmp(const u256& a, const u256& b); // raw ops (no mod reduction) Loading @@ -23,7 +42,7 @@ u256 u256_sub_raw(const u256& a, const u256& b, uint32_t& borrow); u256 u256_from_be(const uint8_t be32[32]); void u256_to_be(uint8_t out32[32], const u256& a); u512 u512_from_be(const uint8_t in[64]); u256 bytes_be_to_u256(const uint8_t in[32]); // multiply u512 u256_mul_raw(const u256& a, const u256& b); Loading src/socket.h +4 −0 Original line number Diff line number Diff line Loading @@ -477,6 +477,7 @@ namespace netplus { void _tls13_transcript_add_raw(const std::vector<uint8_t>& hsmsg); std::vector<uint8_t> _tls13_build_server_finished_body(); std::vector<uint8_t> _tls13_build_server_finished(); bool _handshakeStarted = false; Loading Loading @@ -532,6 +533,9 @@ namespace netplus { std::vector<uint8_t> _client_session_id; bool _client_offered_tls13 = false; bool _encflight_queued = false; bool _compat_ccs_sent = false; bool _client_has_tls13_keyshare_x25519=false; // --- TLS 1.3 (minimal) --- bool _is_tls13 = false; Loading Loading
src/crypto/ecc_p256.cpp +205 −149 Original line number Diff line number Diff line Loading @@ -11,42 +11,45 @@ namespace netplus { // b coefficient (NORMAL domain literal, then converted once into Montgomery) static const u256 P256_B_STD = { 0x27D2604B, 0x3BCE3C3E, 0xCC53B0F6, 0x651D06B0, 0x769886BC, 0xB3EBBD55, 0xAA3A93E7, 0x5AC635D8 static const uint8_t P256_B_BE[32] = { 0x5a,0xc6,0x35,0xd8,0xaa,0x3a,0x93,0xe7, 0xb3,0xeb,0xbd,0x55,0x76,0x98,0x86,0xbc, 0x65,0x1d,0x06,0xb0,0xcc,0x53,0xb0,0xf6, 0x3b,0xce,0x3c,0x3e,0x27,0xd2,0x60,0x4b }; const u256 P256_B = to_mont(P256_B_STD); const u256 P256_B = bytes_be_to_u256(P256_B_BE); // ------------------------------------------------------------ // Base point G (Generator) in Montgomery domain // ------------------------------------------------------------ // NIST P-256 / secp256r1 base point G (uncompressed) // Gx = 6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296 // Gy = 4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 // Base point in standard (normal) representation // Standard basepoint (normal domain, LE u256) static const P256Point P256_G_STD = []{ P256Point g; g.inf = false; g.mont = false; g.x = bytes_be_to_u256(P256_GX_BE); g.y = bytes_be_to_u256(P256_GY_BE); return g; }(); // Gx (std domain) g.x = {0xd898c296, 0xf4a13945, 0x2deb33a0, 0x77037d81, 0x63a440f2, 0xf8bce6e5, 0xe12c4247, 0x6b17d1f2}; const P256Point& P256_G() { return P256_G_STD; } // Gy (std domain) g.y = {0x37bf51f5, 0xcbb64068, 0x6b315ece, 0x2bce3357, 0x7c0f9e16, 0x8ee7eb4a, 0xfe1a7f9b, 0x4fe342e2}; return g; }(); // Montgomery basepoint (for internal jacobian arithmetic) static const P256Point P256_G_JAC_MONT = []{ P256Point j; j.mont=true; j.inf = false; j.x = to_mont(P256_G_STD.x); j.y = to_mont(P256_G_STD.y); static const P256Point P256_G_MONT = []{ P256Point g = P256_G_STD; g.x = to_mont(g.x); g.y = to_mont(g.y); g.inf = false; return g; }(); u256 one; u256_zero(one); one.w[0]=1; j.z = to_mont(one); // Z=1 in mont const P256Point& P256_G() { return P256_G_MONT; } return j; }(); // ------------------------------------------------------------ // Point Transformations Loading @@ -56,16 +59,14 @@ const P256Point& P256_G() { return P256_G_MONT; } * Converts Affine (Montgomery) to Jacobian (Montgomery). * Z starts as 1 in Montgomery domain: Z = to_mont(1). */ P256PointJ toJac(const P256Point& a) { P256PointJ r; r.inf = a.inf; r.X = a.x; r.Y = a.y; // Z = 1 (Montgomery) u256 one = {{1,0,0,0,0,0,0,0}}; r.Z = to_mont(one); static inline P256Point jac_inf() { P256Point r; r.inf = true; r.mont = true; u256 zero; u256_zero(zero); r.x = to_mont(zero); r.y = to_mont(zero); r.z = to_mont(zero); // Z=0 => infinity return r; } Loading @@ -73,24 +74,40 @@ P256PointJ toJac(const P256Point& a) { * Converts Jacobian (Montgomery) to Affine (Montgomery). * x = X / Z^2, y = Y / Z^3 (all Montgomery domain) */ P256Point toAff(const P256PointJ& a) { P256Point r; if (a.inf || u256_is_zero(a.Z)) { r.inf = true; return r; P256Point toAff(const P256Point& Jm) { P256Point P; P.mont = false; // ✅ because we return STD if (Jm.inf) { P.inf = true; return P; } // zi = 1/Z (Montgomery) u256 zi = fp_inv(a.Z); u256 zi2 = fp_sqr(zi); u256 zi3 = fp_mul(zi2, zi); // Z == 0 ? u256 z_std = from_mont(Jm.z); if (u256_is_zero(z_std)) { P.inf = true; return P; } r.x = fp_mul(a.X, zi2); r.y = fp_mul(a.Y, zi3); r.inf = false; return r; // zinv = 1/Z in MONT u256 zinv = fp_inv(Jm.z); u256 zinv2 = fp_mul(zinv, zinv); u256 zinv3 = fp_mul(zinv2, zinv); // affine in MONT u256 Xm = fp_mul(Jm.x, zinv2); u256 Ym = fp_mul(Jm.y, zinv3); // convert to STD for output P.inf = false; P.x = from_mont(Xm); P.y = from_mont(Ym); return P; } // ------------------------------------------------------------ // Point Arithmetic // ------------------------------------------------------------ Loading @@ -99,18 +116,18 @@ P256Point toAff(const P256PointJ& a) { * Jacobian point doubling: R = 2P (all Montgomery domain) * Optimized for a=-3. */ P256PointJ point_double(const P256PointJ& a) { if (a.inf || u256_is_zero(a.Z)) return a; P256Point point_double(const P256Point& a) { if (a.inf || u256_is_zero(a.z)) return a; u256 t1 = fp_sqr(a.Z); // Z^2 u256 t2 = fp_sub(a.X, t1); // X - Z^2 u256 t3 = fp_add(a.X, t1); // X + Z^2 u256 t1 = fp_sqr(a.z); // Z^2 u256 t2 = fp_sub(a.x, t1); // X - Z^2 u256 t3 = fp_add(a.x, t1); // X + Z^2 t2 = fp_mul(t2, t3); // (X-Z^2)(X+Z^2) u256 t4 = fp_add(t2, t2); // 2*t2 u256 M = fp_add(t4, t2); // 3*(X^2 - Z^4) u256 Y2 = fp_sqr(a.Y); // Y^2 u256 S = fp_mul(a.X, Y2); // X*Y^2 u256 Y2 = fp_sqr(a.y); // Y^2 u256 S = fp_mul(a.x, Y2); // X*Y^2 S = fp_add(S, S); S = fp_add(S, S); // S = 4*X*Y^2 Loading @@ -126,36 +143,36 @@ P256PointJ point_double(const P256PointJ& a) { u256 S_minus_X3 = fp_sub(S, X3); u256 Y3 = fp_sub(fp_mul(M, S_minus_X3), Y8); u256 YZ = fp_mul(a.Y, a.Z); u256 YZ = fp_mul(a.y, a.z); u256 Z3 = fp_add(YZ, YZ); P256PointJ r; P256Point r; r.inf = false; r.X = X3; r.Y = Y3; r.Z = Z3; r.x = X3; r.y = Y3; r.z = Z3; return r; } /** * Full Jacobian point addition: R = a + b (Montgomery) */ P256PointJ point_add_jac(const P256PointJ& a, const P256PointJ& b) { P256Point point_add_jac(const P256Point& a, const P256Point& b) { if (a.inf) return b; if (b.inf) return a; u256 z1z1 = fp_sqr(a.Z); u256 z2z2 = fp_sqr(b.Z); u256 z1z1 = fp_sqr(a.z); u256 z2z2 = fp_sqr(b.z); u256 u1 = fp_mul(a.X, z2z2); u256 u2 = fp_mul(b.X, z1z1); u256 u1 = fp_mul(a.x, z2z2); u256 u2 = fp_mul(b.x, z1z1); u256 s1 = fp_mul(fp_mul(a.Y, b.Z), z2z2); u256 s2 = fp_mul(fp_mul(b.Y, a.Z), z1z1); u256 s1 = fp_mul(fp_mul(a.y, b.z), z2z2); u256 s2 = fp_mul(fp_mul(b.y, a.z), z1z1); if (u256_cmp(u1, u2) == 0) { if (u256_cmp(s1, s2) == 0) return point_double(a); P256PointJ r; r.inf = true; return r; P256Point r; r.inf = true; return r; } u256 h = fp_sub(u2, u1); Loading @@ -166,13 +183,13 @@ P256PointJ point_add_jac(const P256PointJ& a, const P256PointJ& b) { u256 x3 = fp_sub(fp_sub(fp_sqr(r_val), h3), fp_add(v, v)); u256 y3 = fp_sub(fp_mul(r_val, fp_sub(v, x3)), fp_mul(s1, h3)); u256 z3 = fp_mul(fp_mul(a.Z, b.Z), h); u256 z3 = fp_mul(fp_mul(a.z, b.z), h); P256PointJ res; P256Point res; res.inf = false; res.X = x3; res.Y = y3; res.Z = z3; res.x = x3; res.y = y3; res.z = z3; return res; } Loading @@ -180,50 +197,111 @@ P256PointJ point_add_jac(const P256PointJ& a, const P256PointJ& b) { // Scalar Multiplication (Montgomery Ladder) // ------------------------------------------------------------ static inline void cswap_jac(P256PointJ& a, P256PointJ& b, uint32_t swap) { swap = 0 - swap; // 0x00000000 or 0xFFFFFFFF static inline void cswap_jac(P256Point& A, P256Point& B, uint32_t swap) { // swap is 0 or 1 cswap_u256(A.x, B.x, swap); cswap_u256(A.y, B.y, swap); cswap_u256(A.z, B.z, swap); // IMPORTANT: swap the infinity flag too uint32_t t = (uint32_t)A.inf ^ (uint32_t)B.inf; t &= (0u - swap); A.inf = (bool)((uint32_t)A.inf ^ t); B.inf = (bool)((uint32_t)B.inf ^ t); } P256Point toJac(const P256Point& Pstd) { P256Point J; J.mont = true; if (Pstd.inf) { J.inf = true; u256 z; u256_zero(z); J.x = to_mont(z); J.y = to_mont(z); J.z = to_mont(z); // Z=0 => INF return J; } for (int i = 0; i < 8; i++) { uint32_t t; J.inf = false; J.x = to_mont(Pstd.x); J.y = to_mont(Pstd.y); t = swap & (a.X.w[i] ^ b.X.w[i]); a.X.w[i] ^= t; b.X.w[i] ^= t; t = swap & (a.Y.w[i] ^ b.Y.w[i]); a.Y.w[i] ^= t; b.Y.w[i] ^= t; t = swap & (a.Z.w[i] ^ b.Z.w[i]); a.Z.w[i] ^= t; b.Z.w[i] ^= t; u256 one; u256_zero(one); one.w[0]=1; J.z = to_mont(one); // Z=1 return J; } uint32_t tinf = swap & (uint32_t)(a.inf ^ b.inf); a.inf ^= tinf; b.inf ^= tinf; static bool scalar_is_zero(const uint8_t k[32]) { for (int i=0;i<32;i++) if (k[i]) return false; return true; } P256Point scalar_mul(const uint8_t priv[32], const P256Point& P) { P256PointJ R0; R0.inf = true; P256Point scalar_mul(const uint8_t k[32], const P256Point& Pstd) { // R = INF (Jacobian, MONT) P256Point R; R.inf = true; R.mont = true; u256 z; u256_zero(z); R.x = to_mont(z); R.y = to_mont(z); R.z = to_mont(z); // Q = P as Jacobian (MONT) P256Point Q = toJac(Pstd); // classic double-and-add MSB->LSB (matches get_bit_be) for (int i=0;i<256;i++){ uint32_t bit = get_bit_be(k,i); // For infinity point, set dummy values in Montgomery (so ops are defined) u256 zero; u256_zero(zero); R0.X = to_mont(zero); R0.Y = to_mont(zero); R0.Z = to_mont(zero); R = point_double(R); if(bit) R = point_add_jac(R,Q); } P256PointJ R1 = toJac(P); // ✅ Return affine STD return toAff(R); } uint32_t prev = 0; for (int i = 0; i < 256; i++) { uint32_t b = get_bit_be(priv, i); uint32_t swap = b ^ prev; prev = b; cswap_jac(R0, R1, swap); R1 = point_add_jac(R0, R1); R0 = point_double(R0); P256Point scalar_mul_G(const uint8_t priv[32]) { if (scalar_is_zero(priv)) { P256Point R; R.inf = true; R.mont = false; return R; } cswap_jac(R0, R1, prev); return toAff(R0); return scalar_mul(priv, P256_G()); } P256Point scalar_mul_G(const uint8_t priv[32]) { return scalar_mul(priv, P256_G()); bool is_on_curve(const P256Point& P) { if (P.inf) return true; // reject x>=p or y>=p if (u256_cmp(P.x, P256_P) >= 0) return false; if (u256_cmp(P.y, P256_P) >= 0) return false; u256 x = P.mont ? P.x : to_mont(P.x); u256 y = P.mont ? P.y : to_mont(P.y); u256 b = to_mont(P256_B); u256 y2 = fp_mul(y,y); u256 x2 = fp_mul(x,x); u256 x3 = fp_mul(x2,x); // rhs = x^3 - 3x + b u256 three_std = {{3,0,0,0,0,0,0,0}}; u256 three = to_mont(three_std); u256 rhs = fp_sub(x3, fp_mul(three,x)); rhs = fp_add(rhs,b); return u256_cmp(y2,rhs)==0; } // ------------------------------------------------------------ Loading @@ -235,66 +313,44 @@ P256Point scalar_mul_G(const uint8_t priv[32]) { * Internally stores coordinates in Montgomery domain. * Also validates curve equation in Montgomery domain. */ bool decode_tls_point(P256Point& out, const uint8_t* p, size_t n) { if (!p || n < 65 || p[0] != 0x04) return false; // Read coordinates from network Big Endian -> normal u256 u256 x_std = u256_from_be(p + 1); u256 y_std = u256_from_be(p + 33); bool decode_tls_point(P256Point& out, const uint8_t* in, size_t len) { if (len != 65 || in[0] != 0x04) return false; // Convert to Montgomery internal representation out.x = to_mont(x_std); out.y = to_mont(y_std); out.inf = false; // Validate y^2 == x^3 - 3x + b (all Montgomery) u256 y2 = fp_sqr(out.y); u256 x2 = fp_sqr(out.x); u256 x3 = fp_mul(x2, out.x); u256 three_x = fp_add(out.x, out.x); three_x = fp_add(three_x, out.x); u256 rhs = fp_sub(x3, three_x); rhs = fp_add(rhs, P256_B); if (u256_cmp(y2, rhs) != 0) { out.inf = true; return false; } out.mont = false; out.x = bytes_be_to_u256(in + 1); out.y = bytes_be_to_u256(in + 33); return true; } /** * Encodes affine point (Montgomery) to TLS (std big endian). */ std::vector<uint8_t> encode_tls_point(const P256Point& P) { if (P.inf) return {}; std::vector<uint8_t> res(65); res[0] = 0x04; // Convert from Montgomery -> standard for output u256 x_std = from_mont(P.x); u256 y_std = from_mont(P.y); std::vector<uint8_t> encode_tls_point(const P256Point& P) { std::vector<uint8_t> out(65); out[0]=0x04; u256_to_be(&out[1], P.x); u256_to_be(&out[33],P.y); return out; } u256_to_be(res.data() + 1, x_std); u256_to_be(res.data() + 33, y_std); return res; } /** * ECDH: returns Big-Endian X-coordinate of shared point. */ bool ecdh_shared_secret(uint8_t out32[32], const uint8_t priv[32], const P256Point& peer) { bool ecdh_shared_secret(uint8_t out32[32], const uint8_t priv[32], const P256Point& peer) { if (!out32 || peer.inf) return false; P256Point shared_point = scalar_mul(priv, peer); if (shared_point.inf) return false; // scalar_mul returns affine STD P256Point shared = scalar_mul(priv, peer); if (shared.inf) return false; u256 x_std = from_mont(shared_point.x); u256_to_be(out32, x_std); u256_to_be(out32, shared.x); // ✅ already STD return true; } Loading
src/crypto/ecc_p256.h +34 −17 Original line number Diff line number Diff line Loading @@ -6,6 +6,31 @@ namespace netplus { static const u256 P256_N = {{ 0xFC632551, // w[0] LSW 0xF3B9CAC2, 0xA7179E84, 0xBCE6FAAD, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF // w[7] MSW }}; static const uint8_t P256_GX_BE[32] = { 0x6b,0x17,0xd1,0xf2,0xe1,0x2c,0x42,0x47, 0xf8,0xbc,0xe6,0xe5,0x63,0xa4,0x40,0xf2, 0x77,0x03,0x7d,0x81,0x2d,0xeb,0x33,0xa0, 0xf4,0xa1,0x39,0x45,0xd8,0x98,0xc2,0x96 }; static const uint8_t P256_GY_BE[32] = { 0x4f,0xe3,0x42,0xe2,0xfe,0x1a,0x7f,0x9b, 0x8e,0xe7,0xeb,0x4a,0x7c,0x0f,0x9e,0x16, 0x2b,0xce,0x33,0x57,0x6b,0x31,0x5e,0xce, 0xcb,0xb6,0x40,0x68,0x37,0xbf,0x51,0xf5 }; /** * @brief Affine point on the NIST P-256 curve. * Equation: y^2 = x^3 - 3x + b (mod p) Loading @@ -14,19 +39,9 @@ namespace netplus { struct P256Point { u256 x; u256 y; bool inf = true; // Flag for Point at Infinity (neutral element) }; /** * @brief Jacobian point representation (X:Y:Z). * Conversion to affine: x = X/Z^2, y = Y/Z^3. * Used for efficient point arithmetic to avoid frequent modular inversions. */ struct P256PointJ { u256 X; u256 Y; u256 Z; u256 z; // only valid if mont=true (Jacobian) bool inf = true; bool mont = false; }; // --- NIST P-256 Curve Constants --- Loading @@ -46,26 +61,26 @@ extern const u256 P256_B; /** * @brief Converts affine coordinates to Jacobian coordinates. * Sets Z = 1. */ P256PointJ toJac(const P256Point& a); P256Point toJac(const P256Point& P); /** * @brief Converts Jacobian coordinates back to Affine. * Requires one modular inversion. */ P256Point toAff(const P256PointJ& a); P256Point toAff(const P256Point& a); /** * @brief Point doubling: R = 2 * P. * Optimized for the NIST case where a = -3. */ P256PointJ point_double(const P256PointJ& a); P256Point point_double(const P256Point& a); /** * @brief Full Jacobian point addition: R = P1 + P2. */ P256PointJ point_add_jac(const P256PointJ& a, const P256PointJ& b); P256Point point_add_jac(const P256Point& a, const P256Point& b); /** * @brief Mixed addition: R = P1 (Jacobian) + P2 (Affine). * Faster than full Jacobian addition when one point is already affine. */ P256PointJ point_add_mixed(const P256PointJ& a, const P256Point& b); P256Point point_add_mixed(const P256Point& a, const P256Point& b); // --- Scalar Multiplication --- Loading @@ -81,6 +96,8 @@ P256Point scalar_mul(const uint8_t priv[32], const P256Point& P); */ P256Point scalar_mul_G(const uint8_t priv[32]); bool is_on_curve(const P256Point& P); // --- TLS / Interoperability --- /** Loading
src/crypto/ecc_u256.cpp +21 −7 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ const u256 P256_P = {{ 0x00000000u, 0x00000000u, 0x00000001u, 0xFFFFFFFFu }}; static const u256 P256_R = {{ 0x00000001u, 0x00000000u, Loading Loading @@ -115,6 +116,20 @@ void u256_to_be(uint8_t out32[32], const u256& a) { } } u256 bytes_be_to_u256(const uint8_t in[32]) { u256 r; for (int i = 0; i < 8; i++) { int o = 28 - 4*i; // ✅ same mapping as u256_to_be r.w[i] = ((uint32_t)in[o+0] << 24) | ((uint32_t)in[o+1] << 16) | ((uint32_t)in[o+2] << 8) | ((uint32_t)in[o+3] << 0); } return r; } // ============================================================================ // 256x256 -> 512 multiply (schoolbook, correct carry), NO __int128 // ============================================================================ Loading Loading @@ -167,7 +182,6 @@ static inline u512 u512_from_be64(const uint8_t be64[64]) { // ============================================================================ // Montgomery REDC for P-256 (8 limbs, 32-bit, no __int128) // This version is stable and matches classic HAC REDC. // ============================================================================ u256 mont_mul(const u256& a, const u256& b) { uint32_t t[16] = {0}; uint32_t extra = 0; // overflow beyond t[15] Loading Loading @@ -246,7 +260,6 @@ u256 mont_mul(const u256& a, const u256& b) { return r; } // ---------------------------------------------------------------------------- // to/from Montgomery // ---------------------------------------------------------------------------- Loading @@ -254,9 +267,9 @@ u256 to_mont(const u256& a) { return mont_mul(a, P256_R2); } u256 from_mont(const u256& a) { u256 from_mont(const u256& aR) { u256 one; u256_zero(one); one.w[0]=1; return mont_mul(a, one); return mont_mul(aR, one); // NUR wenn mont_mul als REDC(t) implementiert ist } // ---------------------------------------------------------------------------- Loading Loading @@ -294,8 +307,9 @@ u256 fp_sqr(const u256& a) { // bit access // ---------------------------------------------------------------------------- uint32_t get_bit_be(const uint8_t priv[32], int bit_index) { int byte = bit_index / 8; int bit = 7 - (bit_index % 8); // i=0 -> MSB of priv[0] int byte = bit_index >> 3; int bit = 7 - (bit_index & 7); return (priv[byte] >> bit) & 1; } Loading
src/crypto/ecc_u256.h +20 −1 Original line number Diff line number Diff line Loading @@ -13,6 +13,25 @@ extern const u256 P256_R2; // -------- low-level helpers -------- void u256_zero(u256& a); bool u256_is_zero(const u256& a); static inline void u256_one(u256& a) { u256_zero(a); a.w[0] = 1; } static inline void cswap_u256(u256& A, u256& B, uint32_t swap) { // swap is 0 or 1 uint32_t mask = 0u - swap; // 0x00000000 or 0xFFFFFFFF for (int i = 0; i < 8; i++) { uint32_t t = (A.w[i] ^ B.w[i]) & mask; A.w[i] ^= t; B.w[i] ^= t; } } int u256_cmp(const u256& a, const u256& b); // raw ops (no mod reduction) Loading @@ -23,7 +42,7 @@ u256 u256_sub_raw(const u256& a, const u256& b, uint32_t& borrow); u256 u256_from_be(const uint8_t be32[32]); void u256_to_be(uint8_t out32[32], const u256& a); u512 u512_from_be(const uint8_t in[64]); u256 bytes_be_to_u256(const uint8_t in[32]); // multiply u512 u256_mul_raw(const u256& a, const u256& b); Loading
src/socket.h +4 −0 Original line number Diff line number Diff line Loading @@ -477,6 +477,7 @@ namespace netplus { void _tls13_transcript_add_raw(const std::vector<uint8_t>& hsmsg); std::vector<uint8_t> _tls13_build_server_finished_body(); std::vector<uint8_t> _tls13_build_server_finished(); bool _handshakeStarted = false; Loading Loading @@ -532,6 +533,9 @@ namespace netplus { std::vector<uint8_t> _client_session_id; bool _client_offered_tls13 = false; bool _encflight_queued = false; bool _compat_ccs_sent = false; bool _client_has_tls13_keyshare_x25519=false; // --- TLS 1.3 (minimal) --- bool _is_tls13 = false; Loading