Add BccHandoverParse

To help with accessing the fields of the handover, add a function to
parse the top-level CBOR map. Do this by extracting and exporting the
existing logic. Users of the library need to do this parsing e.g. to get
access to the sealing CDI in order to derive a key for sealing data.

Change-Id: If8b6ffa16f50c2872486d22de3ea05cf489ecb55
Reviewed-on: https://pigweed-review.googlesource.com/c/open-dice/+/93561
Commit-Queue: Andrew Scull <ascull@google.com>
Pigweed-Auto-Submit: Andrew Scull <ascull@google.com>
Reviewed-by: Darren Krahn <dkrahn@google.com>
Reviewed-by: Hasini Gunasinghe <hasinitg@google.com>
diff --git a/include/dice/android/bcc.h b/include/dice/android/bcc.h
index e3e6c55..06f9fcb 100644
--- a/include/dice/android/bcc.h
+++ b/include/dice/android/bcc.h
@@ -80,6 +80,17 @@
                                size_t buffer_size, uint8_t* buffer,
                                size_t* actual_size);
 
+// Parses a BCC handover to extract the fields.
+//
+// Given a pointer to a BCC handover, pointers to the CDIs and, optionally, the
+// BCC in the buffer are returned. If the BCC is not included in the handover,
+// the pointer is NULL and the size is 0.
+DiceResult BccHandoverParse(const uint8_t* bcc_handover,
+                            size_t bcc_handover_size,
+                            const uint8_t** cdi_attest,
+                            const uint8_t** cdi_seal, const uint8_t** bcc,
+                            size_t* bcc_size);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/src/android/bcc.c b/src/android/bcc.c
index d58e80a..5dd8620 100644
--- a/src/android/bcc.c
+++ b/src/android/bcc.c
@@ -192,60 +192,28 @@
   return result;
 }
 
+static const int64_t kCdiAttestLabel = 1;
+static const int64_t kCdiSealLabel = 2;
+static const int64_t kBccLabel = 3;
+
 DiceResult BccHandoverMainFlow(void* context, const uint8_t* bcc_handover,
                                size_t bcc_handover_size,
                                const DiceInputValues* input_values,
                                size_t buffer_size, uint8_t* buffer,
                                size_t* actual_size) {
-  static const int64_t kCdiAttestLabel = 1;
-  static const int64_t kCdiSealLabel = 2;
-  static const int64_t kBccLabel = 3;
-
   DiceResult result;
   const uint8_t* current_cdi_attest;
   const uint8_t* current_cdi_seal;
   const uint8_t* bcc;
+  size_t bcc_size;
 
-  // Extract details from the handover data.
-  //
-  // BccHandover = {
-  //   1 : bstr .size 32,     ; CDI_Attest
-  //   2 : bstr .size 32,     ; CDI_Seal
-  //   ? 3 : Bcc,             ; Certificate chain
-  // }
-  struct CborIn in;
-  int64_t label;
-  size_t num_pairs;
-  size_t item_size;
-  CborInInit(bcc_handover, bcc_handover_size, &in);
-  if (CborReadMap(&in, &num_pairs) != CBOR_READ_RESULT_OK || num_pairs < 2 ||
-      // Read the attestation CDI.
-      CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
-      label != kCdiAttestLabel ||
-      CborReadBstr(&in, &item_size, &current_cdi_attest) !=
-          CBOR_READ_RESULT_OK ||
-      item_size != DICE_CDI_SIZE ||
-      // Read the sealing CDI.
-      CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
-      label != kCdiSealLabel ||
-      CborReadBstr(&in, &item_size, &current_cdi_seal) != CBOR_READ_RESULT_OK ||
-      item_size != DICE_CDI_SIZE) {
+  result =
+      BccHandoverParse(bcc_handover, bcc_handover_size, &current_cdi_attest,
+                       &current_cdi_seal, &bcc, &bcc_size);
+  if (result != kDiceResultOk) {
     return kDiceResultInvalidInput;
   }
 
-  size_t bcc_size = 0;
-  if (num_pairs >= 3 && CborReadInt(&in, &label) == CBOR_READ_RESULT_OK) {
-    if (label == kBccLabel) {
-      // Calculate the BCC size, if the BCC is present in the BccHandover.
-      size_t bcc_start = CborInOffset(&in);
-      if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
-        return kDiceResultInvalidInput;
-      }
-      bcc = bcc_handover + bcc_start;
-      bcc_size = CborInOffset(&in) - bcc_start;
-    }
-  }
-
   // Write the new handover data.
   struct CborOut out;
   CborOutInit(buffer, buffer_size, &out);
@@ -281,3 +249,51 @@
   *actual_size = CborOutSize(&out) + bcc_size;
   return kDiceResultOk;
 }
