Skip to content

Commit

Permalink
[+] add encryption option to load balancer cid generator (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
ruiqizhou authored Jul 26, 2022
1 parent 23be25c commit 05c0b70
Show file tree
Hide file tree
Showing 9 changed files with 397 additions and 7 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ set(
"src/transport/xqc_utils.c"
"src/transport/xqc_defs.c"
"src/transport/xqc_transport_params.c"
"src/transport/xqc_quic_lb.c"
)

# TLS source
Expand Down
1 change: 1 addition & 0 deletions cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ set(
"src/transport/xqc_utils.c"
"src/transport/xqc_defs.c"
"src/transport/xqc_transport_params.c"
"src/transport/xqc_quic_lb.c"
)

# TLS source
Expand Down
3 changes: 3 additions & 0 deletions include/xquic/xqc_errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ typedef enum {
XQC_EMP_INVALID_FRAME = 654, /* Multipath - invalid frame */
XQC_EMP_INVALID_QOE_SIGNAL = 660, /* Multipath - invalid qoe signal */

XQC_EENCRYPT_LB_CID = 670, /* load balance connection ID encryption error */
XQC_EENCRYPT_AES_128_ECB = 671, /* aes_128_ecb algorithm error */

XQC_E_MAX,
} xqc_transport_error_t;

Expand Down
17 changes: 17 additions & 0 deletions include/xquic/xquic.h
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,23 @@ xqc_int_t xqc_conn_continue_send(xqc_engine_t *engine, const xqc_cid_t *cid);
XQC_EXPORT_PUBLIC_API
xqc_conn_stats_t xqc_conn_get_stats(xqc_engine_t *engine, const xqc_cid_t *cid);

/**
* @brief load balance cid encryption.
* According to Draft : https://datatracker.ietf.org/doc/html/draft-ietf-quic-load-balancers-13#section-4.3.2
* @param enc_len plaintext length.
* @param cid_buf the plaintext to be encrypted.
* @param out_buf the ciphertext of the plaintext encrypted.
* @param out_buf_len the length of the ciphertext to be encrypted.
* @param lb_cid_key encryption secret.
* @param lb_cid_key_len secret length.
* @param engine engine from `xqc_engine_create`
* @return negative for failed, 0 for the success.
*
* 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.
* The length of lb_cid_key should be exactly 16 bytes.
*/
XQC_EXPORT_PUBLIC_API
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);

#ifdef __cplusplus
}
Expand Down
3 changes: 3 additions & 0 deletions include/xquic/xquic_typedef.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ typedef uint8_t xqc_bool_t;
#define XQC_MAX_CID_LEN 20
#define XQC_MIN_CID_LEN 4

/* restrictions of key length in lb cid encryption */
#define XQC_LB_CID_KEY_LEN 16

typedef struct xqc_cid_s {
uint8_t cid_len;
uint8_t cid_buf[XQC_MAX_CID_LEN];
Expand Down
15 changes: 15 additions & 0 deletions scripts/case_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,21 @@ else
case_print_result "load_balancer_cid_generate" "fail"
fi

clear_log
killall test_server 2> /dev/null
echo -e "load balancer cid generate with encryption...\c"
./test_server -l d -e -S "server_id_0" -E > /dev/null &
sleep 1
./test_client -s 1024000 -l d -t 1 >> clog
result=`grep "|lb cid encrypted|" slog`
errlog=`grep_err_log`
if [ -z "$errlog" ] && [ "$result" != "" ]; then
echo ">>>>>>>> pass:1"
case_print_result "load_balancer_cid_generate_with_encryption" "pass"
else
echo ">>>>>>>> pass:0"
case_print_result "load_balancer_cid_generate_with_encryption" "fail"
fi

clear_log
echo -e "set cipher suites ...\c"
Expand Down
1 change: 1 addition & 0 deletions scripts/xquic.lds
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ XQUIC_VERS_1.0 {
xqc_h3_request_finish;
xqc_now;
xqc_h3_engine_set_qpack_compat_duplicate;
xqc_lb_cid_encryption;
local:
*;
};
307 changes: 307 additions & 0 deletions src/transport/xqc_quic_lb.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
#include <openssl/evp.h>
#include <openssl/ssl.h>
#include <xquic/xquic.h>
#include "src/transport/xqc_engine.h"


#define XQC_MAX_TRUNCATE_LEN 128
#define XQC_EN_SINGLE_PASS_ENCRYPTION_LEN 16
#define XQC_FIRST_OCTET 1

/* Each encrypted CID creates and releases a cipher ctx, which may occupies cpu resources a lot. It should be optimaized in the future.*/
xqc_int_t
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)
{
xqc_int_t update_len = 0, final_len = 0;
xqc_log_t *log = engine->log;

if (plaintext_len != XQC_EN_SINGLE_PASS_ENCRYPTION_LEN) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid aes_128_ecb encryption parameter plaintext'length illegal(expect = 16)|");
return -XQC_EPARAM;
}

