QUIC wire format support
authorHugo Landau <hlandau@openssl.org>
Mon, 23 May 2022 09:42:03 +0000 (10:42 +0100)
committerTomas Mraz <tomas@openssl.org>
Fri, 27 May 2022 06:00:52 +0000 (08:00 +0200)
Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18382)

crypto/build.info
crypto/packet.c
crypto/quic_vlint.c [new file with mode: 0644]
include/internal/packet.h
include/internal/quic_vlint.h [new file with mode: 0644]
ssl/build.info
test/build.info
test/packettest.c
test/testutil.h
test/testutil/tests.c
test/wpackettest.c

index f5682340816d82633b6e3b77b573f3abef7ac20f..27ef6ba92edd6738858d0d7cd538bcae0e62f932 100644 (file)
@@ -97,7 +97,8 @@ $UTIL_COMMON=\
         cryptlib.c params.c params_from_text.c bsearch.c ex_data.c o_str.c \
         threads_pthread.c threads_win.c threads_none.c initthread.c \
         context.c sparse_array.c asn1_dsa.c packet.c param_build.c \
-        param_build_set.c der_writer.c threads_lib.c params_dup.c
+        param_build_set.c der_writer.c threads_lib.c params_dup.c \
+        quic_vlint.c
 
 SOURCE[../libcrypto]=$UTIL_COMMON \
         mem.c mem_sec.c \
index 09f6a9cea92c4a0df326af1a069408cf9f10a8b9..5123426d7555e3ccf7d63b0de3c6f0af91131ac8 100644 (file)
@@ -225,6 +225,18 @@ static int put_value(unsigned char *data, size_t value, size_t len)
     return 1;
 }
 
+static int put_quic_value(unsigned char *data, size_t value, size_t len)
+{
+    if (data == NULL)
+        return 1;
+
+    /* Value too large for field. */
+    if (ossl_quic_vlint_encode_len(value) > len)
+        return 0;
+
+    ossl_quic_vlint_encode_n(data, value, len);
+    return 1;
+}
 
 /*
  * Internal helper function used by WPACKET_close(), WPACKET_finish() and
@@ -261,10 +273,15 @@ static int wpacket_intern_close(WPACKET *pkt, WPACKET_SUB *sub, int doclose)
     if (sub->lenbytes > 0) {
         unsigned char *buf = GETBUF(pkt);
 
-        if (buf != NULL
-                && !put_value(&buf[sub->packet_len], packlen,
-                              sub->lenbytes))
-            return 0;
+        if (buf != NULL) {
+            if ((sub->flags & WPACKET_FLAGS_QUIC_VLINT) == 0) {
+                if (!put_value(&buf[sub->packet_len], packlen, sub->lenbytes))
+                    return 0;
+            } else {
+                if (!put_quic_value(&buf[sub->packet_len], packlen, sub->lenbytes))
+                    return 0;
+            }
+        }
     } else if (pkt->endfirst && sub->parent != NULL
                && (packlen != 0
                    || (sub->flags
@@ -510,3 +527,54 @@ void WPACKET_cleanup(WPACKET *pkt)
     }
     pkt->subs = NULL;
 }
+
+int WPACKET_start_quic_sub_packet_bound(WPACKET *pkt, size_t max_len)
+{
+    size_t enclen = ossl_quic_vlint_encode_len(max_len);
+
+    if (enclen == 0)
+        return 0;
+
+    if (WPACKET_start_sub_packet_len__(pkt, enclen) == 0)
+        return 0;
+
+    pkt->subs->flags |= WPACKET_FLAGS_QUIC_VLINT;
+    return 1;
+}
+
+int WPACKET_start_quic_sub_packet(WPACKET *pkt)
+{
+    /*
+     * Assume no (sub)packet will exceed 4GiB, thus the 8-byte encoding need not
+     * be used.
+     */
+    return WPACKET_start_quic_sub_packet_bound(pkt, OSSL_QUIC_VLINT_4B_MIN);
+}
+
+int WPACKET_quic_sub_allocate_bytes(WPACKET *pkt, size_t len, unsigned char **allocbytes)
+{
+    if (!WPACKET_start_quic_sub_packet_bound(pkt, len)
+            || !WPACKET_allocate_bytes(pkt, len, allocbytes)
+            || !WPACKET_close(pkt))
+        return 0;
+
+    return 1;
+}
+
+/*
+ * Write a QUIC variable-length integer to the packet.
+ */
+int WPACKET_quic_write_vlint(WPACKET *pkt, uint64_t v)
+{
+    unsigned char *b = NULL;
+    size_t enclen = ossl_quic_vlint_encode_len(v);
+
+    if (enclen == 0)
+        return 0;
+
+    if (WPACKET_allocate_bytes(pkt, enclen, &b) == 0)
+        return 0;
+
+    ossl_quic_vlint_encode(b, v);
+    return 1;
+}
diff --git a/crypto/quic_vlint.c b/crypto/quic_vlint.c
new file mode 100644 (file)
index 0000000..92f14c6
--- /dev/null
@@ -0,0 +1,77 @@
+#include "internal/quic_vlint.h"
+#include "internal/e_os.h"
+
+void ossl_quic_vlint_encode_n(uint8_t *buf, uint64_t v, int n)
+{
+    if (n == 1) {
+        buf[0] = (uint8_t)v;
+    } else if (n == 2) {
+        buf[0] = (uint8_t)(0x40 | ((v >> 8) & 0x3F));
+        buf[1] = (uint8_t)v;
+    } else if (n == 4) {
+        buf[0] = (uint8_t)(0x80 | ((v >> 24) & 0x3F));
+        buf[1] = (uint8_t)(v >> 16);
+        buf[2] = (uint8_t)(v >>  8);
+        buf[3] = (uint8_t)v;
+    } else {
+        buf[0] = (uint8_t)(0xC0 | ((v >> 56) & 0x3F));
+        buf[1] = (uint8_t)(v >> 48);
+        buf[2] = (uint8_t)(v >> 40);
+        buf[3] = (uint8_t)(v >> 32);
+        buf[4] = (uint8_t)(v >> 24);
+        buf[5] = (uint8_t)(v >> 16);
+        buf[6] = (uint8_t)(v >>  8);
+        buf[7] = (uint8_t)v;
+    }
+}
+
+void ossl_quic_vlint_encode(uint8_t *buf, uint64_t v)
+{
+    ossl_quic_vlint_encode_n(buf, v, ossl_quic_vlint_encode_len(v));
+}
+
+uint64_t ossl_quic_vlint_decode_unchecked(const unsigned char *buf)
+{
+    uint8_t first_byte = buf[0];
+    size_t sz = ossl_quic_vlint_decode_len(first_byte);
+
+    if (sz == 1)
+        return first_byte & 0x3F;
+
+    if (sz == 2)
+        return ((uint64_t)(first_byte & 0x3F) << 8)
+             | buf[1];
+
+    if (sz == 4)
+        return ((uint64_t)(first_byte & 0x3F) << 24)
+             | ((uint64_t)buf[1] << 16)
+             | ((uint64_t)buf[2] <<  8)
+             |  buf[3];
+
+    return ((uint64_t)(first_byte & 0x3F) << 56)
+         | ((uint64_t)buf[1] << 48)
+         | ((uint64_t)buf[2] << 40)
+         | ((uint64_t)buf[3] << 32)
+         | ((uint64_t)buf[4] << 24)
+         | ((uint64_t)buf[5] << 16)
+         | ((uint64_t)buf[6] <<  8)
+         |  buf[7];
+}
+
+int ossl_quic_vlint_decode(const unsigned char *buf, size_t buf_len, uint64_t *v)
+{
+    size_t dec_len;
+    uint64_t x;
+
+    if (buf_len < 1)
+        return 0;
+
+    dec_len = ossl_quic_vlint_decode_len(buf[0]);
+    if (buf_len < dec_len)
+        return 0;
+
+    x = ossl_quic_vlint_decode_unchecked(buf);
+
+    *v = x;
+    return dec_len;
+}
index 170997db60852cef2ea9cee97d6183b45ba620c5..ab211f0dc28d99b8e8301d424dfcb0f8f771ba85 100644 (file)
@@ -18,6 +18,7 @@
 # include <openssl/e_os2.h>
 
 # include "internal/numbers.h"
