Refactor COSE encoding to their own functions

The CBOR certificate operations produce COSE encoded public keys and
signatures. Those functions could be reused by clients of the library
that also need to encode these kinds of data. Make the functions
available such that they can be reused and don't need to be duplicated.

Example uses include formatting the root public key or signing some data
with an attestation key.

The verification of the signature is removed as it is not needed
assuming the signing function is correct.

Change-Id: I763af40d7c4571c81f837c86b5d94605da989e95
Reviewed-on: https://pigweed-review.googlesource.com/c/open-dice/+/62680
Reviewed-by: Darren Krahn <dkrahn@google.com>
Pigweed-Auto-Submit: Andrew Scull <ascull@google.com>
Pigweed-Auto-Submit: Darren Krahn <dkrahn@google.com>
Commit-Queue: Darren Krahn <dkrahn@google.com>
diff --git a/include/dice/ops/trait/cose.h b/include/dice/ops/trait/cose.h
new file mode 100644
index 0000000..63404a5
--- /dev/null
+++ b/include/dice/ops/trait/cose.h
@@ -0,0 +1,54 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#ifndef DICE_OPS_TRAIT_COSE_H_
+#define DICE_OPS_TRAIT_COSE_H_
+
+#include <dice/config.h>
+#include <dice/dice.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// These functions may optionally be implemented by a COSE based integration.
+// They aren't directly depended on by the main DICE functions but provide
+// extra utilities that can be used as part of the integration.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Encodes a public key into |buffer| as a COSE_Key structure, setting
+// |encoded_size| to the number of bytes used.
+DiceResult DiceCoseEncodePublicKey(
+    void* context, const uint8_t public_key[DICE_PUBLIC_KEY_SIZE],
+    size_t buffer_size, uint8_t* buffer, size_t* encoded_size);
+
+// Signs the payload and additional authenticated data, formatting the result
+// into a COSE_Sign1 structure. There are no unprotected attributes included in
+// the result.
+//
+// |buffer| is used to hold the intermediate To-Be-Signed (TBS) structure and
+// then the final result. |encoded_size| is set to the size of the final result
+// in |buffer|.
+DiceResult DiceCoseSignAndEncodeSign1(
+    void* context, const uint8_t* payload, size_t payload_size,
+    const uint8_t* aad, size_t aad_size,
+    const uint8_t private_key[DICE_PRIVATE_KEY_SIZE], size_t buffer_size,
+    uint8_t* buffer, size_t* encoded_size);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // DICE_OPS_TRAIT_COSE_H_
diff --git a/src/cbor_cert_op.c b/src/cbor_cert_op.c
index 1746083..3e9ebeb 100644
--- a/src/cbor_cert_op.c
+++ b/src/cbor_cert_op.c
@@ -22,6 +22,7 @@
 #include "dice/cbor_writer.h"
 #include "dice/dice.h"
 #include "dice/ops.h"
+#include "dice/ops/trait/cose.h"
 #include "dice/utils.h"
 
 #if DICE_PUBLIC_KEY_SIZE != 32
@@ -38,28 +39,11 @@
 // Max size of the COSE_Sign1 protected attributes.
 static const size_t kMaxProtectedAttributesSize = 16;
 
