diff options
author | Andrew Scull <ascull@google.com> | 2021-05-17 10:53:49 +0000 |
---|---|---|
committer | CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2021-10-19 08:32:53 +0000 |
commit | 87d91de9dc9faadfcba6a177fa6bb64a1b8efa60 (patch) | |
tree | a362d4fd6b7de16f5ec91b61b8733ac2f55ebd1c | |
parent | 8ff2adfdf6996ca5b387758da646646c60d2d9c7 (diff) | |
download | open-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.gn | 23 | ||||
-rw-r--r-- | include/dice/cbor_reader.h | 88 | ||||
-rw-r--r-- | src/cbor_reader.c | 267 | ||||
-rw-r--r-- | src/cbor_reader_fuzzer.cc | 64 | ||||
-rw-r--r-- | src/cbor_reader_test.cc | 372 |
5 files changed, 814 insertions, 0 deletions
@@ -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 |