+# include "internal/quic_vlint.h"
 
 typedef struct {
     /* Pointer to where we are currently reading from */
@@ -228,6 +229,28 @@ __owur static ossl_inline int PACKET_peek_net_4(const PACKET *pkt,
     return 1;
 }
 
+/*
+ * Decodes a QUIC variable-length integer in |pkt| and stores the result in
+ * |data|.
+ */
+__owur static ossl_inline int PACKET_get_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);
+    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)
@@ -594,6 +617,33 @@ __owur static ossl_inline int PACKET_get_length_prefixed_3(PACKET *pkt,
     return 1;
 }
 
+/*
+ * Reads a variable-length vector prefixed with a QUIC variable-length integer
+ * denoting the length, and stores the contents in |subpkt|. |pkt| can equal
+ * |subpkt|. Data is not copied: the |subpkt| packet will share its underlying
+ * buffer with the original |pkt|, so data wrapped by |pkt| must outlive the
+ * |subpkt|. Upon failure, the original |pkt| and |subpkt| are not modified.
+ */
+__owur static ossl_inline int PACKET_get_quic_length_prefixed(PACKET *pkt,
+                                                              PACKET *subpkt)
+{
+    uint64_t length;
+    const unsigned char *data;
+    PACKET tmp = *pkt;
+
+    if (!PACKET_get_quic_vlint(&tmp, &length) ||
+        length > SIZE_MAX ||
+        !PACKET_get_bytes(&tmp, &data, (size_t)length)) {
+        return 0;
+    }
+
+    *pkt = tmp;
+    subpkt->curr = data;
+    subpkt->remaining = (size_t)length;
+
+    return 1;
+}
+
 /* Writeable packets */
 
 typedef struct wpacket_sub WPACKET_SUB;
