/*
* Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/err.h>
#include "internal/common.h"
#include "internal/quic_wire_pkt.h"
int ossl_quic_hdr_protector_init(QUIC_HDR_PROTECTOR *hpr,
OSSL_LIB_CTX *libctx,
const char *propq,
uint32_t cipher_id,
const unsigned char *quic_hp_key,
size_t quic_hp_key_len)
{
const char *cipher_name = NULL;
switch (cipher_id) {
case QUIC_HDR_PROT_CIPHER_AES_128:
cipher_name = "AES-128-ECB";
break;
case QUIC_HDR_PROT_CIPHER_AES_256:
cipher_name = "AES-256-ECB";
break;
case QUIC_HDR_PROT_CIPHER_CHACHA:
cipher_name = "ChaCha20";
break;
default:
ERR_raise(ERR_LIB_SSL, ERR_R_UNSUPPORTED);
return 0;
}
hpr->cipher_ctx = EVP_CIPHER_CTX_new();
if (hpr->cipher_ctx == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);
return 0;
}
hpr->cipher = EVP_CIPHER_fetch(libctx, cipher_name, propq);
if (hpr->cipher == NULL
|| quic_hp_key_len != (size_t)EVP_CIPHER_get_key_length(hpr->cipher)) {
ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);
goto err;
}
if (!EVP_CipherInit_ex(hpr->cipher_ctx, hpr->cipher, NULL,
quic_hp_key, NULL, 1)) {
ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);
goto err;
}
hpr->libctx = libctx;
hpr->propq = propq;
hpr->cipher_id = cipher_id;
return 1;
err:
ossl_quic_hdr_protector_cleanup(hpr);
return 0;
}
void ossl_quic_hdr_protector_cleanup(QUIC_HDR_PROTECTOR *hpr)
{
EVP_CIPHER_CTX_free(hpr->cipher_ctx);
hpr->cipher_ctx = NULL;
EVP_CIPHER_free(hpr->cipher);
hpr->cipher = NULL;
}
static int hdr_generate_mask(QUIC_HDR_PROTECTOR *hpr,
const unsigned char *sample, size_t sample_len,
unsigned char *mask)
{
int l = 0;
unsigned char dst[16];
static const unsigned char zeroes[5] = {0};
size_t i;
if (hpr->cipher_id == QUIC_HDR_PROT_CIPHER_AES_128
|| hpr->cipher_id == QUIC_HDR_PROT_CIPHER_AES_256) {
if (sample_len < 16) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
return 0;
}
if (!EVP_CipherInit_ex(hpr->cipher_ctx, NULL, NULL, NULL, NULL, 1)
|| !EVP_CipherUpdate(hpr->cipher_ctx, dst, &l, sample, 16)) {
ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);
return 0;
}
for (i = 0; i < 5; ++i)
mask[i] = dst[i];
} else if (hpr->cipher_id == QUIC_HDR_PROT_CIPHER_CHACHA) {
if (sample_len < 16) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
return 0;
}
if (!EVP_CipherInit_ex(hpr->cipher_ctx, NULL, NULL, NULL, sample, 1)
|| !EVP_CipherUpdate(hpr->cipher_ctx, mask, &l,
zeroes, sizeof(zeroes))) {
ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);
return 0;
}
} else {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
assert(0);
return 0;
}
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
/* No matter what we did above we use the same mask in fuzzing mode */
memset(mask, 0, 5);
#endif
return 1;
}
int ossl_quic_hdr_protector_decrypt(QUIC_HDR_PROTECTOR *hpr,
QUIC_PKT_HDR_PTRS *ptrs)
{
return ossl_quic_hdr_protector_decrypt_fields(hpr,
ptrs->raw_sample,
ptrs->raw_sample_len,
ptrs->raw_start,
ptrs->