Change QuicCryptoStream to track crypto-substreams by packet number space instead of encryption level such that ZERO_RTT and FORWARD_SECURE crypto data are sent/received/re-transmitted on the same stream.

PiperOrigin-RevId: 447566612
diff --git a/quiche/quic/core/frames/quic_crypto_frame.h b/quiche/quic/core/frames/quic_crypto_frame.h
index bc76da1..19fb579 100644
--- a/quiche/quic/core/frames/quic_crypto_frame.h
+++ b/quiche/quic/core/frames/quic_crypto_frame.h
@@ -25,6 +25,8 @@
   friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
                                                       const QuicCryptoFrame& s);
 
+  // TODO(haoyuewang) Consider replace the EncryptionLevel here with
+  // PacketNumberSpace.
   // When writing a crypto frame to a packet, the packet must be encrypted at
   // |level|. When a crypto frame is read, the encryption level of the packet it
   // was received in is put in |level|.
diff --git a/quiche/quic/core/http/quic_spdy_session_test.cc b/quiche/quic/core/http/quic_spdy_session_test.cc
index a8fb322..accd1b0 100644
--- a/quiche/quic/core/http/quic_spdy_session_test.cc
+++ b/quiche/quic/core/http/quic_spdy_session_test.cc
@@ -199,11 +199,25 @@
       EncryptionLevel level) const override {
     return level != ENCRYPTION_ZERO_RTT;
   }
+  EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
+      PacketNumberSpace space) const override {
+    switch (space) {
+      case INITIAL_DATA:
+        return ENCRYPTION_INITIAL;
+      case HANDSHAKE_DATA:
+        return ENCRYPTION_HANDSHAKE;
+      case APPLICATION_DATA:
+        return ENCRYPTION_FORWARD_SECURE;
+      default:
+        QUICHE_DCHECK(false);
+        return NUM_ENCRYPTION_LEVELS;
+    }
+  }
 
   bool ExportKeyingMaterial(absl::string_view /*label*/,
                             absl::string_view /*context*/,
-                            size_t /*result_len*/,
-                            std::string* /*result*/) override {
+                            size_t /*result_len*/, std::string*
+                            /*result*/) override {
     return false;
   }
 
diff --git a/quiche/quic/core/http/quic_spdy_stream_test.cc b/quiche/quic/core/http/quic_spdy_stream_test.cc
index 281f359..de8a5be 100644
--- a/quiche/quic/core/http/quic_spdy_stream_test.cc
+++ b/quiche/quic/core/http/quic_spdy_stream_test.cc
@@ -192,6 +192,21 @@
     return level != ENCRYPTION_ZERO_RTT;
   }
 
+  EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
+      PacketNumberSpace space) const override {
+    switch (space) {
+      case INITIAL_DATA:
+        return ENCRYPTION_INITIAL;
+      case HANDSHAKE_DATA:
+        return ENCRYPTION_HANDSHAKE;
+      case APPLICATION_DATA:
+        return ENCRYPTION_FORWARD_SECURE;
+      default:
+        QUICHE_DCHECK(false);
+        return NUM_ENCRYPTION_LEVELS;
+    }
+  }
+
  private:
   using QuicCryptoStream::session;
 
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 73f5763..3913635 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -4120,6 +4120,21 @@
   visitor_->OnAckNeedsRetransmittableFrame();
 }
 
