--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_hpack_decode.cc author Maya Dagon <mdagon@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http2_hpack_decode.h"
+
+#include "http2_enum.h"
+
+using namespace Http2Enums;
+
+static const uint8_t VAL_MASK = 0x7F;
+static const uint8_t FLAG_BIT = 0x80;
+
+Http2HpackIntDecode::Http2HpackIntDecode(uint8_t prefix, Http2EventGen* events,
+ Http2Infractions* infractions) : prefix_mask(((uint16_t)1 << prefix) - 1), events(events),
+ infractions(infractions)
+{
+ assert ((0 < prefix) && (prefix < 9));
+}
+
+bool Http2HpackIntDecode::translate(const Field& msg, int32_t& bytes_consumed, uint64_t& result)
+{
+ bytes_consumed = 0;
+ result = 0;
+
+ if (bytes_consumed >= msg.length())
+ {
+ *infractions += INF_INT_EMPTY_BUFF;
+ events->create_event(EVENT_INT_DECODE_FAILURE);
+ return false;
+ }
+
+ const uint8_t* buff = msg.start();
+ const uint8_t prefix_val = buff[bytes_consumed++] & prefix_mask;
+
+ if (prefix_val < prefix_mask)
+ {
+ result = prefix_val;
+ return true;
+ }
+
+ uint8_t byte = 0;
+ for (uint8_t multiplier = 0; multiplier < 64; multiplier += 7)
+ {
+ if (bytes_consumed >= msg.length())
+ {
+ *infractions += INF_INT_MISSING_BYTES;
+ events->create_event(EVENT_INT_DECODE_FAILURE);
+ return false;
+ }
+ byte = buff[bytes_consumed++];
+
+ // For multiplier == 63, do overflow checks
+ if (multiplier == 63)
+ {
+ if (((byte & FLAG_BIT) != 0) || ((byte & VAL_MASK) > 1) ||
+ ((result + ((uint64_t)(byte & VAL_MASK) << multiplier) + prefix_mask) < result))
+ {
+ *infractions += INF_INT_OVERFLOW;
+ events->create_event(EVENT_INT_DECODE_FAILURE);
+ return false;
+ }
+ }
+
+ result += (uint64_t)(byte & VAL_MASK) << multiplier;
+
+ if ((byte & FLAG_BIT) == 0)
+ break;
+ }
+
+ // Alert on leading 0s, allow for value 2^N-1
+ if (((byte & VAL_MASK) == 0) && (bytes_consumed != 2))
+ {
+ *infractions += INF_INT_LEADING_ZEROS;
+ events->create_event(EVENT_INT_LEADING_ZEROS);
+ }
+
+ result += prefix_mask;
+
+ return true;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// http2_hpack_decode_test.cc author Maya Dagon <mdagon@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "../http2_enum.h"
+#include "../http2_hpack_decode.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+namespace snort
+{
+// Stubs whose sole purpose is to make the test code link
+int DetectionEngine::queue_event(unsigned int, unsigned int, Actions::Type) { return 0; }
+}
+
+using namespace Http2Enums;
+
+
+//
+// The following tests should result in a successful decode, no infractions/events
+//
+TEST_GROUP(http2_hpack_decode_success)
+{
+ Http2EventGen events;
+ Http2Infractions inf;
+ Http2HpackIntDecode* const decode = new Http2HpackIntDecode(5, &events, &inf);
+
+ void teardown() override
+ {
+ CHECK(inf.none_found() == true);
+ CHECK(events.none_found() == true);
+ delete decode;
+ }
+};
+
+TEST(http2_hpack_decode_success, 10_using_5_bits)
+{
+ // prepare field to decode - example from RFC 7541 c.1.1
+ uint8_t buf = 10;
+ Field f(1, &buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = decode->translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == true);
+ CHECK(res == 10);
+ CHECK(bytes_processed == 1);
+}
+
+TEST(http2_hpack_decode_success, 10_using_5_bits_wtail)
+{
+ // prepare field to decode - same as above with an extra byte as leftover
+ uint8_t buf[2] = { 10, 0xff };
+ Field f(2, buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = decode->translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == true);
+ CHECK(res == 10);
+ CHECK(bytes_processed == 1);
+}
+
+TEST(http2_hpack_decode_success, 1337_using_5_bits)
+{
+ // prepare field to decode - example from RFC 7541 c.1.2
+ uint8_t buf[3] = { 31, 0x9a, 10 };
+ Field f(3, buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = decode->translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == true);
+ CHECK(res == 1337);
+ CHECK(bytes_processed == 3);
+}
+
+TEST(http2_hpack_decode_success, 42_using_8_bits)
+{
+ // prepare decode object
+ Http2HpackIntDecode decode_8(8, &events, &inf);
+ // prepare field to decode - example from RFC 7541 c.1.3
+ uint8_t buf = 42;
+ Field f(1, &buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = decode_8.translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == true);
+ CHECK(res == 42);
+ CHECK(bytes_processed == 1);
+}
+
+TEST(http2_hpack_decode_success, max_val_using_5_bit)
+{
+ // prepare field to decode - 2^64-1
+ uint8_t buf[11] = { 31, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1};
+ Field f(11, buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = decode->translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == true);
+ CHECK(res == 0xFFFFFFFFFFFFFFFF);
+ CHECK(bytes_processed == 11);
+}
+
+TEST(http2_hpack_decode_success, 31_using_5_bits)
+{
+ // prepare field to decode - 2^N -1
+ uint8_t buf[2] = {31, 0};
+ Field f(2, buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = decode->translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == true);
+ CHECK(res == 31);
+ CHECK(bytes_processed == 2);
+}
+
+TEST(http2_hpack_decode_success, 0_using_5_bits)
+{
+ // prepare field to decode - 0 using 5 bits
+ uint8_t buf = 0;
+ Field f(1, &buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = decode->translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == true);
+ CHECK(res == 0);
+ CHECK(bytes_processed == 1);
+}
+
+
+//
+// The following tests should result in a failure and set infractions/events
+//
+TEST_GROUP(http2_hpack_decode_failure)
+{
+};
+
+TEST(http2_hpack_decode_failure, 0_len_field)
+{
+ // prepare decode object
+ Http2EventGen local_events;
+ Http2Infractions local_inf;
+ Http2HpackIntDecode decode_8(8, &local_events, &local_inf);
+ // prepare field to decode - use field length 0
+ uint8_t buf = 42;
+ Field f(0, &buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = decode_8.translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == false);
+ CHECK(bytes_processed == 0);
+ CHECK(local_inf.get_raw() == (1<<INF_INT_EMPTY_BUFF));
+ CHECK(local_events.get_raw() == (1<<(EVENT_INT_DECODE_FAILURE-1)));
+}
+
+TEST(http2_hpack_decode_failure, too_short)
+{
+ // prepare decode object
+ Http2EventGen local_events;
+ Http2Infractions local_inf;
+ Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ // prepare field to decode - buffer ends before decode finished
+ uint8_t buf[3] = { 31, 0x9a, 10 };
+ Field f(2, buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = local_decode.translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == false);
+ CHECK(bytes_processed == 2);
+ CHECK(local_inf.get_raw() == (1<<INF_INT_MISSING_BYTES));
+ CHECK(local_events.get_raw() == (1<<(EVENT_INT_DECODE_FAILURE-1)));
+}
+
+TEST(http2_hpack_decode_failure, multiplier_bigger_than_63)
+{
+ // prepare decode object
+ Http2EventGen local_events;
+ Http2Infractions local_inf;
+ Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ // prepare field to decode - multiplier > 63
+ uint8_t buf[13] = { 31, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 1 };
+ Field f(13, buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = local_decode.translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == false);
+ CHECK(bytes_processed == 11);
+ CHECK(local_inf.get_raw() == (1<<INF_INT_OVERFLOW));
+ CHECK(local_events.get_raw() == (1<<(EVENT_INT_DECODE_FAILURE-1)));
+}
+
+TEST(http2_hpack_decode_failure, add_val_overflow)
+{
+ // prepare decode object
+ Http2EventGen local_events;
+ Http2Infractions local_inf;
+ Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ // prepare field to decode - value to add itself is already creating overflow
+ uint8_t buf[12] = { 31, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF, 1 };
+ Field f(12, buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = local_decode.translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == false);
+ CHECK(bytes_processed == 11);
+ CHECK(local_inf.get_raw() == (1<<INF_INT_OVERFLOW));
+ CHECK(local_events.get_raw() == (1<<(EVENT_INT_DECODE_FAILURE-1)));
+}
+
+TEST(http2_hpack_decode_failure, add_val_overflow2)
+{
+ // prepare decode object
+ Http2EventGen local_events;
+ Http2Infractions local_inf;
+ Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ // prepare field to decode - adding value to result kept so far creates overflow
+ uint8_t buf[11] = { 31, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1};
+ Field f(11, buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = local_decode.translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == false);
+ CHECK(bytes_processed == 11);
+ CHECK(local_inf.get_raw() == (1<<INF_INT_OVERFLOW));
+ CHECK(local_events.get_raw() == (1<<(EVENT_INT_DECODE_FAILURE-1)));
+}
+
+TEST(http2_hpack_decode_failure, 2_64_using_5_bit)
+{
+ // prepare decode object
+ Http2EventGen local_events;
+ Http2Infractions local_inf;
+ Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ // prepare field to decode - 2^64
+ uint8_t buf[11] = { 31, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1};
+ Field f(11, buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = local_decode.translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == false);
+ CHECK(bytes_processed == 11);
+ CHECK(local_inf.get_raw() == (1<<INF_INT_OVERFLOW));
+ CHECK(local_events.get_raw() == (1<<(EVENT_INT_DECODE_FAILURE-1)));
+}
+
+
+//
+// The following tests should result in a successful decode and set
+// leading zeros infraction and event
+//
+TEST_GROUP(http2_hpack_decode_leading_zeros)
+{
+};
+
+TEST(http2_hpack_decode_leading_zeros, leading_zeros)
+{
+ // prepare decode object
+ Http2EventGen local_events;
+ Http2Infractions local_inf;
+ Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ // prepare field to decode - MSB is zero
+ uint8_t buf[3] = { 31, 0x80, 0};
+ Field f(3, buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = local_decode.translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == true);
+ CHECK(res == 31);
+ CHECK(bytes_processed == 3);
+ CHECK(local_inf.get_raw() == (1<<INF_INT_LEADING_ZEROS));
+ CHECK(local_events.get_raw() == (1<<(EVENT_INT_LEADING_ZEROS-1)));
+}
+
+TEST(http2_hpack_decode_leading_zeros, leading_0_byte_11)
+{
+ // prepare decode object
+ Http2EventGen local_events;
+ Http2Infractions local_inf;
+ Http2HpackIntDecode local_decode(5, &local_events, &local_inf);
+ // prepare field to decode - multiplier 63 doesn't create overflow, should alert on
+ // leading 0
+ uint8_t buf[11] = { 31, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0};
+ Field f(11, buf, false);
+ // decode
+ int32_t bytes_processed = 0;
+ uint64_t res = 0;
+ bool success = local_decode.translate(f, bytes_processed, res);
+ // check results
+ CHECK(success == true);
+ CHECK(res == 0x7FFFFFFFFFFFFFFF);
+ CHECK(bytes_processed == 11);
+ CHECK(local_inf.get_raw() == (1<<INF_INT_LEADING_ZEROS));
+ CHECK(local_events.get_raw() == (1<<(EVENT_INT_LEADING_ZEROS-1)));
+}
+
+int main(int argc, char** argv)
+{
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}