Skip to content

Commit 05c0b70

Browse files
authored
[+] add encryption option to load balancer cid generator (#212)
1 parent 23be25c commit 05c0b70

File tree

9 files changed

+397
-7
lines changed

9 files changed

+397
-7
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ set(
171171
"src/transport/xqc_utils.c"
172172
"src/transport/xqc_defs.c"
173173
"src/transport/xqc_transport_params.c"
174+
"src/transport/xqc_quic_lb.c"
174175
)
175176

176177
# TLS source

cmake/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ set(
172172
"src/transport/xqc_utils.c"
173173
"src/transport/xqc_defs.c"
174174
"src/transport/xqc_transport_params.c"
175+
"src/transport/xqc_quic_lb.c"
175176
)
176177

177178
# TLS source

include/xquic/xqc_errno.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ typedef enum {
117117
XQC_EMP_INVALID_FRAME = 654, /* Multipath - invalid frame */
118118
XQC_EMP_INVALID_QOE_SIGNAL = 660, /* Multipath - invalid qoe signal */
119119

120+
XQC_EENCRYPT_LB_CID = 670, /* load balance connection ID encryption error */
121+
XQC_EENCRYPT_AES_128_ECB = 671, /* aes_128_ecb algorithm error */
122+
120123
XQC_E_MAX,
121124
} xqc_transport_error_t;
122125

include/xquic/xquic.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,23 @@ xqc_int_t xqc_conn_continue_send(xqc_engine_t *engine, const xqc_cid_t *cid);
11631163
XQC_EXPORT_PUBLIC_API
11641164
xqc_conn_stats_t xqc_conn_get_stats(xqc_engine_t *engine, const xqc_cid_t *cid);
11651165

1166+
/**
1167+
* @brief load balance cid encryption.
1168+
* According to Draft : https://datatracker.ietf.org/doc/html/draft-ietf-quic-load-balancers-13#section-4.3.2
1169+
* @param enc_len plaintext length.
1170+
* @param cid_buf the plaintext to be encrypted.
1171+
* @param out_buf the ciphertext of the plaintext encrypted.
1172+
* @param out_buf_len the length of the ciphertext to be encrypted.
1173+
* @param lb_cid_key encryption secret.
1174+
* @param lb_cid_key_len secret length.
1175+
* @param engine engine from `xqc_engine_create`
1176+
* @return negative for failed, 0 for the success.
1177+
*
1178+
* The length of cid_buf must not exceed the maximum length of the cid (20 byte), the length of out_buf should be no less than cid_buf_length.
1179+
* The length of lb_cid_key should be exactly 16 bytes.
1180+
*/
1181+
XQC_EXPORT_PUBLIC_API
1182+
xqc_int_t xqc_lb_cid_encryption(uint8_t *cid_buf, size_t enc_len, uint8_t *out_buf, size_t out_buf_len, uint8_t *lb_cid_key, size_t lb_cid_key_len, xqc_engine_t *engine);
11661183

11671184
#ifdef __cplusplus
11681185
}

include/xquic/xquic_typedef.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ typedef uint8_t xqc_bool_t;
109109
#define XQC_MAX_CID_LEN 20
110110
#define XQC_MIN_CID_LEN 4
111111

