|
| 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; |