@@ -658,6 +708,8 @@ struct wpacket_st {
  */
 #define WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH    2
 
+/* QUIC variable-length integer length prefix */
+#define WPACKET_FLAGS_QUIC_VLINT                4
 
 /*
  * Initialise a WPACKET with the buffer in |buf|. The buffer must exist
@@ -899,4 +951,33 @@ int WPACKET_is_null_buf(WPACKET *pkt);
 /* Release resources in a WPACKET if a failure has occurred. */
 void WPACKET_cleanup(WPACKET *pkt);
 
+/*
+ * Starts a QUIC sub-packet headed by a QUIC variable-length integer. A 4-byte
+ * representation is used.
+ */
+__owur int WPACKET_start_quic_sub_packet(WPACKET *pkt);
+
+/*
+ * Starts a QUIC sub-packet headed by a QUIC variable-length integer. max_len
+ * specifies the upper bound for the sub-packet size at the time the sub-packet
+ * is closed, which determines the encoding size for tthe variable-length
+ * integer header. max_len can be a precise figure or a worst-case bound
+ * if a precise figure is not available.
+ */
+__owur int WPACKET_start_quic_sub_packet_bound(WPACKET *pkt, size_t max_len);
+
+/*
+ * Allocates a QUIC sub-packet with exactly len bytes of payload, headed by a
+ * QUIC variable-length integer. The pointer to the payload buffer is output and
+ * must be filled by the caller. This function assures optimal selection of
+ * variable-length integer encoding length.
+ */
+__owur int WPACKET_quic_sub_allocate_bytes(WPACKET *pkt, size_t len,
+                                           unsigned char **bytes);
+
+/*
+ * Write a QUIC variable-length integer to the packet.
+ */
+__owur int WPACKET_quic_write_vlint(WPACKET *pkt, uint64_t v);
+
 #endif                          /* OSSL_INTERNAL_PACKET_H */
diff --git a/include/internal/quic_vlint.h b/include/internal/quic_vlint.h
new file mode 100644 (file)
index 0000000..b21dd3f
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+* 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_VLINT_H
+# define OSSL_INTERNAL_QUIC_VLINT_H
+# pragma once
+
+#include "internal/e_os.h"
+
+/* The smallest value requiring a 1, 2, 4, or 8-byte representation. */
+#define OSSL_QUIC_VLINT_1B_MIN 0
+#define OSSL_QUIC_VLINT_2B_MIN 64
+#define OSSL_QUIC_VLINT_4B_MIN 16384
+#define OSSL_QUIC_VLINT_8B_MIN 1073741824
+
+/* The largest value representable in a given number of bytes. */
+#define OSSL_QUIC_VLINT_1B_MAX (OSSL_QUIC_VLINT_2B_MIN - 1)
+#define OSSL_QUIC_VLINT_2B_MAX (OSSL_QUIC_VLINT_4B_MIN - 1)
+#define OSSL_QUIC_VLINT_4B_MAX (OSSL_QUIC_VLINT_8B_MIN - 1)
+#define OSSL_QUIC_VLINT_8B_MAX (((uint64_t)1 << 62) - 1)
+
+/* The largest value representable as a variable-length integer. */
+#define OSSL_QUIC_VLINT_MAX    OSSL_QUIC_VLINT_8B_MAX
+
+/*
+ * Returns the number of bytes needed to encode v in the QUIC variable-length
+ * integer encoding.
+ *
+ * Returns 0 if v exceeds OSSL_QUIC_VLINT_MAX.
+ */
+static ossl_unused ossl_inline size_t ossl_quic_vlint_encode_len(uint64_t v)
+{
+    if (v < OSSL_QUIC_VLINT_2B_MIN)
+        return 1;
+
+    if (v < OSSL_QUIC_VLINT_4B_MIN)
+        return 2;
+
+    if (v < OSSL_QUIC_VLINT_8B_MIN)
+        return 4;
+
+    if (v <= OSSL_QUIC_VLINT_MAX)
+        return 8;
+
+    return 0;
+}
+
+/*
+ * This function writes a QUIC varable-length encoded integer to buf.
+ * The smallest usable representation is used.
+ *
+ * It is the caller's responsibility to ensure that the buffer is big enough by
+ * calling ossl_quic_vlint_encode_len(v) before calling this function.
+ *
+ * Precondition: buf is at least ossl_quic_vlint_enc_len(v) bytes in size
+ *   (unchecked)
+ * Precondition: v does not exceed OSSL_QUIC_VLINT_MAX
+ *   (unchecked)
+ */
+void ossl_quic_vlint_encode(unsigned char *buf, uint64_t v);
+
+/*
+ * This function writes a QUIC variable-length encoded integer to buf. The
+ * specified number of bytes n are used for the encoding, which means that the
+ * encoded value may take up more space than necessary.
+ *
+ * It is the caller's responsibility to ensure that the buffer is of at least n
+ * bytes, and that v is representable by a n-byte QUIC variable-length integer.
+ * The representable ranges are:
+ *
+ *   1-byte encoding: [0, 2** 6-1]
+ *   2-byte encoding: [0, 2**14-1]
+ *   4-byte encoding: [0, 2**30-1]
+ *   8-byte encoding: [0, 2**62-1]
+ *
+ * Precondition: buf is at least n bytes in size (unchecked)
+ * Precondition: v does not exceed the representable range
+ *   (ossl_quic_vlint_encode_len(v) <= n) (unchecked)
+ * Precondition: v does not exceed OSSL_QUIC_VLINT_MAX
+ *   (unchecked)
+ */
+void ossl_quic_vlint_encode_n(unsigned char *buf, uint64_t v, int n);
+
+/*
+ * Given the first byte of an encoded QUIC variable-length integer, returns
+ * the number of bytes comprising the encoded integer, including the first
+ * byte.
+ */
+static ossl_unused ossl_inline size_t ossl_quic_vlint_decode_len(uint8_t first_byte)
+{
+    return 1U << ((first_byte & 0xC0) >> 6);
+}
+
+/*
+ * Given a buffer containing an encoded QUIC variable-length integer, returns
+ * the decoded value. The buffer must be of at least
+ * ossl_quic_vlint_decode_len(buf[0]) bytes in size, and the caller is responsible
+ * for checking this.
+ *
+ * Precondition: buf is at least ossl_quic_vlint_decode_len(buf[0]) bytes in size
+ *   (unchecked)
+ */
+uint64_t ossl_quic_vlint_decode_unchecked(const unsigned char *buf);
+
+/*
+ * Given a buffer buf of buf_len bytes in length, attempts to decode an encoded
+ * QUIC variable-length integer at the start of the buffer and writes the result
+ * to *v. If buf_len is inadequate, suggesting a truncated encoded integer, the
+ * function fails and 0 is returned. Otherwise, returns the number of bytes
+ * consumed.
+ *
+ * Precondition: buf is at least buf_len bytes in size
+ * Precondition: v (unchecked)
+ */
+int ossl_quic_vlint_decode(const unsigned char *buf, size_t buf_len, uint64_t *v);
+
+#endif
index 0851357f81eba572e6849e788a111d9710a0ca0c..fd13ede5e86d9aad91be5d3797acd0ebea161e37 100644 (file)
@@ -32,7 +32,7 @@ SOURCE[../libssl]=\
         tls_depr.c $KTLSSRC
 # For shared builds we need to include the libcrypto packet.c and sources
 # needed in providers (s3_cbc.c and record/tls_pad.c) in libssl as well.
-SHARED_SOURCE[../libssl]=record/tls_pad.c ../crypto/packet.c
+SHARED_SOURCE[../libssl]=record/tls_pad.c ../crypto/packet.c ../crypto/quic_vlint.c
 IF[{- !$disabled{'deprecated-3.0'} -}]
   SHARED_SOURCE[../libssl]=s3_cbc.c
   SOURCE[../libssl]=ssl_rsa_legacy.c
index 4552c7686e018057b4122425f494be2023530dab..a6dffe280e6af90665ddc8dd29a665445116ad68 100644 (file)
@@ -270,7 +270,7 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[bad_dtls_test]=../include ../apps/include
   DEPEND[bad_dtls_test]=../libcrypto ../libssl libtestutil.a
 
-  SOURCE[packettest]=packettest.c
+  SOURCE[packettest]=packettest.c ../crypto/quic_vlint.c
   INCLUDE[packettest]=../include ../apps/include
   DEPEND[packettest]=../libcrypto libtestutil.a
 
@@ -816,7 +816,7 @@ IF[{- !$disabled{tests} -}]
     PROGRAMS{noinst}=tls13secretstest
     SOURCE[tls13secretstest]=tls13secretstest.c
     DEFINE[tls13secretstest]=OPENSSL_NO_KTLS
-    SOURCE[tls13secretstest]= ../ssl/tls13_enc.c ../crypto/packet.c
+    SOURCE[tls13secretstest]= ../ssl/tls13_enc.c ../crypto/packet.c ../crypto/quic_vlint.c
     INCLUDE[tls13secretstest]=.. ../include ../apps/include
     DEPEND[tls13secretstest]=../libcrypto ../libssl libtestutil.a
   ENDIF
index e8aec474463fe284ce28c3b2379d568a1f498218..c62247f9be7b4c360b9bfc421e3d0673f8206886 100644 (file)
@@ -465,6 +465,110 @@ static int test_PACKET_as_length_prefixed_2(void)
     return 1;
 }
 
