/*
* Copyright 2022 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 "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:
return 0;
}
hpr->cipher_ctx = EVP_CIPHER_CTX_new();
if (hpr->cipher_ctx == NULL)
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))
goto err;
if (!EVP_CipherInit_ex(hpr->cipher_ctx, hpr->cipher, NULL,
quic_hp_key, NULL, 1))
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)
return 0;
if (!EVP_CipherInit_ex(hpr->cipher_ctx, NULL, NULL, NULL, NULL, 1)
|| !EVP_CipherUpdate(hpr->cipher_ctx, dst, &l, sample, 16))
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)
return 0;
if (!EVP_CipherInit_ex(hpr->cipher_ctx, NULL, NULL, NULL, sample, 1)
|| !EVP_CipherUpdate(hpr->cipher_ctx, mask, &l,
zeroes, sizeof(zeroes)))
return 0;
} else {
assert(0);
return 0;
}
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->raw_pn);
}
int ossl_quic_hdr_protector_decrypt_fields(QUIC_HDR_PROTECTOR *hpr,
const unsigned char *sample,
size_t sample_len,
unsigned char *first_byte,
unsigned char *pn_bytes)
{
unsigned char mask[5], pn_len, i;
if (!hdr_generate_mask(hpr, sample, sample_len, mask))
return 0;
*first_byte ^= mask[0] & ((*first_byte & 0x80) != 0 ? 0xf : 0x1f);
pn_len = (*first_byte & 0x3) + 1;
for (i = 0; i < pn_len; ++i)
pn_bytes[i] ^= mask[i + 1];
return 1;
}
int ossl_quic_hdr_protector_encrypt(QUIC_HDR_PROTECTOR *hpr,
QUIC_PKT_HDR_PTRS *ptrs)
{
return ossl_quic_hdr_protector_encrypt_fields(hpr,
ptrs->raw_sample,
ptrs->raw_sample_len,
ptrs->raw_start,
ptrs