+EncryptionLevel QuicConnection::GetEncryptionLevelToSendPingForSpace(
+    PacketNumberSpace space) const {
+  switch (space) {
+    case INITIAL_DATA:
+      return ENCRYPTION_INITIAL;
+    case HANDSHAKE_DATA:
+      return ENCRYPTION_HANDSHAKE;
+    case APPLICATION_DATA:
+      return framer_.GetEncryptionLevelToSendApplicationData();
+    default:
+      QUICHE_DCHECK(false);
+      return NUM_ENCRYPTION_LEVELS;
+  }
+}
+
 void QuicConnection::OnRetransmissionTimeout() {
   ScopedRetransmissionTimeoutIndicator indicator(this);
 #ifndef NDEBUG
@@ -4182,7 +4197,8 @@
       if (sent_packet_manager_
               .GetEarliestPacketSentTimeForPto(&packet_number_space)
               .IsInitialized()) {
-        SendPingAtLevel(QuicUtils::GetEncryptionLevel(packet_number_space));
+        SendPingAtLevel(
+            GetEncryptionLevelToSendPingForSpace(packet_number_space));
       } else {
         // The client must PTO when there is nothing in flight if the server
         // could be blocked from sending by the amplification limit
@@ -5911,7 +5927,8 @@
                   << PacketNumberSpaceToString(
                          static_cast<PacketNumberSpace>(i));
     ScopedEncryptionLevelContext context(
-        this, QuicUtils::GetEncryptionLevel(static_cast<PacketNumberSpace>(i)));
+        this, QuicUtils::GetEncryptionLevelToSendAckofSpace(
+                  static_cast<PacketNumberSpace>(i)));
     QuicFrames frames;
     frames.push_back(uber_received_packet_manager_.GetUpdatedAckFrame(
         static_cast<PacketNumberSpace>(i), clock_->ApproximateNow()));
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index 28d5747..b214361 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -1863,6 +1863,10 @@
   bool ShouldSetRetransmissionAlarmOnPacketSent(bool in_flight,
                                                 EncryptionLevel level) const;
 
+  // Determines encryption level to send ping in `packet_number_space`.
+  EncryptionLevel GetEncryptionLevelToSendPingForSpace(
+      PacketNumberSpace space) const;
+
   QuicConnectionContext context_;
 
   QuicFramer framer_;
diff --git a/quiche/quic/core/quic_crypto_client_handshaker.cc b/quiche/quic/core/quic_crypto_client_handshaker.cc
index 6a9fa6c..4ec9b48 100644
--- a/quiche/quic/core/quic_crypto_client_handshaker.cc
+++ b/quiche/quic/core/quic_crypto_client_handshaker.cc
@@ -11,9 +11,11 @@
 #include "quiche/quic/core/crypto/crypto_protocol.h"
 #include "quiche/quic/core/crypto/crypto_utils.h"
 #include "quiche/quic/core/quic_session.h"
+#include "quiche/quic/core/quic_types.h"
 #include "quiche/quic/platform/api/quic_client_stats.h"
 #include "quiche/quic/platform/api/quic_flags.h"
 #include "quiche/quic/platform/api/quic_logging.h"
+#include "quiche/common/platform/api/quiche_logging.h"
 
 namespace quic {
 
@@ -149,6 +151,16 @@
   return true;
 }
 
+EncryptionLevel
+QuicCryptoClientHandshaker::GetEncryptionLevelToSendCryptoDataOfSpace(
+    PacketNumberSpace space) const {
+  if (space == INITIAL_DATA) {
+    return ENCRYPTION_INITIAL;
+  }
+  QUICHE_DCHECK(false);
+  return NUM_ENCRYPTION_LEVELS;
+}
+
 bool QuicCryptoClientHandshaker::one_rtt_keys_available() const {
   return one_rtt_keys_available_;
 }
diff --git a/quiche/quic/core/quic_crypto_client_handshaker.h b/quiche/quic/core/quic_crypto_client_handshaker.h
index 9c19f89..dbbd942 100644
--- a/quiche/quic/core/quic_crypto_client_handshaker.h
+++ b/quiche/quic/core/quic_crypto_client_handshaker.h
@@ -11,6 +11,7 @@
 #include "quiche/quic/core/crypto/quic_crypto_client_config.h"
 #include "quiche/quic/core/quic_crypto_client_stream.h"
 #include "quiche/quic/core/quic_server_id.h"
+#include "quiche/quic/core/quic_types.h"
 #include "quiche/quic/platform/api/quic_export.h"
 #include "quiche/common/platform/api/quiche_logging.h"
 
@@ -45,6 +46,8 @@
   bool encryption_established() const override;
   bool IsCryptoFrameExpectedForEncryptionLevel(
       EncryptionLevel level) const override;
+  EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
+      PacketNumberSpace space) const override;
   bool one_rtt_keys_available() const override;
   const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
       const override;
diff --git a/quiche/quic/core/quic_crypto_client_stream.cc b/quiche/quic/core/quic_crypto_client_stream.cc
index a241ecb..3c6dbe7 100644
--- a/quiche/quic/core/quic_crypto_client_stream.cc
+++ b/quiche/quic/core/quic_crypto_client_stream.cc
@@ -169,4 +169,10 @@
   return handshaker_->IsCryptoFrameExpectedForEncryptionLevel(level);
 }
 
+EncryptionLevel
+QuicCryptoClientStream::GetEncryptionLevelToSendCryptoDataOfSpace(
+    PacketNumberSpace space) const {
+  return handshaker_->GetEncryptionLevelToSendCryptoDataOfSpace(space);
+}
+
 }  // namespace quic
diff --git a/quiche/quic/core/quic_crypto_client_stream.h b/quiche/quic/core/quic_crypto_client_stream.h
index d3da4db..0a3e5e4 100644
--- a/quiche/quic/core/quic_crypto_client_stream.h
+++ b/quiche/quic/core/quic_crypto_client_stream.h
@@ -17,6 +17,7 @@
 #include "quiche/quic/core/quic_crypto_stream.h"
 #include "quiche/quic/core/quic_server_id.h"
 #include "quiche/quic/core/quic_session.h"
+#include "quiche/quic/core/quic_types.h"
 #include "quiche/quic/core/quic_versions.h"
 #include "quiche/quic/platform/api/quic_export.h"
 
@@ -173,6 +174,10 @@
     virtual bool IsCryptoFrameExpectedForEncryptionLevel(
         EncryptionLevel level) const = 0;
 
+    // Returns the encryption level to send CRYPTO_FRAME for `space`.
+    virtual EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
+        PacketNumberSpace space) const = 0;
+
     // Returns true once 1RTT keys are available.
     virtual bool one_rtt_keys_available() const = 0;
 
@@ -289,6 +294,9 @@
   SSL* GetSsl() const override;
   bool IsCryptoFrameExpectedForEncryptionLevel(
       EncryptionLevel level) const override;
+  EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
+      PacketNumberSpace space) const override;
+
   bool ExportKeyingMaterial(absl::string_view label, absl::string_view context,
                             size_t result_len, std::string* result) override;
   std::string chlo_hash() const;
