Update DiceMainFlow to compute CDI certificate only if requested.

In the existing code, DiceMainFlow computes i) the two CDIs and
ii) the CDI certificate. However, in certain cases, we need
to compute only the two CDIs for the next stage.
For e.g. due to privacy reasons, certain parent stages might not
pass the CDI certificate to the next stage. In such cases,
the caller can signal this by passing zero/null values to the
certificate parameters.

Tests: Added a test to verify that passing null/zero values to
       DiceMainFlow doesn't cause panic.

Change-Id: I1b5c4939b82f79bf72c3f22d2d9d5a40c944a6cb
Reviewed-on: https://pigweed-review.googlesource.com/c/open-dice/+/87560
Reviewed-by: Andrew Scull <ascull@google.com>
Commit-Queue: Hasini Gunasinghe <hasinitg@google.com>
diff --git a/include/dice/dice.h b/include/dice/dice.h
index 2e83424..cf54942 100644
--- a/include/dice/dice.h
+++ b/include/dice/dice.h
@@ -116,6 +116,9 @@
 // Given a full set of input values and the current CDI values, computes the
 // next CDI values and a matching certificate. See the Open Profile for DICE
 // specification for a detailed explanation of this flow.
+// In certain cases, the caller may not need to generate the CDI certificate.
+// The caller should signal this by setting the certificate parameters to
+// null/zero values appropriately.
 //
 // Parameters:
 //    context: Context provided by the caller that is opaque to this library
@@ -129,16 +132,19 @@
 //    input_values: A set of input values describing the target program or
 //        system.
 //    next_cdi_certificate_buffer_size: The size in bytes of the buffer pointed
-//        to by the |next_cdi_certificate| argument.
+//        to by the |next_cdi_certificate| argument. This should be set to zero
+//        if next CDI certificate should not be computed.
 //    next_cdi_certificate: On success, will be populated with the generated
 //        certificate, up to |next_cdi_certificate_buffer_size| in size. If the
 //        certificate cannot fit in the buffer, |next_cdi_certificate_size| is
 //        populated with the required size and kDiceResultBufferTooSmall is
-//        returned.
+//        returned. This should be set to NULL if next CDI certificate should
+//        not be computed.
 //    next_cdi_certificate_actual_size: On success, will be populated with the
 //        size, in bytes, of the certificate data written to
 //        |next_cdi_certificate|. If kDiceResultBufferTooSmall is returned, will
-//        be populated with the required buffer size.
+//        be populated with the required buffer size. This should be set to NULL
+//        if next CDI certificate should not be computed.
 //    next_cdi_attest: On success, will be populated with the next CDI value for
 //        attestation.
 //    next_cdi_seal: On success, will be populated with the next CDI value for
diff --git a/src/dice.c b/src/dice.c
index e9b0cd9..4bbd712 100644
--- a/src/dice.c
+++ b/src/dice.c
@@ -152,6 +152,14 @@
     goto out;
   }
 
+  // Create the CDI certificate only if it is required (i.e. non-null/non-zero
+  // values are provided for the next CDI certificate parameters).
+  if (next_cdi_certificate == NULL &&
+      next_cdi_certificate_actual_size == NULL &&
+      next_cdi_certificate_buffer_size == 0) {
+    goto out;
+  }
+
   // Derive asymmetric private key seeds from the attestation CDI values.
   result = DiceDeriveCdiPrivateKeySeed(context, current_cdi_attest,
                                        current_cdi_private_key_seed);
@@ -170,9 +178,7 @@
       context, next_cdi_private_key_seed, current_cdi_private_key_seed,
       input_values, next_cdi_certificate_buffer_size, next_cdi_certificate,
       next_cdi_certificate_actual_size);
-  if (result != kDiceResultOk) {
-    goto out;
-  }
+
 out:
   // Clear sensitive memory.
   DiceClearMemory(context, sizeof(input_buffer), input_buffer);
diff --git a/src/dice_test.cc b/src/dice_test.cc
index 65330f7..a5d3b9e 100644
--- a/src/dice_test.cc
+++ b/src/dice_test.cc
@@ -198,4 +198,24 @@
   EXPECT_LE(ops.generate_certificate_count_, 1);
 }
 
+TEST(DiceTest, NoCertParamsPreservesCDIs) {
+  FakeDiceOps ops;
+  DiceStateForTest current_state = {};
+  DiceStateForTest next_state = {};
+  DiceStateForTest next_state_no_cert = {};
+  DiceInputValues input_values = {};
+  DiceResult result = DiceMainFlow(
+      &ops, current_state.cdi_attest, current_state.cdi_seal, &input_values,
+      sizeof(next_state.certificate), next_state.certificate,
+      &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal);
+  result = DiceMainFlow(
+      &ops, current_state.cdi_attest, current_state.cdi_seal, &input_values, 0,
+      NULL, NULL, next_state_no_cert.cdi_attest, next_state_no_cert.cdi_seal);
+  EXPECT_EQ(kDiceResultOk, result);
+  EXPECT_EQ(0, memcmp(next_state.cdi_attest, next_state_no_cert.cdi_attest,
+                      DICE_CDI_SIZE));
+  EXPECT_EQ(0, memcmp(next_state.cdi_seal, next_state_no_cert.cdi_seal,
+                      DICE_CDI_SIZE));
+}
+
 }  // namespace