+static int test_PACKET_get_quic_vlint(void)
+{
+    struct quic_test_case {
+        unsigned char buf[16];
+        size_t expected_read_count;
+        uint64_t value;
+    };
+
+    static const struct quic_test_case cases[] = {
+        { {0x00}, 1, 0  },
+        { {0x01}, 1, 1  },
+        { {0x3e}, 1, 62 },
+        { {0x3f}, 1, 63 },
+        { {0x40,0x00}, 2, 0 },
+        { {0x40,0x01}, 2, 1 },
+        { {0x40,0x02}, 2, 2 },
+        { {0x40,0xff}, 2, 255 },
+        { {0x41,0x00}, 2, 256 },
+        { {0x7f,0xfe}, 2, 16382 },
+        { {0x7f,0xff}, 2, 16383 },
+        { {0x80,0x00,0x00,0x00}, 4, 0 },
+        { {0x80,0x00,0x00,0x01}, 4, 1 },
+        { {0x80,0x00,0x01,0x02}, 4, 258 },
+        { {0x80,0x18,0x49,0x65}, 4, 1591653 },
+        { {0xbe,0x18,0x49,0x65}, 4, 1041779045 },
+        { {0xbf,0xff,0xff,0xff}, 4, 1073741823 },
+        { {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, 8, 0 },
+        { {0xc0,0x00,0x00,0x00,0x00,0x00,0x01,0x02}, 8, 258 },
+        { {0xfd,0x1f,0x59,0x8d,0xc9,0xf8,0x71,0x8a}, 8, 4404337426105397642 },
+    };
+
+    PACKET pkt;
+    size_t i;
+    uint64_t v;
+
+    for (i = 0; i < OSSL_NELEM(cases); ++i) {
+        memset(&pkt, 0, sizeof(pkt));
+        v = 55;
+
+        if (!TEST_true(PACKET_buf_init(&pkt, cases[i].buf, sizeof(cases[i].buf)))
+                || !TEST_true(PACKET_get_quic_vlint(&pkt, &v))
+                || !TEST_uint64_t_eq(v, cases[i].value)
+                || !TEST_size_t_eq(PACKET_remaining(&pkt),
+                                   sizeof(cases[i].buf) - cases[i].expected_read_count)
+           )
+            return 0;
+    }
+
+    return 1;
+}
+
+static int test_PACKET_get_quic_length_prefixed(void)
+{
+    struct quic_test_case {
+        unsigned char buf[16];
+        size_t enclen, len;
+        int fail;
+    };
+
+    static const struct quic_test_case cases[] = {
+        /* success cases */
+        { {0x00}, 1, 0, 0 },
+        { {0x01}, 1, 1, 0 },
+        { {0x02}, 1, 2, 0 },
+        { {0x03}, 1, 3, 0 },
+        { {0x04}, 1, 4, 0 },
+        { {0x05}, 1, 5, 0 },
+
+        /* failure cases */
+        { {0x10}, 1, 0, 1 },
+        { {0x3f}, 1, 0, 1 },
+    };
+
+    size_t i;
+    PACKET pkt, subpkt = {0};
+
+    for (i = 0; i < OSSL_NELEM(cases); ++i) {
+        memset(&pkt, 0, sizeof(pkt));
+
+        if (!TEST_true(PACKET_buf_init(&pkt, cases[i].buf,
+                                       cases[i].fail
+                                         ? sizeof(cases[i].buf)
+                                         : cases[i].enclen + cases[i].len)))
+            return 0;
+
+        if (!TEST_int_eq(PACKET_get_quic_length_prefixed(&pkt, &subpkt), !cases[i].fail))
+            return 0;
+
+        if (cases[i].fail) {
+            if (!TEST_ptr_eq(pkt.curr, cases[i].buf))
+                return 0;
+            continue;
+        }
+
+        if (!TEST_ptr_eq(subpkt.curr, cases[i].buf + cases[i].enclen))
+            return 0;
+
+        if (!TEST_size_t_eq(subpkt.remaining, cases[i].len))
+            return 0;
+    }
+
+    return 1;
+}
+
 int setup_tests(void)
 {
     unsigned int i;
@@ -495,5 +599,7 @@ int setup_tests(void)
     ADD_TEST(test_PACKET_get_length_prefixed_3);
     ADD_TEST(test_PACKET_as_length_prefixed_1);
     ADD_TEST(test_PACKET_as_length_prefixed_2);
+    ADD_TEST(test_PACKET_get_quic_vlint);
+    ADD_TEST(test_PACKET_get_quic_length_prefixed);
     return 1;
 }
index 52fb17c3c679a64ccc7561ecd0513054e355b87d..397ab62f155b500ad312bf0e6f319a5cc89c264a 100644 (file)
@@ -282,6 +282,8 @@ DECLARE_COMPARISONS(char, char)
 DECLARE_COMPARISONS(unsigned char, uchar)
 DECLARE_COMPARISONS(long, long)
 DECLARE_COMPARISONS(unsigned long, ulong)
+DECLARE_COMPARISONS(int64_t, int64_t)
+DECLARE_COMPARISONS(uint64_t, uint64_t)
 DECLARE_COMPARISONS(double, double)
 DECLARE_COMPARISONS(time_t, time_t)
 
@@ -431,6 +433,13 @@ void test_perror(const char *s);
 # define TEST_ulong_gt(a, b)  test_ulong_gt(__FILE__, __LINE__, #a, #b, a, b)
 # define TEST_ulong_ge(a, b)  test_ulong_ge(__FILE__, __LINE__, #a, #b, a, b)
 
+# define TEST_uint64_t_eq(a, b)  test_uint64_t_eq(__FILE__, __LINE__, #a, #b, a, b)
+# define TEST_uint64_t_ne(a, b)  test_uint64_t_ne(__FILE__, __LINE__, #a, #b, a, b)
+# define TEST_uint64_t_lt(a, b)  test_uint64_t_lt(__FILE__, __LINE__, #a, #b, a, b)
+# define TEST_uint64_t_le(a, b)  test_uint64_t_le(__FILE__, __LINE__, #a, #b, a, b)
+# define TEST_uint64_t_gt(a, b)  test_uint64_t_gt(__FILE__, __LINE__, #a, #b, a, b)
+# define TEST_uint64_t_ge(a, b)  test_uint64_t_ge(__FILE__, __LINE__, #a, #b, a, b)
+
 # define TEST_size_t_eq(a, b) test_size_t_eq(__FILE__, __LINE__, #a, #b, a, b)
 # define TEST_size_t_ne(a, b) test_size_t_ne(__FILE__, __LINE__, #a, #b, a, b)
 # define TEST_size_t_lt(a, b) test_size_t_lt(__FILE__, __LINE__, #a, #b, a, b)
index b431657e056d4564264e7d899787691e34d70ca0..fbc2a0958ee8f866ed01c8576fa91462f07b8e8c 100644 (file)
@@ -208,7 +208,7 @@ void test_openssl_errors(void)
  * The desc argument is a printf format string followed by its arguments and
  * this is included in the output if the condition being tested for is false.
  */
-#define DEFINE_COMPARISON(type, name, opname, op, fmt)                  \
+#define DEFINE_COMPARISON(type, name, opname, op, fmt, cast)            \
     int test_ ## name ## _ ## opname(const char *file, int line,        \
                                      const char *s1, const char *s2,    \
                                      const type t1, const type t2)      \