diff --git a/quiche/quic/core/quic_crypto_server_stream.cc b/quiche/quic/core/quic_crypto_server_stream.cc
index 11a5728..5d41e47 100644
--- a/quiche/quic/core/quic_crypto_server_stream.cc
+++ b/quiche/quic/core/quic_crypto_server_stream.cc
@@ -10,6 +10,7 @@
 #include "absl/base/macros.h"
 #include "absl/strings/string_view.h"
 #include "openssl/sha.h"
+#include "quiche/quic/core/quic_types.h"
 #include "quiche/quic/platform/api/quic_flag_utils.h"
 #include "quiche/quic/platform/api/quic_testvalue.h"
 #include "quiche/common/platform/api/quiche_logging.h"
@@ -532,4 +533,17 @@
   return true;
 }
 
+EncryptionLevel
+QuicCryptoServerStream::GetEncryptionLevelToSendCryptoDataOfSpace(
+    PacketNumberSpace space) const {
+  if (space == INITIAL_DATA) {
+    return ENCRYPTION_INITIAL;
+  }
+  if (space == APPLICATION_DATA) {
+    return ENCRYPTION_ZERO_RTT;
+  }
+  QUICHE_DCHECK(false);
+  return NUM_ENCRYPTION_LEVELS;
+}
+
 }  // namespace quic
diff --git a/quiche/quic/core/quic_crypto_server_stream.h b/quiche/quic/core/quic_crypto_server_stream.h
index 5bdfb54..f91ceba 100644
--- a/quiche/quic/core/quic_crypto_server_stream.h
+++ b/quiche/quic/core/quic_crypto_server_stream.h
@@ -12,6 +12,7 @@
 #include "quiche/quic/core/quic_crypto_handshaker.h"
 #include "quiche/quic/core/quic_crypto_server_stream_base.h"
 #include "quiche/quic/core/quic_session.h"
+#include "quiche/quic/core/quic_types.h"
 #include "quiche/quic/platform/api/quic_export.h"
 
 namespace quic {
@@ -74,6 +75,8 @@
   SSL* GetSsl() const override;
   bool IsCryptoFrameExpectedForEncryptionLevel(
       EncryptionLevel level) const override;
+  EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
+      PacketNumberSpace space) const override;
 
   // From QuicCryptoHandshaker
   void OnHandshakeMessage(const CryptoHandshakeMessage& message) override;
diff --git a/quiche/quic/core/quic_crypto_stream.cc b/quiche/quic/core/quic_crypto_stream.cc
index 33eb543..3839db3 100644
--- a/quiche/quic/core/quic_crypto_stream.cc
+++ b/quiche/quic/core/quic_crypto_stream.cc
@@ -37,10 +37,7 @@
           QuicVersionUsesCryptoFrames(session->transport_version())
               ? CRYPTO
               : BIDIRECTIONAL),
-      substreams_{{{this, ENCRYPTION_INITIAL},
-                   {this, ENCRYPTION_HANDSHAKE},
-                   {this, ENCRYPTION_ZERO_RTT},
-                   {this, ENCRYPTION_FORWARD_SECURE}}} {
+      substreams_{{{this}, {this}, {this}}} {
   // The crypto stream is exempt from connection level flow control.
   DisableConnectionFlowControlForThisStream();
 }
@@ -82,9 +79,11 @@
         absl::StrCat("CRYPTO_FRAME is unexpectedly received at level ", level));
     return;
   }
-  substreams_[level].sequencer.OnCryptoFrame(frame);
+  CryptoSubstream& substream =
+      substreams_[QuicUtils::GetPacketNumberSpace(level)];
+  substream.sequencer.OnCryptoFrame(frame);
   EncryptionLevel frame_level = level;
-  if (substreams_[level].sequencer.NumBytesBuffered() >
+  if (substream.sequencer.NumBytesBuffered() >
       BufferSizeLimitForLevel(frame_level)) {
     OnUnrecoverableError(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA,
                          "Too much crypto data received");
@@ -109,7 +108,8 @@
     OnDataAvailableInSequencer(sequencer(), level);
     return;
   }
-  OnDataAvailableInSequencer(&substreams_[level].sequencer, level);
+  OnDataAvailableInSequencer(
+      &substreams_[QuicUtils::GetPacketNumberSpace(level)].sequencer, level);
 }
 
 void QuicCryptoStream::OnDataAvailableInSequencer(
@@ -146,7 +146,8 @@
   }
   const bool had_buffered_data = HasBufferedCryptoFrames();
   // Append |data| to the send buffer for this encryption level.
