aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Scull <ascull@google.com>2021-05-17 10:53:49 +0000
committerCQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>2021-10-19 08:32:53 +0000
commit87d91de9dc9faadfcba6a177fa6bb64a1b8efa60 (patch)
treea362d4fd6b7de16f5ec91b61b8733ac2f55ebd1c
parent8ff2adfdf6996ca5b387758da646646c60d2d9c7 (diff)
downloadopen-dice-87d91de9dc9faadfcba6a177fa6bb64a1b8efa60.tar.gz
Add CBOR reading utilities
The reading is very simplistic, parsing individual tokens. The larger structure of the data would be interpreted by the client. There is a function to skip over a more complex items but it doesn't extract any of the data and just moves past it in the input buffer. Skipping supports a limited depth of nesting and fails if the nesting goes too deep. A fuzzer is included. As are a set of tests that no doubt could be expanded upon. Change-Id: I251e34aea69eba18a81edc42ab34a54a6a04b45b Reviewed-on: https://pigweed-review.googlesource.com/c/open-dice/+/65401 Reviewed-by: Darren Krahn <dkrahn@google.com> Commit-Queue: Andrew Scull <ascull@google.com> Pigweed-Auto-Submit: Andrew Scull <ascull@google.com>
-rw-r--r--BUILD.gn23
-rw-r--r--include/dice/cbor_reader.h88
-rw-r--r--src/cbor_reader.c267
-rw-r--r--src/cbor_reader_fuzzer.cc64
-rw-r--r--src/cbor_reader_test.cc372
5 files changed, 814 insertions, 0 deletions
diff --git a/BUILD.gn b/BUILD.gn
index 917b4e8..af1a1fc 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -32,6 +32,13 @@ pw_source_set("cbor_writer") {
sources = [ "src/cbor_writer.c" ]
}
+pw_source_set("cbor_reader") {
+ public = [
+ "include/dice/cbor_reader.h",
+ ]
+ sources = [ "src/cbor_reader.c" ]
+}
+
config("standalone_ops_config") {
include_dirs = [ "//include/dice/config/standalone" ]
}
@@ -157,6 +164,13 @@ pw_test("cbor_writer_test") {
]
}
+pw_test("cbor_reader_test") {
+ sources = [ "src/cbor_reader_test.cc" ]
+ deps = [
+ ":cbor_reader",
+ ]
+}
+
pw_executable("cbor_writer_fuzzer") {
sources = [ "src/cbor_writer_fuzzer.cc" ]
deps = [
@@ -164,6 +178,13 @@ pw_executable("cbor_writer_fuzzer") {
]
}
+pw_executable("cbor_reader_fuzzer") {
+ sources = [ "src/cbor_reader_fuzzer.cc" ]
+ deps = [
+ ":cbor_reader",
+ ]
+}
+
pw_test("dice_test") {
sources = [ "src/dice_test.cc" ]
deps = [
@@ -276,6 +297,7 @@ pw_test_group("tests") {
tests = [
":boringssl_ops_test",
":cbor_cert_op_test",
+ ":cbor_reader_test",
":cbor_writer_test",
":dice_test",
":mbedtls_ops_test",
@@ -288,6 +310,7 @@ group("fuzzers") {
deps = [
":boringssl_ops_fuzzer",
":cbor_cert_op_fuzzer",
+ ":cbor_reader_fuzzer",
":cbor_writer_fuzzer",
":mbedtls_ops_fuzzer",
":template_cbor_cert_op_fuzzer",
diff --git a/include/dice/cbor_reader.h b/include/dice/cbor_reader.h
new file mode 100644
index 0000000..5086d9e
--- /dev/null
+++ b/include/dice/cbor_reader.h
@@ -0,0 +1,88 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#ifndef DICE_CBOR_READER_H_
+#define DICE_CBOR_READER_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct CborIn {
+ const uint8_t* buffer;
+ size_t buffer_size;
+ size_t cursor;
+};
+
+enum CborReadResult {
+ CBOR_READ_RESULT_OK,
+ // The end of the input was reached before the token was fully read.
+ CBOR_READ_RESULT_END,
+ // A malformed or unsupported token was found.
+ CBOR_READ_RESULT_MALFORMED,
+ // The requested token was not found.
+ CBOR_READ_RESULT_NOT_FOUND,
+};
+
+// Initializes an input stream for reading CBOR tokens.
+static inline void CborInInit(const uint8_t* buffer, size_t buffer_size,
+ struct CborIn* in) {
+ in->buffer = buffer;
+ in->buffer_size = buffer_size;
+ in->cursor = 0;
+}
+
+// Returns the number of bytes that have been read from the input.
+static inline size_t CborInOffset(const struct CborIn* in) {
+ return in->cursor;
+}
+
+// Returns whether the input stream has been fully consumed.
+static inline bool CborInAtEnd(const struct CborIn* in) {
+ return in->cursor == in->buffer_size;
+}
+
+// These functions read simple CBOR tokens from the input stream. Interpreting
+// the greater structure of the data left to the caller and it is expected that
+// these functions are just being used to validate and extract data from a known
+// structure.
+enum CborReadResult CborReadInt(struct CborIn* in, int64_t* val);
+enum CborReadResult CborReadUint(struct CborIn* in, uint64_t* val);
+enum CborReadResult CborReadBstr(struct CborIn* in, size_t* data_size,
+ const uint8_t** data);
+enum CborReadResult CborReadTstr(struct CborIn* in, size_t* size,
+ const char** str);
+enum CborReadResult CborReadArray(struct CborIn* in, size_t* num_elements);
+enum CborReadResult CborReadMap(struct CborIn* in, size_t* num_pairs);
+enum CborReadResult CborReadFalse(struct CborIn* in);
+enum CborReadResult CborReadTrue(struct CborIn* in);
+enum CborReadResult CborReadNull(struct CborIn* in);
+
+// Skips over the next CBOR item in the input. The item may contain nested
+// items, in the case of an array or map, and this function will attempt to
+// descend and skip all nested items in order to skip the parent item. There is
+// a limit on the level of nesting, after which this function will fail with
+// CBOR_READ_RESULT_MALFORMED.
+#define CBOR_READ_SKIP_STACK_SIZE 10
+enum CborReadResult CborReadSkip(struct CborIn* in);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // DICE_CBOR_READER_H_
diff --git a/src/cbor_reader.c b/src/cbor_reader.c
new file mode 100644
index 0000000..3b0b343
--- /dev/null
+++ b/src/cbor_reader.c
@@ -0,0 +1,267 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "dice/cbor_reader.h"
+
+enum CborType {
+ CBOR_TYPE_UINT = 0,
+ CBOR_TYPE_NINT = 1,
+ CBOR_TYPE_BSTR = 2,
+ CBOR_TYPE_TSTR = 3,
+ CBOR_TYPE_ARRAY = 4,
+ CBOR_TYPE_MAP = 5,
+ CBOR_TYPE_TAG_NOT_SUPPORTED = 6,
+ CBOR_TYPE_SIMPLE = 7,
+};
+
+static bool CborReadWouldOverflow(size_t size, struct CborIn* in) {
+ return size > SIZE_MAX - in->cursor || in->cursor + size > in->buffer_size;
+}
+
+static enum CborReadResult CborPeekIntialValueAndArgument(struct CborIn* in,
+ uint8_t* size,
+ enum CborType* type,
+ uint64_t* val) {
+ uint8_t initial_byte;
+ uint8_t additional_information;
+ uint64_t value;
+ uint8_t bytes = 1;
+ if (CborInAtEnd(in)) {
+ return CBOR_READ_RESULT_END;
+ }
+ initial_byte = in->buffer[in->cursor];
+ *type = initial_byte >> 5;
+ additional_information = initial_byte & 0x1f;
+ if (additional_information <= 23) {
+ value = additional_information;
+ } else if (additional_information <= 27) {
+ bytes += 1 << (additional_information - 24);
+ if (CborReadWouldOverflow(bytes, in)) {
+ return CBOR_READ_RESULT_END;
+ }
+ value = 0;
+ if (bytes == 2) {
+ value |= in->buffer[in->cursor + 1];
+ } else if (bytes == 3) {
+ value |= (uint16_t)in->buffer[in->cursor + 1] << 8;
+ value |= (uint16_t)in->buffer[in->cursor + 2];
+ } else if (bytes == 5) {
+ value |= (uint32_t)in->buffer[in->cursor + 1] << 24;
+ value |= (uint32_t)in->buffer[in->cursor + 2] << 16;
+ value |= (uint32_t)in->buffer[in->cursor + 3] << 8;
+ value |= (uint32_t)in->buffer[in->cursor + 4];
+ } else if (bytes == 9) {
+ value |= (uint64_t)in->buffer[in->cursor + 1] << 56;
+ value |= (uint64_t)in->buffer[in->cursor + 2] << 48;
+ value |= (uint64_t)in->buffer[in->cursor + 3] << 40;
+ value |= (uint64_t)in->buffer[in->cursor + 4] << 32;
+ value |= (uint64_t)in->buffer[in->cursor + 5] << 24;
+ value |= (uint64_t)in->buffer[in->cursor + 6] << 16;
+ value |= (uint64_t)in->buffer[in->cursor + 7] << 8;
+ value |= (uint64_t)in->buffer[in->cursor + 8];
+ }
+ } else {
+ // Indefinite lengths and reserved values are not supported.
+ return CBOR_READ_RESULT_MALFORMED;
+ }
+ *val = value;
+ *size = bytes;
+ return CBOR_READ_RESULT_OK;
+}
+
+static enum CborReadResult CborReadSize(struct CborIn* in, enum CborType type,
+ size_t* size) {
+ uint8_t bytes;
+ enum CborType in_type;
+ uint64_t raw;
+ enum CborReadResult res =
+ CborPeekIntialValueAndArgument(in, &bytes, &in_type, &raw);
+ if (res != CBOR_READ_RESULT_OK) {
+ return res;
+ }
+ if (in_type != type) {
+ return CBOR_READ_RESULT_NOT_FOUND;
+ }
+ if (raw > SIZE_MAX) {
+ return CBOR_READ_RESULT_MALFORMED;
+ }
+ *size = raw;
+ in->cursor += bytes;
+ return CBOR_READ_RESULT_OK;
+}
+
+static enum CborReadResult CborReadStr(struct CborIn* in, enum CborType type,
+ size_t* data_size,
+ const uint8_t** data) {
+ size_t size;
+ struct CborIn peeker = *in;
+ enum CborReadResult res = CborReadSize(&peeker, type, &size);
+ if (res != CBOR_READ_RESULT_OK) {
+ return res;
+ }
+ if (CborReadWouldOverflow(size, &peeker)) {
+ return CBOR_READ_RESULT_END;
+ }
+ *data_size = size;
+ *data = &in->buffer[peeker.cursor];
+ in->cursor = peeker.cursor + size;
+ return CBOR_READ_RESULT_OK;
+}
+
+static enum CborReadResult CborReadSimple(struct CborIn* in, uint8_t val) {
+ uint8_t bytes;
+ enum CborType type;
+ uint64_t raw;
+ enum CborReadResult res =
+ CborPeekIntialValueAndArgument(in, &bytes, &type, &raw);
+ if (res != CBOR_READ_RESULT_OK) {
+ return res;
+ }
+ if (type != CBOR_TYPE_SIMPLE || raw != val) {
+ return CBOR_READ_RESULT_NOT_FOUND;
+ }
+ in->cursor += bytes;
+ return CBOR_READ_RESULT_OK;
+}
+
+enum CborReadResult CborReadInt(struct CborIn* in, int64_t* val) {
+ uint8_t bytes;
+ enum CborType type;
+ uint64_t raw;
+ enum CborReadResult res =
+ CborPeekIntialValueAndArgument(in, &bytes, &type, &raw);
+ if (res != CBOR_READ_RESULT_OK) {
+ return res;
+ }
+ if (type != CBOR_TYPE_UINT && type != CBOR_TYPE_NINT) {
+ return CBOR_READ_RESULT_NOT_FOUND;
+ }
+ if (raw > INT64_MAX) {
+ return CBOR_READ_RESULT_MALFORMED;
+ }
+ *val = (type == CBOR_TYPE_NINT) ? (-1 - (int64_t)raw) : (int64_t)raw;
+ in->cursor += bytes;
+ return CBOR_READ_RESULT_OK;
+}
+
+enum CborReadResult CborReadUint(struct CborIn* in, uint64_t* val) {
+ uint8_t bytes;
+ enum CborType type;
+ enum CborReadResult res =
+ CborPeekIntialValueAndArgument(in, &bytes, &type, val);
+ if (res != CBOR_READ_RESULT_OK) {
+ return res;
+ }
+ if (type != CBOR_TYPE_UINT) {
+ return CBOR_READ_RESULT_NOT_FOUND;
+ }
+ in->cursor += bytes;
+ return CBOR_READ_RESULT_OK;
+}
+
+enum CborReadResult CborReadBstr(struct CborIn* in, size_t* data_size,
+ const uint8_t** data) {
+ return CborReadStr(in, CBOR_TYPE_BSTR, data_size, data);
+}
+
+enum CborReadResult CborReadTstr(struct CborIn* in, size_t* size,
+ const char** str) {
+ return CborReadStr(in, CBOR_TYPE_TSTR, size, (const uint8_t**)str);
+}
+
+enum CborReadResult CborReadArray(struct CborIn* in, size_t* num_elements) {
+ return CborReadSize(in, CBOR_TYPE_ARRAY, num_elements);
+}
+
+enum CborReadResult CborReadMap(struct CborIn* in, size_t* num_pairs) {
+ return CborReadSize(in, CBOR_TYPE_MAP, num_pairs);
+}
+
+enum CborReadResult CborReadFalse(struct CborIn* in) {
+ return CborReadSimple(in, /*val=*/20);
+}
+
+enum CborReadResult CborReadTrue(struct CborIn* in) {
+ return CborReadSimple(in, /*val=*/21);
+}
+
+enum CborReadResult CborReadNull(struct CborIn* in) {
+ return CborReadSimple(in, /*val=*/22);
+}
+
+enum CborReadResult CborReadSkip(struct CborIn* in) {
+ struct CborIn peeker = *in;
+ size_t size_stack[CBOR_READ_SKIP_STACK_SIZE];
+ size_t stack_size = 0;
+
+ size_stack[stack_size++] = 1;
+
+ while (stack_size > 0) {
+ // Get the type
+ uint8_t bytes;
+ enum CborType type;
+ uint64_t val;
+ enum CborReadResult res;
+
+ res = CborPeekIntialValueAndArgument(&peeker, &bytes, &type, &val);
+ if (res != CBOR_READ_RESULT_OK) {
+ return res;
+ }
+
+ if (CborReadWouldOverflow(bytes, &peeker)) {
+ return CBOR_READ_RESULT_END;
+ }
+ peeker.cursor += bytes;
+
+ if (--size_stack[stack_size - 1] == 0) {
+ --stack_size;
+ }
+
+ switch (type) {
+ case CBOR_TYPE_UINT:
+ case CBOR_TYPE_NINT:
+ case CBOR_TYPE_SIMPLE:
+ continue;
+ case CBOR_TYPE_BSTR:
+ case CBOR_TYPE_TSTR:
+ if (CborReadWouldOverflow(val, &peeker)) {
+ return CBOR_READ_RESULT_END;
+ }
+ peeker.cursor += val;
+ continue;
+ case CBOR_TYPE_MAP:
+ if (val > UINT64_MAX / 2) {
+ return CBOR_READ_RESULT_END;
+ }
+ val *= 2;
+ break;
+ case CBOR_TYPE_ARRAY:
+ break;
+ default:
+ return CBOR_READ_RESULT_MALFORMED;
+ }
+
+ // Push a new level of nesting to the stack.
+ if (val == 0) {
+ continue;
+ }
+ if (stack_size == CBOR_READ_SKIP_STACK_SIZE) {
+ return CBOR_READ_RESULT_MALFORMED;
+ }
+ size_stack[stack_size++] = val;
+ }
+
+ in->cursor = peeker.cursor;
+ return CBOR_READ_RESULT_OK;
+}
diff --git a/src/cbor_reader_fuzzer.cc b/src/cbor_reader_fuzzer.cc
new file mode 100644
index 0000000..9dc08fb
--- /dev/null
+++ b/src/cbor_reader_fuzzer.cc
@@ -0,0 +1,64 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "dice/cbor_reader.h"
+#include "fuzzer/FuzzedDataProvider.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ int64_t signed_int;
+ uint64_t unsigned_int;
+ size_t sz;
+ const uint8_t* ptr;
+ const char* str;
+ CborIn in;
+ CborIn peeker;
+
+ CborInInit(data, size, &in);
+
+ do {
+ peeker = in;
+ CborReadInt(&peeker, &signed_int);
+
+ peeker = in;
+ CborReadUint(&peeker, &unsigned_int);
+
+ peeker = in;
+ CborReadBstr(&peeker, &sz, &ptr);
+
+ peeker = in;
+ CborReadTstr(&peeker, &sz, &str);
+
+ peeker = in;
+ CborReadArray(&peeker, &sz);
+
+ peeker = in;
+ CborReadMap(&peeker, &sz);
+
+ peeker = in;
+ CborReadFalse(&peeker);
+
+ peeker = in;
+ CborReadTrue(&peeker);
+
+ peeker = in;
+ CborReadNull(&peeker);
+
+ if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
+ // Cannot progress futher with this buffer.
+ break;
+ }
+ } while (!CborInAtEnd(&in));
+
+ return 0;
+}
diff --git a/src/cbor_reader_test.cc b/src/cbor_reader_test.cc
new file mode 100644
index 0000000..b491025
--- /dev/null
+++ b/src/cbor_reader_test.cc
@@ -0,0 +1,372 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "dice/cbor_reader.h"
+
+#include "dice/test_framework.h"
+
+namespace {
+
+extern "C" {
+
+TEST(CborReaderTest, Int1Byte) {
+ const uint8_t buffer[] = {0, 23, 0x20, 0x37};
+ int64_t val;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(0, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(23, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(-1, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(-24, val);
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, Int2Bytes) {
+ const uint8_t buffer[] = {24, 24, 24, 0xff, 0x38, 24, 0x38, 0xff};
+ int64_t val;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(24, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(0xff, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(-25, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(-0x100, val);
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, Int3Bytes) {
+ const uint8_t buffer[] = {25, 0x01, 0x00, 25, 0xff, 0xff,
+ 0x39, 0x01, 0x00, 0x39, 0xff, 0xff};
+ int64_t val;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(0x100, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(0xffff, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(-0x101, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(-0x10000, val);
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, Int5Bytes) {
+ const uint8_t buffer[] = {26, 0x00, 0x01, 0x00, 0x00, 26, 0xff,
+ 0xff, 0xff, 0xff, 0x3a, 0x00, 0x01, 0x00,
+ 0x00, 0x3a, 0xff, 0xff, 0xff, 0xff};
+ int64_t val;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(0x10000, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(0xffffffff, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(-0x10001, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(-0x100000000, val);
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, Int9Bytes) {
+ const uint8_t buffer[] = {
+ 27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 27, 0x7f, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3b, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ int64_t val;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(0x100000000, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(INT64_MAX, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(-0x100000001, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(INT64_MIN, val);
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, Uint9Bytes) {
+ const uint8_t buffer[] = {27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 27, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ uint64_t val;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadUint(&in, &val));
+ EXPECT_EQ(0x100000000u, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadUint(&in, &val));
+ EXPECT_EQ(UINT64_MAX, val);
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, IntByteOrder) {
+ const uint8_t buffer[] = {
+ 25, 0x12, 0x34, 26, 0x12, 0x34, 0x56, 0x78, 27,
+ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
+ };
+ int64_t val;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(0x1234, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(0x12345678, val);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadInt(&in, &val));
+ EXPECT_EQ(0x123456789abcdef0, val);
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, IntMalformed) {
+ const uint8_t kTooBigBuffer[] = {27, 0x80, 0, 0, 0, 0, 0, 0, 0};
+ const uint8_t kTooSmallBuffer[] = {0x3b, 0x80, 0, 0, 0, 0, 0, 0, 0};
+ const uint8_t kBadAddlBuffer[] = {30};
+ const uint8_t kNegBadAddlBuffer[] = {0x3c};
+ int64_t val;
+ CborIn in;
+ CborInInit(kTooBigBuffer, sizeof(kTooBigBuffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadInt(&in, &val));
+ CborInInit(kTooSmallBuffer, sizeof(kTooSmallBuffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadInt(&in, &val));
+ CborInInit(kBadAddlBuffer, sizeof(kBadAddlBuffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadInt(&in, &val));
+ CborInInit(kNegBadAddlBuffer, sizeof(kNegBadAddlBuffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadInt(&in, &val));
+ EXPECT_FALSE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, IntTooShort) {
+ const uint8_t buffer[] = {27, 0x40, 0, 0, 0, 0, 0, 0};
+ int64_t val;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_END, CborReadInt(&in, &val));
+ EXPECT_FALSE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, BstrEncoding) {
+ const uint8_t buffer[] = {0x45, 'h', 'e', 'l', 'l', 'o'};
+ const uint8_t kData[] = {'h', 'e', 'l', 'l', 'o'};
+ size_t data_size;
+ const uint8_t* data;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadBstr(&in, &data_size, &data));
+ EXPECT_EQ(sizeof(kData), data_size);
+ EXPECT_EQ(0, memcmp(data, kData, data_size));
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, BstrLongEncoding) {
+ const uint8_t buffer[] = {
+ 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99,
+ };
+ size_t data_size;
+ const uint8_t* data;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadBstr(&in, &data_size, &data));
+ EXPECT_EQ(32u, data_size);
+ EXPECT_EQ(0, memcmp(data, buffer + 2, 32));
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, TstrEncoding) {
+ const uint8_t buffer[] = {0x65, 'w', 'o', 'r', 'l', 'd'};
+ const char kStr[] = "world";
+ size_t size;
+ const char* str;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTstr(&in, &size, &str));
+ EXPECT_EQ(strlen(kStr), size);
+ EXPECT_EQ(0, memcmp(str, kStr, size));
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, ArrayEncoding) {
+ const uint8_t buffer[] = {0x98, 29};
+ size_t num_elements;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadArray(&in, &num_elements));
+ EXPECT_EQ(29u, num_elements);
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, MapEncoding) {
+ const uint8_t buffer[] = {0xb9, 0x02, 0x50};
+ size_t num_pairs;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadMap(&in, &num_pairs));
+ EXPECT_EQ(592u, num_pairs);
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, FalseEncoding) {
+ const uint8_t buffer[] = {0xf4};
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadFalse(&in));
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, TrueEncoding) {
+ const uint8_t buffer[] = {0xf5};
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTrue(&in));
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, NullEncoding) {
+ const uint8_t buffer[] = {0xf6};
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadNull(&in));
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, Skip) {
+ const uint8_t buffer[] = {0x84, 0x03, 0xa2, 0x82, 0x23, 0x05, 0xf4,
+ 0x16, 0xf6, 0x61, 0x44, 0x41, 0xaa};
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadSkip(&in));
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, SkipTooDeeplyNestedMalformed) {
+ const uint8_t map[] = {0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1};
+ const uint8_t array[] = {0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82};
+ CborIn in;
+ CborInInit(map, sizeof(map), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadSkip(&in));
+ EXPECT_EQ(0u, CborInOffset(&in));
+ CborInInit(array, sizeof(array), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadSkip(&in));
+ EXPECT_EQ(0u, CborInOffset(&in));
+}
+
+TEST(CborReaderTest, SkipTagMalformed) {
+ const uint8_t tag[] = {0xc4, 0xf5};
+ const uint8_t nested_tag[] = {0x82, 0xa1, 0x02, 0xc7, 0x04, 0x09};
+ CborIn in;
+ CborInInit(tag, sizeof(tag), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadSkip(&in));
+ EXPECT_EQ(0u, CborInOffset(&in));
+ CborInInit(nested_tag, sizeof(nested_tag), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadSkip(&in));
+ EXPECT_EQ(0u, CborInOffset(&in));
+}
+
+TEST(CborReaderTest, EmptyBufferAtEnd) {
+ int64_t val;
+ uint64_t uval;
+ size_t size;
+ const uint8_t* data;
+ const char* str;
+ CborIn in;
+ CborInInit(nullptr, 0, &in);
+ EXPECT_TRUE(CborInAtEnd(&in));
+ EXPECT_EQ(CBOR_READ_RESULT_END, CborReadInt(&in, &val));
+ EXPECT_EQ(CBOR_READ_RESULT_END, CborReadUint(&in, &uval));
+ EXPECT_EQ(CBOR_READ_RESULT_END, CborReadBstr(&in, &size, &data));
+ EXPECT_EQ(CBOR_READ_RESULT_END, CborReadTstr(&in, &size, &str));
+ EXPECT_EQ(CBOR_READ_RESULT_END, CborReadArray(&in, &size));
+ EXPECT_EQ(CBOR_READ_RESULT_END, CborReadMap(&in, &size));
+ EXPECT_EQ(CBOR_READ_RESULT_END, CborReadFalse(&in));
+ EXPECT_EQ(CBOR_READ_RESULT_END, CborReadTrue(&in));
+ EXPECT_EQ(CBOR_READ_RESULT_END, CborReadNull(&in));
+ EXPECT_EQ(0u, CborInOffset(&in));
+}
+
+TEST(CborReaderTest, NotFound) {
+ const uint8_t buffer[] = {0xc0, 0x08};
+ int64_t val;
+ uint64_t uval;
+ size_t size;
+ const uint8_t* data;
+ const char* str;
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadInt(&in, &val));
+ EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadUint(&in, &uval));
+ EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadBstr(&in, &size, &data));
+ EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadTstr(&in, &size, &str));
+ EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadArray(&in, &size));
+ EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadMap(&in, &size));
+ EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadFalse(&in));
+ EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadTrue(&in));
+ EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadNull(&in));
+ EXPECT_EQ(0u, CborInOffset(&in));
+}
+
+TEST(CborReaderTest, SimpleValueNotFound) {
+ const uint8_t buffer[] = {0xf7};
+ CborIn in;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadFalse(&in));
+ EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadTrue(&in));
+ EXPECT_EQ(CBOR_READ_RESULT_NOT_FOUND, CborReadNull(&in));
+ EXPECT_EQ(0u, CborInOffset(&in));
+}
+
+TEST(CborReaderTest, IndefiniteLengthMalformed) {
+ size_t size;
+ const uint8_t* data;
+ const char* str;
+ CborIn in;
+ const uint8_t bstr[] = {0x5f, 0x44, 0xaa, 0xbb, 0xcc, 0xdd,
+ 0x43, 0xee, 0xff, 0x99, 0xff};
+ CborInInit(bstr, sizeof(bstr), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadBstr(&in, &size, &data));
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadSkip(&in));
+ EXPECT_EQ(0u, CborInOffset(&in));
+ const uint8_t tstr[] = {0x7f, 0x64, 0x41, 0x42, 0x43, 0x44,
+ 0x63, 0x30, 0x31, 0x32, 0xff};
+ CborInInit(tstr, sizeof(tstr), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadTstr(&in, &size, &str));
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadSkip(&in));
+ EXPECT_EQ(0u, CborInOffset(&in));
+ const uint8_t array[] = {0x9f, 0x01, 0x82, 0x02, 0x03,
+ 0x82, 0x04, 0x05, 0xff};
+ CborInInit(array, sizeof(array), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadArray(&in, &size));
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadSkip(&in));
+ EXPECT_EQ(0u, CborInOffset(&in));
+ const uint8_t map[] = {0xbf, 0x63, 0x46, 0x75, 0x6e, 0xf5,
+ 0x63, 0x41, 0x6d, 0x74, 0x21, 0xff};
+ CborInInit(map, sizeof(map), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadMap(&in, &size));
+ EXPECT_EQ(CBOR_READ_RESULT_MALFORMED, CborReadSkip(&in));
+ EXPECT_EQ(0u, CborInOffset(&in));
+}
+}
+
+} // namespace