-static DiceResult EncodeProtectedAttributes(size_t buffer_size, uint8_t* buffer,
-                                            size_t* encoded_size) {
-  // Constants per RFC 8152.
-  const int64_t kCoseHeaderAlgLabel = 1;
-  const int64_t kCoseAlgEdDSA = -8;
+DiceResult DiceCoseEncodePublicKey(
+    void* context_not_used, const uint8_t public_key[DICE_PUBLIC_KEY_SIZE],
+    size_t buffer_size, uint8_t* buffer, size_t* encoded_size) {
+  (void)context_not_used;
 
-  struct CborOut out;
-  CborOutInit(buffer, buffer_size, &out);
-  CborWriteMap(/*num_elements=*/1, &out);
-  // Add the algorithm.
-  CborWriteInt(kCoseHeaderAlgLabel, &out);
-  CborWriteInt(kCoseAlgEdDSA, &out);
-  if (CborOutOverflowed(&out)) {
-    return kDiceResultBufferTooSmall;
-  }
-  *encoded_size = CborOutSize(&out);
-  return kDiceResultOk;
-}
-
-static DiceResult EncodePublicKey(uint8_t subject_public_key[DICE_PUBLIC_KEY_SIZE],
-                                  size_t buffer_size, uint8_t* buffer,
-                                  size_t* encoded_size) {
   // Constants per RFC 8152.
   const int64_t kCoseKeyKtyLabel = 1;
   const int64_t kCoseKeyAlgLabel = 3;
@@ -87,9 +71,9 @@
   // Add the curve.
   CborWriteInt(kCoseOkpCrvLabel, &out);
   CborWriteInt(kCoseCrvEd25519, &out);
-  // Add the subject public key.
+  // Add the public key.
   CborWriteInt(kCoseOkpXLabel, &out);
-  CborWriteBstr(/*data_size=*/DICE_PUBLIC_KEY_SIZE, subject_public_key, &out);
+  CborWriteBstr(/*data_size=*/DICE_PUBLIC_KEY_SIZE, public_key, &out);
   if (CborOutOverflowed(&out)) {
     return kDiceResultBufferTooSmall;
   }
@@ -97,6 +81,117 @@
   return kDiceResultOk;
 }
 
+static DiceResult EncodeProtectedAttributes(size_t buffer_size, uint8_t* buffer,
+                                            size_t* encoded_size) {
+  // Constants per RFC 8152.
+  const int64_t kCoseHeaderAlgLabel = 1;
+  const int64_t kCoseAlgEdDSA = -8;
+
+  struct CborOut out;
+  CborOutInit(buffer, buffer_size, &out);
+  CborWriteMap(/*num_elements=*/1, &out);
+  // Add the algorithm.
+  CborWriteInt(kCoseHeaderAlgLabel, &out);
+  CborWriteInt(kCoseAlgEdDSA, &out);
+  if (CborOutOverflowed(&out)) {
+    return kDiceResultBufferTooSmall;
+  }
+  *encoded_size = CborOutSize(&out);
+  return kDiceResultOk;
+}
+
+static DiceResult EncodeCoseTbs(const uint8_t* protected_attributes,
+                                size_t protected_attributes_size,
+                                const uint8_t* payload, size_t payload_size,
+                                const uint8_t* aad, size_t aad_size,
+                                size_t buffer_size, uint8_t* buffer,
+                                size_t* encoded_size) {
+  struct CborOut out;
+  CborOutInit(buffer, buffer_size, &out);
+  // TBS is an array of four elements.
+  CborWriteArray(/*num_elements=*/4, &out);
+  // Context string field.
+  CborWriteTstr("Signature1", &out);
+  // Protected attributes from COSE_Sign1.
+  CborWriteBstr(protected_attributes_size, protected_attributes, &out);
+  // Additional authenticated data.
+  CborWriteBstr(aad_size, aad, &out);
+  // Payload from COSE_Sign1.
+  CborWriteBstr(payload_size, payload, &out);
+  if (CborOutOverflowed(&out)) {
+    return kDiceResultBufferTooSmall;
+  }
+  *encoded_size = CborOutSize(&out);
+  return kDiceResultOk;
+}
+
+static DiceResult EncodeCoseSign1(const uint8_t* protected_attributes,
+                                  size_t protected_attributes_size,
+                                  const uint8_t* payload, size_t payload_size,
+                                  const uint8_t signature[DICE_SIGNATURE_SIZE],
+                                  size_t buffer_size, uint8_t* buffer,
+                                  size_t* encoded_size) {
+  struct CborOut out;
+  CborOutInit(buffer, buffer_size, &out);
+  // COSE_Sign1 is an array of four elements.
+  CborWriteArray(/*num_elements=*/4, &out);
+  // Protected attributes.
+  CborWriteBstr(protected_attributes_size, protected_attributes, &out);
+  // Empty map for unprotected attributes.
+  CborWriteMap(/*num_pairs=*/0, &out);
+  // Payload.
+  CborWriteBstr(payload_size, payload, &out);
+  // Signature.
+  CborWriteBstr(/*num_elements=*/DICE_SIGNATURE_SIZE, signature, &out);
+  if (CborOutOverflowed(&out)) {
+    return kDiceResultBufferTooSmall;
+  }
+  *encoded_size = CborOutSize(&out);
+  return kDiceResultOk;
+}
+
+DiceResult DiceCoseSignAndEncodeSign1(
+    void* context, const uint8_t* payload, size_t payload_size,
+    const uint8_t* aad, size_t aad_size,
+    const uint8_t private_key[DICE_PRIVATE_KEY_SIZE], size_t buffer_size,
+    uint8_t* buffer, size_t* encoded_size) {
+  DiceResult result;
+
+  *encoded_size = 0;
+
+  // The encoded protected attributes are used in the TBS and the final
+  // COSE_Sign1 structure.
+  uint8_t protected_attributes[kMaxProtectedAttributesSize];
+  size_t protected_attributes_size = 0;
+  result = EncodeProtectedAttributes(sizeof(protected_attributes),
+                                     protected_attributes,
+                                     &protected_attributes_size);
+  if (result != kDiceResultOk) {
+    return result;
+  }
+
+  // Construct a To-Be-Signed (TBS) structure based on the relevant fields of
+  // the COSE_Sign1.
+  result = EncodeCoseTbs(protected_attributes, protected_attributes_size,
+                         payload, payload_size, aad, aad_size, buffer_size,
+                         buffer, encoded_size);
+  if (result != kDiceResultOk) {
+    return result;
+  }
+
+  // Sign the TBS with the authority key.
+  uint8_t signature[DICE_SIGNATURE_SIZE];
+  result = DiceSign(context, buffer, *encoded_size, private_key, signature);
+  if (result != kDiceResultOk) {
+    return result;
+  }
+
+  // The final certificate is an untagged COSE_Sign1 structure.
+  return EncodeCoseSign1(protected_attributes, protected_attributes_size,
+                         payload, payload_size, signature, buffer_size, buffer,
+                         encoded_size);
+}
+
 // Encodes a CBOR Web Token (CWT) with an issuer, subject, and additional
 // fields.
 static DiceResult EncodeCwt(void* context, const DiceInputValues* input_values,
@@ -200,55 +295,6 @@
   return kDiceResultOk;
 }
 
-static DiceResult EncodeCoseTbs(const uint8_t* protected_attributes,
-                                size_t protected_attributes_size,
-                                const uint8_t* payload, size_t payload_size,
-                                size_t buffer_size, uint8_t* buffer,
-                                size_t* encoded_size) {
-  struct CborOut out;
-  CborOutInit(buffer, buffer_size, &out);
-  // TBS is an array of four elements.
-  CborWriteArray(/*num_elements=*/4, &out);
-  // Context string field.
-  CborWriteTstr("Signature1", &out);
-  // Protected attributes from COSE_Sign1.
-  CborWriteBstr(protected_attributes_size, protected_attributes, &out);
-  // Empty application data.
-  CborWriteBstr(/*data_size=*/0, /*data=*/NULL, &out);
-  // Payload from COSE_Sign1.
-  CborWriteBstr(payload_size, payload, &out);
-  if (CborOutOverflowed(&out)) {
-    return kDiceResultBufferTooSmall;
-  }
-  *encoded_size = CborOutSize(&out);
-  return kDiceResultOk;
-}
-
-static DiceResult EncodeCoseSign1(const uint8_t* protected_attributes,
-                                  size_t protected_attributes_size,
-                                  const uint8_t* payload, size_t payload_size,
-                                  const uint8_t signature[DICE_SIGNATURE_SIZE],
-                                  size_t buffer_size, uint8_t* buffer,
-                                  size_t* encoded_size) {
-  struct CborOut out;
-  CborOutInit(buffer, buffer_size, &out);
-  // COSE_Sign1 is an array of four elements.
-  CborWriteArray(/*num_elements=*/4, &out);
-  // Protected attributes.
-  CborWriteBstr(protected_attributes_size, protected_attributes, &out);
-  // Empty map for unprotected attributes.
-  CborWriteMap(/*num_pairs=*/0, &out);
-  // Payload.
-  CborWriteBstr(payload_size, payload, &out);
-  // Signature.
-  CborWriteBstr(/*num_elements=*/DICE_SIGNATURE_SIZE, signature, &out);
-  if (CborOutOverflowed(&out)) {
-    return kDiceResultBufferTooSmall;
-  }
-  *encoded_size = CborOutSize(&out);
-  return kDiceResultOk;
-}
-
 DiceResult DiceGenerateCertificate(
     void* context,
     const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
@@ -270,7 +316,6 @@
   // These are 'variably modified' types so need to be declared upfront.
   uint8_t encoded_public_key[kMaxPublicKeySize];
   uint8_t payload[kMaxCertificateSize];
-  uint8_t protected_attributes[kMaxProtectedAttributesSize];
 
   // Derive keys and IDs from the private key seeds.
   uint8_t subject_public_key[DICE_PUBLIC_KEY_SIZE];
@@ -281,8 +326,8 @@
   }
 
   uint8_t subject_id[20];
-  result =
-      DiceDeriveCdiCertificateId(context, subject_public_key, DICE_PUBLIC_KEY_SIZE, subject_id);
+  result = DiceDeriveCdiCertificateId(context, subject_public_key,
+                                      DICE_PUBLIC_KEY_SIZE, subject_id);
   if (result != kDiceResultOk) {
     goto out;
   }
@@ -309,20 +354,11 @@
                 sizeof(authority_id_hex));
   authority_id_hex[sizeof(authority_id_hex) - 1] = '\0';
 
