Update LoadBalancerEncoder to draft-15.

Affects only short, encrypted connection IDs, and thus has no effect on current production, or firmly planned deployments.

PiperOrigin-RevId: 483467615
diff --git a/quiche/quic/load_balancer/load_balancer_config.cc b/quiche/quic/load_balancer/load_balancer_config.cc
index 46a0f68..62d8898 100644
--- a/quiche/quic/load_balancer/load_balancer_config.cc
+++ b/quiche/quic/load_balancer/load_balancer_config.cc
@@ -54,54 +54,59 @@
 // TakePlaintextFrom{Left,Right}() reads the left or right half of 'from' and
 // expands it into a full encryption block ('to') in accordance with the
 // internet-draft.
-void TakePlaintextFromLeft(uint8_t *to, uint8_t *from, uint8_t plaintext_len,
-                           uint8_t index) {
+void TakePlaintextFromLeft(const uint8_t *from, const uint8_t plaintext_len,
+                           const uint8_t index, uint8_t *to) {
   uint8_t half = plaintext_len / 2;
-  memset(to, 0, kLoadBalancerBlockSize - 1);
-  memcpy(to, from, half);
+
+  to[0] = plaintext_len;
+  to[1] = index;
+  memcpy(to + 2, from, half);
   if (plaintext_len % 2) {
-    to[half] = from[half] & 0xf0;
+    to[2 + half] = from[half] & 0xf0;
+    half++;
   }
-  to[kLoadBalancerBlockSize - 1] = plaintext_len + 1;
-  to[kLoadBalancerBlockSize - 2] = index;
+  memset(to + 2 + half, 0, kLoadBalancerBlockSize - 2 - half);
 }
 
-void TakePlaintextFromRight(uint8_t *to, uint8_t *from, uint8_t plaintext_len,
-                            uint8_t index) {
-  const uint8_t half = plaintext_len / 2;
-  const uint8_t write_point = kLoadBalancerBlockSize - half;
-  const uint8_t read_point = plaintext_len - half;
-  memset((to + 1), 0, kLoadBalancerBlockSize - 1);
-  memcpy(to + write_point, from + read_point, half);
-  if (plaintext_len % 2) {
-    to[write_point - 1] = from[read_point - 1] & 0x0f;
-  }
-  to[0] = plaintext_len + 1;
+void TakePlaintextFromRight(const uint8_t *from, const uint8_t plaintext_len,
+                            const uint8_t index, uint8_t *to) {
+  uint8_t half = plaintext_len / 2;
+
+  to[0] = plaintext_len;
   to[1] = index;
+  memcpy(to + 2, from + half, half + (plaintext_len % 2));
+  if (plaintext_len % 2) {
+    to[2] &= 0x0f;
+    half++;
+  }
+  memset(to + 2 + half, 0, kLoadBalancerBlockSize - 2 - half);
 }
 
 // CiphertextXorWith{Left,Right}() takes the relevant end of the ciphertext in
 // 'from' and XORs it with half of the ConnectionId stored at 'to', in
 // accordance with the internet-draft.
-void CiphertextXorWithLeft(uint8_t *to, uint8_t *from, uint8_t plaintext_len) {
+void CiphertextXorWithLeft(const uint8_t *from, const uint8_t plaintext_len,
+                           uint8_t *to) {
   uint8_t half = plaintext_len / 2;
   for (int i = 0; i < half; i++) {
-    *(to + i) ^= *(from + i);
+    to[i] ^= from[i];
   }
   if (plaintext_len % 2) {
-    *(to + half) ^= (*(from + half) & 0xf0);
+    to[half] ^= (from[half] & 0xf0);
   }
 }
 
-void CiphertextXorWithRight(uint8_t *to, uint8_t *from, uint8_t plaintext_len) {
-  const uint8_t half = plaintext_len / 2;
-  const uint8_t write_point = plaintext_len - half;
-  const uint8_t read_point = kLoadBalancerBlockSize - half;
+void CiphertextXorWithRight(const uint8_t *from, const uint8_t plaintext_len,
+                            uint8_t *to) {
+  uint8_t half = plaintext_len / 2;
+  int i = 0;
   if (plaintext_len % 2) {
-    *(to + write_point - 1) ^= (*(from + read_point - 1) & 0x0f);
+    to[half] ^= (from[0] & 0x0f);
+    i++;
   }
-  for (int i = 0; i < half; i++) {
-    *(to + write_point + i) ^= *(from + read_point + i);
+  while ((half + i) < plaintext_len) {
+    to[half + i] ^= from[i];
+    i++;
   }
 }
 
@@ -146,18 +151,18 @@
     return false;
   }
   if (index % 2) {  // Odd indices go from left to right
-    TakePlaintextFromLeft(buf, target.data(), plaintext_len(), index);
+    TakePlaintextFromLeft(target.data(), plaintext_len(), index, buf);
   } else {
-    TakePlaintextFromRight(buf, target.data(), plaintext_len(), index);
+    TakePlaintextFromRight(target.data(), plaintext_len(), index, buf);
   }
   if (!BlockEncrypt(buf, buf)) {
     return false;
   }
   // XOR bits over the correct half.
   if (index % 2) {
-    CiphertextXorWithRight(target.data(), buf, plaintext_len());
+    CiphertextXorWithRight(buf, plaintext_len(), target.data());
   } else {
-    CiphertextXorWithLeft(target.data(), buf, plaintext_len());
+    CiphertextXorWithLeft(buf, plaintext_len(), target.data());
   }
   return true;
 }
diff --git a/quiche/quic/load_balancer/load_balancer_config_test.cc b/quiche/quic/load_balancer/load_balancer_config_test.cc
index e19a3c9..bb3d367 100644
--- a/quiche/quic/load_balancer/load_balancer_config_test.cc
+++ b/quiche/quic/load_balancer/load_balancer_config_test.cc
@@ -85,29 +85,28 @@
 }
 
 // Compare EncryptionPass() results to the example in
-// draft-ietf-quic-load-balancers-14, Section 4.3.2.
+// draft-ietf-quic-load-balancers-15, Section 4.3.2.
 TEST_F(LoadBalancerConfigTest, TestEncryptionPassExample) {
   auto config =
       LoadBalancerConfig::Create(0, 3, 4, absl::string_view(raw_key, 16));
   EXPECT_TRUE(config.has_value());
   EXPECT_TRUE(config->IsEncrypted());
-  std::array<uint8_t, 7> bytes = {
-      0x31, 0x44, 0x1a, 0x9c, 0x69, 0xc2, 0x75,
-  };
+  std::array<uint8_t, 7> bytes = {0x31, 0x44, 0x1a, 0x9c, 0x69, 0xc2, 0x75};
+  std::array<uint8_t, 7> pass1 = {0x31, 0x44, 0x1a, 0x9f, 0x1a, 0x5b, 0x6b};
+  std::array<uint8_t, 7> pass2 = {0x02, 0x8e, 0x1b, 0x5f, 0x1a, 0x5b, 0x6b};
+  std::array<uint8_t, 7> pass3 = {0x02, 0x8e, 0x1b, 0x54, 0x94, 0x97, 0x62};
+  std::array<uint8_t, 7> pass4 = {0x8e, 0x9a, 0x91, 0xf4, 0x94, 0x97, 0x62};
+
   // Input is too short.
   EXPECT_FALSE(config->EncryptionPass(absl::Span<uint8_t>(bytes.data(), 6), 0));
   EXPECT_TRUE(config->EncryptionPass(absl::Span<uint8_t>(bytes), 1));
-  EXPECT_TRUE((bytes == std::array<uint8_t, 7>(
-                            {0x31, 0x44, 0x1a, 0x92, 0x52, 0xaa, 0xef})));
+  EXPECT_EQ(bytes, pass1);
   EXPECT_TRUE(config->EncryptionPass(absl::Span<uint8_t>(bytes), 2));
-  EXPECT_TRUE((bytes == std::array<uint8_t, 7>(
-                            {0xe6, 0xa1, 0x3a, 0xb2, 0x52, 0xaa, 0xef})));
+  EXPECT_EQ(bytes, pass2);
   EXPECT_TRUE(config->EncryptionPass(absl::Span<uint8_t>(bytes), 3));
-  EXPECT_TRUE((bytes == std::array<uint8_t, 7>(
-                            {0xe6, 0xa1, 0x3a, 0xbc, 0xe1, 0xe0, 0xd2})));
+  EXPECT_EQ(bytes, pass3);
   EXPECT_TRUE(config->EncryptionPass(absl::Span<uint8_t>(bytes), 4));
-  EXPECT_TRUE((bytes == std::array<uint8_t, 7>(
-                            {0x32, 0xc3, 0x63, 0xfc, 0xe1, 0xe0, 0xd2})));
+  EXPECT_EQ(bytes, pass4);
 }
 
 TEST_F(LoadBalancerConfigTest, EncryptionPassPlaintext) {
@@ -116,6 +115,25 @@
   EXPECT_FALSE(config->EncryptionPass(absl::Span<uint8_t>(bytes), 1));
 }
 
+// Check that the encryption pass code can decode its own ciphertext. Various
+// pointer errors could cause the code to overwrite bits that contain
+// important information.
+TEST_F(LoadBalancerConfigTest, EncryptionPassesAreReversible) {
+  auto config =
+      LoadBalancerConfig::Create(0, 3, 4, absl::string_view(raw_key, 16));
+  std::array<uint8_t, 7> bytes = {
+      0x31, 0x44, 0x1a, 0x9c, 0x69, 0xc2, 0x75,
+  };
+  std::array<uint8_t, 7> orig_bytes;
+  memcpy(orig_bytes.data(), bytes.data(), bytes.size());
+  // Work left->right and right->left passes.
+  EXPECT_TRUE(config->EncryptionPass(absl::Span<uint8_t>(bytes), 1));
+  EXPECT_TRUE(config->EncryptionPass(absl::Span<uint8_t>(bytes), 2));
+  EXPECT_TRUE(config->EncryptionPass(absl::Span<uint8_t>(bytes), 2));
+  EXPECT_TRUE(config->EncryptionPass(absl::Span<uint8_t>(bytes), 1));
+  EXPECT_EQ(bytes, orig_bytes);
+}
+
 TEST_F(LoadBalancerConfigTest, InvalidBlockEncryption) {
   uint8_t pt[kLoadBalancerBlockSize], ct[kLoadBalancerBlockSize];
   auto pt_config = LoadBalancerConfig::CreateUnencrypted(0, 8, 8);
@@ -133,7 +151,7 @@
 }
 
 // Block decrypt test from the Test Vector in
-// draft-ietf-quic-load-balancers-14, Appendix B.
+// draft-ietf-quic-load-balancers-15, Appendix B.
 TEST_F(LoadBalancerConfigTest, BlockEncryptionExample) {
   const uint8_t ptext[] = {0xed, 0x79, 0x3a, 0x51, 0xd4, 0x9b, 0x8f, 0x5f,
                            0xee, 0x08, 0x0d, 0xbf, 0x48, 0xc0, 0xd1, 0xe5};
diff --git a/quiche/quic/load_balancer/load_balancer_decoder_test.cc b/quiche/quic/load_balancer/load_balancer_decoder_test.cc
index ee1feac..728467f 100644
--- a/quiche/quic/load_balancer/load_balancer_decoder_test.cc
+++ b/quiche/quic/load_balancer/load_balancer_decoder_test.cc
@@ -57,7 +57,7 @@
   }
 }
 
-// Compare test vectors from Appendix B of draft-ietf-quic-load-balancers-14.
+// Compare test vectors from Appendix B of draft-ietf-quic-load-balancers-15.
 TEST_F(LoadBalancerDecoderTest, DecoderTestVectors) {
   // Try (1) the "standard" CID length of 8
   // (2) server_id_len > nonce_len, so there is a fourth decryption pass
@@ -66,13 +66,13 @@
   const struct LoadBalancerDecoderTestCase test_vectors[4] = {
       {
           *LoadBalancerConfig::Create(0, 3, 4, kKey),
-          QuicConnectionId({0x07, 0x27, 0xed, 0xaa, 0x37, 0xe7, 0xfa, 0xc8}),
+          QuicConnectionId({0x07, 0x41, 0x26, 0xee, 0x38, 0xbf, 0x54, 0x54}),
           MakeServerId(kServerId, 3),
       },
       {
           *LoadBalancerConfig::Create(1, 10, 5, kKey),
-          QuicConnectionId({0x4f, 0x22, 0x61, 0x4a, 0x97, 0xce, 0xee, 0x84,
-                            0x34, 0x1e, 0xd7, 0xfb, 0xfe, 0xb1, 0xe6, 0xe2}),
+          QuicConnectionId({0x4f, 0xcd, 0x3f, 0x57, 0x2d, 0x4e, 0xef, 0xb0,
+                            0x46, 0xfd, 0xb5, 0x1d, 0x16, 0x4e, 0xfc, 0xcc}),
           MakeServerId(kServerId, 10),
       },
       {
@@ -84,9 +84,9 @@
       },
       {
           *LoadBalancerConfig::Create(0, 9, 9, kKey),
-          QuicConnectionId({0x12, 0x5e, 0x3b, 0x00, 0xaa, 0x5f, 0xcf, 0xd1,
-                            0xa9, 0xa5, 0x81, 0x02, 0xa8, 0x9a, 0x19, 0xa1,
-                            0xe4, 0xa1, 0x0e}),
+          QuicConnectionId({0x12, 0x12, 0x4d, 0x1e, 0xb8, 0xfb, 0xb2, 0x1e,
+                            0x4a, 0x49, 0x0c, 0xa5, 0x3c, 0xfe, 0x21, 0xd0,
+                            0x4a, 0xe6, 0x3a}),
           MakeServerId(kServerId, 9),
       },
   };
diff --git a/quiche/quic/load_balancer/load_balancer_encoder_test.cc b/quiche/quic/load_balancer/load_balancer_encoder_test.cc
index 12d8fc7..88d09f4 100644
--- a/quiche/quic/load_balancer/load_balancer_encoder_test.cc
+++ b/quiche/quic/load_balancer/load_balancer_encoder_test.cc
@@ -184,7 +184,7 @@
   }
 }
 
