]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
QUIC Frame Encoding and Decoding Functions
authorHugo Landau <hlandau@openssl.org>
Mon, 11 Jul 2022 18:16:20 +0000 (19:16 +0100)
committerPauli <pauli@openssl.org>
Fri, 29 Jul 2022 06:28:37 +0000 (16:28 +1000)
This adds functions for encoding and decoding QUIC frames.

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18795)

crypto/packet.c
include/internal/packet.h
include/internal/quic_types.h [new file with mode: 0644]
include/internal/quic_wire.h [new file with mode: 0644]
include/internal/time.h
ssl/quic/build.info
ssl/quic/quic_wire.c [new file with mode: 0644]
test/build.info
test/quic_wire_test.c [new file with mode: 0644]
test/recipes/70-test_quic_wire.t [new file with mode: 0644]

index 5123426d7555e3ccf7d63b0de3c6f0af91131ac8..753bbdc59c7b627903b4559f3700d8e2f3f593bc 100644 (file)
@@ -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;
index ab211f0dc28d99b8e8301d424dfcb0f8f771ba85..476a1b72755c9ede07ddc4be3b0ec18546d69446 100644 (file)
@@ -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 (file)
index 0000000..6787de9
--- /dev/null
@@ -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 <openssl/ssl.h>
+
+/* 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 (file)
index 0000000..1ead6fc
--- /dev/null
@@ -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
index 4765274f17fc5fe66dd88243eb90d95d76109a52..b4a67fb998e5c8f606cb41abf84722f5ee44f17b 100644 (file)
 # 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
index f9578454e8a22c97144a9029f51bf71f6ff4db97..3e76055c6276ddc2af9bc4aada93984d9403581d 100644 (file)
@@ -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 (file)
index 0000000..0adb41e
--- /dev/null
@@ -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 <openssl/macros.h>
+#include <openssl/objects.h>
+#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;
+}
index 2047cf29b81277b83242787fc95090116ffa9c66..500999ca3467ac77712c315d9f324021909a8bf7 100644 (file)
@@ -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 (file)
index 0000000..55d18aa
--- /dev/null
@@ -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 (file)
index 0000000..0ca9e94
--- /dev/null
@@ -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"])));