/*
* Decodes a QUIC variable-length integer in |pkt| and stores the result in
* |data|. Unlike PACKET_get_quic_vlint, this does not advance the current
- * position.
+ * position. If was_minimal is non-NULL, *was_minimal is set to 1 if the integer
+ * was encoded using the minimal possible number of bytes and 0 otherwise.
*/
-__owur static ossl_inline int PACKET_peek_quic_vlint(PACKET *pkt,
- uint64_t *data)
+__owur static ossl_inline int PACKET_peek_quic_vlint_ex(PACKET *pkt,
+ uint64_t *data,
+ int *was_minimal)
{
size_t enclen;
return 0;
*data = ossl_quic_vlint_decode_unchecked(pkt->curr);
+
+ if (was_minimal != NULL)
+ *was_minimal = (enclen == ossl_quic_vlint_encode_len(*data));
+
return 1;
}
+__owur static ossl_inline int PACKET_peek_quic_vlint(PACKET *pkt,
+ uint64_t *data)
+{
+ return PACKET_peek_quic_vlint_ex(pkt, data, NULL);
+}
+
/*
* Skips over a QUIC variable-length integer in |pkt| without decoding it.
*/
* position). This can be used to determine the frame type and determine which
* frame decoding function to call.
*/
-int ossl_quic_wire_peek_frame_header(PACKET *pkt, uint64_t *type);
+int ossl_quic_wire_peek_frame_header(PACKET *pkt, uint64_t *type,
+ int *was_minimal);
/*
* Like ossl_quic_wire_peek_frame_header, but advances the current position
}
while (PACKET_remaining(pkt) > 0) {
+ int was_minimal;
uint64_t frame_type;
const unsigned char *sof = NULL;
uint64_t datalen = 0;
if (ch->msg_callback != NULL)
sof = PACKET_data(pkt);
- if (!ossl_quic_wire_peek_frame_header(pkt, &frame_type))
+ if (!ossl_quic_wire_peek_frame_header(pkt, &frame_type, &was_minimal)) {
+ ossl_quic_channel_raise_protocol_error(ch,
+ QUIC_ERR_PROTOCOL_VIOLATION,
+ 0,
+ "malformed frame header");
+ return 0;
+ }
+
+ if (!was_minimal) {
+ ossl_quic_channel_raise_protocol_error(ch,
+ QUIC_ERR_PROTOCOL_VIOLATION,
+ frame_type,
+ "non-minimal frame type encoding");
return 0;
+ }
switch (frame_type) {
case OSSL_QUIC_FRAME_TYPE_PING:
/* Unknown frame type */
ackm_data->is_ack_eliciting = 1;
ossl_quic_channel_raise_protocol_error(ch,
- QUIC_ERR_PROTOCOL_VIOLATION,
+ QUIC_ERR_FRAME_ENCODING_ERROR,
frame_type,
"Unknown frame type received");
return 0;
{
uint64_t frame_type;
- if (!ossl_quic_wire_peek_frame_header(pkt, &frame_type))
+ if (!ossl_quic_wire_peek_frame_header(pkt, &frame_type, NULL))
return 0;
switch (frame_type) {
PACKET pkt;
if (!PACKET_buf_init(&pkt, h->txn.data, l)
- || !ossl_quic_wire_peek_frame_header(&pkt, &ftype)) {
+ || !ossl_quic_wire_peek_frame_header(&pkt, &ftype, NULL)) {
tx_helper_end(h, 0);
return 0;
}
* QUIC Wire Format Decoding
* =========================
*/
-int ossl_quic_wire_peek_frame_header(PACKET *pkt, uint64_t *type)
+int ossl_quic_wire_peek_frame_header(PACKET *pkt, uint64_t *type,
+ int *was_minimal)
{
- return PACKET_peek_quic_vlint(pkt, type);
+ return PACKET_peek_quic_vlint_ex(pkt, type, was_minimal);
}
int ossl_quic_wire_skip_frame_header(PACKET *pkt, uint64_t *type)
cause = ossl_quic_tserver_get_terminate_cause(qtserv);
if (!TEST_ptr(cause)
|| !TEST_true(cause->remote)
+ || !TEST_false(cause->app)
|| !TEST_uint64_t_eq(cause->error_code, code))
return 0;
return qtest_check_server_transport_err(qtserv, QUIC_ERR_PROTOCOL_VIOLATION);
}
+int qtest_check_server_frame_encoding_err(QUIC_TSERVER *qtserv)
+{
+ return qtest_check_server_transport_err(qtserv, QUIC_ERR_FRAME_ENCODING_ERROR);
+}
+
void qtest_fault_free(QTEST_FAULT *fault)
{
if (fault == NULL)
*/
int qtest_check_server_protocol_err(QUIC_TSERVER *qtserv);
+/*
+ * Confirm the server has received a frame encoding error. Equivalent to calling
+ * qtest_check_server_transport_err with a code of QUIC_ERR_FRAME_ENCODING_ERROR
+ */
+int qtest_check_server_frame_encoding_err(QUIC_TSERVER *qtserv);
+
/*
* Enable tests to listen for pre-encryption QUIC packets being sent
*/
{
uint64_t frame_type;
- if (!ossl_quic_wire_peek_frame_header(&h->pkt, &frame_type))
+ if (!ossl_quic_wire_peek_frame_header(&h->pkt, &frame_type, NULL))
return; /* EOF */
if (frame_type == OSSL_QUIC_FRAME_TYPE_PADDING)
break;
case OPK_NEXT_FRAME:
skip_padding(&h);
- if (!ossl_quic_wire_peek_frame_header(&h.pkt, &h.frame_type)) {
+ if (!ossl_quic_wire_peek_frame_header(&h.pkt, &h.frame_type, NULL)) {
h.frame_type = UINT64_MAX;
break;
}
{
uint64_t max_streams_1 = 0, max_streams_2 = 0,
frame_type_1 = 0, frame_type_2 = 0;
+ int is_minimal;
- if (!TEST_int_eq(ossl_quic_wire_peek_frame_header(pkt, &frame_type_1),
+ if (!TEST_int_eq(ossl_quic_wire_peek_frame_header(pkt, &frame_type_1,
+ &is_minimal),
fail < 0 || fail >= 1))
return 0;
+ if (!TEST_true(is_minimal))
+ return 0;
+
if (!TEST_int_eq(ossl_quic_wire_decode_frame_max_streams(pkt,
&max_streams_1),
fail < 0 || fail >= 3))
return 0;
- if (!TEST_int_eq(ossl_quic_wire_peek_frame_header(pkt, &frame_type_2),
+ if (!TEST_int_eq(ossl_quic_wire_peek_frame_header(pkt, &frame_type_2,
+ &is_minimal),
fail < 0 || fail >= 4))
return 0;
+ if (!TEST_true(is_minimal))
+ return 0;
+
if (!TEST_int_eq(ossl_quic_wire_decode_frame_max_streams(pkt,
&max_streams_2),
fail < 0))
{
uint64_t max_streams_1 = 0, max_streams_2 = 0,
frame_type_1 = 0, frame_type_2 = 0;
+ int is_minimal;
- if (!TEST_int_eq(ossl_quic_wire_peek_frame_header(pkt, &frame_type_1),
+ if (!TEST_int_eq(ossl_quic_wire_peek_frame_header(pkt, &frame_type_1,
+ &is_minimal),
fail < 0 || fail >= 1))
return 0;
+ if (!TEST_true(is_minimal))
+ return 0;
+
if (!TEST_int_eq(ossl_quic_wire_decode_frame_streams_blocked(pkt,
&max_streams_1),
fail < 0 || fail >= 3))
return 0;
- if (!TEST_int_eq(ossl_quic_wire_peek_frame_header(pkt, &frame_type_2),
+ if (!TEST_int_eq(ossl_quic_wire_peek_frame_header(pkt, &frame_type_2,
+ &is_minimal),
fail < 0 || fail >= 4))
return 0;
+ if (!TEST_true(is_minimal))
+ return 0;
+
if (!TEST_int_eq(ossl_quic_wire_decode_frame_streams_blocked(pkt,
&max_streams_2),
fail < 0 || fail >= 8))
return testresult;
}
+/* is_minimal=0 test */
+static const unsigned char non_minimal_1[] = {
+ 0x40, 0x00,
+};
+
+static const unsigned char non_minimal_2[] = {
+ 0x40, 0x3F,
+};
+
+static const unsigned char non_minimal_3[] = {
+ 0x80, 0x00, 0x00, 0x00,
+};
+
+static const unsigned char non_minimal_4[] = {
+ 0x80, 0x00, 0x3F, 0xFF,
+};
+
+static const unsigned char non_minimal_5[] = {
+ 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const unsigned char non_minimal_6[] = {
+ 0xC0, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF
+};
+
+static const unsigned char *const non_minimal[] = {
+ non_minimal_1,
+ non_minimal_2,
+ non_minimal_3,
+ non_minimal_4,
+ non_minimal_5,
+ non_minimal_6,
+};
+
+static const size_t non_minimal_len[] = {
+ OSSL_NELEM(non_minimal_1),
+ OSSL_NELEM(non_minimal_2),
+ OSSL_NELEM(non_minimal_3),
+ OSSL_NELEM(non_minimal_4),
+ OSSL_NELEM(non_minimal_5),
+ OSSL_NELEM(non_minimal_6),
+};
+
+static int test_wire_minimal(int idx)
+{
+ int testresult = 0;
+ int is_minimal;
+ uint64_t frame_type;
+ PACKET pkt;
+
+ if (!TEST_true(PACKET_buf_init(&pkt, non_minimal[idx],
+ non_minimal_len[idx])))
+ goto err;
+
+ if (!TEST_true(ossl_quic_wire_peek_frame_header(&pkt, &frame_type,
+ &is_minimal)))
+ goto err;
+
+ if (!TEST_false(is_minimal))
+ goto err;
+
+ testresult = 1;
+err:
+ return testresult;
+}
+
int setup_tests(void)
{
ADD_ALL_TESTS(test_wire_encode, OSSL_NELEM(encode_cases));
ADD_ALL_TESTS(test_wire_ack, OSSL_NELEM(ack_cases));
ADD_ALL_TESTS(test_wire_pkt_hdr_pn, OSSL_NELEM(pn_tests));
ADD_TEST(test_wire_retry_integrity_tag);
+ ADD_ALL_TESTS(test_wire_minimal, OSSL_NELEM(non_minimal_len));
return 1;
}
goto err;
#endif
- if (!TEST_true(qtest_check_server_protocol_err(qtserv)))
+ if (!TEST_true(qtest_check_server_frame_encoding_err(qtserv)))
goto err;
testresult = 1;