aws / s2n-tls

An implementation of the TLS/SSL protocols
https://aws.github.io/s2n-tls/usage-guide/
Apache License 2.0
4.51k stars 704 forks source link

improper calculation of session ticket age #4583

Open jmayclin opened 3 months ago

jmayclin commented 3 months ago

Problem:

s2n-tls configures STEK lifetime through two parameters

  1. encrypt lifetime: how long the material can be used for both encryption and decryption
  2. decrypt lifetime: how long the material can only be used for decryption.

The session ticket lifetime is then set to the sum of these values: https://github.com/aws/s2n-tls/blob/7ec1446ac1d836cefb6a5363b9bfb438a4faa792/tls/s2n_server_new_session_ticket.c#L193-L199

However, this fails to account for the fact that the STEK has likely already been in use for some amount of time.

Consider the following scenario

  1. encrypt_lifetime = 1000, decrypt_lifetime = 1000
  2. time=0: stek added to s2n-tls
  3. time=500: a client handshakes with the server
  4. time=500: the client receives a session ticket with session ticket lifetime set to 2000.
  5. time=2000: the stek is expired by s2n-tls.
  6. time=2100: the client attempts resumption, which is expects to succeed, because only 1600 seconds have elapsed
  7. time=2100: the resumption attempt fails, because the STEK has expired.

While the above scenario is simplest to understand, the real-world scenario is a bit more complicated.

s2n-tls will choose a STEK based upon the remaining encryption lifetime of the key. The maximum probability of selection occurs once encryption_lifetime / 2 has elapsed. https://github.com/aws/s2n-tls/blob/7ec1446ac1d836cefb6a5363b9bfb438a4faa792/tls/s2n_resume.c#L656-L665 Therefore the expected key material lifetime for a session_ticket is encryption_lifetime / 2 + decryption_lifetime, but the session_ticket_lifetime will always be set to encryption_lifetime + decryption_lifetime.

I wrote a simulation to confirm this behavior, which can be viewed here

It shows the following output.

2024-06-04T23:37:43.655801Z ERROR node{name="client"}: session_resumption: 251: Ticket supposed to be valid for 360s, only 310.017s elapsed
2024-06-04T23:37:43.664387Z DEBUG node{name="client"}: session_resumption: 110: stek name is 7
2024-06-04T23:37:44.395429Z ERROR node{name="client"}: session_resumption: 251: Ticket supposed to be valid for 360s, only 309.867s elapsed
2024-06-04T23:37:44.399744Z DEBUG node{name="client"}: session_resumption: 110: stek name is 8
2024-06-04T23:37:45.146100Z ERROR node{name="client"}: session_resumption: 251: Ticket supposed to be valid for 360s, only 309.916s elapsed
2024-06-04T23:37:45.149128Z DEBUG node{name="client"}: session_resumption: 110: stek name is 9
2024-06-04T23:37:45.892796Z DEBUG node{name="client"}: session_resumption: 110: stek name is 9
2024-06-04T23:37:46.627179Z  INFO node{name="client"}: session_resumption: 273: session resumption attempts: 29, successes: 10

Solution:

Option 1: STEK lifetime calculation

s2n_generate_ticket_lifetime should be made aware of the specific STEK that the ticket is being encrypted under. Then the actual ticket lifetime could be accurately set to the remaining lifetime of the STEK.

Option 2: Bigger Overhaul

We don't have great documentation around it, but I am a little dubious of the "weighted key selection" logic. To get an effective lifetime of $TARGET, the STEK lifetime has to be set strictly larger than $TARGET, which is non-ideal because it forces sensitive cryptographic secrets to be kept around longer.

This might just be an opportunity to do a larger overhaul of our STEK selection logic 😄 .

Requirements / Acceptance Criteria:

s2n-tls should return session tickets with accurate session ticket lifetimes.

jmayclin commented 3 months ago

Similar to #2756. Gonna keep this issue open since it has a bit more information and isn't TLS 1.3 specific.