-  // The encoded protected attributes are used in the TBS and the final
-  // COSE_Sign1 structure.
-  size_t protected_attributes_size = 0;
-  result = EncodeProtectedAttributes(sizeof(protected_attributes),
-                                     protected_attributes,
-                                     &protected_attributes_size);
-  if (result != kDiceResultOk) {
-    goto out;
-  }
-
   // The public key encoded as a COSE_Key structure is embedded in the CWT.
   size_t encoded_public_key_size = 0;
-  result = EncodePublicKey(subject_public_key, sizeof(encoded_public_key),
-                           encoded_public_key, &encoded_public_key_size);
+  result = DiceCoseEncodePublicKey(
+      context, subject_public_key, sizeof(encoded_public_key),
+      encoded_public_key, &encoded_public_key_size);
   if (result != kDiceResultOk) {
     goto out;
   }
@@ -336,32 +372,10 @@
     goto out;
   }
 
-  // Construct a To-Be-Signed (TBS) structure based on the relevant fields of
-  // the COSE_Sign1.
-  result = EncodeCoseTbs(protected_attributes, protected_attributes_size,
-                         payload, payload_size, certificate_buffer_size,
-                         certificate, certificate_actual_size);
-  if (result != kDiceResultOk) {
-    goto out;
-  }
-
-  // Sign the TBS with the authority key.
-  uint8_t signature[DICE_SIGNATURE_SIZE];
-  result = DiceSign(context, certificate, *certificate_actual_size,
-                    authority_private_key, signature);
-  if (result != kDiceResultOk) {
-    goto out;
-  }
-  result = DiceVerify(context, certificate, *certificate_actual_size, signature,
-                      authority_public_key);
-  if (result != kDiceResultOk) {
-    goto out;
-  }
-
-  // The final certificate is an untagged COSE_Sign1 structure.
-  result = EncodeCoseSign1(
-      protected_attributes, protected_attributes_size, payload, payload_size,
-      signature, certificate_buffer_size, certificate, certificate_actual_size);
+  result = DiceCoseSignAndEncodeSign1(
+      context, payload, payload_size, /*aad=*/NULL, /*aad_size=*/0,
+      authority_private_key, certificate_buffer_size, certificate,
+      certificate_actual_size);
 
 out:
   DiceClearMemory(context, sizeof(subject_private_key), subject_private_key);