-  QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
+  QuicStreamSendBuffer* send_buffer =
+      &substreams_[QuicUtils::GetPacketNumberSpace(level)].send_buffer;
   QuicStreamOffset offset = send_buffer->stream_offset();
   send_buffer->SaveStreamData(data);
   if (kMaxStreamLength - offset < data.length()) {
@@ -173,8 +174,9 @@
 bool QuicCryptoStream::OnCryptoFrameAcked(const QuicCryptoFrame& frame,
                                           QuicTime::Delta /*ack_delay_time*/) {
   QuicByteCount newly_acked_length = 0;
-  if (!substreams_[frame.level].send_buffer.OnStreamDataAcked(
-          frame.offset, frame.data_length, &newly_acked_length)) {
+  if (!substreams_[QuicUtils::GetPacketNumberSpace(frame.level)]
+           .send_buffer.OnStreamDataAcked(frame.offset, frame.data_length,
+                                          &newly_acked_length)) {
     OnUnrecoverableError(QUIC_INTERNAL_ERROR,
                          "Trying to ack unsent crypto data.");
     return false;
@@ -201,9 +203,10 @@
     }
     return;
   }
-  QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
-  // TODO(nharper): Consider adding a Clear() method to QuicStreamSendBuffer to
-  // replace the following code.
+  QuicStreamSendBuffer* send_buffer =
+      &substreams_[QuicUtils::GetPacketNumberSpace(level)].send_buffer;
+  // TODO(nharper): Consider adding a Clear() method to QuicStreamSendBuffer
+  // to replace the following code.
   QuicIntervalSet<QuicStreamOffset> to_ack = send_buffer->bytes_acked();
   to_ack.Complement(0, send_buffer->stream_offset());
   for (const auto& interval : to_ack) {
@@ -225,22 +228,13 @@
   QuicStream::OnStreamDataConsumed(bytes_consumed);
 }
 
-namespace {
-
-constexpr std::array<EncryptionLevel, NUM_ENCRYPTION_LEVELS>
-AllEncryptionLevels() {
-  return {ENCRYPTION_INITIAL, ENCRYPTION_HANDSHAKE, ENCRYPTION_ZERO_RTT,
-          ENCRYPTION_FORWARD_SECURE};
-}
-
-}  // namespace
 
 bool QuicCryptoStream::HasPendingCryptoRetransmission() const {
   if (!QuicVersionUsesCryptoFrames(session()->transport_version())) {
     return false;
   }
-  for (EncryptionLevel level : AllEncryptionLevels()) {
-    if (substreams_[level].send_buffer.HasPendingRetransmission()) {
+  for (const auto& substream : substreams_) {
+    if (substream.send_buffer.HasPendingRetransmission()) {
       return true;
     }
   }
@@ -251,12 +245,15 @@
   QUIC_BUG_IF(quic_bug_12573_3,
               !QuicVersionUsesCryptoFrames(session()->transport_version()))
       << "Versions less than 47 don't write CRYPTO frames";
-  for (EncryptionLevel level : AllEncryptionLevels()) {
-    QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
+  for (uint8_t i = INITIAL_DATA; i <= APPLICATION_DATA; ++i) {
+    auto packet_number_space = static_cast<PacketNumberSpace>(i);
+    QuicStreamSendBuffer* send_buffer =
+        &substreams_[packet_number_space].send_buffer;
     while (send_buffer->HasPendingRetransmission()) {
       auto pending = send_buffer->NextPendingRetransmission();
       size_t bytes_consumed = stream_delegate()->SendCryptoData(
-          level, pending.length, pending.offset, HANDSHAKE_RETRANSMISSION);
+          GetEncryptionLevelToSendCryptoDataOfSpace(packet_number_space),
+          pending.length, pending.offset, HANDSHAKE_RETRANSMISSION);
       send_buffer->OnStreamDataRetransmitted(pending.offset, bytes_consumed);
       if (bytes_consumed < pending.length) {
         return;
@@ -351,14 +348,17 @@
     return stream_bytes_read();
   }
   uint64_t bytes_read = 0;
-  for (EncryptionLevel level : AllEncryptionLevels()) {
-    bytes_read += substreams_[level].sequencer.NumBytesConsumed();
+  for (const CryptoSubstream& substream : substreams_) {
+    bytes_read += substream.sequencer.NumBytesConsumed();
   }
   return bytes_read;
 }
 
+// TODO(haoyuewang) Move this test-only method under
+// quiche/quic/test_tools.
 uint64_t QuicCryptoStream::BytesReadOnLevel(EncryptionLevel level) const {
-  return substreams_[level].sequencer.NumBytesConsumed();
+  return substreams_[QuicUtils::GetPacketNumberSpace(level)]
+      .sequencer.NumBytesConsumed();
 }
 
 bool QuicCryptoStream::WriteCryptoFrame(EncryptionLevel level,
@@ -368,16 +368,17 @@
   QUIC_BUG_IF(quic_bug_12573_4,
               !QuicVersionUsesCryptoFrames(session()->transport_version()))
       << "Versions less than 47 don't write CRYPTO frames (2)";
-  return substreams_[level].send_buffer.WriteStreamData(offset, data_length,
-                                                        writer);
+  return substreams_[QuicUtils::GetPacketNumberSpace(level)]
+      .send_buffer.WriteStreamData(offset, data_length, writer);
 }
 
 void QuicCryptoStream::OnCryptoFrameLost(QuicCryptoFrame* crypto_frame) {
   QUIC_BUG_IF(quic_bug_12573_5,
               !QuicVersionUsesCryptoFrames(session()->transport_version()))
       << "Versions less than 47 don't lose CRYPTO frames";
-  substreams_[crypto_frame->level].send_buffer.OnStreamDataLost(
-      crypto_frame->offset, crypto_frame->data_length);
+  substreams_[QuicUtils::GetPacketNumberSpace(crypto_frame->level)]
+      .send_buffer.OnStreamDataLost(crypto_frame->offset,
+                                    crypto_frame->data_length);
 }
 
 bool QuicCryptoStream::RetransmitData(QuicCryptoFrame* crypto_frame,
@@ -388,7 +389,8 @@
   QuicIntervalSet<QuicStreamOffset> retransmission(
       crypto_frame->offset, crypto_frame->offset + crypto_frame->data_length);
   QuicStreamSendBuffer* send_buffer =
-      &substreams_[crypto_frame->level].send_buffer;
+      &substreams_[QuicUtils::GetPacketNumberSpace(crypto_frame->level)]
+           .send_buffer;
   retransmission.Difference(send_buffer->bytes_acked());
   if (retransmission.Empty()) {
     return true;
@@ -396,9 +398,12 @@
   for (const auto& interval : retransmission) {
     size_t retransmission_offset = interval.min();
     size_t retransmission_length = interval.max() - interval.min();
+    EncryptionLevel retransmission_encryption_level =
+        GetEncryptionLevelToSendCryptoDataOfSpace(
+            QuicUtils::GetPacketNumberSpace(crypto_frame->level));
     size_t bytes_consumed = stream_delegate()->SendCryptoData(
-        crypto_frame->level, retransmission_length, retransmission_offset,
-        type);
+        retransmission_encryption_level, retransmission_length,
+        retransmission_offset, type);
     send_buffer->OnStreamDataRetransmitted(retransmission_offset,
                                            bytes_consumed);
     if (bytes_consumed < retransmission_length) {
@@ -412,8 +417,10 @@
   QUIC_BUG_IF(quic_bug_12573_7,
               !QuicVersionUsesCryptoFrames(session()->transport_version()))
       << "Versions less than 47 don't use CRYPTO frames";
-  for (EncryptionLevel level : AllEncryptionLevels()) {
-    QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
+  for (uint8_t i = INITIAL_DATA; i <= APPLICATION_DATA; ++i) {
+    auto packet_number_space = static_cast<PacketNumberSpace>(i);
+    QuicStreamSendBuffer* send_buffer =
+        &substreams_[packet_number_space].send_buffer;
     const size_t data_length =
         send_buffer->stream_offset() - send_buffer->stream_bytes_written();
     if (data_length == 0) {
@@ -421,8 +428,8 @@
       continue;
     }
     size_t bytes_consumed = stream_delegate()->SendCryptoData(
-        level, data_length, send_buffer->stream_bytes_written(),
-        NOT_RETRANSMISSION);
+        GetEncryptionLevelToSendCryptoDataOfSpace(packet_number_space),
+        data_length, send_buffer->stream_bytes_written(), NOT_RETRANSMISSION);
     send_buffer->OnStreamDataConsumed(bytes_consumed);
     if (bytes_consumed < data_length) {
       // Connection is write blocked.
@@ -435,8 +442,8 @@
   QUIC_BUG_IF(quic_bug_12573_8,
               !QuicVersionUsesCryptoFrames(session()->transport_version()))
       << "Versions less than 47 don't use CRYPTO frames";
-  for (EncryptionLevel level : AllEncryptionLevels()) {
-    const QuicStreamSendBuffer& send_buffer = substreams_[level].send_buffer;
+  for (const CryptoSubstream& substream : substreams_) {
+    const QuicStreamSendBuffer& send_buffer = substream.send_buffer;
     QUICHE_DCHECK_GE(send_buffer.stream_offset(),
                      send_buffer.stream_bytes_written());
     if (send_buffer.stream_offset() > send_buffer.stream_bytes_written()) {
@@ -457,15 +464,16 @@
     // the wrong transport version.
     return false;
   }
-  return substreams_[level].send_buffer.IsStreamDataOutstanding(offset, length);
+  return substreams_[QuicUtils::GetPacketNumberSpace(level)]
+      .send_buffer.IsStreamDataOutstanding(offset, length);
 }
 
 bool QuicCryptoStream::IsWaitingForAcks() const {
   if (!QuicVersionUsesCryptoFrames(session()->transport_version())) {
     return QuicStream::IsWaitingForAcks();
   }
-  for (EncryptionLevel level : AllEncryptionLevels()) {
-    if (substreams_[level].send_buffer.stream_bytes_outstanding()) {
+  for (const CryptoSubstream& substream : substreams_) {
+    if (substream.send_buffer.stream_bytes_outstanding()) {
       return true;
     }
   }
@@ -473,7 +481,7 @@
 }
 
 QuicCryptoStream::CryptoSubstream::CryptoSubstream(
-    QuicCryptoStream* crypto_stream, EncryptionLevel)
+    QuicCryptoStream* crypto_stream)
     : sequencer(crypto_stream),
       send_buffer(crypto_stream->session()
                       ->connection()
diff --git a/quiche/quic/core/quic_crypto_stream.h b/quiche/quic/core/quic_crypto_stream.h
index be61a82..6d29d4b 100644
--- a/quiche/quic/core/quic_crypto_stream.h
+++ b/quiche/quic/core/quic_crypto_stream.h
@@ -236,8 +236,9 @@
   virtual void OnDataAvailableInSequencer(QuicStreamSequencer* sequencer,
                                           EncryptionLevel level);
 
-  QuicStreamSequencer* GetStreamSequencerForLevel(EncryptionLevel level) {
-    return &substreams_[level].sequencer;
+  QuicStreamSequencer* GetStreamSequencerForPacketNumberSpace(
+      PacketNumberSpace packet_number_space) {
+    return &substreams_[packet_number_space].sequencer;
   }
 
   // Called by OnCryptoFrame to check if a CRYPTO frame is received at an
@@ -245,13 +246,17 @@
   virtual bool IsCryptoFrameExpectedForEncryptionLevel(
       EncryptionLevel level) const = 0;
 
+  // Called to determine the encryption level to send/retransmit crypto data.
+  virtual EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
+      PacketNumberSpace space) const = 0;
+
  private:
-  // Data sent and received in CRYPTO frames is sent at multiple encryption
-  // levels. Some of the state for the single logical crypto stream is split
-  // across encryption levels, and a CryptoSubstream is used to manage that
-  // state for a particular encryption level.
+  // Data sent and received in CRYPTO frames is sent at multiple packet number
+  // spaces. Some of the state for the single logical crypto stream is split
+  // across packet number spaces, and a CryptoSubstream is used to manage that
+  // state for a particular packet number space.
   struct QUIC_EXPORT_PRIVATE CryptoSubstream {
-    CryptoSubstream(QuicCryptoStream* crypto_stream, EncryptionLevel);
+    CryptoSubstream(QuicCryptoStream* crypto_stream);
 
     QuicStreamSequencer sequencer;
     QuicStreamSendBuffer send_buffer;
@@ -262,9 +267,9 @@
   // TLS 1.3, which never encrypts crypto data.
   QuicIntervalSet<QuicStreamOffset> bytes_consumed_[NUM_ENCRYPTION_LEVELS];
 
-  // Keeps state for data sent/received in CRYPTO frames at each encryption
-  // level.
-  std::array<CryptoSubstream, NUM_ENCRYPTION_LEVELS> substreams_;
+  // Keeps state for data sent/received in CRYPTO frames at each packet number
+  // space;
+  std::array<CryptoSubstream, NUM_PACKET_NUMBER_SPACES> substreams_;
 };
 
 }  // namespace quic
diff --git a/quiche/quic/core/quic_crypto_stream_test.cc b/quiche/quic/core/quic_crypto_stream_test.cc
index 7e27d77..ea99926 100644
--- a/quiche/quic/core/quic_crypto_stream_test.cc
+++ b/quiche/quic/core/quic_crypto_stream_test.cc
@@ -13,6 +13,7 @@
 #include "quiche/quic/core/crypto/crypto_handshake.h"
 #include "quiche/quic/core/crypto/crypto_protocol.h"
 #include "quiche/quic/core/crypto/null_encrypter.h"
+#include "quiche/quic/core/quic_types.h"
 #include "quiche/quic/core/quic_utils.h"
 #include "quiche/quic/platform/api/quic_socket_address.h"
 #include "quiche/quic/platform/api/quic_test.h"
@@ -101,6 +102,22 @@
     return level != ENCRYPTION_ZERO_RTT;
   }
 
+  EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
+      PacketNumberSpace space) const override {
+    switch (space) {
+      case INITIAL_DATA:
+        return ENCRYPTION_INITIAL;
+      case HANDSHAKE_DATA:
+        return ENCRYPTION_HANDSHAKE;
+      case APPLICATION_DATA:
+        return QuicCryptoStream::session()
+            ->GetEncryptionLevelToSendApplicationData();
+      default:
+        QUICHE_DCHECK(false);
+        return NUM_ENCRYPTION_LEVELS;
+    }
+  }
+
  private:
   quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
   std::vector<CryptoHandshakeMessage> messages_;
@@ -266,6 +283,17 @@
       .WillOnce(Invoke(connection_,
                        &MockQuicConnection::QuicConnection_SendCryptoData));
   stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data);
+
+  // Before encryption moves to ENCRYPTION_FORWARD_SECURE, ZERO RTT data are
+  // retranmitted at ENCRYPTION_ZERO_RTT.
+  QuicCryptoFrame lost_frame = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 650);
+  stream_->OnCryptoFrameLost(&lost_frame);
+
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 650, 0))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  stream_->WritePendingCryptoRetransmission();
+
   connection_->SetEncrypter(
       ENCRYPTION_FORWARD_SECURE,
       std::make_unique<NullEncrypter>(Perspective::IS_CLIENT));
@@ -273,7 +301,7 @@
   EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
 
   // Lost [0, 1000).
-  QuicCryptoFrame lost_frame(ENCRYPTION_INITIAL, 0, 1000);
+  lost_frame = QuicCryptoFrame(ENCRYPTION_INITIAL, 0, 1000);
   stream_->OnCryptoFrameLost(&lost_frame);
   EXPECT_TRUE(stream_->HasPendingCryptoRetransmission());
   // Lost [1200, 2000).
@@ -289,7 +317,7 @@
   EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 150, 1200))
       .WillOnce(Invoke(connection_,
                        &MockQuicConnection::QuicConnection_SendCryptoData));
-  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 650, 0))
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_FORWARD_SECURE, 650, 0))
       .WillOnce(Invoke(connection_,
                        &MockQuicConnection::QuicConnection_SendCryptoData));
   stream_->WritePendingCryptoRetransmission();
@@ -533,7 +561,7 @@
       stream_->OnCryptoFrameAcked(acked_frame, QuicTime::Delta::Zero()));
 
   // Retransmit only [1350, 1500).
-  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 150, 0))
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_FORWARD_SECURE, 150, 0))
       .WillOnce(Invoke(connection_,
                        &MockQuicConnection::QuicConnection_SendCryptoData));
   QuicCryptoFrame frame_to_retransmit(ENCRYPTION_ZERO_RTT, 0, 150);