-// Follow example in draft-ietf-quic-load-balancers-14.
+// Follow example in draft-ietf-quic-load-balancers-15.
 TEST_F(LoadBalancerEncoderTest, FollowSpecExample) {
   const uint8_t config_id = 0, server_id_len = 3, nonce_len = 4;
   const uint8_t raw_server_id[] = {
@@ -205,14 +205,14 @@
   EXPECT_TRUE(encoder->UpdateConfig(
       *config, *LoadBalancerServerId::Create(raw_server_id)));
   EXPECT_TRUE(encoder->IsEncoding());
-  const char raw_connection_id[] = {0x07, 0x32, 0xc3, 0x63,
-                                    0xfc, 0xe1, 0xe0, 0xd2};
+  const char raw_connection_id[] = {0x07, 0x8e, 0x9a, 0x91,
+                                    0xf4, 0x94, 0x97, 0x62};
   auto expected =
       QuicConnectionId(raw_connection_id, 1 + server_id_len + nonce_len);
   EXPECT_EQ(encoder->GenerateConnectionId(), expected);
 }
 
-// Compare test vectors from Appendix B of draft-ietf-quic-load-balancers-14.
+// Compare test vectors from Appendix B of draft-ietf-quic-load-balancers-15.
 TEST_F(LoadBalancerEncoderTest, EncoderTestVectors) {
   // Try (1) the "standard" ConnectionId length of 8
   // (2) server_id_len > nonce_len, so there is a fourth decryption pass
@@ -221,13 +221,13 @@
   const LoadBalancerEncoderTestCase test_vectors[4] = {
       {
           *LoadBalancerConfig::Create(0, 3, 4, kKey),
-          QuicConnectionId({0x07, 0x27, 0xed, 0xaa, 0x37, 0xe7, 0xfa, 0xc8}),
+          QuicConnectionId({0x07, 0x41, 0x26, 0xee, 0x38, 0xbf, 0x54, 0x54}),
           MakeServerId(kServerId, 3),
       },
       {
           *LoadBalancerConfig::Create(1, 10, 5, kKey),
-          QuicConnectionId({0x4f, 0x22, 0x61, 0x4a, 0x97, 0xce, 0xee, 0x84,
-                            0x34, 0x1e, 0xd7, 0xfb, 0xfe, 0xb1, 0xe6, 0xe2}),
+          QuicConnectionId({0x4f, 0xcd, 0x3f, 0x57, 0x2d, 0x4e, 0xef, 0xb0,
+                            0x46, 0xfd, 0xb5, 0x1d, 0x16, 0x4e, 0xfc, 0xcc}),
           MakeServerId(kServerId, 10),
       },
       {
@@ -239,9 +239,9 @@
       },
       {
           *LoadBalancerConfig::Create(0, 9, 9, kKey),
-          QuicConnectionId({0x12, 0x5e, 0x3b, 0x00, 0xaa, 0x5f, 0xcf, 0xd1,
-                            0xa9, 0xa5, 0x81, 0x02, 0xa8, 0x9a, 0x19, 0xa1,
-                            0xe4, 0xa1, 0x0e}),
+          QuicConnectionId({0x12, 0x12, 0x4d, 0x1e, 0xb8, 0xfb, 0xb2, 0x1e,
+                            0x4a, 0x49, 0x0c, 0xa5, 0x3c, 0xfe, 0x21, 0xd0,
+                            0x4a, 0xe6, 0x3a}),
           MakeServerId(kServerId, 9),
       },
   };
@@ -267,7 +267,7 @@
   LoadBalancerEncoderPeer::SetNumNoncesLeft(*encoder, 2);
   EXPECT_EQ(encoder->num_nonces_left(), 2);
   EXPECT_EQ(encoder->GenerateConnectionId(),
-            QuicConnectionId({0x07, 0xf4, 0xeb, 0x21, 0xfb, 0x22, 0xa8, 0x40}));
+            QuicConnectionId({0x07, 0x1d, 0x4a, 0xb8, 0xc6, 0x1d, 0xd6, 0x5d}));
   EXPECT_EQ(encoder->num_nonces_left(), 1);
   encoder->GenerateConnectionId();
   EXPECT_EQ(encoder->IsEncoding(), false);