text: /*++
  
      Copyright (c) Microsoft Corporation.
      Licensed under the MIT License.
  
  Abstract:
  
      This file contains all the state and logic for the cryptographic handshake.
      This abstracts dealing with TLS 1.3 messages, as a multiple, serial streams
      of bytes. Each stream of bytes is secured with a different encryption key.
  
      QUIC_CRYPTO represents the multiple streams as a single contiguous buffer
      internally, and keeps tracks of the offsets at which each different stream
      starts (and therefore where the previous stream ends).
  
      Many of the internals of QUIC_CRYPTO are similar to QUIC_STREAM. This
      includes ACK tracking and receive buffer reassembly.
  
  --*/
  
  #include "precomp.h"
  #ifdef QUIC_CLOG
  #include "crypto.c.clog.h"
  #endif
  
  CXPLAT_TLS_RECEIVE_TP_CALLBACK QuicConnReceiveTP;
  CXPLAT_TLS_RECEIVE_TICKET_CALLBACK QuicConnRecvResumptionTicket;
  CXPLAT_TLS_PEER_CERTIFICATE_RECEIVED_CALLBACK QuicConnPeerCertReceived;
  
  CXPLAT_TLS_CALLBACKS QuicTlsCallbacks = {
      QuicConnReceiveTP,
      QuicConnRecvResumptionTicket,
      QuicConnPeerCertReceived
  };
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  void
  QuicCryptoDumpSendState(
      _In_ QUIC_CRYPTO* Crypto
      )
  {
      if (QuicTraceLogVerboseEnabled()) {
  
          QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
  
          QuicTraceLogConnVerbose(
              CryptoDump,
              Connection,
              "QS:%u MAX:%u UNA:%u NXT:%u RECOV:%u-%u",
              Crypto->TlsState.BufferTotalLength,
              Crypto->MaxSentLength,
              Crypto->UnAckedOffset,
              Crypto->NextSendOffset,
              Crypto->InRecovery ? Crypto->RecoveryNextOffset : 0,
              Crypto->InRecovery ? Crypto->RecoveryEndOffset : 0);
  
          uint64_t UnAcked = Crypto->UnAckedOffset;
          uint32_t i = 0;
          QUIC_SUBRANGE* Sack;
          while ((Sack = QuicRangeGetSafe(&Crypto->SparseAckRanges, i++)) != NULL) {
              QuicTraceLogConnVerbose(
                  CryptoDumpUnacked,
                  Connection,
                  "  unACKed: [%llu, %llu]",
                  UnAcked,
                  Sack->Low);
              UnAcked = Sack->Low + Sack->Count;
          }
          if (UnAcked < (uint64_t)Crypto->MaxSentLength) {
              QuicTraceLogConnVerbose(
                  CryptoDumpUnacked2,
                  Connection,
                  "  unACKed: [%llu, %u]",
                  UnAcked,
                  Crypto->MaxSentLength);
          }
  
          CXPLAT_DBG_ASSERT(Crypto->UnAckedOffset <= Crypto->NextSendOffset);
      }
  }
  
  #if DEBUG
  _IRQL_requires_max_(PASSIVE_LEVEL)
  void
  QuicCryptoValidate(
      _In_ const QUIC_CRYPTO* Crypto
      )
  {
      CXPLAT_DBG_ASSERT(Crypto->TlsState.BufferTotalLength >= Crypto->MaxSentLength);
      CXPLAT_DBG_ASSERT(Crypto->MaxSentLength >= Crypto->UnAckedOffset);
      CXPLAT_DBG_ASSERT(Crypto->MaxSentLength >= Crypto->NextSendOffset);
      CXPLAT_DBG_ASSERT(Crypto->MaxSentLength >= Crypto->RecoveryNextOffset);
      CXPLAT_DBG_ASSERT(Crypto->MaxSentLength >= Crypto->RecoveryEndOffset);
      CXPLAT_DBG_ASSERT(Crypto->NextSendOffset >= Crypto->UnAckedOffset);
      CXPLAT_DBG_ASSERT(Crypto->TlsState.BufferLength + Crypto->UnAckedOffset == Crypto->TlsState.BufferTotalLength);
  }
  #else
  #define QuicCryptoValidate(Crypto)
  #endif
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  QUIC_STATUS
  QuicCryptoInitialize(
      _Inout_ QUIC_CRYPTO* Crypto
      )
  {
      QUIC_STATUS Status;
      QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
      uint16_t SendBufferLength =
          QuicConnIsServer(Connection) ?
              QUIC_MAX_TLS_SERVER_SEND_BUFFER : QUIC_MAX_TLS_CLIENT_SEND_BUFFER;
      uint16_t InitialRecvBufferLength =
          QuicConnIsServer(Connection) ?
              QUIC_MAX_TLS_CLIENT_SEND_BUFFER : QUIC_DEFAULT_STREAM_RECV_BUFFER_SIZE;
      const uint8_t* HandshakeCid;
      uint8_t HandshakeCidLength;
      BOOLEAN RecvBufferInitialized = FALSE;
  
      const uint8_t* Salt = QuicSupportedVersionList[0].Salt; // Default to latest
      for (uint32_t i = 0; i < ARRAYSIZE(QuicSupportedVersionList); ++i) {
          if (QuicSupportedVersionList[i].Number == Connection->Stats.QuicVersion) {
              Salt = QuicSupportedVersionList[i].Salt;
              break;
          }
      }
  
      CXPLAT_PASSIVE_CODE();
  
      QuicRangeInitialize(
          QUIC_MAX_RANGE_ALLOC_SIZE,
          &Crypto->SparseAckRanges);
  
      Crypto->TlsState.BufferAllocLength = SendBufferLength;
      Crypto->TlsState.Buffer = CXPLAT_ALLOC_NONPAGED(SendBufferLength, QUIC_POOL_TLS_BUFFER);
      if (Crypto->TlsState.Buffer == NULL) {
          QuicTraceEvent(
              AllocFailure,
              "Allocation of '%s' failed. (%llu bytes)",
              "crypto send buffer",
              SendBufferLength);
          Status = QUIC_STATUS_OUT_OF_MEMORY;
          goto Exit;
      }
  
      QuicRangeInitialize(
          QUIC_MAX_RANGE_ALLOC_SIZE,
          &Crypto->SparseAckRanges);
  
      Status =
          QuicRecvBufferInitialize(
              &Crypto->RecvBuffer,
              InitialRecvBufferLength,
              QUIC_DEFAULT_STREAM_FC_WINDOW_SIZE / 2,
              TRUE,
              NULL);
      if (QUIC_FAILED(Status)) {
          goto Exit;
      }
      RecvBufferInitialized = TRUE;
  
      if (QuicConnIsServer(Connection)) {
          CXPLAT_DBG_ASSERT(Connection->SourceCids.Next != NULL);
          QUIC_CID_HASH_ENTRY* SourceCid =
              CXPLAT_CONTAINING_RECORD(
                  Connection->SourceCids.Next,
                  QUIC_CID_HASH_ENTRY,
                  Link);
  
          HandshakeCid = SourceCid->CID.Data;
          HandshakeCidLength = SourceCid->CID.Length;
  
      } else {
          CXPLAT_DBG_ASSERT(!CxPlatListIsEmpty(&Connection->DestCids));
          QUIC_CID_LIST_ENTRY* DestCid =
              CXPLAT_CONTAINING_RECORD(
                  Connection->DestCids.Flink,
                  QUIC_CID_LIST_ENTRY,
                  Link);
  
          HandshakeCid = DestCid->CID.Data;
          HandshakeCidLength = DestCid->CID.Length;
      }
  
      Status =
          QuicPacketKeyCreateInitial(
              QuicConnIsServer(Connection),
              Salt,
              HandshakeCidLength,
              HandshakeCid,
              &Crypto->TlsState.ReadKeys[QUIC_PACKET_KEY_INITIAL],
              &Crypto->TlsState.WriteKeys[QUIC_PACKET_KEY_INITIAL]);
      if (QUIC_FAILED(Status)) {
          QuicTraceEvent(
              ConnErrorStatus,
              "[conn][%p] ERROR, %u, %s.",
              Connection,
              Status,
              "Creating initial keys");
          goto Exit;
      }
      CXPLAT_DBG_ASSERT(Crypto->TlsState.ReadKeys[QUIC_PACKET_KEY_INITIAL] != NULL);
      CXPLAT_DBG_ASSERT(Crypto->TlsState.WriteKeys[QUIC_PACKET_KEY_INITIAL] != NULL);
  
      Crypto->Initialized = TRUE;
      QuicCryptoValidate(Crypto);
  
  Exit:
  
      if (QUIC_FAILED(Status)) {
          for (size_t i = 0; i < QUIC_PACKET_KEY_COUNT; ++i) {
              QuicPacketKeyFree(Crypto->TlsState.ReadKeys[i]);
              Crypto->TlsState.ReadKeys[i] = NULL;
              QuicPacketKeyFree(Crypto->TlsState.WriteKeys[i]);
              Crypto->TlsState.WriteKeys[i] = NULL;
          }
          if (RecvBufferInitialized) {
              QuicRecvBufferUninitialize(&Crypto->RecvBuffer);
          }
          if (Crypto->TlsState.Buffer != NULL) {
              CXPLAT_FREE(Crypto->TlsState.Buffer, QUIC_POOL_TLS_BUFFER);
              Crypto->TlsState.Buffer = NULL;
          }
      }
  
      return Status;
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  void
  QuicCryptoUninitialize(
      _In_ QUIC_CRYPTO* Crypto
      )
  {
      for (size_t i = 0; i < QUIC_PACKET_KEY_COUNT; ++i) {
          QuicPacketKeyFree(Crypto->TlsState.ReadKeys[i]);
          Crypto->TlsState.ReadKeys[i] = NULL;
          QuicPacketKeyFree(Crypto->TlsState.WriteKeys[i]);
          Crypto->TlsState.WriteKeys[i] = NULL;
      }
      if (Crypto->TLS != NULL) {
          CxPlatTlsUninitialize(Crypto->TLS);
          Crypto->TLS = NULL;
      }
      if (Crypto->ResumptionTicket != NULL) {
          CXPLAT_FREE(Crypto->ResumptionTicket, QUIC_POOL_CRYPTO_RESUMPTION_TICKET);
          Crypto->ResumptionTicket = NULL;
      }
      if (Crypto->TlsState.NegotiatedAlpn != NULL &&
          QuicConnIsServer(QuicCryptoGetConnection(Crypto))) {
          if (Crypto->TlsState.NegotiatedAlpn != Crypto->TlsState.SmallAlpnBuffer) {
              CXPLAT_FREE(Crypto->TlsState.NegotiatedAlpn, QUIC_POOL_ALPN);
          }
          Crypto->TlsState.NegotiatedAlpn = NULL;
      }
      if (Crypto->Initialized) {
          QuicRecvBufferUninitialize(&Crypto->RecvBuffer);
          QuicRangeUninitialize(&Crypto->SparseAckRanges);
          CXPLAT_FREE(Crypto->TlsState.Buffer, QUIC_POOL_TLS_BUFFER);
          Crypto->TlsState.Buffer = NULL;
          Crypto->Initialized = FALSE;
      }
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  QUIC_STATUS
  QuicCryptoInitializeTls(
      _Inout_ QUIC_CRYPTO* Crypto,
      _In_ CXPLAT_SEC_CONFIG* SecConfig,
      _In_ const QUIC_TRANSPORT_PARAMETERS* Params
      )
  {
      QUIC_STATUS Status;
      CXPLAT_TLS_CONFIG TlsConfig = { 0 };
      QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
      BOOLEAN IsServer = QuicConnIsServer(Connection);
  
      CXPLAT_DBG_ASSERT(Params != NULL);
      CXPLAT_DBG_ASSERT(SecConfig != NULL);
      CXPLAT_DBG_ASSERT(Connection->Configuration != NULL);
  
      Crypto->MaxSentLength = 0;
      Crypto->UnAckedOffset = 0;
      Crypto->NextSendOffset = 0;
      Crypto->RecoveryNextOffset = 0;
      Crypto->RecoveryEndOffset = 0;
      Crypto->InRecovery = FALSE;
  
      Crypto->TlsState.BufferLength = 0;
      Crypto->TlsState.BufferTotalLength = 0;
  
      TlsConfig.IsServer = IsServer;
      if (IsServer) {
          TlsConfig.AlpnBuffer = Crypto->TlsState.NegotiatedAlpn;
          TlsConfig.AlpnBufferLength = 1 + Crypto->TlsState.NegotiatedAlpn[0];
      } else {
          TlsConfig.AlpnBuffer = Connection->Configuration->AlpnList;
          TlsConfig.AlpnBufferLength = Connection->Configuration->AlpnListLength;
      }
      TlsConfig.SecConfig = SecConfig;
      TlsConfig.Connection = Connection;
      TlsConfig.ResumptionTicketBuffer = Crypto->ResumptionTicket;
      TlsConfig.ResumptionTicketLength = Crypto->ResumptionTicketLength;
      if (!QuicConnIsServer(Connection)) {
          TlsConfig.ServerName = Connection->RemoteServerName;
      }
  #ifdef CXPLAT_TLS_SECRETS_SUPPORT
      TlsConfig.TlsSecrets = Connection->TlsSecrets;
  #endif
  
      TlsConfig.TPType =
          Connection->Stats.QuicVersion != QUIC_VERSION_DRAFT_29 ?
              TLS_EXTENSION_TYPE_QUIC_TRANSPORT_PARAMETERS :
              TLS_EXTENSION_TYPE_QUIC_TRANSPORT_PARAMETERS_DRAFT;
      TlsConfig.LocalTPBuffer =
          QuicCryptoTlsEncodeTransportParameters(
              Connection,
              QuicConnIsServer(Connection),
              Params,
              (Connection->State.TestTransportParameterSet ?
                  &Connection->TestTransportParameter : NULL),
              &TlsConfig.LocalTPLength);
      if (TlsConfig.LocalTPBuffer == NULL) {
          Status = QUIC_STATUS_OUT_OF_MEMORY;
          goto Error;
      }
  
      if (Crypto->TLS != NULL) {
          CxPlatTlsUninitialize(Crypto->TLS);
          Crypto->TLS = NULL;
      }
  
      Status = CxPlatTlsInitialize(&TlsConfig, &Crypto->TlsState, &Crypto->TLS);
      if (QUIC_FAILED(Status)) {
          QuicTraceEvent(
              ConnErrorStatus,
              "[conn][%p] ERROR, %u, %s.",
              Connection,
              Status,
              "CxPlatTlsInitialize");
          CXPLAT_FREE(TlsConfig.LocalTPBuffer, QUIC_POOL_TLS_TRANSPARAMS);
          goto Error;
      }
  
      Crypto->ResumptionTicket = NULL; // Owned by TLS now.
      Crypto->ResumptionTicketLength = 0;
      Status = QuicCryptoProcessData(Crypto, !IsServer);
  
  Error:
  
      return Status;
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  void
  QuicCryptoReset(
      _In_ QUIC_CRYPTO* Crypto
      )
  {
      CXPLAT_DBG_ASSERT(!QuicConnIsServer(QuicCryptoGetConnection(Crypto)));
      CXPLAT_TEL_ASSERT(Crypto->RecvTotalConsumed == 0);
  
      Crypto->MaxSentLength = 0;
      Crypto->UnAckedOffset = 0;
      Crypto->NextSendOffset = 0;
      Crypto->RecoveryNextOffset = 0;
      Crypto->RecoveryEndOffset = 0;
      Crypto->InRecovery = FALSE;
  
      QuicSendSetSendFlag(
          &QuicCryptoGetConnection(Crypto)->Send,
          QUIC_CONN_SEND_FLAG_CRYPTO);
  
      QuicCryptoValidate(Crypto);
  }
  
  QUIC_STATUS
  QuicCryptoOnVersionChange(
      _In_ QUIC_CRYPTO* Crypto
      )
  {
      QUIC_STATUS Status;
      QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
      const uint8_t* HandshakeCid;
      uint8_t HandshakeCidLength;
  
      const uint8_t* Salt = QuicSupportedVersionList[0].Salt; // Default to latest
      for (uint32_t i = 0; i < ARRAYSIZE(QuicSupportedVersionList); ++i) {
          if (QuicSupportedVersionList[i].Number == Connection->Stats.QuicVersion) {
              Salt = QuicSupportedVersionList[i].Salt;
              break;
          }
      }
  
      if (QuicConnIsServer(Connection)) {
          CXPLAT_DBG_ASSERT(Connection->SourceCids.Next != NULL);
          QUIC_CID_HASH_ENTRY* SourceCid =
              CXPLAT_CONTAINING_RECORD(
                  Connection->SourceCids.Next,
                  QUIC_CID_HASH_ENTRY,
                  Link);
  
          HandshakeCid = SourceCid->CID.Data;
          HandshakeCidLength = SourceCid->CID.Length;
  
      } else {
          CXPLAT_DBG_ASSERT(!CxPlatListIsEmpty(&Connection->DestCids));
          QUIC_CID_LIST_ENTRY* DestCid =
              CXPLAT_CONTAINING_RECORD(
                  Connection->DestCids.Flink,
                  QUIC_CID_LIST_ENTRY,
                  Link);
  
          HandshakeCid = DestCid->CID.Data;
          HandshakeCidLength = DestCid->CID.Length;
      }
  
      if (Crypto->TlsState.ReadKeys[QUIC_PACKET_KEY_INITIAL] != NULL) {
          CXPLAT_FRE_ASSERT(Crypto->TlsState.WriteKeys[QUIC_PACKET_KEY_INITIAL] != NULL);
          QuicPacketKeyFree(Crypto->TlsState.ReadKeys[QUIC_PACKET_KEY_INITIAL]);
          QuicPacketKeyFree(Crypto->TlsState.WriteKeys[QUIC_PACKET_KEY_INITIAL]);
          Crypto->TlsState.ReadKeys[QUIC_PACKET_KEY_INITIAL] = NULL;
          Crypto->TlsState.WriteKeys[QUIC_PACKET_KEY_INITIAL] = NULL;
      }
  
      Status =
          QuicPacketKeyCreateInitial(
              QuicConnIsServer(Connection),
              Salt,
              HandshakeCidLength,
              HandshakeCid,
              &Crypto->TlsState.ReadKeys[QUIC_PACKET_KEY_INITIAL],
              &Crypto->TlsState.WriteKeys[QUIC_PACKET_KEY_INITIAL]);
      if (QUIC_FAILED(Status)) {
          QuicTraceEvent(
              ConnErrorStatus,
              "[conn][%p] ERROR, %u, %s.",
              Connection,
              Status,
              "Creating initial keys");
          goto Exit;
      }
      CXPLAT_DBG_ASSERT(Crypto->TlsState.ReadKeys[QUIC_PACKET_KEY_INITIAL] != NULL);
      CXPLAT_DBG_ASSERT(Crypto->TlsState.WriteKeys[QUIC_PACKET_KEY_INITIAL] != NULL);
  
      QuicCryptoValidate(Crypto);
  
  Exit:
  
      if (QUIC_FAILED(Status)) {
          for (size_t i = 0; i < QUIC_PACKET_KEY_COUNT; ++i) {
              QuicPacketKeyFree(Crypto->TlsState.ReadKeys[i]);
              Crypto->TlsState.ReadKeys[i] = NULL;
              QuicPacketKeyFree(Crypto->TlsState.WriteKeys[i]);
              Crypto->TlsState.WriteKeys[i] = NULL;
          }
      }
      return Status;
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  void
  QuicCryptoHandshakeConfirmed(
      _In_ QUIC_CRYPTO* Crypto
      )
  {
      QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
      Connection->State.HandshakeConfirmed = TRUE;
  
      QUIC_PATH* Path = &Connection->Paths[0];
      CXPLAT_DBG_ASSERT(Path->Binding != NULL);
      QuicBindingOnConnectionHandshakeConfirmed(Path->Binding, Connection);
  
      QuicCryptoDiscardKeys(Crypto, QUIC_PACKET_KEY_HANDSHAKE);
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  BOOLEAN
  QuicCryptoDiscardKeys(
      _In_ QUIC_CRYPTO* Crypto,
      _In_ QUIC_PACKET_KEY_TYPE KeyType
      )
  {
      if (Crypto->TlsState.WriteKeys[KeyType] == NULL &&
          Crypto->TlsState.ReadKeys[KeyType] == NULL) {
          //
          // The keys have already been discarded.
          //
          return FALSE;
      }
  
      QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
      QuicTraceLogConnInfo(
          DiscardKeyType,
          Connection,
          "Discarding key type = %hhu",
          (uint8_t)KeyType);
  
      QuicPacketKeyFree(Crypto->TlsState.WriteKeys[KeyType]);
      QuicPacketKeyFree(Crypto->TlsState.ReadKeys[KeyType]);
      Crypto->TlsState.WriteKeys[KeyType] = NULL;
      Crypto->TlsState.ReadKeys[KeyType] = NULL;
  
      QUIC_ENCRYPT_LEVEL EncryptLevel = QuicKeyTypeToEncryptLevel(KeyType);
      _Analysis_assume_(EncryptLevel >= 0);
      if (EncryptLevel >= QUIC_ENCRYPT_LEVEL_1_RTT) {
          //
          // No additional state clean up required for 1-RTT encrytion level.
          //
          return TRUE;
      }
  
      //
      // Clean up send/recv tracking state for the encryption level.
      //
  
      CXPLAT_DBG_ASSERT(Connection->Packets[EncryptLevel] != NULL);
      BOOLEAN HasAckElicitingPacketsToAcknowledge =
          Connection->Packets[EncryptLevel]->AckTracker.AckElicitingPacketsToAcknowledge != 0;
      QuicLossDetectionDiscardPackets(&Connection->LossDetection, KeyType);
      QuicPacketSpaceUninitialize(Connection->Packets[EncryptLevel]);
      Connection->Packets[EncryptLevel] = NULL;
  
      //
      // Clean up any possible left over recovery state.
      //
      uint32_t BufferOffset =
          KeyType == QUIC_PACKET_KEY_INITIAL ?
              Crypto->TlsState.BufferOffsetHandshake :
              Crypto->TlsState.BufferOffset1Rtt;
      CXPLAT_DBG_ASSERT(BufferOffset != 0);
      CXPLAT_DBG_ASSERT(Crypto->MaxSentLength >= BufferOffset);
      if (Crypto->NextSendOffset < BufferOffset) {
          Crypto->NextSendOffset = BufferOffset;
      }
      if (Crypto->RecoveryNextOffset < BufferOffset) {
          Crypto->RecoveryNextOffset = BufferOffset;
      }
      if (Crypto->UnAckedOffset < BufferOffset) {
          uint32_t DrainLength = BufferOffset - Crypto->UnAckedOffset;
          CXPLAT_DBG_ASSERT(DrainLength <= (uint32_t)Crypto->TlsState.BufferLength);
          if ((uint32_t)Crypto->TlsState.BufferLength > DrainLength) {
              Crypto->TlsState.BufferLength -= (uint16_t)DrainLength;
              CxPlatMoveMemory(
                  Crypto->TlsState.Buffer,
                  Crypto->TlsState.Buffer + DrainLength,
                  Crypto->TlsState.BufferLength);
          } else {
              Crypto->TlsState.BufferLength = 0;
          }
          Crypto->UnAckedOffset = BufferOffset;
          QuicRangeSetMin(&Crypto->SparseAckRanges, Crypto->UnAckedOffset);
      }
  
      if (HasAckElicitingPacketsToAcknowledge) {
          QuicSendUpdateAckState(&Connection->Send);
      }
  
      QuicCryptoValidate(Crypto);
  
      return TRUE;
  }
  
  //
  // Send Interfaces
  //
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  QUIC_ENCRYPT_LEVEL
  QuicCryptoGetNextEncryptLevel(
      _In_ QUIC_CRYPTO* Crypto
      )
  {
      uint64_t SendOffset =
          RECOV_WINDOW_OPEN(Crypto) ?
              Crypto->RecoveryNextOffset : Crypto->NextSendOffset;
  
      if (Crypto->TlsState.BufferOffset1Rtt != 0 &&
          SendOffset >= Crypto->TlsState.BufferOffset1Rtt) {
          return QUIC_ENCRYPT_LEVEL_1_RTT;
      }
  
      if (Crypto->TlsState.BufferOffsetHandshake != 0 &&
          SendOffset >= Crypto->TlsState.BufferOffsetHandshake) {
          return QUIC_ENCRYPT_LEVEL_HANDSHAKE;
      }
  
      return QUIC_ENCRYPT_LEVEL_INITIAL;
  }
  
  //
  // Writes data at the requested stream offset to a stream frame.
  //
  _IRQL_requires_max_(PASSIVE_LEVEL)
  _Success_(return != FALSE)
  BOOLEAN
  QuicCryptoWriteOneFrame(
      _In_ QUIC_CRYPTO* Crypto,
      _In_ uint32_t EncryptLevelStart,
      _In_ uint32_t CryptoOffset,
      _Inout_ uint16_t* FramePayloadBytes,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer,
      _Inout_ QUIC_SENT_PACKET_METADATA* PacketMetadata
      )
  {
      QuicCryptoValidate(Crypto);
      CXPLAT_DBG_ASSERT(*FramePayloadBytes > 0);
      CXPLAT_DBG_ASSERT(CryptoOffset >= EncryptLevelStart);
      CXPLAT_DBG_ASSERT(CryptoOffset <= Crypto->TlsState.BufferTotalLength);
      CXPLAT_DBG_ASSERT(CryptoOffset >= (Crypto->TlsState.BufferTotalLength - Crypto->TlsState.BufferLength));
  
      QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
      QUIC_CRYPTO_EX Frame = { CryptoOffset - EncryptLevelStart, 0, 0 };
      Frame.Data =
          Crypto->TlsState.Buffer +
          (CryptoOffset - (Crypto->TlsState.BufferTotalLength - Crypto->TlsState.BufferLength));
  
      //
      // From the remaining amount of space in the packet, calculate the size of
      // the CRYPTO frame header to then determine how much room is left for
      // payload.
      //
  
      uint16_t HeaderLength = sizeof(uint8_t) + QuicVarIntSize(CryptoOffset);
      if (BufferLength < *Offset + HeaderLength + 4) {
          QuicTraceLogConnVerbose(
              NoMoreRoomForCrypto,
              Connection,
              "No room for CRYPTO frame");
          return FALSE;
      }
  
      Frame.Length = BufferLength - *Offset - HeaderLength;
      uint16_t LengthFieldByteCount = QuicVarIntSize(Frame.Length);
      Frame.Length -= LengthFieldByteCount;
  
      //
      // Even if there is room in the buffer, we can't write more data than is
      // currently queued.
      //
      if (Frame.Length > *FramePayloadBytes) {
          Frame.Length = *FramePayloadBytes;
      }
  
      CXPLAT_DBG_ASSERT(Frame.Length > 0);
      *FramePayloadBytes = (uint16_t)Frame.Length;
  
      QuicTraceLogConnVerbose(
          AddCryptoFrame,
          Connection,
          "Sending %hu crypto bytes, offset=%u",
          (uint16_t)Frame.Length,
          CryptoOffset);
  
      //
      // We're definitely writing a frame and we know how many bytes it contains,
      // so do the real call to QuicFrameEncodeStreamHeader to write the header.
      //
      CXPLAT_FRE_ASSERT(
          QuicCryptoFrameEncode(&Frame, Offset, BufferLength, Buffer));
  
      PacketMetadata->Flags.IsAckEliciting = TRUE;
      PacketMetadata->Frames[PacketMetadata->FrameCount].Type = QUIC_FRAME_CRYPTO;
      PacketMetadata->Frames[PacketMetadata->FrameCount].CRYPTO.Offset = CryptoOffset;
      PacketMetadata->Frames[PacketMetadata->FrameCount].CRYPTO.Length = (uint16_t)Frame.Length;
      PacketMetadata->Frames[PacketMetadata->FrameCount].Flags = 0;
      PacketMetadata->FrameCount++;
  
      return TRUE;
  }
  
  //
  // Writes CRYPTO frames into a packet buffer.
  //
  _IRQL_requires_max_(PASSIVE_LEVEL)
  void
  QuicCryptoWriteCryptoFrames(
      _In_ QUIC_CRYPTO* Crypto,
      _Inout_ QUIC_PACKET_BUILDER* Builder,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      QuicCryptoValidate(Crypto);
  
      //
      // Write frames until we've filled the provided space.
      //
  
      while (*Offset < BufferLength &&
          Builder->Metadata->FrameCount < QUIC_MAX_FRAMES_PER_PACKET) {
  
          //
          // Find the bounds of this frame. Left is the offset of the
          // first byte in the frame, and Right is the offset of the
          // first byte AFTER the frame.
          //
          uint32_t Left;
          uint32_t Right;
  
          BOOLEAN Recovery;
          if (RECOV_WINDOW_OPEN(Crypto)) {
              Left = Crypto->RecoveryNextOffset;
              Recovery = TRUE;
          } else {
              Left = Crypto->NextSendOffset;
              Recovery = FALSE;
          }
  
          if (Left == Crypto->TlsState.BufferTotalLength) {
              //
              // No more data left to send.
              //
              break;
          }
  
          Right = Left + BufferLength - *Offset;
  
          if (Recovery &&
              Right > Crypto->RecoveryEndOffset &&
              Crypto->RecoveryEndOffset != Crypto->NextSendOffset) {
              Right = Crypto->RecoveryEndOffset;
          }
  
          //
          // Find the first SACK after the selected offset.
          //
          QUIC_SUBRANGE* Sack;
          if (Left == Crypto->MaxSentLength) {
              //
              // Transmitting new bytes; no such SACK can exist.
              //
              Sack = NULL;
          } else {
              uint32_t i = 0;
              while ((Sack = QuicRangeGetSafe(&Crypto->SparseAckRanges, i++)) != NULL &&
                  Sack->Low < (uint64_t)Left) {
                  CXPLAT_DBG_ASSERT(Sack->Low + Sack->Count <= (uint64_t)Left);
              }
          }
  
          if (Sack) {
              if ((uint64_t)Right > Sack->Low) {
                  Right = (uint32_t)Sack->Low;
              }
          } else {
              if (Right > Crypto->TlsState.BufferTotalLength) {
                  Right = Crypto->TlsState.BufferTotalLength;
              }
          }
  
          CXPLAT_DBG_ASSERT(Right >= Left);
  
          uint32_t EncryptLevelStart;
          uint32_t PacketTypeRight;
          switch (Builder->PacketType) {
          case QUIC_INITIAL:
              EncryptLevelStart = 0;
              if (Crypto->TlsState.BufferOffsetHandshake != 0) {
                  PacketTypeRight = Crypto->TlsState.BufferOffsetHandshake;
              } else {
                  PacketTypeRight = Crypto->TlsState.BufferTotalLength;
              }
              break;
          case QUIC_0_RTT_PROTECTED:
              CXPLAT_FRE_ASSERT(FALSE);
              EncryptLevelStart = 0;
              PacketTypeRight = 0; // To get build to stop complaining.
              break;
          case QUIC_HANDSHAKE:
              CXPLAT_DBG_ASSERT(Crypto->TlsState.BufferOffsetHandshake != 0);
              CXPLAT_DBG_ASSERT(Left >= Crypto->TlsState.BufferOffsetHandshake);
              EncryptLevelStart = Crypto->TlsState.BufferOffsetHandshake;
              PacketTypeRight =
                  Crypto->TlsState.BufferOffset1Rtt == 0 ?
                      Crypto->TlsState.BufferTotalLength : Crypto->TlsState.BufferOffset1Rtt;
              break;
          default:
              CXPLAT_DBG_ASSERT(Crypto->TlsState.BufferOffset1Rtt != 0);
              CXPLAT_DBG_ASSERT(Left >= Crypto->TlsState.BufferOffset1Rtt);
              EncryptLevelStart = Crypto->TlsState.BufferOffset1Rtt;
              PacketTypeRight = Crypto->TlsState.BufferTotalLength;
              break;
          }
  
          if (Right > PacketTypeRight) {
              Right = PacketTypeRight;
          }
  
          if (Left >= Right) {
              //
              // No more data to write at this encryption level, though we should
              // have at least written something. If not, then the logic that
              // decided to call this function in the first place is wrong.
              //
              break;
          }
  
          CXPLAT_DBG_ASSERT(Right > Left);
  
          uint16_t FramePayloadBytes = (uint16_t)(Right - Left);
  
          if (!QuicCryptoWriteOneFrame(
                  Crypto,
                  EncryptLevelStart,
                  Left,
                  &FramePayloadBytes,
                  Offset,
                  BufferLength,
                  Buffer,
                  Builder->Metadata)) {
              //
              // No more data could be written.
              //
              break;
          }
  
          //
          // FramePayloadBytes may have been reduced.
          //
          Right = Left + FramePayloadBytes;
  
          //
          // Move the "next" offset (RecoveryNextOffset if we are sending
          // recovery bytes or NextSendOffset otherwise) forward by the
          // number of bytes we've written. If we wrote up to the edge
          // of a SACK, skip past the SACK.
          //
  
          if (Recovery) {
              CXPLAT_DBG_ASSERT(Crypto->RecoveryNextOffset <= Right);
              Crypto->RecoveryNextOffset = Right;
              if (Sack && (uint64_t)Crypto->RecoveryNextOffset == Sack->Low) {
                  Crypto->RecoveryNextOffset += (uint32_t)Sack->Count;
              }
          }
  
          if (Crypto->NextSendOffset < Right) {
              Crypto->NextSendOffset = Right;
              if (Sack && (uint64_t)Crypto->NextSendOffset == Sack->Low) {
                  Crypto->NextSendOffset += (uint32_t)Sack->Count;
              }
          }
  
          if (Crypto->MaxSentLength < Right) {
              Crypto->MaxSentLength = Right;
          }
  
          QuicCryptoValidate(Crypto);
      }
  
      QuicCryptoDumpSendState(Crypto);
      QuicCryptoValidate(Crypto);
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  BOOLEAN
  QuicCryptoWriteFrames(
      _In_ QUIC_CRYPTO* Crypto,
      _Inout_ QUIC_PACKET_BUILDER* Builder
      )
  {
      CXPLAT_DBG_ASSERT(Builder->Metadata->FrameCount < QUIC_MAX_FRAMES_PER_PACKET);
  
      QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
  
      if (!QuicCryptoHasPendingCryptoFrame(Crypto)) {
          //
          // Likely an ACK after retransmission got us into this state. Just
          // remove the send flag and continue on.
          //
          Connection->Send.SendFlags &= ~QUIC_CONN_SEND_FLAG_CRYPTO;
          return TRUE;
      }
  
      if (Builder->PacketType !=
          QuicEncryptLevelToPacketType(QuicCryptoGetNextEncryptLevel(Crypto))) {
          //
          // Nothing to send in this packet / encryption level, just continue on.
          //
          return TRUE;
      }
  
      uint8_t PrevFrameCount = Builder->Metadata->FrameCount;
  
      uint16_t AvailableBufferLength =
          (uint16_t)Builder->Datagram->Length - Builder->EncryptionOverhead;
  
      QuicCryptoWriteCryptoFrames(
          Crypto,
          Builder,
          &Builder->DatagramLength,
          AvailableBufferLength,
          Builder->Datagram->Buffer);
  
      if (!QuicCryptoHasPendingCryptoFrame(Crypto)) {
          Connection->Send.SendFlags &= ~QUIC_CONN_SEND_FLAG_CRYPTO;
      }
  
      return Builder->Metadata->FrameCount > PrevFrameCount;
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  BOOLEAN
  QuicCryptoOnLoss(
      _In_ QUIC_CRYPTO* Crypto,
      _In_ QUIC_SENT_FRAME_METADATA* FrameMetadata
      )
  {
      uint64_t Start = FrameMetadata->CRYPTO.Offset;
      uint64_t End = Start + FrameMetadata->CRYPTO.Length;
  
      //
      // First check to make sure this data wasn't already acknowledged in a
      // different packet.
      //
  
      if (End <= Crypto->UnAckedOffset) {
          //
          // Already completely acknowledged.
          //
          return FALSE;
      }
  
      if (Start < Crypto->UnAckedOffset) {
          //
          // The 'lost' range overlaps with UNA. Move Start forward.
          //
          Start = Crypto->UnAckedOffset;
      }
  
      QUIC_SUBRANGE* Sack;
      uint32_t i = 0;
      while ((Sack = QuicRangeGetSafe(&Crypto->SparseAckRanges, i++)) != NULL &&
          Sack->Low < End) {
          if (Start < Sack->Low + Sack->Count) {
              //
              // This SACK overlaps with the 'lost' range.
              //
              if (Start >= Sack->Low) {
                  //
                  // The SACK fully covers the Start of the 'lost' range.
                  //
                  if (End <= Sack->Low + Sack->Count) {
                      //
                      // The SACK fully covers the whole 'lost' range.
                      //
                      return FALSE;
  
                  }
                  //
                  // The SACK only covers the beginning of the 'lost'
                  // range. Move Start forward to the end of the SACK.
                  //
                  Start = Sack->Low + Sack->Count;
  
              } else if (End <= Sack->Low + Sack->Count) {
                  //
                  // The SACK fully covers the End of the 'lost' range. Move
                  // the End backward to right before the SACK.
                  //
                  End = Sack->Low;
  
              } else {
                  //
                  // The SACK is fully covered by the 'lost' range. Don't do
                  // anything special in this case, because we still have stuff
                  // that needs to be retransmitted in that case.
                  //
              }
          }
      }
  
      BOOLEAN UpdatedRecoveryWindow = FALSE;
  
      //
      // Expand the recovery window to encompass the crypto frame that was lost.
      //
  
      if (Start < Crypto->RecoveryNextOffset) {
          Crypto->RecoveryNextOffset = (uint32_t)Start;
          UpdatedRecoveryWindow = TRUE;
      }
  
      if (Crypto->RecoveryEndOffset < End) {
          Crypto->RecoveryEndOffset = (uint32_t)End;
          UpdatedRecoveryWindow = TRUE;
      }
  
      if (UpdatedRecoveryWindow) {
  
          QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
  
          QuicTraceLogConnVerbose(
              RecoverCrypto,
              Connection,
              "Recovering crypto from %llu up to %llu",
              Start,
              End);
  
          if (!Crypto->InRecovery) {
              Crypto->InRecovery = TRUE;
          }
  
          BOOLEAN DataQueued =
              QuicSendSetSendFlag(
                  &Connection->Send,
                  QUIC_CONN_SEND_FLAG_CRYPTO);
  
          QuicCryptoDumpSendState(Crypto);
          QuicCryptoValidate(Crypto);
  
          return DataQueued;
      }
  
      return FALSE;
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  void
  QuicCryptoOnAck(
      _In_ QUIC_CRYPTO* Crypto,
      _In_ QUIC_SENT_FRAME_METADATA* FrameMetadata
      )
  {
      uint32_t Offset = FrameMetadata->CRYPTO.Offset;
      uint32_t Length = FrameMetadata->CRYPTO.Length;
  
      //
      // The offset directly following this frame.
      //
      uint32_t FollowingOffset = Offset + Length;
  
      CXPLAT_DBG_ASSERT(FollowingOffset <= Crypto->TlsState.BufferTotalLength);
  
      QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
  
      QuicTraceLogConnVerbose(
          AckCrypto,
          Connection,
          "Received ack for %u crypto bytes, offset=%u",
          Length,
          Offset);
  
      if (Offset <= Crypto->UnAckedOffset) {
  
          //
          // No unacknowledged bytes before this ACK. If any new
          // bytes are acknowledged then we'll advance UnAckedOffset.
          //
  
          if (Crypto->UnAckedOffset < FollowingOffset) {
  
              uint32_t OldUnAckedOffset = Crypto->UnAckedOffset;
              Crypto->UnAckedOffset = FollowingOffset;
  
              //
              // Delete any SACKs that UnAckedOffset caught up to.
              //
              QuicRangeSetMin(&Crypto->SparseAckRanges, Crypto->UnAckedOffset);
              QUIC_SUBRANGE* Sack = QuicRangeGetSafe(&Crypto->SparseAckRanges, 0);
              if (Sack && Sack->Low == (uint64_t)Crypto->UnAckedOffset) {
                  Crypto->UnAckedOffset = (uint32_t)(Sack->Low + Sack->Count);
                  QuicRangeRemoveSubranges(&Crypto->SparseAckRanges, 0, 1);
              }
  
              //
              // Drain the front of the send buffer.
              //
              uint32_t DrainLength = Crypto->UnAckedOffset - OldUnAckedOffset;
              CXPLAT_DBG_ASSERT(DrainLength <= (uint32_t)Crypto->TlsState.BufferLength);
              if ((uint32_t)Crypto->TlsState.BufferLength > DrainLength) {
                  Crypto->TlsState.BufferLength -= (uint16_t)DrainLength;
                  CxPlatMoveMemory(
                      Crypto->TlsState.Buffer,
                      Crypto->TlsState.Buffer + DrainLength,
                      Crypto->TlsState.BufferLength);
              } else {
                  Crypto->TlsState.BufferLength = 0;
              }
  
              if (Crypto->NextSendOffset < Crypto->UnAckedOffset) {
                  Crypto->NextSendOffset = Crypto->UnAckedOffset;
              }
              if (Crypto->RecoveryNextOffset < Crypto->UnAckedOffset) {
                  Crypto->RecoveryNextOffset = Crypto->UnAckedOffset;
              }
              if (Crypto->RecoveryEndOffset < Crypto->UnAckedOffset) {
                  Crypto->InRecovery = FALSE;
              }
              if (Connection->State.Connected && QuicConnIsServer(Connection) &&
                  Crypto->TlsState.BufferOffset1Rtt != 0 &&
                  Crypto->UnAckedOffset == Crypto->TlsState.BufferTotalLength) {
                  QuicConnCleanupServerResumptionState(Connection);
              }
          }
  
      } else {
  
          BOOLEAN SacksUpdated;
          QUIC_SUBRANGE* Sack =
              QuicRangeAddRange(
                  &Crypto->SparseAckRanges,
                  Offset,
                  Length,
                  &SacksUpdated);
          if (Sack == NULL) {
              QuicConnFatalError(Connection, QUIC_STATUS_OUT_OF_MEMORY, "Out of memory");
              return;
          }
  
          if (SacksUpdated) {
  
              //
              // Sack points to a new or expanded SACK, and any bytes that are
              // newly ACKed are covered by this SACK.
              //
  
              //
              // In QuicCryptoWriteFrames we assume that the starting offset
              // (NextSendOffset or RecoveryNextOffset) is not acknowledged, so
              // fix up these two offsets.
              //
              if ((uint64_t)Crypto->NextSendOffset >= Sack->Low &&
                  (uint64_t)Crypto->NextSendOffset < Sack->Low + Sack->Count) {
                  Crypto->NextSendOffset = (uint32_t)(Sack->Low + Sack->Count);
              }
              if ((uint64_t)Crypto->RecoveryNextOffset >= Sack->Low &&
                  (uint64_t)Crypto->RecoveryNextOffset < Sack->Low + Sack->Count) {
                  Crypto->RecoveryNextOffset = (uint32_t)(Sack->Low + Sack->Count);
              }
          }
      }
  
      if (!QuicCryptoHasPendingCryptoFrame(Crypto)) {
          //
          // Make the crypto stream isn't queued to send.
          //
          QuicSendClearSendFlag(
              &Connection->Send,
              QUIC_CONN_SEND_FLAG_CRYPTO);
      }
  
      QuicCryptoDumpSendState(Crypto);
      QuicCryptoValidate(Crypto);
  }
  
  //
  // Receive Interfaces
  //
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  QUIC_STATUS
  QuicCryptoProcessDataFrame(
      _In_ QUIC_CRYPTO* Crypto,
      _In_ QUIC_PACKET_KEY_TYPE KeyType,
      _In_ const QUIC_CRYPTO_EX* Frame,
      _Out_ BOOLEAN* DataReady
      )
  {
      QUIC_STATUS Status;
      QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
      uint64_t FlowControlLimit = UINT16_MAX;
  
      *DataReady = FALSE;
  
      if (Frame->Length == 0) {
  
          Status = QUIC_STATUS_SUCCESS;
  
      } else if (!Crypto->Initialized) {
  
          Status = QUIC_STATUS_SUCCESS;
          QuicTraceLogConnWarning(
              IgnoreCryptoFrame,
              Connection,
              "Ignoring received crypto after cleanup");
  
      } else {
  
          if (KeyType == QUIC_PACKET_KEY_1_RTT_OLD ||
              KeyType == QUIC_PACKET_KEY_1_RTT_NEW) {
              KeyType = QUIC_PACKET_KEY_1_RTT; // Treat them all as the same
          }
  
          CXPLAT_DBG_ASSERT(KeyType <= Crypto->TlsState.ReadKey);
          if (KeyType < Crypto->TlsState.ReadKey) {
              Status = QUIC_STATUS_SUCCESS; // Old, likely retransmitted data.
              goto Error;
          }
  
          //
          // Write the received data (could be duplicate) to the stream buffer. The
          // stream buffer will indicate if there is data to process.
          //
          Status =
              QuicRecvBufferWrite(
                  &Crypto->RecvBuffer,
                  Crypto->RecvEncryptLevelStartOffset + Frame->Offset,
                  (uint16_t)Frame->Length,
                  Frame->Data,
                  &FlowControlLimit,
                  DataReady);
          if (QUIC_FAILED(Status)) {
              if (Status == QUIC_STATUS_BUFFER_TOO_SMALL) {
                  QuicTraceEvent(
                      ConnError,
                      "[conn][%p] ERROR, %s.",
                      Connection,
                      "Tried to write beyond crypto flow control limit.");
                  QuicConnTransportError(Connection, QUIC_ERROR_CRYPTO_BUFFER_EXCEEDED);
              }
              goto Error;
          }
      }
  
      QuicTraceLogConnVerbose(
          RecvCrypto,
          Connection,
          "Received %hu crypto bytes, offset=%llu Ready=%hhu",
          (uint16_t)Frame->Length,
          Frame->Offset,
          *DataReady);
  
  Error:
  
      return Status;
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  QUIC_STATUS
  QuicCryptoProcessFrame(
      _In_ QUIC_CRYPTO* Crypto,
      _In_ QUIC_PACKET_KEY_TYPE KeyType,
      _In_ const QUIC_CRYPTO_EX* const Frame
      )
  {
      QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
      BOOLEAN DataReady;
  
      Status =
          QuicCryptoProcessDataFrame(
              Crypto, KeyType, Frame, &DataReady);
      if (QUIC_FAILED(Status) || !DataReady) {
          goto Error;
      }
  
      Status = QuicCryptoProcessData(Crypto, FALSE);
      if (QUIC_FAILED(Status)) {
          goto Error;
      }
  
      QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
      if (Connection->State.ClosedLocally) {
          //
          // If processing the received frame caused us to close the
          // connection, make sure to stop processing anything else in the
          // packet.
          //
          Status = QUIC_STATUS_INVALID_STATE;
      }
  
  Error:
  
      return Status;
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  BOOLEAN
  QuicConnReceiveTP(
      _In_ QUIC_CONNECTION* Connection,
      _In_ uint16_t TPLength,
      _In_reads_(TPLength) const uint8_t* TPBuffer
      )
  {
      CXPLAT_DBG_ASSERT(!QuicConnIsServer(Connection));
  
      if (!QuicCryptoTlsDecodeTransportParameters(
              Connection,
              TRUE,
              TPBuffer,
              TPLength,
              &Connection->PeerTransportParams)) {
          return FALSE;
      }
  
      (void)QuicConnProcessPeerTransportParameters(Connection, FALSE);
  
      return TRUE;
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  void
  QuicCryptoProcessTlsCompletion(
      _In_ QUIC_CRYPTO* Crypto
      )
  {
      QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
  
      if (Crypto->ResultFlags & CXPLAT_TLS_RESULT_ERROR) {
          QuicTraceEvent(
              ConnErrorStatus,
              "[conn][%p] ERROR, %u, %s.",
              Connection,
              Crypto->TlsState.AlertCode,
              "Received alert from TLS");
          QuicConnTransportError(
              Connection,
              QUIC_ERROR_CRYPTO_ERROR(0xFF & Crypto->TlsState.AlertCode));
          return;
      }
  
      QuicCryptoValidate(Crypto);
  
      if (Crypto->ResultFlags & CXPLAT_TLS_RESULT_EARLY_DATA_ACCEPT) {
          QuicTraceLogConnInfo(
              ZeroRttAccepted,
              Connection,
              "0-RTT accepted");
          CXPLAT_TEL_ASSERT(Crypto->TlsState.EarlyDataState == CXPLAT_TLS_EARLY_DATA_ACCEPTED);
      }
  
      if (Crypto->ResultFlags & CXPLAT_TLS_RESULT_EARLY_DATA_REJECT) {
          QuicTraceLogConnInfo(
              ZeroRttRejected,
              Connection,
              "0-RTT rejected");
          CXPLAT_TEL_ASSERT(Crypto->TlsState.EarlyDataState != CXPLAT_TLS_EARLY_DATA_ACCEPTED);
          if (!QuicConnIsServer(Connection)) {
              QuicCryptoDiscardKeys(Crypto, QUIC_PACKET_KEY_0_RTT);
              QuicLossDetectionOnZeroRttRejected(&Connection->LossDetection);
          } else {
              QuicConnDiscardDeferred0Rtt(Connection);
          }
      }
  
      if (Crypto->ResultFlags & CXPLAT_TLS_RESULT_WRITE_KEY_UPDATED) {
          QuicTraceEvent(
              ConnWriteKeyUpdated,
              "[conn][%p] Write Key Updated, %hhu.",
              Connection,
              Crypto->TlsState.WriteKey);
          CXPLAT_DBG_ASSERT(Crypto->TlsState.WriteKey <= QUIC_PACKET_KEY_1_RTT);
          _Analysis_assume_(Crypto->TlsState.WriteKey >= 0);
          CXPLAT_TEL_ASSERT(Crypto->TlsState.WriteKeys[Crypto->TlsState.WriteKey] != NULL);
          if (Crypto->TlsState.WriteKey == QUIC_PACKET_KEY_HANDSHAKE &&
              !QuicConnIsServer(Connection)) {
              //
              // Per spec, client MUST discard Initial keys when it starts
              // encrypting packets with handshake keys.
              //
              QuicCryptoDiscardKeys(Crypto, QUIC_PACKET_KEY_INITIAL);
          }
          if (Crypto->TlsState.WriteKey == QUIC_PACKET_KEY_1_RTT) {
              if (!QuicConnIsServer(Connection)) {
                  //
                  // The client has the 1-RTT keys so we can get rid of 0-RTT
                  // keys.
                  //
                  QuicCryptoDiscardKeys(Crypto, QUIC_PACKET_KEY_0_RTT);
              }
              //
              // We have the 1-RTT key so we can start sending application data.
              //
              QuicSendQueueFlush(&Connection->Send, REASON_NEW_KEY);
          }
  
          if (QuicConnIsServer(Connection)) {
              if (Crypto->TlsState.WriteKey == QUIC_PACKET_KEY_1_RTT) {
                  //
                  // Done with the server's flight.
                  //
                  Connection->Stats.Handshake.ServerFlight1Bytes = Crypto->TlsState.BufferOffset1Rtt;
              }
          } else {
              if (Crypto->TlsState.WriteKey == QUIC_PACKET_KEY_HANDSHAKE) {
                  //
                  // Done with the client's Initial flight.
                  //
                  Connection->Stats.Handshake.ClientFlight1Bytes = Crypto->TlsState.BufferOffsetHandshake;
              }
  
              if (Crypto->TlsState.WriteKey == QUIC_PACKET_KEY_1_RTT) {
                  //
                  // Done with the client's second flight, consisting of Handshake packets.
                  //
                  Connection->Stats.Handshake.ClientFlight2Bytes =
                      Crypto->TlsState.BufferOffset1Rtt - Crypto->TlsState.BufferOffsetHandshake;
              }
          }
      }
  
      if (Crypto->ResultFlags & CXPLAT_TLS_RESULT_READ_KEY_UPDATED) {
          //
          // Make sure there isn't any data received past the current Recv offset
          // at the previous encryption level.
          //
          if (QuicRecvBufferHasUnreadData(&Crypto->RecvBuffer)) {
              QuicTraceEvent(
                  ConnError,
                  "[conn][%p] ERROR, %s.",
                  Connection,
                  "Leftover crypto data in previous encryption level.");
              QuicConnTransportError(Connection, QUIC_ERROR_PROTOCOL_VIOLATION);
              return;
          }
  
          Crypto->RecvEncryptLevelStartOffset = Crypto->RecvTotalConsumed;
          QuicTraceEvent(
              ConnReadKeyUpdated,
              "[conn][%p] Read Key Updated, %hhu.",
              Connection,
              Crypto->TlsState.ReadKey);
  
          //
          // If we have the read key, we must also have the write key.
          //
          CXPLAT_DBG_ASSERT(Crypto->TlsState.ReadKey <= QUIC_PACKET_KEY_1_RTT);
          _Analysis_assume_(Crypto->TlsState.ReadKey >= 0);
          CXPLAT_TEL_ASSERT(Crypto->TlsState.WriteKey >= Crypto->TlsState.ReadKey);
          CXPLAT_TEL_ASSERT(Crypto->TlsState.ReadKeys[Crypto->TlsState.ReadKey] != NULL);
  
          if (QuicConnIsServer(Connection)) {
              if (Crypto->TlsState.ReadKey == QUIC_PACKET_KEY_HANDSHAKE) {
                  //
                  // Done with the client's Initial flight.
                  //
                  Connection->Stats.Handshake.ClientFlight1Bytes = Crypto->RecvTotalConsumed;
              }
  
              if (Crypto->TlsState.ReadKey == QUIC_PACKET_KEY_1_RTT) {
                  //
                  // Done with the client's second flight, consisting of Handshake packets.
                  //
                  Connection->Stats.Handshake.ClientFlight2Bytes =
                      Crypto->RecvTotalConsumed - Connection->Stats.Handshake.ClientFlight1Bytes;
              }
          } else {
              if (Crypto->TlsState.ReadKey == QUIC_PACKET_KEY_1_RTT) {
                  //
                  // Done with the server's flight.
                  //
                  Connection->Stats.Handshake.ServerFlight1Bytes = Crypto->RecvTotalConsumed;
              }
          }
  
          if (Connection->Stats.Timing.InitialFlightEnd == 0) {
              //
              // Any read key change means we are done with the initial flight.
              //
              Connection->Stats.Timing.InitialFlightEnd = CxPlatTimeUs64();
          }
  
          if (Crypto->TlsState.ReadKey == QUIC_PACKET_KEY_1_RTT) {
              //
              // Once TLS is consuming 1-RTT data, we are done with the Handshake
              // flight.
              //
              Connection->Stats.Timing.HandshakeFlightEnd = CxPlatTimeUs64();
          }
      }
  
      if (Crypto->ResultFlags & CXPLAT_TLS_RESULT_DATA) {
  #ifdef CXPLAT_TLS_SECRETS_SUPPORT
          //
          // Parse the client initial to populate the TlsSecrets with the
          // ClientRandom
          //
          if (Connection->TlsSecrets != NULL &&
              !QuicConnIsServer(Connection) &&
              Crypto->TlsState.WriteKey == QUIC_PACKET_KEY_INITIAL &&
              Crypto->TlsState.BufferLength > 0) {
              QUIC_NEW_CONNECTION_INFO Info = { 0 };
              QuicCryptoTlsReadInitial(
                  Connection,
                  Crypto->TlsState.Buffer,
                  Crypto->TlsState.BufferLength,
                  &Info,
                  Connection->TlsSecrets);
              //
              // Connection is done with TlsSecrets, clean up.
              //
              Connection->TlsSecrets = NULL;
          }
  #endif
          QuicSendSetSendFlag(
              &QuicCryptoGetConnection(Crypto)->Send,
              QUIC_CONN_SEND_FLAG_CRYPTO);
          QuicCryptoDumpSendState(Crypto);
          QuicCryptoValidate(Crypto);
      }
  
      if (Crypto->ResultFlags & CXPLAT_TLS_RESULT_HANDSHAKE_COMPLETE) {
          CXPLAT_DBG_ASSERT(!(Crypto->ResultFlags & CXPLAT_TLS_RESULT_ERROR));
          CXPLAT_TEL_ASSERT(!Connection->State.Connected);
  
          QuicTraceEvent(
              ConnHandshakeComplete,
              "[conn][%p] Handshake complete",
              Connection);
  
          //
          // We should have the 1-RTT keys by connection complete time.
          //
          CXPLAT_TEL_ASSERT(Crypto->TlsState.ReadKeys[QUIC_PACKET_KEY_1_RTT] != NULL);
          CXPLAT_TEL_ASSERT(Crypto->TlsState.WriteKeys[QUIC_PACKET_KEY_1_RTT] != NULL);
  
          if (QuicConnIsServer(Connection)) {
              //
              // Handshake is confirmed on the server side as soon as it completes.
              //
              QuicTraceLogConnInfo(
                  HandshakeConfirmedServer,
                  Connection,
                  "Handshake confirmed (server)");
              QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_HANDSHAKE_DONE);
              QuicCryptoHandshakeConfirmed(&Connection->Crypto);
          }
  
          //
          // Only set the connected flag after we do the confirmation code path
          // above so that TLS state isn't prematurely destroyed (before the
          // CONNECTED event is indicated to the app).
          //
          Connection->State.Connected = TRUE;
          QuicPerfCounterIncrement(QUIC_PERF_COUNTER_CONN_CONNECTED);
  
          QuicConnGenerateNewSourceCids(Connection, FALSE);
  
          CXPLAT_DBG_ASSERT(Crypto->TlsState.NegotiatedAlpn != NULL);
          if (!QuicConnIsServer(Connection)) {
              //
              // Currently, NegotiatedAlpn points into TLS state memory, which
              // doesn't live as long as the connection. Update it to point to the
              // configuration state memory instead.
              //
              Crypto->TlsState.NegotiatedAlpn =
                  CxPlatTlsAlpnFindInList(
                      Connection->Configuration->AlpnListLength,
                      Connection->Configuration->AlpnList,
                      Crypto->TlsState.NegotiatedAlpn[0],
                      Crypto->TlsState.NegotiatedAlpn + 1);
              CXPLAT_TEL_ASSERT(Crypto->TlsState.NegotiatedAlpn != NULL);
          }
  
          QUIC_CONNECTION_EVENT Event;
          Event.Type = QUIC_CONNECTION_EVENT_CONNECTED;
          Event.CONNECTED.SessionResumed = Crypto->TlsState.SessionResumed;
          Event.CONNECTED.NegotiatedAlpnLength = Crypto->TlsState.NegotiatedAlpn[0];
          Event.CONNECTED.NegotiatedAlpn = Crypto->TlsState.NegotiatedAlpn + 1;
          QuicTraceLogConnVerbose(
              IndicateConnected,
              Connection,
              "Indicating QUIC_CONNECTION_EVENT_CONNECTED (Resume=%hhu)",
              Event.CONNECTED.SessionResumed);
          (void)QuicConnIndicateEvent(Connection, &Event);
          if (Crypto->TlsState.SessionResumed) {
              QuicPerfCounterIncrement(QUIC_PERF_COUNTER_CONN_RESUMED);
          }
          Connection->Stats.ResumptionSucceeded = Crypto->TlsState.SessionResumed;
  
          QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_PMTUD);
  
          if (QuicConnIsServer(Connection) &&
              Crypto->TlsState.BufferOffset1Rtt != 0 &&
              Crypto->UnAckedOffset == Crypto->TlsState.BufferTotalLength) {
              QuicConnCleanupServerResumptionState(Connection);
          }
      }
  
      QuicCryptoValidate(Crypto);
  
      if (Crypto->ResultFlags & CXPLAT_TLS_RESULT_READ_KEY_UPDATED) {
          QuicConnFlushDeferred(Connection);
      }
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  void
  QuicCryptoProcessDataComplete(
      _In_ QUIC_CRYPTO* Crypto,
      _In_ uint32_t RecvBufferConsumed
      )
  {
      if (RecvBufferConsumed != 0) {
          Crypto->RecvTotalConsumed += RecvBufferConsumed;
          QuicTraceLogConnVerbose(
              DrainCrypto,
              QuicCryptoGetConnection(Crypto),
              "Draining %u crypto bytes",
              RecvBufferConsumed);
          QuicRecvBufferDrain(&Crypto->RecvBuffer, RecvBufferConsumed);
      }
  
      QuicCryptoValidate(Crypto);
  
      if (!Crypto->CertValidationPending) {
          QuicCryptoProcessTlsCompletion(Crypto);
      }
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  void
  QuicCryptoCustomCertValidationComplete(
      _In_ QUIC_CRYPTO* Crypto,
      _In_ BOOLEAN Result
      )
  {
      CXPLAT_TEL_ASSERT(Crypto->CertValidationPending);
      if (!Crypto->CertValidationPending) {
          return;
      }
  
      Crypto->CertValidationPending = FALSE;
      if (Result) {
          QuicTraceLogConnInfo(
              CustomCertValidationSuccess,
              QuicCryptoGetConnection(Crypto),
              "Custom cert validation succeeded");
          QuicCryptoProcessTlsCompletion(Crypto);
  
      } else {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              QuicCryptoGetConnection(Crypto),
              "Custom cert validation failed.");
          QuicConnTransportError(
              QuicCryptoGetConnection(Crypto),
              QUIC_ERROR_CRYPTO_ERROR(0xFF & CXPLAT_TLS_ALERT_CODE_BAD_CERTIFICATE));
      }
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  QUIC_STATUS
  QuicCryptoProcessData(
      _In_ QUIC_CRYPTO* Crypto,
      _In_ BOOLEAN IsClientInitial
      )
  {
      QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
      uint32_t BufferCount = 1;
      QUIC_BUFFER Buffer;
  
      if (IsClientInitial) {
          Buffer.Length = 0;
          Buffer.Buffer = NULL;
  
      } else {
          uint64_t BufferOffset;
          BOOLEAN DataAvailable =
              QuicRecvBufferRead(
                  &Crypto->RecvBuffer,
                  &BufferOffset,
                  &BufferCount,
                  &Buffer);
  
          UNREFERENCED_PARAMETER(DataAvailable);
          CXPLAT_TEL_ASSERT(DataAvailable);
          CXPLAT_DBG_ASSERT(BufferCount == 1);
  
          QUIC_CONNECTION* Connection = QuicCryptoGetConnection(Crypto);
  
          Buffer.Length =
              QuicCrytpoTlsGetCompleteTlsMessagesLength(
                  Buffer.Buffer, Buffer.Length);
          if (Buffer.Length == 0) {
              QuicTraceLogConnVerbose(
                  CryptoNotReady,
                  Connection,
                  "No complete TLS messages to process");
              goto Error;
          }
  
          if (QuicConnIsServer(Connection) && !Connection->State.ListenerAccepted) {
              //
              // Preprocess the TLS ClientHello to find the ALPN (and optionally
              // SNI) to match the connection to a listener.
              //
              CXPLAT_DBG_ASSERT(BufferOffset == 0);
              QUIC_NEW_CONNECTION_INFO Info = {0};
              Status =
                  QuicCryptoTlsReadInitial(
                      Connection,
                      Buffer.Buffer,
                      Buffer.Length,
                      &Info
  #ifdef CXPLAT_TLS_SECRETS_SUPPORT
                      //
                      // On server, TLS is initialized before the listener
                      // is told about the connection, so TlsSecrets is still
                      // NULL.
                      //
                      ,  NULL
  #endif
                      );
              if (QUIC_FAILED(Status)) {
                  QuicConnTransportError(
                      Connection,
                      QUIC_ERROR_CRYPTO_HANDSHAKE_FAILURE);
                  goto Error;
              } else if (Status == QUIC_STATUS_PENDING) {
                  //
                  // The full ClientHello hasn't been received yet.
                  //
                  goto Error;
              }
  
              Status =
                  QuicConnProcessPeerTransportParameters(Connection, FALSE);
              if (Status == QUIC_STATUS_VER_NEG_ERROR) {
                  //
                  // Communicate error up the stack to perform Incompatible
                  // Version Negotiation.
                  //
                  goto Error;
              }
  
              QuicRecvBufferDrain(&Crypto->RecvBuffer, 0);
              QuicCryptoValidate(Crypto);
  
              Info.QuicVersion = Connection->Stats.QuicVersion;
              Info.LocalAddress = &Connection->Paths[0].LocalAddress;
              Info.RemoteAddress = &Connection->Paths[0].RemoteAddress;
              Info.CryptoBufferLength = Buffer.Length;
              Info.CryptoBuffer = Buffer.Buffer;
  
              QuicBindingAcceptConnection(
                  Connection->Paths[0].Binding,
                  Connection,
                  &Info);
              return Status;
          }
      }
  
      CXPLAT_DBG_ASSERT(Crypto->TLS != NULL);
      if (Crypto->TLS == NULL) {
          //
          // The listener still hasn't given us the security config to initialize
          // TLS with yet.
          //
          goto Error;
      }
  
      QuicCryptoValidate(Crypto);
  
      Crypto->ResultFlags =
          CxPlatTlsProcessData(
              Crypto->TLS,
              CXPLAT_TLS_CRYPTO_DATA,
              Buffer.Buffer,
              &Buffer.Length,
              &Crypto->TlsState);
  
      QuicCryptoProcessDataComplete(Crypto, Buffer.Length);
  
      return Status;
  
  Error:
  
      QuicRecvBufferDrain(&Crypto->RecvBuffer, 0);
      QuicCryptoValidate(Crypto);
  
      return Status;
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  QUIC_STATUS
  QuicCryptoProcessAppData(
      _In_ QUIC_CRYPTO* Crypto,
      _In_ uint32_t DataLength,
      _In_reads_bytes_(DataLength)
          const uint8_t* AppData
      )
  {
      QUIC_STATUS Status;
  
      Crypto->ResultFlags =
          CxPlatTlsProcessData(
              Crypto->TLS,
              CXPLAT_TLS_TICKET_DATA,
              AppData,
              &DataLength,
              &Crypto->TlsState);
      if (Crypto->ResultFlags & CXPLAT_TLS_RESULT_ERROR) {
          if (Crypto->TlsState.AlertCode != 0) {
              Status = QUIC_STATUS_TLS_ALERT(Crypto->TlsState.AlertCode);
          } else {
              Status = QUIC_STATUS_INTERNAL_ERROR;
          }
          goto Error;
      }
  
      QuicCryptoProcessDataComplete(Crypto, 0);
  
      Status = QUIC_STATUS_SUCCESS;
  
  Error:
      return Status;
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  QUIC_STATUS
  QuicCryptoGenerateNewKeys(
      _In_ QUIC_CONNECTION* Connection
      )
  {
      QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
      QUIC_PACKET_KEY** NewReadKey = &Connection->Crypto.TlsState.ReadKeys[QUIC_PACKET_KEY_1_RTT_NEW];
      QUIC_PACKET_KEY** NewWriteKey = &Connection->Crypto.TlsState.WriteKeys[QUIC_PACKET_KEY_1_RTT_NEW];
  
      //
      // Detect torn key updates; either both keys exist, or they don't.
      //
      CXPLAT_DBG_ASSERT(!((*NewReadKey == NULL) ^ (*NewWriteKey == NULL)));
  
      if (*NewReadKey == NULL) {
          //
          // Make New packet key.
          //
          Status =
              QuicPacketKeyUpdate(
                  Connection->Crypto.TlsState.ReadKeys[QUIC_PACKET_KEY_1_RTT],
                  NewReadKey);
          if (QUIC_FAILED(Status)) {
              QuicTraceEvent(
                  ConnErrorStatus,
                  "[conn][%p] ERROR, %u, %s.",
                  Connection,
                  Status,
                  "Failed to update read packet key.");
              goto Error;
          }
  
          Status =
              QuicPacketKeyUpdate(
                  Connection->Crypto.TlsState.WriteKeys[QUIC_PACKET_KEY_1_RTT],
                  NewWriteKey);
          if (QUIC_FAILED(Status)) {
              QuicTraceEvent(
                  ConnErrorStatus,
                  "[conn][%p] ERROR, %u, %s.",
                  Connection,
                  Status,
                  "Failed to update write packet key");
              goto Error;
          }
      }
  
  Error:
  
      if (QUIC_FAILED(Status)) {
          QuicPacketKeyFree(*NewReadKey);
          *NewReadKey = NULL;
      } else {
          QuicTraceEvent(
              ConnNewPacketKeys,
              "[conn][%p] New packet keys created successfully.",
              Connection);
      }
  
      return Status;
  }
  
  _IRQL_requires_max_(PASSIVE_LEVEL)
  void
  QuicCryptoUpdateKeyPhase(
      _In_ QUIC_CONNECTION* Connection,
      _In_ BOOLEAN LocalUpdate
      )
  {
      //
      // Free the old read key state (if it exists).
      //
      QUIC_PACKET_KEY** Old = &Connection->Crypto.TlsState.ReadKeys[QUIC_PACKET_KEY_1_RTT_OLD];
      QuicPacketKeyFree(*Old);
  
      QUIC_PACKET_KEY** Current = &Connection->Crypto.TlsState.ReadKeys[QUIC_PACKET_KEY_1_RTT];
      QUIC_PACKET_KEY** New = &Connection->Crypto.TlsState.ReadKeys[QUIC_PACKET_KEY_1_RTT_NEW];
  
      //
      // Move the header key forward.
      //
      (*New)->HeaderKey = (*Current)->HeaderKey;
      //
      // Don't copy the header key backwards.
      //
      (*Current)->HeaderKey = NULL;
  
      //
      // Shift the current and new read keys down.
      //
      *Old = *Current;
      *Current = *New;
      *New = NULL;
  
      //
      // Free the old write key state (if it exists).
      //
      Old = &Connection->Crypto.TlsState.WriteKeys[QUIC_PACKET_KEY_1_RTT_OLD];
      QuicPacketKeyFree(*Old);
  
      Current = &Connection->Crypto.TlsState.WriteKeys[QUIC_PACKET_KEY_1_RTT];
      New = &Connection->Crypto.TlsState.WriteKeys[QUIC_PACKET_KEY_1_RTT_NEW];
  
      //
      // Move the header key forward.
      //
      (*New)->HeaderKey = (*Current)->HeaderKey;
      //
      // Don't copy the header key backwards.
      //
      (*Current)->HeaderKey = NULL;
  
      //
      // Shift the current and new write keys down.
      //
      *Old = *Current;
      *Current = *New;
      *New = NULL;
  
      if (Connection->Stats.Misc.KeyUpdateCount < UINT32_MAX ) {
          Connection->Stats.Misc.KeyUpdateCount++;
      }
  
      QUIC_PACKET_SPACE* PacketSpace = Connection->Packets[QUIC_ENCRYPT_LEVEL_1_RTT];
  
      UNREFERENCED_PARAMETER(LocalUpdate);
      QuicTraceEvent(
          ConnKeyPhaseChange,
          "[conn][%p] Key phase change (locally initiated=%hhu).",
          Connection,
          LocalUpdate);
  
      PacketSpace->WriteKeyPhaseStartPacketNumber = Connection->Send.NextPacketNumber;
      PacketSpace->CurrentKeyPhase = !PacketSpace->CurrentKeyPhase;
  
      //
      // Reset the read packet space so any new packet will be properly detected.
      //
      PacketSpace->ReadKeyPhaseStartPacketNumber = UINT64_MAX;
  
      PacketSpace->AwaitingKeyPhaseConfirmation = TRUE;
  
      PacketSpace->CurrentKeyPhaseBytesSent = 0;
  }
  
  QUIC_STATUS
  QuicCryptoEncodeServerTicket(
      _In_opt_ QUIC_CONNECTION* Connection,
      _In_ uint32_t QuicVersion,
      _In_ uint32_t AppDataLength,
      _In_reads_bytes_opt_(AppDataLength)
          const uint8_t* const AppResumptionData,
      _In_ const QUIC_TRANSPORT_PARAMETERS* HandshakeTP,
      _In_ uint8_t AlpnLength,
      _In_reads_bytes_(AlpnLength)
          const uint8_t* const NegotiatedAlpn,
      _Outptr_result_buffer_(*TicketLength)
          uint8_t** Ticket,
      _Out_ uint32_t* TicketLength
      )
  {
      QUIC_STATUS Status;
      uint32_t EncodedTPLength = 0;
      uint8_t* TicketBuffer = NULL;
      const uint8_t* EncodedHSTP = NULL;
  
      *Ticket = NULL;
      *TicketLength = 0;
  
      //
      // Don't use a deep copy here because only a subset of
      // transport parameters are copied.
      //
      QUIC_TRANSPORT_PARAMETERS HSTPCopy = *HandshakeTP;
      HSTPCopy.Flags &= (
          QUIC_TP_FLAG_ACTIVE_CONNECTION_ID_LIMIT |
          QUIC_TP_FLAG_INITIAL_MAX_DATA |
          QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_BIDI_LOCAL |
          QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_BIDI_REMOTE |
          QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_UNI |
          QUIC_TP_FLAG_INITIAL_MAX_STRMS_BIDI |
          QUIC_TP_FLAG_INITIAL_MAX_STRMS_UNI);
  
      EncodedHSTP =
          QuicCryptoTlsEncodeTransportParameters(
              Connection,
              TRUE,
              &HSTPCopy,
              NULL,
              &EncodedTPLength);
      if (EncodedHSTP == NULL) {
          Status = QUIC_STATUS_OUT_OF_MEMORY;
          goto Error;
      }
  
      //
      // Adjust TP buffer for TLS header, if present.
      //
      EncodedTPLength -= CxPlatTlsTPHeaderSize;
  
      uint32_t TotalTicketLength =
          (uint32_t)(QuicVarIntSize(CXPLAT_TLS_RESUMPTION_TICKET_VERSION) +
          sizeof(QuicVersion) +
          QuicVarIntSize(AlpnLength) +
          QuicVarIntSize(EncodedTPLength) +
          QuicVarIntSize(AppDataLength) +
          AlpnLength +
          EncodedTPLength +
          AppDataLength);
  
      TicketBuffer = CXPLAT_ALLOC_NONPAGED(TotalTicketLength, QUIC_POOL_SERVER_CRYPTO_TICKET);
      if (TicketBuffer == NULL) {
          QuicTraceEvent(
              AllocFailure,
              "Allocation of '%s' failed. (%llu bytes)",
              "Server resumption ticket",
              TotalTicketLength);
          Status = QUIC_STATUS_OUT_OF_MEMORY;
          goto Error;
      }
  
      //
      // Encoded ticket format is as follows:
      //   Ticket Version (QUIC_VAR_INT) [1..4]
      //   Quic Version (network byte order) [4]
      //   Negotiated ALPN length (QUIC_VAR_INT) [1..2]
      //   Transport Parameters length (QUIC_VAR_INT) [1..2]
      //   App Ticket length (QUIC_VAR_INT) [1..2]
      //   Negotiated ALPN [...]
      //   Transport Parameters [...]
      //   App Ticket (omitted if length is zero) [...]
      //
  
      _Analysis_assume_(sizeof(*TicketBuffer) >= 8);
      uint8_t* TicketCursor = QuicVarIntEncode(CXPLAT_TLS_RESUMPTION_TICKET_VERSION, TicketBuffer);
      CxPlatCopyMemory(TicketCursor, &QuicVersion, sizeof(QuicVersion));
      TicketCursor += sizeof(QuicVersion);
      TicketCursor = QuicVarIntEncode(AlpnLength, TicketCursor);
      TicketCursor = QuicVarIntEncode(EncodedTPLength, TicketCursor);
      TicketCursor = QuicVarIntEncode(AppDataLength, TicketCursor);
      CxPlatCopyMemory(TicketCursor, NegotiatedAlpn, AlpnLength);
      TicketCursor += AlpnLength;
      CxPlatCopyMemory(TicketCursor, EncodedHSTP + CxPlatTlsTPHeaderSize, EncodedTPLength);
      TicketCursor += EncodedTPLength;
      if (AppDataLength > 0) {
          CxPlatCopyMemory(TicketCursor, AppResumptionData, AppDataLength);
          TicketCursor += AppDataLength;
      }
      CXPLAT_DBG_ASSERT(TicketCursor == TicketBuffer + TotalTicketLength);
  
      *Ticket = TicketBuffer;
      *TicketLength = TotalTicketLength;
  
      Status = QUIC_STATUS_SUCCESS;
  
  Error:
  
      if (EncodedHSTP != NULL) {
          CXPLAT_FREE(EncodedHSTP, QUIC_POOL_TLS_TRANSPARAMS);
      }
  
      return Status;
  }
  
  QUIC_STATUS
  QuicCryptoDecodeServerTicket(
      _In_ QUIC_CONNECTION* Connection,
      _In_ uint16_t TicketLength,
      _In_reads_bytes_(TicketLength)
          const uint8_t* Ticket,
      _In_ const uint8_t* AlpnList,
      _In_ uint16_t AlpnListLength,
      _Out_ QUIC_TRANSPORT_PARAMETERS* DecodedTP,
      _Outptr_result_buffer_maybenull_(*AppDataLength)
          const uint8_t** AppData,
      _Out_ uint32_t* AppDataLength
      )
  {
      QUIC_STATUS Status = QUIC_STATUS_INVALID_PARAMETER;
      uint16_t Offset = 0;
      QUIC_VAR_INT TicketVersion = 0, AlpnLength = 0, TPLength = 0, AppTicketLength = 0;
  
      *AppData = NULL;
      *AppDataLength = 0;
  
      if (!QuicVarIntDecode(TicketLength, Ticket, &Offset, &TicketVersion)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket version failed to decode");
          goto Error;
      }
      if (TicketVersion != CXPLAT_TLS_RESUMPTION_TICKET_VERSION) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket version unsupported");
          goto Error;
      }
  
      if (TicketLength < Offset + sizeof(uint32_t)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket too short to hold QUIC version");
          goto Error;
      }
  
      uint32_t QuicVersion;
      memcpy(&QuicVersion, Ticket + Offset, sizeof(QuicVersion));
      if (!QuicVersionNegotiationExtIsVersionClientSupported(Connection, QuicVersion)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket for unsupported QUIC version");
          goto Error;
      }
      Offset += sizeof(QuicVersion);
  
      if (!QuicVarIntDecode(TicketLength, Ticket, &Offset, &AlpnLength)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket ALPN length failed to decode");
          goto Error;
      }
  
      if (!QuicVarIntDecode(TicketLength, Ticket, &Offset, &TPLength)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket TP length failed to decode");
          goto Error;
      }
  
      if (!QuicVarIntDecode(TicketLength, Ticket, &Offset, &AppTicketLength)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket app data length failed to decode");
          goto Error;
      }
  
      if (TicketLength < Offset + AlpnLength) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket too small for ALPN");
          goto Error;
      }
  
      if (CxPlatTlsAlpnFindInList(AlpnListLength, AlpnList, (uint8_t)AlpnLength, Ticket + Offset) == NULL) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket ALPN not present in ALPN list");
          goto Error;
      }
      Offset += (uint16_t)AlpnLength;
  
      if (TicketLength < Offset + TPLength) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket too small for Transport Parameters");
          goto Error;
      }
  
      if (!QuicCryptoTlsDecodeTransportParameters(
              Connection,
              TRUE,   // IsServerTP
              Ticket + Offset,
              (uint16_t)TPLength,
              DecodedTP)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket TParams failed to decode");
          goto Error;
      }
      Offset += (uint16_t)TPLength;
  
      if (TicketLength == Offset + AppTicketLength) {
          Status = QUIC_STATUS_SUCCESS;
          *AppDataLength = (uint32_t)AppTicketLength;
          if (AppTicketLength > 0) {
              *AppData = Ticket + Offset;
          }
      } else {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket app data length corrupt");
      }
  
  Error:
      return Status;
  }
  
  QUIC_STATUS
  QuicCryptoEncodeClientTicket(
      _In_opt_ QUIC_CONNECTION* Connection,
      _In_ uint32_t TicketLength,
      _In_reads_bytes_(TicketLength)
          const uint8_t* Ticket,
      _In_ const QUIC_TRANSPORT_PARAMETERS* ServerTP,
      _In_ uint32_t QuicVersion,
      _Outptr_result_buffer_(*ClientTicketLength)
          const uint8_t** ClientTicket,
      _Out_ uint32_t* ClientTicketLength
      )
  {
      QUIC_STATUS Status;
      uint32_t EncodedTPLength = 0;
      uint8_t* ClientTicketBuffer = NULL;
      const uint8_t* EncodedServerTP = NULL;
  
      *ClientTicket = NULL;
      *ClientTicketLength = 0;
  
      QUIC_TRANSPORT_PARAMETERS ServerTPCopy = *ServerTP;
      //
      // The client must remember all TPs it can process except for the following
      //
      ServerTPCopy.Flags &= ~(
          QUIC_TP_FLAG_ACK_DELAY_EXPONENT |
          QUIC_TP_FLAG_MAX_ACK_DELAY |
          QUIC_TP_FLAG_INITIAL_SOURCE_CONNECTION_ID |
          QUIC_TP_FLAG_ORIGINAL_DESTINATION_CONNECTION_ID |
          QUIC_TP_FLAG_PREFERRED_ADDRESS |
          QUIC_TP_FLAG_RETRY_SOURCE_CONNECTION_ID |
          QUIC_TP_FLAG_STATELESS_RESET_TOKEN);
  
      EncodedServerTP =
          QuicCryptoTlsEncodeTransportParameters(
              Connection,
              TRUE,
              &ServerTPCopy,
              NULL,
              &EncodedTPLength);
      if (EncodedServerTP == NULL) {
          Status = QUIC_STATUS_OUT_OF_MEMORY;
          goto Error;
      }
  
      //
      // Adjust for any TLS header potentially added to the TP buffer
      //
      EncodedTPLength -= CxPlatTlsTPHeaderSize;
  
      uint32_t ClientTicketBufferLength =
          (uint32_t)(QuicVarIntSize(CXPLAT_TLS_RESUMPTION_CLIENT_TICKET_VERSION) +
          sizeof(QuicVersion) +
          QuicVarIntSize(EncodedTPLength) +
          QuicVarIntSize(TicketLength) +
          EncodedTPLength +
          TicketLength);
  
      ClientTicketBuffer = CXPLAT_ALLOC_NONPAGED(ClientTicketBufferLength, QUIC_POOL_CLIENT_CRYPTO_TICKET);
      if (ClientTicketBuffer == NULL) {
          QuicTraceEvent(
              AllocFailure,
              "Allocation of '%s' failed. (%llu bytes)",
              "Client resumption ticket",
              ClientTicketBufferLength);
          Status = QUIC_STATUS_OUT_OF_MEMORY;
          goto Error;
      }
  
      //
      // Encoded ticket blob format is as follows:
      //   Ticket Version (QUIC_VAR_INT) [1..4]
      //   Negotiated Quic Version (network byte order) [4]
      //   Transport Parameters length (QUIC_VAR_INT) [1..2]
      //   Received Ticket length (QUIC_VAR_INT) [1..2]
      //   Transport Parameters [...]
      //   Received Ticket (omitted if length is zero) [...]
      //
  
      _Analysis_assume_(sizeof(*ClientTicketBuffer) >= 8);
      uint8_t* TicketCursor = QuicVarIntEncode(CXPLAT_TLS_RESUMPTION_CLIENT_TICKET_VERSION, ClientTicketBuffer);
      CxPlatCopyMemory(TicketCursor, &QuicVersion, sizeof(QuicVersion));
      TicketCursor += sizeof(QuicVersion);
      TicketCursor = QuicVarIntEncode(EncodedTPLength, TicketCursor);
      TicketCursor = QuicVarIntEncode(TicketLength, TicketCursor);
      CxPlatCopyMemory(TicketCursor, EncodedServerTP + CxPlatTlsTPHeaderSize, EncodedTPLength);
      TicketCursor += EncodedTPLength;
      if (TicketLength > 0) {
          CxPlatCopyMemory(TicketCursor, Ticket, TicketLength);
          TicketCursor += TicketLength;
      }
      CXPLAT_DBG_ASSERT(TicketCursor == ClientTicketBuffer + ClientTicketBufferLength);
  
      *ClientTicket = ClientTicketBuffer;
      *ClientTicketLength = ClientTicketBufferLength;
  
      Status = QUIC_STATUS_SUCCESS;
  
  Error:
  
      if (EncodedServerTP != NULL) {
          CXPLAT_FREE(EncodedServerTP, QUIC_POOL_TLS_TRANSPARAMS);
      }
  
      return Status;
  }
  
  QUIC_STATUS
  QuicCryptoDecodeClientTicket(
      _In_opt_ QUIC_CONNECTION* Connection,
      _In_ uint16_t ClientTicketLength,
      _In_reads_bytes_(ClientTicketLength)
          const uint8_t* ClientTicket,
      _Out_ QUIC_TRANSPORT_PARAMETERS* DecodedTP,
      _Outptr_result_buffer_maybenull_(*ServerTicketLength)
          uint8_t** ServerTicket,
      _Out_ uint32_t* ServerTicketLength,
      _Out_ uint32_t* QuicVersion
      )
  {
      QUIC_STATUS Status = QUIC_STATUS_INVALID_PARAMETER;
      uint16_t Offset = 0;
      QUIC_VAR_INT TicketVersion = 0, TPLength = 0, TicketLength = 0;
  
      *ServerTicket = NULL;
      *ServerTicketLength = 0;
      *QuicVersion = 0;
  
      if (!QuicVarIntDecode(ClientTicketLength, ClientTicket, &Offset, &TicketVersion)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Client Ticket version failed to decode");
          goto Error;
      }
      if (TicketVersion != CXPLAT_TLS_RESUMPTION_CLIENT_TICKET_VERSION) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Client Ticket version unsupported");
          goto Error;
      }
      if (ClientTicketLength < Offset + sizeof(uint32_t)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Client Ticket not long enough for QUIC version");
          goto Error;
      }
      CxPlatCopyMemory(QuicVersion, ClientTicket + Offset, sizeof(*QuicVersion));
      if (!QuicIsVersionSupported(*QuicVersion)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket for unsupported QUIC version");
          goto Error;
      }
      Offset += sizeof(*QuicVersion);
      if (!QuicVarIntDecode(ClientTicketLength, ClientTicket, &Offset, &TPLength)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Client Ticket TP length failed to decode");
          goto Error;
      }
      if (!QuicVarIntDecode(ClientTicketLength, ClientTicket, &Offset, &TicketLength)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket data length failed to decode");
          goto Error;
      }
      if (ClientTicketLength < Offset + TPLength) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Client Ticket not long enough for Client Transport Parameters");
          goto Error;
      }
      if (!QuicCryptoTlsDecodeTransportParameters(
              Connection,
              TRUE,  // IsServerTP
              ClientTicket + Offset,
              (uint16_t)TPLength,
              DecodedTP)) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Resumption Ticket TParams failed to decode");
          goto Error;
      }
      Offset += (uint16_t)TPLength;
      if (Offset + TicketLength != ClientTicketLength) {
          QuicTraceEvent(
              ConnError,
              "[conn][%p] ERROR, %s.",
              Connection,
              "Client resumption ticket length is corrupt");
          goto Error;
      }
      if (TicketLength != 0) {
          *ServerTicket = CXPLAT_ALLOC_NONPAGED((uint32_t)TicketLength, QUIC_POOL_CRYPTO_RESUMPTION_TICKET);
          if (*ServerTicket == NULL) {
              QuicTraceEvent(
                  AllocFailure,
                  "Allocation of '%s' failed. (%llu bytes)",
                  "Resumption ticket copy",
                  TicketLength);
              Status = QUIC_STATUS_OUT_OF_MEMORY;
              goto Error;
          }
          CxPlatCopyMemory(*ServerTicket, (uint8_t*)ClientTicket + Offset, (uint16_t)TicketLength);
      }
      *ServerTicketLength = (uint32_t)TicketLength;
      Offset += (uint16_t)TicketLength;
      CXPLAT_DBG_ASSERT(ClientTicketLength == Offset);
  
      Status = QUIC_STATUS_SUCCESS;
  
  Error:
      return Status;
  }
  