@@ -543,10 +571,11 @@
   EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
 
   // Retransmit [1350, 2700) again and all data is sent.
-  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 650, 0))
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_FORWARD_SECURE, 650, 0))
       .WillOnce(Invoke(connection_,
                        &MockQuicConnection::QuicConnection_SendCryptoData));
-  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 200, 1150))
+  EXPECT_CALL(*connection_,
+              SendCryptoData(ENCRYPTION_FORWARD_SECURE, 200, 1150))
       .WillOnce(Invoke(connection_,
                        &MockQuicConnection::QuicConnection_SendCryptoData));
   frame_to_retransmit = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 1350);
diff --git a/quiche/quic/core/quic_session.cc b/quiche/quic/core/quic_session.cc
index 6450da0..807bfdd 100644
--- a/quiche/quic/core/quic_session.cc
+++ b/quiche/quic/core/quic_session.cc
@@ -1558,7 +1558,7 @@
     bool set_alternative_decrypter, bool latch_once_used) {
   if (connection_->version().handshake_protocol == PROTOCOL_TLS1_3 &&
       !connection()->framer().HasEncrypterOfEncryptionLevel(
-          QuicUtils::GetEncryptionLevel(
+          QuicUtils::GetEncryptionLevelToSendAckofSpace(
               QuicUtils::GetPacketNumberSpace(level)))) {
     // This should never happen because connection should never decrypt a packet
     // while an ACK for it cannot be encrypted.
diff --git a/quiche/quic/core/quic_session_test.cc b/quiche/quic/core/quic_session_test.cc
index bb198e4..9b90c4f 100644
--- a/quiche/quic/core/quic_session_test.cc
+++ b/quiche/quic/core/quic_session_test.cc
@@ -38,6 +38,7 @@
 #include "quiche/quic/test_tools/quic_stream_peer.h"
 #include "quiche/quic/test_tools/quic_stream_send_buffer_peer.h"
 #include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/common/platform/api/quiche_logging.h"
 #include "quiche/common/quiche_mem_slice_storage.h"
 
 using spdy::kV3HighestPriority;
@@ -186,6 +187,21 @@
     return level != ENCRYPTION_ZERO_RTT;
   }
 
+  EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
+      PacketNumberSpace space) const override {
+    switch (space) {
+      case INITIAL_DATA:
+        return ENCRYPTION_INITIAL;
+      case HANDSHAKE_DATA:
+        return ENCRYPTION_HANDSHAKE;
+      case APPLICATION_DATA:
+        return ENCRYPTION_FORWARD_SECURE;
+      default:
+        QUICHE_DCHECK(false);
+        return NUM_ENCRYPTION_LEVELS;
+    }
+  }
+
  private:
   using QuicCryptoStream::session;
 
@@ -2959,7 +2975,11 @@
 
   EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 350, 1000))
       .WillOnce(Return(350));
