Loading debian/changelog +11 −0 Original line number Diff line number Diff line libnetplus (20260528+23) unstable; urgency=medium * ecc: fix mod_n256 infinite loop in ECDSA signing — replace O(2^32) repeated subtraction with O(512) bit-by-bit long division * tls: implement ECDHE-ECDSA-AES128-GCM-SHA256 cipher suite for TLS 1.2 with Let's Encrypt ECDSA certificates * tls: fix TLS 1.3 key buffer overflow (16→32 bytes for AES-256) * tls: add GCM record encryption/decryption for TLS 1.2 -- Jan Koester <jan.koester@tuxist.de> Wed, 28 May 2026 12:00:00 +0200 libnetplus (20260521+22) unstable; urgency=medium * x509: add getNotAfter() method for certificate expiry checking Loading src/crypto/ecc_u256.cpp +27 −81 Original line number Diff line number Diff line Loading @@ -456,27 +456,15 @@ static const u256 SCALAR_N = {{ }}; // Reduction mod n (group order) - used for ECDSA scalar arithmetic // Uses schoolbook division since n doesn't have a special form like p // Bit-by-bit long division: O(512) iterations, each O(8) word ops 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 // Fast path: if high 256 bits are zero, just reduce low 256 bits bool hasHigh = false; for (int i = 8; i < 16; i++) if (t[i] != 0) { hasHigh = true; break; } for (int i = 8; i < 16; i++) if (in.w[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]; for (int i = 0; i < 8; i++) r.w[i] = in.w[i]; while (u256_cmp(r, SCALAR_N) >= 0) { uint32_t br = 0; r = u256_sub_raw(r, SCALAR_N, br); Loading @@ -484,70 +472,28 @@ u256 mod_n256(const u512& in) { 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; } } // Bit-by-bit long division from MSB (bit 511) to LSB (bit 0) u256 r = {}; for (int bit = 511; bit >= 0; bit--) { // Shift remainder left by 1 uint32_t carry = 0; for (int i = 0; i < 8; i++) { uint32_t newCarry = r.w[i] >> 31; r.w[i] = (r.w[i] << 1) | carry; carry = newCarry; } // Result is now in t[0..7] u256 r; for (int i = 0; i < 8; i++) r.w[i] = (uint32_t)t[i]; // Bring down next bit from input int wordIdx = bit / 32; int bitIdx = bit % 32; r.w[0] |= (in.w[wordIdx] >> bitIdx) & 1; // Final normalization while (u256_cmp(r, SCALAR_N) >= 0) { // If carry (value >= 2^256 > n) 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; } Loading Loading
debian/changelog +11 −0 Original line number Diff line number Diff line libnetplus (20260528+23) unstable; urgency=medium * ecc: fix mod_n256 infinite loop in ECDSA signing — replace O(2^32) repeated subtraction with O(512) bit-by-bit long division * tls: implement ECDHE-ECDSA-AES128-GCM-SHA256 cipher suite for TLS 1.2 with Let's Encrypt ECDSA certificates * tls: fix TLS 1.3 key buffer overflow (16→32 bytes for AES-256) * tls: add GCM record encryption/decryption for TLS 1.2 -- Jan Koester <jan.koester@tuxist.de> Wed, 28 May 2026 12:00:00 +0200 libnetplus (20260521+22) unstable; urgency=medium * x509: add getNotAfter() method for certificate expiry checking Loading
src/crypto/ecc_u256.cpp +27 −81 Original line number Diff line number Diff line Loading @@ -456,27 +456,15 @@ static const u256 SCALAR_N = {{ }}; // Reduction mod n (group order) - used for ECDSA scalar arithmetic // Uses schoolbook division since n doesn't have a special form like p // Bit-by-bit long division: O(512) iterations, each O(8) word ops 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 // Fast path: if high 256 bits are zero, just reduce low 256 bits bool hasHigh = false; for (int i = 8; i < 16; i++) if (t[i] != 0) { hasHigh = true; break; } for (int i = 8; i < 16; i++) if (in.w[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]; for (int i = 0; i < 8; i++) r.w[i] = in.w[i]; while (u256_cmp(r, SCALAR_N) >= 0) { uint32_t br = 0; r = u256_sub_raw(r, SCALAR_N, br); Loading @@ -484,70 +472,28 @@ u256 mod_n256(const u512& in) { 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; } } // Bit-by-bit long division from MSB (bit 511) to LSB (bit 0) u256 r = {}; for (int bit = 511; bit >= 0; bit--) { // Shift remainder left by 1 uint32_t carry = 0; for (int i = 0; i < 8; i++) { uint32_t newCarry = r.w[i] >> 31; r.w[i] = (r.w[i] << 1) | carry; carry = newCarry; } // Result is now in t[0..7] u256 r; for (int i = 0; i < 8; i++) r.w[i] = (uint32_t)t[i]; // Bring down next bit from input int wordIdx = bit / 32; int bitIdx = bit % 32; r.w[0] |= (in.w[wordIdx] >> bitIdx) & 1; // Final normalization while (u256_cmp(r, SCALAR_N) >= 0) { // If carry (value >= 2^256 > n) 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; } Loading