Update BccHandoverMainFlow to handle BccHandovers where Bcc is absent.

For privacy reasons, Bcc might be absent in BccHandover in some cases.
E.g. aosp/2025443
This CL updates BccHandoverMainFlow to handle such BccHandovers and
to produce BccHandover to the next stage.

Bug: 220375452
Test: A new test is added to test bcc-handover without bcc given as
      input to BccHandoverMainFlow.
Change-Id: I72bf06beb134a262acd6d2851170a7a415551adc
Reviewed-on: https://pigweed-review.googlesource.com/c/open-dice/+/88200
Reviewed-by: Andrew Scull <ascull@google.com>
Reviewed-by: Darren Krahn <dkrahn@google.com>
Commit-Queue: Hasini Gunasinghe <hasinitg@google.com>
diff --git a/src/android/BUILD.gn b/src/android/BUILD.gn
index 4cef1b0..dd8bdbc 100644
--- a/src/android/BUILD.gn
+++ b/src/android/BUILD.gn
@@ -21,6 +21,7 @@
   deps = [
     "//:cbor_reader",
     "//:cbor_writer",
+    "//:dice_with_cbor_cert",
   ]
 }
 
@@ -30,7 +31,6 @@
   ]
   deps = [
     ":bcc",
-    "//:dice_with_cbor_cert",
   ]
 }
 
@@ -40,6 +40,5 @@
   ]
   deps = [
     ":bcc",
-    "//:dice_with_boringssl_ops",
   ]
 }
diff --git a/src/android/bcc.c b/src/android/bcc.c
index 60a98a0..1990e5c 100644
--- a/src/android/bcc.c
+++ b/src/android/bcc.c
@@ -18,7 +18,9 @@
 
 #include "dice/cbor_reader.h"
 #include "dice/cbor_writer.h"
+#include "dice/ops/trait/cose.h"
 #include "dice/dice.h"
+#include "dice/ops.h"
 
 // Completely gratuitous bit twiddling.
 static size_t PopulationCount(uint32_t n) {
@@ -127,6 +129,69 @@
   return kDiceResultOk;
 }
 
+static DiceResult BccMainFlowWithNewBcc(
+    void* context, const uint8_t current_cdi_attest[DICE_CDI_SIZE],
+    const uint8_t current_cdi_seal[DICE_CDI_SIZE],
+    const DiceInputValues* input_values, size_t buffer_size, uint8_t* buffer,
+    size_t* bcc_size, uint8_t next_cdi_attest[DICE_CDI_SIZE],
+    uint8_t next_cdi_seal[DICE_CDI_SIZE]) {
+  uint8_t current_cdi_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE];
+  uint8_t attestation_public_key[DICE_PUBLIC_KEY_SIZE];
+  uint8_t attestation_private_key[DICE_PRIVATE_KEY_SIZE];
+  // Derive an asymmetric private key seed from the current attestation CDI
+  // value.
+  DiceResult result = DiceDeriveCdiPrivateKeySeed(context, current_cdi_attest,
+                                                  current_cdi_private_key_seed);
+  if (result != kDiceResultOk) {
+    goto out;
+  }
+  // Derive attestation key pair.
+  result = DiceKeypairFromSeed(context, current_cdi_private_key_seed,
+                               attestation_public_key, attestation_private_key);
+  if (result != kDiceResultOk) {
+    goto out;
+  }
+
+  // Consruct the BCC from the attestation public key and the next CDI
+  // certificate.
+  struct CborOut out;
+  CborOutInit(buffer, buffer_size, &out);
+  CborWriteArray(2, &out);
+  if (CborOutOverflowed(&out)) {
+    result = kDiceResultBufferTooSmall;
+    goto out;
+  }
+  size_t encoded_size_used = CborOutSize(&out);
+  buffer += encoded_size_used;
+  buffer_size -= encoded_size_used;
+
+  size_t encoded_pub_key_size = 0;
+  result = DiceCoseEncodePublicKey(context, attestation_public_key, buffer_size,
+                                   buffer, &encoded_pub_key_size);
+  if (result != kDiceResultOk) {
+    goto out;
+  }
+
+  buffer += encoded_pub_key_size;
+  buffer_size -= encoded_pub_key_size;
+
+  result = DiceMainFlow(context, current_cdi_attest, current_cdi_seal,
+                        input_values, buffer_size, buffer, bcc_size,
+                        next_cdi_attest, next_cdi_seal);
+  if (result != kDiceResultOk) {
+    return result;
+  }
+  bcc_size += encoded_size_used + encoded_pub_key_size;
+
+out:
+  DiceClearMemory(context, sizeof(current_cdi_private_key_seed),
+                  current_cdi_private_key_seed);
+  DiceClearMemory(context, sizeof(attestation_private_key),
+                  attestation_private_key);
+
+  return result;
+}
+
 DiceResult BccHandoverMainFlow(void* context, const uint8_t* bcc_handover,
                                size_t bcc_handover_size,
                                const DiceInputValues* input_values,
@@ -146,13 +211,13 @@
   // BccHandover = {
   //   1 : bstr .size 32,     ; CDI_Attest
   //   2 : bstr .size 32,     ; CDI_Seal
-  //   3 : Bcc,               ; Certificate chain
+  //   ? 3 : Bcc,             ; Certificate chain
   // }
   struct CborIn in;
   int64_t label;
   size_t item_size;
   CborInInit(bcc_handover, bcc_handover_size, &in);
-  if (CborReadMap(&in, &item_size) != CBOR_READ_RESULT_OK || item_size < 3 ||
+  if (CborReadMap(&in, &item_size) != CBOR_READ_RESULT_OK || item_size < 2 ||
       // Read the attestation CDI.
       CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
       label != kCdiAttestLabel ||
@@ -163,17 +228,23 @@
       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 ||
-      // Read the BCC.
-      CborReadInt(&in, &label) != CBOR_READ_RESULT_OK || label != kBccLabel) {
+      item_size != DICE_CDI_SIZE) {
     return kDiceResultInvalidInput;
   }
-  size_t bcc_start = CborInOffset(&in);
-  bcc = bcc_handover + bcc_start;
-  if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
-    return kDiceResultInvalidInput;
+
+  size_t bcc_size = 0;
+  // Calculate the BCC size, if the BCC is present in the BccHandover.
+  if (CborReadInt(&in, &label) == CBOR_READ_RESULT_OK) {
+    if (label != kBccLabel) {
+      return kDiceResultInvalidInput;
+    }
+    size_t bcc_start = CborInOffset(&in);
+    bcc = bcc_handover + bcc_start;
+    if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
+      return kDiceResultInvalidInput;
+    }
+    bcc_size = CborInOffset(&in) - bcc_start;
   }
-  size_t bcc_size = CborInOffset(&in) - bcc_start;
 
   // Write the new handover data.
   struct CborOut out;
@@ -189,14 +260,24 @@
     return kDiceResultBufferTooSmall;
   }
 