+
+DiceResult BccHandoverParse(const uint8_t* bcc_handover,
+                            size_t bcc_handover_size,
+                            const uint8_t** cdi_attest,
+                            const uint8_t** cdi_seal, const uint8_t** bcc,
+                            size_t* bcc_size) {
+  // Extract details from the handover data.
+  //
+  // BccHandover = {
+  //   1 : bstr .size 32,     ; CDI_Attest
+  //   2 : bstr .size 32,     ; CDI_Seal
+  //   ? 3 : Bcc,             ; Certificate chain
+  // }
+  struct CborIn in;
+  int64_t label;
+  size_t num_pairs;
+  size_t item_size;
+  CborInInit(bcc_handover, bcc_handover_size, &in);
+  if (CborReadMap(&in, &num_pairs) != CBOR_READ_RESULT_OK || num_pairs < 2 ||
+      // Read the attestation CDI.
+      CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
+      label != kCdiAttestLabel ||
+      CborReadBstr(&in, &item_size, cdi_attest) != CBOR_READ_RESULT_OK ||
+      item_size != DICE_CDI_SIZE ||
+      // Read the sealing CDI.
+      CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
+      label != kCdiSealLabel ||
+      CborReadBstr(&in, &item_size, cdi_seal) != CBOR_READ_RESULT_OK ||
+      item_size != DICE_CDI_SIZE) {
+    return kDiceResultInvalidInput;
+  }
+
+  *bcc = NULL;
+  *bcc_size = 0;
+  if (num_pairs >= 3 && CborReadInt(&in, &label) == CBOR_READ_RESULT_OK) {
+    if (label == kBccLabel) {
+      // Calculate the BCC size, if the BCC is present in the BccHandover.
+      size_t bcc_start = CborInOffset(&in);
+      if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
+        return kDiceResultInvalidInput;
+      }
+      *bcc = bcc_handover + bcc_start;
+      *bcc_size = CborInOffset(&in) - bcc_start;
+    }
+  }
+
+  return kDiceResultOk;
+}
diff --git a/src/android/bcc_test.cc b/src/android/bcc_test.cc
index 397eee7..0c45e06 100644
--- a/src/android/bcc_test.cc
+++ b/src/android/bcc_test.cc
@@ -164,6 +164,111 @@
   EXPECT_GT(next_bcc_handover_size, sizeof(bcc_handover));
   EXPECT_EQ(0xa3, next_bcc_handover[0]);
 }
+
+TEST(BccHandoverTest, ParseHandover) {
+  const uint8_t bcc_handover[] = {
+      0xa3,
+      // CDI attest
+      0x01, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // CDI seal
+      0x02, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // BCC
+      0x03, 0x82, 0xa6, 0x01, 0x02, 0x03, 0x27, 0x04, 0x02, 0x20, 0x01, 0x21,
+      0x40, 0x22, 0x40, 0x84, 0x40, 0xa0, 0x40, 0x40,
+      // 8-bytes of trailing data that aren't part of the BCC.
+      0x00, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40};
+  const uint8_t *cdi_attest;
+  const uint8_t *cdi_seal;
+  const uint8_t *bcc;
+  size_t bcc_size;
+  DiceResult result = BccHandoverParse(bcc_handover, sizeof(bcc_handover),
+                                       &cdi_attest, &cdi_seal, &bcc, &bcc_size);
+  EXPECT_EQ(kDiceResultOk, result);
+  EXPECT_EQ(bcc_handover + 4, cdi_attest);
+  EXPECT_EQ(bcc_handover + 39, cdi_seal);
+  EXPECT_EQ(bcc_handover + 72, bcc);
+  EXPECT_EQ(19u, bcc_size);
+}
+
+TEST(BccHandoverTest, ParseHandoverWithoutBcc) {
+  const uint8_t bcc_handover[] = {
+      0xa2,
+      // CDI attest
+      0x01, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // CDI seal
+      0x02, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // 8-bytes of trailing data that aren't part of the BCC.
+      0x00, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40};
+  const uint8_t *cdi_attest;
+  const uint8_t *cdi_seal;
+  const uint8_t *bcc;
+  size_t bcc_size;
+  DiceResult result = BccHandoverParse(bcc_handover, sizeof(bcc_handover),
+                                       &cdi_attest, &cdi_seal, &bcc, &bcc_size);
+  EXPECT_EQ(kDiceResultOk, result);
+  EXPECT_EQ(bcc_handover + 4, cdi_attest);
+  EXPECT_EQ(bcc_handover + 39, cdi_seal);
+  EXPECT_EQ(nullptr, bcc);
+  EXPECT_EQ(0u, bcc_size);
+}
+
+TEST(BccHandoverTest, ParseHandoverWithoutBccButUnknownField) {
+  const uint8_t bcc_handover[] = {
+      0xa3,
+      // CDI attest
+      0x01, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // CDI seal
+      0x02, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // Ignored unknown field
+      0x04, 0x01,
+      // 8-bytes of trailing data that aren't part of the BCC.
+      0x00, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40};
+  const uint8_t *cdi_attest;
+  const uint8_t *cdi_seal;
+  const uint8_t *bcc;
+  size_t bcc_size;
+  DiceResult result = BccHandoverParse(bcc_handover, sizeof(bcc_handover),
+                                       &cdi_attest, &cdi_seal, &bcc, &bcc_size);
+  EXPECT_EQ(kDiceResultOk, result);
+  EXPECT_EQ(bcc_handover + 4, cdi_attest);
+  EXPECT_EQ(bcc_handover + 39, cdi_seal);
+  EXPECT_EQ(nullptr, bcc);
+  EXPECT_EQ(0u, bcc_size);
+}
+
+TEST(BccHandoverTest, ParseHandoverCdiTooLarge) {
+  const uint8_t bcc_handover[] = {
+      0xa2,
+      // CDI attest
+      0x01, 0x58, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // CDI seal
+      0x02, 0x58, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      // 8-bytes of trailing data that aren't part of the BCC.
+      0x00, 0x41, 0x55, 0xa0, 0x42, 0x11, 0x22, 0x40};
+  const uint8_t *cdi_attest;
+  const uint8_t *cdi_seal;
+  const uint8_t *bcc;
+  size_t bcc_size;
+  DiceResult result = BccHandoverParse(bcc_handover, sizeof(bcc_handover),
+                                       &cdi_attest, &cdi_seal, &bcc, &bcc_size);
+  EXPECT_EQ(kDiceResultInvalidInput, result);
+}
 }
 
 }  // namespace