COSE dissector from dtn-wireshark project
This commit is contained in:
parent
4010502a82
commit
abd0f1183f
|
@ -3572,6 +3572,7 @@ add_custom_target(test-programs
|
|||
reassemble_test
|
||||
tvbtest
|
||||
wmem_test
|
||||
wscbor_test
|
||||
test_wsutil
|
||||
COMMENT "Building unit test programs and wrapper"
|
||||
)
|
||||
|
|
|
@ -1221,6 +1221,14 @@ libwireshark.so.0 libwireshark0 #MINVER#
|
|||
proto_tree_add_bytes_format_value@Base 1.9.1
|
||||
proto_tree_add_bytes_item@Base 1.12.0~rc1
|
||||
proto_tree_add_bytes_with_length@Base 1.99.3
|
||||
proto_tree_add_cbor_bitmask@Base 3.5.1
|
||||
proto_tree_add_cbor_boolean@Base 3.5.1
|
||||
proto_tree_add_cbor_bstr@Base 3.5.1
|
||||
proto_tree_add_cbor_container@Base 3.5.1
|
||||
proto_tree_add_cbor_ctrl@Base 3.5.1
|
||||
proto_tree_add_cbor_int64@Base 3.5.1
|
||||
proto_tree_add_cbor_tstr@Base 3.5.1
|
||||
proto_tree_add_cbor_uint64@Base 3.5.1
|
||||
proto_tree_add_checksum@Base 2.1.1
|
||||
proto_tree_add_debug_text@Base 1.9.1
|
||||
proto_tree_add_double@Base 1.9.1
|
||||
|
@ -2026,6 +2034,24 @@ libwireshark.so.0 libwireshark0 #MINVER#
|
|||
ws_find_media_type_parameter@Base 2.2.0
|
||||
ws_strdup_escape_char@Base 1.9.1
|
||||
ws_strdup_unescape_char@Base 1.9.1
|
||||
wscbor_chunk_free@Base 3.5.1
|
||||
wscbor_chunk_mark_errors@Base 3.5.1
|
||||
wscbor_chunk_read@Base 3.5.1
|
||||
wscbor_error_new@Base 3.5.1
|
||||
wscbor_has_errors@Base 3.5.1
|
||||
wscbor_init@Base 3.5.1
|
||||
wscbor_is_indefinite_break@Base 3.5.1
|
||||
wscbor_require_array@Base 3.5.1
|
||||
wscbor_require_array_size@Base 3.5.1
|
||||
wscbor_require_boolean@Base 3.5.1
|
||||
wscbor_require_bstr@Base 3.5.1
|
||||
wscbor_require_int64@Base 3.5.1
|
||||
wscbor_require_major_type@Base 3.5.1
|
||||
wscbor_require_map@Base 3.5.1
|
||||
wscbor_require_tstr@Base 3.5.1
|
||||
wscbor_require_uint64@Base 3.5.1
|
||||
wscbor_skip_if_errors@Base 3.5.1
|
||||
wscbor_skip_next_item@Base 3.5.1
|
||||
wslua_count_plugins@Base 1.12.0~rc1
|
||||
wslua_plugin_type_name@Base 2.5.0
|
||||
wslua_plugins_dump_all@Base 1.12.0~rc1
|
||||
|
|
|
@ -118,6 +118,7 @@ Vector Informatik Binary Log File (BLF)
|
|||
[commaize]
|
||||
--
|
||||
Bluetooth Link Manager Protocol (BT LMP)
|
||||
CBOR Object Signing and Encryption (COSE)
|
||||
E2 Application Protocol (E2AP)
|
||||
Event Tracing for Windows (ETW)
|
||||
High-Performance Connectivity Tracer (HiPerConTracer)
|
||||
|
|
|
@ -161,6 +161,7 @@ set(LIBWIRESHARK_PUBLIC_HEADERS
|
|||
unit_strings.h
|
||||
value_string.h
|
||||
wmem_scopes.h
|
||||
wscbor.h
|
||||
x264_prt_id.h
|
||||
xdlc.h
|
||||
)
|
||||
|
@ -257,6 +258,7 @@ set(LIBWIRESHARK_NONGENERATED_FILES
|
|||
tvbuff_lznt1.c
|
||||
uat.c
|
||||
value_string.c
|
||||
wscbor.c
|
||||
unit_strings.c
|
||||
wmem_scopes.c
|
||||
xdlc.c
|
||||
|
@ -417,6 +419,13 @@ set_target_properties(tvbtest PROPERTIES
|
|||
COMPILE_DEFINITIONS "WS_BUILD_DLL"
|
||||
)
|
||||
|
||||
add_executable(wscbor_test EXCLUDE_FROM_ALL wscbor_test.c)
|
||||
target_link_libraries(wscbor_test epan)
|
||||
set_target_properties(wscbor_test PROPERTIES
|
||||
FOLDER "Tests"
|
||||
EXCLUDE_FROM_DEFAULT_BUILD True
|
||||
)
|
||||
|
||||
CHECKAPI(
|
||||
NAME
|
||||
epan
|
||||
|
|
|
@ -294,6 +294,7 @@ set(DISSECTOR_PUBLIC_HEADERS
|
|||
packet-cmp.h
|
||||
packet-cms.h
|
||||
packet-coap.h
|
||||
packet-cose.h
|
||||
packet-credssp.h
|
||||
packet-crmf.h
|
||||
packet-csn1.h
|
||||
|
@ -852,6 +853,7 @@ set(DISSECTOR_SRC
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/packet-cops.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-corosync-totemnet.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-corosync-totemsrp.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-cose.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-cosine.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-couchbase.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-cp2179.c
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,70 @@
|
|||
/* packet-cose.h
|
||||
* Definitions for CBOR Object Signing and Encryption (COSE) dissection
|
||||
* References:
|
||||
* RFC 8152: https://tools.ietf.org/html/rfc8152
|
||||
*
|
||||
* Copyright 2019-2021, Brian Sipos <brian.sipos@gmail.com>
|
||||
*
|
||||
* Wireshark - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@wireshark.org>
|
||||
* Copyright 1998 Gerald Combs
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
#ifndef __PACKET_COSE_H__
|
||||
#define __PACKET_COSE_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/**
|
||||
* COSE message dissectors are registered multiple ways:
|
||||
* 1. The unit-keyed dissector table "cose.msgtag" with keys being
|
||||
* IANA-registered CBOR tag values (e.g., 18 is COSE_Sign1).
|
||||
* 2. The string-keyed dissector table "media_type" with keys being
|
||||
* IANA-registered media type IDs
|
||||
* (e.g., application/cose; cose-type="cose-sign1" is COSE_Sign1).
|
||||
* 3. The registered dissectors for names "cose" and message names in
|
||||
* all-lowercase form (e.g., "cose_sign1").
|
||||
* There is currently no CoAP dissector table to register with.
|
||||
*
|
||||
* COSE message dissectors use the tag (wscbor_tag_t *) value, if used to
|
||||
* discriminate the message type, as the user data pointer.
|
||||
*
|
||||
* COSE header label dissectors are registered with the dissector table
|
||||
* "cose.header" and key parameter dissectors with the table "cose.keyparam"
|
||||
* both with cose_param_key_t* keys.
|
||||
* The header/parameter dissectors use a cose_header_context_t* as the user
|
||||
* data pointer.
|
||||
*
|
||||
* An additional dissector "cose.msg.headers" will dissect an individual
|
||||
* header map structure outside of a COSE message.
|
||||
*/
|
||||
|
||||
// A header parameter or key-type parameter key
|
||||
typedef struct {
|
||||
/// The Algorithm or Key Type context or NULL for
|
||||
/// all-context keys.
|
||||
GVariant *principal;
|
||||
|
||||
/// Label simple value (int or tstr) as variant.
|
||||
/// Object owned by this struct.
|
||||
GVariant *label;
|
||||
} cose_param_key_t;
|
||||
|
||||
/** Compatible with GHashFunc signature.
|
||||
*/
|
||||
guint cose_param_key_hash(gconstpointer ptr);
|
||||
|
||||
/** Compatible with GEqualFunc signature.
|
||||
*/
|
||||
gboolean cose_param_key_equal(gconstpointer a, gconstpointer b);
|
||||
|
||||
/// User data for header/key-parameter dissectors
|
||||
typedef struct {
|
||||
/// Principal value (alg or kty) of the map, if defined.
|
||||
GVariant *principal;
|
||||
/// Current label being processed
|
||||
GVariant *label;
|
||||
} cose_header_context_t;
|
||||
|
||||
#endif /* __PACKET_COSE_H__ */
|
|
@ -58,6 +58,7 @@
|
|||
#include "stats_tree.h"
|
||||
#include "secrets.h"
|
||||
#include "funnel.h"
|
||||
#include "wscbor.h"
|
||||
#include <dtd.h>
|
||||
|
||||
#ifdef HAVE_PLUGINS
|
||||
|
@ -303,6 +304,7 @@ epan_init(register_cb cb, gpointer client_data, gboolean load_plugins)
|
|||
g_slist_foreach(epan_plugins, epan_plugin_register_all_tap_listeners, NULL);
|
||||
packet_cache_proto_handles();
|
||||
dfilter_init();
|
||||
wscbor_init();
|
||||
final_registration_all_protocols();
|
||||
print_cache_field_handles();
|
||||
expert_packet_init();
|
||||
|
|
|
@ -0,0 +1,546 @@
|
|||
/* wscbor.c
|
||||
* Wireshark CBOR item decoding API.
|
||||
* References:
|
||||
* RFC 8949: https://tools.ietf.org/html/rfc8949
|
||||
*
|
||||
* Copyright 2019-2021, Brian Sipos <brian.sipos@gmail.com>
|
||||
*
|
||||
* Wireshark - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@wireshark.org>
|
||||
* Copyright 1998 Gerald Combs
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include <epan/packet.h>
|
||||
#include <epan/exceptions.h>
|
||||
#include <epan/expert.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "wscbor.h"
|
||||
|
||||
/// Pseudo-protocol to register expert info
|
||||
static int proto_wscbor = -1;
|
||||
|
||||
static expert_field ei_cbor_invalid = EI_INIT;
|
||||
static expert_field ei_cbor_overflow = EI_INIT;
|
||||
static expert_field ei_cbor_wrong_type = EI_INIT;
|
||||
static expert_field ei_cbor_array_wrong_size = EI_INIT;
|
||||
static ei_register_info expertitems[] = {
|
||||
{&ei_cbor_invalid, {"_ws.wscbor.cbor_invalid", PI_MALFORMED, PI_ERROR, "CBOR cannot be decoded", EXPFILL}},
|
||||
{&ei_cbor_overflow, {"_ws.wscbor.cbor_overflow", PI_UNDECODED, PI_ERROR, "CBOR overflow of Wireshark value", EXPFILL}},
|
||||
{&ei_cbor_wrong_type, {"_ws.wscbor.cbor_wrong_type", PI_MALFORMED, PI_ERROR, "CBOR is wrong type", EXPFILL}},
|
||||
{&ei_cbor_array_wrong_size, {"_ws.wscbor.array_wrong_size", PI_MALFORMED, PI_WARN, "CBOR array is the wrong size", EXPFILL}},
|
||||
};
|
||||
|
||||
/// The basic header structure of CBOR encoding
|
||||
typedef struct {
|
||||
/// The start offset of this header
|
||||
gint start;
|
||||
/// The length of just this header
|
||||
gint length;
|
||||
/// The expert info object (if error)
|
||||
expert_field *error;
|
||||
|
||||
/// Major type of this item (cbor_type)
|
||||
guint8 type_major;
|
||||
/// Minor type of this item
|
||||
guint8 type_minor;
|
||||
/// Raw head "value" which may be from the @c type_minor
|
||||
guint64 rawvalue;
|
||||
} wscbor_head_t;
|
||||
|
||||
/** Read the raw value from a CBOR head.
|
||||
* @param[in,out] head The head to read into.
|
||||
* @param tvb The buffer to read from.
|
||||
*/
|
||||
static void wscbor_read_unsigned(wscbor_head_t *head, tvbuff_t *tvb) {
|
||||
switch (head->type_minor) {
|
||||
case 0x18:
|
||||
head->rawvalue = tvb_get_guint8(tvb, head->start + head->length);
|
||||
head->length += 1;
|
||||
break;
|
||||
case 0x19:
|
||||
head->rawvalue = tvb_get_guint16(tvb, head->start + head->length, ENC_BIG_ENDIAN);
|
||||
head->length += 2;
|
||||
break;
|
||||
case 0x1A:
|
||||
head->rawvalue = tvb_get_guint32(tvb, head->start + head->length, ENC_BIG_ENDIAN);
|
||||
head->length += 4;
|
||||
break;
|
||||
case 0x1B:
|
||||
head->rawvalue = tvb_get_guint64(tvb, head->start + head->length, ENC_BIG_ENDIAN);
|
||||
head->length += 8;
|
||||
break;
|
||||
default:
|
||||
if (head->type_minor <= 0x17) {
|
||||
head->rawvalue = head->type_minor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Read just the CBOR head octet.
|
||||
* @post Will throw wireshark exception if read fails.
|
||||
*/
|
||||
static wscbor_head_t * wscbor_head_read(wmem_allocator_t *alloc, tvbuff_t *tvb, gint start) {
|
||||
wscbor_head_t *head = wmem_new0(alloc, wscbor_head_t);
|
||||
|
||||
head->start = start;
|
||||
const guint8 first = tvb_get_guint8(tvb, head->start);
|
||||
head->length += 1;
|
||||
|
||||
// Match libcbor enums
|
||||
head->type_major = (first & 0xe0) >> 5;
|
||||
head->type_minor = (first & 0x1f);
|
||||
switch ((cbor_type)(head->type_major)) {
|
||||
case CBOR_TYPE_UINT:
|
||||
case CBOR_TYPE_NEGINT:
|
||||
case CBOR_TYPE_TAG:
|
||||
wscbor_read_unsigned(head, tvb);
|
||||
if (head->type_minor > 0x1B) {
|
||||
head->error = &ei_cbor_invalid;
|
||||
}
|
||||
break;
|
||||
case CBOR_TYPE_BYTESTRING:
|
||||
case CBOR_TYPE_STRING:
|
||||
case CBOR_TYPE_ARRAY:
|
||||
case CBOR_TYPE_MAP:
|
||||
case CBOR_TYPE_FLOAT_CTRL:
|
||||
wscbor_read_unsigned(head, tvb);
|
||||
if ((head->type_minor > 0x1B) && (head->type_minor < 0x1F)) {
|
||||
head->error = &ei_cbor_invalid;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
head->error = &ei_cbor_invalid;
|
||||
break;
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
/** Force a head to be freed.
|
||||
*/
|
||||
static void wscbor_head_free(wmem_allocator_t *alloc, wscbor_head_t *head) {
|
||||
wmem_free(alloc, head);
|
||||
}
|
||||
|
||||
/** Get a clamped string length suitable for tvb functions.
|
||||
* @param[in,out] chunk The chunk to read and set errors on.
|
||||
* @return The clamped length value.
|
||||
*/
|
||||
static gint wscbor_get_length(const wscbor_chunk_t *chunk) {
|
||||
gint length;
|
||||
if (chunk->head_value > G_MAXINT) {
|
||||
wmem_list_append(chunk->errors, wscbor_error_new(
|
||||
chunk->_alloc, &ei_cbor_overflow,
|
||||
NULL
|
||||
));
|
||||
length = G_MAXINT;
|
||||
}
|
||||
else {
|
||||
length = (gint) chunk->head_value;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
wscbor_error_t * wscbor_error_new(wmem_allocator_t *alloc, expert_field *ei, const char *format, ...) {
|
||||
wscbor_error_t *err = wmem_new0(alloc, wscbor_error_t);
|
||||
err->ei = ei;
|
||||
if (format) {
|
||||
wmem_strbuf_t *buf = wmem_strbuf_new(alloc, "");
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
wmem_strbuf_append_vprintf(buf, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
err->msg = wmem_strbuf_finalize(buf);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
wscbor_chunk_t * wscbor_chunk_read(wmem_allocator_t *alloc, tvbuff_t *tvb, gint *offset) {
|
||||
DISSECTOR_ASSERT(alloc != NULL);
|
||||
DISSECTOR_ASSERT(offset != NULL);
|
||||
DISSECTOR_ASSERT(tvb != NULL);
|
||||
|
||||
wscbor_chunk_t *chunk = wmem_new0(alloc, wscbor_chunk_t);
|
||||
chunk->_alloc = alloc;
|
||||
chunk->errors = wmem_list_new(alloc);
|
||||
chunk->tags = wmem_list_new(alloc);
|
||||
chunk->start = *offset;
|
||||
|
||||
// Read a sequence of tags followed by an item header
|
||||
while (TRUE) {
|
||||
// This will break out of the loop if it runs out of buffer
|
||||
wscbor_head_t *head = wscbor_head_read(alloc, tvb, *offset);
|
||||
*offset += head->length;
|
||||
chunk->head_length += head->length;
|
||||
if (head->error) {
|
||||
wmem_list_append(chunk->errors, wscbor_error_new(alloc, head->error, NULL));
|
||||
}
|
||||
if (head->type_major == CBOR_TYPE_TAG) {
|
||||
wscbor_tag_t *tag = wmem_new(alloc, wscbor_tag_t);
|
||||
tag->start = head->start;
|
||||
tag->length = head->length;
|
||||
tag->value = head->rawvalue;
|
||||
wmem_list_append(chunk->tags, tag);
|
||||
// same chunk, next part
|
||||
wscbor_head_free(alloc, head);
|
||||
continue;
|
||||
}
|
||||
|
||||
// An actual (non-tag) header
|
||||
chunk->type_major = (cbor_type)head->type_major;
|
||||
chunk->type_minor = head->type_minor;
|
||||
chunk->head_value = head->rawvalue;
|
||||
|
||||
chunk->data_length = chunk->head_length;
|
||||
switch ((cbor_type)(head->type_major)) {
|
||||
case CBOR_TYPE_BYTESTRING:
|
||||
case CBOR_TYPE_STRING:
|
||||
if (chunk->type_minor != 31) {
|
||||
const gint datalen = wscbor_get_length(chunk);
|
||||
// skip over definite data
|
||||
*offset += datalen;
|
||||
chunk->data_length += datalen;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
wscbor_head_free(alloc, head);
|
||||
break;
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
static void wscbor_subitem_free(gpointer data, gpointer userdata) {
|
||||
wmem_allocator_t *alloc = (wmem_allocator_t *) userdata;
|
||||
wmem_free(alloc, data);
|
||||
}
|
||||
|
||||
void wscbor_chunk_free(wscbor_chunk_t *chunk) {
|
||||
DISSECTOR_ASSERT(chunk);
|
||||
wmem_allocator_t *alloc = chunk->_alloc;
|
||||
wmem_list_foreach(chunk->errors, wscbor_subitem_free, alloc);
|
||||
wmem_destroy_list(chunk->errors);
|
||||
wmem_list_foreach(chunk->tags, wscbor_subitem_free, alloc);
|
||||
wmem_destroy_list(chunk->tags);
|
||||
wmem_free(alloc, chunk);
|
||||
}
|
||||
|
||||
guint64 wscbor_chunk_mark_errors(packet_info *pinfo, proto_item *item, const wscbor_chunk_t *chunk) {
|
||||
for (wmem_list_frame_t *it = wmem_list_head(chunk->errors); it;
|
||||
it = wmem_list_frame_next(it)) {
|
||||
wscbor_error_t *err = (wscbor_error_t *) wmem_list_frame_data(it);
|
||||
if (err->msg) {
|
||||
expert_add_info_format(pinfo, item, err->ei, "%s", err->msg);
|
||||
}
|
||||
else {
|
||||
expert_add_info(pinfo, item, err->ei);
|
||||
}
|
||||
}
|
||||
return wmem_list_count(chunk->errors);
|
||||
}
|
||||
|
||||
guint wscbor_has_errors(const wscbor_chunk_t *chunk) {
|
||||
return wmem_list_count(chunk->errors);
|
||||
}
|
||||
|
||||
gboolean wscbor_is_indefinite_break(const wscbor_chunk_t *chunk) {
|
||||
return (
|
||||
(chunk->type_major == CBOR_TYPE_FLOAT_CTRL)
|
||||
&& (chunk->type_minor == 31)
|
||||
);
|
||||
}
|
||||
|
||||
gboolean wscbor_skip_next_item(wmem_allocator_t *alloc, tvbuff_t *tvb, gint *offset) {
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(alloc, tvb, offset);
|
||||
switch (chunk->type_major) {
|
||||
case CBOR_TYPE_UINT:
|
||||
case CBOR_TYPE_NEGINT:
|
||||
case CBOR_TYPE_TAG:
|
||||
case CBOR_TYPE_FLOAT_CTRL:
|
||||
break;
|
||||
case CBOR_TYPE_BYTESTRING:
|
||||
case CBOR_TYPE_STRING:
|
||||
if (chunk->type_minor == 31) {
|
||||
// wait for indefinite break
|
||||
while (!wscbor_skip_next_item(alloc, tvb, offset)) {}
|
||||
}
|
||||
// wscbor_read_chunk() sets offset past definite value
|
||||
break;
|
||||
case CBOR_TYPE_ARRAY: {
|
||||
if (chunk->type_minor == 31) {
|
||||
// wait for indefinite break
|
||||
while (!wscbor_skip_next_item(alloc, tvb, offset)) {}
|
||||
}
|
||||
else {
|
||||
const guint64 count = chunk->head_value;
|
||||
for (guint64 ix = 0; ix < count; ++ix) {
|
||||
wscbor_skip_next_item(alloc, tvb, offset);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CBOR_TYPE_MAP: {
|
||||
if (chunk->type_minor == 31) {
|
||||
// wait for indefinite break
|
||||
while (!wscbor_skip_next_item(alloc, tvb, offset)) {}
|
||||
}
|
||||
else {
|
||||
const guint64 count = chunk->head_value;
|
||||
for (guint64 ix = 0; ix < count; ++ix) {
|
||||
wscbor_skip_next_item(alloc, tvb, offset);
|
||||
wscbor_skip_next_item(alloc, tvb, offset);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
const gboolean is_break = wscbor_is_indefinite_break(chunk);
|
||||
wscbor_chunk_free(chunk);
|
||||
return is_break;
|
||||
}
|
||||
|
||||
gboolean wscbor_skip_if_errors(wmem_allocator_t *alloc, tvbuff_t *tvb, gint *offset, const wscbor_chunk_t *chunk) {
|
||||
if (wscbor_has_errors(chunk) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*offset = chunk->start;
|
||||
wscbor_skip_next_item(alloc, tvb, offset);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void wscbor_init(void) {
|
||||
proto_wscbor = proto_register_protocol(
|
||||
"CBOR Item Decoder",
|
||||
"CBOR Item Decoder",
|
||||
"_ws.wscbor"
|
||||
);
|
||||
|
||||
expert_module_t *expert_wscbor = expert_register_protocol(proto_wscbor);
|
||||
/* This isn't really a protocol, it's an error indication;
|
||||
disabling them makes no sense. */
|
||||
proto_set_cant_toggle(proto_wscbor);
|
||||
|
||||
expert_register_field_array(expert_wscbor, expertitems, array_length(expertitems));
|
||||
}
|
||||
|
||||
gboolean wscbor_require_major_type(wscbor_chunk_t *chunk, cbor_type major) {
|
||||
if (chunk->type_major == major) {
|
||||
return TRUE;
|
||||
}
|
||||
wmem_list_append(chunk->errors, wscbor_error_new(
|
||||
chunk->_alloc, &ei_cbor_wrong_type,
|
||||
"Item has major type %d, should be %d",
|
||||
chunk->type_major, major
|
||||
));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean wscbor_require_array(wscbor_chunk_t *chunk) {
|
||||
return wscbor_require_major_type(chunk, CBOR_TYPE_ARRAY);
|
||||
}
|
||||
|
||||
gboolean wscbor_require_array_size(wscbor_chunk_t *chunk, guint64 count_min, guint64 count_max) {
|
||||
if (!wscbor_require_array(chunk)) {
|
||||
return FALSE;
|
||||
}
|
||||
if ((chunk->head_value < count_min) || (chunk->head_value > count_max)) {
|
||||
wmem_list_append(chunk->errors, wscbor_error_new(
|
||||
chunk->_alloc, &ei_cbor_array_wrong_size,
|
||||
"Array has %" PRId64 " items, should be within [%"PRId64", %"PRId64"]",
|
||||
chunk->head_value, count_min, count_max
|
||||
));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean wscbor_require_map(wscbor_chunk_t *chunk) {
|
||||
return wscbor_require_major_type(chunk, CBOR_TYPE_MAP);
|
||||
}
|
||||
|
||||
gboolean * wscbor_require_boolean(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) {
|
||||
if (!wscbor_require_major_type(chunk, CBOR_TYPE_FLOAT_CTRL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (chunk->type_minor) {
|
||||
case CBOR_CTRL_TRUE:
|
||||
case CBOR_CTRL_FALSE: {
|
||||
gboolean *value = NULL;
|
||||
value = wmem_new(alloc, gboolean);
|
||||
*value = (chunk->type_minor == CBOR_CTRL_TRUE);
|
||||
return value;
|
||||
}
|
||||
default:
|
||||
wmem_list_append(chunk->errors, wscbor_error_new(
|
||||
chunk->_alloc, &ei_cbor_wrong_type,
|
||||
"Item has minor type %d, should be %d or %d",
|
||||
chunk->type_minor, CBOR_CTRL_TRUE, CBOR_CTRL_FALSE
|
||||
));
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
guint64 * wscbor_require_uint64(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) {
|
||||
if (!wscbor_require_major_type(chunk, CBOR_TYPE_UINT)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
guint64 *result = wmem_new(alloc, guint64);
|
||||
*result = chunk->head_value;
|
||||
return result;
|
||||
}
|
||||
|
||||
gint64 * wscbor_require_int64(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) {
|
||||
gint64 *result = NULL;
|
||||
switch (chunk->type_major) {
|
||||
case CBOR_TYPE_UINT:
|
||||
case CBOR_TYPE_NEGINT: {
|
||||
gint64 clamped;
|
||||
if (chunk->head_value > INT64_MAX) {
|
||||
clamped = INT64_MAX;
|
||||
wmem_list_append(chunk->errors, wscbor_error_new(
|
||||
chunk->_alloc, &ei_cbor_overflow,
|
||||
NULL
|
||||
));
|
||||
}
|
||||
else {
|
||||
clamped = chunk->head_value;
|
||||
}
|
||||
|
||||
result = wmem_new(alloc, gint64);
|
||||
if (chunk->type_major == CBOR_TYPE_NEGINT) {
|
||||
*result = -clamped - 1;
|
||||
}
|
||||
else {
|
||||
*result = clamped;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
wmem_list_append(chunk->errors, wscbor_error_new(
|
||||
chunk->_alloc, &ei_cbor_wrong_type,
|
||||
"Item has major type %d, should be %d or %d",
|
||||
chunk->type_major, CBOR_TYPE_UINT, CBOR_TYPE_NEGINT
|
||||
));
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char * wscbor_require_tstr(wmem_allocator_t *alloc, tvbuff_t *parent, wscbor_chunk_t *chunk) {
|
||||
if (!wscbor_require_major_type(chunk, CBOR_TYPE_STRING)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (char *)tvb_get_string_enc(alloc, parent, chunk->start + chunk->head_length, wscbor_get_length(chunk), ENC_UTF_8);
|
||||
}
|
||||
|
||||
tvbuff_t * wscbor_require_bstr(tvbuff_t *parent, wscbor_chunk_t *chunk) {
|
||||
if (!wscbor_require_major_type(chunk, CBOR_TYPE_BYTESTRING)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tvb_new_subset_length(parent, chunk->start + chunk->head_length, wscbor_get_length(chunk));
|
||||
}
|
||||
|
||||
proto_item * proto_tree_add_cbor_container(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) {
|
||||
const header_field_info *hfinfo = proto_registrar_get_nth(hfindex);
|
||||
proto_item *item;
|
||||
if (IS_FT_UINT(hfinfo->type)) {
|
||||
item = proto_tree_add_uint64(tree, hfindex, tvb, chunk->start, chunk->head_length, chunk->head_value);
|
||||
}
|
||||
else if (IS_FT_INT(hfinfo->type)) {
|
||||
item = proto_tree_add_int64(tree, hfindex, tvb, chunk->start, chunk->head_length, chunk->head_value);
|
||||
}
|
||||
else {
|
||||
item = proto_tree_add_item(tree, hfindex, tvb, chunk->start, -1, 0);
|
||||
}
|
||||
wscbor_chunk_mark_errors(pinfo, item, chunk);
|
||||
return item;
|
||||
}
|
||||
|
||||
proto_item * proto_tree_add_cbor_ctrl(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) {
|
||||
proto_item *item = proto_tree_add_item(tree, hfindex, tvb, chunk->start, chunk->head_length, 0);
|
||||
wscbor_chunk_mark_errors(pinfo, item, chunk);
|
||||
return item;
|
||||
}
|
||||
|
||||
proto_item * proto_tree_add_cbor_boolean(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk, const gboolean *value) {
|
||||
proto_item *item = proto_tree_add_boolean(tree, hfindex, tvb, chunk->start, chunk->data_length, value ? *value : FALSE);
|
||||
wscbor_chunk_mark_errors(pinfo, item, chunk);
|
||||
return item;
|
||||
}
|
||||
|
||||
proto_item * proto_tree_add_cbor_uint64(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk, const guint64 *value) {
|
||||
proto_item *item = proto_tree_add_uint64(tree, hfindex, tvb, chunk->start, chunk->head_length, value ? *value : 0);
|
||||
wscbor_chunk_mark_errors(pinfo, item, chunk);
|
||||
return item;
|
||||
}
|
||||
|
||||
proto_item * proto_tree_add_cbor_int64(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk, const gint64 *value) {
|
||||
proto_item *item = proto_tree_add_int64(tree, hfindex, tvb, chunk->start, chunk->head_length, value ? *value : 0);
|
||||
wscbor_chunk_mark_errors(pinfo, item, chunk);
|
||||
return item;
|
||||
}
|
||||
|
||||
proto_item * proto_tree_add_cbor_bitmask(proto_tree *tree, int hfindex, const gint ett, int *const *fields, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk, const guint64 *value) {
|
||||
header_field_info *field = proto_registrar_get_nth(hfindex);
|
||||
gint flagsize = 0;
|
||||
switch (field->type) {
|
||||
case FT_UINT8:
|
||||
flagsize = 1;
|
||||
break;
|
||||
case FT_UINT16:
|
||||
flagsize = 2;
|
||||
break;
|
||||
case FT_UINT32:
|
||||
flagsize = 4;
|
||||
break;
|
||||
case FT_UINT64:
|
||||
flagsize = 8;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unhandled bitmask size: %d", field->type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Fake TVB data for these functions
|
||||
guint8 *flags = (guint8 *) wmem_alloc0(wmem_packet_scope(), flagsize);
|
||||
{ // Inject big-endian value directly
|
||||
guint64 buf = (value ? *value : 0);
|
||||
for (gint ix = flagsize - 1; ix >= 0; --ix) {
|
||||
flags[ix] = buf & 0xFF;
|
||||
buf >>= 8;
|
||||
}
|
||||
}
|
||||
tvbuff_t *tvb_flags = tvb_new_child_real_data(tvb, flags, flagsize, flagsize);
|
||||
|
||||
proto_item *item = proto_tree_add_bitmask_value(tree, tvb_flags, 0, hfindex, ett, fields, value ? *value : 0);
|
||||
wscbor_chunk_mark_errors(pinfo, item, chunk);
|
||||
return item;
|
||||
}
|
||||
|
||||
proto_item * proto_tree_add_cbor_tstr(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) {
|
||||
proto_item *item = proto_tree_add_item(tree, hfindex, tvb, chunk->start + chunk->head_length, wscbor_get_length(chunk), 0);
|
||||
wscbor_chunk_mark_errors(pinfo, item, chunk);
|
||||
return item;
|
||||
}
|
||||
|
||||
proto_item * proto_tree_add_cbor_bstr(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) {
|
||||
proto_item *item = proto_tree_add_item(tree, hfindex, tvb, chunk->start + chunk->head_length, wscbor_get_length(chunk), 0);
|
||||
wscbor_chunk_mark_errors(pinfo, item, chunk);
|
||||
return item;
|
||||
}
|
|
@ -0,0 +1,295 @@
|
|||
/* wscbor.h
|
||||
* Definitions for the Wireshark CBOR item decoding API.
|
||||
* References:
|
||||
* RFC 8949: https://tools.ietf.org/html/rfc8949
|
||||
*
|
||||
* Copyright 2019-2021, Brian Sipos <brian.sipos@gmail.com>
|
||||
*
|
||||
* Wireshark - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@wireshark.org>
|
||||
* Copyright 1998 Gerald Combs
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef __WSCBOR_H__
|
||||
#define __WSCBOR_H__
|
||||
|
||||
#include <ws_symbol_export.h>
|
||||
#include <epan/tvbuff.h>
|
||||
#include <epan/proto.h>
|
||||
#include <epan/expert.h>
|
||||
#include <wsutil/wmem/wmem_list.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Register expert info and other wireshark data.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
void wscbor_init(void);
|
||||
|
||||
/// The same enumeration from libcbor-0.5
|
||||
typedef enum cbor_type {
|
||||
CBOR_TYPE_UINT = 0, ///< positive integers
|
||||
CBOR_TYPE_NEGINT = 1, ///< negative integers
|
||||
CBOR_TYPE_BYTESTRING = 2, ///< byte strings
|
||||
CBOR_TYPE_STRING = 3, ///< text strings
|
||||
CBOR_TYPE_ARRAY = 4, ///< arrays
|
||||
CBOR_TYPE_MAP = 5, ///< maps
|
||||
CBOR_TYPE_TAG = 6, ///< tags
|
||||
CBOR_TYPE_FLOAT_CTRL = 7, ///< decimals and special values (true, false, nil, ...)
|
||||
} cbor_type;
|
||||
|
||||
/// The same enumeration from libcbor-0.5
|
||||
typedef enum {
|
||||
CBOR_CTRL_NONE = 0,
|
||||
CBOR_CTRL_FALSE = 20,
|
||||
CBOR_CTRL_TRUE = 21,
|
||||
CBOR_CTRL_NULL = 22,
|
||||
CBOR_CTRL_UNDEF = 23
|
||||
} _cbor_ctrl;
|
||||
|
||||
/// Decoding or require_* error
|
||||
typedef struct {
|
||||
/// The associated expert info
|
||||
expert_field *ei;
|
||||
/// Optional specific text
|
||||
const char *msg;
|
||||
} wscbor_error_t;
|
||||
|
||||
/** Construct a new error object.
|
||||
*
|
||||
* @param alloc The allocator to use.
|
||||
* @param ei The specific error type.
|
||||
* @param format If non-NULL, a message format string.
|
||||
* @return The new object.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
wscbor_error_t * wscbor_error_new(wmem_allocator_t *alloc, expert_field *ei, const char *format, ...);
|
||||
|
||||
/// Tag metadata and value
|
||||
typedef struct {
|
||||
/// The start offset of this tag head
|
||||
gint start;
|
||||
/// The length of just this tag head
|
||||
gint length;
|
||||
/// The tag value
|
||||
guint64 value;
|
||||
} wscbor_tag_t;
|
||||
|
||||
/// A data-containing, optionally-tagged chunk of CBOR
|
||||
typedef struct {
|
||||
/// The allocator used for #errors and #tags
|
||||
wmem_allocator_t *_alloc;
|
||||
|
||||
/// The start offset of this chunk
|
||||
gint start;
|
||||
/// The length of just this header any any preceding tags
|
||||
gint head_length;
|
||||
/// The length of this chunk and its immediate definite data (i.e. strings)
|
||||
gint data_length;
|
||||
/// Errors processing this chunk (type wscbor_error_t*)
|
||||
wmem_list_t *errors;
|
||||
/// Tags on this chunk, in encoded order (type wscbor_tag_t*)
|
||||
wmem_list_t *tags;
|
||||
|
||||
/// Major type of this block.
|
||||
/// This will be one of the cbor_type values.
|
||||
cbor_type type_major;
|
||||
/// Minor type of this item
|
||||
guint8 type_minor;
|
||||
/// The header-encoded value
|
||||
guint64 head_value;
|
||||
} wscbor_chunk_t;
|
||||
|
||||
/** Scan for a tagged chunk of headers.
|
||||
* The chunk of byte string and text string items includes the data content
|
||||
* in its @c offset.
|
||||
*
|
||||
* @param alloc The allocator to use.
|
||||
* @param tvb The TVB to read from.
|
||||
* @param[in,out] offset The offset with in @c tvb.
|
||||
* @return The chunk of data found, including any errors.
|
||||
* This never returns NULL.
|
||||
* @post This can throw ReportedBoundsError if the read itself ran out of data.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
wscbor_chunk_t * wscbor_chunk_read(wmem_allocator_t *alloc, tvbuff_t *tvb, gint *offset);
|
||||
|
||||
/** Free a chunk and its lists.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
void wscbor_chunk_free(wscbor_chunk_t *chunk);
|
||||
|
||||
/** After both reading and decoding a chunk, report on any errors found.
|
||||
* @param pinfo The associated packet.
|
||||
* @param item The associated tree item.
|
||||
* @param chunk The chunk with possible errors.
|
||||
* @return The error count.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
guint64 wscbor_chunk_mark_errors(packet_info *pinfo, proto_item *item, const wscbor_chunk_t *chunk);
|
||||
|
||||
/** Determine if a chunk has errors.
|
||||
* @param chunk The chunk with possible errors.
|
||||
* @return The error count.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
guint wscbor_has_errors(const wscbor_chunk_t *chunk);
|
||||
|
||||
/** Determine if an indefinite break is present.
|
||||
*
|
||||
* @param chunk The chunk to check.
|
||||
* @return True if it's an indefinite break.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
gboolean wscbor_is_indefinite_break(const wscbor_chunk_t *chunk);
|
||||
|
||||
/** Recursively skip items from a stream.
|
||||
*
|
||||
* @param alloc The allocator to use.
|
||||
* @param tvb The data buffer.
|
||||
* @param[in,out] offset The initial offset to read and skip over.
|
||||
* @return True if the skipped item was an indefinite break.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
gboolean wscbor_skip_next_item(wmem_allocator_t *alloc, tvbuff_t *tvb, gint *offset);
|
||||
|
||||
/** Skip over an item if a chunk has errors.
|
||||
* This allows skipping an entire array or map if the major type or size is
|
||||
* not as expected.
|
||||
*
|
||||
* @param alloc The allocator to use.
|
||||
* @param tvb The data buffer.
|
||||
* @param[in,out] offset The initial offset to read and skip over.
|
||||
* @param chunk The chunk with possible errors.
|
||||
* @return True if there were errors and the item skipped.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
gboolean wscbor_skip_if_errors(wmem_allocator_t *alloc, tvbuff_t *tvb, gint *offset, const wscbor_chunk_t *chunk);
|
||||
|
||||
|
||||
/** Require a specific item major type.
|
||||
*
|
||||
* @param[in,out] chunk The chunk to read from and write errors on.
|
||||
* @param major The required major type.
|
||||
* @return True if the item is that type.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
gboolean wscbor_require_major_type(wscbor_chunk_t *chunk, cbor_type major);
|
||||
|
||||
/** Require an array item.
|
||||
*
|
||||
* @param[in,out] chunk The chunk to read from and write errors on.
|
||||
* @return True if the item is an array.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
gboolean wscbor_require_array(wscbor_chunk_t *chunk);
|
||||
|
||||
/** Require an array have a specific ranged size.
|
||||
*
|
||||
* @param[in,out] chunk The chunk to read from and write errors on.
|
||||
* @param count_min The minimum acceptable size.
|
||||
* @param count_max The maximum acceptable size.
|
||||
* @return True if the size is acceptable.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
gboolean wscbor_require_array_size(wscbor_chunk_t *chunk, guint64 count_min, guint64 count_max);
|
||||
|
||||
/** Require a map item.
|
||||
*
|
||||
* @param[in,out] chunk The chunk to read from and write errors on.
|
||||
* @return True if the item is a map.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
gboolean wscbor_require_map(wscbor_chunk_t *chunk);
|
||||
|
||||
/** Require a CBOR item to have a boolean value.
|
||||
*
|
||||
* @param alloc The allocator to use.
|
||||
* @param[in,out] chunk The chunk to read from and write errors on.
|
||||
* @return Pointer to the boolean value, if the item was boolean.
|
||||
* The value can be deleted with wscbor_require_delete().
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
gboolean * wscbor_require_boolean(wmem_allocator_t *alloc, wscbor_chunk_t *chunk);
|
||||
|
||||
/** Require a CBOR item to have an unsigned-integer value.
|
||||
* @note This reader will clip the most significant bit of the value.
|
||||
*
|
||||
* @param alloc The allocator to use.
|
||||
* @param[in,out] chunk The chunk to read from and write errors on.
|
||||
* @return Pointer to the boolean value, if the item was an integer.
|
||||
* The value can be deleted with wscbor_require_delete().
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
guint64 * wscbor_require_uint64(wmem_allocator_t *alloc, wscbor_chunk_t *chunk);
|
||||
|
||||
/** Require a CBOR item to have an signed- or unsigned-integer value.
|
||||
* @note This reader will clip the most significant bit of the value.
|
||||
*
|
||||
* @param alloc The allocator to use.
|
||||
* @param[in,out] chunk The chunk to read from and write errors on.
|
||||
* @return Pointer to the value, if the item was an integer.
|
||||
* The value can be deleted with wscbor_require_delete().
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
gint64 * wscbor_require_int64(wmem_allocator_t *alloc, wscbor_chunk_t *chunk);
|
||||
|
||||
/** Require a CBOR item to have a text-string value.
|
||||
*
|
||||
* @param alloc The allocator to use.
|
||||
* @param parent The containing buffer.
|
||||
* @param[in,out] chunk The chunk to read from and write errors on.
|
||||
* @return Pointer to the null-terminated UTF-8, if the item was a tstr.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
char * wscbor_require_tstr(wmem_allocator_t *alloc, tvbuff_t *parent, wscbor_chunk_t *chunk);
|
||||
|
||||
/** Require a CBOR item to have a byte-string value.
|
||||
*
|
||||
* @param parent The containing buffer.
|
||||
* @param[in,out] chunk The chunk to read from and write errors on.
|
||||
* @return Pointer to the value, if the item was an string.
|
||||
* The value is memory managed by wireshark.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
tvbuff_t * wscbor_require_bstr(tvbuff_t *parent, wscbor_chunk_t *chunk);
|
||||
|
||||
/** Add an item representing an array or map container.
|
||||
* If the item is type FT_UINT* or FT_INT* the count of (array) items
|
||||
* or map (pairs) is used as the iterm value.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
proto_item * proto_tree_add_cbor_container(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk);
|
||||
|
||||
/** Add an item representing a non-boolean, non-float control value.
|
||||
*/
|
||||
WS_DLL_PUBLIC
|
||||
proto_item * proto_tree_add_cbor_ctrl(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk);
|
||||
|
||||
WS_DLL_PUBLIC
|
||||
proto_item * proto_tree_add_cbor_boolean(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk, const gboolean *value);
|
||||
|
||||
WS_DLL_PUBLIC
|
||||
proto_item * proto_tree_add_cbor_uint64(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk, const guint64 *value);
|
||||
|
||||
WS_DLL_PUBLIC
|
||||
proto_item * proto_tree_add_cbor_int64(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk, const gint64 *value);
|
||||
|
||||
WS_DLL_PUBLIC
|
||||
proto_item * proto_tree_add_cbor_bitmask(proto_tree *tree, int hfindex, const gint ett, int *const *fields, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk, const guint64 *value);
|
||||
|
||||
WS_DLL_PUBLIC
|
||||
proto_item * proto_tree_add_cbor_tstr(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk);
|
||||
|
||||
WS_DLL_PUBLIC
|
||||
proto_item * proto_tree_add_cbor_bstr(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __WSCBOR_H__ */
|
|
@ -0,0 +1,551 @@
|
|||
/* wscbor_test.c
|
||||
* Wireshark CBOR API tests
|
||||
* Copyright 2021, Brian Sipos <brian.sipos@gmail.com>
|
||||
*
|
||||
* Wireshark - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@wireshark.org>
|
||||
* Copyright 1998 Gerald Combs
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#undef G_DISABLE_ASSERT
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "wscbor.h"
|
||||
#include <epan/wmem_scopes.h>
|
||||
#include <epan/exceptions.h>
|
||||
#include <wsutil/wmem/wmem_list.h>
|
||||
|
||||
#include <ws_diag_control.h>
|
||||
|
||||
static wmem_allocator_t *test_scope;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Raw bytes
|
||||
gint enc_len;
|
||||
const guint8 *enc;
|
||||
// Members of cbor_chunk_t
|
||||
gint head_length;
|
||||
gint data_length;
|
||||
guint8 type_major;
|
||||
guint64 head_value;
|
||||
} example_s;
|
||||
|
||||
DIAG_OFF_PEDANTIC
|
||||
example_s ex_uint = {1, (const guint8 *)"\x01", 1, 1, CBOR_TYPE_UINT, 1};
|
||||
example_s ex_nint = {1, (const guint8 *)"\x20", 1, 1, CBOR_TYPE_NEGINT, 0};
|
||||
example_s ex_bstr = {3, (const guint8 *)"\x42\x68\x69", 1, 3, CBOR_TYPE_BYTESTRING, 2};
|
||||
example_s ex_tstr = {3, (const guint8 *)"\x62\x68\x69", 1, 3, CBOR_TYPE_STRING, 2};
|
||||
example_s ex_false = {1, (const guint8 *)"\xF4", 1, 1, CBOR_TYPE_FLOAT_CTRL, CBOR_CTRL_FALSE};
|
||||
example_s ex_true = {1, (const guint8 *)"\xF5", 1, 1, CBOR_TYPE_FLOAT_CTRL, CBOR_CTRL_TRUE};
|
||||
example_s ex_null = {1, (const guint8 *)"\xF6", 1, 1, CBOR_TYPE_FLOAT_CTRL, CBOR_CTRL_NULL};
|
||||
example_s ex_undef = {1, (const guint8 *)"\xF7", 1, 1, CBOR_TYPE_FLOAT_CTRL, CBOR_CTRL_UNDEF};
|
||||
example_s ex_break = {1, (const guint8 *)"\xFF", 1, 1, CBOR_TYPE_FLOAT_CTRL, 0};
|
||||
|
||||
example_s ex_uint_overflow = {9, (const guint8 *)"\x1B\x80\x00\x00\x00\x00\x00\x00\x00", 1, 9, CBOR_TYPE_UINT, 0x8000000000000000};
|
||||
example_s ex_nint_overflow = {9, (const guint8 *)"\x3B\x80\x00\x00\x00\x00\x00\x00\x00", 1, 9, CBOR_TYPE_NEGINT, 0x8000000000000000};
|
||||
example_s ex_bstr_overflow = {11, (const guint8 *)"\x5B\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00", 1, 9, CBOR_TYPE_BYTESTRING, 0x8000000000000000};
|
||||
example_s ex_bstr_short = {2, (const guint8 *)"\x42\x68", 1, 3, CBOR_TYPE_BYTESTRING, 2};
|
||||
example_s ex_tstr_short = {2, (const guint8 *)"\x62\x68", 1, 3, CBOR_TYPE_STRING, 2};
|
||||
DIAG_ON_PEDANTIC
|
||||
|
||||
static const example_s * all_examples[] = {
|
||||
&ex_uint, &ex_nint,
|
||||
&ex_bstr, &ex_bstr_short, &ex_tstr,
|
||||
&ex_false, &ex_true, &ex_null, &ex_undef, &ex_break
|
||||
};
|
||||
|
||||
/*
|
||||
* These test are organized in order of the appearance, in wscbor.h, of
|
||||
* the basic functions that they test. This makes it easier to
|
||||
* get a quick understanding of both the testing and the organization
|
||||
* of the header.
|
||||
*/
|
||||
|
||||
/* WSCBOR TESTING FUNCTIONS (/wscbor/) */
|
||||
|
||||
static void
|
||||
wscbor_test_read_simple(void)
|
||||
{
|
||||
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
|
||||
const example_s *ex = all_examples[ex_ix];
|
||||
printf("simple #%zu\n", ex_ix);
|
||||
|
||||
tvbuff_t *tvb = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
|
||||
gint offset = 0;
|
||||
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(chunk);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
|
||||
g_assert_cmpuint(chunk->head_length, ==, ex->head_length);
|
||||
g_assert_cmpuint(chunk->data_length, ==, ex->data_length);
|
||||
g_assert_cmpuint(wmem_list_count(chunk->tags), ==, 0);
|
||||
g_assert_cmpuint(chunk->type_major, ==, ex->type_major);
|
||||
g_assert_cmpuint(chunk->head_value, ==, ex->head_value);
|
||||
|
||||
wscbor_chunk_free(chunk);
|
||||
tvb_free(tvb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wscbor_test_read_simple_tags(void)
|
||||
{
|
||||
const guint8 *const tags = (const guint8 *)"\xC1\xD8\xC8";
|
||||
tvbuff_t *tvb_tags = tvb_new_real_data(tags, 3, 3);
|
||||
|
||||
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
|
||||
const example_s *ex = all_examples[ex_ix];
|
||||
printf("simple #%zu\n", ex_ix);
|
||||
|
||||
tvbuff_t *tvb_item = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
|
||||
tvbuff_t *tvb = tvb_new_composite();
|
||||
tvb_composite_append(tvb, tvb_tags);
|
||||
tvb_composite_append(tvb, tvb_item);
|
||||
tvb_composite_finalize(tvb);
|
||||
gint offset = 0;
|
||||
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(chunk);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
|
||||
g_assert_cmpuint(chunk->head_length, ==, ex->head_length + 3);
|
||||
g_assert_cmpuint(chunk->data_length, ==, ex->data_length + 3);
|
||||
g_assert_cmpuint(wmem_list_count(chunk->tags), ==, 2);
|
||||
{
|
||||
wmem_list_frame_t *frm = wmem_list_head(chunk->tags);
|
||||
g_assert(frm);
|
||||
{
|
||||
const wscbor_tag_t *tag = wmem_list_frame_data(frm);
|
||||
g_assert_cmpuint(tag->value, ==, 1);
|
||||
}
|
||||
frm = wmem_list_frame_next(frm);
|
||||
g_assert(frm);
|
||||
{
|
||||
const wscbor_tag_t *tag = wmem_list_frame_data(frm);
|
||||
g_assert_cmpuint(tag->value, ==, 200);
|
||||
}
|
||||
frm = wmem_list_frame_next(frm);
|
||||
g_assert(!frm);
|
||||
}
|
||||
g_assert_cmpuint(chunk->type_major, ==, ex->type_major);
|
||||
g_assert_cmpuint(chunk->head_value, ==, ex->head_value);
|
||||
|
||||
wscbor_chunk_free(chunk);
|
||||
tvb_free(tvb_item);
|
||||
}
|
||||
|
||||
tvb_free(tvb_tags);
|
||||
}
|
||||
|
||||
static void
|
||||
wscbor_test_read_invalid(void)
|
||||
{
|
||||
tvbuff_t *tvb = tvb_new_real_data((const guint8 *)"\x00\x01\x02\xC1", 4, 2);
|
||||
gint offset = 2;
|
||||
|
||||
{ // last valid item
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(chunk);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
g_assert_cmpuint(chunk->type_major, ==, CBOR_TYPE_UINT);
|
||||
g_assert_cmpuint(chunk->head_value, ==, 2);
|
||||
wscbor_chunk_free(chunk);
|
||||
}
|
||||
g_assert_cmpint(offset, ==, 3);
|
||||
{ // Tag without item
|
||||
guint caught = 0;
|
||||
TRY {
|
||||
wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(FALSE);
|
||||
}
|
||||
CATCH(ReportedBoundsError) {
|
||||
caught = ReportedBoundsError;
|
||||
}
|
||||
ENDTRY;
|
||||
g_assert_cmpuint(caught, ==, ReportedBoundsError);
|
||||
}
|
||||
g_assert_cmpint(offset, ==, 4);
|
||||
{ // Read past the end
|
||||
guint caught = 0;
|
||||
TRY {
|
||||
wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(FALSE);
|
||||
}
|
||||
CATCH(ReportedBoundsError) {
|
||||
caught = ReportedBoundsError;
|
||||
}
|
||||
ENDTRY;
|
||||
g_assert_cmpuint(caught, ==, ReportedBoundsError);
|
||||
}
|
||||
g_assert_cmpint(offset, ==, 4);
|
||||
|
||||
tvb_free(tvb);
|
||||
}
|
||||
|
||||
static void
|
||||
wscbor_test_is_indefinite_break(void)
|
||||
{
|
||||
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
|
||||
const example_s *ex = all_examples[ex_ix];
|
||||
printf("simple #%zu\n", ex_ix);
|
||||
|
||||
tvbuff_t *tvb = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
|
||||
gint offset = 0;
|
||||
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(chunk);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
|
||||
// this test never modifies the chunk
|
||||
const gboolean val = wscbor_is_indefinite_break(chunk);
|
||||
if (memcmp(ex->enc, "\xFF", 1) == 0) {
|
||||
g_assert(val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
}
|
||||
else {
|
||||
g_assert(!val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
}
|
||||
|
||||
wscbor_chunk_free(chunk);
|
||||
tvb_free(tvb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wscbor_test_skip_next_item(void)
|
||||
{
|
||||
tvbuff_t *tvb = tvb_new_real_data((const guint8 *)"\x00\x01\x02\x03", 4, 4);
|
||||
gint offset = 3;
|
||||
gboolean res = wscbor_skip_next_item(test_scope, tvb, &offset);
|
||||
g_assert(!res);
|
||||
g_assert_cmpint(offset, ==, 4);
|
||||
|
||||
{ // Read past the end
|
||||
guint caught = 0;
|
||||
TRY {
|
||||
wscbor_skip_next_item(test_scope, tvb, &offset);
|
||||
g_assert(FALSE);
|
||||
}
|
||||
CATCH(ReportedBoundsError) {
|
||||
caught = ReportedBoundsError;
|
||||
}
|
||||
ENDTRY;
|
||||
g_assert_cmpuint(caught, ==, ReportedBoundsError);
|
||||
}
|
||||
g_assert_cmpint(offset, ==, 4);
|
||||
|
||||
tvb_free(tvb);
|
||||
}
|
||||
|
||||
static void
|
||||
wscbor_test_require_major_type(void)
|
||||
{
|
||||
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
|
||||
const example_s *ex = all_examples[ex_ix];
|
||||
printf("simple #%zu\n", ex_ix);
|
||||
|
||||
tvbuff_t *tvb = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
|
||||
gint offset = 0;
|
||||
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(chunk);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
|
||||
g_assert(wscbor_require_major_type(chunk, ex->type_major));
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
|
||||
// any other type
|
||||
g_assert(!wscbor_require_major_type(chunk, ex->type_major + 1));
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
|
||||
|
||||
wscbor_chunk_free(chunk);
|
||||
tvb_free(tvb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wscbor_test_require_boolean(void)
|
||||
{
|
||||
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
|
||||
const example_s *ex = all_examples[ex_ix];
|
||||
printf("simple #%zu\n", ex_ix);
|
||||
|
||||
tvbuff_t *tvb = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
|
||||
gint offset = 0;
|
||||
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(chunk);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
|
||||
const gboolean *val = wscbor_require_boolean(test_scope, chunk);
|
||||
if ((ex->type_major == CBOR_TYPE_FLOAT_CTRL)
|
||||
&& ((ex->head_value == CBOR_CTRL_FALSE) || (ex->head_value == CBOR_CTRL_TRUE))) {
|
||||
g_assert(val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
g_assert_cmpint(*val, ==, ex->head_value == CBOR_CTRL_TRUE);
|
||||
}
|
||||
else {
|
||||
g_assert(!val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
|
||||
}
|
||||
|
||||
wscbor_chunk_free(chunk);
|
||||
tvb_free(tvb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wscbor_test_require_int64(void)
|
||||
{
|
||||
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
|
||||
const example_s *ex = all_examples[ex_ix];
|
||||
printf("simple #%zu\n", ex_ix);
|
||||
|
||||
tvbuff_t *tvb = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
|
||||
gint offset = 0;
|
||||
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(chunk);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
|
||||
const gint64 *val = wscbor_require_int64(test_scope, chunk);
|
||||
if (ex->type_major == CBOR_TYPE_UINT) {
|
||||
g_assert(val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
g_assert_cmpint(*val, ==, ex->head_value);
|
||||
}
|
||||
else if (ex->type_major == CBOR_TYPE_NEGINT) {
|
||||
g_assert(val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
g_assert_cmpint(*val, ==, -1 - ex->head_value);
|
||||
}
|
||||
else {
|
||||
g_assert(!val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
|
||||
}
|
||||
|
||||
wscbor_chunk_free(chunk);
|
||||
tvb_free(tvb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wscbor_test_require_int64_overflow(void)
|
||||
{
|
||||
const example_s * examples[] = {
|
||||
&ex_uint_overflow, &ex_nint_overflow,
|
||||
};
|
||||
for (size_t ex_ix = 0; ex_ix < (sizeof(examples) / sizeof(example_s*)); ++ex_ix) {
|
||||
const example_s *ex = examples[ex_ix];
|
||||
printf("simple #%zu\n", ex_ix);
|
||||
|
||||
tvbuff_t *tvb = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
|
||||
gint offset = 0;
|
||||
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(chunk);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
g_assert_cmpuint(chunk->type_major, ==, ex->type_major);
|
||||
g_assert_cmpuint(chunk->head_value, ==, ex->head_value);
|
||||
|
||||
const gint64 *val = wscbor_require_int64(test_scope, chunk);
|
||||
if (ex->type_major == CBOR_TYPE_UINT) {
|
||||
g_assert(val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
|
||||
g_assert_cmpint(*val, ==, G_MAXINT64);
|
||||
}
|
||||
else if (ex->type_major == CBOR_TYPE_NEGINT) {
|
||||
g_assert(val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
|
||||
g_assert_cmpint(*val, ==, G_MININT64);
|
||||
}
|
||||
else {
|
||||
g_assert(!val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
|
||||
}
|
||||
|
||||
wscbor_chunk_free(chunk);
|
||||
tvb_free(tvb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wscbor_test_require_tstr(void)
|
||||
{
|
||||
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
|
||||
const example_s *ex = all_examples[ex_ix];
|
||||
printf("simple #%zu\n", ex_ix);
|
||||
|
||||
tvbuff_t *tvb = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
|
||||
gint offset = 0;
|
||||
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(chunk);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
|
||||
const char *val = wscbor_require_tstr(test_scope, tvb, chunk);
|
||||
if (ex->type_major == CBOR_TYPE_STRING) {
|
||||
g_assert(val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
// only works because this is Latin-1 text
|
||||
g_assert_cmpint(strlen(val), ==, ex->head_value);
|
||||
}
|
||||
else {
|
||||
g_assert(!val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
|
||||
}
|
||||
|
||||
wscbor_chunk_free(chunk);
|
||||
tvb_free(tvb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wscbor_test_require_tstr_short(void)
|
||||
{
|
||||
const example_s * examples[] = {
|
||||
&ex_tstr_short,
|
||||
};
|
||||
for (size_t ex_ix = 0; ex_ix < (sizeof(examples) / sizeof(example_s*)); ++ex_ix) {
|
||||
const example_s *ex = examples[ex_ix];
|
||||
printf("simple #%zu\n", ex_ix);
|
||||
|
||||
tvbuff_t *tvb = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
|
||||
gint offset = 0;
|
||||
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(chunk);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
g_assert_cmpuint(chunk->type_major, ==, ex->type_major);
|
||||
g_assert_cmpuint(chunk->head_value, ==, ex->head_value);
|
||||
|
||||
guint caught = 0;
|
||||
TRY {
|
||||
wscbor_require_tstr(test_scope, tvb, chunk);
|
||||
g_assert(FALSE);
|
||||
}
|
||||
CATCH(ReportedBoundsError) {
|
||||
caught = ReportedBoundsError;
|
||||
}
|
||||
ENDTRY;
|
||||
g_assert_cmpuint(caught, ==, ReportedBoundsError);
|
||||
|
||||
wscbor_chunk_free(chunk);
|
||||
tvb_free(tvb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wscbor_test_require_bstr(void)
|
||||
{
|
||||
for (size_t ex_ix = 0; ex_ix < (sizeof(all_examples) / sizeof(example_s*)); ++ex_ix) {
|
||||
const example_s *ex = all_examples[ex_ix];
|
||||
printf("simple #%zu\n", ex_ix);
|
||||
|
||||
tvbuff_t *tvb = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
|
||||
gint offset = 0;
|
||||
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(chunk);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
|
||||
const tvbuff_t *val = wscbor_require_bstr(tvb, chunk);
|
||||
if (ex->type_major == CBOR_TYPE_BYTESTRING) {
|
||||
g_assert(val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 0);
|
||||
g_assert_cmpint(tvb_reported_length(val), ==, ex->head_value);
|
||||
g_assert_cmpuint(tvb_captured_length(val), <=, ex->head_value);
|
||||
}
|
||||
else {
|
||||
g_assert(!val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
|
||||
}
|
||||
|
||||
wscbor_chunk_free(chunk);
|
||||
tvb_free(tvb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wscbor_test_require_bstr_overflow(void)
|
||||
{
|
||||
const example_s * examples[] = {
|
||||
&ex_bstr_overflow,
|
||||
};
|
||||
for (size_t ex_ix = 0; ex_ix < (sizeof(examples) / sizeof(example_s*)); ++ex_ix) {
|
||||
const example_s *ex = examples[ex_ix];
|
||||
printf("simple #%zu\n", ex_ix);
|
||||
|
||||
tvbuff_t *tvb = tvb_new_real_data(ex->enc, ex->enc_len, ex->enc_len);
|
||||
gint offset = 0;
|
||||
|
||||
wscbor_chunk_t *chunk = wscbor_chunk_read(test_scope, tvb, &offset);
|
||||
g_assert(chunk);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 1);
|
||||
g_assert_cmpuint(chunk->type_major, ==, ex->type_major);
|
||||
g_assert_cmpuint(chunk->head_value, ==, ex->head_value);
|
||||
|
||||
const tvbuff_t *val = wscbor_require_bstr(tvb, chunk);
|
||||
g_assert(val);
|
||||
g_assert_cmpuint(wscbor_has_errors(chunk), ==, 2);
|
||||
g_assert_cmpuint(tvb_reported_length(val), ==, G_MAXINT);
|
||||
g_assert_cmpuint(tvb_captured_length(val), ==, 2);
|
||||
|
||||
wscbor_chunk_free(chunk);
|
||||
tvb_free(tvb);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int result;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func("/wscbor/read/simple", wscbor_test_read_simple);
|
||||
g_test_add_func("/wscbor/read/simple_tags", wscbor_test_read_simple_tags);
|
||||
g_test_add_func("/wscbor/read/invalid", wscbor_test_read_invalid);
|
||||
g_test_add_func("/wscbor/is_indefinite_break", wscbor_test_is_indefinite_break);
|
||||
g_test_add_func("/wscbor/skip_next_item", wscbor_test_skip_next_item);
|
||||
g_test_add_func("/wscbor/require_major_type", wscbor_test_require_major_type);
|
||||
g_test_add_func("/wscbor/require_boolean", wscbor_test_require_boolean);
|
||||
g_test_add_func("/wscbor/require_int64", wscbor_test_require_int64);
|
||||
g_test_add_func("/wscbor/require_int64_overflow", wscbor_test_require_int64_overflow);
|
||||
g_test_add_func("/wscbor/require_tstr", wscbor_test_require_tstr);
|
||||
g_test_add_func("/wscbor/require_tstr_short", wscbor_test_require_tstr_short);
|
||||
g_test_add_func("/wscbor/require_bstr", wscbor_test_require_bstr);
|
||||
g_test_add_func("/wscbor/require_bstr_overflow", wscbor_test_require_bstr_overflow);
|
||||
wmem_init_scopes();
|
||||
|
||||
test_scope = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
|
||||
//cannot use: wscbor_init();
|
||||
result = g_test_run();
|
||||
//none needed: wscbor_cleanup();
|
||||
wmem_destroy_allocator(test_scope);
|
||||
wmem_cleanup_scopes();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
||||
*
|
||||
* Local variables:
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*
|
||||
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
||||
* :indentSize=4:tabSize=8:noTabs=true:
|
||||
*/
|
|
@ -0,0 +1,12 @@
|
|||
16(
|
||||
[
|
||||
/ protected h'a1010a' / << {
|
||||
/ alg / 1:10 / AES-CCM-16-64-128 /
|
||||
} >> ,
|
||||
/ unprotected / {
|
||||
/ iv / 5:h'89f52f65a1c580933b5261a78c'
|
||||
},
|
||||
/ ciphertext / h'5974e1b99a3a4cc09a659aa2e9e7fff161d38ce71cb45ce
|
||||
460ffb569'
|
||||
]
|
||||
)
|
Binary file not shown.
|
@ -0,0 +1,24 @@
|
|||
96(
|
||||
[
|
||||
/ protected h'a1010a' / << {
|
||||
/ alg / 1:10 / AES-CCM-16-64-128 /
|
||||
} >>,
|
||||
/ unprotected / {
|
||||
/ iv / 5:h'89f52f65a1c580933b5261a76c'
|
||||
},
|
||||
/ ciphertext / h'753548a19b1307084ca7b2056924ed95f2e3b17006dfe93
|
||||
1b687b847',
|
||||
/ recipients / [
|
||||
[
|
||||
/ protected h'a10129' / << {
|
||||
/ alg / 1:-10
|
||||
} >>,
|
||||
/ unprotected / {
|
||||
/ salt / -20:'aabbccddeeffgghh',
|
||||
/ kid / 4:'our-secret'
|
||||
},
|
||||
/ ciphertext / h''
|
||||
]
|
||||
]
|
||||
]
|
||||
)
|
Binary file not shown.
|
@ -0,0 +1,19 @@
|
|||
[
|
||||
{
|
||||
1:2,
|
||||
2:'meriadoc.brandybuck@buckland.example',
|
||||
-1:1,
|
||||
-2:h'65eda5a12577c2bae829437fe338701a10aaa375e1bb5b5de108de439c0
|
||||
8551d',
|
||||
-3:h'1e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd008
|
||||
4d19c',
|
||||
-4:h'aff907c99f9ad3aae6c4cdf21122bce2bd68b5283e6907154ad911840fa
|
||||
208cf'
|
||||
},
|
||||
{
|
||||
1:4,
|
||||
2:'018c0ae5-4d9b-471b-bfd6-eef314bc7037',
|
||||
-1:h'849b57219dae48de646d07dbb533566e976686457c1491be3a76dcea6c4
|
||||
27188'
|
||||
}
|
||||
]
|
Binary file not shown.
|
@ -0,0 +1,10 @@
|
|||
17(
|
||||
[
|
||||
/ protected h'a1010f' / << {
|
||||
/ alg / 1:15 / AES-CBC-MAC-256//64 /
|
||||
} >> ,
|
||||
/ unprotected / {},
|
||||
/ payload / 'This is the content.',
|
||||
/ tag / h'726043745027214f'
|
||||
]
|
||||
)
|
Binary file not shown.
|
@ -0,0 +1,40 @@
|
|||
97(
|
||||
[
|
||||
/ protected h'a10105' / << {
|
||||
/ alg / 1:5 / HMAC 256//256 /
|
||||
} >> ,
|
||||
/ unprotected / {},
|
||||
/ payload / 'This is the content.',
|
||||
/ tag / h'bf48235e809b5c42e995f2b7d5fa13620e7ed834e337f6aa43df16
|
||||
1e49e9323e',
|
||||
/ recipients / [
|
||||
[
|
||||
/ protected h'a101381c' / << {
|
||||
/ alg / 1:-29 / ECHD-ES+A128KW /
|
||||
} >> ,
|
||||
/ unprotected / {
|
||||
/ ephemeral / -1:{
|
||||
/ kty / 1:2,
|
||||
/ crv / -1:3,
|
||||
/ x / -2:h'0043b12669acac3fd27898ffba0bcd2e6c366d53bc4db
|
||||
71f909a759304acfb5e18cdc7ba0b13ff8c7636271a6924b1ac63c02688075b55ef2
|
||||
d613574e7dc242f79c3',
|
||||
/ y / -3:true
|
||||
},
|
||||
/ kid / 4:'bilbo.baggins@hobbiton.example'
|
||||
},
|
||||
/ ciphertext / h'339bc4f79984cdc6b3e6ce5f315a4c7d2b0ac466fce
|
||||
a69e8c07dfbca5bb1f661bc5f8e0df9e3eff5'
|
||||
],
|
||||
[
|
||||
/ protected / h'',
|
||||
/ unprotected / {
|
||||
/ alg / 1:-5 / A256KW /,
|
||||
/ kid / 4:'018c0ae5-4d9b-471b-bfd6-eef314bc7037'
|
||||
},
|
||||
/ ciphertext / h'0b2c7cfce04e98276342d6476a7723c090dfdd15f9a
|
||||
518e7736549e998370695e6d6a83b4ae507bb'
|
||||
]
|
||||
]
|
||||
]
|
||||
)
|
Binary file not shown.
|
@ -0,0 +1,14 @@
|
|||
18(
|
||||
[
|
||||
/ protected h'a10126' / << {
|
||||
/ alg / 1:-7 / ECDSA 256 /
|
||||
} >>,
|
||||
/ unprotected / {
|
||||
/ kid / 4:'11'
|
||||
},
|
||||
/ payload / 'This is the content.',
|
||||
/ signature / h'8eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4
|
||||
d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5
|
||||
a4c345cacb36'
|
||||
]
|
||||
)
|
Binary file not shown.
|
@ -0,0 +1,39 @@
|
|||
98(
|
||||
[
|
||||
/ protected h'a2687265736572766564f40281687265736572766564' /
|
||||
<< {
|
||||
"reserved":false,
|
||||
/ crit / 2:[
|
||||
"reserved"
|
||||
]
|
||||
} >>,
|
||||
/ unprotected / {},
|
||||
/ payload / 'This is the content.',
|
||||
/ signatures / [
|
||||
[
|
||||
/ protected h'a10126' / << {
|
||||
/ alg / 1:-7 / ECDSA 256 /
|
||||
} >>,
|
||||
/ unprotected / {
|
||||
/ kid / 4:'11'
|
||||
},
|
||||
/ signature / h'e2aeafd40d69d19dfe6e52077c5d7ff4e408282cbefb
|
||||
5d06cbf414af2e19d982ac45ac98b8544c908b4507de1e90b717c3d34816fe926a2b
|
||||
98f53afd2fa0f30a'
|
||||
],
|
||||
[
|
||||
/ protected h'a1013823' / << {
|
||||
/ alg / 1:-36 / ECDSA 521 /
|
||||
} >> ,
|
||||
/ unprotected / {
|
||||
/ kid / 4:'bilbo.baggins@hobbiton.example'
|
||||
},
|
||||
/ signature / h'00a2d28a7c2bdb1587877420f65adf7d0b9a06635dd1
|
||||
de64bb62974c863f0b160dd2163734034e6ac003b01e8705524c5c4ca479a952f024
|
||||
7ee8cb0b4fb7397ba08d009e0c8bf482270cc5771aa143966e5a469a09f613488030
|
||||
c5b07ec6d722e3835adb5b2d8c44e95ffb13877dd2582866883535de3bb03d01753f
|
||||
83ab87bb4f7a0297'
|
||||
]
|
||||
]
|
||||
]
|
||||
)
|
Binary file not shown.
|
@ -14,6 +14,63 @@ import unittest
|
|||
import fixtures
|
||||
import sys
|
||||
|
||||
@fixtures.mark_usefixtures('test_env')
|
||||
@fixtures.uses_fixtures
|
||||
class case_dissect_cose(subprocesstest.SubprocessTestCase):
|
||||
'''
|
||||
These test captures were generated from the COSE example files with command:
|
||||
for FN in test/captures/cose*.cbordiag; do python3 tools/generate_cbor_pcap.py --content-type 'application/cose' --infile $FN --outfile ${FN%.cbordiag}.pcap; done
|
||||
'''
|
||||
def test_cose_sign_tagged(self, cmd_tshark, features, dirs, capture_file):
|
||||
self.assertRun((cmd_tshark,
|
||||
'-r', capture_file('cose_sign_tagged.pcap'),
|
||||
'-Tfields', '-ecose.msg.signature',
|
||||
))
|
||||
self.assertTrue(self.grepOutput('e2aeafd40d69d19dfe6e52077c5d7ff4e408282cbefb5d06cbf414af2e19d982ac45ac98b8544c908b4507de1e90b717c3d34816fe926a2b98f53afd2fa0f30a'))
|
||||
|
||||
def test_cose_sign1_tagged(self, cmd_tshark, features, dirs, capture_file):
|
||||
self.assertRun((cmd_tshark,
|
||||
'-r', capture_file('cose_sign1_tagged.pcap'),
|
||||
'-Tfields', '-ecose.msg.signature',
|
||||
))
|
||||
self.assertTrue(self.grepOutput('8eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5a4c345cacb36'))
|
||||
|
||||
def test_cose_encrypt_tagged(self, cmd_tshark, features, dirs, capture_file):
|
||||
self.assertRun((cmd_tshark,
|
||||
'-r', capture_file('cose_encrypt_tagged.pcap'),
|
||||
'-Tfields', '-ecose.kid',
|
||||
))
|
||||
self.assertTrue(self.grepOutput('6f75722d736563726574'))
|
||||
|
||||
def test_cose_encrypt0_tagged(self, cmd_tshark, features, dirs, capture_file):
|
||||
self.assertRun((cmd_tshark,
|
||||
'-r', capture_file('cose_encrypt0_tagged.pcap'),
|
||||
'-Tfields', '-ecose.iv',
|
||||
))
|
||||
self.assertTrue(self.grepOutput('89f52f65a1c580933b5261a78c'))
|
||||
|
||||
def test_cose_mac_tagged(self, cmd_tshark, features, dirs, capture_file):
|
||||
self.assertRun((cmd_tshark,
|
||||
'-r', capture_file('cose_mac_tagged.pcap'),
|
||||
'-Tfields', '-ecose.kid',
|
||||
))
|
||||
self.assertTrue(self.grepOutput('30313863306165352d346439622d343731622d626664362d656566333134626337303337'))
|
||||
|
||||
def test_cose_mac0_tagged(self, cmd_tshark, features, dirs, capture_file):
|
||||
self.assertRun((cmd_tshark,
|
||||
'-r', capture_file('cose_mac0_tagged.pcap'),
|
||||
'-Tfields', '-ecose.msg.mac_tag',
|
||||
))
|
||||
self.assertTrue(self.grepOutput('726043745027214f'))
|
||||
|
||||
def test_cose_keyset(self, cmd_tshark, features, dirs, capture_file):
|
||||
self.assertRun((cmd_tshark,
|
||||
'-r', capture_file('cose_keyset.pcap'),
|
||||
'-Tfields', '-ecose.key.k',
|
||||
))
|
||||
self.assertTrue(self.grepOutput('849b57219dae48de646d07dbb533566e976686457c1491be3a76dcea6c427188'))
|
||||
|
||||
|
||||
@fixtures.mark_usefixtures('test_env')
|
||||
@fixtures.uses_fixtures
|
||||
class case_dissect_grpc(subprocesstest.SubprocessTestCase):
|
||||
|
|
|
@ -41,6 +41,10 @@ class case_unittests(subprocesstest.SubprocessTestCase):
|
|||
'--verbose'
|
||||
), env=base_env)
|
||||
|
||||
def test_unit_wscbor_test(self, program, base_env):
|
||||
'''wscbor_test'''
|
||||
self.assertRun(program('wscbor_test'), env=base_env)
|
||||
|
||||
def test_unit_wsutil(self, program, base_env):
|
||||
'''wsutil unit tests'''
|
||||
self.assertRun((program('test_wsutil'),
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env python3
|
||||
'''
|
||||
Convert a CBOR diagnostic notation file into an HTTP request
|
||||
for the encoded cbor.
|
||||
This allows straightforward test and debugging of simple pcap files.
|
||||
|
||||
Copyright 2021 Brian Sipos <brian.sipos@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
'''
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from io import BytesIO
|
||||
import scapy
|
||||
from scapy.layers.l2 import Ether
|
||||
from scapy.layers.inet import IP, TCP
|
||||
from scapy.layers.http import HTTP, HTTPRequest
|
||||
from scapy.packet import Raw
|
||||
from scapy.utils import wrpcap
|
||||
from subprocess import check_output
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('--content-type', default='application/cbor',
|
||||
help='The request content-type header')
|
||||
parser.add_argument('--infile', default='-',
|
||||
help='The diagnostic text input file, or "-" for stdin')
|
||||
parser.add_argument('--outfile', default='-',
|
||||
help='The PCAP output file, or "-" for stdout')
|
||||
args = parser.parse_args()
|
||||
|
||||
# First get the CBOR data itself
|
||||
infile_name = args.infile.strip()
|
||||
if infile_name != '-':
|
||||
infile = open(infile_name, 'r')
|
||||
else:
|
||||
infile = sys.stdin
|
||||
|
||||
cbordata = check_output('diag2cbor.rb', stdin=infile)
|
||||
|
||||
# Now synthesize an HTTP request with that body
|
||||
req = HTTPRequest(
|
||||
Method='POST',
|
||||
Host='example.com',
|
||||
User_Agent='scapy',
|
||||
Content_Type=args.content_type,
|
||||
Content_Length=str(len(cbordata)),
|
||||
) / Raw(cbordata)
|
||||
|
||||
# Write the request directly into pcap
|
||||
outfile_name = args.outfile.strip()
|
||||
if outfile_name != '-':
|
||||
outfile = open(outfile_name, 'wb')
|
||||
else:
|
||||
outfile = sys.stdout.buffer
|
||||
|
||||
pkt = Ether()/IP()/TCP()/HTTP()/req
|
||||
wrpcap(outfile, pkt)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
Loading…
Reference in New Issue