RFR JDK-8240871: SSLEngine handshake status immediately after the handshake can be NOT_HANDSHAKING rather than FINISHED with TLSv1.3
Xuelei Fan
xuelei.fan at oracle.com
Thu May 28 03:44:12 UTC 2020
Tony and I had a private chat, and I understand his concerns better now.
The name of the variable TransportContext.needEmptySessionTicket is
confusing. I updated it to "needHandshakeFinishedStatus" per the
suggestion, together with other change according to the comments.
Here is the new webrev:
http://cr.openjdk.java.net/~xuelei/8240871/webrev.01/
Thanks,
Xuelei
On 5/26/2020 2:40 PM, Xuelei Fan wrote:
> On 5/26/2020 1:26 PM, Anthony Scarpino wrote:
>> On 5/13/20 1:44 PM, Xuelei Fan wrote:
>>> On 5/13/2020 9:41 AM, Anthony Scarpino wrote:
>>>> On 4/30/20 10:19 AM, Xuelei Fan wrote:
>>>>> Hi,
>>>>>
>>>>> Could I get the following update reviewed:
>>>>> http://cr.openjdk.java.net/~xuelei/8240871/webrev.00/
>>>>>
>>>>> For TLS 1.3 full handshake, if the last handshake flight wraps the
>>>>> Finished together with other handshake message, for example client
>>>>> certificate, the flight could be wrapped and encrypted in one
>>>>> record and delegated tasks would be used. There is no chance to
>>>>> return FINISHED handshake status with SSLEngine.(un)wrap().
>>>>> However, per the HandshakeStatus.FINISHED specification, this
>>>>> handshake status is only generated by a call to
>>>>> SSLEngine.wrap()/unwrap() and it is never generated by
>>>>> SSLEngine.getHandshakeStatus().
>>>>>
>>>>> In order to workaround this case for TLS 1.3, the FINISHED status
>>>>> could present with SSLEngine.wrap() while delivering of the
>>>>> NewSessionTicket post-handshake message. If this post-handshake
>>>>> message is not needed, a follow-on SSLEngine.wrap() should be
>>>>> called to indicate the FINISHED handshake status. Although this
>>>>> special SSLEngine.wrap() should not consume or produce any
>>>>> application or network data.
>>>>>
>>>>> I also clean up some debug log, names and code style a little bit.
>>>>>
>>>>> The update could be confirmed with Tomcat and Firefox in private
>>>>> mode, as described in the bug description. As this case happens
>>>>> only when psk_key_exchange_modes does not present, which is not a
>>>>> behavior supported by JDK, I did not find a workaround for a new
>>>>> regression test yet. I added the labels, noreg-external and
>>>>> noreg-hard.
>>>>>
>>>>> Thanks,
>>>>> Xuelei
>>>>
>>>> I do not fully understand the situation, mostly because of SSLEngine
>>>> semantics. In normal operation, does is HandshakeStatus.FINISHED
>>>> returned when Finished is received or after the NewSessionTicket
>>>> message?
>>> Not exactly. For TLS 1.2, FINISHED will be returned with unwrap() of
>>> the finished handshake message. However, for TLS 1.3, FINISHED will
>>> be returned any longer, because the finished handshake message is
>>> wrapped with certificate message in one record.
>>>
>>> For TLS 1.3:
>>> 1. client send certificate, certificate verify and finished handshake
>>> message in one record.
>>> 2. server call unwrap(), and return NEED_TASK to handle the
>>> certificate and certificate verify.
>>>
>>> So, no more FINISHED for the unwrap() return.
>>>
>>> It is fine if there is a after NewSessionTicket message. The wrap()
>>> for the post-handshake message will return FINISHED.
>>>
>>> The bug reported is a special one that the Firefox is run in private
>>> mode, which does not request NewSessionTicket. So there is no
>>> post-handshake generated and sent in server side. Then, there is no
>>> FINISHED can be used if applications depends on it.
>>>
>>> To workaround the case, a dummy wrap() or unwrap() could be used to
>>> get the FINISHED. The wrap() or unwrap() actually do nothing but
>>> return the FINISHED status.
>>>
>>
>> I don't want to be problematic, but I don't really agree with creating
>> dummy messages to generate wrap/unwrap operations in the TLS code.
> No dummy message created. Only need to call wrap() or unwrap(), but not
> data consumed or generated by the call to wrap() or unwrap(), no
> application data consumed, no network data consumed, no application data
> generated, no network data generated.
>
>> If SSLEngine is doing something wrong with not fully reading the
>> buffer, then I feel it's SSLEngine that should be fixed to handle the
>> situation right.
> It is not caused by SSLEngine that does not fully reading the buffer.
> Let me try again about what's the problem.
>
> The client (Firefox) sends, Certificate and CerticateVerify and Finished
> handshake messages in one record. The record is encrypted.
> 1. One call to SSLEngine.unrwap() will read the record, and decrypt the
> record.
> 2. One call to SSLEngine.unwrap() cannot read the Certificate and
> CerticateVerify handshake message only, without reading the Finished
> handshake message. It means the unwrap() method will consume the record
> data fully for all three handshake messages.
> 3. The Certificate and CerticateVerify should be handled in delegated
> tasks, so the call to SSLEngine.unwrap() return NEED_TASK.
> 4. As the SSLEngine.unwrap() return NEED_TASK, it cannot return the
> FINISHED status at the same time.
> 5. The FINISHED status is only be able to return with SSLEngine.unwrap()
> or SSLEngine.wrap(), and cannot returned from delegated tasks. So the
> SSLEngine.getHandshakeStatus() after the tasks cannot be used to
> indicate the FINISHED status.
> 6. Then FINISHED status is missed, applications like Tomcat run into
> problem, like the bug described.
>
> That's the problem of the bug as far as I can see. I agreed it is not
> good to have an additional wrap() or unwrap() that did nothing, but I
> have no better idea. It would be nice if we could have a fix in JDK 15,
> considering the impact on Tomcat and Firefox. I'm open if there is
> alternative solution or workaround.
>
>
>> Maybe not put the finished message or put all these messages together.
>>
>> Maybe Brad may know of a way out of this problem? If creating a dummy
>> message is the only way to fix this, then I'm ok with it. It is just
>> not a clean fix in my mind.
>>
>>
>>>> My understanding would have been after Finished because NST is
>>>> suppose to be a post handshake message. So in this case there is no
>>>> problem, correct?
>>>>
>>> Correct.
>>>
>>>> I'm trying to figure out why you need an empty NST. Is the problem
>>>> when a number of messages are bundled together. For example, the
>>>> Finished message with a partial NST, then Finished isn't processed
>>>> and both sides are waiting? Or do both sides continue normal
>>>> traffic, it's jut the HandshakeStatus.FINISHED is one operation behind?
>>>>
>>> It should be fine as empty NST is just a signal to indicate the next
>>> call to wrap(). The next call to wrap() just use the signal for the
>>> return of FINISHED status, not network data produced, delivered or
>>> consumed.
>>>
>>>>
>>>> One code comment so far:
>>>> 433: The debug message purpose was to say the NST is a stateless
>>>> ticket and not a preshared key. Can we keep "stateless" in the
>>>> message?
>>>>
>>> NewSessionTicket.java? Sure, I may just want to shrink to one line.
>>> It was not intended.
>>
>> shrinking the message is fine, it just needs to be clear which message
>> ticket type got sent.
>>
> Added back.
>
> Xuelei
More information about the security-dev
mailing list