Add functions to read and write CBOR tags
The functions to read and write tags do not perform any tag
validation; this step is left to callers. CborReadSkip treats
tags as a one-element array in order to correcly skip content
with nested tags. This change also modifies the test suite and
fuzzers to support tags.
Change-Id: Ic86b03739e1aaf06a4f0e47ad32fe63ed78cccbe
Reviewed-on: https://pigweed-review.googlesource.com/c/open-dice/+/99900
Reviewed-by: Andrew Scull <ascull@google.com>
Commit-Queue: Andrew Scull <ascull@google.com>
diff --git a/include/dice/cbor_reader.h b/include/dice/cbor_reader.h
index 5086d9e..04cabc6 100644
--- a/include/dice/cbor_reader.h
+++ b/include/dice/cbor_reader.h
@@ -72,11 +72,15 @@
enum CborReadResult CborReadFalse(struct CborIn* in);
enum CborReadResult CborReadTrue(struct CborIn* in);
enum CborReadResult CborReadNull(struct CborIn* in);
+// Returns CBOR_READ_RESULT_OK even if the value read does not correspond to
+// a valid tag. See https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
+// for a registry of reserved and invalid tag values.
+enum CborReadResult CborReadTag(struct CborIn* in, uint64_t* tag);
// 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
+// items, in the case of an array, map, or tag, 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);
diff --git a/include/dice/cbor_writer.h b/include/dice/cbor_writer.h
index 8a96afa..d7ed6a6 100644
--- a/include/dice/cbor_writer.h
+++ b/include/dice/cbor_writer.h
@@ -66,6 +66,7 @@
void CborWriteTstr(const char* str, struct CborOut* out);
void CborWriteArray(size_t num_elements, struct CborOut* out);
void CborWriteMap(size_t num_pairs, struct CborOut* out);
+void CborWriteTag(uint64_t tag, struct CborOut* out);
void CborWriteFalse(struct CborOut* out);
void CborWriteTrue(struct CborOut* out);
void CborWriteNull(struct CborOut* out);
diff --git a/src/cbor_reader.c b/src/cbor_reader.c
index 3b0b343..035a0bc 100644
--- a/src/cbor_reader.c
+++ b/src/cbor_reader.c
@@ -21,7 +21,7 @@
CBOR_TYPE_TSTR = 3,
CBOR_TYPE_ARRAY = 4,
CBOR_TYPE_MAP = 5,
- CBOR_TYPE_TAG_NOT_SUPPORTED = 6,
+ CBOR_TYPE_TAG = 6,
CBOR_TYPE_SIMPLE = 7,
};
@@ -188,6 +188,21 @@
return CborReadSize(in, CBOR_TYPE_MAP, num_pairs);
}
+enum CborReadResult CborReadTag(struct CborIn* in, uint64_t* tag) {
+ uint8_t bytes;
+ enum CborType type;
+ enum CborReadResult res =
+ CborPeekIntialValueAndArgument(in, &bytes, &type, tag);
+ if (res != CBOR_READ_RESULT_OK) {
+ return res;
+ }
+ if (type != CBOR_TYPE_TAG) {
+ return CBOR_READ_RESULT_NOT_FOUND;
+ }
+ in->cursor += bytes;
+ return CBOR_READ_RESULT_OK;
+}
+
enum CborReadResult CborReadFalse(struct CborIn* in) {
return CborReadSimple(in, /*val=*/20);
}
@@ -246,6 +261,9 @@
}
val *= 2;
break;
+ case CBOR_TYPE_TAG:
+ val = 1;
+ break;
case CBOR_TYPE_ARRAY:
break;
default:
diff --git a/src/cbor_reader_fuzzer.cc b/src/cbor_reader_fuzzer.cc
index 9dc08fb..3eeb544 100644
--- a/src/cbor_reader_fuzzer.cc
+++ b/src/cbor_reader_fuzzer.cc
@@ -46,6 +46,9 @@
CborReadMap(&peeker, &sz);
peeker = in;
+ CborReadTag(&peeker, &unsigned_int);
+
+ peeker = in;
CborReadFalse(&peeker);
peeker = in;
diff --git a/src/cbor_reader_test.cc b/src/cbor_reader_test.cc
index b491025..7b14475 100644
--- a/src/cbor_reader_test.cc
+++ b/src/cbor_reader_test.cc
@@ -249,13 +249,65 @@
EXPECT_TRUE(CborInAtEnd(&in));
}
+TEST(CborReaderTest, TagEncoding) {
+ const uint8_t buffer[] = {0xcf, 0xd8, 0x18, 0xd9, 0xd9, 0xf8, 0xda, 0x4f,
+ 0x50, 0x53, 0x4e, 0xdb, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+ CborIn in;
+ uint64_t tag;
+ CborInInit(buffer, sizeof(buffer), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+ EXPECT_EQ(/* Unassigned */15u, tag);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+ EXPECT_EQ(/* COSE_Sign1 */24u, tag);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+ EXPECT_EQ(/* Byte string */0xd9f8u, tag);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+ EXPECT_EQ(/* Openswan cfg */0x4f50534eu, tag);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+ EXPECT_EQ(/* Unassigned */0x1000000000000000u, tag);
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
+TEST(CborReaderTest, TagInvalid) {
+ // The following tags are always invalid but are treated as any other tag.
+ // Reference https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml.
+ const uint8_t invalid16[] = {0xd9, 0xff, 0xff};
+ const uint8_t invalid32[] = {0xda, 0xff, 0xff, 0xff, 0xff};
+ const uint8_t invalid64[] = {0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff};
+ CborIn in;
+ uint64_t tag;
+ CborInInit(invalid16, sizeof(invalid16), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+ EXPECT_TRUE(CborInAtEnd(&in));
+ CborInInit(invalid32, sizeof(invalid32), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+ EXPECT_TRUE(CborInAtEnd(&in));
+ CborInInit(invalid64, sizeof(invalid64), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadTag(&in, &tag));
+ EXPECT_TRUE(CborInAtEnd(&in));
+}
+
TEST(CborReaderTest, Skip) {
const uint8_t buffer[] = {0x84, 0x03, 0xa2, 0x82, 0x23, 0x05, 0xf4,
0x16, 0xf6, 0x61, 0x44, 0x41, 0xaa};
+ const uint8_t tag[] = {0xc4, 0xf5};
+ const uint8_t tagtag[] = {0xc4, 0xc4, 0xf5};
+ const uint8_t nested_tag[] = {0x82, 0xa1, 0x02, 0xc7, 0x04, 0x09};
CborIn in;
CborInInit(buffer, sizeof(buffer), &in);
EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadSkip(&in));
EXPECT_TRUE(CborInAtEnd(&in));
+ CborInInit(tag, sizeof(tag), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadSkip(&in));
+ EXPECT_TRUE(CborInAtEnd(&in));
+ CborInInit(tagtag, sizeof(tagtag), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadSkip(&in));
+ EXPECT_TRUE(CborInAtEnd(&in));
+ CborInInit(nested_tag, sizeof(nested_tag), &in);
+ EXPECT_EQ(CBOR_READ_RESULT_OK, CborReadSkip(&in));
+ EXPECT_TRUE(CborInAtEnd(&in));
}
TEST(CborReaderTest, SkipTooDeeplyNestedMalformed) {
@@ -273,18 +325,6 @@
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;
@@ -303,11 +343,12 @@
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(CBOR_READ_RESULT_END, CborReadTag(&in, &uval));
EXPECT_EQ(0u, CborInOffset(&in));
}
TEST(CborReaderTest, NotFound) {
- const uint8_t buffer[] = {0xc0, 0x08};
+ const uint8_t buffer[] = {0xe0, 0x08};
int64_t val;
uint64_t uval;
size_t size;
@@ -324,6 +365,7 @@
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(CBOR_READ_RESULT_NOT_FOUND, CborReadTag(&in, &uval));
EXPECT_EQ(0u, CborInOffset(&in));
}
diff --git a/src/cbor_writer.c b/src/cbor_writer.c
index bfa403f..e512931 100644
--- a/src/cbor_writer.c
+++ b/src/cbor_writer.c
@@ -26,7 +26,7 @@
CBOR_TYPE_TSTR = 3,
CBOR_TYPE_ARRAY = 4,
CBOR_TYPE_MAP = 5,
- // Type 6, tags, are not supported.
+ CBOR_TYPE_TAG = 6,
CBOR_TYPE_SIMPLE = 7,
};
@@ -142,6 +142,10 @@
CborWriteType(CBOR_TYPE_MAP, num_pairs, out);
}
+void CborWriteTag(uint64_t tag, struct CborOut* out) {
+ CborWriteType(CBOR_TYPE_TAG, tag, out);
+}
+
void CborWriteFalse(struct CborOut* out) {
CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/20, out);
}
diff --git a/src/cbor_writer_fuzzer.cc b/src/cbor_writer_fuzzer.cc
index ebcb1e3..2119e02 100644
--- a/src/cbor_writer_fuzzer.cc
+++ b/src/cbor_writer_fuzzer.cc
@@ -26,6 +26,7 @@
AllocTstr,
WriteArray,
WriteMap,
+ WriteTag,
WriteFalse,
WriteTrue,
WriteNull,
@@ -97,6 +98,11 @@
CborWriteMap(num_pairs, &out);
break;
}
+ case WriteTag: {
+ auto tag = fdp.ConsumeIntegral<uint64_t>();
+ CborWriteTag(tag, &out);
+ break;
+ }
case WriteFalse:
CborWriteNull(&out);
break;
diff --git a/src/cbor_writer_test.cc b/src/cbor_writer_test.cc
index 2ad72a4..c1f7a9d 100644
--- a/src/cbor_writer_test.cc
+++ b/src/cbor_writer_test.cc
@@ -188,6 +188,22 @@
EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
}
+TEST(CborWriterTest, TagEncoding) {
+ const uint8_t kExpectedEncoding[] = {0xcf, 0xd8, 0x18, 0xd9, 0xd9, 0xf8, 0xda,
+ 0x4f, 0x50, 0x53, 0x4e, 0xdb, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ uint8_t buffer[64];
+ CborOut out;
+ CborOutInit(buffer, sizeof(buffer), &out);
+ CborWriteTag(/*tag=*/15, &out);
+ CborWriteTag(/*tag=*/24, &out);
+ CborWriteTag(/*tag=*/0xd9f8u, &out);
+ CborWriteTag(/*tag=*/0x4f50534eu, &out);
+ CborWriteTag(/*tag=*/0x1000000000000000u, &out);
+ EXPECT_FALSE(CborOutOverflowed(&out));
+ EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
+}
+
TEST(CborWriterTest, FalseEncoding) {
const uint8_t kExpectedEncoding[] = {0xf4};
uint8_t buffer[64];
@@ -230,12 +246,13 @@
EXPECT_NE(nullptr, CborAllocTstr(6, &out));
CborWriteArray(/*num_elements=*/16, &out);
CborWriteMap(/*num_pairs=*/35, &out);
+ CborWriteTag(/*tag=*/15, &out);
CborWriteFalse(&out);
CborWriteTrue(&out);
CborWriteNull(&out);
EXPECT_FALSE(CborOutOverflowed(&out));
// Offset is the cumulative size.
- EXPECT_EQ(3 + 6 + 8 + 9 + 7 + 1 + 2 + 1 + 1 + 1u, CborOutSize(&out));
+ EXPECT_EQ(3 + 6 + 8 + 9 + 7 + 1 + 2 + 1 + 1 + 1 + 1u, CborOutSize(&out));
}
TEST(CborWriterTest, NullBufferForMeasurement) {
@@ -245,6 +262,7 @@
CborWriteNull(&out);
CborWriteTrue(&out);
CborWriteFalse(&out);
+ CborWriteTag(/*tag=*/15, &out);
CborWriteMap(/*num_pairs=*/623, &out);
CborWriteArray(/*num_elements=*/70000, &out);
EXPECT_EQ(nullptr, CborAllocTstr(8, &out));
@@ -255,7 +273,7 @@
// Measurement has occurred, but output did not.
EXPECT_TRUE(CborOutOverflowed(&out));
// Offset is the cumulative size.
- EXPECT_EQ(1 + 1 + 1 + 3 + 5 + 9 + 7 + 2 + 8 + 5u, CborOutSize(&out));
+ EXPECT_EQ(1 + 1 + 1 + 1 + 3 + 5 + 9 + 7 + 2 + 8 + 5u, CborOutSize(&out));
}
TEST(CborWriterTest, BufferTooSmall) {