@@ -217,29 +217,31 @@ void test_openssl_errors(void)
             return 1;                                                   \
         test_fail_message(NULL, file, line, #type, s1, s2, #op,         \
                           "[" fmt "] compared to [" fmt "]",            \
-                          t1, t2);                                      \
+                          (cast)t1, (cast)t2);                          \
         return 0;                                                       \
     }
 
-#define DEFINE_COMPARISONS(type, name, fmt)                             \
-    DEFINE_COMPARISON(type, name, eq, ==, fmt)                          \
-    DEFINE_COMPARISON(type, name, ne, !=, fmt)                          \
-    DEFINE_COMPARISON(type, name, lt, <, fmt)                           \
-    DEFINE_COMPARISON(type, name, le, <=, fmt)                          \
-    DEFINE_COMPARISON(type, name, gt, >, fmt)                           \
-    DEFINE_COMPARISON(type, name, ge, >=, fmt)
-
-DEFINE_COMPARISONS(int, int, "%d")
-DEFINE_COMPARISONS(unsigned int, uint, "%u")
-DEFINE_COMPARISONS(char, char, "%c")
-DEFINE_COMPARISONS(unsigned char, uchar, "%u")
-DEFINE_COMPARISONS(long, long, "%ld")
-DEFINE_COMPARISONS(unsigned long, ulong, "%lu")
-DEFINE_COMPARISONS(size_t, size_t, "%zu")
-DEFINE_COMPARISONS(double, double, "%g")
-
-DEFINE_COMPARISON(void *, ptr, eq, ==, "%p")
-DEFINE_COMPARISON(void *, ptr, ne, !=, "%p")
+#define DEFINE_COMPARISONS(type, name, fmt, cast)                       \
+    DEFINE_COMPARISON(type, name, eq, ==, fmt, cast)                    \
+    DEFINE_COMPARISON(type, name, ne, !=, fmt, cast)                    \
+    DEFINE_COMPARISON(type, name, lt, <, fmt, cast)                     \
+    DEFINE_COMPARISON(type, name, le, <=, fmt, cast)                    \
+    DEFINE_COMPARISON(type, name, gt, >, fmt, cast)                     \
+    DEFINE_COMPARISON(type, name, ge, >=, fmt, cast)
+
+DEFINE_COMPARISONS(int, int, "%d", int)
+DEFINE_COMPARISONS(unsigned int, uint, "%u", unsigned int)
+DEFINE_COMPARISONS(char, char, "%c", char)
+DEFINE_COMPARISONS(unsigned char, uchar, "%u", unsigned char)
+DEFINE_COMPARISONS(long, long, "%ld", long)
+DEFINE_COMPARISONS(unsigned long, ulong, "%lu", unsigned long)
+DEFINE_COMPARISONS(int64_t, int64_t, "%lld", long long)
+DEFINE_COMPARISONS(uint64_t, uint64_t, "%llu", unsigned long long)
+DEFINE_COMPARISONS(size_t, size_t, "%zu", size_t)
+DEFINE_COMPARISONS(double, double, "%g", double)
+
+DEFINE_COMPARISON(void *, ptr, eq, ==, "%p", void *)
+DEFINE_COMPARISON(void *, ptr, ne, !=, "%p", void *)
 
 int test_ptr_null(const char *file, int line, const char *s, const void *p)
 {
index b03dfcd2e04e42546b5ee62a1c722277c5f47b60..a90c9a155389290a002eee5be54775c9539c11ef 100644 (file)
@@ -26,6 +26,30 @@ static const unsigned char simpleder[] = {
     0xfc, 0x04, 0x00, 0x01, 0x02, 0x03, 0xff, 0xfe, 0xfd
 };
 
+/* QUIC sub-packet with 4-byte length prefix, containing a 1-byte vlint */
+static const unsigned char quic1[] = { 0x80, 0x00, 0x00, 0x01, 0x09 };
+/* QUIC sub-packet with 1-byte length prefix, containing a 1-byte vlint */
+static const unsigned char quic2[] = { 0x01, 0x09 };
+/* QUIC sub-packet with 2-byte length prefix, containing a 2-byte vlint */
+static const unsigned char quic3[] = { 0x40, 0x02, 0x40, 0x41 };
+/* QUIC sub-packet with 8-byte length prefix, containing a 4-byte vlint */
+static const unsigned char quic4[] = {
+    0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+    0x80, 0x01, 0x3c, 0x6a
+};
+/* QUIC sub-packet with 8-byte length prefix, containing a 8-byte vlint */
+static const unsigned char quic5[] = {
+    0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+    0xef, 0x77, 0x21, 0x3f, 0x3f, 0x50, 0x5b, 0xa5
+};
+/* QUIC sub-packet, length known up-front */
+static const unsigned char quic6[] = { 0x03, 0x55, 0x66, 0x77 };
+/* Nested and sequential sub-packets with length prefixes */
+static const unsigned char quic7[] = {
+    0x07, 0x80, 0x00, 0x00, 0x08, 0x65, 0x14, 0x40, 0x01, 0x05,
+    0x40, 0x01, 0x11, 0x40, 0x01, 0x12, 0x40, 0x01, 0x13
+};
+
 static BUF_MEM *buf;
 
 static int cleanup(WPACKET *pkt)
@@ -424,6 +448,179 @@ static int test_WPACKET_init_der(void)
     return 1;
 }
 