if (plaintext_len != ciphertext_len) {
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)|");
return -XQC_EPARAM;
}

if (key_len != XQC_LB_CID_KEY_LEN) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid aes_128_ecb encryption parameter key'length illegal(expect = 16)|");
return -XQC_EPARAM;
}


EVP_CIPHER_CTX *cipher_ctx = EVP_CIPHER_CTX_new();
if (!cipher_ctx) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid aes_128_ecb encryption ctx generate error|");
EVP_CIPHER_CTX_free(cipher_ctx);
return -XQC_EENCRYPT_AES_128_ECB;
}

if (!EVP_EncryptInit_ex(cipher_ctx, EVP_aes_128_ecb(), NULL, key, NULL)) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid aes_128_ecb encryption init error|");
EVP_CIPHER_CTX_free(cipher_ctx);
return -XQC_EENCRYPT_AES_128_ECB;
}

EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);

if (!EVP_EncryptUpdate(cipher_ctx, ciphertext, &update_len, plaintext, plaintext_len)) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid aes_128_ecb encryption update error|");
EVP_CIPHER_CTX_free(cipher_ctx);
return -XQC_EENCRYPT_AES_128_ECB;
}

if (!EVP_EncryptFinal_ex(cipher_ctx, ciphertext + update_len, &final_len)) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid four-aes_128_ecb encryption final error|");
EVP_CIPHER_CTX_free(cipher_ctx);
return -XQC_EENCRYPT_AES_128_ECB;
}

EVP_CIPHER_CTX_free(cipher_ctx);

return XQC_OK;
}

__uint128_t
xqc_expand_left(__uint128_t left, __uint128_t right)
{
__uint128_t out = 0;
__uint128_t left_bound = 0xf;

left_bound <<= 124;
out |= left;

if (out != 0) {
out <<= (128 - 80);
while ((out & left_bound) == 0){
out <<= 4;
}
}
out |= right;
return out;
}

__uint128_t
xqc_expand_right(__uint128_t left, __uint128_t right)
{
return xqc_expand_left(right, left);
}

__uint128_t
xqc_n_bit_1(xqc_int_t n)
{
__uint128_t out = 0;
for (xqc_int_t i = 0; i < n; i++) {
out <<= 1;
out |= 1;
}
return out;
}

__uint128_t
xqc_truncate_right(__uint128_t in, xqc_int_t cut_len, __uint128_t *out, xqc_engine_t *engine)
{
xqc_log_t *log = engine->log;
if (cut_len > XQC_MAX_TRUNCATE_LEN) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid xqc_truncate_right parameter `cut_len` overflow(expect <= 128)|");
return -XQC_EPARAM;
}
__uint128_t flag;
flag = 0;
flag = xqc_n_bit_1(cut_len);
*out = flag & in;
return XQC_OK ;
}

__uint128_t
xqc_truncate_left(__uint128_t in, xqc_int_t cut_len, __uint128_t *out, xqc_engine_t *engine)
{
xqc_log_t *log = engine->log;
if (cut_len > XQC_MAX_TRUNCATE_LEN) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid xqc_truncate_left parameter `cut_len` overflow(expect <= 128)|");
return -XQC_EPARAM;
}
__uint128_t flag;
flag = 0;
flag = xqc_n_bit_1(cut_len);
flag <<= (XQC_MAX_TRUNCATE_LEN - cut_len);
*out = (flag & in) >> (XQC_MAX_TRUNCATE_LEN - cut_len);
return XQC_OK;
}

void
xqc_array_right_shift(uint8_t *in_out, xqc_int_t bits, xqc_int_t in_out_len)
{
if (bits == 0) {
return;
}
xqc_int_t i = 0;
while (i < in_out_len) {
/* keep the lowest `bits(int)` bits unchanged when i=0 */
if (i) {
in_out[i] >>= bits;
}
if (i + 1 < in_out_len) {
uint8_t tmp = xqc_n_bit_1(bits);
tmp = in_out[i + 1] & tmp;
tmp <<= (8 - bits);
in_out[i] |= tmp;
}
i++;
}
}

xqc_int_t
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)
{
__uint128_t left_0, right_0, left_1, right_1, left_2, right_2;
__uint128_t tmp_out, tmp_exp, tmp_tru;
xqc_int_t bits_per_byte = 8;
xqc_int_t octet_per_128bits = 16;
xqc_int_t left_len_bit, right_len_bit, left_len_byte, right_len_byte;
xqc_int_t shift_offset;
xqc_log_t *log = engine->log;
xqc_int_t ret;

if (in_len > XQC_MAX_CID_LEN - XQC_FIRST_OCTET) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid four-pass encryption parameter in_lengtn illegal(expect > 0 && <= 19)|");
return -XQC_EPARAM;
}

if (out_len < in_len) {
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)|");
return -XQC_EPARAM;
}

if (key_len != XQC_LB_CID_KEY_LEN) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error|lb-cid four-pass encryption parameter key'length illegal(expect = 16)|");
return -XQC_EPARAM;
}

