mirror of
https://github.com/Waujito/youtubeUnblock.git
synced 2026-01-27 12:40:36 +03:00
277 lines
7.7 KiB
C
277 lines
7.7 KiB
C
/*
|
|
youtubeUnblock - https://github.com/Waujito/youtubeUnblock
|
|
|
|
Copyright (C) 2024-2025 Vadim Vetrov <vetrovvd@gmail.com>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "quic.h"
|
|
#include "kdf/hkdf.h"
|
|
#include "hash/sha256.h"
|
|
#include "cipher/aes.h"
|
|
#include "cipher_modes/ecb.h"
|
|
#include "aead/gcm.h"
|
|
#include "logging.h"
|
|
|
|
const uint8_t quic_client_in_info[] = "\0\x20\x0ftls13 client in\0";
|
|
const uint8_t quic_key_info[] = "\0\x10\x0etls13 quic key\0";
|
|
const uint8_t quic_iv_info[] = "\0\x0c\x0dtls13 quic iv\0";
|
|
const uint8_t quic_hp_info[] = "\0\x10\x0dtls13 quic hp\0";
|
|
const uint8_t quic2_key_info[] = "\0\x10\x10tls13 quicv2 key\0";
|
|
const uint8_t quic2_iv_info[] = "\0\x0c\x0ftls13 quicv2 iv\0";
|
|
const uint8_t quic2_hp_info[] = "\0\x10\x0ftls13 quicv2 hp\0";
|
|
|
|
int quic_parse_initial_message(
|
|
const uint8_t *quic_payload, size_t quic_plen,
|
|
uint8_t **udecrypted_payload, size_t *udecrypted_payload_len,
|
|
const uint8_t **udecrypted_message, size_t *udecrypted_message_len
|
|
) {
|
|
int ret;
|
|
const struct quic_lhdr *qch;
|
|
size_t qch_len;
|
|
struct quic_cids qci;
|
|
const uint8_t *inpayload;
|
|
size_t inplen;
|
|
struct quici_hdr qich;
|
|
|
|
size_t quic_header_len;
|
|
size_t inheader_len;
|
|
struct quici_lhdr_typespec qich_ltspc;
|
|
int packet_number_length;
|
|
const uint8_t *packet_number = NULL;
|
|
const uint8_t *protected_payload = NULL;
|
|
size_t protected_payload_length;
|
|
|
|
uint8_t initial_secret[QUIC_INITIAL_SECRET_SIZE];
|
|
uint8_t client_initial_secret[QUIC_CLIENT_IN_SIZE];
|
|
uint8_t quic_key[QUIC_KEY_SIZE];
|
|
uint8_t quic_iv[QUIC_IV_SIZE];
|
|
uint8_t quic_hp[QUIC_HP_SIZE];
|
|
uint8_t mask[QUIC_SAMPLE_SIZE];
|
|
uint8_t *decrypted_payload = NULL;
|
|
size_t decrypted_payload_len;
|
|
uint8_t *decrypted_packet_number = NULL;
|
|
uint8_t *dcptr = NULL;
|
|
// Decrypted plain message without header
|
|
uint8_t *decrypted_message = NULL;
|
|
size_t decrypted_message_len;
|
|
AesContext actx;
|
|
GcmContext gctx;
|
|
uint32_t qversion;
|
|
const uint8_t *iv_info;
|
|
size_t iv_info_size;
|
|
const uint8_t *key_info;
|
|
size_t key_info_size;
|
|
const uint8_t *hp_info;
|
|
size_t hp_info_size;
|
|
const uint8_t *initial_salt;
|
|
size_t initial_salt_size;
|
|
|
|
ret = quic_parse_data(quic_payload, quic_plen,
|
|
&qch, &qch_len, &qci, &inpayload, &inplen
|
|
);
|
|
if (ret < 0) {
|
|
lgerror(ret, "quic_parse_data");
|
|
goto error_nfr;
|
|
}
|
|
|
|
ret = quic_get_version(&qversion, qch);
|
|
if (ret < 0) {
|
|
return -EINVAL;
|
|
}
|
|
if (!quic_check_is_initial(qch)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (qversion) {
|
|
case QUIC_V1:
|
|
iv_info = quic_iv_info;
|
|
iv_info_size = sizeof(quic_iv_info) - 1;
|
|
key_info = quic_key_info;
|
|
key_info_size = sizeof(quic_key_info) - 1;
|
|
hp_info = quic_hp_info;
|
|
hp_info_size = sizeof(quic_hp_info) - 1;
|
|
initial_salt = (const uint8_t *)QUIC_INITIAL_SALT_V1;
|
|
initial_salt_size = sizeof(QUIC_INITIAL_SALT_V1) - 1;
|
|
break;
|
|
case QUIC_V2:
|
|
iv_info = quic2_iv_info;
|
|
iv_info_size = sizeof(quic2_iv_info) - 1;
|
|
key_info = quic2_key_info;
|
|
key_info_size = sizeof(quic2_key_info) - 1;
|
|
hp_info = quic2_hp_info;
|
|
hp_info_size = sizeof(quic2_hp_info) - 1;
|
|
initial_salt = (const uint8_t *)QUIC_INITIAL_SALT_V2;
|
|
initial_salt_size = sizeof(QUIC_INITIAL_SALT_V2) - 1;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
quic_header_len = inpayload - quic_payload;
|
|
|
|
ret = quic_parse_initial_header(inpayload, inplen, &qich);
|
|
if (ret < 0) {
|
|
lgerror(ret, "quic_parse_initial_header");
|
|
goto error_nfr;
|
|
}
|
|
|
|
inheader_len = qich.protected_payload - inpayload;
|
|
|
|
decrypted_payload_len = quic_header_len + inplen;
|
|
decrypted_payload = malloc(decrypted_payload_len);
|
|
if (decrypted_payload == NULL) {
|
|
ret = -ENOMEM;
|
|
goto error_nfr;
|
|
}
|
|
dcptr = decrypted_payload;
|
|
// Copy quic large header
|
|
memcpy(dcptr, quic_payload, quic_header_len);
|
|
dcptr += quic_header_len;
|
|
|
|
|
|
// Copy quic initial large header (until packet number)
|
|
memcpy(dcptr, inpayload, inheader_len);
|
|
dcptr += inheader_len;
|
|
|
|
|
|
ret = hkdfExtract(SHA256_HASH_ALGO, (const unsigned char *)qci.dst_id, qci.dst_len, initial_salt, initial_salt_size, initial_secret);
|
|
if (ret) {
|
|
lgerr("hkdfExtract initial_secret: %d", ret);
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
ret = hkdfExpand(SHA256_HASH_ALGO, initial_secret, SHA256_DIGEST_SIZE, quic_client_in_info, sizeof(quic_client_in_info) - 1, client_initial_secret, QUIC_CLIENT_IN_SIZE);
|
|
if (ret) {
|
|
lgerr("hkdfExpand client_initial_secret: %d", ret);
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
ret = hkdfExpand(SHA256_HASH_ALGO, client_initial_secret, SHA256_DIGEST_SIZE, key_info, key_info_size, quic_key, QUIC_KEY_SIZE);
|
|
if (ret) {
|
|
lgerr("hkdfExpand quic_key: %d", ret);
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
ret = hkdfExpand(SHA256_HASH_ALGO, client_initial_secret, SHA256_DIGEST_SIZE, iv_info, iv_info_size, quic_iv, QUIC_IV_SIZE);
|
|
if (ret) {
|
|
lgerr("hkdfExpand quic_iv: %d", ret);
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
ret = hkdfExpand(SHA256_HASH_ALGO, client_initial_secret, SHA256_DIGEST_SIZE, hp_info, hp_info_size, quic_hp, QUIC_HP_SIZE);
|
|
if (ret) {
|
|
lgerr("hkdfExpand quic_hp: %d", ret);
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
// Decrypt packet number length and packet number
|
|
ret = aesInit(&actx, quic_hp, QUIC_HP_SIZE);
|
|
if (ret) {
|
|
lgerr("aesInit with quic_hp: %d", ret);
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
ret = ecbEncrypt(&aesCipherAlgo, &actx,
|
|
qich.sample, mask, qich.sample_length);
|
|
if (ret) {
|
|
lgerr("ecbEncrypt for mask: %d", ret);
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
// Update decrypted payload header with decrypted packet_number_length
|
|
decrypted_payload[0] ^= mask[0] & 0x0f;
|
|
|
|
qich_ltspc = (struct quici_lhdr_typespec){decrypted_payload[0]};
|
|
|
|
packet_number_length = qich_ltspc.number_length + 1;
|
|
if (qich.length < packet_number_length) {
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
packet_number = qich.protected_payload;
|
|
protected_payload = qich.protected_payload + packet_number_length;
|
|
protected_payload_length = qich.length - packet_number_length;
|
|
|
|
decrypted_packet_number = dcptr;
|
|
|
|
for (int i = 0; i < packet_number_length; i++) {
|
|
decrypted_packet_number[i] = packet_number[i] ^ mask[i + 1];
|
|
}
|
|
dcptr += packet_number_length;
|
|
|
|
for (int i = QUIC_IV_SIZE - packet_number_length, j = 0;
|
|
i < QUIC_IV_SIZE; i++, j++) {
|
|
|
|
quic_iv[i] ^= decrypted_packet_number[j];
|
|
}
|
|
|
|
ret = aesInit(&actx, quic_key, QUIC_KEY_SIZE);
|
|
if (ret) {
|
|
lgerr("aesInit for quic_key: %d", ret);
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
ret = gcmInit(&gctx, &aesCipherAlgo, &actx);
|
|
if (ret) {
|
|
lgerr("gcmInit: %d", ret);
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
decrypted_message = dcptr;
|
|
|
|
ret = gcmDecrypt(
|
|
&gctx, quic_iv, QUIC_IV_SIZE,
|
|
NULL, 0,
|
|
protected_payload, decrypted_message, protected_payload_length,
|
|
quic_key, QUIC_KEY_SIZE
|
|
);
|
|
if (ret != 0 && ret != ERROR_FAILURE) {
|
|
lgerr("gcmInit: %d", ret);
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
// TAG is padded in the end of decrypted message
|
|
decrypted_message_len = protected_payload_length - QUIC_TAG_SIZE;
|
|
|
|
if (!udecrypted_payload) {
|
|
lgerr("decrypted_payload cannot be NULL!");
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
*udecrypted_payload = decrypted_payload;
|
|
if (udecrypted_payload_len)
|
|
*udecrypted_payload_len = decrypted_payload_len;
|
|
if (udecrypted_message)
|
|
*udecrypted_message = decrypted_message;
|
|
if (udecrypted_message_len)
|
|
*udecrypted_message_len = decrypted_message_len;
|
|
|
|
return 0;
|
|
error:
|
|
free(decrypted_payload);
|
|
error_nfr:
|
|
return ret;
|
|
}
|