-  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0))
+  EXPECT_CALL(
+      *connection_,
+      SendCryptoData(crypto_stream->GetEncryptionLevelToSendCryptoDataOfSpace(
+                         QuicUtils::GetPacketNumberSpace(ENCRYPTION_ZERO_RTT)),
+                     1350, 0))
       .WillOnce(Return(1350));
   session_.OnCanWrite();
   EXPECT_FALSE(session_.HasPendingHandshake());
diff --git a/quiche/quic/core/quic_utils.cc b/quiche/quic/core/quic_utils.cc
index 6113c91..e1b1797 100644
--- a/quiche/quic/core/quic_utils.cc
+++ b/quiche/quic/core/quic_utils.cc
@@ -595,7 +595,7 @@
 }
 
 // static
-EncryptionLevel QuicUtils::GetEncryptionLevel(
+EncryptionLevel QuicUtils::GetEncryptionLevelToSendAckofSpace(
     PacketNumberSpace packet_number_space) {
   switch (packet_number_space) {
     case INITIAL_DATA:
diff --git a/quiche/quic/core/quic_utils.h b/quiche/quic/core/quic_utils.h
index 202dc4b..2d4d5e8 100644
--- a/quiche/quic/core/quic_utils.h
+++ b/quiche/quic/core/quic_utils.h
@@ -209,8 +209,8 @@
   static PacketNumberSpace GetPacketNumberSpace(
       EncryptionLevel encryption_level);
 
-  // Determines encryption level to send packets in |packet_number_space|.
-  static EncryptionLevel GetEncryptionLevel(
+  // Determines encryption level to send ACK in |packet_number_space|.
+  static EncryptionLevel GetEncryptionLevelToSendAckofSpace(
       PacketNumberSpace packet_number_space);
 
   // Get the maximum value for a V99/IETF QUIC stream count. If a count
diff --git a/quiche/quic/core/tls_client_handshaker.cc b/quiche/quic/core/tls_client_handshaker.cc
index 7460263..36edcac 100644
--- a/quiche/quic/core/tls_client_handshaker.cc
+++ b/quiche/quic/core/tls_client_handshaker.cc
@@ -361,6 +361,19 @@
   return level != ENCRYPTION_ZERO_RTT;
 }
 
+EncryptionLevel TlsClientHandshaker::GetEncryptionLevelToSendCryptoDataOfSpace(
+    PacketNumberSpace space) const {
+  switch (space) {
+    case INITIAL_DATA:
+      return ENCRYPTION_INITIAL;
+    case HANDSHAKE_DATA:
+      return ENCRYPTION_HANDSHAKE;
+    default:
+      QUICHE_DCHECK(false);
+      return NUM_ENCRYPTION_LEVELS;
+  }
+}
+
 bool TlsClientHandshaker::one_rtt_keys_available() const {
   return state_ >= HANDSHAKE_COMPLETE;
 }
diff --git a/quiche/quic/core/tls_client_handshaker.h b/quiche/quic/core/tls_client_handshaker.h
index 21b6033..06581b5 100644
--- a/quiche/quic/core/tls_client_handshaker.h
+++ b/quiche/quic/core/tls_client_handshaker.h
@@ -56,6 +56,8 @@
   bool encryption_established() const override;
   bool IsCryptoFrameExpectedForEncryptionLevel(
       EncryptionLevel level) const override;
+  EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
+      PacketNumberSpace space) const override;
   bool one_rtt_keys_available() const override;
   const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
       const override;
diff --git a/quiche/quic/core/tls_server_handshaker.cc b/quiche/quic/core/tls_server_handshaker.cc
index cee7a71..3274fa5 100644
--- a/quiche/quic/core/tls_server_handshaker.cc
+++ b/quiche/quic/core/tls_server_handshaker.cc
@@ -1130,4 +1130,19 @@
   return level != ENCRYPTION_ZERO_RTT;
 }
 
+EncryptionLevel TlsServerHandshaker::GetEncryptionLevelToSendCryptoDataOfSpace(
+    PacketNumberSpace space) const {
+  switch (space) {
+    case INITIAL_DATA:
+      return ENCRYPTION_INITIAL;
+    case HANDSHAKE_DATA:
+      return ENCRYPTION_HANDSHAKE;
+    case APPLICATION_DATA:
+      return ENCRYPTION_FORWARD_SECURE;
+    default:
+      QUICHE_DCHECK(false);
+      return NUM_ENCRYPTION_LEVELS;
+  }
+}
+
 }  // namespace quic