left_len_bit = right_len_bit = in_len * 4;
left_len_byte = right_len_byte = (in_len + 1) / 2;
shift_offset = right_len_byte * 8 - right_len_bit;

memset(&left_0, 0, sizeof(left_0));
memset(&left_1, 0, sizeof(left_1));
memset(&left_2, 0, sizeof(left_2));
memset(&right_0, 0, sizeof(right_0));
memset(&right_1, 0, sizeof(right_1));
memset(&right_2, 0, sizeof(right_2));

memcpy(&left_0, in + right_len_byte - 1, left_len_byte);
left_0 >>= right_len_bit - (right_len_byte - 1) * 8;
memcpy(&right_0, in, right_len_byte);
right_0 &= xqc_n_bit_1(right_len_bit);

tmp_exp = xqc_expand_left(left_0, 0x01);
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);
if (ret != XQC_OK) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass encryption first-pass aes encryption error|%d|", ret);
return ret;
}
ret = xqc_truncate_right(tmp_out, right_len_bit, &tmp_tru, engine);
if (ret != XQC_OK) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass encryption first-pass truncate error|%d|", ret);
return ret;
}
right_1 = right_0 ^ tmp_tru;

tmp_exp = xqc_expand_right(right_1, 0x02);
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);
if (ret != XQC_OK) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass second-pass aes encryption error|%d|", ret);
return ret;
}
ret = xqc_truncate_left(tmp_out, left_len_bit, &tmp_tru, engine);
if (ret != XQC_OK) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass encryption second-pass truncate error|%d|", ret);
return ret;
}
left_1 = left_0 ^ tmp_tru;

tmp_exp = xqc_expand_left(left_1, 0x03);
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);
if (ret != XQC_OK) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass third-pass aes encryption error|%d|", ret);
return ret;
}
ret = xqc_truncate_right(tmp_out, right_len_bit, &tmp_tru, engine);
if (ret != XQC_OK) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass encryption third-pass truncate error|%d|", ret);
return ret;
}
right_2= right_1 ^ tmp_tru;

tmp_exp = xqc_expand_right(right_2, 0x04);
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);
if (ret != XQC_OK) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass fourth-pass aes encryption error|%d|", ret);
return ret;
}
ret = xqc_truncate_left(tmp_out, left_len_bit, &tmp_tru, engine);
if (ret != XQC_OK) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid generate|lb-cid four-pass encryption fourth-pass truncate error|%d|", ret);
return ret;
}
left_2 = left_1 ^ tmp_tru;

memcpy(out + right_len_byte, &left_2, left_len_byte);
memcpy(out, &right_2, right_len_byte);
/* The lowest right shift byte will overwrite the high-order bits of its right byte, thus the function requires the address of (startbyte - 1) */
xqc_array_right_shift(out + right_len_byte - 1, shift_offset, left_len_byte + 1);

return XQC_OK;
}

/**
* @brief load balance cid encryption.
* According to Draft : https://datatracker.ietf.org/doc/html/draft-ietf-quic-load-balancers-13#section-4.3
*/
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)
{
size_t cid_buf_len = enc_len + XQC_FIRST_OCTET;

xqc_log_t *log = engine->log;

if (cid_buf_len > XQC_MAX_CID_LEN) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error| parameter enc_len illegal(expect <= 19)|");
return -XQC_EPARAM;
}

if (lb_cid_key_len != XQC_LB_CID_KEY_LEN) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error| parameter lb_cid_key illegal(expect = 16)|");
return -XQC_EPARAM;
}

if (out_buf_len < cid_buf_len) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid encryption error| parameter out_buf_len illegal(expect no less than cid_buf_len)|");
return -XQC_EPARAM;
}

if (enc_len == XQC_EN_SINGLE_PASS_ENCRYPTION_LEN) {
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);
if (res < XQC_OK) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid aes_128_ecb encryption error|%d|", res);
return -XQC_EENCRYPT_LB_CID;
}
} else {
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);
if (res < XQC_OK) {
xqc_log(log, XQC_LOG_ERROR, "|lb-cid four-pass encryption error|%d|", res);
return -XQC_EENCRYPT_LB_CID;
}
}

unsigned char tmp_cid_buf[XQC_MAX_CID_LEN * 2 + 1];
xqc_hex_dump(tmp_cid_buf, cid_buf, enc_len);
tmp_cid_buf[enc_len * 2] = '\0';
unsigned char tmp_out_buf[XQC_MAX_CID_LEN * 2 + 1];
xqc_hex_dump(tmp_out_buf, out_buf, enc_len);
tmp_out_buf[enc_len * 2] = '\0';
xqc_log(log, XQC_LOG_INFO, "|lb cid encrypted|ori:%s|new:%s|",
tmp_cid_buf, tmp_out_buf);
return XQC_OK;
}
Loading

0 comments on commit 05c0b70

Please sign in to comment.