-  result = BccMainFlow(context, current_cdi_attest, current_cdi_seal, bcc,
-                       bcc_size, input_values, buffer_size - CborOutSize(&out),
-                       buffer + CborOutSize(&out), &bcc_size, next_cdi_attest,
-                       next_cdi_seal);
-  if (result != kDiceResultOk) {
-    return result;
+  if (bcc_size != 0) {
+    // If BCC is present in the bcc_handover, append the next certificate to the
+    // existing BCC.
+    result = BccMainFlow(context, current_cdi_attest, current_cdi_seal, bcc,
+                         bcc_size, input_values, buffer_size - CborOutSize(&out),
+                         buffer + CborOutSize(&out), &bcc_size, next_cdi_attest,
+                         next_cdi_seal);
+  } else {
+    // If BCC is not present in the bcc_handover, construct BCC from the public key
+    // derived from the current CDI attest and the next CDI certificate.
+    result = BccMainFlowWithNewBcc(
+        context, current_cdi_attest, current_cdi_seal, input_values,
+        buffer_size - CborOutSize(&out), buffer + CborOutSize(&out), &bcc_size,
+        next_cdi_attest, next_cdi_seal);
   }
-
+  if (result != kDiceResultOk) {
+      return result;
+  }
   *actual_size = CborOutSize(&out) + bcc_size;
   return kDiceResultOk;
 }
diff --git a/src/android/bcc_test.cc b/src/android/bcc_test.cc
index 20bea0d..44dce5b 100644
--- a/src/android/bcc_test.cc
+++ b/src/android/bcc_test.cc
@@ -114,6 +114,29 @@
   EXPECT_EQ(0, memcmp(next_bcc_handover + 73, bcc_handover + 73,
                       sizeof(bcc_handover) - 8 - 73));
 }
+
+TEST(BccHandoverNoCertTest, InHandoverWithoutBccOutHandoverWithBcc) {
+  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,
+  };
+  DiceInputValues input_values = {};
+  uint8_t next_bcc_handover[1024] = {};
+  size_t next_bcc_handover_size;
+  DiceResult result = BccHandoverMainFlow(
+      /*context=*/NULL, bcc_handover, sizeof(bcc_handover), &input_values,
+      sizeof(next_bcc_handover), next_bcc_handover, &next_bcc_handover_size);
+  EXPECT_EQ(kDiceResultOk, result);
+  EXPECT_GT(next_bcc_handover_size, sizeof(bcc_handover));
+  EXPECT_EQ(0xa3, next_bcc_handover[0]);
+}
 }
 
 }  // namespace