diff --git a/quiche/quic/core/tls_server_handshaker.h b/quiche/quic/core/tls_server_handshaker.h
index fa5ca75..68abf73 100644
--- a/quiche/quic/core/tls_server_handshaker.h
+++ b/quiche/quic/core/tls_server_handshaker.h
@@ -73,6 +73,8 @@
   SSL* GetSsl() const override;
   bool IsCryptoFrameExpectedForEncryptionLevel(
       EncryptionLevel level) const override;
+  EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
+      PacketNumberSpace space) const override;
 
   // From QuicCryptoServerStreamBase and TlsHandshaker
   ssl_early_data_reason_t EarlyDataReason() const override;
diff --git a/quiche/quic/core/uber_received_packet_manager_test.cc b/quiche/quic/core/uber_received_packet_manager_test.cc
index 030e55c..5474421 100644
--- a/quiche/quic/core/uber_received_packet_manager_test.cc
+++ b/quiche/quic/core/uber_received_packet_manager_test.cc
@@ -33,6 +33,20 @@
 const QuicTime::Delta kDelayedAckTime =
     QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
 
+EncryptionLevel GetEncryptionLevel(PacketNumberSpace packet_number_space) {
+  switch (packet_number_space) {
+    case INITIAL_DATA:
+      return ENCRYPTION_INITIAL;
+    case HANDSHAKE_DATA:
+      return ENCRYPTION_HANDSHAKE;
+    case APPLICATION_DATA:
+      return ENCRYPTION_FORWARD_SECURE;
+    default:
+      QUICHE_DCHECK(false);
+      return NUM_ENCRYPTION_LEVELS;
+  }
+}
+
 class UberReceivedPacketManagerTest : public QuicTest {
  protected:
   UberReceivedPacketManagerTest() {
@@ -108,7 +122,7 @@
         continue;
       }
       manager_->ResetAckStates(
-          QuicUtils::GetEncryptionLevel(static_cast<PacketNumberSpace>(i)));
+          GetEncryptionLevel(static_cast<PacketNumberSpace>(i)));
     }
   }
 
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h
index f43b12d..df742be 100644
--- a/quiche/quic/test_tools/quic_test_utils.h
+++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -859,6 +859,20 @@
       quic::EncryptionLevel level) const override {
     return level != ENCRYPTION_ZERO_RTT;
   }
+  EncryptionLevel GetEncryptionLevelToSendCryptoDataOfSpace(
+      PacketNumberSpace space) const override {
+    switch (space) {
+      case INITIAL_DATA:
+        return ENCRYPTION_INITIAL;
+      case HANDSHAKE_DATA:
+        return ENCRYPTION_HANDSHAKE;
+      case APPLICATION_DATA:
+        return ENCRYPTION_FORWARD_SECURE;
+      default:
+        QUICHE_DCHECK(false);
+        return NUM_ENCRYPTION_LEVELS;
+    }
+  }
 
  private:
   quiche::QuicheReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;