Add functions to write simple CBOR values

The unit value, null, and the boolean values, true and false, have their
own CBOR encoding. Expose functions to write these values.

Change-Id: I8528f89eb949ffe985cea17ed9110be3672b3d6e
Reviewed-on: https://pigweed-review.googlesource.com/c/open-dice/+/39340
Commit-Queue: Andrew Scull <ascull@google.com>
Pigweed-Auto-Submit: Andrew Scull <ascull@google.com>
Reviewed-by: Darren Krahn <dkrahn@google.com>
diff --git a/include/dice/cbor_writer.h b/include/dice/cbor_writer.h
index 8652eaf..9835c6e 100644
--- a/include/dice/cbor_writer.h
+++ b/include/dice/cbor_writer.h
@@ -47,6 +47,9 @@
 size_t CborWriteTstr(const char* str, struct CborOut* out);
 size_t CborWriteArray(size_t num_elements, struct CborOut* out);
 size_t CborWriteMap(size_t num_pairs, struct CborOut* out);
+size_t CborWriteFalse(struct CborOut* out);
+size_t CborWriteTrue(struct CborOut* out);
+size_t CborWriteNull(struct CborOut* out);
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/src/cbor_writer.c b/src/cbor_writer.c
index 7ec1c67..b5517d9 100644
--- a/src/cbor_writer.c
+++ b/src/cbor_writer.c
@@ -26,6 +26,8 @@
   CBOR_TYPE_TSTR = 3,
   CBOR_TYPE_ARRAY = 4,
   CBOR_TYPE_MAP = 5,
+  // Type 6, tags, are not supported.
+  CBOR_TYPE_SIMPLE = 7,
 };
 
 static size_t CborWriteType(enum CborType type, uint64_t val,
@@ -132,3 +134,15 @@
 size_t CborWriteMap(size_t num_pairs, struct CborOut* out) {
   return CborWriteType(CBOR_TYPE_MAP, num_pairs, out);
 }
+
+size_t CborWriteFalse(struct CborOut* out) {
+  return CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/20, out);
+}
+
+size_t CborWriteTrue(struct CborOut* out) {
+  return CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/21, out);
+}
+
+size_t CborWriteNull(struct CborOut* out) {
+  return CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/22, out);
+}
diff --git a/src/cbor_writer_fuzzer.cc b/src/cbor_writer_fuzzer.cc
index 42db6dd..fc1b739 100644
--- a/src/cbor_writer_fuzzer.cc
+++ b/src/cbor_writer_fuzzer.cc
@@ -23,13 +23,16 @@
   WriteTstr,
   WriteArray,
   WriteMap,
-  kMaxValue = WriteMap,
+  WriteFalse,
+  WriteTrue,
+  WriteNull,
+  kMaxValue = WriteNull,
 };
 
 // Use data sizes that exceed the 16-bit range without being excessive.
 constexpr size_t kMaxDataSize = 0xffff + 0x5000;
 constexpr size_t kMaxBufferSize = kMaxDataSize * 3;
-constexpr size_t kIterations = 10;
+constexpr size_t kIterations = 16;
 
 }  // namespace
 
@@ -73,6 +76,15 @@
         CborWriteMap(num_pairs, &out);
         break;
       }
+      case WriteFalse:
+        CborWriteNull(&out);
+        break;
+      case WriteTrue:
+        CborWriteNull(&out);
+        break;
+      case WriteNull:
+        CborWriteNull(&out);
+        break;
     }
   }
 
diff --git a/src/cbor_writer_test.cc b/src/cbor_writer_test.cc
index 4cb9711..ed0cdc8 100644
--- a/src/cbor_writer_test.cc
+++ b/src/cbor_writer_test.cc
@@ -159,6 +159,39 @@
   EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
 }
 
