mirror of
https://github.com/openssl/openssl.git
synced 2025-01-18 13:44:20 +08:00
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:
parent
1aef2c10f1
commit
416d0a638c
@ -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 \
|
||||
|
@ -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
77
crypto/quic_vlint.c
Normal 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;
|
||||
}
|
@ -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 */
|
||||
|
123
include/internal/quic_vlint.h
Normal file
123
include/internal/quic_vlint.h
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user