112+
/* restrictions of key length in lb cid encryption */
113+
#define XQC_LB_CID_KEY_LEN 16
114+
112115
typedef struct xqc_cid_s {
113116
uint8_t cid_len;
114117
uint8_t cid_buf[XQC_MAX_CID_LEN];

scripts/case_test.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,21 @@ else
10351035
case_print_result "load_balancer_cid_generate" "fail"
10361036
fi
10371037

1038+
clear_log
1039+
killall test_server 2> /dev/null
1040+
echo -e "load balancer cid generate with encryption...\c"
1041+
./test_server -l d -e -S "server_id_0" -E > /dev/null &
1042+
sleep 1
1043+
./test_client -s 1024000 -l d -t 1 >> clog
1044+
result=`grep "|lb cid encrypted|" slog`
1045+
errlog=`grep_err_log`
1046+
if [ -z "$errlog" ] && [ "$result" != "" ]; then
1047+
echo ">>>>>>>> pass:1"
1048+
case_print_result "load_balancer_cid_generate_with_encryption" "pass"
1049+
else
1050+
echo ">>>>>>>> pass:0"
1051+
case_print_result "load_balancer_cid_generate_with_encryption" "fail"
1052+
fi
10381053

10391054
clear_log
10401055
echo -e "set cipher suites ...\c"

scripts/xquic.lds

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ XQUIC_VERS_1.0 {
6767
xqc_h3_request_finish;
6868
xqc_now;
6969
xqc_h3_engine_set_qpack_compat_duplicate;
70+
xqc_lb_cid_encryption;
7071
local:
7172
*;
7273
};

src/transport/xqc_quic_lb.c

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
#include <openssl/evp.h>
2+
#include <openssl/ssl.h>
3+
#include <xquic/xquic.h>
4+
#include "src/transport/xqc_engine.h"
5+
6+
7+
#define XQC_MAX_TRUNCATE_LEN 128
8+
#define XQC_EN_SINGLE_PASS_ENCRYPTION_LEN 16
9+
#define XQC_FIRST_OCTET 1
10+
11+
/* Each encrypted CID creates and releases a cipher ctx, which may occupies cpu resources a lot. It should be optimaized in the future.*/
12+
xqc_int_t
13+
xqc_cid_encryption_aes_128_ecb(unsigned char *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, uint8_t *key, size_t key_len, xqc_engine_t *engine)
14+
{
15+
xqc_int_t update_len = 0, final_len = 0;
16+
xqc_log_t *log = engine->log;
17+
18+
if (plaintext_len != XQC_EN_SINGLE_PASS_ENCRYPTION_LEN) {
19+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid aes_128_ecb encryption parameter plaintext'length illegal(expect = 16)|");
20+
return -XQC_EPARAM;
21+
}
22+
23+
if (plaintext_len != ciphertext_len) {
24+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid aes_128_ecb encryption parameter plaintext and ciphertext illegal(expect equals in length)|");
25+
return -XQC_EPARAM;
26+
}
27+
28+
if (key_len != XQC_LB_CID_KEY_LEN) {
29+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid aes_128_ecb encryption parameter key'length illegal(expect = 16)|");
30+
return -XQC_EPARAM;
31+
}
32+
33+
34+
EVP_CIPHER_CTX *cipher_ctx = EVP_CIPHER_CTX_new();
35+
if (!cipher_ctx) {
36+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid aes_128_ecb encryption ctx generate error|");
37+
EVP_CIPHER_CTX_free(cipher_ctx);
38+
return -XQC_EENCRYPT_AES_128_ECB;
39+
}
40+
41+
if (!EVP_EncryptInit_ex(cipher_ctx, EVP_aes_128_ecb(), NULL, key, NULL)) {
42+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid aes_128_ecb encryption init error|");
43+
EVP_CIPHER_CTX_free(cipher_ctx);
44+
return -XQC_EENCRYPT_AES_128_ECB;
45+
}
46+
47+
EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);
48+
49+
if (!EVP_EncryptUpdate(cipher_ctx, ciphertext, &update_len, plaintext, plaintext_len)) {
50+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid aes_128_ecb encryption update error|");
51+
EVP_CIPHER_CTX_free(cipher_ctx);
52+
return -XQC_EENCRYPT_AES_128_ECB;
53+
}
54+
55+
if (!EVP_EncryptFinal_ex(cipher_ctx, ciphertext + update_len, &final_len)) {
56+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid four-aes_128_ecb encryption final error|");
57+
EVP_CIPHER_CTX_free(cipher_ctx);
58+
return -XQC_EENCRYPT_AES_128_ECB;
59+
}
60+
61+
EVP_CIPHER_CTX_free(cipher_ctx);
62+
63+
return XQC_OK;
64+
}
65+
66+
__uint128_t
67+
xqc_expand_left(__uint128_t left, __uint128_t right)
68+
{
69+
__uint128_t out = 0;
70+
__uint128_t left_bound = 0xf;
71+
72+
left_bound <<= 124;
73+
out |= left;
74+
75+
if (out != 0) {
76+
out <<= (128 - 80);
77+
while ((out & left_bound) == 0){
78+
out <<= 4;
79+
}
80+
}
81+
out |= right;
82+
return out;
83+
}
84+
85+
__uint128_t
86+
xqc_expand_right(__uint128_t left, __uint128_t right)
87+
{
88+
return xqc_expand_left(right, left);
89+
}
90+
91+
__uint128_t
92+
xqc_n_bit_1(xqc_int_t n)
93+
{
94+
__uint128_t out = 0;
95+
for (xqc_int_t i = 0; i < n; i++) {
96+
out <<= 1;
97+
out |= 1;
98+
}
99+
return out;
100+
}
101+
102+
__uint128_t
103+
xqc_truncate_right(__uint128_t in, xqc_int_t cut_len, __uint128_t *out, xqc_engine_t *engine)
104+
{
105+
xqc_log_t *log = engine->log;
106+
if (cut_len > XQC_MAX_TRUNCATE_LEN) {
107+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid xqc_truncate_right parameter `cut_len` overflow(expect <= 128)|");
108+
return -XQC_EPARAM;
109+
}
110+
__uint128_t flag;
111+
flag = 0;
112+
flag = xqc_n_bit_1(cut_len);
113+
*out = flag & in;
114+
return XQC_OK ;
115+
}
116+
117+
__uint128_t
118+
xqc_truncate_left(__uint128_t in, xqc_int_t cut_len, __uint128_t *out, xqc_engine_t *engine)
119+
{
120+
xqc_log_t *log = engine->log;
121+
if (cut_len > XQC_MAX_TRUNCATE_LEN) {
122+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid xqc_truncate_left parameter `cut_len` overflow(expect <= 128)|");
123+
return -XQC_EPARAM;
124+
}
125+
__uint128_t flag;
126+
flag = 0;
127+
flag = xqc_n_bit_1(cut_len);
128+
flag <<= (XQC_MAX_TRUNCATE_LEN - cut_len);
129+
*out = (flag & in) >> (XQC_MAX_TRUNCATE_LEN - cut_len);
130+
return XQC_OK;
131+
}
132+
133+
void
134+
xqc_array_right_shift(uint8_t *in_out, xqc_int_t bits, xqc_int_t in_out_len)
135+
{
136+
if (bits == 0) {
137+
return;
138+
}
139+
xqc_int_t i = 0;
140+
while (i < in_out_len) {
141+
/* keep the lowest `bits(int)` bits unchanged when i=0 */
142+
if (i) {
143+
in_out[i] >>= bits;
144+
}
145+
if (i + 1 < in_out_len) {
146+
uint8_t tmp = xqc_n_bit_1(bits);
147+
tmp = in_out[i + 1] & tmp;
148+
tmp <<= (8 - bits);
149+
in_out[i] |= tmp;
150+
}
151+
i++;
152+
}
153+
}
154+
155+
xqc_int_t
156+
xqc_cid_encryption_four_pass(uint8_t *in, size_t in_len, uint8_t *out, size_t out_len, uint8_t *key, size_t key_len, xqc_engine_t *engine)
157+
{
158+
__uint128_t left_0, right_0, left_1, right_1, left_2, right_2;
159+
__uint128_t tmp_out, tmp_exp, tmp_tru;
160+
xqc_int_t bits_per_byte = 8;
161+
xqc_int_t octet_per_128bits = 16;
162+
xqc_int_t left_len_bit, right_len_bit, left_len_byte, right_len_byte;
163+
xqc_int_t shift_offset;
164+
xqc_log_t *log = engine->log;
165+
xqc_int_t ret;
166+
167+
if (in_len > XQC_MAX_CID_LEN - XQC_FIRST_OCTET) {
168+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid four-pass encryption parameter in_lengtn illegal(expect > 0 && <= 19)|");
169+
return -XQC_EPARAM;
170+
}
171+
172+
if (out_len < in_len) {
173+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid four-pass encryption parameter out_len illegal(expect no less than in_len)|");
174+
return -XQC_EPARAM;
175+
}
176+
177+
if (key_len != XQC_LB_CID_KEY_LEN) {
178+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid four-pass encryption parameter key'length illegal(expect = 16)|");
179+
return -XQC_EPARAM;
180+
}
181+
182+
left_len_bit = right_len_bit = in_len * 4;
183+
left_len_byte = right_len_byte = (in_len + 1) / 2;
184+
shift_offset = right_len_byte * 8 - right_len_bit;
185+
186+
memset(&left_0, 0, sizeof(left_0));
187+
memset(&left_1, 0, sizeof(left_1));
188+
memset(&left_2, 0, sizeof(left_2));
189+
memset(&right_0, 0, sizeof(right_0));
190+
memset(&right_1, 0, sizeof(right_1));
191+
memset(&right_2, 0, sizeof(right_2));
192+
193+
memcpy(&left_0, in + right_len_byte - 1, left_len_byte);
194+
left_0 >>= right_len_bit - (right_len_byte - 1) * 8;
195+
memcpy(&right_0, in, right_len_byte);
196+
right_0 &= xqc_n_bit_1(right_len_bit);
197+
198+
tmp_exp = xqc_expand_left(left_0, 0x01);
199+
ret = xqc_cid_encryption_aes_128_ecb((uint8_t *)&tmp_exp, octet_per_128bits, (uint8_t *)&tmp_out, octet_per_128bits, key, key_len, engine);
200+
if (ret != XQC_OK) {
201+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass encryption first-pass aes encryption error|%d|", ret);
202+
return ret;
203+
}
204+
ret = xqc_truncate_right(tmp_out, right_len_bit, &tmp_tru, engine);
205+
if (ret != XQC_OK) {
206+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass encryption first-pass truncate error|%d|", ret);
207+
return ret;
208+
}
209+
right_1 = right_0 ^ tmp_tru;
210+
211+
tmp_exp = xqc_expand_right(right_1, 0x02);
212+
ret = xqc_cid_encryption_aes_128_ecb((uint8_t *)&tmp_exp, octet_per_128bits, (uint8_t *)&tmp_out, octet_per_128bits, key, key_len, engine);
213+
if (ret != XQC_OK) {
214+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass second-pass aes encryption error|%d|", ret);
215+
return ret;
216+
}
217+
ret = xqc_truncate_left(tmp_out, left_len_bit, &tmp_tru, engine);
218+
if (ret != XQC_OK) {
219+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass encryption second-pass truncate error|%d|", ret);
220+
return ret;
221+
}
222+
left_1 = left_0 ^ tmp_tru;
223+
224+
tmp_exp = xqc_expand_left(left_1, 0x03);
225+
ret = xqc_cid_encryption_aes_128_ecb((uint8_t *)&tmp_exp, octet_per_128bits, (uint8_t *)&tmp_out, octet_per_128bits, key, key_len, engine);
226+
if (ret != XQC_OK) {
227+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass third-pass aes encryption error|%d|", ret);
228+
return ret;
229+
}
230+
ret = xqc_truncate_right(tmp_out, right_len_bit, &tmp_tru, engine);
231+
if (ret != XQC_OK) {
232+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass encryption third-pass truncate error|%d|", ret);
233+
return ret;
234+
}
235+
right_2= right_1 ^ tmp_tru;
236+
237+
tmp_exp = xqc_expand_right(right_2, 0x04);
238+
ret = xqc_cid_encryption_aes_128_ecb((uint8_t *)&tmp_exp, octet_per_128bits, (uint8_t *)&tmp_out, octet_per_128bits, key, key_len, engine);
239+
if (ret != XQC_OK) {
240+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass fourth-pass aes encryption error|%d|", ret);
241+
return ret;
242+
}
243+
ret = xqc_truncate_left(tmp_out, left_len_bit, &tmp_tru, engine);
244+
if (ret != XQC_OK) {
245+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass encryption fourth-pass truncate error|%d|", ret);
246+
return ret;
247+
}
248+
left_2 = left_1 ^ tmp_tru;
249+
250+
memcpy(out + right_len_byte, &left_2, left_len_byte);
251+
memcpy(out, &right_2, right_len_byte);
252+
/* The lowest right shift byte will overwrite the high-order bits of its right byte, thus the function requires the address of (startbyte - 1) */
253+
xqc_array_right_shift(out + right_len_byte - 1, shift_offset, left_len_byte + 1);
254+
255+
return XQC_OK;
256+
}
257+
258+
/**
259+
* @brief load balance cid encryption.
260+
* According to Draft : https://datatracker.ietf.org/doc/html/draft-ietf-quic-load-balancers-13#section-4.3
261+
*/
262+
xqc_int_t
263+
xqc_lb_cid_encryption(uint8_t *cid_buf, size_t enc_len, uint8_t *out_buf, size_t out_buf_len, uint8_t *lb_cid_key, size_t lb_cid_key_len, xqc_engine_t *engine)
264+
{
265+
size_t cid_buf_len = enc_len + XQC_FIRST_OCTET;
266+
267+
xqc_log_t *log = engine->log;
268+
269+
if (cid_buf_len > XQC_MAX_CID_LEN) {
270+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error| parameter enc_len illegal(expect <= 19)|");
271+
return -XQC_EPARAM;
272+
}
273+
274+
if (lb_cid_key_len != XQC_LB_CID_KEY_LEN) {
275+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error| parameter lb_cid_key illegal(expect = 16)|");
276+
return -XQC_EPARAM;
277+
}
278+
279+
if (out_buf_len < cid_buf_len) {
280+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error| parameter out_buf_len illegal(expect no less than cid_buf_len)|");
281+
return -XQC_EPARAM;
282+
}
283+
284+
if (enc_len == XQC_EN_SINGLE_PASS_ENCRYPTION_LEN) {
285+
xqc_int_t res = xqc_cid_encryption_aes_128_ecb(cid_buf + XQC_FIRST_OCTET, enc_len, out_buf + XQC_FIRST_OCTET, enc_len, lb_cid_key, lb_cid_key_len, engine);
286+
if (res < XQC_OK) {
287+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid aes_128_ecb encryption error|%d|", res);
288+
return -XQC_EENCRYPT_LB_CID;
289+
}
290+
} else {
291+
xqc_int_t res = xqc_cid_encryption_four_pass(cid_buf + XQC_FIRST_OCTET, enc_len, out_buf + XQC_FIRST_OCTET, enc_len, lb_cid_key, lb_cid_key_len, engine);
292+
if (res < XQC_OK) {
293+
xqc_log(log, XQC_LOG_ERROR, "|lb-cid four-pass encryption error|%d|", res);
294+
return -XQC_EENCRYPT_LB_CID;
295+
}
296+
}
297+
298+
unsigned char tmp_cid_buf[XQC_MAX_CID_LEN * 2 + 1];
299+
xqc_hex_dump(tmp_cid_buf, cid_buf, enc_len);
300+
tmp_cid_buf[enc_len * 2] = '\0';
301+
unsigned char tmp_out_buf[XQC_MAX_CID_LEN * 2 + 1];
302+
xqc_hex_dump(tmp_out_buf, out_buf, enc_len);
303+
tmp_out_buf[enc_len * 2] = '\0';
304+
xqc_log(log, XQC_LOG_INFO, "|lb cid encrypted|ori:%s|new:%s|",
305+
tmp_cid_buf, tmp_out_buf);
306+
return XQC_OK;
307+
}

0 commit comments

Comments
 (0)