+static int test_WPACKET_quic(void)
+{
+    WPACKET pkt;
+    size_t written, len;
+    unsigned char *bytes;
+
+    /* QUIC sub-packet with 4-byte length prefix, containing a 1-byte vlint */
+    if (!TEST_true(WPACKET_init(&pkt, buf))
+            || !TEST_true(WPACKET_start_quic_sub_packet(&pkt))
+            || !TEST_true(WPACKET_quic_write_vlint(&pkt, 0x09))
+                /* Can't finish because we have a sub packet */
+            || !TEST_false(WPACKET_finish(&pkt))
+            || !TEST_true(WPACKET_close(&pkt))
+                /* Sub packet is closed so can't close again */
+            || !TEST_false(WPACKET_close(&pkt))
+                /* Now a top level so finish should succeed */
+            || !TEST_true(WPACKET_finish(&pkt))
+            || !TEST_true(WPACKET_get_total_written(&pkt, &written))
+            || !TEST_mem_eq(buf->data, written, quic1, sizeof(quic1)))
+        return cleanup(&pkt);
+
+    /* QUIC sub-packet with 1-byte length prefix, containing a 1-byte vlint */
+    if (!TEST_true(WPACKET_init(&pkt, buf))
+            || !TEST_true(WPACKET_start_quic_sub_packet_bound(&pkt, OSSL_QUIC_VLINT_1B_MAX))
+            || !TEST_true(WPACKET_quic_write_vlint(&pkt, 0x09))
+            || !TEST_false(WPACKET_finish(&pkt))
+            || !TEST_true(WPACKET_close(&pkt))
+            || !TEST_false(WPACKET_close(&pkt))
+            || !TEST_true(WPACKET_finish(&pkt))
+            || !TEST_true(WPACKET_get_total_written(&pkt, &written))
+            || !TEST_mem_eq(buf->data, written, quic2, sizeof(quic2)))
+        return cleanup(&pkt);
+
+    /* QUIC sub-packet with 2-byte length prefix, containing a 2-byte vlint */
+    if (!TEST_true(WPACKET_init(&pkt, buf))
+            || !TEST_true(WPACKET_start_quic_sub_packet_bound(&pkt, OSSL_QUIC_VLINT_2B_MIN))
+            || !TEST_true(WPACKET_quic_write_vlint(&pkt, 0x41))
+            || !TEST_false(WPACKET_finish(&pkt))
+            || !TEST_true(WPACKET_close(&pkt))
+            || !TEST_false(WPACKET_close(&pkt))
+            || !TEST_true(WPACKET_finish(&pkt))
+            || !TEST_true(WPACKET_get_total_written(&pkt, &written))
+            || !TEST_mem_eq(buf->data, written, quic3, sizeof(quic3)))
+        return cleanup(&pkt);
+
+    /* QUIC sub-packet with 8-byte length prefix, containing a 4-byte vlint */
+    if (!TEST_true(WPACKET_init(&pkt, buf))
+            || !TEST_true(WPACKET_start_quic_sub_packet_bound(&pkt, OSSL_QUIC_VLINT_8B_MIN))
+            || !TEST_true(WPACKET_quic_write_vlint(&pkt, 0x13c6a))
+            || !TEST_false(WPACKET_finish(&pkt))
+            || !TEST_true(WPACKET_close(&pkt))
+            || !TEST_false(WPACKET_close(&pkt))
+            || !TEST_true(WPACKET_finish(&pkt))
+            || !TEST_true(WPACKET_get_total_written(&pkt, &written))
+            || !TEST_mem_eq(buf->data, written, quic4, sizeof(quic4)))
+        return cleanup(&pkt);
+
+    /* QUIC sub-packet with 8-byte length prefix, containing a 8-byte vlint */
+    if (!TEST_true(WPACKET_init(&pkt, buf))
+            || !TEST_true(WPACKET_start_quic_sub_packet_bound(&pkt, OSSL_QUIC_VLINT_8B_MIN))
+            || !TEST_true(WPACKET_quic_write_vlint(&pkt, 0x2f77213f3f505ba5ULL))
+            || !TEST_false(WPACKET_finish(&pkt))
+            || !TEST_true(WPACKET_close(&pkt))
+            || !TEST_false(WPACKET_close(&pkt))
+            || !TEST_true(WPACKET_finish(&pkt))
+            || !TEST_true(WPACKET_get_total_written(&pkt, &written))
+            || !TEST_mem_eq(buf->data, written, quic5, sizeof(quic5)))
+        return cleanup(&pkt);
+
+    /* QUIC sub-packet, length known up-front */
+    if (!TEST_true(WPACKET_init(&pkt, buf))
+            || !TEST_true(WPACKET_quic_sub_allocate_bytes(&pkt, 3, &bytes)))
+        return cleanup(&pkt);
+
+    bytes[0] = 0x55;
+    bytes[1] = 0x66;
+    bytes[2] = 0x77;
+
+    if (!TEST_true(WPACKET_finish(&pkt))
+            || !TEST_true(WPACKET_get_total_written(&pkt, &written))
+            || !TEST_mem_eq(buf->data, written, quic6, sizeof(quic6)))
+        return cleanup(&pkt);
+
+    /* Nested and sequential sub-packets with length prefixes */
+    if (!TEST_true(WPACKET_init(&pkt, buf))
+            || !TEST_true(WPACKET_quic_write_vlint(&pkt, 0x07))
+            || !TEST_true(WPACKET_get_length(&pkt, &len))
+            || !TEST_size_t_eq(len, 1)
+            || !TEST_true(WPACKET_start_quic_sub_packet_bound(&pkt, OSSL_QUIC_VLINT_4B_MIN))
+            || !TEST_true(WPACKET_quic_write_vlint(&pkt, 0x2514))
+            || !TEST_true(WPACKET_get_length(&pkt, &len))
+            || !TEST_size_t_eq(len, 2)
+            || !TEST_true(WPACKET_start_quic_sub_packet_bound(&pkt, OSSL_QUIC_VLINT_2B_MIN))
+            || !TEST_true(WPACKET_quic_write_vlint(&pkt, 0x05))
+            || !TEST_true(WPACKET_get_length(&pkt, &len))
+            || !TEST_size_t_eq(len, 1)
+            || !TEST_true(WPACKET_close(&pkt))
+            || !TEST_true(WPACKET_start_quic_sub_packet_bound(&pkt, OSSL_QUIC_VLINT_2B_MIN))
+            || !TEST_true(WPACKET_quic_write_vlint(&pkt, 0x11))
+            || !TEST_true(WPACKET_close(&pkt))
+            || !TEST_true(WPACKET_get_length(&pkt, &len))
+            || !TEST_size_t_eq(len, 8)
+            || !TEST_true(WPACKET_close(&pkt))
+            || !TEST_true(WPACKET_start_quic_sub_packet_bound(&pkt, OSSL_QUIC_VLINT_2B_MIN))
+            || !TEST_true(WPACKET_quic_write_vlint(&pkt, 0x12))
+            || !TEST_true(WPACKET_close(&pkt))
+            || !TEST_true(WPACKET_start_quic_sub_packet_bound(&pkt, OSSL_QUIC_VLINT_2B_MIN))
+            || !TEST_true(WPACKET_quic_write_vlint(&pkt, 0x13))
+            || !TEST_true(WPACKET_close(&pkt))
+            || !TEST_true(WPACKET_finish(&pkt))
+            || !TEST_true(WPACKET_get_total_written(&pkt, &written))
+            || !TEST_mem_eq(buf->data, written, quic7, sizeof(quic7)))
+        return cleanup(&pkt);
+
+    /* Trying to encode a value above OSSL_QUIC_VLINT_MAX should fail */
+    if (!TEST_true(WPACKET_init(&pkt, buf))
+            || !TEST_false(WPACKET_quic_write_vlint(&pkt, OSSL_QUIC_VLINT_MAX+1))
+            || !TEST_true(WPACKET_quic_write_vlint(&pkt, OSSL_QUIC_VLINT_MAX)))
+            return cleanup(&pkt);
+
+    WPACKET_cleanup(&pkt);
+    return 1;
+}
+
+static int test_WPACKET_quic_vlint_random(void)
+{
+    size_t i, written;
+    uint64_t expected, actual = 0;
+    unsigned char rand_data[9];
+    WPACKET pkt;
+    PACKET read_pkt = {0};
+
+    for (i = 0; i < 10000; ++i) {
+        if (!TEST_true(RAND_bytes(rand_data, sizeof(rand_data))))
+            return cleanup(&pkt);
+
+        expected = *(uint64_t*)rand_data;
+
+        /*
+         * Ensure that all size classes get tested with equal probability.
+         */
+        switch (rand_data[8] & 3) {
+            case 0:
+                expected &= OSSL_QUIC_VLINT_1B_MAX;
+                break;
+            case 1:
+                expected &= OSSL_QUIC_VLINT_2B_MAX;
+                break;
+            case 2:
+                expected &= OSSL_QUIC_VLINT_4B_MAX;
+                break;
+            case 3:
+                expected &= OSSL_QUIC_VLINT_8B_MAX;
+                break;
+        }
+
+        if (!TEST_true(WPACKET_init(&pkt, buf))
+                || !TEST_true(WPACKET_quic_write_vlint(&pkt, expected))
+                || !TEST_true(WPACKET_get_total_written(&pkt, &written)))
+            return cleanup(&pkt);
+
+        if (!TEST_true(PACKET_buf_init(&read_pkt, (unsigned char *)buf->data, written))
+                || !TEST_true(PACKET_get_quic_vlint(&read_pkt, &actual))
+                || !TEST_uint64_t_eq(expected, actual))
+            return cleanup(&pkt);
+
+        WPACKET_cleanup(&pkt);
+    }
+
+    WPACKET_cleanup(&pkt);
+    return 1;
+}
+
 int setup_tests(void)
 {
     if (!TEST_ptr(buf = BUF_MEM_new()))
@@ -436,6 +633,8 @@ int setup_tests(void)
     ADD_TEST(test_WPACKET_allocate_bytes);
     ADD_TEST(test_WPACKET_memcpy);
     ADD_TEST(test_WPACKET_init_der);
+    ADD_TEST(test_WPACKET_quic);
+    ADD_TEST(test_WPACKET_quic_vlint_random);
     return 1;
 }