From dffafaf48174497a724d546c3483d2493fc9b64c Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Mon, 11 Jul 2022 19:16:20 +0100 Subject: [PATCH] QUIC Frame Encoding and Decoding Functions This adds functions for encoding and decoding QUIC frames. Reviewed-by: Tomas Mraz Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/18795) --- crypto/packet.c | 6 +- include/internal/packet.h | 78 +- include/internal/quic_types.h | 29 + include/internal/quic_wire.h | 695 +++++++++++++++ include/internal/time.h | 52 +- ssl/quic/build.info | 2 +- ssl/quic/quic_wire.c | 823 ++++++++++++++++++ test/build.info | 6 +- test/quic_wire_test.c | 1364 ++++++++++++++++++++++++++++++ test/recipes/70-test_quic_wire.t | 19 + 10 files changed, 3067 insertions(+), 7 deletions(-) create mode 100644 include/internal/quic_types.h create mode 100644 include/internal/quic_wire.h create mode 100644 ssl/quic/quic_wire.c create mode 100644 test/quic_wire_test.c create mode 100644 test/recipes/70-test_quic_wire.t diff --git a/crypto/packet.c b/crypto/packet.c index 5123426d7555e..753bbdc59c7b6 100644 --- a/crypto/packet.c +++ b/crypto/packet.c @@ -207,7 +207,7 @@ int WPACKET_set_flags(WPACKET *pkt, unsigned int flags) } /* Store the |value| of length |len| at location |data| */ -static int put_value(unsigned char *data, size_t value, size_t len) +static int put_value(unsigned char *data, uint64_t value, size_t len) { if (data == NULL) return 1; @@ -396,12 +396,12 @@ int WPACKET_start_sub_packet(WPACKET *pkt) return WPACKET_start_sub_packet_len__(pkt, 0); } -int WPACKET_put_bytes__(WPACKET *pkt, unsigned int val, size_t size) +int WPACKET_put_bytes__(WPACKET *pkt, uint64_t val, size_t size) { unsigned char *data; /* Internal API, so should not fail */ - if (!ossl_assert(size <= sizeof(unsigned int)) + if (!ossl_assert(size <= sizeof(uint64_t)) || !WPACKET_allocate_bytes(pkt, size, &data) || !put_value(data, val, size)) return 0; diff --git a/include/internal/packet.h b/include/internal/packet.h index ab211f0dc28d9..476a1b72755c9 100644 --- a/include/internal/packet.h +++ b/include/internal/packet.h @@ -229,6 +229,28 @@ __owur static ossl_inline int PACKET_peek_net_4(const PACKET *pkt, return 1; } +/* + * Peek ahead at 8 bytes in network order from |pkt| and store the value in + * |*data| + */ +__owur static ossl_inline int PACKET_peek_net_8(const PACKET *pkt, + uint64_t *data) +{ + if (PACKET_remaining(pkt) < 8) + return 0; + + *data = ((uint64_t)(*pkt->curr)) << 56; + *data |= ((uint64_t)(*(pkt->curr + 1))) << 48; + *data |= ((uint64_t)(*(pkt->curr + 2))) << 40; + *data |= ((uint64_t)(*(pkt->curr + 3))) << 32; + *data |= ((uint64_t)(*(pkt->curr + 4))) << 24; + *data |= ((uint64_t)(*(pkt->curr + 5))) << 16; + *data |= ((uint64_t)(*(pkt->curr + 6))) << 8; + *data |= *(pkt->curr + 7); + + return 1; +} + /* * Decodes a QUIC variable-length integer in |pkt| and stores the result in * |data|. @@ -251,6 +273,47 @@ __owur static ossl_inline int PACKET_get_quic_vlint(PACKET *pkt, return 1; } +/* + * Decodes a QUIC variable-length integer in |pkt| and stores the result in + * |data|. Unlike PACKET_get_quic_vlint, this does not advance the current + * position. + */ +__owur static ossl_inline int PACKET_peek_quic_vlint(PACKET *pkt, + uint64_t *data) +{ + size_t enclen; + + if (PACKET_remaining(pkt) < 1) + return 0; + + enclen = ossl_quic_vlint_decode_len(*pkt->curr); + + if (PACKET_remaining(pkt) < enclen) + return 0; + + *data = ossl_quic_vlint_decode_unchecked(pkt->curr); + return 1; +} + +/* + * Skips over a QUIC variable-length integer in |pkt| without decoding it. + */ +__owur static ossl_inline int PACKET_skip_quic_vlint(PACKET *pkt) +{ + size_t enclen; + + if (PACKET_remaining(pkt) < 1) + return 0; + + enclen = ossl_quic_vlint_decode_len(*pkt->curr); + + if (PACKET_remaining(pkt) < enclen) + return 0; + + packet_forward(pkt, enclen); + return 1; +} + /* Equivalent of n2l */ /* Get 4 bytes in network order from |pkt| and store the value in |*data| */ __owur static ossl_inline int PACKET_get_net_4(PACKET *pkt, unsigned long *data) @@ -275,6 +338,17 @@ __owur static ossl_inline int PACKET_get_net_4_len(PACKET *pkt, size_t *data) return ret; } +/* Get 8 bytes in network order from |pkt| and store the value in |*data| */ +__owur static ossl_inline int PACKET_get_net_8(PACKET *pkt, uint64_t *data) +{ + if (!PACKET_peek_net_8(pkt, data)) + return 0; + + packet_forward(pkt, 8); + + return 1; +} + /* Peek ahead at 1 byte from |pkt| and store the value in |*data| */ __owur static ossl_inline int PACKET_peek_1(const PACKET *pkt, unsigned int *data) @@ -885,7 +959,7 @@ int WPACKET_sub_reserve_bytes__(WPACKET *pkt, size_t len, * 1 byte will fail. Don't call this directly. Use the convenience macros below * instead. */ -int WPACKET_put_bytes__(WPACKET *pkt, unsigned int val, size_t bytes); +int WPACKET_put_bytes__(WPACKET *pkt, uint64_t val, size_t bytes); /* * Convenience macros for calling WPACKET_put_bytes with different @@ -899,6 +973,8 @@ int WPACKET_put_bytes__(WPACKET *pkt, unsigned int val, size_t bytes); WPACKET_put_bytes__((pkt), (val), 3) #define WPACKET_put_bytes_u32(pkt, val) \ WPACKET_put_bytes__((pkt), (val), 4) +#define WPACKET_put_bytes_u64(pkt, val) \ + WPACKET_put_bytes__((pkt), (val), 8) /* Set a maximum size that we will not allow the WPACKET to grow beyond */ int WPACKET_set_max_size(WPACKET *pkt, size_t maxsize); diff --git a/include/internal/quic_types.h b/include/internal/quic_types.h new file mode 100644 index 0000000000000..6787de9c1183c --- /dev/null +++ b/include/internal/quic_types.h @@ -0,0 +1,29 @@ +/* + * 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 + */ + +#ifndef OSSL_QUIC_TYPES_H +# define OSSL_QUIC_TYPES_H + +# include + +/* QUIC packet number representation. */ +typedef uint64_t QUIC_PN; +# define QUIC_PN_INVALID UINT64_MAX + +static ossl_unused ossl_inline QUIC_PN ossl_quic_pn_max(QUIC_PN a, QUIC_PN b) +{ + return a > b ? a : b; +} + +static ossl_unused ossl_inline QUIC_PN ossl_quic_pn_min(QUIC_PN a, QUIC_PN b) +{ + return a < b ? a : b; +} + +#endif diff --git a/include/internal/quic_wire.h b/include/internal/quic_wire.h new file mode 100644 index 0000000000000..1ead6fcf99def --- /dev/null +++ b/include/internal/quic_wire.h @@ -0,0 +1,695 @@ +/* +* 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 +*/ + +#ifndef OSSL_INTERNAL_QUIC_WIRE_H +# define OSSL_INTERNAL_QUIC_WIRE_H +# pragma once + +#include "internal/e_os.h" +#include "internal/time.h" +#include "internal/quic_types.h" +#include "internal/packet.h" + +#define OSSL_QUIC_FRAME_TYPE_PADDING 0x00 +#define OSSL_QUIC_FRAME_TYPE_PING 0x01 +#define OSSL_QUIC_FRAME_TYPE_ACK_WITHOUT_ECN 0x02 +#define OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN 0x03 +#define OSSL_QUIC_FRAME_TYPE_RESET_STREAM 0x04 +#define OSSL_QUIC_FRAME_TYPE_STOP_SENDING 0x05 +#define OSSL_QUIC_FRAME_TYPE_CRYPTO 0x06 +#define OSSL_QUIC_FRAME_TYPE_NEW_TOKEN 0x07 +#define OSSL_QUIC_FRAME_TYPE_MAX_DATA 0x10 +#define OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA 0x11 +#define OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI 0x12 +#define OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI 0x13 +#define OSSL_QUIC_FRAME_TYPE_DATA_BLOCKED 0x14 +#define OSSL_QUIC_FRAME_TYPE_STREAM_DATA_BLOCKED 0x15 +#define OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI 0x16 +#define OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_UNI 0x17 +#define OSSL_QUIC_FRAME_TYPE_NEW_CONN_ID 0x18 +#define OSSL_QUIC_FRAME_TYPE_RETIRE_CONN_ID 0x19 +#define OSSL_QUIC_FRAME_TYPE_PATH_CHALLENGE 0x1A +#define OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE 0x1B +#define OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_TRANSPORT 0x1C +#define OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_APP 0x1D +#define OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE 0x1E + +#define OSSL_QUIC_FRAME_FLAG_STREAM_FIN 0x01 +#define OSSL_QUIC_FRAME_FLAG_STREAM_LEN 0x02 +#define OSSL_QUIC_FRAME_FLAG_STREAM_OFF 0x04 +#define OSSL_QUIC_FRAME_FLAG_STREAM_MASK ((uint64_t)0x07) + +/* Low 3 bits of the type contain flags */ +#define OSSL_QUIC_FRAME_TYPE_STREAM 0x08 /* base ID */ +#define OSSL_QUIC_FRAME_TYPE_STREAM_FIN \ + (OSSL_QUIC_FRAME_TYPE_STREAM | \ + OSSL_QUIC_FRAME_FLAG_STREAM_FIN) +#define OSSL_QUIC_FRAME_TYPE_STREAM_LEN \ + (OSSL_QUIC_FRAME_TYPE_STREAM | \ + OSSL_QUIC_FRAME_FLAG_STREAM_LEN) +#define OSSL_QUIC_FRAME_TYPE_STREAM_LEN_FIN \ + (OSSL_QUIC_FRAME_TYPE_STREAM | \ + OSSL_QUIC_FRAME_FLAG_STREAM_LEN | \ + OSSL_QUIC_FRAME_FLAG_STREAM_FIN) +#define OSSL_QUIC_FRAME_TYPE_STREAM_OFF \ + (OSSL_QUIC_FRAME_TYPE_STREAM | \ + OSSL_QUIC_FRAME_FLAG_STREAM_OFF) +#define OSSL_QUIC_FRAME_TYPE_STREAM_OFF_FIN \ + (OSSL_QUIC_FRAME_TYPE_STREAM | \ + OSSL_QUIC_FRAME_FLAG_STREAM_OFF | \ + OSSL_QUIC_FRAME_FLAG_STREAM_FIN) +#define OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN \ + (OSSL_QUIC_FRAME_TYPE_STREAM | \ + OSSL_QUIC_FRAME_FLAG_STREAM_OFF | \ + OSSL_QUIC_FRAME_FLAG_STREAM_LEN) +#define OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN_FIN \ + (OSSL_QUIC_FRAME_TYPE_STREAM | \ + OSSL_QUIC_FRAME_FLAG_STREAM_OFF | \ + OSSL_QUIC_FRAME_FLAG_STREAM_LEN | \ + OSSL_QUIC_FRAME_FLAG_STREAM_FIN) + +#define OSSL_QUIC_FRAME_TYPE_IS_STREAM(x) \ + (((x) & ~OSSL_QUIC_FRAME_FLAG_STREAM_MASK) == OSSL_QUIC_FRAME_TYPE_STREAM) +#define OSSL_QUIC_FRAME_TYPE_IS_ACK(x) \ + (((x) & ~(uint64_t)1) == OSSL_QUIC_FRAME_TYPE_ACK_WITHOUT_ECN) +#define OSSL_QUIC_FRAME_TYPE_IS_MAX_STREAMS(x) \ + (((x) & ~(uint64_t)1) == OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI) +#define OSSL_QUIC_FRAME_TYPE_IS_STREAMS_BLOCKED(x) \ + (((x) & ~(uint64_t)1) == OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI) +#define OSSL_QUIC_FRAME_TYPE_IS_CONN_CLOSE(x) \ + (((x) & ~(uint64_t)1) == OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_TRANSPORT) + +/* + * QUIC Frame Logical Representations + * ================================== + */ + +/* QUIC Frame: ACK */ +typedef struct ossl_quic_ack_range_st { + /* + * Represents an inclusive range of packet numbers [start, end]. + * start must be <= end. + */ + QUIC_PN start, end; +} OSSL_QUIC_ACK_RANGE; + +typedef struct ossl_quic_frame_ack_st { + /* + * A sequence of packet number ranges [[start, end]...]. + * + * The ranges must be sorted in descending order, for example: + * [ 95, 100] + * [ 90, 92] + * etc. + * + * As such, ack_ranges[0].end is always the highest packet number + * being acknowledged and ack_ranges[num_ack_ranges-1].start is + * always the lowest packet number being acknowledged. + * + * num_ack_ranges must be greater than zero, as an ACK frame must + * acknowledge at least one packet number. + */ + OSSL_QUIC_ACK_RANGE *ack_ranges; + size_t num_ack_ranges; + + OSSL_TIME delay_time; + uint64_t ect0, ect1, ecnce; + unsigned int ecn_present : 1; +} OSSL_QUIC_FRAME_ACK; + +/* QUIC Frame: STREAM */ +typedef struct ossl_quic_frame_stream_st { + uint64_t stream_id; /* Stream ID */ + uint64_t offset; /* Logical offset in stream */ + uint64_t len; /* Length of data in bytes */ + const unsigned char *data; + + /* + * On encode, this determines whether the len field should be encoded or + * not. If zero, the len field is not encoded and it is assumed the frame + * runs to the end of the packet. + * + * On decode, this determines whether the frame had an explicitly encoded + * length. If not set, the frame runs to the end of the packet and len has + * been set accordingly. + */ + unsigned int has_explicit_len : 1; + + /* 1 if this is the end of the stream */ + unsigned int is_fin : 1; +} OSSL_QUIC_FRAME_STREAM; + +/* QUIC Frame: CRYPTO */ +typedef struct ossl_quic_frame_crypto_st { + uint64_t offset; /* Logical offset in stream */ + uint64_t len; /* Length of the data in bytes */ + const unsigned char *data; +} OSSL_QUIC_FRAME_CRYPTO; + +/* QUIC Frame: RESET_STREAM */ +typedef struct ossl_quic_frame_reset_stream_st { + uint64_t stream_id; + uint64_t app_error_code; + uint64_t final_size; +} OSSL_QUIC_FRAME_RESET_STREAM; + +/* QUIC Frame: STOP_SENDING */ +typedef struct ossl_quic_frame_stop_sending_st { + uint64_t stream_id; + uint64_t app_error_code; +} OSSL_QUIC_FRAME_STOP_SENDING; + +/* QUIC Frame: NEW_CONNECTION_ID */ +#define OSSL_QUIC_MAX_CONN_ID_LEN 20 +typedef struct ossl_quic_conn_id_st { + unsigned char id_len; /* length of id in bytes */ + unsigned char id[OSSL_QUIC_MAX_CONN_ID_LEN]; +} OSSL_QUIC_CONN_ID; + +typedef struct ossl_quic_frame_new_conn_id_st { + uint64_t seq_num; + uint64_t retire_prior_to; + OSSL_QUIC_CONN_ID conn_id; + unsigned char stateless_reset_token[16]; +} OSSL_QUIC_FRAME_NEW_CONN_ID; + +/* QUIC Frame: CONNECTION_CLOSE */ +typedef struct ossl_quic_frame_conn_close_st { + unsigned int is_app : 1; /* 0: transport error, 1: app error */ + uint64_t error_code; /* 62-bit transport or app error code */ + uint64_t frame_type; /* transport errors only */ + const char *reason; /* UTF-8 string, not necessarily zero-terminated */ + size_t reason_len; /* Length of reason in bytes */ +} OSSL_QUIC_FRAME_CONN_CLOSE; + +/* + * QUIC Wire Format Encoding + * ========================= + * + * These functions return 1 on success and 0 on failure. + */ + +/* + * Encodes zero or more QUIC PADDING frames to the packet writer. Each PADDING + * frame consumes one byte; num_bytes specifies the number of bytes of padding + * to write. + */ +int ossl_quic_wire_encode_padding(WPACKET *pkt, size_t num_bytes); + +/* + * Encodes a QUIC PING frame to the packet writer. This frame type takes + * no arguments. +*/ +int ossl_quic_wire_encode_frame_ping(WPACKET *pkt); + +/* + * Encodes a QUIC ACK frame to the packet writer, given a logical representation + * of the ACK frame. + * + * The ACK ranges passed must be sorted in descending order. + * + * The logical representation stores a list of packet number ranges. The wire + * encoding is slightly different and stores the first range in the list + * in a different manner. + * + * The ack_delay_exponent argument specifies the index of a power of two by + * which the ack->ack_delay field is be divided. This exponent value must match + * the value used when decoding. + */ +int ossl_quic_wire_encode_frame_ack(WPACKET *pkt, + uint32_t ack_delay_exponent, + const OSSL_QUIC_FRAME_ACK *ack); + +/* + * Encodes a QUIC RESET_STREAM frame to the packet writer, given a logical + * representation of the RESET_STREAM frame. + */ +int ossl_quic_wire_encode_frame_reset_stream(WPACKET *pkt, + const OSSL_QUIC_FRAME_RESET_STREAM *f); + +/* + * Encodes a QUIC STOP_SENDING frame to the packet writer, given a logical + * representation of the STOP_SENDING frame. + */ +int ossl_quic_wire_encode_frame_stop_sending(WPACKET *pkt, + const OSSL_QUIC_FRAME_STOP_SENDING *f); + +/* + * Encodes a QUIC CRYPTO frame header to the packet writer. + * + * To create a well-formed frame, the data written using this function must be + * immediately followed by f->len bytes of data. + */ +int ossl_quic_wire_encode_frame_crypto_hdr(WPACKET *hdr, + const OSSL_QUIC_FRAME_CRYPTO *f); + +/* + * Encodes a QUIC CRYPTO frame to the packet writer. + * + * This function returns a pointer to a buffer of f->len bytes which the caller + * should fill however it wishes. If f->data is non-NULL, it is automatically + * copied to the target buffer, otherwise the caller must fill the returned + * buffer. Returns NULL on failure. + */ +void *ossl_quic_wire_encode_frame_crypto(WPACKET *pkt, + const OSSL_QUIC_FRAME_CRYPTO *f); + +/* + * Encodes a QUIC NEW_TOKEN frame to the packet writer. + */ +int ossl_quic_wire_encode_frame_new_token(WPACKET *pkt, + const unsigned char *token, + size_t token_len); + +/* + * Encodes a QUIC STREAM frame's header to the packet writer. The f->stream_id, + * f->offset and f->len fields are the values for the respective Stream ID, + * Offset and Length fields. + * + * If f->is_fin is non-zero, the frame is marked as the final frame in the + * stream. + * + * If f->has_explicit_len is zerro, the frame is assumed to be the final frame + * in the packet, which the caller is responsible for ensuring; the Length + * field is then omitted. + * + * To create a well-formed frame, the data written using this function must be + * immediately followed by f->len bytes of stream data. + */ +int ossl_quic_wire_encode_frame_stream_hdr(WPACKET *pkt, + const OSSL_QUIC_FRAME_STREAM *f); + +/* + * Functions similarly to ossl_quic_wire_encode_frame_stream_hdr, but it also + * allocates space for f->len bytes of data after the header, creating a + * well-formed QUIC STREAM frame in one call. + * + * A pointer to the bytes allocated for the framme payload is returned, + * which the caller can fill however it wishes. If f->data is non-NULL, + * it is automatically copied to the target buffer, otherwise the caller + * must fill the returned buffer. Returns NULL on failure. + */ +void *ossl_quic_wire_encode_frame_stream(WPACKET *pkt, + const OSSL_QUIC_FRAME_STREAM *f); + +/* + * Encodes a QUIC MAX_DATA frame to the packet writer. + */ +int ossl_quic_wire_encode_frame_max_data(WPACKET *pkt, + uint64_t max_data); + +/* + * Encodes a QUIC MAX_STREAM_DATA frame to the packet writer. + */ +int ossl_quic_wire_encode_frame_max_stream_data(WPACKET *pkt, + uint64_t stream_id, + uint64_t max_data); + +/* + * Encodes a QUIC MAX_STREAMS frame to the packet writer. + * + * If is_uni is 0, the count specifies the maximum number of + * bidirectional streams; else it specifies the maximum number of unidirectional + * streams. + */ +int ossl_quic_wire_encode_frame_max_streams(WPACKET *pkt, + char is_uni, + uint64_t max_streams); + +/* + * Encodes a QUIC DATA_BLOCKED frame to the packet writer. + */ +int ossl_quic_wire_encode_frame_data_blocked(WPACKET *pkt, + uint64_t max_data); + +/* + * Encodes a QUIC STREAM_DATA_BLOCKED frame to the packet writer. + */ +int ossl_quic_wire_encode_frame_stream_data_blocked(WPACKET *pkt, + uint64_t stream_id, + uint64_t max_stream_data); +/* + * Encodes a QUIC STREAMS_BLOCKED frame to the packet writer. + * + * If is_uni is 0, the count specifies the maximum number of + * bidirectional streams; else it specifies the maximum number of unidirectional + * streams. + */ +int ossl_quic_wire_encode_frame_streams_blocked(WPACKET *pkt, + char is_uni, + uint64_t max_streams); + +/* + * Encodes a QUIC NEW_CONNECTION_ID frame to the packet writer, given a logical + * representation of the NEW_CONNECTION_ID frame. + * + * The buffer pointed to by the conn_id field must be valid for the duration of + * the call. + */ +int ossl_quic_wire_encode_frame_new_conn_id(WPACKET *pkt, + const OSSL_QUIC_FRAME_NEW_CONN_ID *f); + +/* + * Encodes a QUIC RETIRE_CONNECTION_ID frame to the packet writer. + */ +int ossl_quic_wire_encode_frame_retire_conn_id(WPACKET *pkt, + uint64_t seq_num); + +/* + * Encodes a QUIC PATH_CHALLENGE frame to the packet writer. + */ +int ossl_quic_wire_encode_frame_path_challenge(WPACKET *pkt, + uint64_t data); + +/* + * Encodes a QUIC PATH_RESPONSE frame to the packet writer. + */ +int ossl_quic_wire_encode_frame_path_response(WPACKET *pkt, + uint64_t data); + +/* + * Encodes a QUIC CONNECTION_CLOSE frame to the packet writer, given a logical + * representation of the CONNECTION_CLOSE frame. + * + * The reason field may be NULL, in which case no reason is encoded. If the + * reason field is non-NULL, it must point to a valid UTF-8 string and + * reason_len must be set to the length of the reason string in bytes. The + * reason string need not be zero terminated. + */ +int ossl_quic_wire_encode_frame_conn_close(WPACKET *pkt, + const OSSL_QUIC_FRAME_CONN_CLOSE *f); + +/* + * Encodes a QUIC HANDSHAKE_DONE frame to the packet writer. This frame type + * takes no arguiments. + */ +int ossl_quic_wire_encode_frame_handshake_done(WPACKET *pkt); + +/* + * Encodes a QUIC transport parameter TLV with the given ID into the WPACKET. + * The payload is an arbitrary buffer. + * + * If value is non-NULL, the value is copied into the packet. + * If it is NULL, value_len bytes are allocated for the payload and the caller + * should fill the buffer using the returned pointer. + * + * Returns a pointer to the start of the payload on success, or NULL on failure. + */ +unsigned char *ossl_quic_wire_encode_transport_param_bytes(WPACKET *pkt, + uint64_t id, + const unsigned char *value, + size_t value_len); + +/* + * Encodes a QUIC transport parameter TLV with the given ID into the WPACKET. + * The payload is a QUIC variable-length integer with the given value. + */ +int ossl_quic_wire_encode_transport_param_int(WPACKET *pkt, + uint64_t id, + uint64_t value); + +/* + * QUIC Wire Format Decoding + * ========================= + * + * These functions return 1 on success or 0 for failure. Typical reasons + * why these functions may fail include: + * + * - A frame decode function is called but the frame in the PACKET's buffer + * is not of the correct type. + * + * - A variable-length field in the encoded frame appears to exceed the bounds + * of the PACKET's buffer. + * + * These functions should be called with the PACKET pointing to the start of the + * frame (including the initial type field), and consume an entire frame + * including its type field. The expectation is that the caller will have + * already discerned the frame type using ossl_quic_wire_peek_frame_header(). + */ + +/* + * Decodes the type field header of a QUIC frame (without advancing the current + * position). This can be used to determine the frame type and determine which + * frame decoding function to call. + */ +int ossl_quic_wire_peek_frame_header(PACKET *pkt, uint64_t *type); + +/* + * Like ossl_quic_wire_peek_frame_header, but advances the current position + * so that the type field is consumed. For advanced use only. + */ +int ossl_quic_wire_skip_frame_header(PACKET *pkt, uint64_t *type); + +/* + * Determines how many ranges are needed to decode a QUIC ACK frame. + * + * The number of ranges which must be allocated before the call to + * ossl_quic_wire_decode_frame_ack is written to *total_ranges. + * + * The PACKET is not advanced. + */ +int ossl_quic_wire_peek_frame_ack_num_ranges(const PACKET *pkt, + uint64_t *total_ranges); + +/* + * Decodes a QUIC ACK frame. The ack_ranges field of the passed structure should + * point to a preallocated array of ACK ranges and the num_ack_ranges field + * should specify the length of allocation. + * + * *total_ranges is written with the number of ranges in the decoded frame, + * which may be greater than the number of ranges which were decoded (i.e. if + * num_ack_ranges was too small to decode all ranges). + * + * On success, this function modifies the num_ack_ranges field to indicate the + * number of ranges in the decoded frame. This is the number of entries in the + * ACK ranges array written by this function; any additional entries are not + * modified. + * + * If the number of ACK ranges in the decoded frame exceeds that in + * num_ack_ranges, as many ACK ranges as possible are decoded into the range + * array. The caller can use the value written to *total_ranges to detect this + * condition, as *total_ranges will exceed num_ack_ranges. + * + * If ack is NULL, the frame is still decoded, but only *total_ranges is + * written. This can be used to determine the number of ranges which must be + * allocated. + * + * The ack_delay_exponent argument specifies the index of a power of two used to + * decode the ack_delay field. This must match the ack_delay_exponent value used + * to encode the frame. + */ +int ossl_quic_wire_decode_frame_ack(PACKET *pkt, + uint32_t ack_delay_exponent, + OSSL_QUIC_FRAME_ACK *ack, + uint64_t *total_ranges); + +/* + * Decodes a QUIC RESET_STREAM frame. + */ +int ossl_quic_wire_decode_frame_reset_stream(PACKET *pkt, + OSSL_QUIC_FRAME_RESET_STREAM *f); + +/* + * Decodes a QUIC STOP_SENDING frame. + */ +int ossl_quic_wire_decode_frame_stop_sending(PACKET *pkt, + OSSL_QUIC_FRAME_STOP_SENDING *f); + +/* + * Decodes a QUIC CRYPTO frame. + * + * f->data is set to point inside the packet buffer inside the PACKET, therefore + * it is safe to access for as long as the packet buffer exists. + */ +int ossl_quic_wire_decode_frame_crypto(PACKET *pkt, + OSSL_QUIC_FRAME_CRYPTO *f); + +/* + * Decodes a QUIC NEW_TOKEN frame. *token is written with a pointer to the token + * bytes and *token_len is written with the length of the token in bytes. + */ +int ossl_quic_wire_decode_frame_new_token(PACKET *pkt, + const unsigned char **token, + size_t *token_len); + +/* + * Decodes a QUIC STREAM frame. + * + * If the frame did not contain an offset field, f->offset is set to 0, as the + * absence of an offset field is equivalent to an offset of 0. + * + * If the frame contained a length field, f->has_explicit_len is set to 1 and + * the length of the data is placed in f->len. This function ensures that the + * length does not exceed the packet buffer, thus it is safe to access f->data. + * + * If the frame did not contain a length field, this means that the frame runs + * until the end of the packet. This function sets f->has_explicit_len to zero, + * and f->len to the amount of data remaining in the input buffer. Therefore, + * this function should be used with a PACKET representing a single packet (and + * not e.g. multiple packets). + * + * Note also that this means f->len is always valid after this function returns + * successfully, regardless of the value of f->has_explicit_len. + * + * f->data points inside the packet buffer inside the PACKET, therefore it is + * safe to access for as long as the packet buffer exists. + * + * f->is_fin is set according to whether the frame was marked as ending the + * stream. + */ +int ossl_quic_wire_decode_frame_stream(PACKET *pkt, + OSSL_QUIC_FRAME_STREAM *f); + +/* + * Decodes a QUIC MAX_DATA frame. The Maximum Data field is written to + * *max_data. + */ +int ossl_quic_wire_decode_frame_max_data(PACKET *pkt, + uint64_t *max_data); + +/* + * Decodes a QUIC MAX_STREAM_DATA frame. The Stream ID is written to *stream_id + * and Maximum Stream Data field is written to *max_stream_data. + */ +int ossl_quic_wire_decode_frame_max_stream_data(PACKET *pkt, + uint64_t *stream_id, + uint64_t *max_stream_data); +/* + * Decodes a QUIC MAX_STREAMS frame. The Maximum Streams field is written to + * *max_streams. + * + * Whether the limit concerns bidirectional streams or unidirectional streams is + * denoted by the frame type; the caller should examine the frame type to + * determine this. + */ +int ossl_quic_wire_decode_frame_max_streams(PACKET *pkt, + uint64_t *max_streams); + +/* + * Decodes a QUIC DATA_BLOCKED frame. The Maximum Data field is written to + * *max_data. + */ +int ossl_quic_wire_decode_frame_data_blocked(PACKET *pkt, + uint64_t *max_data); + +/* + * Decodes a QUIC STREAM_DATA_BLOCKED frame. The Stream ID and Maximum Stream + * Data fields are written to *stream_id and *max_stream_data respectively. + */ +int ossl_quic_wire_decode_frame_stream_data_blocked(PACKET *pkt, + uint64_t *stream_id, + uint64_t *max_stream_data); + +/* + * Decodes a QUIC STREAMS_BLOCKED frame. The Maximum Streams field is written to + * *max_streams. + * + * Whether the limit concerns bidirectional streams or unidirectional streams is + * denoted by the frame type; the caller should examine the frame type to + * determine this. + */ +int ossl_quic_wire_decode_frame_streams_blocked(PACKET *pkt, + uint64_t *max_streams); + + +/* + * Decodes a QUIC NEW_CONNECTION_ID frame. The logical representation of the + * frame is written to *f. + * + * The conn_id field is set to point to the connection ID string inside the + * packet buffer; it is therefore valid for as long as the PACKET's buffer is + * valid. The conn_id_len field is set to the length of the connection ID string + * in bytes. + */ +int ossl_quic_wire_decode_frame_new_conn_id(PACKET *pkt, + OSSL_QUIC_FRAME_NEW_CONN_ID *f); + +/* + * Decodes a QUIC RETIRE_CONNECTION_ID frame. The Sequence Number field + * is written to *seq_num. + */ +int ossl_quic_wire_decode_frame_retire_conn_id(PACKET *pkt, + uint64_t *seq_num); + +/* + * Decodes a QUIC PATH_CHALLENGE frame. The Data field is written to *data. + */ +int ossl_quic_wire_decode_frame_path_challenge(PACKET *pkt, + uint64_t *data); + +/* + * Decodes a QUIC PATH_CHALLENGE frame. The Data field is written to *data. + */ +int ossl_quic_wire_decode_frame_path_response(PACKET *pkt, + uint64_t *data); + +/* + * Decodes a QUIC CONNECTION_CLOSE frame. The logical representation + * of the frame is written to *f. + * + * The reason field is set to point to the UTF-8 reason string inside + * the packet buffer; it is therefore valid for as long as the PACKET's + * buffer is valid. The reason_len field is set to the length of the + * reason string in bytes. + * + * IMPORTANT: The reason string is not zero-terminated. + * + * Returns 1 on success or 0 on failure. + */ +int ossl_quic_wire_decode_frame_conn_close(PACKET *pkt, + OSSL_QUIC_FRAME_CONN_CLOSE *f); + +/* + * Decodes one or more PADDING frames. PADDING frames have no arguments. + * + * Returns the number of PADDING frames decoded or 0 on error. + */ +size_t ossl_quic_wire_decode_padding(PACKET *pkt); + +/* + * Decodes a PING frame. The frame has no arguments. + */ +int ossl_quic_wire_decode_frame_ping(PACKET *pkt); + +/* + * Decodes a HANDSHAKE_DONE frame. The frame has no arguments. + */ +int ossl_quic_wire_decode_frame_handshake_done(PACKET *pkt); + +/* + * Peeks at the ID of the next QUIC transport parameter TLV in the stream. + * The ID is written to *id. + */ +int ossl_quic_wire_peek_transport_param(PACKET *pkt, uint64_t *id); + +/* + * Decodes a QUIC transport parameter TLV. A pointer to the value buffer is + * returned on success. This points inside the PACKET's buffer and is therefore + * valid as long as the PACKET's buffer is valid. + * + * The transport parameter ID is written to *id and the length of the payload + * in bytes is written to *len. + * + * Returns NULL on failure. + */ +const unsigned char *ossl_quic_wire_decode_transport_param_bytes(PACKET *pkt, + uint64_t *id, + size_t *len); + +/* + * Decodes a QUIC transport parameter TLV containing a variable-length integer. + * + * The transport parameter ID is written to *id and the value is written to + * *value. + */ +int ossl_quic_wire_decode_transport_param_int(PACKET *pkt, + uint64_t *id, + uint64_t *value); + +#endif diff --git a/include/internal/time.h b/include/internal/time.h index 4765274f17fc5..b4a67fb998e5c 100644 --- a/include/internal/time.h +++ b/include/internal/time.h @@ -16,7 +16,13 @@ # include "internal/safe_math.h" /* The precision of times allows this many values per second */ -# define OSSL_TIME_SECOND 1000000000 +# define OSSL_TIME_SECOND ((uint64_t)1000000000) + +/* One millisecond. */ +# define OSSL_TIME_MS (OSSL_TIME_SECOND / 1000) + +/* One microsecond. */ +# define OSSL_TIME_US (OSSL_TIME_MS / 1000) /* Macro representing the most distant future time */ # define OSSL_TIME_INFINITY (~(OSSL_TIME)0) @@ -24,6 +30,9 @@ /* Macro that's guaranteed to be now or before */ # define OSSL_TIME_IMMEDIATE 0 +/* Macro representing the zero value */ +# define OSSL_TIME_ZERO 0 + /* * Internal type defining a time. * The time datum is Unix's 1970 and at nanosecond precision, this gives @@ -84,4 +93,45 @@ OSSL_TIME ossl_time_subtract(OSSL_TIME a, OSSL_TIME b) return err ? 0 : r; } +/* Returns |a - b|. */ +static ossl_unused ossl_inline +OSSL_TIME ossl_time_abs_difference(OSSL_TIME a, OSSL_TIME b) +{ + return a > b ? ossl_time_subtract(a, b) : ossl_time_subtract(b, a); +} + +static ossl_unused ossl_inline +OSSL_TIME ossl_time_multiply(OSSL_TIME a, uint64_t b) +{ + OSSL_TIME r; + int err = 0; + + r = safe_mul_time(a, b, &err); + return err ? OSSL_TIME_INFINITY : r; +} + +static ossl_unused ossl_inline +OSSL_TIME ossl_time_divide(OSSL_TIME a, uint64_t b) +{ + OSSL_TIME r; + int err = 0; + + r = safe_div_time(a, b, &err); + return err ? 0 : r; +} + +/* Return higher of the two given time values. */ +static ossl_unused ossl_inline +OSSL_TIME ossl_time_max(OSSL_TIME a, OSSL_TIME b) +{ + return a > b ? a : b; +} + +/* Return the lower of the two given time values. */ +static ossl_unused ossl_inline +OSSL_TIME ossl_time_min(OSSL_TIME a, OSSL_TIME b) +{ + return a < b ? a : b; +} + #endif diff --git a/ssl/quic/build.info b/ssl/quic/build.info index f9578454e8a22..3e76055c6276d 100644 --- a/ssl/quic/build.info +++ b/ssl/quic/build.info @@ -1,3 +1,3 @@ $LIBSSL=../../libssl -SOURCE[$LIBSSL]=quic_method.c quic_impl.c +SOURCE[$LIBSSL]=quic_method.c quic_impl.c quic_wire.c diff --git a/ssl/quic/quic_wire.c b/ssl/quic/quic_wire.c new file mode 100644 index 0000000000000..0adb41ef590ec --- /dev/null +++ b/ssl/quic/quic_wire.c @@ -0,0 +1,823 @@ +/* + * 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 +#include +#include "quic_local.h" +#include "internal/quic_vlint.h" +#include "internal/quic_wire.h" + +OSSL_SAFE_MATH_UNSIGNED(uint64_t, uint64_t) + +/* + * QUIC Wire Format Encoding + * ========================= + */ + +int ossl_quic_wire_encode_padding(WPACKET *pkt, size_t num_bytes) +{ + /* + * PADDING is frame type zero, which as a variable-length integer is + * represented as a single zero byte. As an optimisation, just use memset. + */ + return WPACKET_memset(pkt, 0, num_bytes); +} + +static int encode_frame_hdr(WPACKET *pkt, uint64_t frame_type) +{ + return WPACKET_quic_write_vlint(pkt, frame_type); +} + +int ossl_quic_wire_encode_frame_ping(WPACKET *pkt) +{ + return encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_PING); +} + +int ossl_quic_wire_encode_frame_ack(WPACKET *pkt, + uint32_t ack_delay_exponent, + const OSSL_QUIC_FRAME_ACK *ack) +{ + uint64_t frame_type = ack->ecn_present ? OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN + : OSSL_QUIC_FRAME_TYPE_ACK_WITHOUT_ECN; + + uint64_t largest_ackd, first_ack_range, ack_delay_enc; + size_t i, num_ack_ranges = ack->num_ack_ranges; + + if (num_ack_ranges == 0) + return 0; + + ack_delay_enc = ossl_time_divide(ossl_time_divide(ack->delay_time, + OSSL_TIME_US), + 1UL << ack_delay_exponent); + largest_ackd = ack->ack_ranges[0].end; + first_ack_range = ack->ack_ranges[0].end - ack->ack_ranges[0].start; + + if (!encode_frame_hdr(pkt, frame_type) + || !WPACKET_quic_write_vlint(pkt, largest_ackd) + || !WPACKET_quic_write_vlint(pkt, ack_delay_enc) + || !WPACKET_quic_write_vlint(pkt, num_ack_ranges - 1) + || !WPACKET_quic_write_vlint(pkt, first_ack_range)) + return 0; + + for (i = 1; i < num_ack_ranges; ++i) { + uint64_t gap, range_len; + + gap = ack->ack_ranges[i - 1].start - ack->ack_ranges[i].end - 2; + range_len = ack->ack_ranges[i].end - ack->ack_ranges[i].start; + + if (!WPACKET_quic_write_vlint(pkt, gap) + || !WPACKET_quic_write_vlint(pkt, range_len)) + return 0; + } + + if (ack->ecn_present) + if (!WPACKET_quic_write_vlint(pkt, ack->ect0) + || !WPACKET_quic_write_vlint(pkt, ack->ect1) + || !WPACKET_quic_write_vlint(pkt, ack->ecnce)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_reset_stream(WPACKET *pkt, + const OSSL_QUIC_FRAME_RESET_STREAM *f) +{ + if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_RESET_STREAM) + || !WPACKET_quic_write_vlint(pkt, f->stream_id) + || !WPACKET_quic_write_vlint(pkt, f->app_error_code) + || !WPACKET_quic_write_vlint(pkt, f->final_size)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_stop_sending(WPACKET *pkt, + const OSSL_QUIC_FRAME_STOP_SENDING *f) +{ + if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_STOP_SENDING) + || !WPACKET_quic_write_vlint(pkt, f->stream_id) + || !WPACKET_quic_write_vlint(pkt, f->app_error_code)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_crypto_hdr(WPACKET *pkt, + const OSSL_QUIC_FRAME_CRYPTO *f) +{ + if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_CRYPTO) + || !WPACKET_quic_write_vlint(pkt, f->offset) + || !WPACKET_quic_write_vlint(pkt, f->len)) + return 0; + + return 1; +} + +void *ossl_quic_wire_encode_frame_crypto(WPACKET *pkt, + const OSSL_QUIC_FRAME_CRYPTO *f) +{ + unsigned char *p = NULL; + + if (!ossl_quic_wire_encode_frame_crypto_hdr(pkt, f) + || !WPACKET_allocate_bytes(pkt, f->len, &p)) + return NULL; + + if (f->data != NULL) + memcpy(p, f->data, f->len); + + return p; +} + +int ossl_quic_wire_encode_frame_new_token(WPACKET *pkt, + const unsigned char *token, + size_t token_len) +{ + if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_NEW_TOKEN) + || !WPACKET_quic_write_vlint(pkt, token_len) + || !WPACKET_memcpy(pkt, token, token_len)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_stream_hdr(WPACKET *pkt, + const OSSL_QUIC_FRAME_STREAM *f) +{ + uint64_t frame_type = OSSL_QUIC_FRAME_TYPE_STREAM; + + if (f->offset != 0) + frame_type |= OSSL_QUIC_FRAME_FLAG_STREAM_OFF; + if (f->has_explicit_len) + frame_type |= OSSL_QUIC_FRAME_FLAG_STREAM_LEN; + if (f->is_fin) + frame_type |= OSSL_QUIC_FRAME_FLAG_STREAM_FIN; + + if (!encode_frame_hdr(pkt, frame_type) + || !WPACKET_quic_write_vlint(pkt, f->stream_id)) + return 0; + + if (f->offset != 0 && !WPACKET_quic_write_vlint(pkt, f->offset)) + return 0; + + if (f->has_explicit_len && !WPACKET_quic_write_vlint(pkt, f->len)) + return 0; + + return 1; +} + +void *ossl_quic_wire_encode_frame_stream(WPACKET *pkt, + const OSSL_QUIC_FRAME_STREAM *f) +{ + + unsigned char *p = NULL; + + if (!ossl_quic_wire_encode_frame_stream_hdr(pkt, f)) + return NULL; + + if (!WPACKET_allocate_bytes(pkt, f->len, &p)) + return NULL; + + if (f->data != NULL) + memcpy(p, f->data, f->len); + + return p; +} + +int ossl_quic_wire_encode_frame_max_data(WPACKET *pkt, + uint64_t max_data) +{ + if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_MAX_DATA) + || !WPACKET_quic_write_vlint(pkt, max_data)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_max_stream_data(WPACKET *pkt, + uint64_t stream_id, + uint64_t max_data) +{ + if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA) + || !WPACKET_quic_write_vlint(pkt, stream_id) + || !WPACKET_quic_write_vlint(pkt, max_data)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_max_streams(WPACKET *pkt, + char is_uni, + uint64_t max_streams) +{ + if (!encode_frame_hdr(pkt, is_uni ? OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI + : OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI) + || !WPACKET_quic_write_vlint(pkt, max_streams)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_data_blocked(WPACKET *pkt, + uint64_t max_data) +{ + if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_DATA_BLOCKED) + || !WPACKET_quic_write_vlint(pkt, max_data)) + return 0; + + return 1; +} + + +int ossl_quic_wire_encode_frame_stream_data_blocked(WPACKET *pkt, + uint64_t stream_id, + uint64_t max_stream_data) +{ + if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_STREAM_DATA_BLOCKED) + || !WPACKET_quic_write_vlint(pkt, stream_id) + || !WPACKET_quic_write_vlint(pkt, max_stream_data)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_streams_blocked(WPACKET *pkt, + char is_uni, + uint64_t max_streams) +{ + if (!encode_frame_hdr(pkt, is_uni ? OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_UNI + : OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI) + || !WPACKET_quic_write_vlint(pkt, max_streams)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_new_conn_id(WPACKET *pkt, + const OSSL_QUIC_FRAME_NEW_CONN_ID *f) +{ + if (f->conn_id.id_len > OSSL_QUIC_MAX_CONN_ID_LEN) + return 0; + + if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_NEW_CONN_ID) + || !WPACKET_quic_write_vlint(pkt, f->seq_num) + || !WPACKET_quic_write_vlint(pkt, f->retire_prior_to) + || !WPACKET_put_bytes_u8(pkt, f->conn_id.id_len) + || !WPACKET_memcpy(pkt, f->conn_id.id, f->conn_id.id_len) + || !WPACKET_memcpy(pkt, f->stateless_reset_token, + sizeof(f->stateless_reset_token))) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_retire_conn_id(WPACKET *pkt, + uint64_t seq_num) +{ + if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_RETIRE_CONN_ID) + || !WPACKET_quic_write_vlint(pkt, seq_num)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_path_challenge(WPACKET *pkt, + uint64_t data) +{ + if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_PATH_CHALLENGE) + || !WPACKET_put_bytes_u64(pkt, data)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_path_response(WPACKET *pkt, + uint64_t data) +{ + if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE) + || !WPACKET_put_bytes_u64(pkt, data)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_conn_close(WPACKET *pkt, + const OSSL_QUIC_FRAME_CONN_CLOSE *f) +{ + if (!encode_frame_hdr(pkt, f->is_app ? OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_APP + : OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_TRANSPORT) + || !WPACKET_quic_write_vlint(pkt, f->error_code)) + return 0; + + if (!f->is_app && !WPACKET_quic_write_vlint(pkt, f->frame_type)) + return 0; + + if (!WPACKET_quic_write_vlint(pkt, f->reason_len) + || !WPACKET_memcpy(pkt, f->reason, f->reason_len)) + return 0; + + return 1; +} + +int ossl_quic_wire_encode_frame_handshake_done(WPACKET *pkt) +{ + return encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE); +} + +unsigned char *ossl_quic_wire_encode_transport_param_bytes(WPACKET *pkt, + uint64_t id, + const unsigned char *value, + size_t value_len) +{ + unsigned char *b = NULL; + + if (!WPACKET_quic_write_vlint(pkt, id) + || !WPACKET_quic_write_vlint(pkt, value_len) + || !WPACKET_allocate_bytes(pkt, value_len, (unsigned char **)&b)) + return NULL; + + if (value != NULL) + memcpy(b, value, value_len); + + return b; +} + +int ossl_quic_wire_encode_transport_param_int(WPACKET *pkt, + uint64_t id, + uint64_t value) +{ + if (!WPACKET_quic_write_vlint(pkt, id) + || !WPACKET_quic_write_vlint(pkt, ossl_quic_vlint_encode_len(value)) + || !WPACKET_quic_write_vlint(pkt, value)) + return 0; + + return 1; +} + +/* + * QUIC Wire Format Decoding + * ========================= + */ +int ossl_quic_wire_peek_frame_header(PACKET *pkt, uint64_t *type) +{ + return PACKET_peek_quic_vlint(pkt, type); +} + +int ossl_quic_wire_skip_frame_header(PACKET *pkt, uint64_t *type) +{ + return PACKET_get_quic_vlint(pkt, type); +} + +static int expect_frame_header_mask(PACKET *pkt, + uint64_t expected_frame_type, + uint64_t mask_bits, + uint64_t *actual_frame_type) +{ + uint64_t actual_frame_type_; + + if (!ossl_quic_wire_skip_frame_header(pkt, &actual_frame_type_) + || (actual_frame_type_ & ~mask_bits) != expected_frame_type) + return 0; + + if (actual_frame_type != NULL) + *actual_frame_type = actual_frame_type_; + + return 1; +} + +static int expect_frame_header(PACKET *pkt, uint64_t expected_frame_type) +{ + uint64_t actual_frame_type; + + if (!ossl_quic_wire_skip_frame_header(pkt, &actual_frame_type) + || actual_frame_type != expected_frame_type) + return 0; + + return 1; +} + +int ossl_quic_wire_peek_frame_ack_num_ranges(const PACKET *orig_pkt, + uint64_t *total_ranges) +{ + PACKET pkt = *orig_pkt; + uint64_t ack_range_count; + + if (!expect_frame_header_mask(&pkt, OSSL_QUIC_FRAME_TYPE_ACK_WITHOUT_ECN, + 1, NULL) + || !PACKET_skip_quic_vlint(&pkt) + || !PACKET_skip_quic_vlint(&pkt) + || !PACKET_get_quic_vlint(&pkt, &ack_range_count)) + return 0; + + /* (cannot overflow because QUIC vlints can only encode up to 2**62-1) */ + *total_ranges = ack_range_count + 1; + return 1; +} + +int ossl_quic_wire_decode_frame_ack(PACKET *pkt, + uint32_t ack_delay_exponent, + OSSL_QUIC_FRAME_ACK *ack, + uint64_t *total_ranges) { + uint64_t frame_type, largest_ackd, ack_delay_raw; + uint64_t ack_range_count, first_ack_range, start, end, i; + + /* This call matches both ACK_WITHOUT_ECN and ACK_WITH_ECN. */ + if (!expect_frame_header_mask(pkt, OSSL_QUIC_FRAME_TYPE_ACK_WITHOUT_ECN, + 1, &frame_type) + || !PACKET_get_quic_vlint(pkt, &largest_ackd) + || !PACKET_get_quic_vlint(pkt, &ack_delay_raw) + || !PACKET_get_quic_vlint(pkt, &ack_range_count) + || !PACKET_get_quic_vlint(pkt, &first_ack_range)) + return 0; + + if (first_ack_range > largest_ackd) + return 0; + + start = largest_ackd - first_ack_range; + + if (ack != NULL) { + int err = 0; + ack->delay_time + = ossl_time_multiply(OSSL_TIME_US, + safe_mul_uint64_t(ack_delay_raw, + 1UL << ack_delay_exponent, + &err)); + if (err) + ack->delay_time = OSSL_TIME_INFINITY; + + if (ack->num_ack_ranges > 0) { + ack->ack_ranges[0].end = largest_ackd; + ack->ack_ranges[0].start = start; + } + } + + for (i = 0; i < ack_range_count; ++i) { + uint64_t gap, len; + + if (!PACKET_get_quic_vlint(pkt, &gap) + || !PACKET_get_quic_vlint(pkt, &len)) + return 0; + + end = start - gap - 2; + if (start < gap + 2 || len > end) + return 0; + + if (ack != NULL && i + 1 < ack->num_ack_ranges) { + ack->ack_ranges[i + 1].start = start = end - len; + ack->ack_ranges[i + 1].end = end; + } + } + + if (ack != NULL && ack_range_count + 1 < ack->num_ack_ranges) + ack->num_ack_ranges = ack_range_count + 1; + + if (total_ranges != NULL) + *total_ranges = ack_range_count + 1; + + if (frame_type == OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN) { + uint64_t ect0, ect1, ecnce; + + if (!PACKET_get_quic_vlint(pkt, &ect0) + || !PACKET_get_quic_vlint(pkt, &ect1) + || !PACKET_get_quic_vlint(pkt, &ecnce)) + return 0; + + if (ack != NULL) { + ack->ect0 = ect0; + ack->ect1 = ect1; + ack->ecnce = ecnce; + ack->ecn_present = 1; + } + } else if (ack != NULL) { + ack->ecn_present = 0; + } + + return 1; +} + +int ossl_quic_wire_decode_frame_reset_stream(PACKET *pkt, + OSSL_QUIC_FRAME_RESET_STREAM *f) +{ + if (!expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_RESET_STREAM) + || !PACKET_get_quic_vlint(pkt, &f->stream_id) + || !PACKET_get_quic_vlint(pkt, &f->app_error_code) + || !PACKET_get_quic_vlint(pkt, &f->final_size)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_stop_sending(PACKET *pkt, + OSSL_QUIC_FRAME_STOP_SENDING *f) +{ + if (!expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_STOP_SENDING) + || !PACKET_get_quic_vlint(pkt, &f->stream_id) + || !PACKET_get_quic_vlint(pkt, &f->app_error_code)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_crypto(PACKET *pkt, + OSSL_QUIC_FRAME_CRYPTO *f) +{ + if (!expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_CRYPTO) + || !PACKET_get_quic_vlint(pkt, &f->offset) + || !PACKET_get_quic_vlint(pkt, &f->len)) + return 0; + + if (PACKET_remaining(pkt) < f->len) + return 0; + + f->data = PACKET_data(pkt); + + if (!PACKET_forward(pkt, f->len)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_new_token(PACKET *pkt, + const unsigned char **token, + size_t *token_len) +{ + uint64_t token_len_; + + if (!expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_NEW_TOKEN) + || !PACKET_get_quic_vlint(pkt, &token_len_)) + return 0; + + if (token_len_ > SIZE_MAX) + return 0; + + *token = PACKET_data(pkt); + *token_len = token_len_; + + if (!PACKET_forward(pkt, token_len_)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_stream(PACKET *pkt, + OSSL_QUIC_FRAME_STREAM *f) +{ + uint64_t frame_type; + + /* This call matches all STREAM values (low 3 bits are masked). */ + if (!expect_frame_header_mask(pkt, OSSL_QUIC_FRAME_TYPE_STREAM, + OSSL_QUIC_FRAME_FLAG_STREAM_MASK, + &frame_type) + || !PACKET_get_quic_vlint(pkt, &f->stream_id)) + return 0; + + if ((frame_type & OSSL_QUIC_FRAME_FLAG_STREAM_OFF) != 0) { + if (!PACKET_get_quic_vlint(pkt, &f->offset)) + return 0; + } else { + f->offset = 0; + } + + f->has_explicit_len = ((frame_type & OSSL_QUIC_FRAME_FLAG_STREAM_LEN) != 0); + f->is_fin = ((frame_type & OSSL_QUIC_FRAME_FLAG_STREAM_FIN) != 0); + + if (f->has_explicit_len) { + if (!PACKET_get_quic_vlint(pkt, &f->len)) + return 0; + } else { + f->len = PACKET_remaining(pkt); + } + + f->data = PACKET_data(pkt); + + if (!PACKET_forward(pkt, f->len)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_max_data(PACKET *pkt, + uint64_t *max_data) +{ + if (!expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_MAX_DATA) + || !PACKET_get_quic_vlint(pkt, max_data)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_max_stream_data(PACKET *pkt, + uint64_t *stream_id, + uint64_t *max_stream_data) +{ + if (!expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA) + || !PACKET_get_quic_vlint(pkt, stream_id) + || !PACKET_get_quic_vlint(pkt, max_stream_data)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_max_streams(PACKET *pkt, + uint64_t *max_streams) +{ + /* This call matches both MAX_STREAMS_BIDI and MAX_STREAMS_UNI. */ + if (!expect_frame_header_mask(pkt, OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI, + 1, NULL) + || !PACKET_get_quic_vlint(pkt, max_streams)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_data_blocked(PACKET *pkt, + uint64_t *max_data) +{ + if (!expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_DATA_BLOCKED) + || !PACKET_get_quic_vlint(pkt, max_data)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_stream_data_blocked(PACKET *pkt, + uint64_t *stream_id, + uint64_t *max_stream_data) +{ + if (!expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_STREAM_DATA_BLOCKED) + || !PACKET_get_quic_vlint(pkt, stream_id) + || !PACKET_get_quic_vlint(pkt, max_stream_data)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_streams_blocked(PACKET *pkt, + uint64_t *max_streams) +{ + /* This call matches both STREAMS_BLOCKED_BIDI and STREAMS_BLOCKED_UNI. */ + if (!expect_frame_header_mask(pkt, OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI, + 1, NULL) + || !PACKET_get_quic_vlint(pkt, max_streams)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_new_conn_id(PACKET *pkt, + OSSL_QUIC_FRAME_NEW_CONN_ID *f) +{ + unsigned int len; + + if (!expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_NEW_CONN_ID) + || !PACKET_get_quic_vlint(pkt, &f->seq_num) + || !PACKET_get_quic_vlint(pkt, &f->retire_prior_to) + || !PACKET_get_1(pkt, &len) + || len > OSSL_QUIC_MAX_CONN_ID_LEN) + return 0; + + f->conn_id.id_len = (unsigned char)len; + if (!PACKET_copy_bytes(pkt, f->conn_id.id, len)) + return 0; + + /* Clear unused bytes to allow consistent memcmp. */ + if (len < OSSL_QUIC_MAX_CONN_ID_LEN) + memset(f->conn_id.id + len, 0, OSSL_QUIC_MAX_CONN_ID_LEN - len); + + if (!PACKET_copy_bytes(pkt, f->stateless_reset_token, + sizeof(f->stateless_reset_token))) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_retire_conn_id(PACKET *pkt, + uint64_t *seq_num) +{ + if (!expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_RETIRE_CONN_ID) + || !PACKET_get_quic_vlint(pkt, seq_num)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_path_challenge(PACKET *pkt, + uint64_t *data) +{ + if (!expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_PATH_CHALLENGE) + || !PACKET_get_net_8(pkt, data)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_path_response(PACKET *pkt, + uint64_t *data) +{ + if (!expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE) + || !PACKET_get_net_8(pkt, data)) + return 0; + + return 1; +} + +int ossl_quic_wire_decode_frame_conn_close(PACKET *pkt, + OSSL_QUIC_FRAME_CONN_CLOSE *f) +{ + uint64_t frame_type, reason_len; + + /* This call matches both CONN_CLOSE_TRANSPORT and CONN_CLOSE_APP. */ + if (!expect_frame_header_mask(pkt, OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_TRANSPORT, + 1, &frame_type) + || !PACKET_get_quic_vlint(pkt, &f->error_code)) + return 0; + + f->is_app = ((frame_type & 1) != 0); + + if (!f->is_app) { + if (!PACKET_get_quic_vlint(pkt, &f->frame_type)) + return 0; + } else { + f->frame_type = 0; + } + + if (!PACKET_get_quic_vlint(pkt, &reason_len) + || reason_len > SIZE_MAX) + return 0; + + if (!PACKET_get_bytes(pkt, (const unsigned char **)&f->reason, reason_len)) + return 0; + + f->reason_len = reason_len; + return 1; +} + +size_t ossl_quic_wire_decode_padding(PACKET *pkt) +{ + const unsigned char *start = PACKET_data(pkt), *end = PACKET_end(pkt), + *p = start; + + while (p < end && *p == 0) + ++p; + + if (!PACKET_forward(pkt, p - start)) + return 0; + + return p - start; +} + +int ossl_quic_wire_decode_frame_ping(PACKET *pkt) +{ + return expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_PING); +} + +int ossl_quic_wire_decode_frame_handshake_done(PACKET *pkt) +{ + return expect_frame_header(pkt, OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE); +} + +int ossl_quic_wire_peek_transport_param(PACKET *pkt, uint64_t *id) +{ + return PACKET_peek_quic_vlint(pkt, id); +} + +const unsigned char *ossl_quic_wire_decode_transport_param_bytes(PACKET *pkt, + uint64_t *id, + size_t *len) +{ + uint64_t len_; + const unsigned char *b = NULL; + + if (!PACKET_get_quic_vlint(pkt, id) + || !PACKET_get_quic_vlint(pkt, &len_)) + return NULL; + + if (len_ > SIZE_MAX + || !PACKET_get_bytes(pkt, (const unsigned char **)&b, (size_t)len_)) + return NULL; + + *len = (size_t)len_; + return b; +} + +int ossl_quic_wire_decode_transport_param_int(PACKET *pkt, + uint64_t *id, + uint64_t *value) +{ + PACKET sub; + + sub.curr = ossl_quic_wire_decode_transport_param_bytes(pkt, + id, &sub.remaining); + if (sub.curr == NULL) + return 0; + + if (!PACKET_get_quic_vlint(&sub, value)) + return 0; + + return 1; +} diff --git a/test/build.info b/test/build.info index 2047cf29b8127..500999ca3467a 100644 --- a/test/build.info +++ b/test/build.info @@ -278,6 +278,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[packettest]=../include ../apps/include DEPEND[packettest]=../libcrypto libtestutil.a + SOURCE[quic_wire_test]=quic_wire_test.c + INCLUDE[quic_wire_test]=../include ../apps/include + DEPEND[quic_wire_test]=../libcrypto.a ../libssl.a libtestutil.a + SOURCE[asynctest]=asynctest.c INCLUDE[asynctest]=../include ../apps/include DEPEND[asynctest]=../libcrypto @@ -953,7 +957,7 @@ ENDIF DEPEND[build_wincrypt_test]=../libssl ../libcrypto IF[{- !$disabled{'quic'} -}] - PROGRAMS{noinst}=quicapitest + PROGRAMS{noinst}=quicapitest quic_wire_test ENDIF SOURCE[quicapitest]=quicapitest.c helpers/ssltestlib.c diff --git a/test/quic_wire_test.c b/test/quic_wire_test.c new file mode 100644 index 0000000000000..55d18aa27cc7b --- /dev/null +++ b/test/quic_wire_test.c @@ -0,0 +1,1364 @@ +/* + * Copyright 2015-2018 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/packet.h" +#include "internal/quic_wire.h" +#include "testutil.h" + +struct encode_test_case { + int (*serializer)(WPACKET *pkt); + const unsigned char *expect_buf; + size_t expect_buf_len; + /* + * fail: -1 if not truncated (function should test for success), else number + * of bytes to which the input has been truncated (function should test that + * decoding fails) + */ + int (*deserializer)(PACKET *pkt, ossl_ssize_t fail); +}; + +/* 1. PADDING */ +static int encode_case_1_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_padding(pkt, 3), 1)) + return 0; + + return 1; +} + +static int encode_case_1_dec(PACKET *pkt, ossl_ssize_t fail) +{ + if (fail >= 0) + /* No failure modes for padding */ + return 1; + + if (!TEST_int_eq(ossl_quic_wire_decode_padding(pkt), 3)) + return 0; + + return 1; +} + +static const unsigned char encode_case_1_expect[] = { + 0, 0, 0 +}; + +/* 2. PING */ +static int encode_case_2_enc(WPACKET *pkt) +{ + + if (!TEST_int_eq(ossl_quic_wire_encode_frame_ping(pkt), 1)) + return 0; + + return 1; +} + +static int encode_case_2_dec(PACKET *pkt, ossl_ssize_t fail) +{ + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_ping(pkt), fail < 0)) + return 0; + + return 1; +} + +static const unsigned char encode_case_2_expect[] = { + 0x01 +}; + +/* 3. ACK */ +static const OSSL_QUIC_ACK_RANGE encode_case_3_ranges[] = { + { 20, 30 }, + { 0, 10 } +}; + +static const OSSL_QUIC_FRAME_ACK encode_case_3_f = { + (OSSL_QUIC_ACK_RANGE *)encode_case_3_ranges, + OSSL_NELEM(encode_case_3_ranges), + OSSL_TIME_MS, + 60, 70, 80, 1 +}; + +static int encode_case_3_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_ack(pkt, 3, &encode_case_3_f), 1)) + return 0; + + return 1; +} + +static int encode_case_3_dec(PACKET *pkt, ossl_ssize_t fail) +{ + OSSL_QUIC_ACK_RANGE ranges[4] = {0}; + OSSL_QUIC_FRAME_ACK f = {0}; + uint64_t total_ranges = 0, peek_total_ranges = 0; + int ret; + + f.ack_ranges = ranges; + f.num_ack_ranges = OSSL_NELEM(ranges); + + ret = ossl_quic_wire_peek_frame_ack_num_ranges(pkt, &peek_total_ranges); + if (fail < 0 && !TEST_int_eq(ret, 1)) + return 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_ack(pkt, 3, &f, &total_ranges), fail < 0)) + return 0; + + if (ret == 1 && !TEST_uint64_t_eq(peek_total_ranges, 2)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_uint64_t_eq(total_ranges, peek_total_ranges)) + return 0; + + if (!TEST_mem_eq(f.ack_ranges, f.num_ack_ranges * sizeof(OSSL_QUIC_ACK_RANGE), + encode_case_3_f.ack_ranges, + encode_case_3_f.num_ack_ranges * sizeof(OSSL_QUIC_ACK_RANGE))) + return 0; + + if (!TEST_uint64_t_eq(f.delay_time, encode_case_3_f.delay_time)) + return 0; + + if (!TEST_true(f.ecn_present)) + return 0; + + if (!TEST_uint64_t_eq(f.ect0, encode_case_3_f.ect0)) + return 0; + + if (!TEST_uint64_t_eq(f.ect1, encode_case_3_f.ect1)) + return 0; + + if (!TEST_uint64_t_eq(f.ecnce, encode_case_3_f.ecnce)) + return 0; + + return 1; +} + +static const unsigned char encode_case_3_expect[] = { + 0x03, /* Type */ + 0x1E, /* Largest Acknowledged */ + 0x40, 0x7d, /* ACK Delay */ + 1, /* ACK Range Count */ + 10, /* First ACK Range */ + + 8, /* Gap */ + 10, /* Length */ + + 0x3c, /* ECT0 Count */ + 0x40, 0x46, /* ECT1 Count */ + 0x40, 0x50, /* ECNCE Count */ +}; + +/* 4. RESET_STREAM */ +static const OSSL_QUIC_FRAME_RESET_STREAM encode_case_4_f = { + 0x1234, 0x9781, 0x11717 +}; + +static int encode_case_4_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_reset_stream(pkt, + &encode_case_4_f), 1)) + return 0; + + return 1; +} + +static int encode_case_4_dec(PACKET *pkt, ossl_ssize_t fail) +{ + OSSL_QUIC_FRAME_RESET_STREAM f = {0}; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_reset_stream(pkt, &f), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_mem_eq(&f, sizeof(f), &encode_case_4_f, sizeof(encode_case_4_f))) + return 0; + + return 1; +} + +static const unsigned char encode_case_4_expect[] = { + 0x04, /* Type */ + 0x52, 0x34, /* Stream ID */ + 0x80, 0x00, 0x97, 0x81, /* App Error Code */ + 0x80, 0x01, 0x17, 0x17, /* Final Size */ +}; + +/* 5. STOP_SENDING */ +static const OSSL_QUIC_FRAME_STOP_SENDING encode_case_5_f = { + 0x1234, 0x9781 +}; + +static int encode_case_5_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_stop_sending(pkt, + &encode_case_5_f), 1)) + return 0; + + return 1; +} + +static int encode_case_5_dec(PACKET *pkt, ossl_ssize_t fail) +{ + OSSL_QUIC_FRAME_STOP_SENDING f = {0}; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_stop_sending(pkt, &f), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_mem_eq(&f, sizeof(f), &encode_case_5_f, sizeof(encode_case_5_f))) + return 0; + + return 1; +} + +static const unsigned char encode_case_5_expect[] = { + 0x05, /* Type */ + 0x52, 0x34, /* Stream ID */ + 0x80, 0x00, 0x97, 0x81 /* App Error Code */ +}; + +/* 6. CRYPTO */ +static const unsigned char encode_case_6_data[] = { + 93, 18, 17, 102, 33 +}; + +static const OSSL_QUIC_FRAME_CRYPTO encode_case_6_f = { + 0x1234, sizeof(encode_case_6_data), encode_case_6_data +}; + +static int encode_case_6_enc(WPACKET *pkt) +{ + if (!TEST_ptr(ossl_quic_wire_encode_frame_crypto(pkt, + &encode_case_6_f))) + return 0; + + return 1; +} + +static int encode_case_6_dec(PACKET *pkt, ossl_ssize_t fail) +{ + OSSL_QUIC_FRAME_CRYPTO f = {0}; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_crypto(pkt, &f), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_uint64_t_eq(f.offset, 0x1234)) + return 0; + + if (!TEST_mem_eq(f.data, f.len, encode_case_6_data, sizeof(encode_case_6_data))) + return 0; + + return 1; +} + +static const unsigned char encode_case_6_expect[] = { + 0x06, /* Type */ + 0x52, 0x34, /* Offset */ + 0x05, /* Length */ + 93, 18, 17, 102, 33 /* Data */ +}; + +/* 7. NEW_TOKEN */ +static const unsigned char encode_case_7_token[] = { + 0xde, 0x06, 0xcb, 0x76, 0x5d, 0xb1, 0xa7, 0x71, + 0x78, 0x09, 0xbb, 0xe8, 0x50, 0x19, 0x12, 0x9a +}; + +static int encode_case_7_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_new_token(pkt, + encode_case_7_token, + sizeof(encode_case_7_token)), 1)) + return 0; + + return 1; +} + +static int encode_case_7_dec(PACKET *pkt, ossl_ssize_t fail) +{ + const unsigned char *token = NULL; + size_t token_len = 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_new_token(pkt, + &token, + &token_len), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_mem_eq(token, token_len, + encode_case_7_token, sizeof(encode_case_7_token))) + return 0; + + return 1; +} + +static const unsigned char encode_case_7_expect[] = { + 0x07, /* Type */ + 0x10, /* Length */ + 0xde, 0x06, 0xcb, 0x76, 0x5d, 0xb1, 0xa7, 0x71, /* Token */ + 0x78, 0x09, 0xbb, 0xe8, 0x50, 0x19, 0x12, 0x9a +}; + +/* 8. STREAM (no length, no offset, no fin) */ +static const unsigned char encode_case_8_data[] = { + 0xde, 0x06, 0xcb, 0x76, 0x5d +}; +static const OSSL_QUIC_FRAME_STREAM encode_case_8_f = { + 0x1234, 0, 5, encode_case_8_data, 0, 0 +}; + +static int encode_case_8_enc(WPACKET *pkt) +{ + if (!TEST_ptr(ossl_quic_wire_encode_frame_stream(pkt, + &encode_case_8_f))) + return 0; + + return 1; +} + +static int encode_case_8_dec(PACKET *pkt, ossl_ssize_t fail) +{ + OSSL_QUIC_FRAME_STREAM f = {0}; + + if (fail >= 3) + /* + * This case uses implicit length signalling so truncation will not + * cause it to fail unless the header (which is 3 bytes) is truncated. + */ + return 1; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_stream(pkt, &f), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_mem_eq(f.data, f.len, + encode_case_8_data, sizeof(encode_case_8_data))) + return 0; + + if (!TEST_uint64_t_eq(f.stream_id, 0x1234)) + return 0; + + if (!TEST_uint64_t_eq(f.offset, 0)) + return 0; + + if (!TEST_int_eq(f.has_explicit_len, 0)) + return 0; + + if (!TEST_int_eq(f.is_fin, 0)) + return 0; + + return 1; +} + +static const unsigned char encode_case_8_expect[] = { + 0x08, /* Type (OFF=0, LEN=0, FIN=0) */ + 0x52, 0x34, /* Stream ID */ + 0xde, 0x06, 0xcb, 0x76, 0x5d /* Data */ +}; + +/* 9. STREAM (length, offset, fin) */ +static const unsigned char encode_case_9_data[] = { + 0xde, 0x06, 0xcb, 0x76, 0x5d +}; +static const OSSL_QUIC_FRAME_STREAM encode_case_9_f = { + 0x1234, 0x39, 5, encode_case_9_data, 1, 1 +}; + +static int encode_case_9_enc(WPACKET *pkt) +{ + if (!TEST_ptr(ossl_quic_wire_encode_frame_stream(pkt, + &encode_case_9_f))) + return 0; + + return 1; +} + +static int encode_case_9_dec(PACKET *pkt, ossl_ssize_t fail) +{ + OSSL_QUIC_FRAME_STREAM f = {0}; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_stream(pkt, &f), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_mem_eq(f.data, f.len, + encode_case_9_data, sizeof(encode_case_9_data))) + return 0; + + if (!TEST_uint64_t_eq(f.stream_id, 0x1234)) + return 0; + + if (!TEST_uint64_t_eq(f.offset, 0x39)) + return 0; + + if (!TEST_int_eq(f.has_explicit_len, 1)) + return 0; + + if (!TEST_int_eq(f.is_fin, 1)) + return 0; + + return 1; +} + +static const unsigned char encode_case_9_expect[] = { + 0x0f, /* Type (OFF=1, LEN=1, FIN=1) */ + 0x52, 0x34, /* Stream ID */ + 0x39, /* Offset */ + 0x05, /* Length */ + 0xde, 0x06, 0xcb, 0x76, 0x5d /* Data */ +}; + +/* 10. MAX_DATA */ +static int encode_case_10_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_max_data(pkt, 0x1234), 1)) + return 0; + + return 1; +} + +static int encode_case_10_dec(PACKET *pkt, ossl_ssize_t fail) +{ + uint64_t max_data = 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_max_data(pkt, &max_data), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_uint64_t_eq(max_data, 0x1234)) + return 0; + + return 1; +} + +static const unsigned char encode_case_10_expect[] = { + 0x10, /* Type */ + 0x52, 0x34, /* Max Data */ +}; + +/* 11. MAX_STREAM_DATA */ +static int encode_case_11_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_max_stream_data(pkt, + 0x1234, + 0x9781), 1)) + return 0; + + return 1; +} + +static int encode_case_11_dec(PACKET *pkt, ossl_ssize_t fail) +{ + uint64_t stream_id = 0, max_data = 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_max_stream_data(pkt, + &stream_id, + &max_data), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_uint64_t_eq(stream_id, 0x1234)) + return 0; + + if (!TEST_uint64_t_eq(max_data, 0x9781)) + return 0; + + return 1; +} + +static const unsigned char encode_case_11_expect[] = { + 0x11, /* Type */ + 0x52, 0x34, /* Stream ID */ + 0x80, 0x00, 0x97, 0x81, /* Max Data */ +}; + +/* 12. MAX_STREAMS */ +static int encode_case_12_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_max_streams(pkt, 0, 0x1234), 1)) + return 0; + + if (!TEST_int_eq(ossl_quic_wire_encode_frame_max_streams(pkt, 1, 0x9781), 1)) + return 0; + + return 1; +} + +static int encode_case_12_dec(PACKET *pkt, ossl_ssize_t fail) +{ + uint64_t max_streams_1 = 0, max_streams_2 = 0, + frame_type_1 = 0, frame_type_2 = 0; + + if (!TEST_int_eq(ossl_quic_wire_peek_frame_header(pkt, &frame_type_1), + fail < 0 || fail >= 1)) + return 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_max_streams(pkt, + &max_streams_1), + fail < 0 || fail >= 3)) + return 0; + + if (!TEST_int_eq(ossl_quic_wire_peek_frame_header(pkt, &frame_type_2), + fail < 0 || fail >= 4)) + return 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_max_streams(pkt, + &max_streams_2), + fail < 0)) + return 0; + + if ((fail < 0 || fail >= 3) + && !TEST_uint64_t_eq(frame_type_1, OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI)) + return 0; + + if ((fail < 0 || fail >= 3) + && !TEST_uint64_t_eq(max_streams_1, 0x1234)) + return 0; + + if ((fail < 0 || fail >= 8) + && !TEST_uint64_t_eq(frame_type_2, OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI)) + return 0; + + if ((fail < 0 || fail >= 8) + && !TEST_uint64_t_eq(max_streams_2, 0x9781)) + return 0; + + return 1; +} + +static const unsigned char encode_case_12_expect[] = { + 0x12, /* Type (MAX_STREAMS Bidirectional) */ + 0x52, 0x34, /* Max Streams */ + 0x13, /* Type (MAX_STREAMS Unidirectional) */ + 0x80, 0x00, 0x97, 0x81, /* Max Streams */ +}; + +/* 13. DATA_BLOCKED */ +static int encode_case_13_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_data_blocked(pkt, 0x1234), 1)) + return 0; + + return 1; +} + +static int encode_case_13_dec(PACKET *pkt, ossl_ssize_t fail) +{ + uint64_t max_data = 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_data_blocked(pkt, + &max_data), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_uint64_t_eq(max_data, 0x1234)) + return 0; + + return 1; +} + +static const unsigned char encode_case_13_expect[] = { + 0x14, /* Type */ + 0x52, 0x34, /* Max Data */ +}; + +/* 14. STREAM_DATA_BLOCKED */ +static int encode_case_14_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_stream_data_blocked(pkt, + 0x1234, + 0x9781), 1)) + return 0; + + return 1; +} + +static int encode_case_14_dec(PACKET *pkt, ossl_ssize_t fail) +{ + uint64_t stream_id = 0, max_data = 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_stream_data_blocked(pkt, + &stream_id, + &max_data), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_uint64_t_eq(stream_id, 0x1234)) + return 0; + + if (!TEST_uint64_t_eq(max_data, 0x9781)) + return 0; + + return 1; +} + +static const unsigned char encode_case_14_expect[] = { + 0x15, /* Type */ + 0x52, 0x34, /* Stream ID */ + 0x80, 0x00, 0x97, 0x81, /* Max Data */ +}; + +/* 15. STREAMS_BLOCKED */ +static int encode_case_15_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_streams_blocked(pkt, 0, 0x1234), 1)) + return 0; + + if (!TEST_int_eq(ossl_quic_wire_encode_frame_streams_blocked(pkt, 1, 0x9781), 1)) + return 0; + + return 1; +} + +static int encode_case_15_dec(PACKET *pkt, ossl_ssize_t fail) +{ + uint64_t max_streams_1 = 0, max_streams_2 = 0, + frame_type_1 = 0, frame_type_2 = 0; + + if (!TEST_int_eq(ossl_quic_wire_peek_frame_header(pkt, &frame_type_1), + fail < 0 || fail >= 1)) + return 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_streams_blocked(pkt, + &max_streams_1), + fail < 0 || fail >= 3)) + return 0; + + if (!TEST_int_eq(ossl_quic_wire_peek_frame_header(pkt, &frame_type_2), + fail < 0 || fail >= 4)) + return 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_streams_blocked(pkt, + &max_streams_2), + fail < 0 || fail >= 8)) + return 0; + + if ((fail < 0 || fail >= 1) + && !TEST_uint64_t_eq(frame_type_1, OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI)) + return 0; + + if ((fail < 0 || fail >= 3) + && !TEST_uint64_t_eq(max_streams_1, 0x1234)) + return 0; + + if ((fail < 0 || fail >= 4) + && !TEST_uint64_t_eq(frame_type_2, OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_UNI)) + return 0; + + if ((fail < 0 || fail >= 8) + && !TEST_uint64_t_eq(max_streams_2, 0x9781)) + return 0; + + return 1; +} + +static const unsigned char encode_case_15_expect[] = { + 0x16, /* Type (STREAMS_BLOCKED Bidirectional) */ + 0x52, 0x34, /* Max Streams */ + 0x17, /* Type (STREAMS_BLOCKED Unidirectional) */ + 0x80, 0x00, 0x97, 0x81, /* Max Streams */ +}; + +/* 16. NEW_CONNECTION_ID */ +static const unsigned char encode_case_16_conn_id[] = { + 0x33, 0x44, 0x55, 0x66 +}; + +static const OSSL_QUIC_FRAME_NEW_CONN_ID encode_case_16_f = { + 0x1234, + 0x9781, + { + 0x4, + {0x33, 0x44, 0x55, 0x66} + }, + { + 0xde, 0x06, 0xcb, 0x76, 0x5d, 0xb1, 0xa7, 0x71, + 0x78, 0x09, 0xbb, 0xe8, 0x50, 0x19, 0x12, 0x9a + } +}; + +static int encode_case_16_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_new_conn_id(pkt, + &encode_case_16_f), 1)) + return 0; + + return 1; +} + +static int encode_case_16_dec(PACKET *pkt, ossl_ssize_t fail) +{ + OSSL_QUIC_FRAME_NEW_CONN_ID f = {0}; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_new_conn_id(pkt, &f), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_uint64_t_eq(f.seq_num, 0x1234)) + return 0; + + if (!TEST_uint64_t_eq(f.retire_prior_to, 0x9781)) + return 0; + + if (!TEST_uint64_t_eq(f.conn_id.id_len, sizeof(encode_case_16_conn_id))) + return 0; + + if (!TEST_mem_eq(f.conn_id.id, f.conn_id.id_len, + encode_case_16_conn_id, sizeof(encode_case_16_conn_id))) + return 0; + + if (!TEST_mem_eq(f.stateless_reset_token, + sizeof(f.stateless_reset_token), + encode_case_16_f.stateless_reset_token, + sizeof(encode_case_16_f.stateless_reset_token))) + return 0; + + return 1; +} + +static const unsigned char encode_case_16_expect[] = { + 0x18, /* Type */ + 0x52, 0x34, /* Sequence Number */ + 0x80, 0x00, 0x97, 0x81, /* Retire Prior To */ + 0x04, /* Connection ID Length */ + 0x33, 0x44, 0x55, 0x66, /* Connection ID */ + 0xde, 0x06, 0xcb, 0x76, 0x5d, 0xb1, 0xa7, 0x71, /* Stateless Reset Token */ + 0x78, 0x09, 0xbb, 0xe8, 0x50, 0x19, 0x12, 0x9a +}; + +/* 17. RETIRE_CONNECTION_ID */ +static int encode_case_17_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_retire_conn_id(pkt, 0x1234), 1)) + return 0; + + return 1; +} + +static int encode_case_17_dec(PACKET *pkt, ossl_ssize_t fail) +{ + uint64_t seq_num = 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_retire_conn_id(pkt, &seq_num), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_uint64_t_eq(seq_num, 0x1234)) + return 0; + + return 1; +} + +static const unsigned char encode_case_17_expect[] = { + 0x19, /* Type */ + 0x52, 0x34, /* Seq Num */ +}; + +/* 18. PATH_CHALLENGE */ +static const uint64_t encode_case_18_data + = (((uint64_t)0x5f4b12)<<40) | (uint64_t)0x731834UL; + +static int encode_case_18_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_path_challenge(pkt, + encode_case_18_data), 1)) + return 0; + + return 1; +} + +static int encode_case_18_dec(PACKET *pkt, ossl_ssize_t fail) +{ + uint64_t challenge = 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_path_challenge(pkt, &challenge), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_uint64_t_eq(challenge, encode_case_18_data)) + return 0; + + return 1; +} + +static const unsigned char encode_case_18_expect[] = { + 0x1A, /* Type */ + 0x5f, 0x4b, 0x12, 0x00, 0x00, 0x73, 0x18, 0x34, /* Data */ +}; + +/* 19. PATH_RESPONSE */ +static const uint64_t encode_case_19_data + = (((uint64_t)0x5f4b12)<<40) | (uint64_t)0x731834UL; + +static int encode_case_19_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_path_response(pkt, + encode_case_19_data), 1)) + return 0; + + return 1; +} + +static int encode_case_19_dec(PACKET *pkt, ossl_ssize_t fail) +{ + uint64_t challenge = 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_path_response(pkt, &challenge), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_uint64_t_eq(challenge, encode_case_19_data)) + return 0; + + return 1; +} + +static const unsigned char encode_case_19_expect[] = { + 0x1B, /* Type */ + 0x5f, 0x4b, 0x12, 0x00, 0x00, 0x73, 0x18, 0x34, /* Data */ +}; + +/* 20. CONNECTION_CLOSE (transport) */ +static const char encode_case_20_reason[] = { + /* "reason for closure" */ + 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x63, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65 +}; + +static const OSSL_QUIC_FRAME_CONN_CLOSE encode_case_20_f = { + 0, + 0x1234, + 0x9781, + encode_case_20_reason, + sizeof(encode_case_20_reason) +}; + +static int encode_case_20_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_frame_conn_close(pkt, + &encode_case_20_f), 1)) + return 0; + + return 1; +} + +static int encode_case_20_dec(PACKET *pkt, ossl_ssize_t fail) +{ + OSSL_QUIC_FRAME_CONN_CLOSE f = {0}; + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_conn_close(pkt, &f), fail < 0)) + return 0; + + if (fail >= 0) + return 1; + + if (!TEST_int_eq(f.is_app, 0)) + return 0; + + if (!TEST_uint64_t_eq(f.error_code, 0x1234)) + return 0; + + if (!TEST_uint64_t_eq(f.frame_type, 0x9781)) + return 0; + + if (!TEST_size_t_eq(f.reason_len, 18)) + return 0; + + if (!TEST_mem_eq(f.reason, f.reason_len, + encode_case_20_f.reason, encode_case_20_f.reason_len)) + return 0; + + return 1; +} + +static const unsigned char encode_case_20_expect[] = { + 0x1C, /* Type */ + 0x52, 0x34, /* Sequence Number */ + 0x80, 0x00, 0x97, 0x81, /* Frame Type */ + 0x12, /* Reason Length */ + 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x20, 0x66, 0x6f, /* Reason */ + 0x72, 0x20, 0x63, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65 +}; + +/* 21. HANDSHAKE_DONE */ +static int encode_case_21_enc(WPACKET *pkt) +{ + + if (!TEST_int_eq(ossl_quic_wire_encode_frame_handshake_done(pkt), 1)) + return 0; + + return 1; +} + +static int encode_case_21_dec(PACKET *pkt, ossl_ssize_t fail) +{ + + if (!TEST_int_eq(ossl_quic_wire_decode_frame_handshake_done(pkt), fail < 0)) + return 0; + + return 1; +} + +static const unsigned char encode_case_21_expect[] = { + 0x1E +}; + +/* 22. Buffer Transport Parameter */ +static const unsigned char encode_case_22_data[] = {0x55,0x77,0x32,0x46,0x99}; + +static int encode_case_22_enc(WPACKET *pkt) +{ + unsigned char *p; + + if (!TEST_ptr(ossl_quic_wire_encode_transport_param_bytes(pkt, 0x1234, + encode_case_22_data, + sizeof(encode_case_22_data)))) + return 0; + + if (!TEST_ptr(p = ossl_quic_wire_encode_transport_param_bytes(pkt, 0x9781, + NULL, 2))) + return 0; + + p[0] = 0x33; + p[1] = 0x44; + + return 1; +} + +static int encode_case_22_dec(PACKET *pkt, ossl_ssize_t fail) +{ + uint64_t id = 0; + size_t len = 0; + const unsigned char *p; + static const unsigned char data[] = {0x33, 0x44}; + + if (!TEST_int_eq(ossl_quic_wire_peek_transport_param(pkt, &id), + fail < 0 || fail >= 2)) + return 0; + + if ((fail < 0 || fail >= 2) + && !TEST_uint64_t_eq(id, 0x1234)) + return 0; + + id = 0; + + p = ossl_quic_wire_decode_transport_param_bytes(pkt, &id, &len); + if (fail < 0 || fail >= 8) { + if (!TEST_ptr(p)) + return 0; + } else { + if (!TEST_ptr_null(p)) + return 0; + } + + if ((fail < 0 || fail >= 8) + && !TEST_uint64_t_eq(id, 0x1234)) + return 0; + + if ((fail < 0 || fail >= 8) + && !TEST_mem_eq(p, len, encode_case_22_data, sizeof(encode_case_22_data))) + return 0; + + if ((fail < 0 || fail >= 8) + && !TEST_int_eq(ossl_quic_wire_peek_transport_param(pkt, &id), + fail < 0 || fail >= 12)) + return 0; + + if ((fail < 0 || fail >= 12) + && !TEST_uint64_t_eq(id, 0x9781)) + return 0; + + id = 0; + + p = ossl_quic_wire_decode_transport_param_bytes(pkt, &id, &len); + if (fail < 0 || fail >= 15) { + if (!TEST_ptr(p)) + return 0; + } else { + if (!TEST_ptr_null(p)) + return 0; + } + + if ((fail < 0 || fail >= 15) + && !TEST_uint64_t_eq(id, 0x9781)) + return 0; + + if ((fail < 0 || fail >= 15) + && !TEST_mem_eq(p, len, data, sizeof(data))) + return 0; + + return 1; +} + +static const unsigned char encode_case_22_expect[] = { + 0x52, 0x34, /* ID */ + 0x05, /* Length */ + 0x55, 0x77, 0x32, 0x46, 0x99, /* Data */ + + 0x80, 0x00, 0x97, 0x81, /* ID */ + 0x02, /* Length */ + 0x33, 0x44 /* Data */ +}; + +/* 23. Integer Transport Parameter */ +static int encode_case_23_enc(WPACKET *pkt) +{ + if (!TEST_int_eq(ossl_quic_wire_encode_transport_param_int(pkt, 0x1234, 0x9781), 1)) + return 0; + + if (!TEST_int_eq(ossl_quic_wire_encode_transport_param_int(pkt, 0x2233, 0x4545), 1)) + return 0; + + return 1; +} + +static int encode_case_23_dec(PACKET *pkt, ossl_ssize_t fail) +{ + uint64_t id = 0, value = 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_transport_param_int(pkt, + &id, &value), + fail < 0 || fail >= 7)) + return 0; + + if ((fail < 0 || fail >= 7) + && !TEST_uint64_t_eq(id, 0x1234)) + return 0; + + if ((fail < 0 || fail >= 7) + && !TEST_uint64_t_eq(value, 0x9781)) + return 0; + + if (!TEST_int_eq(ossl_quic_wire_decode_transport_param_int(pkt, + &id, &value), + fail < 0 || fail >= 14)) + return 0; + + if ((fail < 0 || fail >= 14) + && !TEST_uint64_t_eq(id, 0x2233)) + return 0; + + if ((fail < 0 || fail >= 14) + && !TEST_uint64_t_eq(value, 0x4545)) + return 0; + + return 1; +} + +static const unsigned char encode_case_23_expect[] = { + 0x52, 0x34, + 0x04, + 0x80, 0x00, 0x97, 0x81, + + 0x62, 0x33, + 0x04, + 0x80, 0x00, 0x45, 0x45, +}; + +#define ENCODE_CASE(n) \ + { \ + encode_case_##n##_enc, \ + encode_case_##n##_expect, \ + OSSL_NELEM(encode_case_##n##_expect), \ + encode_case_##n##_dec \ + }, + +static const struct encode_test_case encode_cases[] = { + ENCODE_CASE(1) + ENCODE_CASE(2) + ENCODE_CASE(3) + ENCODE_CASE(4) + ENCODE_CASE(5) + ENCODE_CASE(6) + ENCODE_CASE(7) + ENCODE_CASE(8) + ENCODE_CASE(9) + ENCODE_CASE(10) + ENCODE_CASE(11) + ENCODE_CASE(12) + ENCODE_CASE(13) + ENCODE_CASE(14) + ENCODE_CASE(15) + ENCODE_CASE(16) + ENCODE_CASE(17) + ENCODE_CASE(18) + ENCODE_CASE(19) + ENCODE_CASE(20) + ENCODE_CASE(21) + ENCODE_CASE(22) + ENCODE_CASE(23) +}; + +static int test_wire_encode(int idx) +{ + int testresult = 0; + WPACKET wpkt; + PACKET pkt; + BUF_MEM *buf = NULL; + size_t written; + const struct encode_test_case *c = &encode_cases[idx]; + int have_wpkt = 0; + size_t i; + + if (!TEST_ptr(buf = BUF_MEM_new())) + goto err; + + if (!TEST_int_eq(WPACKET_init(&wpkt, buf), 1)) + goto err; + + have_wpkt = 1; + if (!TEST_int_eq(c->serializer(&wpkt), 1)) + goto err; + + if (!TEST_int_eq(WPACKET_get_total_written(&wpkt, &written), 1)) + goto err; + + if (!TEST_mem_eq(buf->data, written, c->expect_buf, c->expect_buf_len)) + goto err; + + if (!TEST_int_eq(PACKET_buf_init(&pkt, (unsigned char *)buf->data, written), 1)) + goto err; + + if (!TEST_int_eq(c->deserializer(&pkt, -1), 1)) + goto err; + + if (!TEST_false(PACKET_remaining(&pkt))) + goto err; + + for (i = 0; i < c->expect_buf_len; ++i) { + PACKET pkt2; + + /* + * Check parsing truncated (i.e., malformed) input is handled correctly. + * Generate all possible truncations of our reference encoding and + * verify that they are handled correctly. The number of bytes of the + * truncated encoding is passed as an argument to the deserializer to + * help it determine whether decoding should fail or not. + */ + if (!TEST_int_eq(PACKET_buf_init(&pkt2, (unsigned char *)c->expect_buf, i), 1)) + goto err; + + if (!TEST_int_eq(c->deserializer(&pkt2, i), 1)) + goto err; + } + + testresult = 1; +err: + if (have_wpkt) + WPACKET_finish(&wpkt); + BUF_MEM_free(buf); + return testresult; +} + +struct ack_test_case { + const unsigned char *input_buf; + size_t input_buf_len; + int (*deserializer)(PACKET *pkt); + int expect_fail; +}; + +/* ACK Frame with Excessive First ACK Range Field */ +static const unsigned char ack_case_1_input[] = { + 0x02, /* ACK Without ECN */ + 0x08, /* Largest Acknowledged */ + 0x01, /* ACK Delay */ + 0x00, /* ACK Range Count */ + 0x09, /* First ACK Range */ +}; + +/* ACK Frame with Valid ACK Range Field */ +static const unsigned char ack_case_2_input[] = { + 0x02, /* ACK Without ECN */ + 0x08, /* Largest Acknowledged */ + 0x01, /* ACK Delay */ + 0x00, /* ACK Range Count */ + 0x08, /* First ACK Range */ +}; + +/* ACK Frame with Excessive ACK Range Gap */ +static const unsigned char ack_case_3_input[] = { + 0x02, /* ACK Without ECN */ + 0x08, /* Largest Acknowledged */ + 0x01, /* ACK Delay */ + 0x01, /* ACK Range Count */ + 0x01, /* First ACK Range */ + + 0x05, /* Gap */ + 0x01, /* ACK Range Length */ +}; + +/* ACK Frame with Valid ACK Range */ +static const unsigned char ack_case_4_input[] = { + 0x02, /* ACK Without ECN */ + 0x08, /* Largest Acknowledged */ + 0x01, /* ACK Delay */ + 0x01, /* ACK Range Count */ + 0x01, /* First ACK Range */ + + 0x04, /* Gap */ + 0x01, /* ACK Range Length */ +}; + +/* ACK Frame with Excessive ACK Range Length */ +static const unsigned char ack_case_5_input[] = { + 0x02, /* ACK Without ECN */ + 0x08, /* Largest Acknowledged */ + 0x01, /* ACK Delay */ + 0x01, /* ACK Range Count */ + 0x01, /* First ACK Range */ + + 0x04, /* Gap */ + 0x02, /* ACK Range Length */ +}; + +/* ACK Frame with Multiple ACK Ranges, Final Having Excessive Length */ +static const unsigned char ack_case_6_input[] = { + 0x02, /* ACK Without ECN */ + 0x08, /* Largest Acknowledged */ + 0x01, /* ACK Delay */ + 0x02, /* ACK Range Count */ + 0x01, /* First ACK Range */ + + 0x01, /* Gap */ + 0x02, /* ACK Range Length */ + + 0x00, /* Gap */ + 0x01, /* ACK Range Length */ +}; + +/* ACK Frame with Multiple ACK Ranges, Valid */ +static const unsigned char ack_case_7_input[] = { + 0x02, /* ACK Without ECN */ + 0x08, /* Largest Acknowledged */ + 0x01, /* ACK Delay */ + 0x02, /* ACK Range Count */ + 0x01, /* First ACK Range */ + + 0x01, /* Gap */ + 0x02, /* ACK Range Length */ + + 0x00, /* Gap */ + 0x00, /* ACK Range Length */ +}; + +static int ack_generic_decode(PACKET *pkt) +{ + OSSL_QUIC_ACK_RANGE ranges[8] = {0}; + OSSL_QUIC_FRAME_ACK f = {0}; + uint64_t total_ranges = 0, peek_total_ranges = 0; + int r; + size_t i; + + f.ack_ranges = ranges; + f.num_ack_ranges = OSSL_NELEM(ranges); + + if (!TEST_int_eq(ossl_quic_wire_peek_frame_ack_num_ranges(pkt, + &peek_total_ranges), 1)) + return 0; + + r = ossl_quic_wire_decode_frame_ack(pkt, 3, &f, &total_ranges); + if (r == 0) + return 0; + + if (!TEST_uint64_t_eq(total_ranges, peek_total_ranges)) + return 0; + + for (i = 0; i < f.num_ack_ranges; ++i) { + if (!TEST_uint64_t_le(f.ack_ranges[i].start, f.ack_ranges[i].end)) + return 0; + if (!TEST_uint64_t_lt(f.ack_ranges[i].end, 1000)) + return 0; + } + + return 1; +} + +#define ACK_CASE(n, expect_fail, dec) \ + { \ + ack_case_##n##_input, \ + sizeof(ack_case_##n##_input), \ + (dec), \ + (expect_fail) \ + }, + +static const struct ack_test_case ack_cases[] = { + ACK_CASE(1, 1, ack_generic_decode) + ACK_CASE(2, 0, ack_generic_decode) + ACK_CASE(3, 1, ack_generic_decode) + ACK_CASE(4, 0, ack_generic_decode) + ACK_CASE(5, 1, ack_generic_decode) + ACK_CASE(6, 1, ack_generic_decode) + ACK_CASE(7, 0, ack_generic_decode) +}; + +static int test_wire_ack(int idx) +{ + int testresult = 0, r; + PACKET pkt; + const struct ack_test_case *c = &ack_cases[idx]; + + if (!TEST_int_eq(PACKET_buf_init(&pkt, + (unsigned char *)c->input_buf, + c->input_buf_len), 1)) + goto err; + + r = c->deserializer(&pkt); + if (c->expect_fail) { + if (!TEST_int_eq(r, 0)) + goto err; + } else { + if (!TEST_int_eq(r, 1)) + goto err; + + if (!TEST_false(PACKET_remaining(&pkt))) + goto err; + } + + testresult = 1; +err: + return testresult; +} + +int setup_tests(void) +{ + ADD_ALL_TESTS(test_wire_encode, OSSL_NELEM(encode_cases)); + ADD_ALL_TESTS(test_wire_ack, OSSL_NELEM(ack_cases)); + return 1; +} diff --git a/test/recipes/70-test_quic_wire.t b/test/recipes/70-test_quic_wire.t new file mode 100644 index 0000000000000..0ca9e949472b4 --- /dev/null +++ b/test/recipes/70-test_quic_wire.t @@ -0,0 +1,19 @@ +#! /usr/bin/env perl +# 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 + +use OpenSSL::Test; +use OpenSSL::Test::Utils; + +setup("test_quic_wire"); + +plan skip_all => "QUIC protocol is not supported by this OpenSSL build" + if disabled('quic'); + +plan tests => 1; + +ok(run(test(["quic_wire_test"])));