QUIC wire format support

Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18382)
This commit is contained in:
Hugo Landau 2022-05-23 10:42:03 +01:00 committed by Tomas Mraz
parent 1aef2c10f1
commit 416d0a638c
11 changed files with 693 additions and 27 deletions

View 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 \

View 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;
}

77
crypto/quic_vlint.c Normal file
View File

@ -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;
}

View 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 */

View File

@ -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

View 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

View 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

View 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;
}

View 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)

View 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 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")
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_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")
DEFINE_COMPARISON(void *, ptr, ne, !=, "%p")
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)
{

View 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;
}