+TEST(CborWriterTest, FalseEncoding) {
+  const uint8_t kExpectedEncoding[] = {0xf4};
+  uint8_t buffer[64];
+  CborOut out = {
+      .buffer = buffer,
+      .size = sizeof(buffer),
+  };
+  EXPECT_EQ(sizeof(kExpectedEncoding), CborWriteFalse(&out));
+  EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
+}
+
+TEST(CborWriterTest, TrueEncoding) {
+  const uint8_t kExpectedEncoding[] = {0xf5};
+  uint8_t buffer[64];
+  CborOut out = {
+      .buffer = buffer,
+      .size = sizeof(buffer),
+  };
+  EXPECT_EQ(sizeof(kExpectedEncoding), CborWriteTrue(&out));
+  EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
+}
+
+TEST(CborWriterTest, NullEncoding) {
+  const uint8_t kExpectedEncoding[] = {0xf6};
+  uint8_t buffer[64];
+  CborOut out = {
+      .buffer = buffer,
+      .size = sizeof(buffer),
+  };
+  EXPECT_EQ(sizeof(kExpectedEncoding), CborWriteNull(&out));
+  EXPECT_EQ(0, memcmp(buffer, kExpectedEncoding, sizeof(kExpectedEncoding)));
+}
+
 TEST(CborWriterTest, CborOutInvariants) {
   const uint8_t kData[] = {0xb2, 0x34, 0x75, 0x92, 0x52};
   uint8_t buffer[64];
@@ -171,8 +204,11 @@
   EXPECT_EQ(9u, CborWriteTstr("A string", &out));
   EXPECT_EQ(1u, CborWriteArray(/*num_elements=*/16, &out));
   EXPECT_EQ(2u, CborWriteMap(/*num_pairs=*/35, &out));
+  EXPECT_EQ(1u, CborWriteFalse(&out));
+  EXPECT_EQ(1u, CborWriteTrue(&out));
+  EXPECT_EQ(1u, CborWriteNull(&out));
   // Offset is the cumulative size.
-  EXPECT_EQ(3 + 6 + 9 + 1 + 2u, out.offset);
+  EXPECT_EQ(3 + 6 + 9 + 1 + 2 + 1 + 1 + 1u, out.offset);
   // Buffer and size are unchanged.
   EXPECT_EQ(buffer, out.buffer);
   EXPECT_EQ(sizeof(buffer), out.size);
@@ -184,13 +220,16 @@
       .buffer = nullptr,
       .size = 2,  // Ignored.
   };
+  EXPECT_EQ(1u, CborWriteNull(&out));
+  EXPECT_EQ(1u, CborWriteTrue(&out));
+  EXPECT_EQ(1u, CborWriteFalse(&out));
   EXPECT_EQ(3u, CborWriteMap(/*num_pairs=*/623, &out));
   EXPECT_EQ(5u, CborWriteArray(/*num_elements=*/70000, &out));
   EXPECT_EQ(7u, CborWriteTstr("length", &out));
   EXPECT_EQ(8u, CborWriteBstr(sizeof(kData), kData, &out));
   EXPECT_EQ(5u, CborWriteInt(-10002000, &out));
   // Offset is the cumulative size.
-  EXPECT_EQ(3 + 5 + 7 + 8 + 5u, out.offset);
+  EXPECT_EQ(1 + 1 + 1 + 3 + 5 + 7 + 8 + 5u, out.offset);
   // Buffer and size are unchanged.
   EXPECT_EQ(nullptr, out.buffer);
   EXPECT_EQ(2u, out.size);
@@ -214,6 +253,13 @@
   EXPECT_EQ(0u, CborWriteArray(/*num_elements=*/563, &out));
   out.offset = 0;
   EXPECT_EQ(0u, CborWriteMap(/*num_pairs=*/29, &out));
+  out.size = 0;
+  out.offset = 0;
+  EXPECT_EQ(0u, CborWriteFalse(&out));
+  out.offset = 0;
+  EXPECT_EQ(0u, CborWriteTrue(&out));
+  out.offset = 0;
+  EXPECT_EQ(0u, CborWriteNull(&out));
 }
 
 TEST(CborWriterTest, NotEnoughRemainingSpace) {
@@ -234,6 +280,12 @@
   EXPECT_EQ(0u, CborWriteArray(/*num_elements=*/352, &out));
   out.offset = sizeof(buffer) - 1;
   EXPECT_EQ(0u, CborWriteMap(/*num_pairs=*/73, &out));
+  out.offset = sizeof(buffer);
+  EXPECT_EQ(0u, CborWriteFalse(&out));
+  out.offset = sizeof(buffer);
+  EXPECT_EQ(0u, CborWriteTrue(&out));
+  out.offset = sizeof(buffer);
+  EXPECT_EQ(0u, CborWriteNull(&out));
 }
 
 TEST(CborWriterTest, OffsetOverflow) {
@@ -254,6 +306,12 @@
   EXPECT_EQ(0u, CborWriteArray(/*num_elements=*/41, &out));
   out.offset = SIZE_MAX - 1;
   EXPECT_EQ(0u, CborWriteMap(/*num_pairs=*/998844, &out));
+  out.offset = SIZE_MAX;
+  EXPECT_EQ(0u, CborWriteFalse(&out));
+  out.offset = SIZE_MAX;
+  EXPECT_EQ(0u, CborWriteTrue(&out));
+  out.offset = SIZE_MAX;
+  EXPECT_EQ(0u, CborWriteNull(&out));
 }
 
 TEST(CborWriterTest, MeasurementOffsetOverflow) {
@@ -272,6 +330,12 @@
   EXPECT_EQ(0u, CborWriteArray(/*num_elements=*/8368257314, &out));
   out.offset = SIZE_MAX - 1;
   EXPECT_EQ(0u, CborWriteMap(/*num_pairs=*/92, &out));
+  out.offset = SIZE_MAX;
+  EXPECT_EQ(0u, CborWriteFalse(&out));
+  out.offset = SIZE_MAX;
+  EXPECT_EQ(0u, CborWriteTrue(&out));
+  out.offset = SIZE_MAX;
+  EXPECT_EQ(0u, CborWriteNull(&out));
 }
 }