Path: blob/main/crypto/openssl/ssl/quic/quic_wire_pkt.c
108779 views
/*1* Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89#include <openssl/err.h>10#include "internal/common.h"11#include "internal/quic_wire_pkt.h"1213int ossl_quic_hdr_protector_init(QUIC_HDR_PROTECTOR *hpr,14OSSL_LIB_CTX *libctx,15const char *propq,16uint32_t cipher_id,17const unsigned char *quic_hp_key,18size_t quic_hp_key_len)19{20const char *cipher_name = NULL;2122switch (cipher_id) {23case QUIC_HDR_PROT_CIPHER_AES_128:24cipher_name = "AES-128-ECB";25break;26case QUIC_HDR_PROT_CIPHER_AES_256:27cipher_name = "AES-256-ECB";28break;29case QUIC_HDR_PROT_CIPHER_CHACHA:30cipher_name = "ChaCha20";31break;32default:33ERR_raise(ERR_LIB_SSL, ERR_R_UNSUPPORTED);34return 0;35}3637hpr->cipher_ctx = EVP_CIPHER_CTX_new();38if (hpr->cipher_ctx == NULL) {39ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);40return 0;41}4243hpr->cipher = EVP_CIPHER_fetch(libctx, cipher_name, propq);44if (hpr->cipher == NULL45|| quic_hp_key_len != (size_t)EVP_CIPHER_get_key_length(hpr->cipher)) {46ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);47goto err;48}4950if (!EVP_CipherInit_ex(hpr->cipher_ctx, hpr->cipher, NULL,51quic_hp_key, NULL, 1)) {52ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);53goto err;54}5556hpr->libctx = libctx;57hpr->propq = propq;58hpr->cipher_id = cipher_id;59return 1;6061err:62ossl_quic_hdr_protector_cleanup(hpr);63return 0;64}6566void ossl_quic_hdr_protector_cleanup(QUIC_HDR_PROTECTOR *hpr)67{68EVP_CIPHER_CTX_free(hpr->cipher_ctx);69hpr->cipher_ctx = NULL;7071EVP_CIPHER_free(hpr->cipher);72hpr->cipher = NULL;73}7475static int hdr_generate_mask(QUIC_HDR_PROTECTOR *hpr,76const unsigned char *sample, size_t sample_len,77unsigned char *mask)78{79int l = 0;80unsigned char dst[16];81static const unsigned char zeroes[5] = { 0 };82size_t i;8384if (hpr->cipher_id == QUIC_HDR_PROT_CIPHER_AES_12885|| hpr->cipher_id == QUIC_HDR_PROT_CIPHER_AES_256) {86if (sample_len < 16) {87ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);88return 0;89}9091if (!EVP_CipherInit_ex(hpr->cipher_ctx, NULL, NULL, NULL, NULL, 1)92|| !EVP_CipherUpdate(hpr->cipher_ctx, dst, &l, sample, 16)) {93ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);94return 0;95}9697for (i = 0; i < 5; ++i)98mask[i] = dst[i];99} else if (hpr->cipher_id == QUIC_HDR_PROT_CIPHER_CHACHA) {100if (sample_len < 16) {101ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);102return 0;103}104105if (!EVP_CipherInit_ex(hpr->cipher_ctx, NULL, NULL, NULL, sample, 1)106|| !EVP_CipherUpdate(hpr->cipher_ctx, mask, &l,107zeroes, sizeof(zeroes))) {108ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);109return 0;110}111} else {112ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);113assert(0);114return 0;115}116117#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION118/* No matter what we did above we use the same mask in fuzzing mode */119memset(mask, 0, 5);120#endif121122return 1;123}124125int ossl_quic_hdr_protector_decrypt(QUIC_HDR_PROTECTOR *hpr,126QUIC_PKT_HDR_PTRS *ptrs)127{128return ossl_quic_hdr_protector_decrypt_fields(hpr,129ptrs->raw_sample,130ptrs->raw_sample_len,131ptrs->raw_start,132ptrs->raw_pn);133}134135int ossl_quic_hdr_protector_decrypt_fields(QUIC_HDR_PROTECTOR *hpr,136const unsigned char *sample,137size_t sample_len,138unsigned char *first_byte,139unsigned char *pn_bytes)140{141unsigned char mask[5], pn_len, i;142143if (!hdr_generate_mask(hpr, sample, sample_len, mask))144return 0;145146*first_byte ^= mask[0] & ((*first_byte & 0x80) != 0 ? 0xf : 0x1f);147pn_len = (*first_byte & 0x3) + 1;148149for (i = 0; i < pn_len; ++i)150pn_bytes[i] ^= mask[i + 1];151152return 1;153}154155int ossl_quic_hdr_protector_encrypt(QUIC_HDR_PROTECTOR *hpr,156QUIC_PKT_HDR_PTRS *ptrs)157{158return ossl_quic_hdr_protector_encrypt_fields(hpr,159ptrs->raw_sample,160ptrs->raw_sample_len,161ptrs->raw_start,162ptrs->raw_pn);163}164165int ossl_quic_hdr_protector_encrypt_fields(QUIC_HDR_PROTECTOR *hpr,166const unsigned char *sample,167size_t sample_len,168unsigned char *first_byte,169unsigned char *pn_bytes)170{171unsigned char mask[5], pn_len, i;172173if (!hdr_generate_mask(hpr, sample, sample_len, mask))174return 0;175176pn_len = (*first_byte & 0x3) + 1;177for (i = 0; i < pn_len; ++i)178pn_bytes[i] ^= mask[i + 1];179180*first_byte ^= mask[0] & ((*first_byte & 0x80) != 0 ? 0xf : 0x1f);181return 1;182}183184int ossl_quic_wire_decode_pkt_hdr(PACKET *pkt,185size_t short_conn_id_len,186int partial,187int nodata,188QUIC_PKT_HDR *hdr,189QUIC_PKT_HDR_PTRS *ptrs,190uint64_t *fail_cause)191{192unsigned int b0;193unsigned char *pn = NULL;194size_t l = PACKET_remaining(pkt);195196if (fail_cause != NULL)197*fail_cause = QUIC_PKT_HDR_DECODE_DECODE_ERR;198199if (ptrs != NULL) {200ptrs->raw_start = (unsigned char *)PACKET_data(pkt);201ptrs->raw_sample = NULL;202ptrs->raw_sample_len = 0;203ptrs->raw_pn = NULL;204}205206if (l < QUIC_MIN_VALID_PKT_LEN207|| !PACKET_get_1(pkt, &b0))208return 0;209210hdr->partial = partial;211hdr->unused = 0;212hdr->reserved = 0;213214if ((b0 & 0x80) == 0) {215/* Short header. */216if (short_conn_id_len > QUIC_MAX_CONN_ID_LEN)217return 0;218219if ((b0 & 0x40) == 0 /* fixed bit not set? */220|| l < QUIC_MIN_VALID_PKT_LEN_CRYPTO)221return 0;222223hdr->type = QUIC_PKT_TYPE_1RTT;224hdr->fixed = 1;225hdr->spin_bit = (b0 & 0x20) != 0;226if (partial) {227hdr->key_phase = 0; /* protected, zero for now */228hdr->pn_len = 0; /* protected, zero for now */229hdr->reserved = 0; /* protected, zero for now */230} else {231hdr->key_phase = (b0 & 0x04) != 0;232hdr->pn_len = (b0 & 0x03) + 1;233hdr->reserved = (b0 & 0x18) >> 3;234}235236/* Copy destination connection ID field to header structure. */237if (!PACKET_copy_bytes(pkt, hdr->dst_conn_id.id, short_conn_id_len))238return 0;239240hdr->dst_conn_id.id_len = (unsigned char)short_conn_id_len;241242/*243* Skip over the PN. If this is a partial decode, the PN length field244* currently has header protection applied. Thus we do not know the245* length of the PN but we are allowed to assume it is 4 bytes long at246* this stage.247*/248memset(hdr->pn, 0, sizeof(hdr->pn));249pn = (unsigned char *)PACKET_data(pkt);250if (partial) {251if (!PACKET_forward(pkt, sizeof(hdr->pn)))252return 0;253} else {254if (!PACKET_copy_bytes(pkt, hdr->pn, hdr->pn_len))255return 0;256}257258/* Fields not used in short-header packets. */259hdr->version = 0;260hdr->src_conn_id.id_len = 0;261hdr->token = NULL;262hdr->token_len = 0;263264/*265* Short-header packets always come last in a datagram, the length266* is the remainder of the buffer.267*/268hdr->len = PACKET_remaining(pkt);269hdr->data = PACKET_data(pkt);270271/*272* Skip over payload. Since this is a short header packet, which cannot273* be followed by any other kind of packet, this advances us to the end274* of the datagram.275*/276if (!PACKET_forward(pkt, hdr->len))277return 0;278} else {279/* Long header. */280unsigned long version;281unsigned int dst_conn_id_len, src_conn_id_len, raw_type;282283if (!PACKET_get_net_4(pkt, &version))284return 0;285286/*287* All QUIC packets must have the fixed bit set, except exceptionally288* for Version Negotiation packets.289*/290if (version != 0 && (b0 & 0x40) == 0)291return 0;292293if (!PACKET_get_1(pkt, &dst_conn_id_len)294|| dst_conn_id_len > QUIC_MAX_CONN_ID_LEN295|| !PACKET_copy_bytes(pkt, hdr->dst_conn_id.id, dst_conn_id_len)296|| !PACKET_get_1(pkt, &src_conn_id_len)297|| src_conn_id_len > QUIC_MAX_CONN_ID_LEN298|| !PACKET_copy_bytes(pkt, hdr->src_conn_id.id, src_conn_id_len))299return 0;300301hdr->version = (uint32_t)version;302hdr->dst_conn_id.id_len = (unsigned char)dst_conn_id_len;303hdr->src_conn_id.id_len = (unsigned char)src_conn_id_len;304305if (version == 0) {306/*307* Version negotiation packet. Version negotiation packets are308* identified by a version field of 0 and the type bits in the first309* byte are ignored (they may take any value, and we ignore them).310*/311hdr->type = QUIC_PKT_TYPE_VERSION_NEG;312hdr->fixed = (b0 & 0x40) != 0;313314hdr->data = PACKET_data(pkt);315hdr->len = PACKET_remaining(pkt);316317/*318* Version negotiation packets must contain an array of u32s, so it319* is invalid for their payload length to not be divisible by 4.320*/321if ((hdr->len % 4) != 0)322return 0;323324/* Version negotiation packets are always fully decoded. */325hdr->partial = 0;326327/* Fields not used in version negotiation packets. */328hdr->pn_len = 0;329hdr->spin_bit = 0;330hdr->key_phase = 0;331hdr->token = NULL;332hdr->token_len = 0;333memset(hdr->pn, 0, sizeof(hdr->pn));334335if (!PACKET_forward(pkt, hdr->len))336return 0;337} else if (version != QUIC_VERSION_1) {338if (fail_cause != NULL)339*fail_cause |= QUIC_PKT_HDR_DECODE_BAD_VERSION;340/* Unknown version, do not decode. */341return 0;342} else {343if (l < QUIC_MIN_VALID_PKT_LEN_CRYPTO)344return 0;345346/* Get long packet type and decode to QUIC_PKT_TYPE_*. */347raw_type = ((b0 >> 4) & 0x3);348349switch (raw_type) {350case 0:351hdr->type = QUIC_PKT_TYPE_INITIAL;352break;353case 1:354hdr->type = QUIC_PKT_TYPE_0RTT;355break;356case 2:357hdr->type = QUIC_PKT_TYPE_HANDSHAKE;358break;359case 3:360hdr->type = QUIC_PKT_TYPE_RETRY;361break;362}363364hdr->pn_len = 0;365hdr->fixed = 1;366367/* Fields not used in long-header packets. */368hdr->spin_bit = 0;369hdr->key_phase = 0;370371if (hdr->type == QUIC_PKT_TYPE_INITIAL) {372/* Initial packet. */373uint64_t token_len;374375if (!PACKET_get_quic_vlint(pkt, &token_len)376|| token_len > SIZE_MAX377|| !PACKET_get_bytes(pkt, &hdr->token, (size_t)token_len))378return 0;379380hdr->token_len = (size_t)token_len;381if (token_len == 0)382hdr->token = NULL;383} else {384hdr->token = NULL;385hdr->token_len = 0;386}387388if (hdr->type == QUIC_PKT_TYPE_RETRY) {389/* Retry packet. */390hdr->data = PACKET_data(pkt);391hdr->len = PACKET_remaining(pkt);392393/* Retry packets are always fully decoded. */394hdr->partial = 0;395396/* Unused bits in Retry header. */397hdr->unused = b0 & 0x0f;398399/* Fields not used in Retry packets. */400memset(hdr->pn, 0, sizeof(hdr->pn));401402if (!PACKET_forward(pkt, hdr->len))403return 0;404} else {405/* Initial, 0-RTT or Handshake packet. */406uint64_t len;407408hdr->pn_len = partial ? 0 : ((b0 & 0x03) + 1);409hdr->reserved = partial ? 0 : ((b0 & 0x0C) >> 2);410411if (!PACKET_get_quic_vlint(pkt, &len)412|| len < sizeof(hdr->pn))413return 0;414415if (!nodata && len > PACKET_remaining(pkt))416return 0;417418/*419* Skip over the PN. If this is a partial decode, the PN length420* field currently has header protection applied. Thus we do not421* know the length of the PN but we are allowed to assume it is422* 4 bytes long at this stage.423*/424pn = (unsigned char *)PACKET_data(pkt);425memset(hdr->pn, 0, sizeof(hdr->pn));426if (partial) {427if (!PACKET_forward(pkt, sizeof(hdr->pn)))428return 0;429430hdr->len = (size_t)(len - sizeof(hdr->pn));431} else {432if (!PACKET_copy_bytes(pkt, hdr->pn, hdr->pn_len))433return 0;434435hdr->len = (size_t)(len - hdr->pn_len);436}437438if (nodata) {439hdr->data = NULL;440} else {441hdr->data = PACKET_data(pkt);442443/* Skip over packet body. */444if (!PACKET_forward(pkt, hdr->len))445return 0;446}447}448}449}450451if (ptrs != NULL) {452ptrs->raw_pn = pn;453if (pn != NULL) {454ptrs->raw_sample = pn + 4;455ptrs->raw_sample_len = PACKET_end(pkt) - ptrs->raw_sample;456}457}458459/*460* Good decode, clear the generic DECODE_ERR flag461*/462if (fail_cause != NULL)463*fail_cause &= ~QUIC_PKT_HDR_DECODE_DECODE_ERR;464465return 1;466}467468int ossl_quic_wire_encode_pkt_hdr(WPACKET *pkt,469size_t short_conn_id_len,470const QUIC_PKT_HDR *hdr,471QUIC_PKT_HDR_PTRS *ptrs)472{473unsigned char b0;474size_t off_start, off_sample, off_pn;475unsigned char *start = WPACKET_get_curr(pkt);476477if (!WPACKET_get_total_written(pkt, &off_start))478return 0;479480if (ptrs != NULL) {481/* ptrs would not be stable on non-static WPACKET */482if (!ossl_assert(pkt->staticbuf != NULL))483return 0;484ptrs->raw_start = NULL;485ptrs->raw_sample = NULL;486ptrs->raw_sample_len = 0;487ptrs->raw_pn = 0;488}489490/* Cannot serialize a partial header, or one whose DCID length is wrong. */491if (hdr->partial492|| (hdr->type == QUIC_PKT_TYPE_1RTT493&& hdr->dst_conn_id.id_len != short_conn_id_len))494return 0;495496if (hdr->type == QUIC_PKT_TYPE_1RTT) {497/* Short header. */498499/*500* Cannot serialize a header whose DCID length is wrong, or with an501* invalid PN length.502*/503if (hdr->dst_conn_id.id_len != short_conn_id_len504|| short_conn_id_len > QUIC_MAX_CONN_ID_LEN505|| hdr->pn_len < 1 || hdr->pn_len > 4)506return 0;507508b0 = (hdr->spin_bit << 5)509| (hdr->key_phase << 2)510| (hdr->pn_len - 1)511| (hdr->reserved << 3)512| 0x40; /* fixed bit */513514if (!WPACKET_put_bytes_u8(pkt, b0)515|| !WPACKET_memcpy(pkt, hdr->dst_conn_id.id, short_conn_id_len)516|| !WPACKET_get_total_written(pkt, &off_pn)517|| !WPACKET_memcpy(pkt, hdr->pn, hdr->pn_len))518return 0;519} else {520/* Long header. */521unsigned int raw_type;522523if (hdr->dst_conn_id.id_len > QUIC_MAX_CONN_ID_LEN524|| hdr->src_conn_id.id_len > QUIC_MAX_CONN_ID_LEN)525return 0;526527if (ossl_quic_pkt_type_has_pn(hdr->type)528&& (hdr->pn_len < 1 || hdr->pn_len > 4))529return 0;530531switch (hdr->type) {532case QUIC_PKT_TYPE_VERSION_NEG:533if (hdr->version != 0)534return 0;535536/* Version negotiation packets use zero for the type bits */537raw_type = 0;538break;539540case QUIC_PKT_TYPE_INITIAL:541raw_type = 0;542break;543case QUIC_PKT_TYPE_0RTT:544raw_type = 1;545break;546case QUIC_PKT_TYPE_HANDSHAKE:547raw_type = 2;548break;549case QUIC_PKT_TYPE_RETRY:550raw_type = 3;551break;552default:553return 0;554}555556b0 = (raw_type << 4) | 0x80; /* long */557if (hdr->type != QUIC_PKT_TYPE_VERSION_NEG || hdr->fixed)558b0 |= 0x40; /* fixed */559if (ossl_quic_pkt_type_has_pn(hdr->type)) {560b0 |= hdr->pn_len - 1;561b0 |= (hdr->reserved << 2);562}563if (hdr->type == QUIC_PKT_TYPE_RETRY)564b0 |= hdr->unused;565566if (!WPACKET_put_bytes_u8(pkt, b0)567|| !WPACKET_put_bytes_u32(pkt, hdr->version)568|| !WPACKET_put_bytes_u8(pkt, hdr->dst_conn_id.id_len)569|| !WPACKET_memcpy(pkt, hdr->dst_conn_id.id,570hdr->dst_conn_id.id_len)571|| !WPACKET_put_bytes_u8(pkt, hdr->src_conn_id.id_len)572|| !WPACKET_memcpy(pkt, hdr->src_conn_id.id,573hdr->src_conn_id.id_len))574return 0;575576if (hdr->type == QUIC_PKT_TYPE_VERSION_NEG) {577if (hdr->len > 0 && !WPACKET_reserve_bytes(pkt, hdr->len, NULL))578return 0;579580return 1;581}582583if (hdr->type == QUIC_PKT_TYPE_INITIAL) {584if (!WPACKET_quic_write_vlint(pkt, hdr->token_len)585|| !WPACKET_memcpy(pkt, hdr->token, hdr->token_len))586return 0;587}588589if (hdr->type == QUIC_PKT_TYPE_RETRY) {590if (!WPACKET_memcpy(pkt, hdr->token, hdr->token_len))591return 0;592return 1;593}594595if (!WPACKET_quic_write_vlint(pkt, hdr->len + hdr->pn_len)596|| !WPACKET_get_total_written(pkt, &off_pn)597|| !WPACKET_memcpy(pkt, hdr->pn, hdr->pn_len))598return 0;599}600601if (hdr->len > 0 && !WPACKET_reserve_bytes(pkt, hdr->len, NULL))602return 0;603604off_sample = off_pn + 4;605606if (ptrs != NULL) {607ptrs->raw_start = start;608ptrs->raw_sample = start + (off_sample - off_start);609ptrs->raw_sample_len610= WPACKET_get_curr(pkt) + hdr->len - ptrs->raw_sample;611ptrs->raw_pn = start + (off_pn - off_start);612}613614return 1;615}616617int ossl_quic_wire_get_encoded_pkt_hdr_len(size_t short_conn_id_len,618const QUIC_PKT_HDR *hdr)619{620size_t len = 0, enclen;621622/* Cannot serialize a partial header, or one whose DCID length is wrong. */623if (hdr->partial624|| (hdr->type == QUIC_PKT_TYPE_1RTT625&& hdr->dst_conn_id.id_len != short_conn_id_len))626return 0;627628if (hdr->type == QUIC_PKT_TYPE_1RTT) {629/* Short header. */630631/*632* Cannot serialize a header whose DCID length is wrong, or with an633* invalid PN length.634*/635if (hdr->dst_conn_id.id_len != short_conn_id_len636|| short_conn_id_len > QUIC_MAX_CONN_ID_LEN637|| hdr->pn_len < 1 || hdr->pn_len > 4)638return 0;639640return 1 + short_conn_id_len + hdr->pn_len;641} else {642/* Long header. */643if (hdr->dst_conn_id.id_len > QUIC_MAX_CONN_ID_LEN644|| hdr->src_conn_id.id_len > QUIC_MAX_CONN_ID_LEN)645return 0;646647len += 1 /* Initial byte */ + 4 /* Version */648+ 1 + hdr->dst_conn_id.id_len /* DCID Len, DCID */649+ 1 + hdr->src_conn_id.id_len /* SCID Len, SCID */650;651652if (ossl_quic_pkt_type_has_pn(hdr->type)) {653if (hdr->pn_len < 1 || hdr->pn_len > 4)654return 0;655656len += hdr->pn_len;657}658659if (hdr->type == QUIC_PKT_TYPE_INITIAL) {660enclen = ossl_quic_vlint_encode_len(hdr->token_len);661if (!enclen)662return 0;663664len += enclen + hdr->token_len;665}666667if (!ossl_quic_pkt_type_must_be_last(hdr->type)) {668enclen = ossl_quic_vlint_encode_len(hdr->len + hdr->pn_len);669if (!enclen)670return 0;671672len += enclen;673}674675return len;676}677}678679int ossl_quic_wire_get_pkt_hdr_dst_conn_id(const unsigned char *buf,680size_t buf_len,681size_t short_conn_id_len,682QUIC_CONN_ID *dst_conn_id)683{684unsigned char b0;685size_t blen;686687if (buf_len < QUIC_MIN_VALID_PKT_LEN688|| short_conn_id_len > QUIC_MAX_CONN_ID_LEN)689return 0;690691b0 = buf[0];692if ((b0 & 0x80) != 0) {693/*694* Long header. We need 6 bytes (initial byte, 4 version bytes, DCID695* length byte to begin with). This is covered by the buf_len test696* above.697*/698699/*700* If the version field is non-zero (meaning that this is not a Version701* Negotiation packet), the fixed bit must be set.702*/703if ((buf[1] || buf[2] || buf[3] || buf[4]) && (b0 & 0x40) == 0)704return 0;705706blen = (size_t)buf[5]; /* DCID Length */707if (blen > QUIC_MAX_CONN_ID_LEN708|| buf_len < QUIC_MIN_VALID_PKT_LEN + blen)709return 0;710711dst_conn_id->id_len = (unsigned char)blen;712memcpy(dst_conn_id->id, buf + 6, blen);713return 1;714} else {715/* Short header. */716if ((b0 & 0x40) == 0)717/* Fixed bit not set, not a valid QUIC packet header. */718return 0;719720if (buf_len < QUIC_MIN_VALID_PKT_LEN_CRYPTO + short_conn_id_len)721return 0;722723dst_conn_id->id_len = (unsigned char)short_conn_id_len;724memcpy(dst_conn_id->id, buf + 1, short_conn_id_len);725return 1;726}727}728729int ossl_quic_wire_decode_pkt_hdr_pn(const unsigned char *enc_pn,730size_t enc_pn_len,731QUIC_PN largest_pn,732QUIC_PN *res_pn)733{734int64_t expected_pn, truncated_pn, candidate_pn, pn_win, pn_hwin, pn_mask;735736switch (enc_pn_len) {737case 1:738truncated_pn = enc_pn[0];739break;740case 2:741truncated_pn = ((QUIC_PN)enc_pn[0] << 8)742| (QUIC_PN)enc_pn[1];743break;744case 3:745truncated_pn = ((QUIC_PN)enc_pn[0] << 16)746| ((QUIC_PN)enc_pn[1] << 8)747| (QUIC_PN)enc_pn[2];748break;749case 4:750truncated_pn = ((QUIC_PN)enc_pn[0] << 24)751| ((QUIC_PN)enc_pn[1] << 16)752| ((QUIC_PN)enc_pn[2] << 8)753| (QUIC_PN)enc_pn[3];754break;755default:756return 0;757}758759/* Implemented as per RFC 9000 Section A.3. */760expected_pn = largest_pn + 1;761pn_win = ((int64_t)1) << (enc_pn_len * 8);762pn_hwin = pn_win / 2;763pn_mask = pn_win - 1;764candidate_pn = (expected_pn & ~pn_mask) | truncated_pn;765if (candidate_pn <= expected_pn - pn_hwin766&& candidate_pn < (((int64_t)1) << 62) - pn_win)767*res_pn = candidate_pn + pn_win;768else if (candidate_pn > expected_pn + pn_hwin769&& candidate_pn >= pn_win)770*res_pn = candidate_pn - pn_win;771else772*res_pn = candidate_pn;773return 1;774}775776/* From RFC 9000 Section A.2. Simplified implementation. */777int ossl_quic_wire_determine_pn_len(QUIC_PN pn,778QUIC_PN largest_acked)779{780uint64_t num_unacked781= (largest_acked == QUIC_PN_INVALID) ? pn + 1 : pn - largest_acked;782783/*784* num_unacked \in [ 0, 2** 7] -> 1 byte785* num_unacked \in (2** 7, 2**15] -> 2 bytes786* num_unacked \in (2**15, 2**23] -> 3 bytes787* num_unacked \in (2**23, ] -> 4 bytes788*/789790if (num_unacked <= (1U << 7))791return 1;792if (num_unacked <= (1U << 15))793return 2;794if (num_unacked <= (1U << 23))795return 3;796return 4;797}798799int ossl_quic_wire_encode_pkt_hdr_pn(QUIC_PN pn,800unsigned char *enc_pn,801size_t enc_pn_len)802{803switch (enc_pn_len) {804case 1:805enc_pn[0] = (unsigned char)pn;806break;807case 2:808enc_pn[1] = (unsigned char)pn;809enc_pn[0] = (unsigned char)(pn >> 8);810break;811case 3:812enc_pn[2] = (unsigned char)pn;813enc_pn[1] = (unsigned char)(pn >> 8);814enc_pn[0] = (unsigned char)(pn >> 16);815break;816case 4:817enc_pn[3] = (unsigned char)pn;818enc_pn[2] = (unsigned char)(pn >> 8);819enc_pn[1] = (unsigned char)(pn >> 16);820enc_pn[0] = (unsigned char)(pn >> 24);821break;822default:823return 0;824}825826return 1;827}828829int ossl_quic_validate_retry_integrity_tag(OSSL_LIB_CTX *libctx,830const char *propq,831const QUIC_PKT_HDR *hdr,832const QUIC_CONN_ID *client_initial_dcid)833{834unsigned char expected_tag[QUIC_RETRY_INTEGRITY_TAG_LEN];835const unsigned char *actual_tag;836837if (hdr == NULL || hdr->len < QUIC_RETRY_INTEGRITY_TAG_LEN)838return 0;839840if (!ossl_quic_calculate_retry_integrity_tag(libctx, propq,841hdr, client_initial_dcid,842expected_tag))843return 0;844845actual_tag = hdr->data + hdr->len - QUIC_RETRY_INTEGRITY_TAG_LEN;846847return !CRYPTO_memcmp(expected_tag, actual_tag,848QUIC_RETRY_INTEGRITY_TAG_LEN);849}850851/* RFC 9001 s. 5.8 */852static const unsigned char retry_integrity_key[] = {8530xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a,8540x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e855};856857static const unsigned char retry_integrity_nonce[] = {8580x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2,8590x23, 0x98, 0x25, 0xbb860};861862int ossl_quic_calculate_retry_integrity_tag(OSSL_LIB_CTX *libctx,863const char *propq,864const QUIC_PKT_HDR *hdr,865const QUIC_CONN_ID *client_initial_dcid,866unsigned char *tag)867{868EVP_CIPHER *cipher = NULL;869EVP_CIPHER_CTX *cctx = NULL;870int ok = 0, l = 0, l2 = 0, wpkt_valid = 0;871WPACKET wpkt;872/* Worst case length of the Retry Psuedo-Packet header is 68 bytes. */873unsigned char buf[128];874QUIC_PKT_HDR hdr2;875size_t hdr_enc_len = 0;876877if (hdr->type != QUIC_PKT_TYPE_RETRY || hdr->version == 0878|| hdr->len < QUIC_RETRY_INTEGRITY_TAG_LEN879|| hdr->data == NULL880|| client_initial_dcid == NULL || tag == NULL881|| client_initial_dcid->id_len > QUIC_MAX_CONN_ID_LEN) {882ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);883goto err;884}885886/*887* Do not reserve packet body in WPACKET. Retry packet header888* does not contain a Length field so this does not affect889* the serialized packet header.890*/891hdr2 = *hdr;892hdr2.len = 0;893894/* Assemble retry psuedo-packet. */895if (!WPACKET_init_static_len(&wpkt, buf, sizeof(buf), 0)) {896ERR_raise(ERR_LIB_SSL, ERR_R_CRYPTO_LIB);897goto err;898}899900wpkt_valid = 1;901902/* Prepend original DCID to the packet. */903if (!WPACKET_put_bytes_u8(&wpkt, client_initial_dcid->id_len)904|| !WPACKET_memcpy(&wpkt, client_initial_dcid->id,905client_initial_dcid->id_len)) {906ERR_raise(ERR_LIB_SSL, ERR_R_CRYPTO_LIB);907goto err;908}909910/* Encode main retry header. */911if (!ossl_quic_wire_encode_pkt_hdr(&wpkt, hdr2.dst_conn_id.id_len,912&hdr2, NULL))913goto err;914915if (!WPACKET_get_total_written(&wpkt, &hdr_enc_len)) {916ERR_raise(ERR_LIB_SSL, ERR_R_CRYPTO_LIB);917goto err;918}919920/* Create and initialise cipher context. */921/* TODO(QUIC FUTURE): Cipher fetch caching. */922if ((cipher = EVP_CIPHER_fetch(libctx, "AES-128-GCM", propq)) == NULL) {923ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);924goto err;925}926927if ((cctx = EVP_CIPHER_CTX_new()) == NULL) {928ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);929goto err;930}931932if (!EVP_CipherInit_ex(cctx, cipher, NULL,933retry_integrity_key, retry_integrity_nonce, /*enc=*/1)) {934ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);935goto err;936}937938/* Feed packet header as AAD data. */939if (EVP_CipherUpdate(cctx, NULL, &l, buf, hdr_enc_len) != 1) {940ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);941goto err;942}943944/* Feed packet body as AAD data. */945if (EVP_CipherUpdate(cctx, NULL, &l, hdr->data,946hdr->len - QUIC_RETRY_INTEGRITY_TAG_LEN)947!= 1) {948ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);949goto err;950}951952/* Finalise and get tag. */953if (EVP_CipherFinal_ex(cctx, NULL, &l2) != 1) {954ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);955goto err;956}957958if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_GET_TAG,959QUIC_RETRY_INTEGRITY_TAG_LEN,960tag)961!= 1) {962ERR_raise(ERR_LIB_SSL, ERR_R_EVP_LIB);963goto err;964}965966ok = 1;967err:968EVP_CIPHER_free(cipher);969EVP_CIPHER_CTX_free(cctx);970if (wpkt_valid)971WPACKET_finish(&wpkt);972973return ok;974}975976977