StreamSec Home  Forum Home 
 
Welcome, Guest.
Your IP: 216.73.216.150
2026-09-26 00:22:50 
 Announcements
 StreamSec Tools 4.0.1.324 - 4.1.1.354 - manual fix P02 Elliptic-curve multiplication loses memory, on every TLS handshake over X25519 or X448.
Bottom
 
Total posts: 1
 Author StreamSec Tools 4.0.1.324 - 4.1.1.354 - manual fix P02 Elliptic-curve multiplication loses memory, on every TLS handshake over X25519 or X448.
Henrick Wibell Hellström

2026-09-25 23:19:46
Registered user
WHAT GOES WRONG
The elliptic-curve scalar multiplication, GroupOpIteration (and, from
4.1.0.348, FixedGroupOpIteration), returns its result through an untyped var
parameter and stores it there with Supports. An untyped parameter is invisible
to reference counting, so whatever the caller's variable held is overwritten
without being released: that point, and the curve objects it keeps alive, are
never freed.

The TLS key exchange passes exactly such a variable on X25519 and X448: the
decoded key share of the other side, which it then multiplies. It does so in
TLS 1.2 from 4.0.1.340, at the client and at the server, and in TLS 1.3 from
4.1.0.348, where a server does it while processing the ClientHello. So every
TLS handshake over X25519 or X448 loses memory at each end - measured on Win64
at 760 bytes for X25519 and 968 bytes for X448 - and none of it is ever given
back. X25519 and X448 are used by default: from 4.0.1.346 they are in the
default SupportedGroups, and before that a server left at its default
EphemeralECDHKeySize (ecsAuto) chooses X25519 or X448 by itself for every
ECDHE cipher suite but the 3DES and RC4 ones. Any client, without
credentials, can therefore make a server lose memory with every connection it
opens, until the process runs out of memory.

The same defect is in the multiplication for the NIST prime curves (from
4.0.1.324) and for Ed25519 and Ed448 (from 4.1.0.348). The TLS key exchange
does not reach it on those curves; code that multiplies into a variable that
already holds a point does, and the library's MQV key agreement does so on
every call. Nothing is corrupted and no key material is exposed: the effect is
memory that grows without bound.

WHAT THE FIX DOES
One line in each affected method releases what the caller left in the
destination before the result is stored there. The line comes after the new
point has been computed, so a call whose destination also holds the point
being multiplied - the TLS key exchange makes such calls - still works. This
is the change 4.1.2.355 made.

WHO NEEDS IT
Every application built with 4.0.1.340 through 4.1.1.354 that uses TLS, on
any platform. Servers matter most, since any client can make one lose memory.
With 4.0.1.324 through 4.0.1.339, which have no X25519 or X448, applications
that use MQV key agreement, or that call the elliptic-curve arithmetic
directly with a destination that already holds a point.

APPLIES TO
StreamSec Tools 4.0.1.324 through 4.1.1.354. The methods are the same in
every release that has them:
  StreamSec.DSI.ECArith.pas      4.0.1.324 - 4.1.1.354   one place up to
                                 4.0.1.347, two from 4.1.0.348
  StreamSec.DSI.CurveXArith.pas  4.0.1.340 - 4.1.1.354   two places
  StreamSec.DSI.CurveEdArith.pas 4.1.0.348 - 4.1.1.354   two places
A release that does not have a unit needs nothing in it. 4.1.2.355 and later
carry the fix.

HOW TO APPLY
1. In StreamSec.DSI.ECArith.pas, find every line that reads exactly
       Result := Supports(lQ.ProjectiveToAffine,IInterface,aResult);
   and insert the line of PART 1 directly above it.
2. In StreamSec.DSI.CurveXArith.pas, and in StreamSec.DSI.CurveEdArith.pas if
   your release has it, find every line that reads exactly
       Supports(lResult,IInterface,aResult);
   and insert the line of PART 1 directly above it.
3. Check the count against APPLIES TO: these lines occur only in the methods
   the fix is for, once in each.
4. Rebuild the packages, and every application that uses TLS or
   elliptic-curve keys. The fix is pure Pascal; no assembler is involved.

-- PART 1: the line to insert (indented by four spaces, as the line below it) --
   IInterface(aResult) := nil;   { release what the caller left in aResult }


-- EXAMPLE: the end of tECurveX25519.GroupOpIteration after the edit ----------
   lResult := NewPoint;
    lResult.X := iDSInteger(lR);
    IInterface(aResult) := nil;   { release what the caller left in aResult }
    Supports(lResult,IInterface,aResult);
  end;
end;
Top

:: Written with and Powered by StreamSec Tools 4.1 ::
Copyright © 2000-2026 StreamSec Handelsbolag
Forum data layer and page templates derived from the RealThinClient SDK web-forum example, Copyright © 2004-2022 Teppi Technology (MIT)