Loading src/crypto/ecc_u256.cpp +164 −0 Original line number Diff line number Diff line Loading @@ -440,4 +440,168 @@ u256 fp_inv(const u256& a_mont) { return fp_pow(a_mont, exp); } // ============================================================================ // P-256 group order n for scalar field arithmetic (ECDSA) // n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 // ============================================================================ static const u256 SCALAR_N = {{ 0xFC632551, // w[0] LSW 0xF3B9CAC2, 0xA7179E84, 0xBCE6FAAD, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF // w[7] MSW }}; // Reduction mod n (group order) - used for ECDSA scalar arithmetic // Uses schoolbook division since n doesn't have a special form like p u256 mod_n256(const u512& in) { // For a 512-bit value, we do trial subtraction of n*2^i for i from 256 down to 0 // This is slow but correct. For production, use Barrett reduction. // Work with extended precision uint64_t t[16]; for (int i = 0; i < 16; i++) t[i] = in.w[i]; // Reduce: while value >= n * 2^256, subtract n * 2^256 // Then while value >= n * 2^224, subtract n * 2^224, etc. // Simple approach: convert to u256, handle overflow with subtraction // First, check if high 256 bits are non-zero bool hasHigh = false; for (int i = 8; i < 16; i++) if (t[i] != 0) { hasHigh = true; break; } if (!hasHigh) { // Just low 256 bits - simple reduction u256 r; for (int i = 0; i < 8; i++) r.w[i] = (uint32_t)t[i]; while (u256_cmp(r, SCALAR_N) >= 0) { uint32_t br = 0; r = u256_sub_raw(r, SCALAR_N, br); } return r; } // Full 512-bit reduction using repeated subtraction // This is O(n) in the worst case but works for random inputs // Compute q ≈ floor(in / n) using high bits estimation // Then r = in - q*n and adjust // For simplicity, use a loop that processes word by word from high to low // Each iteration reduces the value by subtracting n shifted appropriately for (int shift = 256; shift >= 0; shift -= 32) { // Check if we can subtract n << shift int wordShift = shift / 32; while (true) { // Check if t >= n << shift bool canSubtract = false; // Compare t with n << wordShift int topWord = wordShift + 7; if (topWord < 16) { // Check from top bool greater = false; bool equal = true; for (int i = 15; i >= 0; i--) { uint64_t nShifted = 0; int ni = i - wordShift; if (ni >= 0 && ni < 8) nShifted = SCALAR_N.w[ni]; if (t[i] > nShifted) { greater = true; equal = false; break; } if (t[i] < nShifted) { greater = false; equal = false; break; } } canSubtract = greater || equal; } if (!canSubtract) break; // Subtract n << wordShift from t int64_t borrow = 0; for (int i = 0; i < 16; i++) { uint64_t nShifted = 0; int ni = i - wordShift; if (ni >= 0 && ni < 8) nShifted = SCALAR_N.w[ni]; int64_t diff = (int64_t)t[i] - (int64_t)nShifted - borrow; if (diff < 0) { diff += 0x100000000LL; borrow = 1; } else { borrow = 0; } t[i] = (uint64_t)diff; } } } // Result is now in t[0..7] u256 r; for (int i = 0; i < 8; i++) r.w[i] = (uint32_t)t[i]; // Final normalization while (u256_cmp(r, SCALAR_N) >= 0) { uint32_t br = 0; r = u256_sub_raw(r, SCALAR_N, br); } return r; } // Scalar field multiplication: (a * b) mod n u256 scalar_mul_mod_n(const u256& a, const u256& b) { u512 prod = u256_mul_raw(a, b); return mod_n256(prod); } // Scalar field addition: (a + b) mod n u256 scalar_add_mod_n(const u256& a, const u256& b) { uint32_t carry = 0; u256 r = u256_add_raw(a, b, carry); // If carry or r >= n, subtract n if (carry || u256_cmp(r, SCALAR_N) >= 0) { uint32_t br = 0; r = u256_sub_raw(r, SCALAR_N, br); } return r; } // Scalar field inversion using Fermat's little theorem: a^-1 = a^(n-2) mod n u256 scalar_inv_mod_n(const u256& a) { // n-2 for SCALAR_N static const u256 N_MINUS_2 = {{ 0xFC63254F, // w[0] = 0xFC632551 - 2 0xF3B9CAC2, 0xA7179E84, 0xBCE6FAAD, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF }}; // Square-and-multiply: result = a^(n-2) mod n u256 result; u256_zero(result); result.w[0] = 1; u256 base = a; for (int i = 0; i < 8; i++) { for (int bit = 0; bit < 32; bit++) { if ((N_MINUS_2.w[i] >> bit) & 1) { result = scalar_mul_mod_n(result, base); } base = scalar_mul_mod_n(base, base); } } return result; } } // namespace netplus src/crypto/ecc_u256.h +6 −0 Original line number Diff line number Diff line Loading @@ -66,4 +66,10 @@ uint32_t get_bit_be(const uint8_t priv[32], int bit_index); // reduction (512 -> 256 mod P) u256 mod_p256(const u512& in); // scalar field arithmetic (mod n, group order) for ECDSA u256 mod_n256(const u512& in); u256 scalar_mul_mod_n(const u256& a, const u256& b); u256 scalar_add_mod_n(const u256& a, const u256& b); u256 scalar_inv_mod_n(const u256& a); } // namespace netplus src/ssl.cpp +88 −89 Original line number Diff line number Diff line Loading @@ -3862,20 +3862,35 @@ bool netplus::ssl::loadServerPrivateKeyDer(const std::string& keyDerPath) { root.children[2].data != nullptr && root.children[2].len > 0); std::cerr << "[SSL] loadServerPrivateKeyDer: looksPkcs8=" << looksPkcs8 << " children=" << root.children.size() << std::endl; bool ok = false; // Check if this is an EC key (PKCS#8 with ecPublicKey OID) bool isEcKey = false; if (looksPkcs8 && root.children.size() >= 2 && root.children[1].tag == 0x30) { const auto& algId = root.children[1]; std::cerr << "[SSL] algId.children.size()=" << algId.children.size() << std::endl; if (algId.children.size() >= 1 && algId.children[0].tag == 0x06) { // Check algorithm OID const auto& oid = algId.children[0]; std::cerr << "[SSL] Algorithm OID len=" << oid.len << " bytes: "; for (size_t i = 0; i < oid.len && i < 16; i++) { std::cerr << std::hex << (int)oid.data[i] << " "; } std::cerr << std::dec << std::endl; isEcKey = isOidEcPublicKey(oid.data, oid.len); std::cerr << "[SSL] isEcKey after OID check: " << isEcKey << std::endl; // Also check for P-256 curve OID in parameters if (isEcKey && algId.children.size() >= 2 && algId.children[1].tag == 0x06) { const auto& curveOid = algId.children[1]; std::cerr << "[SSL] Curve OID len=" << curveOid.len << " bytes: "; for (size_t i = 0; i < curveOid.len && i < 16; i++) { std::cerr << std::hex << (int)curveOid.data[i] << " "; } std::cerr << std::dec << std::endl; if (!isOidP256(curveOid.data, curveOid.len)) { // Not P-256, we only support P-256 for now isEcKey = false; Loading Loading @@ -4302,141 +4317,125 @@ std::vector<uint8_t> netplus::ssl::_rsa_pss_sha256_sign(const std::vector<uint8_ } // ECDSA-SHA256 signature using P-256 curve // Now uses proper scalar field arithmetic from ecc_u256.cpp std::vector<uint8_t> netplus::ssl::_ecdsa_sha256_sign(const std::vector<uint8_t>& in){ if (!_has_ec_key) throwSSL(NetException::Error, "TLS1.3: no EC private key"); std::cerr << "[ECDSA] Starting signature generation" << std::endl; // 1. Hash the message with SHA-256 std::vector<uint8_t> hash = sha256_hash(in); // 2. Generate ephemeral k (must be random per RFC 6979, simplified here) uint8_t k[32]; fillRandom(std::vector<uint8_t>(k, k+32)); // Ensure k is in valid range [1, n-1] // 2. Generate ephemeral k (must be random, in range [1, n-1]) uint8_t k_bytes[32]; u256 k_val; for (int retry = 0; retry < 100; ++retry) { fillRandom(std::vector<uint8_t>(k, k+32)); // Check k < n and k != 0 bool kZero = true; for (int i = 0; i < 32; ++i) if (k[i] != 0) { kZero = false; break; } if (kZero) continue; if (u256_cmp(*(const u256*)k, P256_N) < 0) break; std::vector<uint8_t> rnd(32); fillRandom(rnd); std::memcpy(k_bytes, rnd.data(), 32); // Convert to u256 using the proper conversion function k_val = u256_from_be(k_bytes); // Check k != 0 and k < n if (u256_is_zero(k_val)) continue; if (u256_cmp(k_val, P256_N) < 0) break; } // 3. Compute R = k*G, get r = R.x mod n P256Point R = scalar_mul_G(k); std::cerr << "[ECDSA] Generated k" << std::endl; // 3. Compute R = k*G P256Point R = scalar_mul_G(k_bytes); if (R.inf) throwSSL(NetException::Error, "ECDSA: R at infinity"); // Convert R.x to big-endian bytes std::cerr << "[ECDSA] Computed R = k*G" << std::endl; // 4. Get r = R.x mod n std::vector<uint8_t> rx_be = encode_tls_point(R); // rx_be is 65 bytes: 04 || X(32) || Y(32), extract X std::vector<uint8_t> r_bytes(rx_be.begin() + 1, rx_be.begin() + 33); // 4. Compute s = k^-1 * (hash + r * privkey) mod n // This requires modular arithmetic in the scalar field // For simplicity, we use a direct bignum approach uint8_t r_bytes[32]; std::memcpy(r_bytes, rx_be.data() + 1, 32); // Convert values to u256 for arithmetic u256 r_val, s_val, k_val, d_val, z_val; std::memset(&r_val, 0, sizeof(r_val)); std::memset(&k_val, 0, sizeof(k_val)); std::memset(&d_val, 0, sizeof(d_val)); std::memset(&z_val, 0, sizeof(z_val)); u256 r_val = u256_from_be(r_bytes); // Load big-endian bytes into u256 (little-endian words) for (int i = 0; i < 32; ++i) { ((uint8_t*)&r_val)[31-i] = r_bytes[i]; ((uint8_t*)&k_val)[31-i] = k[i]; ((uint8_t*)&d_val)[31-i] = _ec_priv[i]; ((uint8_t*)&z_val)[31-i] = hash[i]; } // r = r mod n (R.x is already < p, but we need mod n) // Reduce r mod n (R.x is < p, but we need mod n which is slightly smaller) while (u256_cmp(r_val, P256_N) >= 0) { u256 tmp; u256_sub(tmp, r_val, P256_N); r_val = tmp; uint32_t br = 0; r_val = u256_sub_raw(r_val, P256_N, br); } // Compute s = k^-1 * (z + r*d) mod n // We need modular inverse and multiplication in the scalar field // Using extended Euclidean algorithm for k^-1 mod n // Check r != 0 if (u256_is_zero(r_val)) throwSSL(NetException::Error, "ECDSA: r = 0"); // For now, use a simplified approach with the existing fp_* functions // adapted for the scalar field n instead of prime p std::cerr << "[ECDSA] r computed" << std::endl; // s = (z + r*d) * k^-1 mod n // This is complex to implement correctly. Let's use a workaround: // Export to external bignum or implement scalar field arithmetic. // 5. Convert hash (z) and private key (d) to u256 u256 z_val = u256_from_be(hash.data()); u256 d_val = u256_from_be(_ec_priv); // Simplified: just produce a valid DER-encoded signature structure // with r and placeholder s (THIS IS NOT CRYPTOGRAPHICALLY CORRECT) // We need proper scalar field arithmetic. // 6. Compute s = k^-1 * (z + r*d) mod n using proper scalar field arithmetic // Actually, let's compute it properly using the existing u256 operations // adapted for mod n arithmetic // First: rd = r * d mod n u256 rd = scalar_mul_mod_n(r_val, d_val); // Compute r*d mod n u512 rd_wide; u256_mul_wide(rd_wide, r_val, d_val); u256 rd_mod; // Reduce mod n - this needs Barrett reduction or similar // For now, do repeated subtraction (slow but correct for small values) std::memcpy(&rd_mod, &rd_wide, sizeof(u256)); // Take low 256 bits // This is incorrect for values >= n, but a proper implementation would use Barrett while (u256_cmp(rd_mod, P256_N) >= 0) { u256 tmp; u256_sub(tmp, rd_mod, P256_N); rd_mod = tmp; } // Second: sum = z + rd mod n u256 sum = scalar_add_mod_n(z_val, rd); // Compute z + rd mod n u256 sum; u256_add(sum, z_val, rd_mod); while (u256_cmp(sum, P256_N) >= 0) { u256 tmp; u256_sub(tmp, sum, P256_N); sum = tmp; } // Third: k_inv = k^-1 mod n u256 k_inv = scalar_inv_mod_n(k_val); // Compute k^-1 mod n using Fermat's little theorem: k^-1 = k^(n-2) mod n // This requires modular exponentiation which is expensive // For production, use extended Euclidean algorithm std::cerr << "[ECDSA] Computed k^-1" << std::endl; // Placeholder: set s = sum (incorrect, but will be fixed) s_val = sum; // Fourth: s = k_inv * sum mod n u256 s_val = scalar_mul_mod_n(k_inv, sum); // Convert r and s back to big-endian bytes std::vector<uint8_t> s_bytes(32); for (int i = 0; i < 32; ++i) { s_bytes[i] = ((uint8_t*)&s_val)[31-i]; } // Check s != 0 if (u256_is_zero(s_val)) throwSSL(NetException::Error, "ECDSA: s = 0"); // Encode as DER: SEQUENCE { INTEGER r, INTEGER s } auto encodeInteger = [](const std::vector<uint8_t>& val) -> std::vector<uint8_t> { std::cerr << "[ECDSA] s computed" << std::endl; // 7. Convert r and s back to big-endian bytes uint8_t r_be[32], s_be[32]; u256_to_be(r_be, r_val); u256_to_be(s_be, s_val); // 8. Encode as DER: SEQUENCE { INTEGER r, INTEGER s } auto encodeInteger = [](const uint8_t* val, size_t len) -> std::vector<uint8_t> { std::vector<uint8_t> out; out.push_back(0x02); // INTEGER tag // Skip leading zeros but keep one if high bit set size_t start = 0; while (start < val.size() - 1 && val[start] == 0) ++start; while (start < len - 1 && val[start] == 0) ++start; bool needPad = (val[start] & 0x80) != 0; size_t len = val.size() - start + (needPad ? 1 : 0); size_t outLen = len - start + (needPad ? 1 : 0); out.push_back((uint8_t)len); out.push_back((uint8_t)outLen); if (needPad) out.push_back(0x00); out.insert(out.end(), val.begin() + start, val.end()); out.insert(out.end(), val + start, val + len); return out; }; std::vector<uint8_t> r_der = encodeInteger(r_bytes); std::vector<uint8_t> s_der = encodeInteger(s_bytes); std::vector<uint8_t> r_der = encodeInteger(r_be, 32); std::vector<uint8_t> s_der = encodeInteger(s_be, 32); std::vector<uint8_t> sig; sig.push_back(0x30); // SEQUENCE tag sig.push_back((uint8_t)(r_der.size() + s_der.size())); size_t inner_len = r_der.size() + s_der.size(); if (inner_len >= 128) { sig.push_back(0x81); sig.push_back((uint8_t)inner_len); } else { sig.push_back((uint8_t)inner_len); } sig.insert(sig.end(), r_der.begin(), r_der.end()); sig.insert(sig.end(), s_der.begin(), s_der.end()); std::cerr << "[ECDSA] Signature generated, size=" << sig.size() << std::endl; return sig; } Loading Loading
src/crypto/ecc_u256.cpp +164 −0 Original line number Diff line number Diff line Loading @@ -440,4 +440,168 @@ u256 fp_inv(const u256& a_mont) { return fp_pow(a_mont, exp); } // ============================================================================ // P-256 group order n for scalar field arithmetic (ECDSA) // n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 // ============================================================================ static const u256 SCALAR_N = {{ 0xFC632551, // w[0] LSW 0xF3B9CAC2, 0xA7179E84, 0xBCE6FAAD, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF // w[7] MSW }}; // Reduction mod n (group order) - used for ECDSA scalar arithmetic // Uses schoolbook division since n doesn't have a special form like p u256 mod_n256(const u512& in) { // For a 512-bit value, we do trial subtraction of n*2^i for i from 256 down to 0 // This is slow but correct. For production, use Barrett reduction. // Work with extended precision uint64_t t[16]; for (int i = 0; i < 16; i++) t[i] = in.w[i]; // Reduce: while value >= n * 2^256, subtract n * 2^256 // Then while value >= n * 2^224, subtract n * 2^224, etc. // Simple approach: convert to u256, handle overflow with subtraction // First, check if high 256 bits are non-zero bool hasHigh = false; for (int i = 8; i < 16; i++) if (t[i] != 0) { hasHigh = true; break; } if (!hasHigh) { // Just low 256 bits - simple reduction u256 r; for (int i = 0; i < 8; i++) r.w[i] = (uint32_t)t[i]; while (u256_cmp(r, SCALAR_N) >= 0) { uint32_t br = 0; r = u256_sub_raw(r, SCALAR_N, br); } return r; } // Full 512-bit reduction using repeated subtraction // This is O(n) in the worst case but works for random inputs // Compute q ≈ floor(in / n) using high bits estimation // Then r = in - q*n and adjust // For simplicity, use a loop that processes word by word from high to low // Each iteration reduces the value by subtracting n shifted appropriately for (int shift = 256; shift >= 0; shift -= 32) { // Check if we can subtract n << shift int wordShift = shift / 32; while (true) { // Check if t >= n << shift bool canSubtract = false; // Compare t with n << wordShift int topWord = wordShift + 7; if (topWord < 16) { // Check from top bool greater = false; bool equal = true; for (int i = 15; i >= 0; i--) { uint64_t nShifted = 0; int ni = i - wordShift; if (ni >= 0 && ni < 8) nShifted = SCALAR_N.w[ni]; if (t[i] > nShifted) { greater = true; equal = false; break; } if (t[i] < nShifted) { greater = false; equal = false; break; } } canSubtract = greater || equal; } if (!canSubtract) break; // Subtract n << wordShift from t int64_t borrow = 0; for (int i = 0; i < 16; i++) { uint64_t nShifted = 0; int ni = i - wordShift; if (ni >= 0 && ni < 8) nShifted = SCALAR_N.w[ni]; int64_t diff = (int64_t)t[i] - (int64_t)nShifted - borrow; if (diff < 0) { diff += 0x100000000LL; borrow = 1; } else { borrow = 0; } t[i] = (uint64_t)diff; } } } // Result is now in t[0..7] u256 r; for (int i = 0; i < 8; i++) r.w[i] = (uint32_t)t[i]; // Final normalization while (u256_cmp(r, SCALAR_N) >= 0) { uint32_t br = 0; r = u256_sub_raw(r, SCALAR_N, br); } return r; } // Scalar field multiplication: (a * b) mod n u256 scalar_mul_mod_n(const u256& a, const u256& b) { u512 prod = u256_mul_raw(a, b); return mod_n256(prod); } // Scalar field addition: (a + b) mod n u256 scalar_add_mod_n(const u256& a, const u256& b) { uint32_t carry = 0; u256 r = u256_add_raw(a, b, carry); // If carry or r >= n, subtract n if (carry || u256_cmp(r, SCALAR_N) >= 0) { uint32_t br = 0; r = u256_sub_raw(r, SCALAR_N, br); } return r; } // Scalar field inversion using Fermat's little theorem: a^-1 = a^(n-2) mod n u256 scalar_inv_mod_n(const u256& a) { // n-2 for SCALAR_N static const u256 N_MINUS_2 = {{ 0xFC63254F, // w[0] = 0xFC632551 - 2 0xF3B9CAC2, 0xA7179E84, 0xBCE6FAAD, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF }}; // Square-and-multiply: result = a^(n-2) mod n u256 result; u256_zero(result); result.w[0] = 1; u256 base = a; for (int i = 0; i < 8; i++) { for (int bit = 0; bit < 32; bit++) { if ((N_MINUS_2.w[i] >> bit) & 1) { result = scalar_mul_mod_n(result, base); } base = scalar_mul_mod_n(base, base); } } return result; } } // namespace netplus
src/crypto/ecc_u256.h +6 −0 Original line number Diff line number Diff line Loading @@ -66,4 +66,10 @@ uint32_t get_bit_be(const uint8_t priv[32], int bit_index); // reduction (512 -> 256 mod P) u256 mod_p256(const u512& in); // scalar field arithmetic (mod n, group order) for ECDSA u256 mod_n256(const u512& in); u256 scalar_mul_mod_n(const u256& a, const u256& b); u256 scalar_add_mod_n(const u256& a, const u256& b); u256 scalar_inv_mod_n(const u256& a); } // namespace netplus
src/ssl.cpp +88 −89 Original line number Diff line number Diff line Loading @@ -3862,20 +3862,35 @@ bool netplus::ssl::loadServerPrivateKeyDer(const std::string& keyDerPath) { root.children[2].data != nullptr && root.children[2].len > 0); std::cerr << "[SSL] loadServerPrivateKeyDer: looksPkcs8=" << looksPkcs8 << " children=" << root.children.size() << std::endl; bool ok = false; // Check if this is an EC key (PKCS#8 with ecPublicKey OID) bool isEcKey = false; if (looksPkcs8 && root.children.size() >= 2 && root.children[1].tag == 0x30) { const auto& algId = root.children[1]; std::cerr << "[SSL] algId.children.size()=" << algId.children.size() << std::endl; if (algId.children.size() >= 1 && algId.children[0].tag == 0x06) { // Check algorithm OID const auto& oid = algId.children[0]; std::cerr << "[SSL] Algorithm OID len=" << oid.len << " bytes: "; for (size_t i = 0; i < oid.len && i < 16; i++) { std::cerr << std::hex << (int)oid.data[i] << " "; } std::cerr << std::dec << std::endl; isEcKey = isOidEcPublicKey(oid.data, oid.len); std::cerr << "[SSL] isEcKey after OID check: " << isEcKey << std::endl; // Also check for P-256 curve OID in parameters if (isEcKey && algId.children.size() >= 2 && algId.children[1].tag == 0x06) { const auto& curveOid = algId.children[1]; std::cerr << "[SSL] Curve OID len=" << curveOid.len << " bytes: "; for (size_t i = 0; i < curveOid.len && i < 16; i++) { std::cerr << std::hex << (int)curveOid.data[i] << " "; } std::cerr << std::dec << std::endl; if (!isOidP256(curveOid.data, curveOid.len)) { // Not P-256, we only support P-256 for now isEcKey = false; Loading Loading @@ -4302,141 +4317,125 @@ std::vector<uint8_t> netplus::ssl::_rsa_pss_sha256_sign(const std::vector<uint8_ } // ECDSA-SHA256 signature using P-256 curve // Now uses proper scalar field arithmetic from ecc_u256.cpp std::vector<uint8_t> netplus::ssl::_ecdsa_sha256_sign(const std::vector<uint8_t>& in){ if (!_has_ec_key) throwSSL(NetException::Error, "TLS1.3: no EC private key"); std::cerr << "[ECDSA] Starting signature generation" << std::endl; // 1. Hash the message with SHA-256 std::vector<uint8_t> hash = sha256_hash(in); // 2. Generate ephemeral k (must be random per RFC 6979, simplified here) uint8_t k[32]; fillRandom(std::vector<uint8_t>(k, k+32)); // Ensure k is in valid range [1, n-1] // 2. Generate ephemeral k (must be random, in range [1, n-1]) uint8_t k_bytes[32]; u256 k_val; for (int retry = 0; retry < 100; ++retry) { fillRandom(std::vector<uint8_t>(k, k+32)); // Check k < n and k != 0 bool kZero = true; for (int i = 0; i < 32; ++i) if (k[i] != 0) { kZero = false; break; } if (kZero) continue; if (u256_cmp(*(const u256*)k, P256_N) < 0) break; std::vector<uint8_t> rnd(32); fillRandom(rnd); std::memcpy(k_bytes, rnd.data(), 32); // Convert to u256 using the proper conversion function k_val = u256_from_be(k_bytes); // Check k != 0 and k < n if (u256_is_zero(k_val)) continue; if (u256_cmp(k_val, P256_N) < 0) break; } // 3. Compute R = k*G, get r = R.x mod n P256Point R = scalar_mul_G(k); std::cerr << "[ECDSA] Generated k" << std::endl; // 3. Compute R = k*G P256Point R = scalar_mul_G(k_bytes); if (R.inf) throwSSL(NetException::Error, "ECDSA: R at infinity"); // Convert R.x to big-endian bytes std::cerr << "[ECDSA] Computed R = k*G" << std::endl; // 4. Get r = R.x mod n std::vector<uint8_t> rx_be = encode_tls_point(R); // rx_be is 65 bytes: 04 || X(32) || Y(32), extract X std::vector<uint8_t> r_bytes(rx_be.begin() + 1, rx_be.begin() + 33); // 4. Compute s = k^-1 * (hash + r * privkey) mod n // This requires modular arithmetic in the scalar field // For simplicity, we use a direct bignum approach uint8_t r_bytes[32]; std::memcpy(r_bytes, rx_be.data() + 1, 32); // Convert values to u256 for arithmetic u256 r_val, s_val, k_val, d_val, z_val; std::memset(&r_val, 0, sizeof(r_val)); std::memset(&k_val, 0, sizeof(k_val)); std::memset(&d_val, 0, sizeof(d_val)); std::memset(&z_val, 0, sizeof(z_val)); u256 r_val = u256_from_be(r_bytes); // Load big-endian bytes into u256 (little-endian words) for (int i = 0; i < 32; ++i) { ((uint8_t*)&r_val)[31-i] = r_bytes[i]; ((uint8_t*)&k_val)[31-i] = k[i]; ((uint8_t*)&d_val)[31-i] = _ec_priv[i]; ((uint8_t*)&z_val)[31-i] = hash[i]; } // r = r mod n (R.x is already < p, but we need mod n) // Reduce r mod n (R.x is < p, but we need mod n which is slightly smaller) while (u256_cmp(r_val, P256_N) >= 0) { u256 tmp; u256_sub(tmp, r_val, P256_N); r_val = tmp; uint32_t br = 0; r_val = u256_sub_raw(r_val, P256_N, br); } // Compute s = k^-1 * (z + r*d) mod n // We need modular inverse and multiplication in the scalar field // Using extended Euclidean algorithm for k^-1 mod n // Check r != 0 if (u256_is_zero(r_val)) throwSSL(NetException::Error, "ECDSA: r = 0"); // For now, use a simplified approach with the existing fp_* functions // adapted for the scalar field n instead of prime p std::cerr << "[ECDSA] r computed" << std::endl; // s = (z + r*d) * k^-1 mod n // This is complex to implement correctly. Let's use a workaround: // Export to external bignum or implement scalar field arithmetic. // 5. Convert hash (z) and private key (d) to u256 u256 z_val = u256_from_be(hash.data()); u256 d_val = u256_from_be(_ec_priv); // Simplified: just produce a valid DER-encoded signature structure // with r and placeholder s (THIS IS NOT CRYPTOGRAPHICALLY CORRECT) // We need proper scalar field arithmetic. // 6. Compute s = k^-1 * (z + r*d) mod n using proper scalar field arithmetic // Actually, let's compute it properly using the existing u256 operations // adapted for mod n arithmetic // First: rd = r * d mod n u256 rd = scalar_mul_mod_n(r_val, d_val); // Compute r*d mod n u512 rd_wide; u256_mul_wide(rd_wide, r_val, d_val); u256 rd_mod; // Reduce mod n - this needs Barrett reduction or similar // For now, do repeated subtraction (slow but correct for small values) std::memcpy(&rd_mod, &rd_wide, sizeof(u256)); // Take low 256 bits // This is incorrect for values >= n, but a proper implementation would use Barrett while (u256_cmp(rd_mod, P256_N) >= 0) { u256 tmp; u256_sub(tmp, rd_mod, P256_N); rd_mod = tmp; } // Second: sum = z + rd mod n u256 sum = scalar_add_mod_n(z_val, rd); // Compute z + rd mod n u256 sum; u256_add(sum, z_val, rd_mod); while (u256_cmp(sum, P256_N) >= 0) { u256 tmp; u256_sub(tmp, sum, P256_N); sum = tmp; } // Third: k_inv = k^-1 mod n u256 k_inv = scalar_inv_mod_n(k_val); // Compute k^-1 mod n using Fermat's little theorem: k^-1 = k^(n-2) mod n // This requires modular exponentiation which is expensive // For production, use extended Euclidean algorithm std::cerr << "[ECDSA] Computed k^-1" << std::endl; // Placeholder: set s = sum (incorrect, but will be fixed) s_val = sum; // Fourth: s = k_inv * sum mod n u256 s_val = scalar_mul_mod_n(k_inv, sum); // Convert r and s back to big-endian bytes std::vector<uint8_t> s_bytes(32); for (int i = 0; i < 32; ++i) { s_bytes[i] = ((uint8_t*)&s_val)[31-i]; } // Check s != 0 if (u256_is_zero(s_val)) throwSSL(NetException::Error, "ECDSA: s = 0"); // Encode as DER: SEQUENCE { INTEGER r, INTEGER s } auto encodeInteger = [](const std::vector<uint8_t>& val) -> std::vector<uint8_t> { std::cerr << "[ECDSA] s computed" << std::endl; // 7. Convert r and s back to big-endian bytes uint8_t r_be[32], s_be[32]; u256_to_be(r_be, r_val); u256_to_be(s_be, s_val); // 8. Encode as DER: SEQUENCE { INTEGER r, INTEGER s } auto encodeInteger = [](const uint8_t* val, size_t len) -> std::vector<uint8_t> { std::vector<uint8_t> out; out.push_back(0x02); // INTEGER tag // Skip leading zeros but keep one if high bit set size_t start = 0; while (start < val.size() - 1 && val[start] == 0) ++start; while (start < len - 1 && val[start] == 0) ++start; bool needPad = (val[start] & 0x80) != 0; size_t len = val.size() - start + (needPad ? 1 : 0); size_t outLen = len - start + (needPad ? 1 : 0); out.push_back((uint8_t)len); out.push_back((uint8_t)outLen); if (needPad) out.push_back(0x00); out.insert(out.end(), val.begin() + start, val.end()); out.insert(out.end(), val + start, val + len); return out; }; std::vector<uint8_t> r_der = encodeInteger(r_bytes); std::vector<uint8_t> s_der = encodeInteger(s_bytes); std::vector<uint8_t> r_der = encodeInteger(r_be, 32); std::vector<uint8_t> s_der = encodeInteger(s_be, 32); std::vector<uint8_t> sig; sig.push_back(0x30); // SEQUENCE tag sig.push_back((uint8_t)(r_der.size() + s_der.size())); size_t inner_len = r_der.size() + s_der.size(); if (inner_len >= 128) { sig.push_back(0x81); sig.push_back((uint8_t)inner_len); } else { sig.push_back((uint8_t)inner_len); } sig.insert(sig.end(), r_der.begin(), r_der.end()); sig.insert(sig.end(), s_der.begin(), s_der.end()); std::cerr << "[ECDSA] Signature generated, size=" << sig.size() << std::endl; return sig; } Loading