forge icon indicating copy to clipboard operation
forge copied to clipboard

Client upgrades to TLSv1.1 unexpectedly and sends plain text data

Open mass85 opened this issue 3 years ago • 2 comments

I'm implementing a system that uses forge for TLS as part of stack on top of Bluetooth. There are two parties of the communication:

  • TLS server - Forge used by node.js 10 service running on Linux device
  • TLS client - Forge used by react native mobile application on Android phone

Recently a following scenario started to occur after establishing Bluetooth connection and requesting TLS handshake from mobile app:

  • Client Hello with TLS v1.1
  • Server Hello with TLS v1.0
  • since now all records have TLS v1.0, handshake succeeds
  • application data is exchanged in a following way:
    • server calls connection.prepare() with data of 309 B length
    • client calls connection.prepare() with data of 147 B length
    • server calls connection.prepare() multiple times with some delays passing data of following lengths: 228, 196, 198, 196, 228 (however this is not super characteristic, there may be more calls to the function)
    • client calls connection.prepare() with data of 184 B length
    • in result server reports error Incompatible TLS version after receiving record with TLS v1.1

All application data gets sent as TLS v1.0 records, but the last one from client is sent as TLS v1.1 and as plain text. When problem occurs it occurs in many connections in a row until it disappears for some time. I do not see anything characteristic.

Data passed to Forge is buffer converted to binary string (buffer.toString('binary')), where buffer is of type:

  • native node Buffer in TLS Server
  • buffer package in react native TLS Client

Transport layer below TLS is reliable, no suspicions in that area. It seems that problem is in Forge library. Initially I used v0.10.0, but with v1.3.0 problem also reproduces.

mass85 avatar Mar 29 '22 12:03 mass85

I've found the reason for downgrade of TLS version by server at the beginning of TLS handshake - lib/tls.js#L1090. This loop skips first item in tls.SupportedVersions array that contains TLS v1.1, so 1.0 is always picked. I created a separate issue for this: https://github.com/digitalbazaar/forge/issues/971

This is however not a primary issue. Fix for that lets both sides of communication use TLS 1.1 from the beginning till the end, but plain text message is also sent in the last step, however server error is this time: Could not decrypt record or bad MAC what makes perfect sense.

mass85 avatar Mar 29 '22 16:03 mass85

I debugged the plain text problem and it turned out to be a bug in mobile app. It used to create a new tlsConnection handle for each connection, but stored the handle only once. In result after reconnecting appplication data from mobile app is sent using the first handle which has a reset connection. Forge has a weird behaviour in that case, as it sends the data as plain text even though there has been no handshake, connection is not ready.

I believe this Forge's behaviour is a bug on its own, a security issue. An exception should be thrown when calling connection.prepare() before handshake is successful.

mass85 avatar Mar 30 '22 16:03 mass85