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