xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/load_balancer/load_balancer_config.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2022 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/quic/load_balancer/load_balancer_config.h"
6 
7 #include <cstdint>
8 #include <cstring>
9 #include <optional>
10 
11 #include "absl/strings/string_view.h"
12 #include "absl/types/span.h"
13 #include "openssl/aes.h"
14 #include "quiche/quic/core/quic_connection_id.h"
15 #include "quiche/quic/load_balancer/load_balancer_server_id.h"
16 #include "quiche/quic/platform/api/quic_bug_tracker.h"
17 
18 namespace quic {
19 
20 namespace {
21 
22 // Validates all non-key parts of the input.
CommonValidation(const uint8_t config_id,const uint8_t server_id_len,const uint8_t nonce_len)23 bool CommonValidation(const uint8_t config_id, const uint8_t server_id_len,
24                       const uint8_t nonce_len) {
25   if (config_id >= kNumLoadBalancerConfigs || server_id_len == 0 ||
26       nonce_len < kLoadBalancerMinNonceLen ||
27       nonce_len > kLoadBalancerMaxNonceLen ||
28       server_id_len >
29           (kQuicMaxConnectionIdWithLengthPrefixLength - nonce_len - 1)) {
30     QUIC_BUG(quic_bug_433862549_01)
31         << "Invalid LoadBalancerConfig "
32         << "Config ID " << static_cast<int>(config_id) << " Server ID Length "
33         << static_cast<int>(server_id_len) << " Nonce Length "
34         << static_cast<int>(nonce_len);
35     return false;
36   }
37   return true;
38 }
39 
40 // Initialize the key in the constructor
BuildKey(absl::string_view key,bool encrypt)41 std::optional<AES_KEY> BuildKey(absl::string_view key, bool encrypt) {
42   if (key.empty()) {
43     return std::optional<AES_KEY>();
44   }
45   AES_KEY raw_key;
46   if (encrypt) {
47     if (AES_set_encrypt_key(reinterpret_cast<const uint8_t *>(key.data()),
48                             key.size() * 8, &raw_key) < 0) {
49       return std::optional<AES_KEY>();
50     }
51   } else if (AES_set_decrypt_key(reinterpret_cast<const uint8_t *>(key.data()),
52                                  key.size() * 8, &raw_key) < 0) {
53     return std::optional<AES_KEY>();
54   }
55   return raw_key;
56 }
57 
58 }  // namespace
59 
Create(const uint8_t config_id,const uint8_t server_id_len,const uint8_t nonce_len,const absl::string_view key)60 std::optional<LoadBalancerConfig> LoadBalancerConfig::Create(
61     const uint8_t config_id, const uint8_t server_id_len,
62     const uint8_t nonce_len, const absl::string_view key) {
63   //  Check for valid parameters.
64   if (key.size() != kLoadBalancerKeyLen) {
65     QUIC_BUG(quic_bug_433862549_02)
66         << "Invalid LoadBalancerConfig Key Length: " << key.size();
67     return std::optional<LoadBalancerConfig>();
68   }
69   if (!CommonValidation(config_id, server_id_len, nonce_len)) {
70     return std::optional<LoadBalancerConfig>();
71   }
72   auto new_config =
73       LoadBalancerConfig(config_id, server_id_len, nonce_len, key);
74   if (!new_config.IsEncrypted()) {
75     // Something went wrong in assigning the key!
76     QUIC_BUG(quic_bug_433862549_03) << "Something went wrong in initializing "
77                                        "the load balancing key.";
78     return std::optional<LoadBalancerConfig>();
79   }
80   return new_config;
81 }
82 
83 // Creates an unencrypted config.
CreateUnencrypted(const uint8_t config_id,const uint8_t server_id_len,const uint8_t nonce_len)84 std::optional<LoadBalancerConfig> LoadBalancerConfig::CreateUnencrypted(
85     const uint8_t config_id, const uint8_t server_id_len,
86     const uint8_t nonce_len) {
87   return CommonValidation(config_id, server_id_len, nonce_len)
88              ? LoadBalancerConfig(config_id, server_id_len, nonce_len, "")
89              : std::optional<LoadBalancerConfig>();
90 }
91 
92 // Note that |ciphertext| does not include the first byte of the connection ID.
FourPassDecrypt(absl::Span<const uint8_t> ciphertext,LoadBalancerServerId & server_id) const93 bool LoadBalancerConfig::FourPassDecrypt(
94     absl::Span<const uint8_t> ciphertext,
95     LoadBalancerServerId& server_id) const {
96   if (ciphertext.size() < plaintext_len()) {
97     QUIC_BUG(quic_bug_599862571_02)
98         << "Called FourPassDecrypt with a short Connection ID";
99     return false;
100   }
101   if (!key_.has_value()) {
102     return false;
103   }
104   // Do 3 or 4 passes. Only 3 are necessary if the server_id is short enough
105   // to fit in the first half of the connection ID (the decoder doesn't need
106   // to extract the nonce).
107   uint8_t* left = server_id.mutable_data();
108   uint8_t right[kLoadBalancerBlockSize];
109   uint8_t half_len;  // half the length of the plaintext, rounded up
110   bool is_length_odd =
111       InitializeFourPass(ciphertext.data(), left, right, &half_len);
112   uint8_t end_index = (server_id_len_ > nonce_len_) ? 1 : 2;
113   for (uint8_t index = kNumLoadBalancerCryptoPasses; index >= end_index;
114        --index) {
115     // Encrypt left/right and xor the result with right/left, respectively.
116     EncryptionPass(index, half_len, is_length_odd, left, right);
117   }
118   // Consolidate left and right into a server ID with minimum copying.
119   if (server_id_len_ < half_len ||
120       (server_id_len_ == half_len && !is_length_odd)) {
121     // There is no half-byte to handle. Server ID is already written in to
122     // server_id.
123     return true;
124   }
125   if (is_length_odd) {
126     right[0] |= *(left + --half_len);  // Combine the halves of the odd byte.
127   }
128   memcpy(server_id.mutable_data() + half_len, right, server_id_len_ - half_len);
129   return true;
130 }
131 
132 // Note that |plaintext| includes the first byte of the connection ID.
FourPassEncrypt(absl::Span<uint8_t> plaintext) const133 QuicConnectionId LoadBalancerConfig::FourPassEncrypt(
134     absl::Span<uint8_t> plaintext) const {
135   if (plaintext.size() < total_len()) {
136     QUIC_BUG(quic_bug_599862571_03)
137         << "Called FourPassEncrypt with a short Connection ID";
138     return QuicConnectionId();
139   }
140   if (!key_.has_value()) {
141     return QuicConnectionId();
142   }
143   uint8_t left[kLoadBalancerBlockSize];
144   uint8_t right[kLoadBalancerBlockSize];
145   uint8_t half_len;  // half the length of the plaintext, rounded up
146   bool is_length_odd =
147       InitializeFourPass(plaintext.data() + 1, left, right, &half_len);
148   for (uint8_t index = 1; index <= kNumLoadBalancerCryptoPasses; ++index) {
149     EncryptionPass(index, half_len, is_length_odd, left, right);
150   }
151   // Consolidate left and right into a server ID with minimum copying.
152   if (is_length_odd) {
153     // Combine the halves of the odd byte.
154     right[0] |= left[--half_len];
155   }
156   memcpy(plaintext.data() + 1, left, half_len);
157   memcpy(plaintext.data() + half_len + 1, right, plaintext_len() - half_len);
158   return QuicConnectionId(reinterpret_cast<char*>(plaintext.data()),
159                           total_len());
160 }
161 
BlockEncrypt(const uint8_t plaintext[kLoadBalancerBlockSize],uint8_t ciphertext[kLoadBalancerBlockSize]) const162 bool LoadBalancerConfig::BlockEncrypt(
163     const uint8_t plaintext[kLoadBalancerBlockSize],
164     uint8_t ciphertext[kLoadBalancerBlockSize]) const {
165   if (!key_.has_value()) {
166     return false;
167   }
168   AES_encrypt(plaintext, ciphertext, &*key_);
169   return true;
170 }
171 
BlockDecrypt(const uint8_t ciphertext[kLoadBalancerBlockSize],uint8_t plaintext[kLoadBalancerBlockSize]) const172 bool LoadBalancerConfig::BlockDecrypt(
173     const uint8_t ciphertext[kLoadBalancerBlockSize],
174     uint8_t plaintext[kLoadBalancerBlockSize]) const {
175   if (!block_decrypt_key_.has_value()) {
176     return false;
177   }
178   AES_decrypt(ciphertext, plaintext, &*block_decrypt_key_);
179   return true;
180 }
181 
LoadBalancerConfig(const uint8_t config_id,const uint8_t server_id_len,const uint8_t nonce_len,const absl::string_view key)182 LoadBalancerConfig::LoadBalancerConfig(const uint8_t config_id,
183                                        const uint8_t server_id_len,
184                                        const uint8_t nonce_len,
185                                        const absl::string_view key)
186     : config_id_(config_id),
187       server_id_len_(server_id_len),
188       nonce_len_(nonce_len),
189       key_(BuildKey(key, /* encrypt = */ true)),
190       block_decrypt_key_((server_id_len + nonce_len == kLoadBalancerBlockSize)
191                              ? BuildKey(key, /* encrypt = */ false)
192                              : std::optional<AES_KEY>()) {}
193 
194 // Note that |input| does not include the first byte of the connection ID.
InitializeFourPass(const uint8_t * input,uint8_t * left,uint8_t * right,uint8_t * half_len) const195 bool LoadBalancerConfig::InitializeFourPass(const uint8_t* input, uint8_t* left,
196                                             uint8_t* right,
197                                             uint8_t* half_len) const {
198   *half_len = plaintext_len() / 2;
199   bool is_length_odd;
200   if (plaintext_len() % 2 == 1) {
201     ++(*half_len);
202     is_length_odd = true;
203   } else {
204     is_length_odd = false;
205   }
206   memset(left, 0, kLoadBalancerBlockSize);
207   memset(right, 0, kLoadBalancerBlockSize);
208   // The first byte is the plaintext/ciphertext length, the second byte will be
209   // the index of the pass. Half the plaintext or ciphertext follows.
210   left[kLoadBalancerBlockSize - 2] = plaintext_len();
211   right[kLoadBalancerBlockSize - 2] = plaintext_len();
212   // Leave left_[15]], right_[15] as zero. It will be set for each pass.
213   memcpy(left, input, *half_len);
214   // If is_length_odd, then both left and right will have part of the middle
215   // byte. Then that middle byte will be split in half via the bitmask in the
216   // next step.
217   memcpy(right, input + (plaintext_len() / 2), *half_len);
218   if (is_length_odd) {
219     left[*half_len - 1] &= 0xf0;
220     right[0] &= 0x0f;
221   }
222   return is_length_odd;
223 }
224 
EncryptionPass(uint8_t index,uint8_t half_len,bool is_length_odd,uint8_t * left,uint8_t * right) const225 void LoadBalancerConfig::EncryptionPass(uint8_t index, uint8_t half_len,
226                                         bool is_length_odd, uint8_t* left,
227                                         uint8_t* right) const {
228   uint8_t ciphertext[kLoadBalancerBlockSize];
229   if (index % 2 == 0) {  // Go right to left.
230     right[kLoadBalancerBlockSize - 1] = index;
231     AES_encrypt(right, ciphertext, &*key_);
232     for (int i = 0; i < half_len; ++i) {
233       // Skip over the first two bytes, which have the plaintext_len and the
234       // index. The CID bits are in [2, half_len - 1].
235       left[i] ^= ciphertext[i];
236     }
237     if (is_length_odd) {
238       left[half_len - 1] &= 0xf0;
239     }
240     return;
241   }
242   // Go left to right.
243   left[kLoadBalancerBlockSize - 1] = index;
244   AES_encrypt(left, ciphertext, &*key_);
245   for (int i = 0; i < half_len; ++i) {
246     right[i] ^= ciphertext[i];
247   }
248   if (is_length_odd) {
249     right[0] &= 0x0f;
250   }
251 }
252 
253 }  // namespace quic
254